Logo AND Algorithmique Numérique Distribuée

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