Logo AND Algorithmique Numérique Distribuée

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