Logo AND Algorithmique Numérique Distribuée

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