Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
a3bea8e5b5728d143f6c59232f6a4d9387c18b5f
[simgrid.git] / tools / tesh2 / src / main.c
1 /*\r
2  * src/main.c - this file contains the main function of tesh.\r
3  *\r
4  * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved. \r
5  *\r
6  * This program is free software; you can redistribute it and/or modify it \r
7  * under the terms of the license (GNU LGPL) which comes with this package.\r
8  *\r
9  *\r
10  */\r
11 #include <runner.h>\r
12 #include <fstream.h>\r
13 #include <fstreams.h>\r
14 #include <directory.h>\r
15 #include <directories.h>\r
16 #include <excludes.h>\r
17 #include <getopt.h>\r
18 #include <getpath.h>\r
19 \r
20 #include <locale.h>\r
21 \r
22 /*\r
23  * entry used to define the parameter of a tesh option.\r
24  */\r
25 typedef struct s_optentry\r
26 {\r
27         int c;                                                          /* the character of the option                                                                                  */\r
28         \r
29         /* \r
30          * the type of the argument of the option \r
31          */\r
32         enum                                            \r
33         {\r
34                 flag,                                                   /* it's a flag option, by default the flag is set to zero                               */                                              \r
35                 string,                                                 /* the option has strings as argument                                                                   */                                      \r
36                 number                                                  /* the option has an integral positive number as argument                               */                      \r
37         }type;\r
38         \r
39         byte* value;                                            /* the value of the option                                                                                              */                      \r
40         byte* optional_value;                           /* the optional value of the option if not specified                                    */\r
41         const char * long_name;                         /* the long name of the command                                                                                 */\r
42 }s_optentry_t,* optentry_t;\r
43 \r
44 \r
45 /* logs */\r
46 XBT_LOG_NEW_DEFAULT_CATEGORY(tesh,"TEst SHell utility");\r
47 \r
48 #ifdef WIN32\r
49 /* Windows specific : the previous process error mode                   */\r
50 static UINT \r
51 prev_error_mode = 0;\r
52 #endif\r
53 \r
54 /* this object represents the root directory */\r
55 directory_t\r
56 root_directory = NULL;\r
57 \r
58 /* the current version of tesh                                                                  */\r
59 static const char* \r
60 version = "1.0";\r
61 \r
62 /* ------------------------------------------------------------ */\r
63 /* options                                                                                                              */\r
64 /* ------------------------------------------------------------ */\r
65 \r
66 /* ------------------------------------------------------------ */\r
67 /* numbers                                                                                                              */\r
68 /* ------------------------------------------------------------ */\r
69 \r
70 \r
71 /* --jobs is specified with arg                                                                 */\r
72 static int \r
73 jobs_nb = -2;\r
74 \r
75 /* --jobs option is not specified (use the default job count)   */\r
76 static int \r
77 default_jobs_nb = 1;\r
78 \r
79 /* --jobs is specified but has no arg (one job per unit)                */\r
80 static int \r
81 optional_jobs_nb = -1;\r
82 \r
83 /* the global timeout                                                                                   */\r
84 static int\r
85 timeout = INDEFINITE;\r
86 \r
87 /* ------------------------------------------------------------ */\r
88 /* strings                                                                                      */\r
89 /* ------------------------------------------------------------ */\r
90 \r
91 /* --C change the directory before running the units                    */\r
92 static directories_t \r
93 directories = NULL;\r
94 \r
95 /* the include directories : see the !i metacommand                             */\r
96 /*vector_t \r
97 include_dirs = NULL;*/\r
98 xbt_dynar_t\r
99 include_dirs = NULL;\r
100 \r
101 /* the list of tesh files to run                                                                */\r
102 static fstreams_t \r
103 fstreams = NULL;\r
104 \r
105 /* xbt logs                                                                                                             */\r
106 static xbt_dynar_t\r
107 logs = NULL;\r
108 \r
109 /* the list of tesh file suffixes                                                               */\r
110 static xbt_dynar_t\r
111 suffixes = NULL;\r
112 \r
113 \r
114 static excludes_t\r
115 excludes = NULL;\r
116 \r
117 \r
118 /* ------------------------------------------------------------ */\r
119 /* flags                                                                                                                */\r
120 /* ------------------------------------------------------------ */\r
121 \r
122 /* if 1, keep going when some commands can't be found\r
123  * default value 0 : not keep going\r
124  */\r
125 int \r
126 keep_going_flag = 0;\r
127 \r
128 /* if 1, ignore failures from commands\r
129  * default value : do not ignore failures\r
130  */\r
131 int \r
132 keep_going_unit_flag = 0;\r
133 \r
134 /* if 1, display tesh usage                                                                             */\r
135 static int \r
136 print_usage_flag = 0;\r
137 \r
138 /* if 1, display the tesh version                                                               */\r
139 static int \r
140 print_version_flag = 0;\r
141 \r
142 /* if 1, the syntax of all tesh files is checked \r
143  * before running them\r
144  */\r
145 static int\r
146 check_syntax_flag = 0;\r
147 \r
148 /* if 1, the status of all the units is display at\r
149  * the end.\r
150  */\r
151 static int\r
152 summary_flag = 0;\r
153 \r
154 /* if 1 and the flag want_summay is set to 1 tesh display the detailed summary of the run */\r
155 int \r
156 detail_summary_flag = 0;\r
157 \r
158 /* if 1, the directories are displayed                                                  */\r
159 int \r
160 print_directory_flag = 0;\r
161 \r
162 /* if 1, just check the syntax of all the tesh files\r
163  * do not run them.\r
164  */\r
165 int\r
166 dry_run_flag = 0;\r
167 \r
168 /* if 1, display the tesh files syntax and exit                                 */\r
169 static int\r
170 print_readme_flag = 0;\r
171 \r
172 int \r
173 silent_flag = 0;\r
174 \r
175 int \r
176 just_print_flag = 0;\r
177 \r
178 static int \r
179 env_overrides_flag  = 0;\r
180 \r
181 static int \r
182 print_database_flag = 0;\r
183 \r
184 static int \r
185 question_flag = 0;\r
186 \r
187 /* the semaphore used to synchronize the jobs */\r
188 xbt_os_sem_t\r
189 jobs_sem = NULL;\r
190 \r
191 /* the semaphore used by the runner to wait the end of all the units */\r
192 xbt_os_sem_t\r
193 units_sem = NULL;\r
194 \r
195 static int\r
196 loaded = 0;\r
197 \r
198 int \r
199 interrupted = 0;\r
200 \r
201 int\r
202 exit_code = 0; \r
203 \r
204 pid_t\r
205 pid =0;\r
206 \r
207 int \r
208 is_tesh_root = 1;\r
209 \r
210 xbt_dynar_t\r
211 errors = NULL;\r
212 \r
213 xbt_os_mutex_t\r
214 err_mutex = NULL;\r
215 \r
216 /* the table of the entries of the options */ \r
217 static const struct s_optentry opt_entries[] =\r
218 {\r
219         { 'C', string, (byte*)NULL, 0, "directory" },\r
220         { 'x', string, (byte*)&suffixes, 0, "suffix" },\r
221         { 'e', flag, (byte*)&env_overrides_flag, 0, "environment-overrides", },\r
222         { 'f', string, (byte*)&fstreams, 0, "file" },\r
223         { 'h', flag, (byte*)&print_usage_flag, 0, "help" },\r
224         { 'a', flag, (byte*)&print_readme_flag, 0, "read-me" },\r
225         { 'i', flag, (byte*)&keep_going_unit_flag, 0, "keep-going-unit" },\r
226         { 'I', string, (byte*)&include_dirs, 0, "include-dir" },\r
227         { 'j', number, (byte*)&jobs_nb, (byte*) &optional_jobs_nb, "jobs" },\r
228         { 'k', flag, (byte*)&keep_going_flag, 0, "keep-going" },\r
229         { 'm', flag, (byte*)&detail_summary_flag, 0, "detail-summary" },\r
230         { 'c', flag, (byte*)&just_print_flag, 0, "just-print" },\r
231         { 'd', flag, (byte*)&print_database_flag, 0,"display-data-base" },\r
232         { 'q', flag, (byte*)&question_flag, 0, "question_flag" },\r
233         { 's', flag, (byte*)&silent_flag, 0, "silent" },\r
234         { 'V', flag, (byte*)&print_version_flag, 0, "version" },\r
235         { 'w', flag, (byte*)&print_directory_flag, 0,"dont-display-directory" },\r
236         { 'n', flag, (byte*)&dry_run_flag, 0, "dry-run"},\r
237         { 't', number, (byte*)&timeout, 0, "timeout" },\r
238         { 'S', flag, (byte*)&check_syntax_flag, 0, "check-syntax"},\r
239         { 'r', string, (byte*)&directories, 0, "load-directory"},\r
240         { 'v', flag, (byte*)&summary_flag, 0, "summary"},\r
241         { 'F', string,(byte*)&excludes, 0, "exclude"},\r
242         { 'l', string,(byte*)&logs,0,"log"},\r
243         { 0, 0, 0, 0, 0}\r
244 };\r
245 \r
246 /* the tesh usage                                                                                               */\r
247 static const char* usage[] =\r
248 {\r
249         "Options:\n",\r
250         "  -C DIRECTORY, --directory=DIRECTORY   Change to DIRECTORY before running any commands.\n",\r
251         "  -e, --environment-overrides           Environment variables override files.\n",\r
252         "  -f FILE, --file=FILE                  Read FILE as a teshfile.\n",\r
253         "                                           remark :\n",\r
254         "                                           all argument of the command line without\n",\r
255         "                                           option is dealed as a tesh file.\n",\r
256         "  -h, --help                            Print this message and exit.\n",\r
257         "  -i, --keep-going-unit                 Ignore failures from commands.\n",\r
258         "                                        The possible failures are :\n",\r
259         "                                         - the exit code differ from the expected\n",\r
260         "                                         - the signal throw differ from the expected\n",\r
261         "                                         - the output differ from the expected\n",\r
262         "                                         - the read pipe is broken\n",\r
263         "                                         - the write pipe is broken\n",\r
264         "                                         - the command assigned delay is outdated\n",\r
265         "  -I DIRECTORY, --include-dir=DIRECTORY Search DIRECTORY for included files.\n",\r
266         "  -j [N], --jobs[=N]                    Allow N commands at once; infinite commands with\n"\r
267         "                                        no arg.\n",\r
268         "  -k, --keep-going                      Keep going when some commands can't be made or\n",\r
269         "                                        failed.\n",\r
270         "  -c, --just-print                      Don't actually run any commands; just print them.\n",\r
271         "  -p, --print-data-base                 Display tesh's internal database.\n",\r
272         "  -q, --question                        Run no commands; exit status says if up to date.\n",\r
273         "  -s, --silent,                         Don't echo commands.\n",\r
274         "  -V, --version                         Print the version number of tesh and exit.\n",\r
275         "  -d, --dont-print-directory            Don't display the current directory.\n",\r
276         "  -n, --dry-run                         Check the syntax of the specified tesh files, display the result and exit.\n",\r
277         "  -t, --timeout                         Wait the end of the commands at most timeout seconds.\n",\r
278         "  -S, --check-syntax                    Check the syntax of the tesh files before run them. \n",\r
279         "  -x, --suffix                          Consider the new suffix for the tesh files.\n"\r
280         "                                           remark :\n",\r
281         "                                           the default suffix for the tesh files is \".tesh\".\n",\r
282         " -a, --read-me                          Print the read me file and exit.\n",\r
283         " -b, --build-file                       Build a tesh file.\n",\r
284         " -r, --load-directory                   Run all the tesh files located in the directories specified by the option --directory.\n",\r
285         " -v, --summary                          Display the status of the commands.\n",\r
286         " -m, --detail-summary                   Detail the summary of the run.\n",\r
287         " -F file , --exclude=FILE               Ignore the tesh file FILE.\n",\r
288         " -l format, --log                       Format of the xbt logs.\n",\r
289         NULL\r
290 };\r
291 \r
292 /* the string of options of tesh                                                                */                                                              \r
293 static char \r
294 optstring[1 + sizeof (opt_entries) / sizeof (opt_entries[0]) * 3];\r
295 \r
296 /* the option table of tesh                                                                             */\r
297 static struct \r
298 option longopts[(sizeof (opt_entries) / sizeof (s_optentry_t))];\r
299 \r
300 static void\r
301 init_options(void);\r
302 \r
303 static int\r
304 process_command_line(int argc, char** argv);\r
305 \r
306 static void\r
307 load(void);\r
308 \r
309 static void\r
310 print_usage(void);\r
311 \r
312 static void\r
313 print_version(void);\r
314 \r
315 static void\r
316 finalize(void);\r
317 \r
318 static void\r
319 print_readme(void);\r
320 \r
321 static int\r
322 init(void);\r
323 \r
324 \r
325 static void \r
326 sig_abort_handler(int signum)\r
327 {\r
328         /* TODO : implement this function */\r
329         INFO0("sig_abort_handler() called");\r
330 }\r
331 \r
332 static void \r
333 sig_int_handler(int signum)\r
334 {\r
335         /* TODO : implement this function */\r
336         INFO0("sig_int_handler() called");\r
337 }\r
338 \r
339 static void \r
340 free_string(void* str)\r
341 {\r
342         free(*(void**)str);\r
343 }\r
344 \r
345 int\r
346 main(int argc, char* argv[])\r
347 {\r
348         int _argc;\r
349 \r
350         /* set the locale to the default*/\r
351         setlocale(LC_ALL,"");\r
352         \r
353         /* initialize tesh */\r
354         if(init() < 0)\r
355                 finalize();\r
356                 \r
357         /* process the command line */\r
358         if(process_command_line(argc, argv) < 0)\r
359                 finalize();\r
360         \r
361         /* move to the root directory (the directory may change during the command line processing) */\r
362         chdir(root_directory->name);\r
363                 \r
364         /* initialize the xbt library \r
365          * for thread portability layer\r
366          */\r
367          \r
368         /* xbt initialization */\r
369 \r
370         if((_argc = xbt_dynar_length(logs)))\r
371         {\r
372                 int i;\r
373 \r
374                 char** _argv = (char**)calloc(_argc, sizeof(char*));\r
375                 \r
376                 for(i = 0; i < _argc; i++)\r
377                         xbt_dynar_pop(logs, &(_argv[i]));\r
378 \r
379                 xbt_init(&_argc, _argv);\r
380 \r
381                 while(--i)\r
382                         free(_argv[i]);\r
383 \r
384                 free(_argv);\r
385         }\r
386         else\r
387                 xbt_init(&argc, argv);\r
388                 \r
389         /* the user wants to display the usage of tesh */\r
390         if(print_version_flag)\r
391         {\r
392                 print_version();\r
393 \r
394                 if(!print_usage_flag)\r
395                         finalize();\r
396         }\r
397         \r
398         /* the user wants to display the usage of tesh */\r
399         if(print_usage_flag)\r
400         {\r
401                 print_usage();\r
402                 finalize();\r
403         }\r
404         \r
405         /* the user wants to display the semantic of the tesh file metacommands */\r
406         if(print_readme_flag)\r
407         {\r
408                 print_readme();\r
409                 finalize();\r
410         }\r
411         \r
412         /* load tesh files */\r
413         load();\r
414         \r
415         \r
416         /* use by the finalize function to known if it must display the tesh usage */   \r
417         loaded = 1;\r
418 \r
419         if(-2 == jobs_nb)\r
420         {/* --jobs is not specified (use the default value) */\r
421                 jobs_nb = default_jobs_nb;\r
422         }\r
423         else if(optional_jobs_nb == jobs_nb)\r
424         {/* --jobs option is specified with no args (use one job per unit) */\r
425                 jobs_nb = fstreams_get_size(fstreams);\r
426         }\r
427 \r
428         if(jobs_nb > fstreams_get_size(fstreams))\r
429         {/* --jobs = N is specified and N is more than the number of tesh files */\r
430                 \r
431                 WARN0("Number of requested jobs exceed the number of files to run");\r
432                 \r
433                 /* assume one job per file */\r
434                 jobs_nb = fstreams_get_size(fstreams);\r
435         }\r
436 \r
437         /* initialize the semaphore used to synchronize all the units */\r
438         jobs_sem = xbt_os_sem_init(jobs_nb);\r
439 \r
440         /* initialize the semaphore used by the runner to wait for the end of all units */\r
441         units_sem = xbt_os_sem_init(0);\r
442         \r
443         /* initialize the runner */\r
444         if(runner_init(check_syntax_flag, timeout, fstreams) < 0)\r
445                 finalize();\r
446                 \r
447         if(just_print_flag && silent_flag)\r
448                 silent_flag = 0;\r
449                 \r
450         if(just_print_flag && dry_run_flag)\r
451                 WARN0("mismatch in the syntax : --just-check-syntax and --just-display options at same time");\r
452         \r
453         /* run all the units */\r
454         runner_run();\r
455         \r
456         \r
457         /* show the result of the units */\r
458         if(summary_flag || dry_run_flag)\r
459                 runner_summarize();\r
460                 \r
461         /* all the test are runned, destroy the runner */\r
462         runner_destroy();\r
463         \r
464         /* then, finalize tesh (release all the allocated memory and exits) */\r
465         finalize();\r
466         \r
467         #ifndef WIN32\r
468         return exit_code;\r
469         #endif\r
470         \r
471 }\r
472 \r
473 /* init --      initialize tesh : allocated all the objects needed by tesh to run\r
474  *                      the tesh files specified in the command line.\r
475  *\r
476  * return       If successful the function returns zero. Otherwise the function returns\r
477  *                      -1 and sets the global variable errno to the appropriate error code.\r
478  */\r
479 \r
480 \r
481 \r
482 static int\r
483 init(void)\r
484 {\r
485         char* buffer;\r
486         char* suffix = strdup(".tesh");\r
487         \r
488         #ifdef WIN32\r
489         /* Windows specific : don't display the general-protection-fault message box and\r
490          * the the critical-error-handler message box (instead the system send the error\r
491          * to the calling process : tesh)\r
492          */\r
493         prev_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);\r
494         #else\r
495         struct sigaction act;\r
496         /* Ignore pipe issues.\r
497          * They will show up when we try to send data to dead buddies, \r
498      * but we will stop doing so when we're done with provided input \r
499      */\r
500         memset(&act,0, sizeof(struct sigaction));\r
501         act.sa_handler = SIG_IGN;\r
502         sigaction(SIGPIPE, &act, NULL);\r
503         \r
504         \r
505         memset(&act,0, sizeof(struct sigaction));\r
506         act.sa_handler = sig_abort_handler;\r
507         sigaction(SIGABRT, &act, NULL);\r
508         \r
509         memset(&act,0, sizeof(struct sigaction));\r
510         act.sa_handler = sig_int_handler;\r
511         sigaction(SIGINT, &act, NULL);\r
512         \r
513         #endif\r
514         \r
515         err_mutex = xbt_os_mutex_init();\r
516         \r
517         /* handle the abort signal */\r
518         /*signal(SIGABRT, sig_abort_handler);*/\r
519         \r
520         /* handle the interrupt signal */\r
521         /*signal(SIGINT, sig_int_handler);*/\r
522         \r
523         /* used to store the files to run */\r
524         if(!(fstreams = fstreams_new(DEFAULT_FSTREAMS_CAPACITY, (void_f_pvoid_t)fstream_free)))\r
525         {\r
526                 ERROR1("(system error) %s", strerror(errno));\r
527                 return -1;\r
528         }\r
529         \r
530         /* register the current directory */\r
531         if(!(buffer  = getcwd(NULL, 0)))\r
532         {\r
533                 ERROR1("(system error) %s", strerror(errno));\r
534                 return -1;\r
535         }\r
536         \r
537         /* save the root directory */\r
538         if(!(root_directory = directory_new(buffer)))\r
539         {\r
540                 ERROR1("(system error) %s", strerror(errno));\r
541                 return -1;\r
542         }\r
543         \r
544         free(buffer);\r
545         \r
546         /* this vector contains all the errors of the run */\r
547         errors = xbt_dynar_new(sizeof(xerror_t), free);\r
548         \r
549         /* the directories to loads */\r
550         if(!(directories = directories_new()))\r
551         {\r
552                 ERROR1("(system error) %s", strerror(errno));\r
553                 return -1;\r
554         }\r
555         \r
556         /* the include directories */\r
557         include_dirs = xbt_dynar_new(sizeof(directory_t), (void_f_pvoid_t)directory_free);\r
558         \r
559         /* xbt logs option */\r
560         if(!(logs = xbt_dynar_new(sizeof(char*), free_string)))\r
561         {\r
562                 ERROR1("(system error) %s", strerror(errno));\r
563                 return -1;\r
564         }\r
565         \r
566         /* the excluded files */\r
567         if(!(excludes = excludes_new()))\r
568         {\r
569                 ERROR1("(system error) %s", strerror(errno));\r
570                 return -1;\r
571         }\r
572         \r
573         /* the suffixes */\r
574         suffixes = xbt_dynar_new(sizeof(char*),free_string);\r
575         \r
576         /* register the default suffix ".tesh" */\r
577         xbt_dynar_push(suffixes, &suffix);\r
578         \r
579         return 0;\r
580 }\r
581 \r
582 /* load -- load the tesh files to run */\r
583 static void\r
584 load(void)\r
585 {\r
586         \r
587         /* if the directories object is not empty load all the tesh files contained in\r
588          * the directories specified in the command line (this tesh files must have the\r
589          * a suffix specified in the suffixes object.\r
590          */\r
591         if(!directories_is_empty(directories))\r
592                 directories_load(directories, fstreams, suffixes);\r
593         \r
594         /* if no tesh file has been specified in the command line try to load the default tesh file\r
595          * teshfile from the current directory\r
596          */\r
597         if(fstreams_is_empty(fstreams))\r
598         {\r
599                 struct stat buffer = {0};\r
600                 \r
601                 /* add the default tesh file if it exists in the current directory */\r
602                 if(!stat("teshfile", &buffer) && S_ISREG(buffer.st_mode))\r
603                         fstreams_add(fstreams, fstream_new(getcwd(NULL, 0), "teshfile"));\r
604         }\r
605         \r
606         /* excludes the files specified in the command line and stored in the excludes object */\r
607         if(!excludes_is_empty(excludes) && !fstreams_is_empty(fstreams))\r
608         {\r
609                 /* check the files to excludes before */        \r
610                 excludes_check(excludes, fstreams);\r
611                 \r
612                 /* exclude the specified tesh files */\r
613                 fstreams_exclude(fstreams, excludes);\r
614         }\r
615         \r
616         /* if the fstreams object is empty use the stdin */\r
617         if(fstreams_is_empty(fstreams))\r
618                 fstreams_add(fstreams, fstream_new(NULL, "stdin"));\r
619         \r
620         /* load the tesh files (open them) */\r
621         fstreams_load(fstreams);\r
622         \r
623 }\r
624 \r
625 /* finalize -- cleanup all the allocated objects and display the tesh usage if needed */\r
626 static void\r
627 finalize(void)\r
628 {\r
629         \r
630         if(is_tesh_root && !summary_flag)\r
631         {\r
632                 xerror_t error;\r
633                 unsigned int i;\r
634 \r
635                 xbt_dynar_foreach(errors, i, error)\r
636                 {\r
637                         if(error->command)\r
638                                 fprintf(stderr, "[tesh/ERROR] %s : <Command `%s'> <Unit `%s'> <C%d (%s)>\n", error->reason, error->command, error->unit, error->errcode, error_to_string(error->errcode));\r
639                         else if(!error->command && error->unit)\r
640                                 fprintf(stderr, "[tesh/ERROR] %s : Unit `%s' - C%d (%s)\n", error->reason, error->unit, error->errcode, error_to_string(error->errcode));\r
641                         else if(!error->command && !error->unit && error->reason)\r
642                                 fprintf(stderr, "[tesh/ERROR] %s : C%d (%s)\n", error->reason, error->errcode, error_to_string(error->errcode));                \r
643                         else if(!error->command && !error->unit && !error->reason)\r
644                                 fprintf(stderr, "[tesh/ERROR] C%d (%s)\n", error->errcode, error_to_string(error->errcode));            \r
645                         \r
646                 }\r
647         }\r
648         \r
649         /* delete vector of errors */\r
650         if(errors)\r
651                 xbt_dynar_free(&errors);\r
652                 \r
653         xbt_os_mutex_destroy(err_mutex);\r
654         \r
655         /* delete the fstreams object */\r
656         if(fstreams)\r
657                 fstreams_free((void**)&fstreams);\r
658         \r
659         /* delete the excludes object */\r
660         if(excludes)    \r
661                 excludes_free((void**)&excludes);\r
662         \r
663         /* delete the directories object */\r
664         if(directories)\r
665                 directories_free((void**)&directories);\r
666                 \r
667         /* delete the root directory object */\r
668         if(root_directory)\r
669                 directory_free((void**)&root_directory);\r
670         \r
671         /* delete the include directories object */\r
672         if(include_dirs)\r
673                 xbt_dynar_free(&include_dirs);\r
674         \r
675         /* delete the list of tesh files suffixes */    \r
676         if(suffixes)\r
677                 xbt_dynar_free(&suffixes);\r
678         \r
679         /* delete the xbt log options list */\r
680         if(logs)\r
681                 xbt_dynar_free(&logs);\r
682                 \r
683         /* destroy the semaphore used to synchronize the units */\r
684         if(jobs_sem)\r
685                 xbt_os_sem_destroy(jobs_sem);\r
686         \r
687         /* destroy the semaphore used by the runner used to wait for the end of the units */\r
688         if(units_sem)\r
689                 xbt_os_sem_destroy(units_sem);\r
690         \r
691         /* exit from the xbt framework */\r
692         xbt_exit();\r
693         \r
694         /* Windows specific (restore the previouse error mode */\r
695         #ifdef WIN32\r
696         SetErrorMode(prev_error_mode);\r
697         #endif\r
698         \r
699         if(!summary_flag && !dry_run_flag && !silent_flag && !just_print_flag && !print_version_flag && !print_usage_flag && is_tesh_root)\r
700         {\r
701                 if(!exit_code)\r
702                         INFO2("tesh terminated with exit code %d : %s",exit_code, "success");\r
703                 else\r
704                         ERROR2("tesh terminated with exit code %d : %s",exit_code, error_to_string(exit_code));\r
705         }\r
706         \r
707         \r
708         /* exit with the last error code */\r
709         exit(exit_code);\r
710 }\r
711 \r
712 /* init_options -- initialize the options string */\r
713 static void\r
714 init_options (void)\r
715 {\r
716         char *p;\r
717         unsigned int i;\r
718         \r
719         /* the function has been already called */\r
720         if(optstring[0] != '\0')\r
721                 return;\r
722         \r
723         p = optstring;\r
724         \r
725         *p++ = '-';\r
726         \r
727         for (i = 0; opt_entries[i].c != '\0'; ++i)\r
728         {\r
729                 /* initialize the long name of the option*/\r
730                 longopts[i].name = (opt_entries[i].long_name == 0 ? "" : opt_entries[i].long_name);\r
731                 \r
732                 /* getopt_long returns the value of val */\r
733                 longopts[i].flag = 0;\r
734                 \r
735                 /* the short option */\r
736                 longopts[i].val = opt_entries[i].c;\r
737                 \r
738                 /* add the short option in the options string */\r
739                 *p++ = opt_entries[i].c;\r
740                 \r
741                 switch (opt_entries[i].type)\r
742                 {\r
743                         /* if this option is used to set a flag or if the argument must be ignored\r
744                          * the option has no argument\r
745                          */\r
746                         case flag:\r
747                         longopts[i].has_arg = no_argument;\r
748                         break;\r
749                 \r
750                         /* the option has an argument */\r
751                         case string:\r
752                         case number:\r
753                         \r
754                         *p++ = ':';\r
755                         \r
756                         if(opt_entries[i].optional_value != 0)\r
757                         {\r
758                                 *p++ = ':';\r
759                                 \r
760                                 longopts[i].has_arg = optional_argument;\r
761                         }\r
762                         else\r
763                                 longopts[i].has_arg = required_argument;\r
764                         \r
765                         break;\r
766                 }\r
767         }\r
768         \r
769         *p = '\0';\r
770         longopts[i].name = 0;\r
771 }\r
772 \r
773 /* process_command_line --      process the command line\r
774  *\r
775  * param                                        argc the number of the arguments contained by the command line.\r
776  * param                                        The array of C strings containing all the arguments of the command \r
777  *                                                      line.\r
778  *\r
779  * return                                       If successful, the function returns 0. Otherwise -1 is returned\r
780  *                                                      and sets the global variable errno to indicate the error.\r
781  *\r
782  * errors       [ENOENT]                A file name specified in the command line does not exist\r
783  */     \r
784 \r
785 static int\r
786 process_command_line(int argc, char** argv)\r
787 {\r
788         register const struct s_optentry* entry;\r
789         register int c;\r
790         directory_t directory;\r
791         fstream_t fstream;\r
792         \r
793         /* initialize the options string of tesh */\r
794         init_options();\r
795         \r
796         /* let the function getopt_long display the errors if any */\r
797         opterr = 1;\r
798         \r
799         /* set option index to zero */\r
800         optind = 0;\r
801         \r
802         while (optind < argc)\r
803         {\r
804                 c = getopt_long(argc, argv, optstring, longopts, (int *) 0);\r
805                 \r
806                 if(c == EOF)\r
807                 {\r
808                         /* end of the command line or "--".  */\r
809                         break;\r
810                 }\r
811                 else if (c == 1)\r
812                 {\r
813                         /* no option specified, assume it's a tesh file to run */\r
814                         char* path;\r
815                         char* delimiter;\r
816                         \r
817                         /* getpath returns -1 when the file to get the path doesn't exist */\r
818                         if(getpath(optarg, &path) < 0)\r
819                         {\r
820                                 exit_code = errno;\r
821                                 \r
822                                 if(ENOENT == errno)\r
823                                         ERROR1("File %s does not exist", optarg);\r
824                                 else\r
825                                         ERROR0("Insufficient memory is available to parse the command line : system error");\r
826                                         \r
827                                 return -1;\r
828                         }\r
829                         \r
830                         /* get to the last / (if any) to get the short name of the file */\r
831                         delimiter = strrchr(optarg,'/');\r
832                         \r
833                         /* create a new file stream which represents the tesh file to run */\r
834                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);\r
835                         \r
836                         free(path);\r
837                         \r
838                         /* if the list of all tesh files to run already contains this file\r
839                          * destroy it and display a warning, otherwise add it in the list.\r
840                          */\r
841                         if(fstreams_contains(fstreams, fstream))\r
842                         {\r
843                                 fstream_free(&fstream);\r
844                                 WARN1("File %s already specified to be run", optarg);\r
845                         }\r
846                         else\r
847                                 fstreams_add(fstreams, fstream);\r
848                                 \r
849                         \r
850                                 \r
851                         \r
852                 }\r
853                 else if (c == '?')\r
854                 {\r
855                         /* unknown option, let getopt_long() displays the error */\r
856                         ERROR0("Command line processing failed : invalid command line");\r
857                         exit_code = EINVCMDLINE;\r
858                         return -1;\r
859                 }\r
860                 else\r
861                 {\r
862                         for(entry = opt_entries; entry->c != '\0'; ++entry)\r
863                                 \r
864                                 if(c == entry->c)\r
865                                 {\r
866                 \r
867                                         switch (entry->type)\r
868                                         {\r
869                                                 /* impossible */\r
870                                                 default:\r
871                                                 ERROR0("Command line processing failed : internal error");\r
872                                                 exit_code = EPROCCMDLINE;\r
873                                                 return -1;\r
874                                                 \r
875                                                 \r
876                                                 /* flag options */\r
877                                                 case flag:\r
878                                                 /* set the flag to one */\r
879                                                 *(int*) entry->value = 1;\r
880                                                 \r
881                                                 break;\r
882                                                 \r
883                                                 /* string options */\r
884                                                 case string:\r
885                 \r
886                                                 if(!optarg)\r
887                                                 {\r
888                                                         /* an option with a optional arg is specified use the entry->optional_value */\r
889                                                         optarg = (char*)entry->optional_value;\r
890                                                 }\r
891                                                 else if (*optarg == '\0')\r
892                                                 {\r
893                                                         /* a non optional argument is not specified */\r
894                                                         ERROR2("Option %c \"%s\"requires an argument",entry->c,entry->long_name);\r
895                                                         exit_code = ENOARG;\r
896                                                         return -1;\r
897                                                 }\r
898                                                 \r
899                                                 /* --load-directory option */\r
900                                                 if(!strcmp(entry->long_name,"load-directory"))\r
901                                                 {\r
902                                                         #ifdef WIN32\r
903                                                         struct stat info = {0};\r
904                                                         if(stat(optarg, &info) || !S_ISDIR(info.st_mode))\r
905                                                         {\r
906                                                                 ERROR1("%s is not a directory",optarg);\r
907                                                                 exit_code = ENOTDIR;\r
908                                                                 return -1;\r
909                                                         }       \r
910 \r
911                                                         #else\r
912                                                         char* path;\r
913                                                         \r
914                                                         if(translatepath(optarg, &path) < 0)\r
915                                                         {\r
916                                                                 exit_code = errno;\r
917                                                                 \r
918                                                                 if(ENOTDIR == errno)\r
919                                                                         ERROR1("%s is not a directory",optarg);\r
920                                                                 else\r
921                                                                         ERROR0("Insufficient memory is available to process the command line - system error");\r
922                                                                         \r
923                                                                 return -1;\r
924                                                                 \r
925                                                         }\r
926                                                         #endif\r
927                                                         else\r
928                                                         {\r
929                                                                 #ifdef WIN32\r
930                                                                 directory = directory_new(optarg);\r
931                                                                 #else\r
932                                                                 directory = directory_new(path);\r
933                                                                 free(path);\r
934                                                                 #endif\r
935                                                 \r
936                                                                 if(directories_contains(directories, directory))\r
937                                                                 {\r
938                                                                         directory_free((void**)&directory);\r
939                                                                         WARN1("Directory %s already specified to be load",optarg);\r
940                                                                 }\r
941                                                                 else\r
942                                                                          directories_add(directories, directory);\r
943                                                                          \r
944                                                                 \r
945                                                         }                       \r
946                                                 }\r
947                                                 else if(!strcmp(entry->long_name,"directory"))\r
948                                                 {\r
949                                                         #ifdef WIN32\r
950                                                         struct stat info = {0};\r
951                                                         if(stat(optarg, &info) || !S_ISDIR(info.st_mode))\r
952                                                         {\r
953                                                                 ERROR1("%s is not a directory",optarg);\r
954                                                                 exit_code = ENOTDIR;\r
955                                                                 return -1;\r
956                                                         }       \r
957 \r
958                                                         #else\r
959                                                         char* path ;\r
960                                                         \r
961                                                         if(translatepath(optarg, &path) < 0)\r
962                                                         {\r
963                                                                 exit_code = errno;\r
964                                                                 \r
965                                                                 if(ENOTDIR == errno)\r
966                                                                         ERROR1("%s is not a directory",optarg);\r
967                                                                 else\r
968                                                                         ERROR0("Insufficient memory is available to process the command line - system error");\r
969                                                                         \r
970                                                                 return -1;\r
971                                                         }\r
972                                                         #endif\r
973                                                         else\r
974                                                         {\r
975                                                                 char* buffer = getcwd(NULL, 0);\r
976 \r
977                                                                 #ifdef WIN32\r
978                                                                 \r
979                                                                 if(!strcmp(buffer, optarg))\r
980                                                                         WARN1("Already in the directory %s", optarg);\r
981                                                                 else if(!print_directory_flag)\r
982                                                                         INFO1("Entering directory \"%s\"",optarg);\r
983                                                                 \r
984                                                                 chdir(optarg);\r
985                                                                 #else\r
986                                                                 \r
987                                                                 if(!strcmp(buffer, path))\r
988                                                                         WARN1("Already in the directory %s", optarg);\r
989                                                                 else if(!print_directory_flag)\r
990                                                                         INFO1("Entering directory \"%s\"",path);\r
991 \r
992                                                                 chdir(path);\r
993                                                                 free(path);\r
994                                                                 #endif\r
995 \r
996                                                                 free(buffer);\r
997                                                                 \r
998                                                                 \r
999                                                         }       \r
1000                                                 }\r
1001                                                 \r
1002                                                 /* --suffix option */\r
1003                                                 else if(!strcmp(entry->long_name,"suffix"))\r
1004                                                 {\r
1005                                                         if(strlen(optarg) > MAX_SUFFIX)\r
1006                                                         {\r
1007                                                                 ERROR1("Suffix %s too long",optarg);\r
1008                                                                 exit_code = ESUFFIXTOOLONG;     \r
1009                                                                 return -1;\r
1010                                                         }\r
1011                                                         \r
1012                                                         if(optarg[0] == '.')\r
1013                                                         {\r
1014                                                                 char* cur;\r
1015                                                                 unsigned int i;\r
1016                                                                 int exists = 0;\r
1017 \r
1018                                                                 char suffix[MAX_SUFFIX + 2] = {0};\r
1019                                                                 sprintf(suffix,".%s",optarg);\r
1020                                                                 \r
1021                                                                 xbt_dynar_foreach(suffixes, i, cur)\r
1022                                                                 {\r
1023                                                                         if(!strcmp(suffix, cur))\r
1024                                                                         {\r
1025                                                                                 exists = 1;\r
1026                                                                                 break;\r
1027                                                                         }\r
1028                                                                 }\r
1029 \r
1030                                                                 if(exists)\r
1031                                                                         WARN1("Suffix %s already specified to be used", optarg);\r
1032                                                                 else\r
1033                                                                         xbt_dynar_push(suffixes, &suffix);\r
1034                                                         }\r
1035                                                         else\r
1036                                                         {\r
1037                                                                 char* cur;\r
1038                                                                 unsigned int i;\r
1039                                                                 int exists = 0;\r
1040 \r
1041                                                                 xbt_dynar_foreach(suffixes, i, cur)\r
1042                                                                 {\r
1043                                                                         if(!strcmp(optarg, cur))\r
1044                                                                         {\r
1045                                                                                 exists = 1;\r
1046                                                                                 break;\r
1047                                                                         }\r
1048                                                                 }\r
1049 \r
1050                                                                 if(exists)\r
1051                                                                         WARN1("Suffix %s already specified to be used", optarg);\r
1052                                                                 else\r
1053                                                                         xbt_dynar_push(suffixes, &optarg);      \r
1054                                                         }\r
1055                                                 }\r
1056                                                 /* --file option */\r
1057                                                 else if(!strcmp(entry->long_name,"file"))\r
1058                                                 {\r
1059                                                         char* path;\r
1060                                                         char* delimiter;\r
1061                                                         \r
1062                                                         if(getpath(optarg, &path) < 0)\r
1063                                                         {\r
1064                                                                 exit_code = errno;\r
1065                                                                 \r
1066                                                                 if(ENOENT == errno)\r
1067                                                                         ERROR1("File %s does not exist", optarg);\r
1068                                                                 else\r
1069                                                                         ERROR0("Insufficient memory is available to process the command line - system error");\r
1070                                                                 \r
1071                                                                 return -1;\r
1072                                                         }\r
1073                                                         \r
1074                                                         delimiter = strrchr(optarg,'/');\r
1075                                                         \r
1076                                                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);\r
1077                                                         \r
1078                                                         free(path);\r
1079                                                         \r
1080                                                         if(fstreams_contains(fstreams, fstream))\r
1081                                                         {\r
1082                                                                 fstream_free(&fstream);\r
1083                                                                 WARN1("File %s already specified to run", optarg);\r
1084                                                         }\r
1085                                                         else\r
1086                                                                 fstreams_add(fstreams, fstream);\r
1087                                                 }\r
1088                                                 /* --include-dir option */\r
1089                                                 else if(!strcmp(entry->long_name,"include-dir"))\r
1090                                                 {\r
1091                                                         #ifdef WIN32\r
1092                                                         struct stat info = {0};\r
1093                                                         if(stat(optarg, &info) || !S_ISDIR(info.st_mode))\r
1094                                                         {\r
1095                                                                 ERROR1("%s is not a directory",optarg);\r
1096                                                                 exit_code = ENOTDIR;\r
1097                                                                 return -1;\r
1098                                                         }       \r
1099 \r
1100                                                         #else\r
1101                                                         char* path ;\r
1102                                                         \r
1103                                                         if(translatepath(optarg, &path) < 0)\r
1104                                                         {\r
1105                                                                 exit_code = errno;\r
1106                                                                 \r
1107                                                                 if(ENOTDIR == errno)\r
1108                                                                         ERROR1("%s is not a directory",optarg);\r
1109                                                                 else\r
1110                                                                         ERROR0("Insufficient memory is available to process the command line - system error");\r
1111                                                                         \r
1112                                                                 return -1;\r
1113                                                         }\r
1114                                                         #endif\r
1115                                                         else\r
1116                                                         {\r
1117                                                                 int exists = 0;\r
1118                                                                 unsigned int i;\r
1119                                                                 directory_t cur;\r
1120                                                                 #ifdef WIN32\r
1121                                                                 directory = directory_new(optarg);\r
1122                                                                 #else\r
1123                                                                 directory = directory_new(path);\r
1124                                                                 free(path);\r
1125                                                                 #endif\r
1126 \r
1127                                                                 xbt_dynar_foreach(include_dirs, i , cur)\r
1128                                                                 {\r
1129                                                                         if(!strcmp(cur->name, optarg))\r
1130                                                                         {\r
1131                                                                                 exists = 1;\r
1132                                                                                 break;\r
1133                                                                         }\r
1134                                                                 }\r
1135 \r
1136                                                                 if(exists)\r
1137                                                                 {\r
1138                                                                         directory_free((void**)&directory);\r
1139                                                                         WARN1("Include directory %s already specified to be used",optarg);\r
1140                                                                         \r
1141                                                                 }\r
1142                                                                 else\r
1143                                                                         xbt_dynar_push(include_dirs, &directory);\r
1144 \r
1145                                                         }\r
1146                                                 }\r
1147                                                 /* --exclude option */ \r
1148                                                 else if(!strcmp(entry->long_name,"exclude"))\r
1149                                                 {\r
1150                                                         char* path;\r
1151                                                         char* delimiter;\r
1152                         \r
1153                                                         if(getpath(optarg, &path) < 0)\r
1154                                                         {\r
1155                                                                 exit_code = errno;\r
1156                                                                 \r
1157                                                                 if(ENOENT == errno)\r
1158                                                                         ERROR1("file %s does not exist", optarg);\r
1159                                                                 else\r
1160                                                                         ERROR0("Insufficient memory is available to process the command line - system error");\r
1161                                                                         \r
1162                                                                 return -1;\r
1163                                                         }\r
1164                         \r
1165                                                         delimiter = strrchr(optarg,'/');\r
1166                         \r
1167                                                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);\r
1168                                                         free(path);\r
1169                         \r
1170                                                         if(excludes_contains(excludes, fstream))\r
1171                                                         {\r
1172                                                                 fstream_free(&fstream);\r
1173                                                                 WARN1("File %s already specified to be exclude", optarg);\r
1174                                                         }\r
1175                                                         else\r
1176                                                                 excludes_add(excludes, fstream);\r
1177                                                                         \r
1178                                                 }\r
1179                                                 /* --log option */\r
1180                                                 else if(!strcmp(entry->long_name,"log"))\r
1181                                                 {\r
1182                                                         xbt_dynar_push(logs, &optarg);\r
1183                                                 }\r
1184                                                 else\r
1185                                                 {\r
1186                                                         INFO1("Unexpected option %s", optarg);\r
1187                                                         return -1;\r
1188                                                 }\r
1189                                                 \r
1190                                                 \r
1191                                                 break;\r
1192                                                 \r
1193                                                 /* strictly positve number options */\r
1194                                                 case number:\r
1195                                                         \r
1196                                                 if ((NULL == optarg) && (argc > optind))\r
1197                                                 {\r
1198                                                         const char* cp;\r
1199                                                         \r
1200                                                         for (cp = argv[optind]; isdigit(cp[0]); ++cp)\r
1201                                                                 if(cp[0] == '\0')\r
1202                                                                         optarg = argv[optind++];\r
1203                                                 }\r
1204                 \r
1205                                                 /* the number option is specified */\r
1206                                                 if(NULL != optarg)\r
1207                                                 {\r
1208                                                         int i = atoi(optarg);\r
1209                                                         const char *cp;\r
1210 \r
1211                                                         for (cp = optarg; isdigit(cp[0]); ++cp);\r
1212                 \r
1213                                                         if (i < 1 || cp[0] != '\0')\r
1214                                                         {\r
1215                                                                 ERROR2("Option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name);\r
1216                                                                 exit_code = ENOTPOSITIVENUM;\r
1217                                                                 return -1;\r
1218                                                         }\r
1219                                                         else\r
1220                                                                 *(int*)entry->value = i;\r
1221                                                 }\r
1222                                                 /* the number option is specified but has no arg, use the optional value*/\r
1223                                                 else\r
1224                                                         *(int*)entry->value = *(int*) entry->optional_value;\r
1225                                                 \r
1226                                                 break;\r
1227                 \r
1228                                 }\r
1229                                 break;\r
1230                         }\r
1231                 }\r
1232         }\r
1233 \r
1234         return 0;\r
1235 }\r
1236 \r
1237 static void\r
1238 print_usage(void)\r
1239 {\r
1240         const char **cpp;\r
1241         FILE* stream;\r
1242         \r
1243         stream = exit_code ? stderr : stdout;\r
1244         \r
1245         fprintf (stream, "Usage: tesh [options] [file] ...\n");\r
1246         \r
1247         for (cpp = usage; *cpp; ++cpp)\r
1248                 fputs (*cpp, stream);\r
1249         \r
1250         fprintf(stream, "\nReport bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>\n");\r
1251 }\r
1252 \r
1253 static void\r
1254 print_version(void)\r
1255 {\r
1256         /* TODO : display the version of tesh */\r
1257         printf("Version :\n");\r
1258         printf("  tesh version %s : Mini shell specialized in running test units by Martin Quinson \n", version);\r
1259         printf("  and Malek Cherier\n");\r
1260         printf("  Copyright (c) 2007, 2008 Martin Quinson, Malek Cherier\n");\r
1261         printf("  All rights reserved\n");\r
1262         printf("  This program is free software; you can redistribute it and/or modify it\n");\r
1263         printf("  under the terms of the license (GNU LGPL) which comes with this package.\n\n");\r
1264         \r
1265         if(!print_usage_flag)\r
1266                 printf("Report bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>");\r
1267 }\r
1268 \r
1269 static void\r
1270 print_readme(void)\r
1271 {\r
1272         size_t len;\r
1273         char * line = NULL;\r
1274         \r
1275         FILE* stream = fopen("README.txt", "r");\r
1276         \r
1277         if(!stream)\r
1278         {\r
1279                 ERROR0("Unable to locate the README.txt file");\r
1280                 exit_code = EREADMENOTFOUND;\r
1281                 return;\r
1282         }\r
1283         \r
1284         while(getline(&line, &len, stream) != -1)\r
1285                 printf("%s",line);\r
1286                 \r
1287         fclose(stream);\r
1288 }\r
1289 \r
1290 \r