Logo AND Algorithmique Numérique Distribuée

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