From: cherierm Date: Thu, 13 Mar 2008 13:46:37 +0000 (+0000) Subject: tesh version 2 X-Git-Tag: v3.3~587 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/bea7d471e30c245e71b6276dcdcd420f8e9d088f tesh version 2 git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@5289 48e7efb5-ca39-0410-a469-dd3cf9ba447f --- diff --git a/tools/tesh2/include/_signal.h b/tools/tesh2/include/_signal.h new file mode 100644 index 0000000000..b629c05a95 --- /dev/null +++ b/tools/tesh2/include/_signal.h @@ -0,0 +1,197 @@ +#ifndef __SIGNAL_H +#define __SIGNAL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 + +/* terminal line hangup */ +#ifndef SIGHUP +#define SIGHUP 1 +#endif + +/* interrupt program */ +#ifndef SIGINT +#define SIGINT 2 +#endif + +/* quit program */ +#ifndef SIGQUIT +#define SIGQUIT 3 +#endif + +/* illegal instruction */ +#ifndef SIGILL +#define SIGILL 4 +#endif + +/* trace trap */ +#ifndef SIGTRAP +#define SIGTRAP 5 +#endif + +/* abnormal termination triggered by abort call */ +#ifndef SIGABRT +#define SIGABRT 6 +#endif + +/* floating point exception */ +#ifndef SIGFPE +#define SIGFPE 8 +#endif + +/* kill program */ +#ifndef SIGKILL +#define SIGKILL 9 +#endif + +/* bus error */ +#ifndef SIGBUS +#define SIGBUS 10 +#endif + +/* segment violation */ +#ifndef SIGSEGV +#define SIGSEGV 11 +#endif + +/* non-existent system call invoked */ +#ifndef SIGSYS +#define SIGSYS 12 +#endif + +/* write on a pipe with no reader */ +#ifndef SIGPIPE +#define SIGPIPE 13 +#endif + +/* real-time timer expired */ +#ifndef SIGALRM +#define SIGALRM 14 +#endif + +/* software termination signal from kill */ +#ifdef SIGTERM +#define SIGTERM 15 +#endif + +/* urgent condition present on socket */ +#ifndef SIGURG +#define SIGURG 16 +#endif + +/* stop (cannot be caught orignored) */ +#ifndef SIGSTOP +#define SIGSTOP 17 +#endif + +/* stop signal generated from keyboard */ +#ifndef SIGTSTP +#define SIGTSTP 18 +#endif + +/* continue after stop */ +#ifndef SIGCONT +#define SIGCONT 19 +#endif + +/* child status has changed */ +#ifndef SIGCHLD +#define SIGCHLD 20 +#endif + +/* background read attempted from control terminal */ +#ifndef SIGTTIN +#define SIGTTIN 21 +#endif + +/* background write attempted to control terminal */ +#ifndef SIGTTOU +#define SIGTTOU 22 +#endif + +/* I/O is possible on a descriptor see fcntl(2)) */ +#ifndef SIGIO +#define SIGIO 23 +#endif + +/* cpu time limit exceeded (see setrlimit(2)) */ +#ifndef SIGXCPU +#define SIGXCPU 24 +#endif + +/* file size limit exceeded (see setrlimit(2)) */ +#ifndef SIGXFSZ +#define SIGXFSZ 25 +#endif + +/* virtual time alarm (see setitimer(2)) */ +#ifndef SIGVTALRM +#define SIGVTALRM 26 +#endif + +/* profiling timer alarm (see setitimer(2)) */ +#ifndef SIGPROF +#define SIGPROF 27 +#endif + +/* window size change */ +#ifndef SIGWINCH +#define SIGWINCH 28 +#endif + +/* user defined signal 1 */ +#ifndef SIGUSR1 +#define SIGUSR1 30 +#endif + +/* user defined signal 2 */ +#ifndef SIGUSR2 +#define SIGUSR2 31 +#endif + + +int +is_an_unhandled_exception(DWORD exit_code); + +/* + *return a non-zero value if status was returned for a child process that terminated normally. + */ +#define WIFEXITED(__status) !is_an_unhandled_exception((__status)) + +/* if the value of WIFEXITED(__status) is non-zero, this macro evaluates the value the child + * process returned from main(). + */ +#define WEXITSTATUS(__status) (__status) + +/* return a non-zero value if status was returned for a child process that terminated due to the + * receipt of a signal that was not caught + */ +#define WIFSIGNALED(__status) is_an_unhandled_exception((__status)) + +/* if the value of WIFSIGNALED(__status) is non-zero, this macro evaluates to the number of the + * signal that caused the termination of the child process. + */ +#define WTERMSIG(__status) (__status) + +#endif /* WIN32 */ + + +#ifdef WIN32 +const char* +signal_name(DWORD got, char* expected); +#else +const char* +signal_name(unsigned int got, char *expected); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !__SIGNAL_H */ diff --git a/tools/tesh2/include/allocator.h b/tools/tesh2/include/allocator.h new file mode 100644 index 0000000000..4db00b892d --- /dev/null +++ b/tools/tesh2/include/allocator.h @@ -0,0 +1,94 @@ +#ifndef __ALLOCATOR_H +#define __ALLOCATOR_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef __FN_FINALIZE_T_DEFINED +typedef int (*fn_finalize_t)(void**); +#define __FN_FINALIZE_T_DEFINED +#endif + +#ifndef __BYTE_T_DEFINED +typedef unsigned char byte; +#define __BYTE_T_DEFINED +#endif + +/* forward declarations */ +struct s_allocator; /* allocator struct */ +struct s_allocator_block; /* allocator block struct */ +struct s_allocator_node; /* allocator node struct */ + + +/* the allocator node type */ +typedef struct s_allocator_node +{ + struct s_allocator_node* next; /* the next node in the block */ + struct s_allocator_block* block; /* the block which contains the node */ + int is_allocated; /* if 1 the node is allocated (used) */ +}s_allocator_node_t,* allocator_node_t; + +/* the allocator block type */ +typedef struct s_allocator_block +{ + struct s_allocator* allocator; /* the allocator which contains the block */ + struct s_allocator_block* next; /* the next block */ +}s_allocator_block_t,* allocator_block_t; + +/* the allocator type */ +typedef struct s_allocator +{ + allocator_block_t head; /* the head of the allocator blocks */ + unsigned int block_capacity; /* the capacity of the allocator blocks (nodes per block) */ + int capacity; /* the current capacity of the allocator (nodes per block x block count)*/ + int type_size; /* size of allocated type */ + int size; /* current node (object) count */ + allocator_node_t free; /* pointer to the next free node */ + allocator_node_t first; /* pointer to the first free node */ + fn_finalize_t fn_finalize; /* pointer to the function used to destroy the allocated objects */ +}s_allocator_t,* allocator_t; + +allocator_t +allocator_new(int block_capacity, int type_size, fn_finalize_t fn_finalize); + +int +allocator_free(allocator_t* allocator_ptr); + +void* +allocator_alloc(allocator_t allocator); + +int +allocator_dealloc(allocator_t allocator,void* block); + +int +allocator_get_size(allocator_t allocator); + +int +allocator_get_capacity(allocator_t allocator); + +int +allocator_get_type_size(allocator_t allocator); + +int +allocator_get_block_capacity(allocator_t allocator); + +int +allocator_clear(allocator_t allocator); + +int +allocator_get_capacity_available(allocator_t allocator); + +int +allocator_is_empty(allocator_t allocator); + +int +allocator_is_full(allocator_t allocator); + + +#ifdef __cplusplus +} +#endif + +#endif /* !__allocator_H */ diff --git a/tools/tesh2/include/com.h b/tools/tesh2/include/com.h new file mode 100644 index 0000000000..80ff496bd9 --- /dev/null +++ b/tools/tesh2/include/com.h @@ -0,0 +1,81 @@ +#ifndef __GLOBAL_H +#define __GLOBAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * if 1, keep going when some commands can't be founded + */ +extern int +want_keep_going; + +/* if 1, ignore failures from commands + * the possibles failures are : + * + * - exit code != expected exit code + * - signal != expected signal + * - output != expected output + * - read pipe broken + * - write pipe broken + * - timeout + * + * remark : + * + * a command not found is not a failure, it's an error + * to keep going when a command is not found specify the + * option --keep-going + */ +extern int +want_keep_going_unit; + +/* + * the semaphore used to synchronize the jobs + */ +extern xbt_os_sem_t +jobs_sem; + +/* + * the semaphore used by the runner to wait the end of all the units + */ +extern xbt_os_sem_t +units_sem; + +/* the dlist of tesh include directories */ +extern vector_t +includes; + +extern int +interrupted; + +extern int +exit_code; + +extern int +want_silent; + +extern int +want_dry_run; + +extern int +want_just_display; + +extern int +dont_want_display_directory; + +extern directory_t +root_directory; + + +#ifdef __cplusplus +} +#endif + +#endif /* !__GLOBAL_H */ + diff --git a/tools/tesh2/include/command.h b/tools/tesh2/include/command.h new file mode 100644 index 0000000000..9f392023f6 --- /dev/null +++ b/tools/tesh2/include/command.h @@ -0,0 +1,43 @@ +#ifndef __COMMAND_H +#define __COMMAND_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +command_t +command_new(unit_t unit, context_t context, xbt_os_mutex_t mutex); + +void +command_free(command_t* command); + +void +command_run(command_t command); + +void +command_exec(command_t command, const char* command_line); + +void +command_wait(command_t command); + +void +command_interrupt(command_t command); + +void +command_display_status(command_t command); + +void +command_handle_failure(command_t command, cs_reason_t reason); + +void command_kill(command_t command); + +void +command_check(command_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* !_COMMAND_H */ diff --git a/tools/tesh2/include/context.h b/tools/tesh2/include/context.h new file mode 100644 index 0000000000..cc2fe9311c --- /dev/null +++ b/tools/tesh2/include/context.h @@ -0,0 +1,37 @@ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +context_t +context_new(void); + +void +context_clear(context_t context); + +void +context_reset(context_t context); + +void +context_input_write(context_t context, const char* buffer); + +void +context_ouput_read(context_t context, const char* buffer); + + + +context_t +context_dup(context_t context); + +void +context_free(context_t* context); + +#ifdef __cplusplus +extern } +#endif + +#endif /* !_CONTEXT_H */ diff --git a/tools/tesh2/include/def.h b/tools/tesh2/include/def.h new file mode 100644 index 0000000000..d5b45aa749 --- /dev/null +++ b/tools/tesh2/include/def.h @@ -0,0 +1,67 @@ +#ifndef __DEF_H +#define __DEF_H + +/* must be defined first */ +#ifdef WIN32 + + #define _WIN32_WINNT 0x0400 + + #if (_MSC_VER >= 1400 && !defined(_CRT_SECURE_NO_DEPRECATE)) + #define _CRT_SECURE_NO_DEPRECATE + #endif + + #include /* for getcwd(), _chdir() */ + +#endif + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 + + #define strdup _strdup + #define chdir _chdir + #define getcwd _getcwd + + #ifndef S_ISDIR + #define S_ISDIR(__mode) (((__mode) & S_IFMT) == S_IFDIR) + #endif + + #ifndef S_ISREG + #define S_ISREG(__mode) (((__mode) & S_IFMT) == S_IFREG) + #endif + + #define INDEFINITE_PID NULL + #define INDEFINITE_FD NULL +#else + #define INDEFINITE_PID ((int)-1) + #define INDEFINITE_FD ((int)-1) +#endif + +#ifndef MAX_PATH + #define MAX_PATH ((unsigned int)255) +#endif + + +#define INDEFINITE ((int)-1) +#define INDEFINITE_SIGNAL NULL + +#define DEFAULT_FSTREAMS_CAPACITY ((int)128) +#define DEFAULT_INCLUDES_CAPACITY DEFAULT_FSTREAMS_CAPACITY +#define DEFAULT_UNITS_CAPACITY ((int)64) +#define DEFAULT_COMMANDS_CAPACITY ((int)1024) +#define DEFAULT_SUITES_CAPACITY ((int)128) + +#define MAX_SUFFIX ((unsigned int)9) + +#ifdef __cplusplus +} +#endif + + +#endif /* !__DEF_H */ + diff --git a/tools/tesh2/include/dictionary.h b/tools/tesh2/include/dictionary.h new file mode 100644 index 0000000000..17bd744b5c --- /dev/null +++ b/tools/tesh2/include/dictionary.h @@ -0,0 +1,50 @@ +#ifndef __dictionary_H +#define __dictionary_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FN_FINALIZE_T_DEFINED +typedef int (*fn_finalize_t)(void**); +#define __FN_FINALIZE_T_DEFINED +#endif + +typedef struct s_dictionary +{ + htable_t htable; +}s_dictionary_t,* dictionary_t; + + +dictionary_t +dictionary_new(fn_finalize_t fn_finalize); + +int +dictionary_set(dictionary_t dictionary,const char* key, const void* val); + +void* +dictionary_get(dictionary_t dictionary,const char* key); + +int +dictionary_free(dictionary_t* dictionaryptr); + +int +dictionary_clear(dictionary_t dictionary); + +int +dictionary_get_size(dictionary_t dictionary); + +int +dictionary_is_empty(dictionary_t dictionary); + +void* +dictionary_remove(dictionary_t dictionary,const char* key); + +#ifdef __cplusplus +} +#endif + + +#endif /* !__dictionary_H */ diff --git a/tools/tesh2/include/directories.h b/tools/tesh2/include/directories.h new file mode 100644 index 0000000000..d000f77546 --- /dev/null +++ b/tools/tesh2/include/directories.h @@ -0,0 +1,42 @@ +#ifndef __DIRECTORIES_H +#define __DIRECTORIES_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +directories_t +directories_new(void); + +int +directories_add(directories_t directories, directory_t directory); + +int +directories_contains(directories_t directories, directory_t directory); + +int +directories_load(directories_t directories, fstreams_t fstreams, lstrings_t suffixes); + +int +directories_free(void** directoriesptr); + +directory_t +directories_get_back(directories_t directories); + +directory_t +directories_search_fstream_directory(directories_t directories, const char* name); + +int +directories_get_size(directories_t directories); + +int +directories_has_directories_to_load(directories_t directories); + +#ifdef __cplusplus +} +#endif + +#endif /*!__DIRECTORIES_H */ diff --git a/tools/tesh2/include/directory.h b/tools/tesh2/include/directory.h new file mode 100644 index 0000000000..42fd920a06 --- /dev/null +++ b/tools/tesh2/include/directory.h @@ -0,0 +1,33 @@ +#ifndef __DIRECTORY_H +#define __DIRECTORY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +directory_t +directory_new(const char* name, int load); + +int +directory_open(directory_t directory); + +int +directory_close(directory_t directory); + +int +directory_load(directory_t directory, fstreams_t fstreams, lstrings_t suffixes); + +int +directory_free(void** directoryptr); + +const char* +directory_get_name(directory_t directory); + + +#ifdef __cplusplus +} +#endif + +#endif /*!__DIRECTORY_H */ diff --git a/tools/tesh2/include/error.h b/tools/tesh2/include/error.h new file mode 100644 index 0000000000..6b0eb84f28 --- /dev/null +++ b/tools/tesh2/include/error.h @@ -0,0 +1,51 @@ +#ifndef __ERROR_H +#define __ERROR_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define EREAD ((int)2000) /* a read pipe operation failed */ +#define EREADPIPE ((int)2001) /* the pipe used to read from the stdout of the command is broken */ +#define ETIMEOUT ((int)2002) /* the command is timeouted */ +#define EWRITE ((int)2003) /* a write operation failed */ +#define EWRITEPIPE ((int)2004) /* the pipe used to write to the stdin of the command is broken */ +#define EEXEC ((int)2005) /* can't execute the command */ +#define EWAIT ((int)2006) /* the wait function failed */ +#define ECMDNOTFOUND ((int)2007) /* the command is not found */ +#define EEXITCODENOTMATCH ((int)2008) /* the exit codes don't match */ +#define EOUTPUTNOTMATCH ((int)2009) /* the outputs don't match */ +#define ESIGNOTMATCH ((int)2010) /* the signals don't match */ +#define EUNEXPECTEDSIG ((int)2011) /* unexpected signal caught */ +#define ESIGNOTRECEIPT ((int)2012) /* the expected signal is not receipt */ +#define EFILENOTFOUND ((int)2013) /* the specified tesh file is not found */ +#define EGETCWD ((int)2014) /* this is a system error : the getcwd() function failed (impossible) */ +#define EDIRNOTFOUND ((int)2015) /* the specified directory is not found */ +#define ECHDIR ((int)2016) /* this is a system error : the chdir() function failed (impossible) */ +#define EPROCESSCMDLINE ((int)2017) /* this is an internal error : the process_command_line() function failed */ +#define EARGNOTSPEC ((int)2018) /* a none optional argument is not specified in the command line */ +#define ENOTPOSITIVENUM ((int)2019) /* the argument of the option is not strictly positive */ +#define ESYNTAX ((int)2020) /* syntax error */ +#define EINVALIDTIMEOUT ((int)2021) /* the timeout value specified by the metacommand is invalid */ +#define EINVALIDEXITCODE ((int)2022) /* the expected exit code value specified by the metacommand is invalid */ +#define ESIGNOTSUPP ((int)2023) /* the signal specified by the metacommand is not supported */ +#define ELEADTIME ((int)2024) /* global timeout */ +#define EREADMENOTFOUND ((int)2025) /* unable to locate the README.txt file */ +#define EINCLUDENOTFOUND ((int)2026) /* the include file specified by a metacommand is not found */ +#define ESUFFIXTOOLONG ((int)2027) /* the suffix is too long */ +#define EFILENOTINSPECDIR ((int)2028) /* file not found in the specified directories */ +#define EFILENOTINCURDIR ((int)2029) /* file not found in the current directory */ + +const char* +error_to_string(int error); + +#ifdef __cplusplus +} +#endif + + +#endif /* !__ERROR_H */ + diff --git a/tools/tesh2/include/excludes.h b/tools/tesh2/include/excludes.h new file mode 100644 index 0000000000..7ba81263d1 --- /dev/null +++ b/tools/tesh2/include/excludes.h @@ -0,0 +1,32 @@ +#ifndef __EXCLUDES_H +#define __EXCLUDES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +excludes_t +excludes_new(void); + +int +excludes_add(excludes_t excludes, fstream_t fstream); + +int +excludes_contains(excludes_t excludes, fstream_t fstream); + +int +excludes_is_empty(excludes_t excludes); + +int +excludes_check(excludes_t excludes, fstreams_t fstreams); + +int +excludes_free(void** excludesptr); + +#ifdef __cplusplus +} +#endif + +#endif /*!__EXCLUDES_H */ diff --git a/tools/tesh2/include/fstream.h b/tools/tesh2/include/fstream.h new file mode 100644 index 0000000000..689d1cca62 --- /dev/null +++ b/tools/tesh2/include/fstream.h @@ -0,0 +1,30 @@ +#ifndef __FSTREAM_H +#define __FSTREAM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +fstream_t +fstream_new(const char* directory, const char* name); + +int +fstream_open(fstream_t fstream); + +int +fstream_close(fstream_t fstream); + +int +fstream_free(void** fstreamptr); + +void +fstream_parse(fstream_t fstream, unit_t unit); + +#ifdef __cplusplus +} +#endif + + +#endif /*! __FSTREAM_H */ diff --git a/tools/tesh2/include/fstreams.h b/tools/tesh2/include/fstreams.h new file mode 100644 index 0000000000..7e55aa045b --- /dev/null +++ b/tools/tesh2/include/fstreams.h @@ -0,0 +1,44 @@ +#ifndef __FSTREAMS_H +#define __FSTREAMS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +fstreams_t +fstreams_new(int capacity, fn_finalize_t fn_finalize); + +int +fstreams_exclude(fstreams_t fstreams, excludes_t excludes); + +int +fstreams_contains(fstreams_t fstreams, fstream_t fstream); + +int +fstreams_add(fstreams_t fstreams, fstream_t fstream); + +int +fstreams_free(void** fstreamsptr); + +int +fstreams_get_size(fstreams_t fstreams); + +int +fstreams_is_empty(fstreams_t fstreams); + +int +fstreams_contains(fstreams_t fstreams, fstream_t fstream); + +int +fstreams_load(fstreams_t fstreams); + + + +#ifdef __cplusplus +} +#endif + + +#endif /* !__FSTREAMS_H */ diff --git a/tools/tesh2/include/global.h b/tools/tesh2/include/global.h new file mode 100644 index 0000000000..aca4c0bdb0 --- /dev/null +++ b/tools/tesh2/include/global.h @@ -0,0 +1,79 @@ +#ifndef __GLOBAL_H +#define __GLOBAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * if 1, keep going when some commands can't be founded + */ +extern int +want_keep_going; + +/* if 1, ignore failures from commands + * the possibles failures are : + * + * - exit code != expected exit code + * - signal != expected signal + * - output != expected output + * - read pipe broken + * - write pipe broken + * - timeout + * + * remark : + * + * a command not found is not a failure, it's an error + * to keep going when a command is not found specify the + * option --keep-going + */ +extern int +want_keep_going_unit; + +/* + * the semaphore used to synchronize the jobs + */ +extern xbt_os_sem_t +jobs_sem; + +/* + * the semaphore used by the runner to wait the end of all the units + */ +extern xbt_os_sem_t +units_sem; + +/* the dlist of tesh include directories */ +extern vector_t +includes; + +extern int +interrupted; + +extern int +exit_code; + +extern int +want_silent; + +extern int +want_dry_run; + +extern int +want_just_display; + +extern int +dont_want_display_directory; + +extern directory_t +root_directory; + + +#ifdef __cplusplus +} +#endif + +#endif /* !__GLOBAL_H */ + diff --git a/tools/tesh2/include/htable.h b/tools/tesh2/include/htable.h new file mode 100644 index 0000000000..0814d4429b --- /dev/null +++ b/tools/tesh2/include/htable.h @@ -0,0 +1,97 @@ +#ifndef __HTABLE +#define __HTABLE + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FN_FINALIZE_T_DEFINED +typedef int (*fn_finalize_t)(void**); +#define __FN_FINALIZE_T_DEFINED +#endif + + +/* + * pointer to a compare key function. + * the function takes two items to compare and returns true if they are equal. + */ +#ifndef __FN_CMP_KEY_T_DEFINED +typedef int (*fn_cmp_key_t)(const void*, const void*); +#define __FN_CMP_KEY_T_DEFINED +#endif + +/* + * pointer to a hash function. + * the function take the value to compute the hash value that it returns. + */ +#ifndef __FN_HFUNC_T_DEFINED +typedef unsigned int (*fn_hfunc_t)(const void*); +#define __FN_HFUNC_T_DEFINED +#endif + +typedef struct s_hassoc +{ + struct s_hassoc* next; + const void* key; + const void* val; +}s_hassoc_t,* hassoc_t; + +typedef struct s_htable +{ + hassoc_t* content; /* the hache table content */ + int size; /* the size of the hash table */ + allocator_t allocator; /* the allocator used to allocate the associations */ + fn_hfunc_t fn_hfunc; /* a pointer to the hash function to use */ + fn_cmp_key_t fn_cmp_key; /* a pointer to the function used to fn_cmp_key the key */ + fn_finalize_t fn_finalize; +}s_htable_t,* htable_t; + + + + + +htable_t +htable_new( + int block_capacity, /* the block capacity of the blocks of the allocator used by the htable */ + int size, /* the size of the table size */ + fn_hfunc_t fn_hfunc, /* the pointer to the function to use */ + fn_cmp_key_t fn_cmp_key, /* the pointer to the function used to fn_cmp_key the keys of the assocs */ + fn_finalize_t fn_finalize /* the pointer to the function used to destroy the values of the assocs */ + ); + +int +htable_set(htable_t htable, const void* key, const void* val); + +void* +htable_lookup(htable_t htable, const void* key); + +void* +htable_remove(htable_t htable, const void* key); + +int +htable_erase(htable_t htable, const void* key); + +int +htable_free(htable_t* htableptr); + +int +htable_clear(htable_t htable); + +int +htable_get_size(htable_t htable); + +int +htable_is_empty(htable_t htable); + +int +htable_is_autodelete(htable_t htable); + +#ifdef __cplusplus +} +#endif + +#endif /* !__HTABLE */ + diff --git a/tools/tesh2/include/lstrings.h b/tools/tesh2/include/lstrings.h new file mode 100644 index 0000000000..948bf2bb55 --- /dev/null +++ b/tools/tesh2/include/lstrings.h @@ -0,0 +1,130 @@ +#ifndef __lstrings_H +#define __lstrings_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef __LINK_T_DEFINED +typedef struct s_link_t +{ + void* item; /* the item associated with the link */ + struct s_link_t* next; /* address to the next link */ + struct s_link_t* prev; /* address to the prev link */ +}s_link_t,* link_t; +#define __LINK_T_DEFINED +#endif + +typedef struct s_lstrings +{ + const char* item; /* not used */ + link_t next; /* point to the last node of the lstrings */ + link_t prev; /* point to the first node of the lstrings */ + int size; /* the number of node contained by the lstrings */ + link_t cur; + int pos; +}s_lstrings_t,* lstrings_t; + +lstrings_t +lstrings_new(void); + +int +lstrings_rewind(lstrings_t lstrings); + +int +lstrings_unwind(lstrings_t lstrings); + +int +lstrings_clear(lstrings_t lstrings); + +int +lstrings_free(lstrings_t* lstrings_ptr); + +int +lstrings_push_front(lstrings_t lstrings, const char* string); + +int +lstrings_push_back(lstrings_t lstrings, const char* string); + +const char* +lstrings_pop_back(lstrings_t lstrings); + +const char* +lstrings_pop_front(lstrings_t lstrings); + +int +lstrings_remove(lstrings_t lstrings, const char* string); + +int +lstrings_get_size(lstrings_t lstrings); +int +lstrings_contains(lstrings_t lstrings, const char* string); + +int +lstrings_is_empty(lstrings_t lstrings); + +int +lstrings_move_next(lstrings_t lstrings); + +const char* +lstrings_get(lstrings_t lstrings); + +const char* +lstrings_set(lstrings_t lstrings, const char* string); + +const char* +lstrings_get_at(lstrings_t lstrings, int pos); + +const char* +lstrings_set_at(lstrings_t lstrings, int pos, const char* string); + +int +lstrings_move_prev(lstrings_t lstrings); + +int +lstrings_seek(lstrings_t lstrings, int offset, int whence); + +int +lstrings_tell(lstrings_t lstrings); + +int +lstrings_getpos(lstrings_t lstrings, int* pos); + +int +lstrings_setpos(lstrings_t lstrings, int pos); + +const char* +lstrings_get_front(lstrings_t lstrings); + +const char* +lstrings_get_back(lstrings_t lstrings); + +int +lstrings_insert_after(lstrings_t lstrings, const char* what, const char* where); + +int +lstrings_insert_before(lstrings_t lstrings, const char* what, const char* where); + +char** +lstrings_to_cstr(lstrings_t lstrings); + + +#ifdef __cplusplus +} +#endif + + +#endif /* !__lstrings_H */ diff --git a/tools/tesh2/include/reader.h b/tools/tesh2/include/reader.h new file mode 100644 index 0000000000..fa47ca489b --- /dev/null +++ b/tools/tesh2/include/reader.h @@ -0,0 +1,28 @@ +#ifndef __READER_H +#define __READER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +reader_t +reader_new(command_t command); + +void +reader_free(reader_t* reader); + +void +reader_read(reader_t reader); + +void +reader_wait(reader_t reader); + +#ifdef __cplusplus +} +#endif + +#endif /* !__READER_H */ diff --git a/tools/tesh2/include/runner.h b/tools/tesh2/include/runner.h new file mode 100644 index 0000000000..4c6e0976a5 --- /dev/null +++ b/tools/tesh2/include/runner.h @@ -0,0 +1,31 @@ +#ifndef __RUNNER_H +#define __RUNNER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int +runner_init(int want_check_syntax, int timeout, fstreams_t fstreams); + +void +runner_destroy(void); + +void +runner_run(void); + +void +runner_display_status(void); + +void +runner_interrupt(void); + + +#ifdef __cplusplus +} +#endif + + +#endif /* !__RUNNER_H */ diff --git a/tools/tesh2/include/suite.h b/tools/tesh2/include/suite.h new file mode 100644 index 0000000000..5f175e9b6e --- /dev/null +++ b/tools/tesh2/include/suite.h @@ -0,0 +1,25 @@ +#ifndef __SUITE_H +#define __SUITE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +suite_t +suite_new(unit_t owner, const char* description); + +void +suite_include_unit(suite_t suite, unit_t unit); + +void +suite_free(suite_t* suite); + +#ifdef __cplusplus +extern } +#endif + + +#endif /* !__SUITE_H */ + diff --git a/tools/tesh2/include/timer.h b/tools/tesh2/include/timer.h new file mode 100644 index 0000000000..fc1c710bc8 --- /dev/null +++ b/tools/tesh2/include/timer.h @@ -0,0 +1,27 @@ +#ifndef __TIMER_H +#define __TIMER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +ttimer_t +timer_new(command_t command); + +void +timer_free(ttimer_t* timer); + +void +timer_time(ttimer_t timer); + +void +timer_wait(ttimer_t timer); + +#ifdef __cplusplus +} +#endif + + +#endif /* !__TIMER_H */ diff --git a/tools/tesh2/include/types.h b/tools/tesh2/include/types.h new file mode 100644 index 0000000000..6c4cff8d0c --- /dev/null +++ b/tools/tesh2/include/types.h @@ -0,0 +1,317 @@ +#ifndef __TYPES_H +#define __TYPES_H + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef _cplusplus +extern "C" { +#endif + +/* + * byte type definition + */ +#ifndef __BYTE_T_DEFINED + typedef unsigned char byte; + #define __BYTE_T_DEFINED +#endif + +/* + * file descriptor and pid types for portability. + */ + +#ifdef WIN32 + + #ifndef __FD_T_DEFINED + typedef HANDLE fd_t; + #define __FD_T_DEFINED + #endif + + #ifndef __PID_T_DEFINED + typedef HANDLE pid_t; + #define __PID_T_DEFINED + #endif + +#else + + #ifndef __FD_T_DEFINED + typedef int fd_t; + #define __FD_T_DEFINED + #endif + + #ifndef __PID_T_DEFINED + typedef int pid_t; + #define __PID_T_DEFINED + #endif +#endif + + +/* forward declarations */ +struct s_runner; +struct s_units; +struct s_unit; +struct s_suites; +struct s_suite; +struct s_excludes; +struct s_fstreams; +struct s_fstream; +struct s_directories; +struct s_directory; +struct s_writer; +struct s_reader; +struct s_timer; +struct s_context; + + +/* + * command status + */ +typedef enum e_command_status +{ + cs_initialized = 0, /* the is initialized */ + cs_started = 1, /* the command is started */ + cs_in_progress = 2, /* the command is execited */ + cs_waiting = 3, /* the command is waiting the writer, the reader and the timer */ + cs_interrupted = 4, /* the command is interrupted */ + cs_failed = 5, /* the command is failed */ + cs_successeded = 6, /* the command is successeded */ + cs_killed = 7 /* the command is killed */ +}command_status_t; + +/* + * reason of the status of the command + */ +typedef enum e_command_status_raison +{ + csr_unknown = 0, /* unknown reason */ + csr_read_failure = 1, /* a read operation failed */ + csr_read_pipe_broken = 2, /* the pipe used to read from the stdout of the command is broken */ + csr_timeout = 3, /* timeout */ + csr_write_failure = 4, /* a write operation failed */ + csr_write_pipe_broken = 5, /* the pipe used to write to the stdin of the command is broken */ + csr_exec_failure = 6, /* can't execute the command */ + csr_wait_failure = 8, /* the wait process function failed */ + csr_interruption_request = 9, /* the command has received an interruption request */ + csr_command_not_found = 10, /* the command is not found */ + csr_exit_codes_dont_match = 11, + csr_outputs_dont_match = 12, + csr_signals_dont_match = 13, + csr_unexpected_signal_caught = 14, + csr_expected_signal_not_receipt = 15, + csr_pipe_function_failed = 16 /* the function pipe() or CreatePipe() fails */ +}cs_reason_t; + + +struct s_command; +struct s_variable; +struct s_variables; + + +/* + * declaration of the tesh timer type + */ +typedef struct s_timer +{ + xbt_os_thread_t thread; /* asynchronous timer */ + struct s_command* command; /* the timed command */ + int timeouted; /* if 1, the timer is timeouted */ + xbt_os_sem_t started; +}s_timer_t,* ttimer_t; + +/* + * declaration of the tesh reader type + */ +typedef struct s_reader +{ + xbt_os_thread_t thread; /* asynchonous reader */ + struct s_command* command; /* the command of the reader */ + int failed; /* if 1, the reader failed */ + int broken_pipe; /* if 1, the pipe used by the reader is broken */ + xbt_os_sem_t started; +}s_reader_t,* reader_t; + +/* + * declaration of the tesh writer type + */ +typedef struct s_writer +{ + xbt_os_thread_t thread; /* asynchronous writer */ + struct s_command* command; /* the command of the writer */ + int failed; /* if 1, the writer failed */ + int broken_pipe; /* if 1, the pipe used by the writer is broken */ + xbt_os_sem_t started; +}s_writer_t,* writer_t; + +typedef struct s_units +{ + vector_t items; /* used to store the units */ + int number_of_runned_units; /* the number of units runned */ + int number_of_ended_units; /* the number of units over */ + +}s_units_t,* units_t; + +/* + * declaration of the tesh unit type + */ +typedef struct s_unit +{ + struct s_fstream* fstream; + struct s_runner* runner; /* the runner of the unit */ + vector_t commands; + int number_of_commands; /* number of created commands of the unit */ + int number_of_started_commands; /* number of runned commands of the unit */ + int number_of_interrupted_commands; /* number of interrupted commands of the unit */ + int number_of_failed_commands; /* number of failed commands of the unit */ + int number_of_successeded_commands; /* number of successeded commands of the unit */ + int number_of_terminated_commands; /* number of ended commands */ + xbt_os_thread_t thread; /* all the units run in its own thread */ + xbt_os_sem_t sem; /* used by the commands of the unit to signal the end of the unit */ + xbt_os_mutex_t mutex; /* used to synchronously access to the properties of the runner */ + int interrupted; /* if 1, the unit is interrupted by the runner */ + int failed; /* if 1, the unit is failed */ + int successeded; /* if 1, the unit is successeded */ + int parsed; /* if 1, the tesh file of the unit is parsed */ + int released; + int parsing_include_file; + struct s_suite* owner; /* the suite containing the unit if any */ + vector_t suites; + int number; /* the number of suites */ + int capacity; /* the number of suites that the unit can contain */ + int running_suite; /* if 1, the suite running a suite */ +}s_unit_t,* unit_t; + + +/* + * declaration of tesh suite type + */ +typedef struct s_suite +{ + const char* description; /* the name of the suite */ + struct s_unit* owner; /* the unit owning the suite */ + vector_t units; /* the units of the suite */ + int number; /* the numnber of suites contained by the suite */ + int capacity; /* the number of suites that the vector can contain */ + int number_of_successed_units; /* the number of successeded units of the suite */ + int number_of_failed_units; /* the number of failed units of the suite */ +}s_suite_t,* suite_t; + +/* + * declaration of the tesh runner type + */ +typedef struct s_runner +{ + + /*vector_t units;*/ /* the vector containing all the units launched by the runner */ + struct s_units* units; + int timeouted; /* if 1, the runner is timeouted */ + int timeout; /* the timeout of the runner */ + int interrupted; /* if 1, the runner failed */ + int number_of_runned_units; /* the number of units runned by the runner */ + int number_of_ended_units; /* the number of ended units */ + int waiting; /* if 1, the runner is waiting the end of all the units */ + xbt_os_thread_t thread; /* the timer thread */ +}s_runner_t,* runner_t; + + +typedef struct s_fstreams +{ + vector_t items; +}s_fstreams_t,* fstreams_t; + + +typedef struct s_fstream +{ + char* name; + char* directory; + FILE* stream; +}s_fstream_t,* fstream_t; + +typedef struct s_excludes +{ + vector_t items; +}s_excludes_t,* excludes_t; + +typedef struct s_directory +{ + char* name; + DIR* stream; + int load; +}s_directory_t,* directory_t; + +typedef struct s_directories +{ + vector_t items; +}s_directories_t,* directories_t; + + +typedef enum +{ + oh_check, + oh_display, + oh_ignore +}output_handling_t; +/* + * declaration of the tesh context type + */ +typedef struct s_context +{ + const char* command_line; /* the command line of the command to execute */ + const char* line; /* the current parsed line */ + int exit_code; /* the expected exit code of the command */ + char* signal; /* the expected signal raised by the command */ + int timeout; /* the timeout of the test */ + xbt_strbuff_t input; /* the input to write in the stdin of the command to run */ + xbt_strbuff_t output; /* the expected output of the command of the test */ + output_handling_t output_handling; + int async; /* if 1, the command is asynchronous */ +}s_context_t,* context_t; + +/* + * declaration of the tesh command type + */ +typedef struct s_command +{ + unit_t unit; /* the unit of the command */ + struct s_context* context; /* the context of the execution of the command */ + xbt_os_thread_t thread; /* asynchronous command */ + struct s_writer* writer; /* the writer used to write in the command stdin */ + struct s_reader* reader; /* the reader used to read from the command stout */ + struct s_timer* timer; /* the timer used for the command */ + command_status_t status; /* the current status of the command */ + cs_reason_t reason; /* the reason of the state of the command */ + int successeded; /* if 1, the command is successeded */ + int interrupted; /* if 1, the command is interrupted */ + int failed; /* if 1, the command is failed */ + pid_t pid; /* the program id of the command */ + xbt_strbuff_t output; /* the output of the command */ + fd_t stdout_fd; /* the stdout fd of the command */ + fd_t stdin_fd; /* the stdin fd of the command */ + int exit_code; /* the exit code of the command */ + #ifdef WIN32 + unsigned long stat_val; + #else + int stat_val; + #endif + char* signal; /* the signal raised by the command if any */ + xbt_os_mutex_t mutex; + #ifndef WIN32 + int killed; /* if 1, the command was killed */ + #endif +}s_command_t,* command_t; + +#ifdef _cplusplus +} +#endif + + +#endif /* !__TYPES_H */ diff --git a/tools/tesh2/include/unit.h b/tools/tesh2/include/unit.h new file mode 100644 index 0000000000..3eb17f3a68 --- /dev/null +++ b/tools/tesh2/include/unit.h @@ -0,0 +1,52 @@ +#ifndef __UNIT_H +#define __UNIT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +unit_t +unit_new(runner_t runner, suite_t owner, fstream_t fstream); + +int +unit_free(void** unitptr); + + +int +unit_reset(unit_t unit); + +void +unit_run(unit_t unit, xbt_os_mutex_t mutex); + +void +unit_interrupt(unit_t unit); + +void +unit_verbose(unit_t unit); + +void +unit_handle_failure(unit_t unit); + +void +unit_handle_line(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char * filepos, char *line); + +void +unit_pushline(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* filepos, char kind, char *line); + +void +unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name); + +void +unit_parse(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, FILE* stream); + +void +unit_handle_suite(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* description); + +#ifdef __cplusplus +} +#endif + + +#endif /* !__UNIT_H */ diff --git a/tools/tesh2/include/units.h b/tools/tesh2/include/units.h new file mode 100644 index 0000000000..0791f710da --- /dev/null +++ b/tools/tesh2/include/units.h @@ -0,0 +1,46 @@ +#ifndef __UNITS_H +#define __UNITS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +units_t +units_new(runner_t runner, fstreams_t fstreams); + +int +units_is_empty(units_t unit); + +int +units_get_size(units_t unit); + +int +units_run_all(units_t units, xbt_os_mutex_t mutex); + +int +units_join_all(units_t units); + +int +units_interrupt_all(units_t units); + +int +units_reset_all(units_t units); + + +int +units_verbose(units_t units); + +int +units_free(void** unitsptr); + + +#ifdef __cplusplus +extern } +#endif + + +#endif /* !__UNITS_H */ + + diff --git a/tools/tesh2/include/variable.h b/tools/tesh2/include/variable.h new file mode 100644 index 0000000000..bd3e5ac5f4 --- /dev/null +++ b/tools/tesh2/include/variable.h @@ -0,0 +1,17 @@ +#ifndef __VARIABLE_H +#define __VARIABLE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + + +#endif /*!__VARIABLE_H */ \ No newline at end of file diff --git a/tools/tesh2/include/variables.h b/tools/tesh2/include/variables.h new file mode 100644 index 0000000000..1b522ec455 --- /dev/null +++ b/tools/tesh2/include/variables.h @@ -0,0 +1,18 @@ +#ifndef __VARIABLES_H +#define __VARIABLES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + + +#endif /*!__VARIABLES_H */ \ No newline at end of file diff --git a/tools/tesh2/include/vector.h b/tools/tesh2/include/vector.h new file mode 100644 index 0000000000..59468edea0 --- /dev/null +++ b/tools/tesh2/include/vector.h @@ -0,0 +1,175 @@ +/** + * File : private/vector.h + * + * Copyright 2006,2007 Malek Cherier, Martin Quinson. All right reserved. + * + * This program is free software; you can redistribute it and/or modify it under the terms + * of the license (GNU LGPL) which comes with this package. + */ + +#ifndef __VECTOR_H +#define __VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef __FN_FINALIZE_T_DEFINED +typedef int (*fn_finalize_t)(void**); +#define __FN_FINALIZE_T_DEFINED +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +/* + * this type represents a vector of void* pointers. + */ +typedef struct s_vector +{ + int size; /* the number of items of the vector */ + int capacity; /* the capacity of the vector */ + void** items; /* the items of the vector */ + fn_finalize_t fn_finalize; /* a pointer to a function used to cleanup the elements of the vector */ + int pos; +}s_vector_t,* vector_t; + +vector_t +vector_new(int capacity,fn_finalize_t fn_finalize); + +int +vector_clear(vector_t vector); + +int +vector_free(vector_t* vector_ptr); + +int +vector_is_empty(vector_t vector); + +void* +vector_get_at(vector_t vector, int pos); + +int +vector_get_size(vector_t vector); + +void* +vector_get_front(vector_t vector); + +void* +vector_get_back(vector_t vector); + +int +vector_get_capacity_available(vector_t vector); + +int +vector_push_back(vector_t vector, void* item); + +void* +vector_pop_back(vector_t vector); + +int +vector_get_upper_bound(vector_t vector); + +void* +vector_set_at(vector_t vector, int index, void* item); + +int +vector_insert(vector_t vector, int index, void* item); + +int +vector_erase_at(vector_t vector, int index); + +int +vector_erase(vector_t vector, void* item); + +int +vector_erase_range(vector_t vector, int first, int last); + +int +vector_remove(vector_t vector, void* item); + +int +vector_search(vector_t vector, void* item); + +int +vector_assign(vector_t dst,vector_t src); + +int +vector_get_capacity(vector_t vector); + +int +vector_equals(vector_t vector, vector_t other); + +int +vector_swap(vector_t vector, vector_t other); + +vector_t +vector_clone(vector_t vector); + +int +vector_contains(vector_t vector,void* item); + +int +vector_reserve(vector_t vector,int size); + +int +vector_is_autodelete(vector_t vector); + +int +vector_has_capacity_available(vector_t vector); + +int +vector_is_full(vector_t vector); + +int +vector_get_max_index(vector_t vector); + +void* +vector_get(vector_t vector); + +void* +vector_get_at(vector_t vector, int pos); + +int +vector_getpos(vector_t vector, int* pos); + +int +vector_move_next(vector_t vector); + +int +vector_move_prev(vector_t vector); + +int +vector_rewind(vector_t vector); + +int +vector_seek(vector_t vector, int offset, int whence); + +void* +vector_set(vector_t vector, void* item); + +int +vector_setpos(vector_t vector, int pos); + +int +vector_tell(vector_t vector); + +int +vector_unwind(vector_t vector); + + +#ifdef __cplusplus +} +#endif + +#endif /* !XBT_PRIVATE_VECTOR_PTR_H */ diff --git a/tools/tesh2/include/writer.h b/tools/tesh2/include/writer.h new file mode 100644 index 0000000000..7a51250d3c --- /dev/null +++ b/tools/tesh2/include/writer.h @@ -0,0 +1,27 @@ +#ifndef __WRITER_H +#define __WRITER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +writer_t +writer_new(command_t command); + +void +writer_free(writer_t* writer); + +void +writer_write(writer_t writer); + +void +writer_wait(writer_t writer); + +#ifdef __cplusplus +} +#endif + +#endif /* !__WRITER_H */ diff --git a/tools/tesh2/makefile b/tools/tesh2/makefile new file mode 100644 index 0000000000..dc120adec7 --- /dev/null +++ b/tools/tesh2/makefile @@ -0,0 +1,22 @@ +PREFIX = /home/mcherier/com/loria/tesh +INCLUDE_DIR = $(PREFIX)/include/ +SRC_DIR = $(PREFIX)/src/ +OBJECTS = main.o command.o context.o reader.o runner.o signal.o timer.o suite.o unit.o writer.o list.o lstrings.o allocator.o vector.o htable.o dictionary.o units.o directory.o directories.o fstream.o fstreams.o excludes.o error.o +CC = gcc +CCFLAGS = -ggdb -g -I$(INCLUDE_DIR) -I/home/mcherier/svn/simgrid/include/ -I/home/mcherier/svn/simgrid/src/ -I/home/mcherier/svn/simgrid/src/include/ -Wall -pedantic +LDLIBS = simgrid + +PROG = tesh + +all: $(PROG) + +$(PROG): $(OBJECTS) + $(CC) -o $(PROG) $(OBJECTS) -L/home/mquinson/simgrid-svn/src/.libs/ -l$(LDLIBS) + +%.o: $(SRC_DIR)%.c $(INCLUDE_DIR)*.h + $(CC) -c $(CCFLAGS) $(SRC_DIR)$*.c + +clean: + rm -f $(OBJECTS) $(PROG) + + \ No newline at end of file diff --git a/tools/tesh2/src/allocator.c b/tools/tesh2/src/allocator.c new file mode 100644 index 0000000000..7121ba6538 --- /dev/null +++ b/tools/tesh2/src/allocator.c @@ -0,0 +1,368 @@ + +#include + +#include +#include /* memset */ +#include /* calloc() free() */ + + +static int +resize(allocator_t allocator) +{ + allocator_node_t cur, next; + allocator_block_t block; + int block_capacity, type_size, node_size; + register int pos; + + if(!allocator) + return EINVAL; + + next = NULL; + block_capacity = allocator->block_capacity; + type_size = allocator->type_size; + node_size = type_size + sizeof(s_allocator_node_t); + + if(!(block = (allocator_block_t)calloc(1,sizeof(s_allocator_block_t) + (block_capacity * node_size)))) + return errno; + + /* update the first block of the allocator */ + block->next = allocator->head; + block->allocator = allocator; + allocator->head = block; + + /* move to the last node of the block. */ + cur = (allocator_node_t)(((byte*)(block + 1)) + ((block_capacity - 1) * node_size)); + + /* initialize all the nodes of the new block */ + for(pos = block_capacity - 1; pos >= 0; pos--, cur = (allocator_node_t)(((byte*)next) - node_size)) + { + cur->next = next; + cur->block = block; + next = cur; + } + + /* allocator->free pointed now on the first node of the new bloc. */ + allocator->free = allocator->first = next; + /* update the allocator capacity. */ + allocator->capacity += block_capacity; + + return 0; +} + +allocator_t +allocator_new(int block_capacity, int type_size, fn_finalize_t fn_finalize) +{ + allocator_t allocator; + + if((block_capacity <= 0) || (type_size <= 0)) + { + errno = EINVAL; + return NULL; + } + + if(!(allocator = (allocator_t)calloc(1,sizeof(s_allocator_t)))) + return NULL; + + /* updates allocator properties */ + allocator->block_capacity = block_capacity; + allocator->type_size = type_size; + + /* first block allocation */ + + if((errno = resize(allocator))) + { + free(allocator); + return NULL; + } + + allocator->fn_finalize = fn_finalize; + return allocator; +} + +int +allocator_free(allocator_t* allocator_ptr) +{ + allocator_block_t cur, next; + allocator_node_t node; + allocator_t allocator; + int pos, node_size; + fn_finalize_t fn_finalize; + void* type; + + if(!(*allocator_ptr)) + return EINVAL; + + + allocator = *allocator_ptr; + cur = allocator->head; + + if(allocator->fn_finalize) + { + fn_finalize = allocator->fn_finalize; + node_size = allocator->type_size + sizeof(s_allocator_node_t); + + while(cur) + { + + /* type points to the first node */ + node = (allocator_node_t)(((byte*)cur) + sizeof(s_allocator_block_t)); + + if(node->is_allocated) + { + type = (void*)(((byte*)node) + sizeof(s_allocator_node_t)); + + /* apply the fn_finalize function to the first type */ + + if((errno = (*fn_finalize)(&type))) + return errno; + } + + /*clear all the other types */ + for(pos = 1; pos < allocator->block_capacity; pos++) + { + node = (allocator_node_t)(((byte*)node) + node_size); + + if(node->is_allocated) + { + type = (void*)(((byte*)node) + sizeof(s_allocator_node_t)); + + /* apply the fn_finalize function to the first type */ + + if((errno = (*fn_finalize)(&type))) + return errno; + } + } + + next = cur->next; + free(cur); + cur = next; + } + } + else + { + while(cur) + { + next = cur->next; + free(cur); + cur = next; + } + } + + free(*allocator_ptr); + *allocator_ptr = NULL; + + return 0; +} + +void* +allocator_alloc(allocator_t allocator) +{ + allocator_node_t node; + + if(!allocator) + { + errno = EINVAL; + return NULL; + } + + /* all allocator memory is used, allocate a new block */ + if(!(allocator->free)) + if(resize(allocator)) + return NULL; + + node = allocator->free; + node->is_allocated = 1; + + allocator->free = allocator->free->next; + allocator->size++; + + return (void*)(((byte*)node) + sizeof(s_allocator_node_t)); +} + +int +allocator_dealloc(allocator_t allocator, void* block) +{ + allocator_node_t node; + + if(!allocator || !block) + return EINVAL; + + if(allocator->fn_finalize) + { + if((errno = (*(allocator->fn_finalize))(&block))) + return errno; + + memset(block, 0, allocator->type_size); + node->is_allocated = 0; + } + + /* get the node address. */ + node = (allocator_node_t)(((byte*)block) - sizeof(s_allocator_node_t)); + + /* the node becomes the free node and the free node become the next free node.*/ + node->next = allocator->free; + allocator->free = node; + allocator->size--; + + return 0; +} + +int +allocator_get_size(allocator_t allocator) +{ + if(!allocator) + { + errno = EINVAL; + return -1; + } + + return allocator->size; +} + +int +allocator_get_capacity(allocator_t allocator) +{ + if(!allocator) + { + errno = EINVAL; + return -1; + } + + return allocator->capacity; +} + +int +allocator_get_type_size(allocator_t allocator) +{ + + if(NULL == allocator) + { + errno = EINVAL; + return -1; + } + + return allocator->type_size; +} + +int +allocator_is_empty(allocator_t allocator) +{ + if(NULL == allocator) + { + errno = EINVAL; + return 0; + } + + return !(allocator->size); +} + + +int +allocator_is_full(allocator_t allocator) +{ + if(NULL == allocator) + { + errno = EINVAL; + return 0; + } + + return (allocator->size == allocator->capacity); +} + +int +allocator_get_block_capacity(allocator_t allocator) +{ + + if(!allocator) + { + errno = EINVAL; + return -1; + } + + return allocator->block_capacity; +} + +int +allocator_get_capacity_available(allocator_t allocator) +{ + if(!allocator) + { + errno = EINVAL; + return -1; + } + + return (allocator->capacity - allocator->size); +} + +int +allocator_clear(allocator_t allocator) +{ + allocator_block_t cur; + allocator_node_t node; + + int block_capacity, node_size, type_size; + fn_finalize_t fn_finalize; + void* type; + register int pos; + + + if(!allocator) + return EINVAL; + + + if(allocator->fn_finalize) + { + fn_finalize = allocator->fn_finalize; + block_capacity = allocator->block_capacity; + type_size = allocator->type_size; + node_size = type_size + sizeof(s_allocator_node_t); + + cur = allocator->head; + + while(cur) + { + /* type points to the first node */ + node = (allocator_node_t)(((byte*)cur) + sizeof(s_allocator_block_t)); + + if(node->is_allocated) + { + + type = (void*)(((byte*)node) + sizeof(s_allocator_node_t)); + + /* apply the fn_finalize function to the first type */ + + if((errno = (*fn_finalize)(&type))) + return errno; + + memset(type, 0, type_size); + node->is_allocated = 0; + } + + /*clear all the other types */ + for(pos = 1; pos < block_capacity; pos++) + { + node = (allocator_node_t)(((byte*)node) + node_size); + + if(node->is_allocated) + { + type = (void*)(((byte*)node) + sizeof(s_allocator_node_t)); + + /* apply the fn_finalize function to the first type */ + + if((errno = (*fn_finalize)(&type))) + return errno; + + memset(type, 0, type_size); + node->is_allocated = 0; + } + } + } + + cur = cur->next; + } + + allocator->free = allocator->first; + allocator->size = 0; + + return 0; +} diff --git a/tools/tesh2/src/command.c b/tools/tesh2/src/command.c new file mode 100644 index 0000000000..15cc6eb7ff --- /dev/null +++ b/tools/tesh2/src/command.c @@ -0,0 +1,890 @@ + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +#include "../include/_signal.h" + + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +static void* +command_start(void* p); + + +command_t +command_new(unit_t unit, context_t context, xbt_os_mutex_t mutex) +{ + command_t command = xbt_new0(s_command_t, 1); + + /* get the context of the execution of the command */ + command->context = context_dup(context); + + /* the exit code of the command is indefinite */ + command->exit_code = INDEFINITE; + + /* the signal of the command is indefinite */ + command->signal = INDEFINITE_SIGNAL; + + command->failed = 0; + command->interrupted = 0; + + /* the mutex used to safetly access to the command unit properties */ + command->mutex = mutex; + + if(context->output->used) + /* instantiate the buffer filled with the content of the command stdout */ + command->output = xbt_strbuff_new(); + else + command->output = NULL; + + command->pid = INDEFINITE_PID; + + command->stat_val = -1; + + /* set the unit of the command */ + command->unit = unit; + + /* all the commands are runned in a thread */ + command->thread = NULL; + + command->successeded = 0; + + if(context->output->used) + command->reader = reader_new(command); + else + command->reader = NULL; + + if(context->input->used) + command->writer = writer_new(command); + else + command->writer = NULL; + + if(context->timeout != INDEFINITE) + command->timer = timer_new(command); + else + command->timer = NULL; + + command->status = cs_initialized; + command->reason = csr_unknown; + + command->stdin_fd = INDEFINITE_FD; + command->stdout_fd = INDEFINITE_FD; + + + /* register the command */ + xbt_os_mutex_acquire(mutex); + /*unit->commands[(unit->number_of_commands)++] = command;*/ + vector_push_back(unit->commands, command); + (unit->number_of_commands)++; + xbt_os_mutex_release(mutex); + + #ifndef WIN32 + command->killed = 0; + #endif + + return command; +} + +void +command_run(command_t command) +{ + if(!want_silent) + INFO1("tesh %s",command->context->command_line); + + if(!want_just_display) + { + if(!interrupted) + { + /* start the command */ + + if(command->context->async) + { + command->thread = xbt_os_thread_create("", command_start, command); + + if(!command->thread) + ERROR0("xbt_os_thread_create() failed\n"); + } + else + command_start(command); + } + else + { + command_interrupt(command); + } + } + +} + +static void* +command_start(void* p) +{ + command_t command = (command_t)p; + unit_t unit = command->unit; + + /* the command is started */ + command->status = cs_started; + + /* increment the number of started commands of the unit */ + xbt_os_mutex_acquire(command->mutex); + (command->unit->number_of_started_commands)++; + xbt_os_mutex_release(command->mutex); + + /* execute the command of the test */ + command_exec(command, command->context->command_line); + + if(cs_in_progress == command->status) + { + /*printf("the command %p is in progress\n",command);*/ + + /* on attend la fin de la commande. + * la command peut soit se terminée normalement, + * soit se terminée à la suite d'un timeout, d'une erreur de lecture des son reader ou d'une erreur d'écriture de son writer + * soit à la suit d'une demande d'interruption + */ + + command_wait(command); + + if(cs_failed != command->status && cs_interrupted != command->status) + { + /*printf("checking the command %p\n",command);*/ + command_check(command); + } + } + + + xbt_os_mutex_acquire(command->mutex); + + /* if it's the last command release its unit */ + if(!unit->interrupted && unit->parsed && (unit->number_of_started_commands == (unit->number_of_failed_commands + unit->number_of_interrupted_commands + unit->number_of_successeded_commands))) + { + /* first release the mutex */ + unit->released = 1; + xbt_os_mutex_release(command->mutex); + /* the last command release the unit */ + xbt_os_sem_release(command->unit->sem); + } + else + xbt_os_mutex_release(command->mutex); + + + /* wait the end of the timer, the reader and the writer */ + if(command->timer && command->timer->thread) + timer_wait(command->timer); + + if(command->writer && command->writer->thread) + writer_wait(command->writer); + + if(command->reader && command->reader->thread) + reader_wait(command->reader); + + return NULL; +} + +#ifdef WIN32 +void +command_exec(command_t command, const char* command_line) +{ + + STARTUPINFO si = {0}; /* contains the informations about the child process windows*/ + PROCESS_INFORMATION pi = {0}; /* contains child process informations */ + SECURITY_ATTRIBUTES sa = {0}; /* contains the security descriptor for the pipe handles */ + HANDLE child_stdin_handle[2] = {NULL}; /* child_stdin_handle[1] <-> stdout of the child process */ + HANDLE child_stdout_handle[2] = {NULL}; /* child_stdout_handle[0] <-> stdin of the child process */ + HANDLE child_stderr = NULL; + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; /* use default security for the pipe handles */ + + sa.bInheritHandle = TRUE; /* the pipe handles can be inherited */ + + if(!CreatePipe(&(child_stdin_handle[0]),&(child_stdin_handle[1]),&sa,0)) + { + ERROR1("CreatePipe1() failed (%lu)",GetLastError()); + command->failed = 1; + command->status = cs_failed; + + return; + } + + + if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[1]),GetCurrentProcess(),&(child_stderr),0,TRUE,DUPLICATE_SAME_ACCESS)) + { + ERROR1("DuplicateHandle1() failed (%lu)",GetLastError()); + + CloseHandle(child_stdin_handle[0]); + CloseHandle(child_stdin_handle[1]); + + command->failed = 1; + command->status = cs_failed; + + return; + } + + if(!CreatePipe(&(child_stdout_handle[0]),&(child_stdout_handle[1]),&sa,0)) + { + ERROR1("CreatePipe2() failed (%lu)",GetLastError()); + + CloseHandle(child_stdout_handle[0]); + CloseHandle(child_stdout_handle[1]); + CloseHandle(child_stdin_handle[0]); + CloseHandle(child_stdin_handle[1]); + + command->failed = 1; + command->status = cs_failed; + + return; + } + + /* Read handle for read operations on the child std output. */ + if(!DuplicateHandle(GetCurrentProcess(),(child_stdin_handle[0]),GetCurrentProcess(),&(command->stdout_fd),0,FALSE, DUPLICATE_SAME_ACCESS)) + { + CloseHandle(child_stdout_handle[0]); + CloseHandle(child_stdout_handle[1]); + CloseHandle(child_stdin_handle[0]); + CloseHandle(child_stdin_handle[1]); + + command->failed = 1; + command->status = cs_failed; + + ERROR1("DuplicateHandle2() failed (%lu)",GetLastError()); + } + + + /* Write handle for write operations on the child std input. */ + if(!DuplicateHandle(GetCurrentProcess(),(child_stdout_handle[1]),GetCurrentProcess(),&(command->stdin_fd), 0,FALSE,DUPLICATE_SAME_ACCESS)) + { + CloseHandle(child_stdout_handle[0]); + CloseHandle(child_stdout_handle[1]); + CloseHandle(child_stdin_handle[0]); + CloseHandle(child_stdin_handle[1]); + + command->failed = 1; + command->status = cs_failed; + + ERROR1("DuplicateHandle3() failed (%lu)",GetLastError()); + } + + + CloseHandle(child_stdin_handle[0]); + CloseHandle(child_stdout_handle[1]); + + if(command->timer) + { + /* launch the timer */ + timer_time(command->timer); + } + + if(command->reader) + { + /* launch the reader */ + reader_read(command->reader); + } + + + if(command->writer) + { + /* launch the writer */ + writer_write(command->writer); + } + + si.cb = sizeof(STARTUPINFO); + + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdOutput = child_stdin_handle[1]; + si.hStdInput = child_stdout_handle[0]; + si.hStdError = child_stderr; + + /* launch the process */ + if(!CreateProcess( + NULL, + (char*)command_line, + NULL, + NULL, + TRUE, + CREATE_NO_WINDOW, + NULL, + NULL, + &si, + &pi) + ) + { + + if(ERROR_FILE_NOT_FOUND == GetLastError()) + { + exit_code = ECMDNOTFOUND; + command_handle_failure(command,csr_command_not_found); + } + else + { + exit_code = EEXEC; + command_handle_failure(command,csr_exec_failure); + } + } + else + { + /* the command is running */ + command->status = cs_in_progress; + + /* save the pid of the command */ + command->pid = pi.hProcess; + + /* close non used thread handle */ + CloseHandle(pi.hThread); + + } + + + /* close non used handles */ + CloseHandle(child_stdin_handle[1]); + CloseHandle(child_stdout_handle[0]); + CloseHandle(child_stderr); + + +} +#else +void +command_exec(command_t command, const char* command_line) +{ + int child_stdin_fd[2] ; + int child_stdout_fd[2]; + + if(pipe(child_stdin_fd) || pipe(child_stdout_fd)) + { + ERROR1("pipe() failed (%d)",errno); + command_handle_failure(command, csr_pipe_function_failed); + + return; + } + + command->pid= fork(); + + if(command->pid < 0) + { + close(child_stdin_fd[0]); + close(child_stdin_fd[1]); + close(child_stdout_fd[0]); + close(child_stdout_fd[1]); + + exit_code = EEXEC; + ERROR1("fork() failed (%d)",errno); + command_handle_failure(command,csr_exec_failure); + } + else + { + if(command->pid) + {/* father */ + close(child_stdin_fd[0]); + close(child_stdout_fd[1]); + + command->stdin_fd = child_stdin_fd[1]; + command->stdout_fd = child_stdout_fd[0]; + + if(command->reader) + { + /* launch the reader */ + reader_read(command->reader); + } + + if(command->writer) + { + /* launch the writer */ + writer_write(command->writer); + } + + if(command->timer) + { + /* launch the timer */ + timer_time(command->timer); + } + + /* the command is running */ + command->status = cs_in_progress; + + } + else + {/* child */ + + close(child_stdin_fd[1]); + + dup2(child_stdin_fd[0],0); + + close(child_stdin_fd[0]); + + close(child_stdout_fd[0]); + + dup2(child_stdout_fd[1],1); + + dup2(child_stdout_fd[1],2); + + close(child_stdout_fd[1]); + + if(command->reader) + xbt_os_sem_acquire(command->reader->started); + + if(command->writer) + xbt_os_sem_acquire(command->writer->started); + + if(command->timer) + xbt_os_sem_acquire(command->timer->started); + + execlp ("/bin/sh", "sh", "-c", command->context->command_line, NULL); + } + } +} +#endif + +#ifdef WIN32 +void +command_wait(command_t command) +{ + /* wait for the command terminaison */ + DWORD rv; + + if(WAIT_FAILED == WaitForSingleObject(command->pid, INFINITE)) + { + ERROR0("WaitForSingleObject() failed"); + /* TODO : see for the interruption */ + } + else + { + /* don't take care of the timer or the writer or the reader failue */ + if(cs_failed != command->status && cs_interrupted != command->status) + { + if(!GetExitCodeProcess(command->pid,&rv)) + { + ERROR1("GetExitCodeProcess() failed for the child %s",command->context->command_line); + /* TODO : see for the interruption */ + } + else + command->stat_val = command->exit_code = rv; + } + } +} +#else +void +command_wait(command_t command) +{ + + int pid; + + /* let this thread wait for the child so that the main thread can detect the timeout without blocking on the wait */ + + pid = waitpid(command->pid, &(command->stat_val), 0); + + /*printf("The %p command ended\n",command);*/ + if(pid != command->pid) + { + ERROR1("waitpid() failed for the child %s",command->context->command_line); + exit_code = EWAIT; + command_handle_failure(command, csr_wait_failure); + } + else + { + if(WIFEXITED(command->stat_val)) + command->exit_code = WEXITSTATUS(command->stat_val); + } +} +#endif + +void +command_check(command_t command) +{ + int success = 1; + cs_reason_t reason; + + /* we have a signal, store it */ + if(WIFSIGNALED(command->stat_val)) + { + command->signal = strdup(signal_name(WTERMSIG(command->stat_val),command->context->signal)); + INFO3("the command -PID %d %s receive the signal : %s",command->pid, command->context->command_line, command->signal); + } + + /* we have a signal and not signal is expected */ + if(WIFSIGNALED(command->stat_val) && !command->context->signal) + { + success = 0; + exit_code = EUNEXPECTEDSIG; + reason = csr_unexpected_signal_caught; + } + + /* we have a signal that differ form the expected signal */ + if(success && WIFSIGNALED(command->stat_val) && command->context->signal && strcmp(signal_name(WTERMSIG(command->stat_val),command->context->signal),command->context->signal)) + { + success = 0; + exit_code = ESIGNOTMATCH; + reason = csr_signals_dont_match; + } + + /* we don't receipt the expected signal */ + if(success && !WIFSIGNALED(command->stat_val) && command->context->signal) + { + success = 0; + exit_code = ESIGNOTRECEIPT; + reason = csr_expected_signal_not_receipt; + } + + /* if the command exit normaly and we expect a exit code : test it */ + if(success && WIFEXITED(command->stat_val) /* && INDEFINITE != command->context->exit_code*/) + { + /* the exit codes don't match */ + if(WEXITSTATUS(command->stat_val) != command->context->exit_code) + { + success = 0; + exit_code = EEXITCODENOTMATCH; + reason = csr_exit_codes_dont_match; + } + } + + /* if ouput handling flag is specified check the output */ + if(success && oh_check == command->context->output_handling && command->reader) + { + /* make sure the reader done */ + while(!command->reader->broken_pipe) + xbt_os_thread_yield(); + + xbt_strbuff_chomp(command->output); + xbt_strbuff_chomp(command->context->output); + xbt_strbuff_trim(command->output); + xbt_strbuff_trim(command->context->output); + + if(command->output->used != command->context->output->used || strcmp(command->output->data, command->context->output->data)) + { + success = 0; + exit_code = EOUTPUTNOTMATCH; + reason = csr_outputs_dont_match; + } + } + + if(success) + { + xbt_os_mutex_acquire(command->mutex); + + if(command->status != cs_interrupted) + { + + /* signal the success of the command */ + command->status = cs_successeded; + command->successeded = 1; + + /* increment the number of successeded command of the unit */ + /*xbt_os_mutex_acquire(command->mutex);*/ + (command->unit->number_of_successeded_commands)++; + } + + xbt_os_mutex_release(command->mutex); + + } + else + { + command_handle_failure(command,reason); + } +} + +#ifdef WIN32 +void +command_kill(command_t command) +{ + if(INDEFINITE_PID != command->pid) + TerminateProcess(command->pid, INDEFINITE); +} +#else +void +command_kill(command_t command) +{ + if(INDEFINITE_PID != command->pid) + { + /*INFO1("Kill the command - PID %d",command->pid);*/ + + kill(command->pid,SIGTERM); + + if(!command->context->signal) + command->context->signal = strdup("SIGTERM"); + + command->exit_code = INDEFINITE; + command->killed = 1; + + usleep(100); + kill(command->pid,SIGKILL); + + + } +} +#endif + +void +command_interrupt(command_t command) +{ + xbt_os_mutex_acquire(command->mutex); + + if((command->status != cs_interrupted) && (command->status != cs_failed) && (command->status != cs_successeded)) + { + /*INFO1("Begin interrupt the command - PID %d",command->pid);*/ + + command->status = cs_interrupted; + command->reason = csr_interruption_request; + command->interrupted = 1; + xbt_os_mutex_acquire(command->unit->mutex); + (command->unit->number_of_interrupted_commands)++; + xbt_os_mutex_release(command->unit->mutex); + + if(command->pid != INDEFINITE_PID) + command_kill(command); + + + /*INFO1("End interrupt the command - PID %d",command->pid);*/ + } + + xbt_os_mutex_release(command->mutex); + + +} + +void +command_display_status(command_t command) +{ + #ifdef WIN32 + printf("\nCommand : PID - %p\n%s\n",command->pid,command->context->command_line); + #else + printf("\nCommand : PID - %d\n%s\n",command->pid,command->context->command_line); + #endif + printf("Status informations :\n"); + printf(" position in the tesh file : %s\n",command->context->line); + + /* the command successeded */ + if(cs_successeded == command->status) + { + /* status */ + printf(" status : success\n"); + } + else + { + + /* display if the command is interrupted, failed or in a unknown status */ + if(cs_interrupted == command->status) + printf(" status : interrupted\n"); + else if(cs_failed == command->status) + printf(" status : failed\n"); + else + printf(" status : unknown\n"); + + #ifndef WIN32 + if(command->killed) + printf(" \n"); + #endif + + /* display the reason of the status of the command */ + switch(command->reason) + { + /* the function pipe or CreatePipe() fails */ + case csr_pipe_function_failed : + printf(" reason : pipe() or CreatePipe() function failed (system error)\n"); + break; + + /* reader failure reasons*/ + case csr_read_pipe_broken : + printf(" reason : command read pipe broken\n"); + break; + + case csr_read_failure : + printf(" reason : command stdout read failed\n"); + break; + + /* writer failure reasons */ + case csr_write_failure : + printf(" reason : command stdin write failed\n"); + break; + + case csr_write_pipe_broken : + printf(" reason : command write pipe broken\n"); + break; + + /* timer reason */ + case csr_timeout : + printf(" reason : command timeouted\n"); + break; + + /* command failure reason */ + case csr_command_not_found : + printf(" reason : command not found\n"); + break; + + /* context failure reasons */ + case csr_exit_codes_dont_match : + printf(" reason : exit codes don't match\n"); + + break; + + case csr_outputs_dont_match : + { + char *diff; + printf(" reason : ouputs don't match\n"); + diff = xbt_str_diff(command->context->output->data,command->output->data); + printf(" output diff :\n%s\n",diff); + free(diff); + } + + break; + + case csr_signals_dont_match : + printf(" reason : signals don't match\n"); + break; + + case csr_unexpected_signal_caught: + printf(" reason : unexpected signal caught\n"); + break; + + case csr_expected_signal_not_receipt : + printf(" reason : expected signal not receipt\n"); + break; + + /* system failure reasons */ + case csr_exec_failure : + printf(" reason : can't excute the command\n"); + break; + + case csr_wait_failure : + printf(" reason : wait command failure\n"); + break; + + /* global/local interruption */ + case csr_interruption_request : + printf(" reason : the command receive a interruption request\n"); + break; + + /* unknown ? */ + case csr_unknown : + printf(" reason : unknown \n"); + } + } + + if(csr_command_not_found != command->reason && csr_exec_failure != command->reason) + { + if(INDEFINITE != command->exit_code) + /* the command exit code */ + printf(" exit code : %d\n",command->exit_code); + + /* if an expected exit code was specified display it */ + if(INDEFINITE != command->context->exit_code) + printf(" expected exit code : %d\n",command->context->exit_code); + else + printf(" no expected exit code specified\n"); + + /* if an expected exit code was specified display it */ + if(NULL == command->context->signal) + printf(" no expected signal specified\n"); + else + { + if(NULL != command->signal) + printf(" signal : %s\n",command->signal); + + printf(" expected signal : %s\n",command->context->signal); + } + + /* if the command has out put and the metacommand display output is specified display it */ + if(command->output && (0 != command->output->used) && (oh_display == command->context->output_handling)) + { + xbt_dynar_t a = xbt_str_split(command->output->data, "\n"); + char *out = xbt_str_join(a,"\n||"); + xbt_dynar_free(&a); + printf(" output :\n||%s",out); + free(out); + } + } + + printf("\n"); + + +} + +void +command_handle_failure(command_t command, cs_reason_t reason) +{ + + unit_t unit = command->unit; + + xbt_os_mutex_acquire(command->mutex); + + if((command->status != cs_interrupted) && (command->status != cs_failed)) + { + command->status = cs_failed; + command->reason = reason; + command->failed = 1; + + xbt_os_mutex_acquire(unit->mutex); + + /* increment the number of failed command of the unit */ + unit->number_of_failed_commands++; + + /* if the --ignore-failures option is not specified */ + if(!want_keep_going_unit) + { + if(!unit->interrupted) + { + /* the unit interrupted (exit for the loop) */ + unit->interrupted = 1; + + /* release the unit */ + xbt_os_sem_release(unit->sem); + } + + /* if the --keep-going option is not specified */ + if(!want_keep_going) + { + if(!interrupted) + { + /* request an global interruption by the runner */ + interrupted = 1; + + /* release the runner */ + xbt_os_sem_release(units_sem); + } + } + } + + xbt_os_mutex_release(unit->mutex); + } + + xbt_os_mutex_release(command->mutex); +} + +void +command_free(command_t* command) +{ + /* close the stdin and the stdout pipe handles */ + + #ifdef WIN32 + if((*command)->stdin_fd != INDEFINITE_FD) + CloseHandle((*command)->stdin_fd); + if((*command)->stdout_fd != INDEFINITE_FD) + CloseHandle((*command)->stdout_fd); + #else + if((*command)->stdin_fd != INDEFINITE_FD) + close((*command)->stdin_fd); + + if((*command)->stdout_fd != INDEFINITE_FD) + close((*command)->stdout_fd); + #endif + + timer_free(&((*command)->timer)); + writer_free(&((*command)->writer)); + reader_free(&((*command)->reader)); + xbt_strbuff_free((*command)->output); + context_free(&((*command)->context)); + + if((*command)->signal) + free((*command)->signal); + + free(*command); + *command = NULL; +} + + + diff --git a/tools/tesh2/src/context.c b/tools/tesh2/src/context.c new file mode 100644 index 0000000000..261d4b7bbd --- /dev/null +++ b/tools/tesh2/src/context.c @@ -0,0 +1,139 @@ +#include + + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +#define INDEFINITE_SIGNAL NULL + +context_t +context_new(void) +{ + context_t context = xbt_new0(s_context_t, 1); + + context->line = NULL; + context->command_line = NULL; + context->exit_code = 0; + context->timeout = INDEFINITE; + context->input = xbt_strbuff_new(); + context->output = xbt_strbuff_new(); + context->signal = INDEFINITE_SIGNAL; + context->output_handling = oh_check; + context->async = 0; + + return context; +} + +context_t +context_dup(context_t context) +{ + + context_t dup = xbt_new0(s_context_t, 1); + + dup->line = context->line; + dup->command_line = context->command_line; + dup->exit_code = context->exit_code; + dup->timeout = context->timeout; + dup->output = NULL; + dup->input = NULL; + dup->signal = NULL; + + if(context->input->used) + { + dup->input = xbt_strbuff_new(); + xbt_strbuff_append(dup->input,context->input->data); + } + + if(context->output->used) + { + dup->output = xbt_strbuff_new(); + xbt_strbuff_append(dup->output,context->output->data); + } + + if(context->signal) + dup->signal = strdup(context->signal); + + dup->output_handling = context->output_handling; + + dup->async = context->async; + + return dup; +} + +void +context_clear(context_t context) +{ + context->line = NULL; + context->command_line = NULL; + context->exit_code = 0; + context->timeout = INDEFINITE; + + if(context->input) + xbt_strbuff_empty(context->input); + + if(context->output) + xbt_strbuff_empty(context->output); + + if(context->signal) + { + free(context->signal); + context->signal = INDEFINITE_SIGNAL; + } + + context->output_handling = oh_check; + context->async = 0; + +} + +void +context_reset(context_t context) +{ + context->line = NULL; + context->command_line = NULL; + + if(context->input) + xbt_strbuff_empty(context->input); + + if(context->output) + xbt_strbuff_empty(context->output); + + if(context->signal) + { + free(context->signal); + context->signal = NULL; + } + + /* default expected exit code */ + context->exit_code = 0; + + context->output_handling = oh_check; + context->async = 0; + +} + +void +context_input_write(context_t context, const char* buffer) +{ + xbt_strbuff_append(context->input, buffer); +} + +void +context_ouput_read(context_t context, const char* buffer) +{ + xbt_strbuff_append(context->output, buffer); +} + +void +context_free(context_t* context) +{ + if(((*context)->input)) + xbt_strbuff_free(((*context)->input)); + + if(((*context)->output)) + xbt_strbuff_free(((*context)->output)); + + if((*context)->signal) + free((*context)->signal); + + *context = NULL; +} + diff --git a/tools/tesh2/src/dictionary.c b/tools/tesh2/src/dictionary.c new file mode 100644 index 0000000000..8c0c5b6bcc --- /dev/null +++ b/tools/tesh2/src/dictionary.c @@ -0,0 +1,136 @@ + +#include +#include + +#include +#include +#include + +#define __DEFAULT_BLOCK_CAPACITY ((int)512) +#define __DEFAULT_TABLE_SIZE ((int)256) + +static unsigned int +hfunc(const char* key) +{ + unsigned int hval = 5381; + int ch; + + while ( (ch = *key++) ) + hval = ((hval << 5) + hval) + ch; + + return hval; +} + +static int +cmp_key(const char* key1, const char* key2) +{ + return !strcmp(key1,key2); +} + + +dictionary_t +dictionary_new(fn_finalize_t fn_finalize) +{ + dictionary_t dictionary; + + if(!(dictionary = (dictionary_t)calloc(1,sizeof(s_dictionary_t)))) + return NULL; + + if(!(dictionary->htable = htable_new( + __DEFAULT_BLOCK_CAPACITY, + __DEFAULT_TABLE_SIZE, + (fn_hfunc_t)hfunc, + (fn_cmp_key_t)cmp_key, + fn_finalize))) + { + free(dictionary); + return NULL; + } + + return dictionary; +} + +int +dictionary_set(dictionary_t dictionary,const char* key, const void* val) +{ + if(!dictionary || !key || !val) + return EINVAL; + + return htable_set(dictionary->htable,(const void*)key,val); +} + +void* +dictionary_get(dictionary_t dictionary, const char* key) +{ + + if(!dictionary || !key) + { + errno = EINVAL; + return NULL; + } + + return htable_lookup(dictionary->htable,(const void*)key); +} + + +int +dictionary_free(dictionary_t* dictionaryptr) +{ + if(!(*dictionaryptr)) + return EINVAL; + + if((errno = htable_free(&((*dictionaryptr)->htable)))) + return errno; + + free(*dictionaryptr); + *dictionaryptr = NULL; + + return 0; +} + +int +dictionary_clear(dictionary_t dictionary) +{ + if(!dictionary) + return EINVAL; + + return htable_clear(dictionary->htable); +} + +int +dictionary_get_size(dictionary_t dictionary) +{ + if(!dictionary) + { + errno = EINVAL; + return -1; + } + + return htable_get_size(dictionary->htable); +} + +int +dictionary_is_empty(dictionary_t dictionary) +{ + if(!dictionary) + { + errno = EINVAL; + return 0; + } + + return htable_is_empty(dictionary->htable); +} + +void* +dictionary_remove(dictionary_t dictionary,const char* key) +{ + if(!dictionary) + { + errno = EINVAL; + return NULL; + } + + return htable_remove(dictionary->htable,key); +} + + diff --git a/tools/tesh2/src/directories.c b/tools/tesh2/src/directories.c new file mode 100644 index 0000000000..83726a71bf --- /dev/null +++ b/tools/tesh2/src/directories.c @@ -0,0 +1,199 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +directories_t +directories_new(void) +{ + directories_t directories = xbt_new0(s_directories_t, 1); + + if(!(directories->items = vector_new(8, directory_free))) + { + free(directories); + return NULL; + } + + return directories; +} + +int +directories_get_size(directories_t directories) +{ + if(!directories) + { + errno = EINVAL; + return -1; + } + + return vector_get_size(directories->items); +} + +int +directories_add(directories_t directories, directory_t directory) +{ + directory_t cur; + + if(!directories) + return EINVAL; + + vector_rewind(directories->items); + + while((cur = vector_get(directories->items))) + { + if(!strcmp(cur->name, directory->name)) + return EEXIST; + + vector_move_next(directories->items); + } + + return vector_push_back(directories->items, directory); +} + +int +directories_contains(directories_t directories, directory_t directory) +{ + directory_t cur; + + if(!directories) + return EINVAL; + + vector_rewind(directories->items); + + while((cur = vector_get(directories->items))) + { + if(!strcmp(cur->name, directory->name)) + return 1; + + vector_move_next(directories->items); + } + + return 0; +} + +directory_t +directories_search_fstream_directory(directories_t directories, const char* name) +{ + + struct stat buffer = {0}; + char* prev; + directory_t directory; + + if(!directories) + { + errno = EINVAL; + return NULL; + } + + prev = getcwd(NULL, 0); + + vector_rewind(directories->items); + + while((directory = vector_get(directories->items))) + { + chdir(directory->name); + + if(!stat(name, &buffer) || S_ISREG(buffer.st_mode)) + { + chdir(prev); + free(prev); + return directory; + } + + vector_move_next(directories->items); + } + + chdir(prev); + free(prev); + errno = ESRCH; + return NULL; +} + +int +directories_load(directories_t directories, fstreams_t fstreams, lstrings_t suffixes) +{ + directory_t directory; + + if(!directories || !fstreams || !suffixes) + return EINVAL; + + vector_rewind(directories->items); + + while((directory = vector_get(directories->items))) + { + if(directory->load) + { + if((errno = directory_open(directory))) + return errno; + + chdir(directory->name); + + if((errno = directory_load(directory, fstreams, suffixes))) + return errno; + + if((errno = directory_close(directory))) + return errno; + + chdir(root_directory->name); + } + + vector_move_next(directories->items); + + + } + + return 0; +} + +int +directories_has_directories_to_load(directories_t directories) +{ + directory_t directory; + + if(!directories) + { + errno = EINVAL; + return 0; + } + + vector_rewind(directories->items); + + while((directory = vector_get(directories->items))) + { + if(directory->load) + return 1; + + vector_move_next(directories->items); + + } + + return 0; +} + +directory_t +directories_get_back(directories_t directories) +{ + if(!directories) + { + errno = EINVAL; + return NULL; + } + + return vector_get_back(directories->items); +} + +int +directories_free(void** directoriesptr) +{ + if(!(*directoriesptr)) + return EINVAL; + + if((errno = vector_free(&((*((directories_t*)directoriesptr))->items)))) + return errno; + + free(*directoriesptr); + *directoriesptr = NULL; + + return 0; + +} diff --git a/tools/tesh2/src/directory.c b/tools/tesh2/src/directory.c new file mode 100644 index 0000000000..6033aa07fa --- /dev/null +++ b/tools/tesh2/src/directory.c @@ -0,0 +1,184 @@ +#include +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +directory_t +directory_new(const char* name, int load) +{ + directory_t directory; + struct stat buffer = {0}; + + if(!name) + { + errno = EINVAL; + return NULL; + } + + if(stat(name, &buffer)) + return NULL; + + if(!S_ISDIR(buffer.st_mode)) + { + errno = ENOTDIR; + return NULL; + } + + directory = xbt_new0(s_directory_t, 1); + + if(!strcmp(".",name)) + { + directory->name = getcwd(NULL, 0); + } + else if(!strcmp("..",name)) + { + char* buffer = getcwd(NULL, 0); + chdir(name); + directory->name = getcwd(NULL, 0); + chdir(buffer); + free(buffer); + } + else + { + directory->name = strdup(name); + } + + + directory->stream = NULL; + directory->load = load; + + return directory; +} + +int +directory_open(directory_t directory) +{ + if(!directory || directory->stream) + return EINVAL; + + if(!(directory->stream = opendir(directory->name))) + return errno; + + return 0; +} + + +int +directory_close(directory_t directory) +{ + if(!directory) + return EINVAL; + + if(!directory->stream) + return EBADF; + + if(closedir(directory->stream)) + return errno; + + directory->stream = NULL; + return 0; +} + +int +directory_load(directory_t directory, fstreams_t fstreams, lstrings_t suffixes) +{ + struct dirent* entry ={0}; + s_fstream_t sfstream = {0}; + const char* suffix; + int has_valid_suffix; + int is_empty = 1; + + if(!directory || !fstreams) + return EINVAL; + + if(!directory->stream) + return EBADF; + + sfstream.directory = strdup(directory->name); + + while((entry = readdir(directory->stream))) + { + has_valid_suffix = 0; + + lstrings_rewind(suffixes); + + while((suffix = lstrings_get(suffixes))) + { + if(!strncmp(suffix, entry->d_name + (strlen(entry->d_name) - strlen(suffix)), strlen(suffix))) + { + has_valid_suffix = 1; + break; + } + + lstrings_move_next(suffixes); + } + + if(!has_valid_suffix) + continue; + + sfstream.name = strdup(entry->d_name); + + /* check first if the file stream is already in the file streams to run */ + if(fstreams_contains(fstreams, &sfstream)) + { + WARN1("file %s already registred", entry->d_name); + free(sfstream.name); + continue; + } + + /* add the fstream to the list of file streams to run */ + if((errno = fstreams_add(fstreams, fstream_new(directory->name, entry->d_name)))) + { + INFO0("fstreams_add() failed"); + free(sfstream.directory); + free(sfstream.name); + return errno; + } + + is_empty = 0; + free(sfstream.name); + } + + if(is_empty) + WARN1("no tesh file found in the directory %s", directory->name); + + free(sfstream.directory); + + + return 0; +} + +int +directory_free(void** directoryptr) +{ + directory_t directory; + + if(!(*directoryptr)) + return EINVAL; + + directory = *((directory_t*)directoryptr); + + if(directory->stream) + if(directory_close(directory)) + return errno; + + free(directory->name); + + free(*directoryptr); + *directoryptr = NULL; + + return 0; +} + +const char* +directory_get_name(directory_t directory) +{ + if(!directory) + { + errno = EINVAL; + return NULL; + } + + return (const char*)directory->name; +} diff --git a/tools/tesh2/src/error.c b/tools/tesh2/src/error.c new file mode 100644 index 0000000000..1f7a7959a0 --- /dev/null +++ b/tools/tesh2/src/error.c @@ -0,0 +1,57 @@ +#include + +typedef struct s_entry +{ + int code; + const char* string; +}entry_t; + +static const +entry_t err[] = +{ + {EREAD, "a read pipe operation failed"}, + {EREADPIPE, "a pipe used to read from the stdout of a command is broken"}, + {ETIMEOUT, "a command is timeouted"}, + {EWRITE, "a write operation failed"}, + {EWRITEPIPE, "a pipe used to write to the stdin of a command is broken"}, + {EEXEC, "can't execute a command"}, + {EWAIT, "wait function failed"}, + {ECMDNOTFOUND, "command is not found"}, + {EEXITCODENOTMATCH, "exit codes don't match"}, + {EOUTPUTNOTMATCH, "outputs don't match"}, + {ESIGNOTMATCH, "signals don't match"}, + {EUNEXPECTEDSIG, "unexpected signal caught"}, + {ESIGNOTRECEIPT, "expected signal not receipt"}, + {EFILENOTFOUND, "specified tesh file not found"}, + {EGETCWD, "system error : the getcwd() function failed"}, + {EDIRNOTFOUND, "specified directory not found"}, + {ECHDIR, "system error : the chdir() function failed"}, + {EPROCESSCMDLINE, "internal error : the process_command_line() function failed"}, + {EARGNOTSPEC, "none optional argument not specified in the command line"}, + {ENOTPOSITIVENUM, "argument option not strictly positive"}, + {ESYNTAX, "syntax error"}, + {EINVALIDTIMEOUT, "timeout value specified by metacommand invalid"}, + {EINVALIDEXITCODE, "expected exit code value specified by the metacommand invalid"}, + {ESIGNOTSUPP, "signal specified by the metacommand not supported (Windows specific)"}, + {ELEADTIME, "lead time"}, + {EREADMENOTFOUND, "unable to locate the README.txt file"}, + {EINCLUDENOTFOUND, "include file specified by a metacommand is not found"}, + {ESUFFIXTOOLONG, "suffix is too long"}, + {EFILENOTINSPECDIR,"file not found in the specified directories"}, + {EFILENOTINCURDIR,"file not found in the current directory"}, + {-1, "unknown"} +}; + +const char* +error_to_string(int error) +{ + int i; + + for(i = 0; error >= 0 && err[i].code != -1; i++) + { + if(err[i].code == error) + return err[i].string; + } + + return "unknow error"; +} diff --git a/tools/tesh2/src/excludes.c b/tools/tesh2/src/excludes.c new file mode 100644 index 0000000000..332997468a --- /dev/null +++ b/tools/tesh2/src/excludes.c @@ -0,0 +1,123 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +excludes_t +excludes_new(void) +{ + excludes_t excludes = xbt_new0(s_excludes_t, 1); + + if(!(excludes->items = vector_new(8,NULL))) + { + free(excludes); + return NULL; + } + + return excludes; +} + +int +excludes_is_empty(excludes_t excludes) +{ + if(!excludes) + { + errno = EINVAL; + return 0; + } + + return vector_is_empty(excludes->items); +} + +int +excludes_contains(excludes_t excludes, fstream_t fstream) +{ + fstream_t cur; + + if(!excludes || !fstream) + { + errno = EINVAL; + return 0; + } + + vector_rewind(excludes->items); + + while((cur = vector_get(excludes->items))) + { + if(!strcmp(fstream->name, cur->name) && !strcmp(fstream->directory, cur->directory)) + return 1; + + vector_move_next(excludes->items); + } + + return 0; +} + +int +excludes_add(excludes_t excludes, fstream_t fstream) +{ + if(!excludes) + return EINVAL; + + if(excludes_contains(excludes, fstream)) + return EEXIST; + + return vector_push_back(excludes->items, fstream); +} + +int +excludes_check(excludes_t excludes, fstreams_t fstreams) +{ + fstream_t exclude; + fstream_t fstream; + int success = 1; + int exists; + + if(!excludes || !fstreams) + return EINVAL; + + vector_rewind(excludes->items); + + while((exclude = vector_get(excludes->items))) + { + vector_rewind(fstreams->items); + + while((fstream = vector_get(fstreams->items))) + { + exists = 0; + + if(!strcmp(fstream->name, exclude->name) && !strcmp(fstream->directory, exclude->directory)) + { + exists = 1; + break; + } + + vector_move_next(fstreams->items); + } + + if(!exists) + { + success = 0; + WARN1("cannot exclude the file %s",exclude->name); + } + + vector_move_next(excludes->items); + } + + return success; +} + +int +excludes_free(void** excludesptr) +{ + if(!(*excludesptr)) + return EINVAL; + + if((errno =vector_free((&(*((excludes_t*)excludesptr))->items)))) + return errno; + + free(*excludesptr); + *excludesptr = NULL; + + return 0; +} diff --git a/tools/tesh2/src/fstream.c b/tools/tesh2/src/fstream.c new file mode 100644 index 0000000000..1349a98ccc --- /dev/null +++ b/tools/tesh2/src/fstream.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +fstream_t +fstream_new(const char* directory, const char* name) +{ + fstream_t fstream; + /*struct stat buffer = {0};*/ + + if(!name) + { + errno = EINVAL; + return NULL; + } + + if(!directory && !strcmp("stdin", name)) + { + fstream = xbt_new0(s_fstream_t, 1); + fstream->name = strdup("stdin"); + return fstream; + } + else if(!directory) + { + errno = EINVAL; + return NULL; + } + + /*if(stat(name, &buffer)) + return NULL; + + if(!S_ISREG(buffer.st_mode)) + { + errno = ENOENT; + return NULL; + }*/ + + fstream = xbt_new0(s_fstream_t, 1); + + fstream->name = strdup(name); + + fstream->directory = strdup(directory); + fstream->stream = NULL; + + + return fstream; +} + +int +fstream_open(fstream_t fstream) +{ + if(!fstream || fstream->stream) + return EINVAL; + + if(!strcmp(fstream->name, "stdin")) + { + fstream->stream = stdin; + return 0; + } + + if(!(fstream->stream = fopen(fstream->name, "r"))) + return errno; + + return 0; +} + +int +fstream_close(fstream_t fstream) +{ + if(!fstream || !strcmp(fstream->name, "stdin")) + return EINVAL; + + if(!fstream->stream) + return EBADF; + + fclose(fstream->stream); + fstream->stream = NULL; + return errno; +} + +int +fstream_free(void** fstreamptr) +{ + if(!(*fstreamptr)) + return EINVAL; + + if((*((fstream_t*)fstreamptr))->stream) + fclose((*((fstream_t*)fstreamptr))->stream); + + free((*((fstream_t*)fstreamptr))->name); + + if((*((fstream_t*)fstreamptr))->directory) + free((*((fstream_t*)fstreamptr))->directory); + + free(*fstreamptr); + + *fstreamptr = NULL; + + return 0; + +} + +void +fstream_parse( fstream_t fstream, unit_t unit) +{ + size_t len; + char * line = NULL; + int line_num = 0; + char file_pos[256]; + xbt_strbuff_t buff; + int buffbegin = 0; + context_t context; + xbt_os_mutex_t mutex = unit->mutex; + + /* Count the line length while checking wheather it's blank */ + int blankline; + int linelen; + /* Deal with \ at the end of the line, and call handle_line on result */ + int to_be_continued; + + buff=xbt_strbuff_new(); + context = context_new(); + + while(!unit->interrupted && getline(&line, &len, fstream->stream) != -1) + { + blankline=1; + linelen = 0; + to_be_continued = 0; + + line_num++; + + while(line[linelen] != '\0') + { + if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n' && line[linelen]!='\r') + blankline = 0; + + linelen++; + } + + if(blankline) + { + if(!context->command_line && (context->input->used || context->output->used)) + { + ERROR1("[%d] Error: no command found in this chunk of lines.",buffbegin); + + if(unit->parsing_include_file) + ERROR1("Unit `%s': NOK (syntax error)", fstream->name); + else + ERROR2("Unit `%s' inclued in `%s' : NOK (syntax error)", fstream->name, fstream->name); + + exit_code = ESYNTAX; + unit_handle_failure(unit); + break; + } + + if(context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + + continue; + + } + + if(linelen>1 && line[linelen-2]=='\\') + { + if (linelen>2 && line[linelen-3] == '\\') + { + /* Damn. Escaped \ */ + line[linelen-2] = '\n'; + line[linelen-1] = '\0'; + } + else + { + to_be_continued = 1; + line[linelen-2] = '\0'; + linelen -= 2; + + if (!buff->used) + buffbegin = line_num; + } + } + + if(buff->used || to_be_continued) + { + xbt_strbuff_append(buff,line); + + if (!to_be_continued) + { + snprintf(file_pos,256,"%s:%d",fstream->name, buffbegin); + unit_handle_line(unit, context, mutex, file_pos, buff->data); + xbt_strbuff_empty(buff); + } + } + else + { + snprintf(file_pos,256,"%s:%d",fstream->name, line_num); + unit_handle_line(unit, context, mutex, file_pos, line); + } + } + + /* Check that last command of the file ran well */ + if(context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + /* Clear buffers */ + if (line) + free(line); + + xbt_strbuff_free(buff); +} + + diff --git a/tools/tesh2/src/fstreams.c b/tools/tesh2/src/fstreams.c new file mode 100644 index 0000000000..27905ee3e0 --- /dev/null +++ b/tools/tesh2/src/fstreams.c @@ -0,0 +1,178 @@ +#include +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +fstreams_t +fstreams_new(int capacity, fn_finalize_t fn_finalize) +{ + fstreams_t fstreams; + fstreams = xbt_new0(s_fstreams_t, 1); + + if(!(fstreams->items = vector_new(capacity, fn_finalize))) + { + free(fstreams); + return NULL; + } + + return fstreams; +} + +int +fstreams_exclude(fstreams_t fstreams, excludes_t excludes) +{ + vector_t to_erase; + fstream_t fstream; + + if(!fstreams || !excludes) + return EINVAL; + + if(excludes_is_empty(excludes)) + return 0; + + if(!(to_erase = vector_new(8, NULL))) + return errno; + + INFO0("excluding file streams"); + + /* collecte the file streams to exclude */ + vector_rewind(fstreams->items); + + while((fstream = vector_get(fstreams->items))) + { + if(excludes_contains(excludes, fstream)) + vector_push_back(to_erase, fstream); + + + vector_move_next(fstreams->items); + } + + if(!vector_is_empty(to_erase)) + { + + /* erase the file streams to exclude from the vector of file streams to run */ + vector_rewind(to_erase); + + while((fstream = vector_get(to_erase))) + { + vector_erase(fstreams->items, fstream); + + vector_move_next(to_erase); + } + } + + return vector_free(&to_erase); +} + +int +fstreams_contains(fstreams_t fstreams, fstream_t fstream) +{ + register fstream_t cur; + + if(!fstreams || !fstream) + { + errno = EINVAL; + return 0; + } + + vector_rewind(fstreams->items); + + while((cur = vector_get(fstreams->items))) + { + if(!strcmp(cur->name, fstream->name) && !strcmp(cur->directory, fstream->directory)) + return 1; + + vector_move_next(fstreams->items); + } + + return 0; +} + +int +fstreams_load(fstreams_t fstreams) +{ + register fstream_t fstream; + const char* directory = NULL; + + + if(!fstreams ) + return EINVAL; + + vector_rewind(fstreams->items); + + while((fstream = vector_get(fstreams->items))) + { + chdir(root_directory->name); + + if(!directory || strcmp(directory, fstream->directory)) + { + directory = fstream->directory; + + if(!dont_want_display_directory) + INFO1("entering directory \"%s\"",directory); + + } + + chdir(fstream->directory); + + fstream_open(fstream); + + vector_move_next(fstreams->items); + } + + return 0; +} + +int +fstreams_add(fstreams_t fstreams, fstream_t fstream) +{ + if(!fstreams) + return EINVAL; + + if(vector_push_back(fstreams->items, fstream)) + return errno; + + return 0; + +} + +int +fstreams_free(void** fstreamsptr) +{ + if(!(* fstreamsptr)) + return EINVAL; + + + if((errno = vector_free(&((*((fstreams_t*)fstreamsptr))->items)))) + return errno; + + free(*fstreamsptr); + + *fstreamsptr = NULL; + return 0; +} + +int +fstreams_get_size(fstreams_t fstreams) +{ + if(!fstreams) + { + errno = EINVAL; + return -1; + } + + return vector_get_size(fstreams->items); +} + +int +fstreams_is_empty(fstreams_t fstreams) +{ + if(!fstreams) + { + errno = EINVAL; + return -1; + } + + return vector_is_empty(fstreams->items); +} diff --git a/tools/tesh2/src/htable.c b/tools/tesh2/src/htable.c new file mode 100644 index 0000000000..5b7cb606ca --- /dev/null +++ b/tools/tesh2/src/htable.c @@ -0,0 +1,317 @@ + +#include + +#include +#include /* calloc() free() */ + +#define __DEFAULT_BLOCK_CAPACITY ((int)512) +#define __DEFAULT_TABLE_SIZE ((int)256) + + +static hassoc_t +get_assoc(htable_t htable, const void* key, unsigned int* hval) +{ + register hassoc_t hassoc; + hassoc_t* content = htable->content; + fn_cmp_key_t fn_cmp_key = htable->fn_cmp_key; + + *hval = (*(htable->fn_hfunc))(key) % htable->size; + + for (hassoc = content[*hval]; hassoc; hassoc = hassoc->next) + if((*fn_cmp_key)(hassoc->key,key)) + return hassoc; + + return NULL; +} + + +htable_t +htable_new( + int block_capacity, + int size, + fn_hfunc_t fn_hfunc, + fn_cmp_key_t fn_cmp_key, + fn_finalize_t fn_finalize +) +{ + htable_t htable; + + if((block_capacity < 0) || (size < 0) ||!fn_hfunc || !fn_cmp_key) + { + errno = EINVAL; + return NULL; + } + + + if(!(htable = (htable_t)calloc(1, sizeof(s_htable_t)))) + return NULL; + + if(!(htable->content = (hassoc_t*)calloc(size ? size : __DEFAULT_TABLE_SIZE, sizeof(hassoc_t)))) + { + free(htable); + return NULL; + } + + if(!(htable->allocator = allocator_new(block_capacity ? block_capacity : __DEFAULT_BLOCK_CAPACITY, sizeof(s_hassoc_t),NULL))) + { + free(htable->content); + free(htable); + return NULL; + } + + htable->size = size; + htable->fn_hfunc = fn_hfunc; + htable->fn_cmp_key = fn_cmp_key; + htable->fn_finalize = fn_finalize; + + return htable; +} + +int +htable_set(htable_t htable, const void* key, const void* val) +{ + hassoc_t hassoc; + unsigned int hval; + + if(!htable || !key || !val) + return EINVAL; + + + if(!(hassoc = get_assoc(htable, key, &hval))) + { + if(!(hassoc = (hassoc_t)allocator_alloc(htable->allocator))) + return errno; + + hassoc->key = key; + hassoc->val = val; + + hassoc->next = (htable->content)[hval]; + (htable->content)[hval] = hassoc; + + } + else + hassoc->val = val; + + return 0; +} + +void* +htable_lookup(htable_t htable, const void* key) +{ + hassoc_t hassoc; + unsigned int hval; + + if(!htable || !key) + { + errno = EINVAL; + return NULL; + } + + if(!(hassoc = get_assoc(htable, key, &hval))) + { + errno = ESRCH; + return NULL; + } + + return (void*)(hassoc->val); + +} + +void* +htable_remove(htable_t htable, const void* key) +{ + register hassoc_t hassoc; + hassoc_t* prev; + fn_cmp_key_t fn_cmp_key; + void* val; + + if(!htable || !key) + { + errno = EINVAL; + return NULL; + } + + prev = &(((htable->content)[(*(htable->fn_hfunc))(key) % htable->size])); + fn_cmp_key =htable->fn_cmp_key; + + for (hassoc = *prev; hassoc; hassoc = hassoc->next) + { + if((*fn_cmp_key)((hassoc->key),key)) + { + *prev = hassoc->next; + val = (void*)hassoc->val; + + if((errno = allocator_dealloc(htable->allocator,hassoc))) + return NULL; + + return val; + } + + prev = &(hassoc->next); + } + + + errno = ESRCH; + return NULL; +} + +int +htable_erase(htable_t htable, const void* key) +{ + register hassoc_t hassoc; + hassoc_t* prev; + fn_cmp_key_t fn_cmp_key; + void* val; + + if(!htable || !key) + return EINVAL; + + prev = &(((htable->content)[(*(htable->fn_hfunc))(key) % htable->size])); + + fn_cmp_key =htable->fn_cmp_key; + + for(hassoc = *prev; hassoc; hassoc = hassoc->next) + { + if((*fn_cmp_key)((hassoc->key),key)) + { + *prev = hassoc->next; + val = (void*)hassoc->val; + + if((errno = allocator_dealloc(htable->allocator,hassoc))) + return errno; + + if(htable->fn_finalize) + { + if((errno = (*(htable->fn_finalize))(&val))) + return errno; + } + + return 0; + } + + prev = &(hassoc->next); + } + + return ESRCH; +} + +int +htable_free(htable_t* htableptr) +{ + htable_t htable; + + if(!(*htableptr)) + return EINVAL; + + htable = (htable_t)(*htableptr); + + if(htable->fn_finalize) + { + hassoc_t* content; + register hassoc_t hassoc; + register int pos; + int size; + void* val; + + content = htable->content; + size = htable->size; + + for(pos = 0; pos < size; pos++) + { + for(hassoc = content[pos]; hassoc; hassoc = hassoc->next) + { + val = (void*)hassoc->val; + if((errno = (*(htable->fn_finalize))(&val))) + return errno; + } + } + } + + free(htable->content); + + if((errno = allocator_free(&(htable->allocator)))) + return errno; + + free(*htableptr); + *htableptr = NULL; + + return 0; +} + +int +htable_clear(htable_t htable) +{ + hassoc_t* content; + register hassoc_t hassoc; + register int pos; + int size; + void* val; + + if(!htable) + return EINVAL; + + + content = htable->content; + size = htable->size; + + if(htable->fn_finalize) + { + for(pos = 0; pos < size; pos++) + { + for(hassoc = content[pos]; hassoc; hassoc = hassoc->next) + { + val = (void*)hassoc->val; + if((errno = (*(htable->fn_finalize))(&val))) + return errno; + } + + content[pos] = NULL; + } + } + else + { + for(pos = 0; pos < size; pos++) + content[pos] = NULL; + } + + return allocator_clear(htable->allocator); +} + +int +htable_get_size(htable_t htable) +{ + + if(!htable) + { + errno = EINVAL; + return -1; + } + + return allocator_get_size(htable->allocator); +} + +int +htable_is_empty(htable_t htable) +{ + if(!htable) + { + errno = EINVAL; + return 0; + } + + return allocator_get_size(htable->allocator); +} + +int +htable_is_autodelete(htable_t htable) +{ + + if(!htable) + { + errno = EINVAL; + return 0; + } + + return (NULL != htable->fn_finalize); +} + diff --git a/tools/tesh2/src/lstrings.c b/tools/tesh2/src/lstrings.c new file mode 100644 index 0000000000..b87f2bf202 --- /dev/null +++ b/tools/tesh2/src/lstrings.c @@ -0,0 +1,816 @@ + +#include + +#include +#include +#include + +#include + +#define __DEFAULT_BLOCK_CAPACITY 128 + +static allocator_t +allocator = NULL; + +static int +ref = 0; + +static link_t +link_new(void* item) +{ + link_t link; + + if(!item) + { + errno = EINVAL; + return NULL; + } + + if(!(link = (link_t)allocator_alloc(allocator))) + return NULL; + + link->next = link->prev = NULL; + link->item = item; + + return link; +} + +static int +link_free(link_t* ref) +{ + if(!(*ref)) + return EINVAL; + + allocator_dealloc(allocator, *ref); + *ref = NULL; + + return 0; +} + +static link_t +search(lstrings_t lstrings, const char* string) +{ + register link_t cur = lstrings->next; + + while(cur != ((link_t)&(lstrings->item))) + { + if(!strcmp((const char*)(cur->item),string)) + return cur; + + cur = cur->next; + } + + errno = ESRCH; + return NULL; +} + + + +lstrings_t +lstrings_new(void) +{ + lstrings_t lstrings; + + if(!(lstrings = (lstrings_t)calloc(1,sizeof(s_lstrings_t)))) + return NULL; + + if(!allocator) + { + if(!(allocator = allocator_new(__DEFAULT_BLOCK_CAPACITY, sizeof(s_link_t), NULL))) + { + free(lstrings); + return NULL; + } + } + + lstrings->next = lstrings->prev = lstrings->cur = (link_t)(&(lstrings->item)); + lstrings->pos = -1; + lstrings->size = 0; + + ref++; + return lstrings; +} + +int +lstrings_rewind(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return 0; + } + + lstrings->cur = lstrings->next; + lstrings->pos = 0; + + return 1; +} + +int +lstrings_unwind(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return 0; + } + + lstrings->cur = lstrings->prev; + lstrings->pos = lstrings->size - 1; + + return 1; +} + +int +lstrings_clear(lstrings_t lstrings) +{ + if(!lstrings) + return EINVAL; + + if(!lstrings->size) + return EAGAIN; + + while(lstrings_pop_back(lstrings)); + + return 0; +} + +int +lstrings_free(lstrings_t* lstrings_ptr) +{ + if(!(*lstrings_ptr)) + return EINVAL; + + if((*lstrings_ptr)->size) + lstrings_clear(*lstrings_ptr); + + if(--ref) + allocator_free(&allocator); + + free(*lstrings_ptr); + *lstrings_ptr = NULL; + + return 0; +} + +int +lstrings_push_front(lstrings_t lstrings, const char* string) +{ + link_t what, where, next; + + if(!lstrings || !string) + return EINVAL; + + if(!(what = (link_t)link_new((void*)string))) + return errno; + + where = (link_t)(&(lstrings->item)); + next = where->next; + + what->prev = where; + what->next = next; + next->prev = what; + where->next = what; + + lstrings->size++; + + /* the iteration functions are now not permited */ + lstrings->pos = -1; + lstrings->cur = (link_t)(&(lstrings->item)); + + return 0; +} + +int +lstrings_push_back(lstrings_t lstrings, const char* string) +{ + link_t what, where, prev; + + if(!lstrings || !string) + return EINVAL; + + if(!(what = (link_t)link_new((void*)string))) + return errno; + + where = (link_t)(&(lstrings->item)); + prev = where->prev; + what->next = where; + what->prev = prev; + prev->next = what; + where->prev = what; + + lstrings->size++; + + /* the iteration functions are now not permited */ + lstrings->pos = -1; + lstrings->cur = (link_t)(&(lstrings->item)); + + return 0; +} + +int +lstrings_insert_after(lstrings_t lstrings, const char* what, const char* where) +{ + link_t __what, __where, __next; + + if(!lstrings || !what || !where) + return EINVAL; + + if(!(__what = link_new((void*)what))) + return errno; + + if((__where = search(lstrings, where))) + return errno; + + __next = __where->next; + + __what->prev = __where; + __what->next = __next; + __next->prev = __what; + __where->next = __what; + + lstrings->size++; + + return 0; +} + +int +lstrings_insert_before(lstrings_t lstrings, const char* what, const char* where) +{ + link_t __what, __where, __prev; + + if(!lstrings || !what || !where) + return EINVAL; + + if(!(__what = link_new((void*)what))) + return errno; + + if((__where = search(lstrings, where))) + return errno; + + __prev = __where->prev; + + __what->next = __where; + __what->prev = __prev; + __prev->next = __what; + __where->prev = __what; + + lstrings->size++; + + return 0; +} + +const char* +lstrings_pop_back(lstrings_t lstrings) +{ + link_t link, next, prev; + const char* string; + + if(!lstrings) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + link = lstrings->prev; + + next = link->next; + prev = link->prev; + + prev->next = next; + next->prev = prev; + + lstrings->size--; + + string = (const char*)link->item; + + link_free((link_t*)&link); + + /* the iteration functions are now not permited */ + lstrings->pos = -1; + lstrings->cur = (link_t)(&(lstrings->item)); + + return string; +} + +const char* +lstrings_pop_front(lstrings_t lstrings) +{ + + link_t link, next, prev; + const char* string; + + if(!lstrings) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + link = lstrings->next; + + next = link->next; + prev = link->prev; + prev->next = next; + next->prev = prev; + + lstrings->size--; + + string = (const char*)link->item; + + link_free((link_t*)&link); + + /* the iteration functions are now not permited */ + lstrings->pos = -1; + lstrings->cur = (link_t)(&(lstrings->item)); + + return string; +} + +int +lstrings_remove(lstrings_t lstrings, const char* string) +{ + link_t link, next, prev; + + if(!lstrings || !string) + return EINVAL; + + if(!lstrings->size) + return EAGAIN; + + if(!(link = search(lstrings, string))) + return errno; + + next = link->next; + prev = link->prev; + prev->next = next; + next->prev = prev; + + lstrings->size--; + + /* the iteration functions are now not permited */ + lstrings->pos = -1; + lstrings->cur = (link_t)(&(lstrings->item)); + + return link_free((link_t*)&link); +} + +int +lstrings_get_size(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return -1; + } + + return lstrings->size; +} + +int +lstrings_contains(lstrings_t lstrings, const char* string) +{ + register link_t cur; + + if(!lstrings || !string) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return 0; + } + + cur = lstrings->next; + + while(cur != ((link_t)&(lstrings->item))) + { + if(!strcmp((const char*)(cur->item), string)) + return 1; + + cur = cur->next; + } + + return 0; +} + +int +lstrings_is_empty(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + return !lstrings->size; +} + +int +lstrings_move_next(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return 0; + } + + if(lstrings->cur != (link_t)(&(lstrings->item))) + { + lstrings->cur = lstrings->cur->next; + lstrings->pos++; + return 1; + } + + lstrings->pos = -1; + errno = ERANGE; + return 0; +} + +const char* +lstrings_get(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return NULL; + } + + return (const char*)(lstrings->cur->item); +} + +const char* +lstrings_set(lstrings_t lstrings, const char* string) +{ + const char* prev_string; + + if(!lstrings || !string) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return NULL; + } + + prev_string = (const char*)(lstrings->cur->item); + lstrings->cur->item = (void*)string; + + return prev_string; +} + +const char* +lstrings_get_at(lstrings_t lstrings, int pos) +{ + register link_t cur; + + if(!lstrings || pos < 0 || pos >= lstrings->size) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + cur = lstrings->next; + + while(pos--) + cur = cur->next; + + + return (const char*)(cur->item); +} + +const char* +lstrings_set_at(lstrings_t lstrings, int pos, const char* string) +{ + register link_t cur; + const char* prev_string; + + if(!lstrings || !string) + { + errno = EINVAL; + return NULL; + } + + if(pos < 0 || pos >= lstrings->size) + { + errno = ERANGE; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + cur = lstrings->next; + + while(pos--) + cur = cur->next; + + prev_string = (const char*)cur->item; + cur->item = (void*)string; + + return prev_string; + +} + +int +lstrings_move_prev(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return 0; + } + + if(lstrings->cur != (link_t)(&(lstrings->item))) + { + lstrings->cur = lstrings->cur->prev; + lstrings->pos--; + return 1; + } + + lstrings->pos = -1; + errno = ERANGE; + return 0; +} + +static int +seek_set(lstrings_t lstrings, int offset) +{ + if(offset > lstrings->size) + return EINVAL; + + lstrings_rewind(lstrings); + + while(offset--) + lstrings_move_next(lstrings); + + return 0; +} + +static int +seek_end(lstrings_t lstrings, int offset) +{ + if(offset > lstrings->size) + return EINVAL; + + lstrings_unwind(lstrings); + + while(offset--) + lstrings_move_prev(lstrings); + + return 0; +} + + +static int +seek_cur(lstrings_t lstrings, int offset) +{ + if(lstrings->cur == lstrings->next) + { + /* we are at the begin of the lstrings */ + seek_set(lstrings, offset); + } + else if(lstrings->cur == lstrings->prev) + { + /* we are at the end of the lstrings */ + seek_end(lstrings, offset); + } + else + { + if(offset > (lstrings->size - lstrings->pos + 1)) + return EINVAL; + + while(offset--) + lstrings_move_next(lstrings); + + } + + return 0; +} + +int +lstrings_seek(lstrings_t lstrings, int offset, int whence) +{ + if(!lstrings) + return EINVAL; + + if(!lstrings->size) + return EAGAIN; + + switch(whence) + { + case SEEK_SET : + return seek_set(lstrings, offset); + + case SEEK_CUR : + return seek_cur(lstrings, offset); + + case SEEK_END : + return seek_end(lstrings, offset); + + } + + return EINVAL; +} + +int +lstrings_tell(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return -1; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return -1; + } + + return lstrings->pos; +} + +int +lstrings_getpos(lstrings_t lstrings, int* pos) +{ + if(!lstrings || !pos) + { + errno = EINVAL; + return 0; + } + + if(!lstrings->size || (-1 == lstrings->pos)) + { + errno = EAGAIN; + return 0; + } + + *pos = lstrings->pos; + return 1; +} + +int +lstrings_setpos(lstrings_t lstrings, int pos) +{ + if(!lstrings) + { + errno = EINVAL; + return 0; + } + + if(pos < 0 || pos >= lstrings->size) + { + errno = ERANGE; + return 0; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return 0; + } + + lstrings->cur = lstrings->next; + lstrings->pos = pos; + + while(pos--) + lstrings->cur = lstrings->cur->next; + + return 1; +} + + +const char* +lstrings_get_front(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + return (const char*)(lstrings->next->item); +} + + +const char* +lstrings_get_back(lstrings_t lstrings) +{ + if(!lstrings) + { + errno = EINVAL; + return NULL; + } + + if(!lstrings->size) + { + errno = EAGAIN; + return NULL; + } + + return (const char*)(lstrings->prev->item); +} + +char** +lstrings_to_cstr(lstrings_t lstrings) +{ + register int i; + link_t cur; + int size; + char** cstr; + + if(!lstrings || !lstrings->size) + { + errno = EINVAL; + return NULL; + } + + if(!(cstr = (char**)calloc(lstrings->size, sizeof(char*)))) + return NULL; + + /* get the first link of the list */ + cur = lstrings->next; + + for(i = 0; i < size; i++) + { + if(!(cstr[i] = strdup(cur->item))) + { + register int j; + + for(j = 0; j next; + } + + + return cstr; +} diff --git a/tools/tesh2/src/main.c b/tools/tesh2/src/main.c new file mode 100644 index 0000000000..94e9599491 --- /dev/null +++ b/tools/tesh2/src/main.c @@ -0,0 +1,1033 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * entry used to define the parameter of a tesh option. + */ +typedef struct s_optentry +{ + int c; /* the character of the option */ + + /* + * the type of the argument of the option + */ + enum + { + flag, /* it's a flag option, by default the flag is set to zero */ + string, /* the option has strings as argument */ + number /* the option has an integral positive number as argument */ + }type; + + byte* value; /* the value of the option */ + byte* optional_value; /* the optional value of the option if not specified */ + const char * long_name; /* the long name of the command */ +}s_optentry_t,* optentry_t; + + + +XBT_LOG_NEW_DEFAULT_CATEGORY(tesh,"TEst SHell utility"); + +#ifdef WIN32 +/* Windows specific : the previous process error mode */ +static UINT +prev_error_mode = 0; +#endif + +directory_t +root_directory = NULL; + +int +exit_code = 0; + +/* the current version of tesh */ +static const char* +version = "1.0"; + +/* ------------------------------------------------------------ */ +/* options */ +/* ------------------------------------------------------------ */ + +/* ------------------------------------------------------------ */ +/* numbers */ +/* ------------------------------------------------------------ */ + + +/* --jobs is specified with arg */ +static int +number_of_jobs = -2; + +/* --jobs option is not specified (use the default job count) */ +static int +default_number_of_jobs = 1; + +/* --jobs is specified but has no arg (one job per unit) */ +static int +optional_number_of_jobs = -1; + +/* the global timeout */ +static int +timeout = INDEFINITE; + +/* ------------------------------------------------------------ */ +/* strings dlists */ +/* ------------------------------------------------------------ */ + +/* --C change the directory before running the units */ +static directories_t +directories = NULL; + +/* the include directories : see the !i metacommand */ +vector_t +includes = NULL; + +/* the list of tesh files to run */ +static fstreams_t +fstreams = NULL; + +/* xbt logs */ +static lstrings_t +logs = NULL; + +static excludes_t +excludes = NULL; + +/* the ddlist of tesh file suffixes */ +static lstrings_t +suffixes = NULL; + +/* ------------------------------------------------------------ */ +/* flags */ +/* ------------------------------------------------------------ */ + +/* if 1, keep going when some commands can't be found + * default value 0 : not keep going + */ +int +want_keep_going = 0; + +/* if 1, ignore failures from commands + * default value : do not ignore failures + */ +int +want_keep_going_unit = 0; + +/* if 1, display tesh usage */ +static int +want_display_usage = 0; + +/* if 1, display the tesh version */ +static int +want_display_version = 0; + +/* if 1, the syntax of all tesh files is checked + * before running them + */ +static int +want_check_syntax = 0; + +/* if 1, all the tesh file of the current directory + * are runned + */ +static int +want_load_directory = 0; + +/* if 1, the status of all the units is display at + * the end. + */ +static int +want_verbose = 0; + +/* if 1, the directories are displayed */ +int +dont_want_display_directory = 0; + +/* if 1, just check the syntax of all the tesh files + * do not run them. + */ +int +want_dry_run = 0; + +/* if 1, display the tesh files syntax and exit */ +static int +want_display_semantic = 0; + +int +want_silent = 0; + +int +want_just_display = 0; + +static int +env_overrides = 0; + +static int +display_data_base = 0; + +static int +question = 0; + +/* the semaphore used to synchronize the jobs */ +xbt_os_sem_t +jobs_sem = NULL; + +/* the semaphore used by the runner to wait the end of all the units */ +xbt_os_sem_t +units_sem = NULL; + +static int +prepared = 0; + + +int +interrupted = 0; + +/* the table of the entries of the options */ +static const struct s_optentry opt_entries[] = +{ + { 'C', string, (byte*)&directories, 0, "directory" }, + { 'x', string, (byte*)&suffixes, 0, "suffix" }, + { 'e', flag, (byte*)&env_overrides, 0, "environment-overrides", }, + { 'f', string, (byte*)&fstreams, 0, "file" }, + { 'h', flag, (byte*)&want_display_usage, 0, "help" }, + { 'a', flag, (byte*)&want_display_semantic, 0, "semantic" }, + { 'i', flag, (byte*)&want_keep_going_unit, 0, "keep-going-unit" }, + { 'I', string, (byte*)&includes, 0, "include-dir" }, + { 'j', number, (byte*)&number_of_jobs, (byte*) &optional_number_of_jobs, "jobs" }, + { 'k', flag, (byte*)&want_keep_going, 0, "keep-going" }, + { 'c', flag, (byte*)&want_just_display, 0, "just-display" }, + { 'd', flag, (byte*)&display_data_base, 0,"display-data-base" }, + { 'q', flag, (byte*)&question, 0, "question" }, + { 's', flag, (byte*)&want_silent, 0, "silent" }, + { 'V', flag, (byte*)&want_display_version, 0, "version" }, + { 'w', flag, (byte*)&dont_want_display_directory, 0,"dont-display-directory" }, + { 'n', flag, (byte*)&want_dry_run, 0, "dry-run"}, + { 't', number, (byte*)&timeout, 0, "timeout" }, + { 'S', flag, (byte*)&want_check_syntax, 0, "check-syntax"}, + { 'r', flag, (byte*)&want_load_directory, 0, "load-directory"}, + { 'v', flag, (byte*)&want_verbose, 0, "verbose"}, + { 'F', string,(byte*)&excludes, 0, "exclude"}, + { 'l', string,(byte*)&logs,0,"log"}, + { 0, 0, 0, 0, 0} + +}; + +/* the tesh usage */ +static const char* usage[] = +{ + "Options:\n", + " -C DIRECTORY, --directory=DIRECTORY Change to DIRECTORY before running any commands.\n", + " -e, --environment-overrides Environment variables override files.\n", + " -f FILE, --file=FILE Read FILE as a teshfile.\n", + " remark :\n", + " all argument of the command line without\n", + " option is dealed as a tesh file.\n", + " -h, --help Display this message and exit.\n", + " -i, --keep-going-unit Ignore failures from commands.\n", + " The possible failures are :\n", + " - the exit code differ from the expected\n", + " - the signal throw differ from the expected\n", + " - the output differ from the expected\n", + " - the read pipe is broken\n", + " - the write pipe is broken\n", + " - the command assigned delay is outdated\n", + " -I DIRECTORY, --include-dir=DIRECTORY Search DIRECTORY for included files.\n", + " -j [N], --jobs[=N] Allow N commands at once; infinite commands with\n" + " no arg.\n", + " -k, --keep-going Keep going when some commands can't be made or\n", + " failed.\n", + " -c, --just-display Don't actually run any commands; just display them.\n", + " -p, --display-data-base Display tesh's internal database.\n", + " -q, --question Run no commands; exit status says if up to date.\n", + " -s, --silent, Don't echo commands.\n", + " -V, --version Display the version number of tesh and exit.\n", + " -d, --dont-display-directory Don't display the current directory.\n", + " -n, --dry-run Check the syntax of the specified tesh files, display the result and exit.\n", + " -t, --timeout Wait the end of the commands at most timeout seconds.\n", + " -S, --check-syntax Check the syntax of the tesh files before run them. \n", + " -x, --suffix Consider the new suffix for the tesh files.\n" + " remark :\n", + " the default suffix for the tesh files is \".tesh\".\n", + " -a, --semantic Display the tesh file metacommands syntax and exit.\n", + " -b, --build-file Build a tesh file.\n", + " -r, --load-directory Run all the tesh files located in the directories specified by the option --directory.\n", + " -v, --verbose Display the status of the commands.\n", + " -F file , --exclude=FILE Ignore the tesh file FILE.\n", + " -l format, --log Format of the xbt logs.\n", + NULL +}; + +/* the string of options of tesh */ +static char +optstring[1 + sizeof (opt_entries) / sizeof (opt_entries[0]) * 3]; + +/* the option table of tesh */ +static struct +option longopts[(sizeof (opt_entries) / sizeof (s_optentry_t))]; + +static void +init_options(void); + +static int +process_command_line(int argc, char** argv); + +static int +load(void); + +static void +display_usage(int exit_code); + +static void +display_version(void); + +static void +finalize(void); + +static void +display_semantic(void); + +static int +init(void); + + + +int +main(int argc, char* argv[]) +{ + init(); + + /* process the command line */ + if((exit_code = process_command_line(argc, argv))) + finalize(); + + /* initialize the xbt library + * for thread portability layer + */ + + if(!lstrings_is_empty(logs)) + { + int size = lstrings_get_size(logs); + char** cstr = lstrings_to_cstr(logs); + + xbt_init(&size, cstr); + + free(cstr); + + } + else + xbt_init(&argc, argv); + + /* the user wants to display the usage of tesh */ + if(want_display_usage) + finalize(); + + /* the user wants to display the version of tesh */ + if(want_display_version) + { + display_version(); + finalize(); + } + + /* the user wants to display the semantic of the tesh file metacommands */ + if(want_display_semantic) + { + display_semantic(); + finalize(); + } + + if(!directories_has_directories_to_load(directories) && want_load_directory) + WARN0("--load-directory specified but no directory specified"); + + excludes_check(excludes, fstreams); + + /* load tesh */ + if((exit_code = load())) + finalize(); + + prepared = 1; + + if(-2 == number_of_jobs) + {/* --jobs is not specified (use the default value) */ + number_of_jobs = default_number_of_jobs; + } + else if(optional_number_of_jobs == number_of_jobs) + {/* --jobs option is specified with no args (use one job per unit) */ + number_of_jobs = fstreams_get_size(fstreams); + } + + if(number_of_jobs > fstreams_get_size(fstreams)) + {/* --jobs = N is specified and N is more than the number of tesh files */ + + WARN0("number of requested jobs exceed the number of files"); + + /* assume one job per file */ + number_of_jobs = fstreams_get_size(fstreams); + } + + /* initialize the semaphore used to synchronize the jobs */ + jobs_sem = xbt_os_sem_init(number_of_jobs); + + /* initialize the semaphore used by the runner to wait for the end of all units */ + units_sem = xbt_os_sem_init(0); + + /* initialize the runner */ + if((0 != (exit_code = runner_init( + want_check_syntax, + timeout, + fstreams)))) + { + finalize(); + } + + if(want_just_display && want_silent) + want_silent = 0; + + if(want_just_display && want_dry_run) + WARN0("mismatch in the syntax : --just-check-syntax and --just-display options at same time"); + + /* run all the units */ + runner_run(); + + /* show the result of the units */ + if(want_verbose || want_dry_run) + runner_display_status(); + + + /* all the test are runned, destroy the runner */ + runner_destroy(); + + /* then, finalize tesh */ + finalize(); + + #ifndef WIN32 + return exit_code; + #endif + +} + +static int +init(void) +{ + char* buffer = getcwd(NULL, 0); + + #ifdef WIN32 + /* Windows specific : don't display the general-protection-fault message box and + * the the critical-error-handler message box (instead the system send the error + * to the calling process : tesh) + */ + prev_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + #endif + + /* used to store the file streams to run */ + fstreams = fstreams_new(DEFAULT_FSTREAMS_CAPACITY, fstream_free); + + root_directory = directory_new(buffer,want_load_directory); + free(buffer); + /* used to store the directories to loads */ + directories = directories_new(); + + /* register the current directory */ + directories_add(directories, root_directory); + + /* used to store the includes directories */ + includes = vector_new(DEFAULT_INCLUDES_CAPACITY, directory_free); + + /* xbt logs */ + logs = lstrings_new(); + + /* used to to store all the excluded file streams */ + excludes = excludes_new(); + + /* list of file streams suffixes */ + suffixes = lstrings_new(); + + lstrings_push_back(suffixes,".tesh"); + + return 0; +} + +static int +load(void) +{ + chdir(directory_get_name(root_directory)); + + if(want_load_directory) + directories_load(directories, fstreams, suffixes); + + /* on a aucun fichier specifie dans la ligne de commande + * l'option --run-current-directory n'a pas ete specifie ou aucun fichier ne se trouve dans le repertoire a charger + */ + if(fstreams_is_empty(fstreams)) + { + struct stat buffer = {0}; + + /* add the default tesh file if it exists */ + if(!stat("teshfile", &buffer) && S_ISREG(buffer.st_mode)) + fstreams_add(fstreams, fstream_new(getcwd(NULL, 0), "teshfile")); + } + + if(!excludes_is_empty(excludes) && !fstreams_is_empty(fstreams)) + fstreams_exclude(fstreams, excludes); + + if(fstreams_is_empty(fstreams)) + fstreams_add(fstreams, fstream_new(NULL, "stdin")); + + fstreams_load(fstreams); + + return 0; +} + +static void +finalize(void) +{ + if((!exit_code && want_display_usage) || (!exit_code && !prepared)) + display_usage(exit_code); + + if(fstreams) + fstreams_free((void**)&fstreams); + + if(excludes) + excludes_free((void**)&excludes); + + if(directories) + directories_free((void**)&directories); + + if(includes) + vector_free(&includes); + + if(suffixes) + lstrings_free(&suffixes); + + if(logs) + lstrings_free(&logs); + + /* destroy the semaphore used to synchronize the jobs */ + if(jobs_sem) + xbt_os_sem_destroy(jobs_sem); + + if(units_sem) + xbt_os_sem_destroy(units_sem); + + /* exit from the xbt framework */ + xbt_exit(); + + #ifdef WIN32 + SetErrorMode(prev_error_mode); + #endif + + if(!want_verbose && !want_dry_run && !want_silent && !want_just_display) + INFO2("tesh terminated with exit code %d : %s",exit_code, (!exit_code ? "success" : error_to_string(exit_code))); + + exit(exit_code); +} + +static void +init_options (void) +{ + char *p; + unsigned int i; + + if(optstring[0] != '\0') + /* déjà traité. */ + return; + + p = optstring; + + /* Return switch and non-switch args in order, regardless of + POSIXLY_CORRECT. Non-switch args are returned as option 1. */ + + /* le premier caractère de la chaîne d'options vaut -. + * les arguments ne correspondant pas à une option sont + * manipulés comme s'ils étaient des arguments d'une option + * dont le caractère est le caractère de code 1 + */ + *p++ = '-'; + + for (i = 0; opt_entries[i].c != '\0'; ++i) + { + /* initialize le nom de l'option longue*/ + longopts[i].name = (opt_entries[i].long_name == 0 ? "" : opt_entries[i].long_name); + + /* getopt_long() retourne la valeur de val */ + longopts[i].flag = 0; + + /* la valeur de l'option courte est le caractère spécifié dans opt_entries[i].c */ + longopts[i].val = opt_entries[i].c; + + /* on l'ajoute à la chaine des optstring */ + *p++ = opt_entries[i].c; + + switch (opt_entries[i].type) + { + /* si c'est une option qui sert a positionner un flag ou que l'on doit ignorée, elle n'a pas d'argument */ + case flag: + longopts[i].has_arg = no_argument; + break; + + /* c'est une option qui attent un argument : + * une chaine de caractères, un nombre flottant, + * ou un entier positif + */ + case string: + case number: + + *p++ = ':'; + + if(opt_entries[i].optional_value != 0) + { + *p++ = ':'; + + longopts[i].has_arg = optional_argument; + } + else + longopts[i].has_arg = required_argument; + + break; + } + } + + *p = '\0'; + longopts[i].name = 0; +} + +static int +process_command_line(int argc, char** argv) +{ + register const struct s_optentry* entry; + register int c; + directory_t directory; + fstream_t fstream; + + /* initialize the options table of tesh */ + init_options(); + + /* display the errors of the function getopt_long() */ + opterr = 1; + + optind = 0; + + while (optind < argc) + { + c = getopt_long (argc, argv, optstring, longopts, (int *) 0); + + if(c == EOF) + { + /* end of the command line or "--". */ + break; + } + else if (c == 1) + { + /* the argument of the command line is not an option (no "-"), assume it's a tesh file */ + /*struct stat buffer = {0}; + char* prev = getcwd(NULL, 0); + + directory = directories_get_back(directories); + + chdir(directory->name); + + if(stat(optarg, &buffer) || !S_ISREG(buffer.st_mode)) + { + chdir(prev); + free(prev); + ERROR1("file %s not found", optarg); + return EFILENOTFOUND; + } + + chdir(prev); + free(prev);*/ + + directory = directories_search_fstream_directory(directories, optarg); + + if(!directory) + { + if(1 == directories_get_size(directories)) + { + ERROR1("file %s not found in the current directory",optarg); + return EFILENOTINCURDIR; + } + else + { + ERROR1("file %s not found in the specified directories",optarg); + return EFILENOTINSPECDIR; + } + } + + if(!(fstream = fstream_new(directory_get_name(directory), optarg))) + { + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + else + { + if(fstreams_contains(fstreams, fstream)) + { + fstream_free((void**)&fstream); + WARN1("file %s already specified", optarg); + } + else + { + if((errno = fstreams_add(fstreams, fstream))) + { + fstream_free((void**)&fstream); + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + } + } + else if (c == '?') + { + /* unknown option, getopt_long() displays the error */ + return 1; + } + else + { + for (entry = opt_entries; entry->c != '\0'; ++entry) + + if(c == entry->c) + { + + switch (entry->type) + { + /* impossible */ + default: + ERROR0("command line processing failed : internal error"); + return EPROCESSCMDLINE; + + + /* flag options */ + case flag: + /* set the flag to one */ + *(int*) entry->value = 1; + + break; + + /* string options */ + case string: + + if(!optarg) + { + /* an option with a optional arg is specified use the entry->optional_value */ + optarg = (char*)entry->optional_value; + } + else if (*optarg == '\0') + { + /* a non optional argument is not specified */ + ERROR2("the option %c \"%s\"requires an argument",entry->c,entry->long_name); + return EARGNOTSPEC; + } + + /* --directory option */ + if(!strcmp(entry->long_name,"directory")) + { + if(!(directory = directory_new(optarg, want_load_directory))) + { + if(ENOTDIR == errno) + { + ERROR1("directory %s not found",optarg); + return EDIRNOTFOUND; + } + else + { + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + else + { + if(directories_contains(directories, directory)) + { + directory_free((void**)&directory); + WARN1("directory %s already specified",optarg); + } + else + { + if((errno = directories_add(directories, directory))) + { + directory_free((void**)&directory); + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + } + } + /* --suffix option */ + else if(!strcmp(entry->long_name,"suffix")) + { + if(strlen(optarg) > MAX_SUFFIX) + { + ERROR1("suffix %s too long",optarg); + return ESUFFIXTOOLONG; + } + + if(optarg[0] == '.') + { + char suffix[MAX_SUFFIX + 2] = {0}; + sprintf(suffix,".%s",optarg); + + if(lstrings_contains(suffixes, suffix)) + WARN1("suffix %s already specified", optarg); + else + lstrings_push_back(suffixes, suffix); + } + else + { + if(lstrings_contains(suffixes, optarg)) + WARN1("suffix %s already specified", optarg); + else + lstrings_push_back(suffixes, optarg); + } + } + /* --file option */ + else if(!strcmp(entry->long_name,"file")) + { + + /* the argument of the command line is not an option (no "-"), assume it's a tesh file */ + /*struct stat buffer = {0}; + char* prev = getcwd(NULL, 0); + + directory = directories_get_back(directories); + + chdir(directory->name); + + if(stat(optarg, &buffer) || !S_ISREG(buffer.st_mode)) + { + chdir(prev); + free(prev); + ERROR1("file %s not found", optarg); + return EFILENOTFOUND; + } + + chdir(prev); + free(prev);*/ + + directory = directories_search_fstream_directory(directories, optarg); + + if(!directory) + { + if(1 == directories_get_size(directories)) + { + ERROR1("file %s not found in the current directory",optarg); + return EFILENOTINCURDIR; + } + else + { + ERROR1("file %s not found in the specified directories",optarg); + return EFILENOTINSPECDIR; + } + } + + if(!(fstream = fstream_new(directory_get_name(directory),optarg))) + { + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + else + { + if(fstreams_contains(fstreams, fstream)) + { + fstream_free((void**)&fstream); + WARN1("file %s already specified", optarg); + } + else + { + if((errno = fstreams_add(fstreams, fstream))) + { + fstream_free((void**)&fstream); + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + } + } + /* --include-dir option */ + else if(!strcmp(entry->long_name,"include-dir")) + { + if(!(directory = directory_new(optarg, want_load_directory))) + { + if(ENOTDIR == errno) + { + ERROR1("%s is not a directory",optarg); + return EDIRNOTFOUND; + } + else + { + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + else + { + if(vector_contains(includes, directory)) + { + directory_free((void**)&directory); + WARN1("include directory %s already specified",optarg); + + } + else + { + if((errno = vector_push_back(includes, directory))) + { + directory_free((void**)&directory); + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + } + } + /* --exclude option */ + else if(!strcmp(entry->long_name,"exclude")) + { + directory = directories_get_back(directories); + + if(!(fstream = fstream_new(directory_get_name(directory), optarg))) + { + if(ENOENT == errno) + { + ERROR1("file to exclude %s not found", optarg); + return EFILENOTFOUND; + } + else + { + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + else + { + if(excludes_contains(excludes, fstream)) + { + fstream_free((void**)&fstream); + WARN1("file to exclude %s already specified", optarg); + } + else + { + if((errno = excludes_add(excludes, fstream))) + { + fstream_free((void**)&fstream); + ERROR1("command line processing failed with the error code %d", errno); + return EPROCESSCMDLINE; + } + } + } + } + /* --log option */ + else if(!strcmp(entry->long_name,"log")) + { + lstrings_push_back(logs, optarg); + } + else + { + /* TODO */ + } + + + break; + + /* strictly positve number options */ + case number: + + if ((NULL == optarg) && (argc > optind)) + { + const char* cp; + + for (cp = argv[optind]; isdigit(cp[0]); ++cp) + if(cp[0] == '\0') + optarg = argv[optind++]; + } + + /* the number option is specified */ + if(NULL != optarg) + { + int i = atoi(optarg); + const char *cp; + + for (cp = optarg; isdigit(cp[0]); ++cp); + + if (i < 1 || cp[0] != '\0') + { + ERROR2("option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name); + return ENOTPOSITIVENUM; + } + else + *(int*)entry->value = i; + } + /* the number option is specified but has no arg, use the optional value*/ + else + *(int*)entry->value = *(int*) entry->optional_value; + + break; + + } + break; + } + } + } + + return 0; +} + +static void +display_usage(int exit_code) +{ + const char **cpp; + FILE* stream; + + if (want_display_version) + display_version(); + + stream = exit_code ? stderr : stdout; + + fprintf (stream, "Usage: tesh [options] [file] ...\n"); + + for (cpp = usage; *cpp; ++cpp) + fputs (*cpp, stream); + + fprintf(stream, "\nReport bugs to \n"); +} + +static void +display_version(void) +{ + /* TODO : display the version of tesh */ + printf("Version :\n"); + printf(" tesh version %s : Mini shell specialized in running test units by Martin Quinson \n", version); + printf(" and Malek Cherier\n"); + printf(" Copyright (c) 2007, 2008 Martin Quinson, Malek Cherier\n"); + printf(" All rights reserved\n"); + printf(" This program is free software; you can redistribute it and/or modify it\n"); + printf(" under the terms of the license (GNU LGPL) which comes with this package.\n\n"); + + if(!want_display_usage) + printf("Report bugs to "); +} + +static void +display_semantic(void) +{ + size_t len; + char * line = NULL; + + FILE* stream = fopen("README.txt", "r"); + + if(!stream) + { + ERROR0("Unable to locate the README.txt file"); + exit_code = EREADMENOTFOUND; + return; + } + + while(getline(&line, &len, stream) != -1) + printf("%s",line); + + fclose(stream); +} + + diff --git a/tools/tesh2/src/reader.c b/tools/tesh2/src/reader.c new file mode 100644 index 0000000000..29b1c9555f --- /dev/null +++ b/tools/tesh2/src/reader.c @@ -0,0 +1,142 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +static void* +reader_start_routine(void* p); + +reader_t +reader_new(command_t command) +{ + reader_t reader = xbt_new0(s_reader_t, 1); + + reader->thread = NULL; + reader->command = command; + reader->broken_pipe = 0; + reader->failed = 0; + + reader->started = xbt_os_sem_init(0); + + return reader; +} + +void +reader_free(reader_t* reader) +{ + free(*reader); + *reader = NULL; +} + +void +reader_read(reader_t reader) +{ + reader->thread = xbt_os_thread_create("", reader_start_routine, reader); +} + +#ifdef WIN32 +static void* +reader_start_routine(void* p) +{ + reader_t reader = (reader_t)p; + command_t command = reader->command; + + xbt_strbuff_t output = command->output; + HANDLE stdout_fd = command->stdout_fd; + + DWORD number_of_bytes_to_read = 4096; + DWORD number_of_bytes_readed; + + char* buffer = (char*)calloc(number_of_bytes_to_read,sizeof(char)); + + while(!command->failed && !command->interrupted && !command->successeded && !reader->failed && !reader->broken_pipe) + { + if(!ReadFile(reader->command->stdout_fd, buffer, number_of_bytes_to_read, &number_of_bytes_readed, NULL) || (0 == number_of_bytes_readed)) + { + if(GetLastError() == ERROR_BROKEN_PIPE) + reader->broken_pipe = 1; + else + reader->failed = 1; + } + else + { + if(number_of_bytes_readed > 0) + { + buffer[number_of_bytes_readed]='\0'; + xbt_strbuff_append(output,buffer); + } + else + { + xbt_os_thread_yield(); + } + } + } + + free(buffer); + + if(reader->failed && !command->failed && !command->interrupted && !command->successeded) + { + command_kill(command); + exit_code = EREAD; + command_handle_failure(command, csr_read_failure); + } + + return NULL; +} +#else +static void* +reader_start_routine(void* p) +{ + reader_t reader = (reader_t)p; + command_t command = reader->command; + xbt_strbuff_t output = command->output; + int stdout_fd = command->stdout_fd; + int number_of_bytes_readed; + int number_of_bytes_to_read = (1024 > SSIZE_MAX) ? SSIZE_MAX : 1024; + + char* buffer = (char*)calloc(number_of_bytes_to_read,sizeof(char)); + xbt_os_sem_release(reader->started); + + do + { + number_of_bytes_readed = read(stdout_fd, buffer, number_of_bytes_to_read); + + if(number_of_bytes_readed < 0 && errno != EINTR && errno != EAGAIN) + { + reader->failed = 1; + } + + if(number_of_bytes_readed > 0) + { + buffer[number_of_bytes_readed]='\0'; + xbt_strbuff_append(output,buffer); + } + else + { + usleep(100); + } + + }while(!command->failed && !command->interrupted && !command->successeded && !reader->failed && (number_of_bytes_readed != 0 /* end of file <-> normal exit */)); + + free(buffer); + + if(reader->failed && !command->failed && !command->interrupted && !command->successeded) + { + command_kill(command); + exit_code = EREAD; + command_handle_failure(command, csr_read_failure); + } + + reader->broken_pipe = 1; + + /*printf("the reader of the command %p is ended\n",command);*/ + + return NULL; +} +#endif + +void +reader_wait(reader_t reader) +{ + xbt_os_thread_join(reader->thread, NULL); +} diff --git a/tools/tesh2/src/runner.c b/tools/tesh2/src/runner.c new file mode 100644 index 0000000000..aeec092fba --- /dev/null +++ b/tools/tesh2/src/runner.c @@ -0,0 +1,254 @@ +#include +#include +#include + +#include /* for error code */ +#include /* for calloc() */ +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +/* the unique tesh runner */ +static runner_t +runner = NULL; + +/* wait for the tesh runner terminaison */ +static void +runner_wait(void); + +static void* +runner_start_routine(void* p); + + +/* check the syntax of the tesh files if + * the want_check_syntax is specified. Returns + * 0 if the syntax is clean. + */ +static int +check_syntax(void); + +#ifdef WIN32 + +static HANDLE +timer_handle = NULL; + +static void* +runner_start_routine(void* p) +{ + + LARGE_INTEGER li; + + li.QuadPart=- runner->timeout * 10000000; /* 10000000 = 10 000 000 * 100 nanoseconds = 1 second */ + + /* create the waitable timer */ + timer_handle = CreateWaitableTimer(NULL, TRUE, NULL); + + /* set a timer to wait for timeout seconds */ + SetWaitableTimer(timer_handle, &li, 0, NULL, NULL, 0); + + /* wait for the timer */ + WaitForSingleObject(timer_handle, INFINITE); + + if(runner->waiting) + { + exit_code = ELEADTIME; + runner->timeouted = 1; + xbt_os_sem_release(units_sem); + } + + return NULL; +} + +#else +static void* +runner_start_routine(void* p) +{ + struct timespec ts; + + ts.tv_sec = runner->timeout; + ts.tv_nsec = 0L; + + do + { + nanosleep(&ts, &ts); + }while(EINTR == errno); + + if(errno) + { + /* TODO process the error */ + } + else + { + if(runner->waiting) + { + exit_code = ELEADTIME; + runner->timeouted = 1; + xbt_os_sem_release(units_sem); + } + } + + return NULL; +} +#endif + + +int +runner_init(int want_check_syntax, int timeout, fstreams_t fstreams) +{ + + if(runner) + { + ERROR0("Runner is already initialized"); + return EEXIST; + } + + + runner = xbt_new0(s_runner_t, 1); + + if(!(runner->units = units_new(runner, fstreams))) + { + ERROR0("Runner initialization failed"); + + free(runner); + runner = NULL; + return errno; + } + + runner->timeout = timeout; + runner->timeouted = 0; + runner->interrupted = 0; + runner->number_of_ended_units = 0; + runner->number_of_runned_units = 0; + runner->waiting = 0; + + if(want_check_syntax) + { + if((errno = check_syntax())) + return errno; + } + + return 0; + +} + +void +runner_destroy(void) +{ + units_free((void**)(&(runner->units))); + + #ifdef WIN32 + CloseHandle(timer_handle); + #endif + + if(runner->thread) + xbt_os_thread_join(runner->thread, NULL); + + free(runner); + + runner = NULL; +} + +void +runner_run(void) +{ + xbt_os_mutex_t mutex; + + mutex = xbt_os_mutex_init(); + + units_run_all(runner->units, mutex); + + if(!interrupted) + runner_wait(); + + /* if the runner is timeouted or receive a interruption request + * , interrupt all the active units. + */ + if(runner->timeouted || interrupted) + runner_interrupt(); + + units_join_all(runner->units); + + xbt_os_mutex_destroy(mutex); + +} + +static void +runner_wait(void) +{ + if(runner->timeout > 0) + runner->thread = xbt_os_thread_create("", runner_start_routine, NULL); + /* signal that the runner is waiting */ + runner->waiting = 1; + + /* wait for the end of all the units */ + xbt_os_sem_acquire(units_sem); + + runner->waiting = 0; +} + + + +/* + * interrupt all the active units. + * this function is called when the lead time of the execution is reached + * or when a failed unit requests an interruption of the execution. + */ +void +runner_interrupt(void) +{ + units_interrupt_all(runner->units); +} + +void +runner_display_status(void) +{ + if(!want_dry_run) + { + + /*unit_t unit;*/ + + printf("Runner\n"); + printf("Status informations :\n"); + + printf(" number of units %d\n",units_get_size(runner->units)); + printf(" exit code %d (%s)\n",exit_code, exit_code ? error_to_string(exit_code) : "success"); + + units_verbose(runner->units); + } + else + { + if(exit_code) + ERROR0("Syntax error detected"); + else if(exit_code == 0) + INFO0("Syntax 0K"); + } +} + +static int +check_syntax(void) +{ + if(!want_dry_run) + { + want_dry_run = 1; + + runner_run(); + + want_dry_run = 0; + + if(0 == exit_code) + { + if(!want_silent) + INFO0("syntax checked (OK)"); + + units_reset_all(runner->units); + + } + + } + else + { + WARN0("mismatch in the syntax : --just-check-syntax and --check-syntax options at same time"); + } + + return exit_code; +} diff --git a/tools/tesh2/src/signal.c b/tools/tesh2/src/signal.c new file mode 100644 index 0000000000..c7ffc611e0 --- /dev/null +++ b/tools/tesh2/src/signal.c @@ -0,0 +1,126 @@ +/* $Id: signal.c 3483 2007-05-07 11:18:56Z mquinson $ */ + +/* signal -- what TESH needs to know about signals */ + +/* Copyright (c) 2007 Martin Quinson. */ +/* All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#include <_signal.h> + +#ifdef WIN32 +int +is_an_unhandled_exception(DWORD exit_code); + +typedef struct s_exception_entry +{ + DWORD value; + const char* signal; +}s_exception_entry_t,* exception_entry_t; + +static const s_exception_entry_t exceptions[] = +{ + {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV"}, + {EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "SIGSEGV"}, + {EXCEPTION_BREAKPOINT, "SIGTRAP"}, + {EXCEPTION_DATATYPE_MISALIGNMENT, "SIGBUS"}, + {EXCEPTION_FLT_DENORMAL_OPERAND, "SIGFPE"}, + {EXCEPTION_FLT_DIVIDE_BY_ZERO, "SIGFPE"}, + {EXCEPTION_FLT_INEXACT_RESULT, "SIGFPE"}, + {EXCEPTION_FLT_INVALID_OPERATION, "SIGFPE"}, + {EXCEPTION_FLT_OVERFLOW, "SIGFPE"}, + {EXCEPTION_FLT_STACK_CHECK, "SIGFPE"}, + {EXCEPTION_FLT_UNDERFLOW, "SIGFPE"}, + {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL"}, + {EXCEPTION_IN_PAGE_ERROR, "SIGSEGV"}, + {EXCEPTION_INT_DIVIDE_BY_ZERO, "SIGFPE"}, + {EXCEPTION_INT_OVERFLOW, "SIGFPE"}, + {EXCEPTION_STACK_OVERFLOW, "SIGILL"}, + {EXCEPTION_SINGLE_STEP, "SIGTRAP"}, + {EXCEPTION_NONCONTINUABLE_EXCEPTION, "SIGILL"}, + {EXCEPTION_PRIV_INSTRUCTION, "SIGILL"} +}; +/* number of the entries in the table of exceptions */ +#define MAX_EXECPTION ((unsigned int)19) + +#endif + +typedef struct s_signal_entry { + const char* name; + int number; +} s_signal_entry_t,* signal_entry_t; + +static const s_signal_entry_t signals[] = { + {"SIGHUP" ,SIGHUP}, + {"SIGINT" ,SIGINT}, + {"SIGQUIT" ,SIGQUIT}, + {"SIGILL" ,SIGILL}, + {"SIGTRAP" ,SIGTRAP}, + {"SIGABRT" ,SIGABRT}, + {"SIGFPE" ,SIGFPE}, + {"SIGKILL" ,SIGKILL}, + {"SIGBUS" ,SIGBUS}, + {"SIGSEGV" ,SIGSEGV}, + {"SIGSYS" ,SIGSYS}, + {"SIGPIPE" ,SIGPIPE}, + {"SIGALRM" ,SIGALRM}, + {"SIGTERM" ,SIGTERM}, + {"SIGURG" ,SIGURG}, + {"SIGSTOP" ,SIGSTOP}, + {"SIGTSTP" ,SIGTSTP}, + {"SIGCONT" ,SIGCONT}, + {"SIGCHLD" ,SIGCHLD}, + {"SIGTTIN" ,SIGTTIN}, + {"SIGTTOU" ,SIGTTOU}, + {"SIGIO" ,SIGIO}, + {"SIGXCPU" ,SIGXCPU}, + {"SIGXFSZ" ,SIGXFSZ}, + {"SIGVTALRM",SIGVTALRM}, + {"SIGPROF" ,SIGPROF}, + {"SIGWINCH" ,SIGWINCH}, + {"SIGUSR1" ,SIGUSR1}, + {"SIGUSR2" ,SIGUSR2}, + {"SIG UNKNOWN" ,-1} +}; + +#ifdef WIN32 +const char* signal_name(DWORD got, char *expected) +#else +const char* signal_name(unsigned int got, char *expected) +#endif +{ + int i; + + #ifdef WIN32 + + for (i=0; i < MAX_EXECPTION; i++) + if (exceptions[i].value == got) + return (exceptions[i].signal); + #else + if((got == SIGBUS) && !strcmp("SIGSEGV",expected)) + got = SIGSEGV; + + for (i=0; signals[i].number != -1; i++) + if (signals[i].number == got) + return (signals[i].name); + + #endif + return bprintf("SIG UNKNOWN (%d)", got); +} + + +#ifdef WIN32 +int +is_an_unhandled_exception(DWORD exit_code) +{ + unsigned int i; + + for(i = 0; i < MAX_EXECPTION; i++) + if(exceptions[i].value == exit_code) + return 1; + + return 0; +} +#endif diff --git a/tools/tesh2/src/suite.c b/tools/tesh2/src/suite.c new file mode 100644 index 0000000000..232b03948c --- /dev/null +++ b/tools/tesh2/src/suite.c @@ -0,0 +1,39 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +suite_t +suite_new(unit_t owner, const char* description) +{ + suite_t suite = (suite_t)malloc (sizeof (s_suite_t)); + + suite->number = 0; + suite->units = vector_new(DEFAULT_UNITS_CAPACITY, unit_free); + suite->owner = owner; + suite->description = description; + suite->number_of_successed_units = 0; + suite->number_of_failed_units = 0; + + return suite; +} + + +void +suite_include_unit(suite_t suite, unit_t unit) +{ + vector_push_back(suite->units, unit); + suite->number++; +} + +void +suite_free(suite_t* suite) +{ + if(*suite) + { + vector_free(&((*suite)->units)); + free(*suite); + *suite = NULL; + } +} + diff --git a/tools/tesh2/src/timer.c b/tools/tesh2/src/timer.c new file mode 100644 index 0000000000..329ce61ac6 --- /dev/null +++ b/tools/tesh2/src/timer.c @@ -0,0 +1,76 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +static void* +timer_start_routine(void* p); + +ttimer_t +timer_new(command_t command) +{ + ttimer_t timer = xbt_new0(s_timer_t, 1); + + timer->command = command; + timer->thread = NULL; + timer->timeouted = 0; + timer->started = xbt_os_sem_init(0); + + return timer; +} + +void +timer_free(ttimer_t* timer) +{ + free(*timer); + *timer = NULL; +} + +void +timer_time(ttimer_t timer) +{ + timer->thread = xbt_os_thread_create("", timer_start_routine, timer); +} + +static void* +timer_start_routine(void* p) +{ + ttimer_t timer = (ttimer_t)p; + command_t command = timer->command; + + int now = (int)time(NULL); + int lead_time = now + command->context->timeout; + + xbt_os_sem_release(timer->started); + + while(!command->failed && !command->interrupted && !command->successeded && !timer->timeouted) + { + if(lead_time >= now) + { + xbt_os_thread_yield(); + /*usleep(100);*/ + now = (int)time(NULL); + } + else + { + /*printf("the timer is timeouted\n");*/ + timer->timeouted = 1; + } + } + + if(timer->timeouted && !command->failed && !command->successeded && !command->interrupted) + { + command_handle_failure(command, csr_timeout); + command_kill(command); + exit_code = ETIMEOUT; + + } + + return NULL; +} + +void +timer_wait(ttimer_t timer) +{ + xbt_os_thread_join(timer->thread, NULL); +} diff --git a/tools/tesh2/src/unit.c b/tools/tesh2/src/unit.c new file mode 100644 index 0000000000..1f540d65f5 --- /dev/null +++ b/tools/tesh2/src/unit.c @@ -0,0 +1,757 @@ +#include +#include +#include +#include +#include + + + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + + +/* the unit thread start routine */ +static void* +unit_start(void* p); + +unit_t +unit_new(runner_t runner, suite_t owner, fstream_t fstream) +{ + unit_t unit = xbt_new0(s_unit_t, 1); + + /* set the owner of the unit */ + unit->runner = runner; + + unit->fstream = fstream; + + unit->sem = NULL; + + unit->commands = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)command_free); + + unit->thread = NULL; + + unit->number_of_started_commands = 0; + unit->number_of_interrupted_commands = 0; + unit->number_of_failed_commands = 0; + unit->number_of_successeded_commands = 0; + unit->number_of_terminated_commands = 0; + unit->interrupted = 0; + unit->failed = 0; + unit->successeded = 0; + unit->parsed = 0; + unit->released = 0; + unit->parsing_include_file = 0; + + + unit->owner = owner; + unit->number = 0; + unit->suites = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)suite_free); + unit->owner = owner; + + unit->running_suite = 0; + + return unit; + +} + +void +unit_add_suite(unit_t unit, suite_t suite) +{ + vector_push_back(unit->suites, suite); +} + +int +unit_free(void** unitptr) +{ + unit_t* __unitptr = (unit_t*)unitptr; + + vector_free(&((*__unitptr)->commands)); + + vector_free(&((*__unitptr)->suites)); + + /* if the unit is interrupted during its run, the semaphore is NULL */ + if((*__unitptr)->sem) + xbt_os_sem_destroy((*__unitptr)->sem); + + + free((*__unitptr)->suites); + + free(*__unitptr); + + *__unitptr = NULL; + + return 0; +} + +static void* +unit_start(void* p) +{ + + xbt_os_thread_t thread; + xbt_os_mutex_t mutex; + context_t context; + int i; + + unit_t unit = (unit_t)p; + + xbt_os_mutex_acquire(unit->mutex); + unit->runner->number_of_runned_units++; + xbt_os_mutex_release(unit->mutex); + + /* try to acquire the jobs semaphore to start */ + xbt_os_sem_acquire(jobs_sem); + + mutex = xbt_os_mutex_init(); + context = context_new(); + + if(want_dry_run) + INFO1("checking unit %s...",unit->fstream->name); + + /* parse the file */ + unit_parse(unit, context, mutex, unit->fstream->name, unit->fstream->stream); + + /* if the unit is not interrupted and not failed the unit, all the file is parsed + * so all the command are launched + */ + if(!unit->interrupted) + { + unit->parsed = 1; + + /* all the commands have terminate before the end of the parsing of the tesh file + * so the unit release the semaphore itself + */ + if(!unit->released && (unit->number_of_started_commands == (unit->number_of_failed_commands + unit->number_of_interrupted_commands + unit->number_of_successeded_commands))) + xbt_os_sem_release(unit->sem); + + } + + /* wait the end of all the commands or a command failure or an interruption */ + + + xbt_os_sem_acquire(unit->sem); + + if(unit->interrupted) + { + command_t command; + + /* interrupt all the running commands of the unit */ + for(i = 0; i < unit->number_of_commands; i++) + { + /*command = unit->commands[i];*/ + command = vector_get_at(unit->commands, i); + + if(command->status == cs_in_progress) + /*command_interrupt(unit->commands[i]);*/ + command_interrupt(command); + } + } + + + /* wait the end of the threads */ + for(i = 0; i < unit->number_of_commands; i++) + { + /*thread = unit->commands[i]->thread;*/ + + command_t command = vector_get_at(unit->commands, i); + thread = command->thread; + + if(thread) + xbt_os_thread_join(thread,NULL); + } + + context_free(&context); + + xbt_os_mutex_destroy(mutex); + + xbt_os_mutex_acquire(unit->mutex); + + /* increment the number of ended units */ + unit->runner->number_of_ended_units++; + + /* it's the last unit, release the runner */ + if(/*!unit->interrupted &&*/ (unit->runner->number_of_runned_units == unit->runner->number_of_ended_units)) + { + if(unit->number_of_successeded_commands == unit->number_of_commands) + unit->successeded = 1; + + /* first release the mutex */ + xbt_os_mutex_release(unit->mutex); + xbt_os_sem_release(units_sem); + } + else + xbt_os_mutex_release(unit->mutex); + + /* release the jobs semaphore, then the next unit can start */ + xbt_os_sem_release(jobs_sem); + + return NULL; + +} + +void +unit_parse(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, FILE* stream) +{ + size_t len; + char * line = NULL; + int line_num=0; + char file_pos[256]; + xbt_strbuff_t buff; + int buffbegin = 0; + + /* Count the line length while checking wheather it's blank */ + int blankline; + int linelen; + /* Deal with \ at the end of the line, and call handle_line on result */ + int to_be_continued; + + buff=xbt_strbuff_new(); + + while(!unit->interrupted && getline(&line, &len, stream) != -1) + { + blankline=1; + linelen = 0; + to_be_continued = 0; + + line_num++; + + while(line[linelen] != '\0') + { + if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n' && line[linelen]!='\r') + blankline = 0; + + linelen++; + } + + if(blankline) + { + if(!context->command_line && (context->input->used || context->output->used)) + { + ERROR1("[%d] Error: no command found in this chunk of lines.",buffbegin); + + if(unit->parsing_include_file) + ERROR1("Unit `%s': NOK (syntax error)", unit->fstream->name); + else + ERROR2("Unit `%s' inclued in `%s' : NOK (syntax error)", file_name, unit->fstream->name); + + exit_code = ESYNTAX; + unit_handle_failure(unit); + break; + } + + if(context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + + continue; + + } + + if(linelen>1 && line[linelen-2]=='\\') + { + if (linelen>2 && line[linelen-3] == '\\') + { + /* Damn. Escaped \ */ + line[linelen-2] = '\n'; + line[linelen-1] = '\0'; + } + else + { + to_be_continued = 1; + line[linelen-2] = '\0'; + linelen -= 2; + + if (!buff->used) + buffbegin = line_num; + } + } + + if(buff->used || to_be_continued) + { + xbt_strbuff_append(buff,line); + + if (!to_be_continued) + { + snprintf(file_pos,256,"%s:%d",file_name,buffbegin); + unit_handle_line(unit, context, mutex, file_pos, buff->data); + xbt_strbuff_empty(buff); + } + } + else + { + snprintf(file_pos,256,"%s:%d",file_name,line_num); + unit_handle_line(unit, context, mutex, file_pos, line); + } + } + + /* Check that last command of the file ran well */ + if(context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + /* Clear buffers */ + if (line) + free(line); + + xbt_strbuff_free(buff); +} + +void +unit_handle_line(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char * filepos, char *line) +{ + /* Search end */ + xbt_str_rtrim(line+2,"\n"); + + switch (line[0]) + { + case '#': + break; + + case '$': + case '&': + + context->async = (line[0] == '&'); + + /* further trim useless chars which are significant for in/output */ + xbt_str_rtrim(line+2," \t"); + + /* Deal with CD commands here, not in rctx */ + if (!strncmp("cd ",line+2,3)) + { + char *dir=line+4; + + if (context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + /* search begining */ + while (*(dir++) == ' '); + + dir--; + + VERB1("Saw cd '%s'",dir); + + /*if(want_dry_run) + { + unit->number_of_successeded_commands++; + }*/ + if(!want_dry_run) + { + if(chdir(dir)) + { + ERROR2("Chdir to %s failed: %s",dir,strerror(errno)); + ERROR1("Test suite `%s': NOK (system error)", unit->fstream->name); + exit_code = ECHDIR; + unit_handle_failure(unit); + } + } + + break; + } /* else, pushline */ + else + { + unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/); + break; + } + + case '<': + case '>': + case '!': + unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/); + break; + + case 'p': + if(!want_dry_run) + INFO2("[%s] %s",filepos,line+2); + break; + + case 'P': + if(!want_dry_run) + CRITICAL2("[%s] %s",filepos,line+2); + break; + + default: + ERROR2("[%s] Syntax error: %s",filepos, line); + ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name); + exit_code = ESYNTAX; + unit_handle_failure(unit); + break; + } +} + +void +unit_pushline(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* filepos, char kind, char *line) +{ + switch (kind) + { + case '$': + case '&': + + if(context->command_line) + { + command_t command; + + if(context->output->used || context->input->used) + { + ERROR2("[%s] More than one command in this chunk of lines (previous: %s).\nDunno which input/output belongs to which command.",filepos,context->command_line); + ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name); + exit_code = ESYNTAX; + unit_handle_failure(unit); + return; + } + + if(!want_dry_run) + { + command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + + VERB1("[%s] More than one command in this chunk of lines",filepos); + } + + context->command_line = strdup(line); + + + context->line = strdup(filepos); + /*INFO2("[%s] %s",filepos,context->command_line);*/ + + break; + + case '<': + xbt_strbuff_append(context->input,line); + xbt_strbuff_append(context->input,"\n"); + break; + + case '>': + xbt_strbuff_append(context->output,line); + xbt_strbuff_append(context->output,"\n"); + break; + + case '!': + + if(context->command_line) + { + if(!want_dry_run) + { + command_t command = command_new(unit, context, mutex); + command_run(command); + } + + context_reset(context); + } + + if(!strncmp(line,"timeout no",strlen("timeout no"))) + { + VERB1("[%s] (disable timeout)", filepos); + context->timeout = INDEFINITE; + } + else if(!strncmp(line,"timeout ",strlen("timeout "))) + { + int i = 0; + char* p = line + strlen("timeout "); + + while(p[i] != '\0') + { + if(!isdigit(p[i])) + { + exit_code = ESYNTAX; + ERROR2("Invalid timeout value `%s' at %s ", line + strlen("timeout "), filepos); + unit_handle_failure(unit); + } + + i++; + } + + context->timeout = atoi(line + strlen("timeout")); + VERB2("[%s] (new timeout value: %d)",filepos,context->timeout); + + } + else if (!strncmp(line,"expect signal ",strlen("expect signal "))) + { + context->signal = strdup(line + strlen("expect signal ")); + + #ifdef WIN32 + if(!strstr(context->signal,"SIGSEGVSIGTRAPSIGBUSSIGFPESIGILL")) + { + exit_code = ESYNTAX; + /*ERROR2("Signal `%s' not supported at %s", line + strlen("expect signal "), filepos);*/ + unit_handle_failure(unit); + } + #endif + + xbt_str_trim(context->signal," \n"); + VERB2("[%s] (next command must raise signal %s)", filepos, context->signal); + + } + else if (!strncmp(line,"expect return ",strlen("expect return "))) + { + + int i = 0; + char* p = line + strlen("expect return "); + + + while(p[i] != '\0') + { + if(!isdigit(p[i])) + { + exit_code = ESYNTAX; + ERROR2("Invalid exit code value `%s' at %s ", line + strlen("expect return "), filepos); + unit_handle_failure(unit); + } + + i++; + } + + context->exit_code = atoi(line+strlen("expect return ")); + VERB2("[%s] (next command must return code %d)",filepos, context->exit_code); + + } + else if (!strncmp(line,"output ignore",strlen("output ignore"))) + { + context->output_handling = oh_ignore; + VERB1("[%s] (ignore output of next command)", filepos); + + } + else if (!strncmp(line,"output display",strlen("output display"))) + { + context->output_handling = oh_display; + VERB1("[%s] (ignore output of next command)", filepos); + + } + else if(!strncmp(line,"include", strlen("include"))) + { + unit_handle_include(unit, context, mutex, line + strlen("include ")); + } + + else if(!strncmp(line,"suite", strlen("suite"))) + { + unit_handle_suite(unit, context, mutex, line + strlen("suite ")); + } + else + { + ERROR2("%s: Malformed metacommand: %s",filepos,line); + ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name); + exit_code = ESYNTAX; + unit_handle_failure(unit); + return; + } + + break; + } +} + + +void +unit_handle_failure(unit_t unit) +{ + if(!want_keep_going_unit) + { + if(!unit->interrupted) + { + /* the unit interrupted (exit for the loop) */ + unit->interrupted = 1; + + /* release the unit */ + xbt_os_sem_release(unit->sem); + } + + /* if the --keep-going option is not specified */ + if(!want_keep_going) + { + if(!interrupted) + { + /* request an global interruption by the runner */ + interrupted = 1; + + /* release the runner */ + xbt_os_sem_release(units_sem); + } + } + } +} + +void +unit_run(unit_t unit, xbt_os_mutex_t mutex) +{ + if(!interrupted) + { + unit->mutex = mutex; + + unit->sem = xbt_os_sem_init(0); + + /* start the unit */ + unit->thread = xbt_os_thread_create("", unit_start, unit); + } + else + /* the unit is interrupted by the runner before its starting + * in this case the unit semaphore is NULL take care of that + * in the function unit_free() + */ + unit->interrupted = 1; + + +} + +void +unit_interrupt(unit_t unit) +{ + /* interrupt the loop */ + unit->interrupted = 1; + xbt_os_sem_release(unit->sem); +} + +void +unit_verbose(unit_t unit) +{ + int i, test_count; + command_t command; + + printf("\nUnit : %s (%s)\n", unit->fstream->name, unit->fstream->directory); + printf("Status informations :"); + + if(unit->parsed && unit->number_of_successeded_commands == unit->number_of_started_commands) + printf(" - (success)"); + else + { + if(unit->interrupted) + printf(" - (interruped)"); + else + printf(" - (failed)"); + } + + printf("\n"); + + printf(" number of commands : %d\n", unit->number_of_commands); + printf(" number of runned commands : %d\n", unit->number_of_started_commands); + printf(" number of successeded commands : %d\n", unit->number_of_successeded_commands); + printf(" number of failed commands : %d\n", unit->number_of_failed_commands); + printf(" number of interrupted commands : %d\n", unit->number_of_interrupted_commands); + + test_count = unit->number_of_commands; + + + for(i = 0; i < test_count; i++) + { + command = vector_get_at(unit->commands, i); + + /*command_display_status(unit->commands[i]);*/ + command_display_status(command); + } + + +} + +void +unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name) +{ + directory_t include; + char* prev_directory = NULL; + fstream_t fstream = NULL; + struct stat buffer = {0}; + + + if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode)) + { + INFO1("the file stream %s is in the current directory", file_name); + + fstream = fstream_new(getcwd(NULL, 0), file_name); + fstream_open(fstream); + } + /* the file to include is not in the current directory, check if it is in a include directory */ + else + { + prev_directory = getcwd(NULL, 0); + + vector_rewind(includes); + + while((include = vector_get(includes))) + { + chdir(include->name); + + if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode)) + { + fstream = fstream_new(include->name, file_name); + fstream_open(fstream); + break; + } + + + vector_move_next(includes); + } + + chdir(prev_directory); + free(prev_directory); + } + + + + /* the file to include is not found handle the failure */ + if(!fstream) + { + exit_code = EINCLUDENOTFOUND; + ERROR1("Include file %s not found",file_name); + unit_handle_failure(unit); + } + else + { + /* parse the include file */ + + /*unit_t __unit = unit_new(unit->runner, NULL, fstream);*/ + unit->parsing_include_file = 1; + + /* add the unit to the list of the runned units */ + + /*xbt_os_mutex_acquire(unit->mutex); + vector_push_back(__unit->runner->units->items, __unit); + xbt_os_mutex_release(unit->mutex);*/ + + + if(want_dry_run) + INFO2("checking unit %s including in %s...",fstream->name, unit->fstream->name); + + unit_parse(unit, context_new(), mutex, fstream->name, fstream->stream); + + fstream_free((void**)&fstream); + unit->parsing_include_file = 0; + } +} + +void +unit_handle_suite(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* description) +{ + suite_t suite = suite_new(unit, description); + unit_add_suite(unit, suite); + unit->running_suite = 1; +} + +int +unit_reset(unit_t unit) +{ + fseek(unit->fstream->stream,0L, SEEK_SET); + unit->parsed = 0; + unit->number_of_commands = 0; + + return 0; +} diff --git a/tools/tesh2/src/units.c b/tools/tesh2/src/units.c new file mode 100644 index 0000000000..500846ffdc --- /dev/null +++ b/tools/tesh2/src/units.c @@ -0,0 +1,205 @@ +#include +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +units_t +units_new(runner_t runner, fstreams_t fstreams) +{ + register fstream_t fstream; + + units_t units = xbt_new0(s_units_t, 1); + + if(!(units->items = vector_new(fstreams_get_size(fstreams), unit_free))) + { + free(units); + return NULL; + } + + + vector_rewind(fstreams->items); + + while((fstream = vector_get(fstreams->items))) + { + if((errno = vector_push_back(units->items, unit_new(runner, NULL, fstream)))) + { + vector_free(&(units->items)); + free(units); + return NULL; + } + + vector_move_next(fstreams->items); + } + + return units; +} + + +int +units_is_empty(units_t units) +{ + if(!units) + { + errno = EINVAL; + return 0; + } + + return vector_is_empty(units->items); +} + +int +units_get_size(units_t units) +{ + if(!units) + { + errno = EINVAL; + return -1; + } + + return vector_get_size(units->items); +} + + +int +units_run_all(units_t units, xbt_os_mutex_t mutex) +{ + register unit_t unit; + + if(!units) + return EINVAL; + + if(vector_is_empty(units->items)) + return EAGAIN; + + /* move the cursor at the begining of the vector */ + vector_rewind(units->items); + + /* run all the units */ + while((unit = vector_get(units->items))) + { + unit_run(unit, mutex); + vector_move_next(units->items); + + } + + return 0; + +} + +int +units_join_all(units_t units) +{ + register unit_t unit; + + if(!units) + return EINVAL; + + if(vector_is_empty(units->items)) + return EAGAIN; + + /* move the cursor at the begining of the vector */ + vector_rewind(units->items); + + /* run all the units */ + while((unit = vector_get(units->items))) + { + if(unit->thread) + xbt_os_thread_join(unit->thread, NULL); + + vector_move_next(units->items); + } + + return 0; +} + +int +units_interrupt_all(units_t units) +{ + register unit_t unit; + + if(!units) + return EINVAL; + + if(vector_is_empty(units->items)) + return EAGAIN; + + /* move the cursor at the begining of the vector */ + vector_rewind(units->items); + + /* run all the units */ + while((unit = vector_get(units->items))) + { + if(!(unit->successeded) && !(unit->interrupted)) + unit_interrupt(unit); + + vector_move_next(units->items); + } + + return 0; +} + +int +units_verbose(units_t units) +{ + register unit_t unit; + + if(!units) + return EINVAL; + + if(vector_is_empty(units->items)) + return EAGAIN; + + /* move the cursor at the begining of the vector */ + vector_rewind(units->items); + + /* run all the units */ + while((unit = vector_get(units->items))) + { + unit_verbose(unit); + vector_move_next(units->items); + } + + return 0; + +} + +int +units_reset_all(units_t units) +{ + register unit_t unit; + + if(!units) + return EINVAL; + + if(vector_is_empty(units->items)) + return EAGAIN; + + /* move the cursor at the begining of the vector */ + vector_rewind(units->items); + + /* run all the units */ + while((unit = vector_get(units->items))) + { + unit_reset(unit); + vector_move_next(units->items); + } + + return 0; +} + +int +units_free(void** unitsptr) +{ + if(!(*unitsptr)) + return EINVAL; + + if((errno = vector_free(&((*((units_t*)unitsptr))->items)))) + return errno; + + free(*unitsptr); + *unitsptr = NULL; + + return 0; +} + diff --git a/tools/tesh2/src/variable.c b/tools/tesh2/src/variable.c new file mode 100644 index 0000000000..e2f66a716d --- /dev/null +++ b/tools/tesh2/src/variable.c @@ -0,0 +1,8 @@ +#include + +typedef struct s_variable +{ + char* name; + char* val; + int used; +}s_variable_t; \ No newline at end of file diff --git a/tools/tesh2/src/variables.c b/tools/tesh2/src/variables.c new file mode 100644 index 0000000000..dff1472f1e --- /dev/null +++ b/tools/tesh2/src/variables.c @@ -0,0 +1,6 @@ +#include + +typedef struct s_variables +{ + dictionary_t items; +}s_variables_t; \ No newline at end of file diff --git a/tools/tesh2/src/vector.c b/tools/tesh2/src/vector.c new file mode 100644 index 0000000000..ca8f8907de --- /dev/null +++ b/tools/tesh2/src/vector.c @@ -0,0 +1,971 @@ + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#define inline _inline +#endif + +static inline int +resize(vector_t vector) +{ + return vector_reserve(vector, !(vector->capacity) ? 1 : (vector->size) << 1); +} + +static inline int +move(vector_t vector, int dst,int src, int size) +{ + return (NULL != memmove(vector->items + dst, vector->items + src,size * sizeof(void*))); +} + +vector_t +vector_new(int capacity, fn_finalize_t fn_finalize) +{ + vector_t vector; + + if(capacity < 0) + { + errno = EINVAL; + return NULL; + } + + if(!(vector = (vector_t)calloc(1,sizeof(s_vector_t)))) + return NULL; + + if(capacity) + { + if(!(vector->items = (void**)calloc(capacity,sizeof(void*)))) + { + free(vector); + return NULL; + } + + vector->capacity = capacity; + } + + vector->fn_finalize = fn_finalize; + vector->pos = -1; + return vector; +} + +int +vector_clear(vector_t vector) +{ + if(!vector) + return EINVAL; + + if(!vector->size) + return EAGAIN; + + if(vector->fn_finalize) + { + int size = vector->size; + fn_finalize_t fn_finalize = vector->fn_finalize; + void** items = vector->items; + register int pos; + + for(pos = 0; pos < size; pos++) + { + if((errno = (*(fn_finalize))(&(items[pos])))) + return errno; + else + vector->size--; + } + } + else + { + vector->size = 0; + } + + vector->pos = -1; + + return 0; +} + +int +vector_free(vector_t* vector_ptr) +{ + if(!(*vector_ptr)) + return EINVAL; + + if((errno = vector_clear(*vector_ptr))) + return errno; + + free(*vector_ptr); + *vector_ptr = NULL; + + return 0; +} + +int +vector_is_empty(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + return !vector->size; +} + +int +vector_get_size(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return -1; + } + + return vector->size; +} + +void* +vector_get_front(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size) + { + errno = EAGAIN; + return NULL; + } + + return vector->items[0]; +} + +void* +vector_get_back(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size) + { + errno = EAGAIN; + return NULL; + } + + return vector->items[vector->size - 1]; +} + +int +vector_get_capacity_available(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return -1; + } + + return (vector->capacity - vector->size); +} + +int +vector_push_back(vector_t vector, void* item) +{ + if(!vector || !item) + return EINVAL; + + + /* if all capacity is used, resize the vector */ + if(vector->capacity <= vector->size) + { + if(!resize(vector)) + return errno; + } + + /* increment the item count and push the new item at the end of the vector */ + vector->items[++(vector->size) - 1] = item; + + vector->pos = -1; + + return 0; +} + +void* +vector_pop_back(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size) + { + errno = EAGAIN; + return NULL; + } + + vector->pos = -1; + + return vector->items[(vector->size)-- - 1]; +} + +int +vector_get_upper_bound(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return -1; + } + + if(!vector->size) + { + errno = EAGAIN; + return -1; + } + + return (vector->size - 1); +} + +void* +vector_set_at(vector_t vector, int pos, void* item) +{ + void* prev_item; + + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size) + { + errno = EAGAIN; + return NULL; + } + + if((pos < 0) || (pos >= vector->size)) + { + errno = ERANGE; + return NULL; + } + + prev_item = vector->items[pos]; + vector->items[pos] = item; + return prev_item; +} + +int +vector_insert(vector_t vector, int pos, void* item) +{ + if(!vector) + return EINVAL; + + if(!vector->size) + { + errno = EAGAIN; + return 0; + } + + if((pos < 0) || (pos >= vector->size)) + { + errno = ERANGE; + return 0; + } + + if(vector->size >= vector->capacity) + { + if(!resize(vector)) + return errno; + } + + if(vector->size) + { + if(!move(vector, pos + 1, pos, vector->size - pos)) + return errno; + } + + vector->size++; + vector->items[pos] = item; + vector->pos = -1; + + return 0; +} + +int +vector_erase_at(vector_t vector, int pos) +{ + if(!vector) + return EINVAL; + + if(!vector->size) + { + errno = EAGAIN; + return 0; + } + + if((pos < 0) || (pos >= vector->size)) + { + errno = ERANGE; + return 0; + } + + if(vector->fn_finalize) + { + if((errno = (*(vector->fn_finalize))(&(vector->items[pos])))) + return errno; + } + + if(pos != (vector->size - 1)) + { + if(!move(vector, pos, pos + 1, (vector->size - (pos + 1)))) + return errno; + } + + vector->size--; + vector->pos = -1; + + return 0; +} + +int +vector_erase(vector_t vector, void* item) +{ + int pos; + + if(!vector || !item) + return EINVAL; + + if(!vector->size) + return EAGAIN; + + if(-1 == (pos = vector_search(vector,item))) + return errno; + + if(vector->fn_finalize) + { + if((errno = (*(vector->fn_finalize))(&item))) + return errno; + } + + if(pos != (vector->size - 1)) + if(!move(vector, pos, pos + 1, (vector->size - (pos + 1)))) + return errno; + + vector->size--; + + vector->pos = -1; + + return 0; +} + +int +vector_erase_range(vector_t vector, int first, int last) +{ + register int width; + + if(!vector || first >= last) + return EINVAL; + + if(vector->size < 2) + return EAGAIN; + + if((first < 0) || (last < 0) || (last >= vector->size)) + return ERANGE; + + width = (last - first) + 1; + + while(width--) + { + if((errno = vector_erase_at(vector,first))) + return errno; + } + + return 0; +} + +int +vector_search(vector_t vector, void* item) +{ + register int pos; + void** items; + int size; + + if(!vector || !item) + { + errno = EINVAL; + return -1; + } + + if(!vector->size) + { + errno = EAGAIN; + return -1; + } + + vector = (vector_t)vector; + + items = vector->items; + size = vector->size; + + for(pos = 0 ; pos < size; pos++) + { + if(items[pos] == item) + return pos; + } + + errno = ESRCH; + return -1; +} + +int +vector_remove(vector_t vector, void* item) +{ + int pos; + + if(!vector || !item) + return EINVAL; + + if(!vector->size) + return EAGAIN; + + if(-1 == (pos = vector_search(vector,item))) + return errno; + + if(pos != (vector->size - 1)) + if(!move(vector, pos, pos + 1, (vector->size - (pos + 1)))) + return errno; + + vector->size--; + + vector->pos = -1; + + return 0; +} + +int +vector_assign(vector_t dst,vector_t src) +{ + register int pos; + int size; + void** items; + + + if(!dst || !src ||(dst == src)) + return EINVAL; + + if(!src->size) + return EAGAIN; + + size = src->size; + + /* if the destination vector has not enough capacity resize it */ + if(size > dst->capacity) + { + if((errno = vector_reserve(dst, size - dst->capacity))) + return errno; + } + + /* clear the destination vector */ + if((errno = vector_clear(dst))) + return errno; + + dst->fn_finalize = NULL; + + items = src->items; + + /* file the destination vector */ + for(pos = 0; pos < size; pos++) + if((errno = vector_push_back(dst,items[pos]))) + return errno; + + dst->pos = -1; + + return 0; +} + +int +vector_get_capacity(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return -1; + } + + return vector->capacity; +} + +int +vector_equals(vector_t first, vector_t second) +{ + register int pos; + int size; + void** __first, ** __second; + + + if(!first || !second || (first == second)) + { + errno = EINVAL; + return 0; + } + + if(first->size != second->size) + return 0; + + size = first->size; + __first = first->items; + __second = second->items; + + for(pos = 0; pos < size; pos++) + { + if(__first[pos] != __second[pos]) + return 0; + } + + return 1; +} + +vector_t +vector_clone(vector_t vector) +{ + int size; + register int pos; + void** dst,** src; + vector_t clone; + + if(!vector) + { + errno = EINVAL; + return NULL; + } + + clone = vector_new(vector->capacity,NULL); + + if(!clone) + return NULL; + + size = vector->size; + src = vector->items; + dst = clone->items; + + for(pos = 0; pos < size; pos++) + dst[pos] = src[pos]; + + clone->size = size; + + return clone; +} + +int +vector_contains(vector_t vector,void* item) +{ + if(!vector || !item) + { + errno = EINVAL; + return 0; + } + + return (-1 != vector_search(vector,item)); +} + +int +vector_swap(vector_t first,vector_t second) +{ + s_vector_t tmp; + + if(!first || !second) + return EINVAL; + + /* save the content or first */ + tmp = *first; + + /* copy second in first */ + if(!memcpy(first, second, sizeof(s_vector_t))) + return 0; + + /* copy tmp in first */ + if(!memcpy(second, &tmp, sizeof(s_vector_t))) + { + *first = tmp; + return 0; + } + + return 1; +} + +int +vector_reserve(vector_t vector, int size) +{ + void** items; + + if(!vector || (size < 0)) + return EINVAL; + + if(vector->capacity >= size) + return EAGAIN; + + if(!(items = (void**)realloc(vector->items, size * sizeof(void*)))) + return errno; + + vector->capacity = size; + vector->items = items; + + return 0; +} + +int +vector_is_autodelete(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + return NULL != vector->fn_finalize; +} + +int +vector_has_capacity_available(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + return (vector->capacity > vector->size); +} + +int +vector_is_full(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + + return (vector->capacity == vector->size); +} + +int +vector_get_max_index(vector_t vector) +{ + + if(!vector) + { + errno = EINVAL; + return -1; + } + + + return vector->size - 1; +} + +void* +vector_get(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return NULL; + } + return vector->items[vector->pos]; +} + +void* +vector_get_at(vector_t vector, int pos) +{ + if(!vector) + { + errno = EINVAL; + return NULL; + } + + if(pos < 0 || pos >= vector->size) + { + errno = ERANGE; + return NULL; + } + + if(!vector->size) + { + errno = EAGAIN; + return NULL; + } + + + return vector->items[pos]; +} + +int +vector_getpos(vector_t vector, int* pos) +{ + + if(!vector || !pos) + { + errno = EINVAL; + return 0; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return 0; + } + + *pos = vector->pos; + return 1; +} + +int +vector_move_next(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return 0; + } + + if(vector->pos < (vector->size - 1)) + { + vector->pos++; + return 1; + } + + vector->pos = -1; + errno = ERANGE; + return 0; +} + +int +vector_move_prev(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return 0; + } + + if(vector->pos > 0) + { + vector->pos--; + return 1; + } + + vector->pos = -1; + errno = ERANGE; + return 0; + +} + +int +vector_rewind(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + if(!vector->size) + { + errno = EAGAIN; + return 0; + } + + vector->pos = 0; + + return 1; +} + +static int +seek_set(vector_t vector, int offset) +{ + if(offset > vector->size) + return EINVAL; + + vector_rewind(vector); + + while(offset--) + vector_move_next(vector); + + return 0; +} + +static int +seek_end(vector_t vector, int offset) +{ + if(offset > vector->size) + return EINVAL; + + vector_unwind(vector); + + while(offset--) + vector_move_prev(vector); + + return 0; +} + + +static int +seek_cur(vector_t vector, int offset) +{ + if(vector->pos == 0) + { + /* we are at the begin of the vector */ + seek_set(vector, offset); + } + else if(vector->pos == vector->size - 1) + { + /* we are at the end of the vector */ + seek_end(vector, offset); + } + else + { + if(offset > (vector->size - vector->pos + 1)) + return EINVAL; + + while(offset--) + vector_move_next(vector); + + } + + return 0; +} + +int +vector_seek(vector_t vector, int offset, int whence) +{ + if(!vector) + return EINVAL; + + if(!vector->size) + return EAGAIN; + + switch(whence) + { + case SEEK_SET : + return seek_set(vector, offset); + + case SEEK_CUR : + return seek_cur(vector, offset); + + case SEEK_END : + return seek_end(vector, offset); + + } + + return EINVAL; +} + +void* +vector_set(vector_t vector, void* item) +{ + void* prev_item; + + if(!vector || !item) + { + errno = EINVAL; + return NULL; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return NULL; + } + + prev_item = vector->items[vector->pos]; + vector->items[vector->pos] = item; + + return prev_item; +} + +int +vector_setpos(vector_t vector, int pos) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + if(pos < 0 || pos >= vector->size) + { + errno = ERANGE; + return 0; + } + + if(!vector->size) + { + errno = EAGAIN; + return 0; + } + + vector->pos = pos; + return 1; +} + +int +vector_tell(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return -1; + } + + if(!vector->size || (-1 == vector->pos)) + { + errno = EAGAIN; + return -1; + } + + return vector->pos; +} + +int +vector_unwind(vector_t vector) +{ + if(!vector) + { + errno = EINVAL; + return 0; + } + + if(!vector->size) + { + errno = EAGAIN; + return 0; + } + + vector->pos = vector->size - 1; + return 1; +} diff --git a/tools/tesh2/src/writer.c b/tools/tesh2/src/writer.c new file mode 100644 index 0000000000..97aa2dcc1e --- /dev/null +++ b/tools/tesh2/src/writer.c @@ -0,0 +1,215 @@ +#include +#include + +XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh); + +static void* +writer_start_routine(void* p); + +writer_t +writer_new(command_t command) +{ + writer_t writer = xbt_new0(s_writer_t, 1); + + writer->thread = NULL; + writer->command = command; + writer->started = xbt_os_sem_init(0); + + return writer; +} + +void +writer_free(writer_t* writer) +{ + free(*writer); + *writer = NULL; +} + +void +writer_write(writer_t writer) +{ + writer->thread = xbt_os_thread_create("", writer_start_routine, writer); +} + +#ifdef WIN32 +static void* +writer_start_routine(void* p) +{ + writer_t writer = (writer_t)p; + command_t command = writer->command; + + char* input = (char*)(command->context->input->data); + + DWORD number_of_bytes_to_write = command->context->input->used; + DWORD number_of_bytes_written = 0; + + + while(!command->failed && !command->interrupted && !command->successeded && ! writer->failed && ! writer->broken_pipe && number_of_bytes_to_write) + { + if(!WriteFile(writer->command->stdin_fd, input, number_of_bytes_to_write, &number_of_bytes_written, NULL)) + { + if(GetLastError() == ERROR_NO_DATA) + writer->broken_pipe = 1; + else + writer->failed = 1; + + } + else + { + input += number_of_bytes_written; + number_of_bytes_to_write -= number_of_bytes_written; + } + } + + command->context->input->data[0]='\0'; + command->context->input->used=0; + + if(writer->failed && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + exit_code = EWRITE; + command_handle_failure(command, csr_write_failure); + } + /*else if(writer->broken_pipe && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + command_handle_failure(command, csr_write_pipe_broken); + }*/ + + + return NULL; +} +#else +/*static void* +writer_start_routine(void* p) +{ + writer_t writer = (writer_t)p; + command_t command = writer->command; + int number_of_bytes_written = 0; + int number_of_bytes_to_write = command->context->input->used; + char* input = (char*)(command->context->input->data); + int got; + + xbt_os_sem_release(writer->started); + + + + while(!command->failed && !command->interrupted && !command->successeded && ! writer->failed && ! writer->broken_pipe && (number_of_bytes_written < number_of_bytes_to_write)) + { + got = write(writer->command->stdin_fd, input + number_of_bytes_written, number_of_bytes_to_write - number_of_bytes_written); + + if(got > 0) + number_of_bytes_written += got; + + if(got < 0) + { + if(errno == EPIPE) + { + writer->broken_pipe = 1; + } + else if(errno != EINTR && errno != EAGAIN && errno != EPIPE) + { + writer->failed = 1; + } + } + + if(got <= 0) + usleep(100); + } + + command->context->input->data[0]='\0'; + command->context->input->used=0; + + if(writer->failed && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + exit_code = EWRITE; + command_handle_failure(command, csr_write_failure); + } + else if(writer->broken_pipe && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + exit_code = EWRITEPIPE; + command_handle_failure(command, csr_write_pipe_broken); + } + + + close(command->stdin_fd); + command->stdin_fd = INDEFINITE_FD; + + return NULL; +}*/ + +static void* +writer_start_routine(void* p) +{ + writer_t writer = (writer_t)p; + command_t command = writer->command; + long number_of_bytes_to_write = command->context->input->used; + char* input = (char*)(command->context->input->data); + int got; + + xbt_os_sem_release(writer->started); + + while(!command->failed && !command->interrupted && !command->successeded && number_of_bytes_to_write > 0) + { + got = number_of_bytes_to_write > SSIZE_MAX ? SSIZE_MAX : number_of_bytes_to_write; + got = write( writer->command->stdin_fd, input, got ); + + if(got < 0) + { + if(EINTR == errno || EAGAIN == errno) + { + continue; + } + else if(EPIPE == errno) + { + writer->broken_pipe = 1; + break; + } + else + { + writer->failed = 1; + break; + } + + } + + number_of_bytes_to_write -= got; + input += got; + + if(got == 0) + usleep(100); + } + + command->context->input->data[0]='\0'; + command->context->input->used=0; + + if(writer->failed && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + exit_code = EWRITE; + command_handle_failure(command, csr_write_failure); + } + else if(writer->broken_pipe && !command->successeded && !command->failed && !command->interrupted) + { + command_kill(command); + exit_code = EWRITEPIPE; + command_handle_failure(command, csr_write_pipe_broken); + } + + + close(command->stdin_fd); + command->stdin_fd = INDEFINITE_FD; + + return NULL; + +} + +#endif +void +writer_wait(writer_t writer) +{ + xbt_os_thread_join(writer->thread, NULL); + +}