Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Define correctly variables for windows.
[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 _XBT_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 _XBT_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 _XBT_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 _XBT_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 _XBT_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
635         finalized = 1;
636         
637         /* exit with the last error code */
638         if(!sig_int)
639                 exit(exit_code);
640 }
641
642 /* init_options -- initialize the options string */
643 static void
644 init_options (void)
645 {
646         char *p;
647         unsigned int i;
648         
649         /* the function has been already called */
650         if(optstring[0] != '\0')
651                 return;
652         
653         p = optstring;
654         
655         *p++ = '-';
656         
657         for (i = 0; opt_entries[i].c != '\0'; ++i)
658         {
659                 /* initialize the long name of the option*/
660                 longopts[i].name = (opt_entries[i].long_name == 0 ? "" : opt_entries[i].long_name);
661                 
662                 /* getopt_long returns the value of val */
663                 longopts[i].flag = 0;
664                 
665                 /* the short option */
666                 longopts[i].val = opt_entries[i].c;
667                 
668                 /* add the short option in the options string */
669                 *p++ = opt_entries[i].c;
670                 
671                 switch (opt_entries[i].type)
672                 {
673                         /* if this option is used to set a flag or if the argument must be ignored
674                          * the option has no argument
675                          */
676                         case flag:
677                         longopts[i].has_arg = no_argument;
678                         break;
679                 
680                         /* the option has an argument */
681                         case string:
682                         case number:
683                         
684                         *p++ = ':';
685                         
686                         if(opt_entries[i].optional_value != 0)
687                         {
688                                 *p++ = ':';
689                                 
690                                 longopts[i].has_arg = optional_argument;
691                         }
692                         else
693                                 longopts[i].has_arg = required_argument;
694                         
695                         break;
696                 }
697         }
698         
699         *p = '\0';
700         longopts[i].name = 0;
701 }
702
703 /* process_command_line --      process the command line
704  *
705  * param                                        argc the number of the arguments contained by the command line.
706  * param                                        The array of C strings containing all the arguments of the command 
707  *                                                      line.
708  *
709  * return                                       If successful, the function returns 0. Otherwise -1 is returned
710  *                                                      and sets the global variable errno to indicate the error.
711  *
712  * errors       [ENOENT]                A file name specified in the command line does not exist
713  */     
714
715 static int
716 process_command_line(int argc, char** argv)
717 {
718         register const struct s_optentry* entry;
719         register int c;
720         directory_t directory;
721         fstream_t fstream;
722         
723         /* initialize the options string of tesh */
724         init_options();
725         
726         /* let the function getopt_long display the errors if any */
727         opterr = 1;
728         
729         /* set option index to zero */
730         optind = 0;
731         
732         while (optind < argc)
733         {
734                 c = getopt_long(argc, argv, optstring, longopts, (int *) 0);
735                 
736                 if(c == EOF)
737                 {
738                         /* end of the command line or "--".  */
739                         break;
740                 }
741                 else if (c == 1)
742                 {
743                         /* no option specified, assume it's a tesh file to run */
744                         char* path;
745                         char* delimiter;
746                         
747                         /* getpath returns -1 when the file to get the path doesn't exist */
748                         if(getpath(optarg, &path) < 0)
749                         {
750                                 exit_code = errno;
751                                 err_kind = 0;
752                                 
753                                 if(ENOENT == errno)
754                                         ERROR1("File %s does not exist", optarg);
755                                 else
756                                         ERROR0("Insufficient memory is available to parse the command line : system error");
757                                         
758                                 return -1;
759                         }
760                         
761                         /* get to the last / (if any) to get the short name of the file */
762                         delimiter = strrchr(optarg,'/');
763                         
764                         #ifdef _XBT_WIN32
765                         if(!delimiter)
766                                 delimiter = strrchr(optarg,'\\');
767                         #endif
768                         
769                         /* create a new file stream which represents the tesh file to run */
770                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
771                         
772                         free(path);
773                         
774                         /* if the list of all tesh files to run already contains this file
775                          * destroy it and display a warning, otherwise add it in the list.
776                          */
777                         if(fstreams_contains(fstreams, fstream))
778                         {
779                                 fstream_free(&fstream);
780                                 WARN1("File %s already specified to be run", optarg);
781                         }
782                         else
783                                 fstreams_add(fstreams, fstream);
784                         
785                 }
786                 else if (c == '?')
787                 {
788                         /* unknown option, let getopt_long() displays the error */
789                         ERROR0("Command line processing failed : invalid command line");
790                         exit_code = EINVCMDLINE;
791                         err_kind = 1;
792                         return -1;
793                 }
794                 else
795                 {
796                         for(entry = opt_entries; entry->c != '\0'; ++entry)
797                                 
798                                 if(c == entry->c)
799                                 {
800                 
801                                         switch (entry->type)
802                                         {
803                                                 /* impossible */
804                                                 default:
805                                                 ERROR0("Command line processing failed : internal error");
806                                                 exit_code = EPROCCMDLINE;
807                                                 err_kind = 1;
808                                                 return -1;
809                                                 
810                                                 
811                                                 /* flag options */
812                                                 case flag:
813                                                 /* set the flag to one */
814                                                 *(int*) entry->value = 1;
815                                                 
816                                                 break;
817                                                 
818                                                 /* string options */
819                                                 case string:
820                 
821                                                 if(!optarg)
822                                                 {
823                                                         /* an option with a optional arg is specified use the entry->optional_value */
824                                                         optarg = (char*)entry->optional_value;
825                                                 }
826                                                 else if (*optarg == '\0')
827                                                 {
828                                                         /* a non optional argument is not specified */
829                                                         ERROR2("Option %c \"%s\"requires an argument",entry->c,entry->long_name);
830                                                         exit_code = ENOARG;
831                                                         err_kind = 1;
832                                                         return -1;
833                                                 }
834                                                 
835                                                 /* --load-directory option */
836                                                 if(!strcmp(entry->long_name,"load-directory"))
837                                                 {
838                                                         char* path;
839                                                         
840                                                         if(translatepath(optarg, &path) < 0)
841                                                         {
842                                                                 exit_code = errno;
843                                                                 err_kind = 0;
844                                                                 
845                                                                 if(ENOTDIR == errno)
846                                                                         ERROR1("%s is not a directory",optarg);
847                                                                 else
848                                                                         ERROR0("Insufficient memory is available to process the command line - system error");
849                                                                         
850                                                                 return -1;
851                                                                 
852                                                         }
853                                                         else
854                                                         {
855                                                                 
856                                                                 directory = directory_new(path);
857                                                                 free(path);
858                                                 
859                                                                 if(directories_contains(directories, directory))
860                                                                 {
861                                                                         directory_free((void**)&directory);
862                                                                         WARN1("Directory %s already specified to be load",optarg);
863                                                                 }
864                                                                 else
865                                                                          directories_add(directories, directory);
866                                                                          
867                                                                 
868                                                         }                       
869                                                 }
870                                                 else if(!strcmp(entry->long_name,"directory"))
871                                                 {
872                                                         char* path ;
873                                                         
874                                                         if(translatepath(optarg, &path) < 0)
875                                                         {
876                                                                 exit_code = errno;
877                                                                 err_kind = 0;
878                                                                 
879                                                                 if(ENOTDIR == errno)
880                                                                         ERROR1("%s is not a directory",optarg);
881                                                                 else
882                                                                         ERROR0("Insufficient memory is available to process the command line - system error");
883                                                                         
884                                                                 return -1;
885                                                         }
886                                                         else
887                                                         {
888                                                                 char* buffer = getcwd(NULL, 0);
889
890                                                                 
891                                                                 if(!strcmp(buffer, path))
892                                                                         WARN1("Already in the directory %s", optarg);
893                                                                 else if(!print_directory_flag)
894                                                                         INFO1("Entering directory \"%s\"",path);
895
896                                                                 chdir(path);
897                                                                 free(path);
898                                                 
899                                                                 free(buffer);
900                                                         }       
901                                                 }
902                                                 
903                                                 /* --suffix option */
904                                                 else if(!strcmp(entry->long_name,"suffix"))
905                                                 {
906                                                         if(strlen(optarg) > MAX_SUFFIX)
907                                                         {
908                                                                 ERROR1("Suffix %s too long",optarg);
909                                                                 exit_code = ESUFFIXTOOLONG;
910                                                                 err_kind = 1;
911                                                                 return -1;
912                                                         }
913                                                         
914                                                         if(optarg[0] == '.')
915                                                         {
916                                                                 char* cur;
917                                                                 unsigned int i;
918                                                                 int exists = 0;
919
920                                                                 char* suffix = xbt_new0(char, MAX_SUFFIX + 2);
921                                                                 sprintf(suffix,".%s",optarg);
922                                                                 
923                                                                 xbt_dynar_foreach(suffixes, i, cur)
924                                                                 {
925                                                                         if(!strcmp(suffix, cur))
926                                                                         {
927                                                                                 exists = 1;
928                                                                                 break;
929                                                                         }
930                                                                 }
931
932                                                                 if(exists)
933                                                                         WARN1("Suffix %s already specified to be used", optarg);
934                                                                 else
935                                                                         xbt_dynar_push(suffixes, &suffix);
936                                                         }
937                                                         else
938                                                         {
939                                                                 char* cur;
940                                                                 unsigned int i;
941                                                                 int exists = 0;
942
943                                                                 xbt_dynar_foreach(suffixes, i, cur)
944                                                                 {
945                                                                         if(!strcmp(optarg, 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                                                                 {
956                                                                         char* suffix = strdup(optarg);
957                                                                         xbt_dynar_push(suffixes, &suffix);
958                                                                 }
959                                                         }
960                                                 }
961                                                 /* --file option */
962                                                 else if(!strcmp(entry->long_name,"file"))
963                                                 {
964                                                         char* path;
965                                                         char* delimiter;
966                                                         
967                                                         if(getpath(optarg, &path) < 0)
968                                                         {
969                                                                 exit_code = errno;
970                                                                 err_kind = 0;
971                                                                 
972                                                                 if(ENOENT == errno)
973                                                                         ERROR1("File %s does not exist", optarg);
974                                                                 else
975                                                                         ERROR1("System error :`(%s)'", strerror(errno));
976                                                                 
977                                                                 return -1;
978                                                         }
979                                                         
980                                                         delimiter = strrchr(optarg,'/');
981
982                                                         #ifdef _XBT_WIN32
983                                                         if(!delimiter)
984                                                                 delimiter = strrchr(optarg,'\\');
985                                                         #endif
986                                                         
987                                                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
988                                                         
989                                                         free(path);
990                                                         
991                                                         if(fstreams_contains(fstreams, fstream))
992                                                         {
993                                                                 fstream_free(&fstream);
994                                                                 WARN1("File %s already specified to run", optarg);
995                                                         }
996                                                         else
997                                                                 fstreams_add(fstreams, fstream);
998                                                 }
999                                                 /* --include-dir option */
1000                                                 else if(!strcmp(entry->long_name,"include-dir"))
1001                                                 {
1002                                                         
1003                                                         char* path ;
1004                                                         
1005                                                         if(translatepath(optarg, &path) < 0)
1006                                                         {
1007                                                                 exit_code = errno;
1008                                                                 err_kind = 0;
1009                                                                 
1010                                                                 if(ENOTDIR == errno)
1011                                                                         ERROR1("%s is not a directory",optarg);
1012                                                                 else
1013                                                                         ERROR0("Insufficient memory is available to process the command line - system error");
1014                                                                         
1015                                                                 return -1;
1016                                                         }
1017                                                         
1018                                                         else
1019                                                         {
1020                                                                 int exists = 0;
1021                                                                 unsigned int i;
1022                                                                 directory_t cur;
1023                                                                 directory = directory_new(path);
1024                                                                 free(path);
1025
1026                                                                 xbt_dynar_foreach(include_dirs, i , cur)
1027                                                                 {
1028                                                                         if(!strcmp(cur->name, optarg))
1029                                                                         {
1030                                                                                 exists = 1;
1031                                                                                 break;
1032                                                                         }
1033                                                                 }
1034
1035                                                                 if(exists)
1036                                                                 {
1037                                                                         directory_free((void**)&directory);
1038                                                                         WARN1("Include directory %s already specified to be used",optarg);
1039                                                                         
1040                                                                 }
1041                                                                 else
1042                                                                         xbt_dynar_push(include_dirs, &directory);
1043
1044                                                         }
1045                                                 }
1046                                                 /* --exclude option */ 
1047                                                 else if(!strcmp(entry->long_name,"exclude"))
1048                                                 {
1049                                                         char* path;
1050                                                         char* delimiter;
1051                         
1052                                                         if(getpath(optarg, &path) < 0)
1053                                                         {
1054                                                                 exit_code = errno;
1055                                                                 err_kind = 0;
1056                                                                 
1057                                                                 if(ENOENT == errno)
1058                                                                         ERROR1("file %s does not exist", optarg);
1059                                                                 else
1060                                                                         ERROR0("Insufficient memory is available to process the command line - system error");
1061                                                                         
1062                                                                 return -1;
1063                                                         }
1064                         
1065                                                         delimiter = strrchr(optarg,'/');
1066
1067                                                         #ifdef _XBT_WIN32
1068                                                         if(!delimiter)
1069                                                                 delimiter = strrchr(optarg,'\\');
1070                                                         #endif
1071                         
1072                                                         fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
1073                                                         free(path);
1074                         
1075                                                         if(excludes_contains(excludes, fstream))
1076                                                         {
1077                                                                 fstream_free(&fstream);
1078                                                                 WARN1("File %s already specified to be exclude", optarg);
1079                                                         }
1080                                                         else
1081                                                                 excludes_add(excludes, fstream);
1082                                                                         
1083                                                 }
1084                                                 else if(!strcmp(entry->long_name,"log"))
1085                                                 {
1086                                                         /* NOTHING TODO : log for xbt */
1087                                                 }
1088                                                 else
1089                                                 {
1090                                                         INFO1("Unexpected option %s", optarg);
1091                                                         return -1;
1092                                                 }
1093                                                 
1094                                                 
1095                                                 break;
1096                                                 
1097                                                 /* strictly positve number options */
1098                                                 case number:
1099                                                         
1100                                                 if ((NULL == optarg) && (argc > optind))
1101                                                 {
1102                                                         const char* cp;
1103                                                         
1104                                                         for (cp = argv[optind]; isdigit(cp[0]); ++cp)
1105                                                                 if(cp[0] == '\0')
1106                                                                         optarg = argv[optind++];
1107                                                 }
1108                 
1109                                                 /* the number option is specified */
1110                                                 if(NULL != optarg)
1111                                                 {
1112                                                         int i = atoi(optarg);
1113                                                         const char *cp;
1114
1115                                                         for (cp = optarg; isdigit(cp[0]); ++cp);
1116                 
1117                                                         if (i < 1 || cp[0] != '\0')
1118                                                         {
1119                                                                 ERROR2("Option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name);
1120                                                                 exit_code = ENOTPOSITIVENUM;
1121                                                                 err_kind = 1;
1122                                                                 return -1;
1123                                                         }
1124                                                         else
1125                                                                 *(int*)entry->value = i;
1126                                                 }
1127                                                 /* the number option is specified but has no arg, use the optional value*/
1128                                                 else
1129                                                         *(int*)entry->value = *(int*) entry->optional_value;
1130                                                 
1131                                                 break;
1132                 
1133                                 }
1134                                 break;
1135                         }
1136                 }
1137         }
1138
1139         return 0;
1140 }
1141
1142 static void
1143 print_usage(void)
1144 {
1145         const char **cpp;
1146         FILE* stream;
1147         
1148         stream = exit_code ? stderr : stdout;
1149
1150         if(!screen_cleaned)
1151         {
1152                 #ifdef _XBT_WIN32
1153                 system("cls");
1154                 #else
1155                 system("clear");
1156                 #endif
1157                 screen_cleaned = 1;
1158         }
1159         
1160         fprintf (stream, "Usage: tesh [options] [file] ...\n");
1161         
1162         for (cpp = usage; *cpp; ++cpp)
1163                 fputs (*cpp, stream);
1164         
1165         fprintf(stream, "\nReport bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>\n");
1166 }
1167
1168 static void
1169 print_version(void)
1170 {
1171         if(!screen_cleaned)
1172         {
1173                 #ifdef _XBT_WIN32
1174                 system("cls");
1175                 #else
1176                 system("clear");
1177                 #endif
1178                 screen_cleaned = 1;
1179         }
1180
1181         /* TODO : display the version of tesh */
1182         printf("Version :\n");
1183         printf("  tesh version %s : Mini shell specialized in running test units by Martin Quinson \n", version);
1184         printf("  and Malek Cherier\n");
1185         printf("  Copyright (c) 2007, 2008 Martin Quinson, Malek Cherier\n");
1186         printf("  All rights reserved\n");
1187         printf("  This program is free software; you can redistribute it and/or modify it\n");
1188         printf("  under the terms of the license (GNU LGPL) which comes with this package.\n\n");
1189         
1190         if(!print_usage_flag)
1191                 printf("Report bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>");
1192 }
1193
1194 static void
1195 print_readme(void)
1196 {
1197         size_t len;
1198         char * line = NULL;
1199         
1200         FILE* stream = fopen("examples/README.tesh", "r");
1201         
1202         if(!stream)
1203         {
1204                 ERROR0("Unable to locate the README.txt file");
1205                 exit_code = EREADMENOTFOUND;
1206                 err_kind = 1;
1207                 return;
1208         }
1209         
1210         while(getline(&line, &len, stream) != -1)
1211                 printf("%s",line);
1212                 
1213         fclose(stream);
1214 }
1215
1216