Logo AND Algorithmique Numérique Distribuée

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