Logo AND Algorithmique Numérique Distribuée

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