Logo AND Algorithmique Numérique Distribuée

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