Logo AND Algorithmique Numérique Distribuée

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