Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
tesh version 2
authorcherierm <cherierm@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Thu, 13 Mar 2008 13:46:37 +0000 (13:46 +0000)
committercherierm <cherierm@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Thu, 13 Mar 2008 13:46:37 +0000 (13:46 +0000)
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@5289 48e7efb5-ca39-0410-a469-dd3cf9ba447f

52 files changed:
tools/tesh2/include/_signal.h [new file with mode: 0644]
tools/tesh2/include/allocator.h [new file with mode: 0644]
tools/tesh2/include/com.h [new file with mode: 0644]
tools/tesh2/include/command.h [new file with mode: 0644]
tools/tesh2/include/context.h [new file with mode: 0644]
tools/tesh2/include/def.h [new file with mode: 0644]
tools/tesh2/include/dictionary.h [new file with mode: 0644]
tools/tesh2/include/directories.h [new file with mode: 0644]
tools/tesh2/include/directory.h [new file with mode: 0644]
tools/tesh2/include/error.h [new file with mode: 0644]
tools/tesh2/include/excludes.h [new file with mode: 0644]
tools/tesh2/include/fstream.h [new file with mode: 0644]
tools/tesh2/include/fstreams.h [new file with mode: 0644]
tools/tesh2/include/global.h [new file with mode: 0644]
tools/tesh2/include/htable.h [new file with mode: 0644]
tools/tesh2/include/lstrings.h [new file with mode: 0644]
tools/tesh2/include/reader.h [new file with mode: 0644]
tools/tesh2/include/runner.h [new file with mode: 0644]
tools/tesh2/include/suite.h [new file with mode: 0644]
tools/tesh2/include/timer.h [new file with mode: 0644]
tools/tesh2/include/types.h [new file with mode: 0644]
tools/tesh2/include/unit.h [new file with mode: 0644]
tools/tesh2/include/units.h [new file with mode: 0644]
tools/tesh2/include/variable.h [new file with mode: 0644]
tools/tesh2/include/variables.h [new file with mode: 0644]
tools/tesh2/include/vector.h [new file with mode: 0644]
tools/tesh2/include/writer.h [new file with mode: 0644]
tools/tesh2/makefile [new file with mode: 0644]
tools/tesh2/src/allocator.c [new file with mode: 0644]
tools/tesh2/src/command.c [new file with mode: 0644]
tools/tesh2/src/context.c [new file with mode: 0644]
tools/tesh2/src/dictionary.c [new file with mode: 0644]
tools/tesh2/src/directories.c [new file with mode: 0644]
tools/tesh2/src/directory.c [new file with mode: 0644]
tools/tesh2/src/error.c [new file with mode: 0644]
tools/tesh2/src/excludes.c [new file with mode: 0644]
tools/tesh2/src/fstream.c [new file with mode: 0644]
tools/tesh2/src/fstreams.c [new file with mode: 0644]
tools/tesh2/src/htable.c [new file with mode: 0644]
tools/tesh2/src/lstrings.c [new file with mode: 0644]
tools/tesh2/src/main.c [new file with mode: 0644]
tools/tesh2/src/reader.c [new file with mode: 0644]
tools/tesh2/src/runner.c [new file with mode: 0644]
tools/tesh2/src/signal.c [new file with mode: 0644]
tools/tesh2/src/suite.c [new file with mode: 0644]
tools/tesh2/src/timer.c [new file with mode: 0644]
tools/tesh2/src/unit.c [new file with mode: 0644]
tools/tesh2/src/units.c [new file with mode: 0644]
tools/tesh2/src/variable.c [new file with mode: 0644]
tools/tesh2/src/variables.c [new file with mode: 0644]
tools/tesh2/src/vector.c [new file with mode: 0644]
tools/tesh2/src/writer.c [new file with mode: 0644]

diff --git a/tools/tesh2/include/_signal.h b/tools/tesh2/include/_signal.h
new file mode 100644 (file)
index 0000000..b629c05
--- /dev/null
@@ -0,0 +1,197 @@
+#ifndef __SIGNAL_H
+#define __SIGNAL_H
+
+#include <signal.h>
+#include <types.h>
+
+#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 (file)
index 0000000..4db00b8
--- /dev/null
@@ -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 (file)
index 0000000..80ff496
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef __GLOBAL_H
+#define __GLOBAL_H
+
+#include <types.h>
+
+#include <ctype.h>
+
+#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 (file)
index 0000000..9f39202
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __COMMAND_H
+#define __COMMAND_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..cc2fe93
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _CONTEXT_H
+#define _CONTEXT_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..d5b45aa
--- /dev/null
@@ -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 <direct.h> /* for getcwd(), _chdir() */
+       
+#endif
+
+
+#include <error.h>
+
+#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 (file)
index 0000000..17bd744
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef __dictionary_H
+#define __dictionary_H
+
+#include <htable.h>
+
+#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 (file)
index 0000000..d000f77
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef __DIRECTORIES_H
+#define __DIRECTORIES_H
+
+
+#include <com.h>
+
+#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 (file)
index 0000000..42fd920
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __DIRECTORY_H
+#define __DIRECTORY_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..6b0eb84
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef __ERROR_H
+#define __ERROR_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+
+#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 (file)
index 0000000..7ba8126
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __EXCLUDES_H
+#define __EXCLUDES_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..689d1cc
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __FSTREAM_H
+#define __FSTREAM_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..7e55aa0
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __FSTREAMS_H
+#define __FSTREAMS_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..aca4c0b
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef __GLOBAL_H
+#define __GLOBAL_H
+
+#include <types.h>
+
+#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 (file)
index 0000000..0814d44
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef __HTABLE
+#define __HTABLE
+
+
+#include <allocator.h>
+
+#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 (file)
index 0000000..948bf2b
--- /dev/null
@@ -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 (file)
index 0000000..fa47ca4
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __READER_H
+#define __READER_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..4c6e097
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __RUNNER_H     
+#define __RUNNER_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..5f175e9
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __SUITE_H      
+#define __SUITE_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..fc1c710
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __TIMER_H
+#define __TIMER_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..6c4cff8
--- /dev/null
@@ -0,0 +1,317 @@
+#ifndef __TYPES_H      
+#define __TYPES_H
+
+#include <def.h>
+
+#include <portable.h>
+#include <xbt/xbt_os_thread.h>
+#include <xbt/strbuff.h>
+#include <xbt.h>
+
+#include <dirent.h>
+
+#include <lstrings.h>
+#include <vector.h>
+#include <dictionary.h>
+
+#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 (file)
index 0000000..3eb17f3
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef __UNIT_H       
+#define __UNIT_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..0791f71
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef __UNITS_H      
+#define __UNITS_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..bd3e5ac
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __VARIABLE_H
+#define __VARIABLE_H
+
+#include <global.h>
+
+#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 (file)
index 0000000..1b522ec
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __VARIABLES_H
+#define __VARIABLES_H
+
+#include <global.h>
+
+#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 (file)
index 0000000..59468ed
--- /dev/null
@@ -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 (file)
index 0000000..7a51250
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __WRITER_H
+#define __WRITER_H
+
+#include <com.h>
+
+#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 (file)
index 0000000..dc120ad
--- /dev/null
@@ -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 (file)
index 0000000..7121ba6
--- /dev/null
@@ -0,0 +1,368 @@
+
+#include <allocator.h>
+
+#include <errno.h>
+#include <string.h>    /* memset                       */
+#include <stdlib.h>    /* 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 (file)
index 0000000..15cc6eb
--- /dev/null
@@ -0,0 +1,890 @@
+
+#include <command.h>
+#include <context.h>
+#include <writer.h>
+#include <reader.h>
+#include <timer.h>
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/wait.h>
+#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("    <killed command>\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 (file)
index 0000000..261d4b7
--- /dev/null
@@ -0,0 +1,139 @@
+#include <context.h>
+
+
+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 (file)
index 0000000..8c0c5b6
--- /dev/null
@@ -0,0 +1,136 @@
+
+#include <dictionary.h>
+#include <htable.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 (file)
index 0000000..83726a7
--- /dev/null
@@ -0,0 +1,199 @@
+#include <directories.h>
+#include <directory.h>
+
+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 (file)
index 0000000..6033aa0
--- /dev/null
@@ -0,0 +1,184 @@
+#include <directory.h>
+#include <fstreams.h>
+#include <fstream.h>
+
+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 (file)
index 0000000..1f7a795
--- /dev/null
@@ -0,0 +1,57 @@
+#include <error.h>\r
+\r
+typedef struct s_entry\r
+{\r
+       int code;\r
+       const char* string;\r
+}entry_t;\r
+\r
+static const\r
+entry_t err[] =\r
+{\r
+       {EREAD, "a read pipe operation failed"},\r
+       {EREADPIPE, "a pipe used to read from the stdout of a command is broken"},\r
+       {ETIMEOUT, "a command is timeouted"},\r
+       {EWRITE, "a write operation failed"},\r
+       {EWRITEPIPE, "a pipe used to write to the stdin of a command is broken"},\r
+       {EEXEC, "can't execute a command"},\r
+       {EWAIT, "wait function failed"},\r
+       {ECMDNOTFOUND, "command is not found"},\r
+       {EEXITCODENOTMATCH, "exit codes don't match"},\r
+       {EOUTPUTNOTMATCH, "outputs don't match"},\r
+       {ESIGNOTMATCH, "signals don't match"},\r
+       {EUNEXPECTEDSIG, "unexpected signal caught"},\r
+       {ESIGNOTRECEIPT, "expected signal not receipt"},\r
+       {EFILENOTFOUND, "specified tesh file not found"},\r
+       {EGETCWD, "system error : the getcwd() function failed"},\r
+       {EDIRNOTFOUND, "specified directory not found"},\r
+       {ECHDIR, "system error : the chdir() function failed"},\r
+       {EPROCESSCMDLINE, "internal error : the process_command_line() function failed"},\r
+       {EARGNOTSPEC, "none optional argument not specified in the command line"},\r
+       {ENOTPOSITIVENUM, "argument option not strictly positive"},\r
+       {ESYNTAX, "syntax error"},\r
+       {EINVALIDTIMEOUT, "timeout value specified by metacommand invalid"},\r
+       {EINVALIDEXITCODE, "expected exit code value specified by the metacommand invalid"},\r
+       {ESIGNOTSUPP, "signal specified by the metacommand not supported (Windows specific)"},\r
+       {ELEADTIME, "lead time"},\r
+       {EREADMENOTFOUND, "unable to locate the README.txt file"},\r
+       {EINCLUDENOTFOUND, "include file specified by a metacommand is not found"},\r
+       {ESUFFIXTOOLONG, "suffix is too long"},\r
+       {EFILENOTINSPECDIR,"file not found in the specified directories"},\r
+       {EFILENOTINCURDIR,"file not found in the current directory"},\r
+       {-1, "unknown"}\r
+};\r
+\r
+const char*\r
+error_to_string(int error)\r
+{\r
+       int i;\r
+       \r
+       for(i = 0; error >= 0 && err[i].code != -1; i++)\r
+       {\r
+               if(err[i].code == error)\r
+                       return err[i].string;\r
+       } \r
+       \r
+       return "unknow error";  \r
+}\r
diff --git a/tools/tesh2/src/excludes.c b/tools/tesh2/src/excludes.c
new file mode 100644 (file)
index 0000000..3329974
--- /dev/null
@@ -0,0 +1,123 @@
+#include <excludes.h>
+#include <vector.h>
+
+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 (file)
index 0000000..1349a98
--- /dev/null
@@ -0,0 +1,232 @@
+#include <fstream.h>
+#include <errno.h>
+#include <context.h>
+#include <command.h>
+#include <unit.h>
+
+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 (file)
index 0000000..27905ee
--- /dev/null
@@ -0,0 +1,178 @@
+#include <fstreams.h>
+#include <excludes.h>
+#include <fstream.h>
+
+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 (file)
index 0000000..5b7cb60
--- /dev/null
@@ -0,0 +1,317 @@
+
+#include <htable.h>
+
+#include <errno.h>
+#include <stdlib.h>    /* 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 (file)
index 0000000..b87f2bf
--- /dev/null
@@ -0,0 +1,816 @@
+
+#include <lstrings.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <allocator.h>
+
+#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 <i; j++)
+                               free(cstr[j]);
+                               
+                       free(cstr);
+                       
+                       return NULL;
+               }
+               
+               cur = cur->next;
+       }
+       
+       
+       return cstr;
+}
diff --git a/tools/tesh2/src/main.c b/tools/tesh2/src/main.c
new file mode 100644 (file)
index 0000000..94e9599
--- /dev/null
@@ -0,0 +1,1033 @@
+
+#include <runner.h>
+#include <fstream.h>
+#include <fstreams.h>
+#include <directory.h>
+#include <directories.h>
+#include <excludes.h>
+#include <error.h>
+
+#include <getopt.h>
+
+/*
+ * 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 <martin.quinson@loria.fr | malek.cherier@loria.fr>\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 <martin.quinson@loria.fr | malek.cherier@loria.fr>");
+}
+
+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 (file)
index 0000000..29b1c95
--- /dev/null
@@ -0,0 +1,142 @@
+#include <reader.h>
+#include <command.h>
+
+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 (file)
index 0000000..aeec092
--- /dev/null
@@ -0,0 +1,254 @@
+#include <runner.h>
+#include <units.h>
+#include <error.h>
+
+#include <errno.h>     /* for error code       */
+#include <stdlib.h>    /* for calloc()         */
+#include <stdio.h>     
+
+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 (file)
index 0000000..c7ffc61
--- /dev/null
@@ -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 (file)
index 0000000..232b039
--- /dev/null
@@ -0,0 +1,39 @@
+#include <suite.h>
+#include <unit.h>
+
+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 (file)
index 0000000..329ce61
--- /dev/null
@@ -0,0 +1,76 @@
+#include <timer.h>
+#include <command.h>
+
+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 (file)
index 0000000..1f540d6
--- /dev/null
@@ -0,0 +1,757 @@
+#include <unit.h>
+#include <suite.h>
+#include <command.h>
+#include <context.h>
+#include <fstream.h>
+
+
+
+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 (file)
index 0000000..500846f
--- /dev/null
@@ -0,0 +1,205 @@
+#include <units.h>
+#include <unit.h>
+#include <fstreams.h>
+
+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 (file)
index 0000000..e2f66a7
--- /dev/null
@@ -0,0 +1,8 @@
+#include <variable.h>\r
+\r
+typedef struct s_variable\r
+{\r
+       char* name;\r
+       char* val;\r
+       int used;\r
+}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 (file)
index 0000000..dff1472
--- /dev/null
@@ -0,0 +1,6 @@
+#include <variables.h>\r
+\r
+typedef struct s_variables\r
+{\r
+       dictionary_t items;\r
+}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 (file)
index 0000000..ca8f890
--- /dev/null
@@ -0,0 +1,971 @@
+
+#include <vector.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 (file)
index 0000000..97aa2dc
--- /dev/null
@@ -0,0 +1,215 @@
+#include <writer.h>
+#include <command.h>
+
+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);
+
+}