Logo AND Algorithmique Numérique Distribuée

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