2 * src/main.c - this file contains the main function of tesh.
\r
4 * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved.
\r
6 * This program is free software; you can redistribute it and/or modify it
\r
7 * under the terms of the license (GNU LGPL) which comes with this package.
\r
12 #include <fstream.h>
\r
13 #include <fstreams.h>
\r
14 #include <directory.h>
\r
15 #include <directories.h>
\r
16 #include <excludes.h>
\r
18 #include <getpath.h>
\r
23 * entry used to define the parameter of a tesh option.
\r
25 typedef struct s_optentry
\r
27 int c; /* the character of the option */
\r
30 * the type of the argument of the option
\r
34 flag, /* it's a flag option, by default the flag is set to zero */
\r
35 string, /* the option has strings as argument */
\r
36 number /* the option has an integral positive number as argument */
\r
39 byte* value; /* the value of the option */
\r
40 byte* optional_value; /* the optional value of the option if not specified */
\r
41 const char * long_name; /* the long name of the command */
\r
42 }s_optentry_t,* optentry_t;
\r
46 XBT_LOG_NEW_DEFAULT_CATEGORY(tesh,"TEst SHell utility");
\r
49 /* Windows specific : the previous process error mode */
\r
51 prev_error_mode = 0;
\r
54 /* this object represents the root directory */
\r
56 root_directory = NULL;
\r
58 /* the current version of tesh */
\r
62 /* ------------------------------------------------------------ */
\r
64 /* ------------------------------------------------------------ */
\r
66 /* ------------------------------------------------------------ */
\r
68 /* ------------------------------------------------------------ */
\r
71 /* --jobs is specified with arg */
\r
75 /* --jobs option is not specified (use the default job count) */
\r
77 default_jobs_nb = 1;
\r
79 /* --jobs is specified but has no arg (one job per unit) */
\r
81 optional_jobs_nb = -1;
\r
83 /* the global timeout */
\r
85 timeout = INDEFINITE;
\r
87 /* ------------------------------------------------------------ */
\r
89 /* ------------------------------------------------------------ */
\r
91 /* --C change the directory before running the units */
\r
92 static directories_t
\r
95 /* the include directories : see the !i metacommand */
\r
97 include_dirs = NULL;*/
\r
99 include_dirs = NULL;
\r
101 /* the list of tesh files to run */
\r
109 /* the list of tesh file suffixes */
\r
118 /* ------------------------------------------------------------ */
\r
120 /* ------------------------------------------------------------ */
\r
122 /* if 1, keep going when some commands can't be found
\r
123 * default value 0 : not keep going
\r
126 keep_going_flag = 0;
\r
128 /* if 1, ignore failures from commands
\r
129 * default value : do not ignore failures
\r
132 keep_going_unit_flag = 0;
\r
134 /* if 1, display tesh usage */
\r
136 print_usage_flag = 0;
\r
138 /* if 1, display the tesh version */
\r
140 print_version_flag = 0;
\r
142 /* if 1, the syntax of all tesh files is checked
\r
143 * before running them
\r
146 check_syntax_flag = 0;
\r
148 /* if 1, the status of all the units is display at
\r
154 /* if 1 and the flag want_summay is set to 1 tesh display the detailed summary of the run */
\r
156 detail_summary_flag = 0;
\r
158 /* if 1, the directories are displayed */
\r
160 print_directory_flag = 0;
\r
162 /* if 1, just check the syntax of all the tesh files
\r
168 /* if 1, display the tesh files syntax and exit */
\r
170 print_readme_flag = 0;
\r
176 just_print_flag = 0;
\r
179 env_overrides_flag = 0;
\r
182 print_database_flag = 0;
\r
187 /* the semaphore used to synchronize the jobs */
\r
191 /* the semaphore used by the runner to wait the end of all the units */
\r
216 /* the table of the entries of the options */
\r
217 static const struct s_optentry opt_entries[] =
\r
219 { 'C', string, (byte*)NULL, 0, "directory" },
\r
220 { 'x', string, (byte*)&suffixes, 0, "suffix" },
\r
221 { 'e', flag, (byte*)&env_overrides_flag, 0, "environment-overrides", },
\r
222 { 'f', string, (byte*)&fstreams, 0, "file" },
\r
223 { 'h', flag, (byte*)&print_usage_flag, 0, "help" },
\r
224 { 'a', flag, (byte*)&print_readme_flag, 0, "semantic" },
\r
225 { 'i', flag, (byte*)&keep_going_unit_flag, 0, "keep-going-unit" },
\r
226 { 'I', string, (byte*)&include_dirs, 0, "include-dir" },
\r
227 { 'j', number, (byte*)&jobs_nb, (byte*) &optional_jobs_nb, "jobs" },
\r
228 { 'k', flag, (byte*)&keep_going_flag, 0, "keep-going" },
\r
229 { 'm', flag, (byte*)&detail_summary_flag, 0, "detail-summary" },
\r
230 { 'c', flag, (byte*)&just_print_flag, 0, "just-print" },
\r
231 { 'd', flag, (byte*)&print_database_flag, 0,"display-data-base" },
\r
232 { 'q', flag, (byte*)&question_flag, 0, "question_flag" },
\r
233 { 's', flag, (byte*)&silent_flag, 0, "silent" },
\r
234 { 'V', flag, (byte*)&print_version_flag, 0, "version" },
\r
235 { 'w', flag, (byte*)&print_directory_flag, 0,"dont-display-directory" },
\r
236 { 'n', flag, (byte*)&dry_run_flag, 0, "dry-run"},
\r
237 { 't', number, (byte*)&timeout, 0, "timeout" },
\r
238 { 'S', flag, (byte*)&check_syntax_flag, 0, "check-syntax"},
\r
239 { 'r', string, (byte*)&directories, 0, "load-directory"},
\r
240 { 'v', flag, (byte*)&summary_flag, 0, "summary"},
\r
241 { 'F', string,(byte*)&excludes, 0, "exclude"},
\r
242 { 'l', string,(byte*)&logs,0,"log"},
\r
246 /* the tesh usage */
\r
247 static const char* usage[] =
\r
250 " -C DIRECTORY, --directory=DIRECTORY Change to DIRECTORY before running any commands.\n",
\r
251 " -e, --environment-overrides Environment variables override files.\n",
\r
252 " -f FILE, --file=FILE Read FILE as a teshfile.\n",
\r
254 " all argument of the command line without\n",
\r
255 " option is dealed as a tesh file.\n",
\r
256 " -h, --help Print this message and exit.\n",
\r
257 " -i, --keep-going-unit Ignore failures from commands.\n",
\r
258 " The possible failures are :\n",
\r
259 " - the exit code differ from the expected\n",
\r
260 " - the signal throw differ from the expected\n",
\r
261 " - the output differ from the expected\n",
\r
262 " - the read pipe is broken\n",
\r
263 " - the write pipe is broken\n",
\r
264 " - the command assigned delay is outdated\n",
\r
265 " -I DIRECTORY, --include-dir=DIRECTORY Search DIRECTORY for included files.\n",
\r
266 " -j [N], --jobs[=N] Allow N commands at once; infinite commands with\n"
\r
268 " -k, --keep-going Keep going when some commands can't be made or\n",
\r
270 " -c, --just-print Don't actually run any commands; just print them.\n",
\r
271 " -p, --print-data-base Display tesh's internal database.\n",
\r
272 " -q, --question Run no commands; exit status says if up to date.\n",
\r
273 " -s, --silent, Don't echo commands.\n",
\r
274 " -V, --version Print the version number of tesh and exit.\n",
\r
275 " -d, --dont-print-directory Don't display the current directory.\n",
\r
276 " -n, --dry-run Check the syntax of the specified tesh files, display the result and exit.\n",
\r
277 " -t, --timeout Wait the end of the commands at most timeout seconds.\n",
\r
278 " -S, --check-syntax Check the syntax of the tesh files before run them. \n",
\r
279 " -x, --suffix Consider the new suffix for the tesh files.\n"
\r
281 " the default suffix for the tesh files is \".tesh\".\n",
\r
282 " -a, --read-me Print the read me file and exit.\n",
\r
283 " -b, --build-file Build a tesh file.\n",
\r
284 " -r, --load-directory Run all the tesh files located in the directories specified by the option --directory.\n",
\r
285 " -v, --summary Display the status of the commands.\n",
\r
286 " -m, --detail-summary Detail the summary of the run.\n",
\r
287 " -F file , --exclude=FILE Ignore the tesh file FILE.\n",
\r
288 " -l format, --log Format of the xbt logs.\n",
\r
292 /* the string of options of tesh */
\r
294 optstring[1 + sizeof (opt_entries) / sizeof (opt_entries[0]) * 3];
\r
296 /* the option table of tesh */
\r
298 option longopts[(sizeof (opt_entries) / sizeof (s_optentry_t))];
\r
301 init_options(void);
\r
304 process_command_line(int argc, char** argv);
\r
313 print_version(void);
\r
319 print_readme(void);
\r
326 sig_abort_handler(int signum)
\r
328 /* TODO : implement this function */
\r
329 INFO0("sig_abort_handler() called");
\r
333 sig_int_handler(int signum)
\r
335 /* TODO : implement this function */
\r
336 INFO0("sig_int_handler() called");
\r
340 free_string(void* str)
\r
342 free(*(void**)str);
\r
346 main(int argc, char* argv[])
\r
350 /* set the locale to the default*/
\r
351 setlocale(LC_ALL,"");
\r
353 /* initialize tesh */
\r
357 /* process the command line */
\r
358 if(process_command_line(argc, argv) < 0)
\r
361 /* move to the root directory (the directory may change during the command line processing) */
\r
362 chdir(root_directory->name);
\r
364 /* initialize the xbt library
\r
365 * for thread portability layer
\r
368 /* xbt initialization */
\r
370 if((_argc = xbt_dynar_length(logs)))
\r
374 char** _argv = (char**)calloc(_argc, sizeof(char*));
\r
376 for(i = 0; i < _argc; i++)
\r
377 xbt_dynar_pop(logs, &(_argv[i]));
\r
379 xbt_init(&_argc, _argv);
\r
387 xbt_init(&argc, argv);
\r
389 /* the user wants to display the usage of tesh */
\r
390 if(print_version_flag)
\r
394 if(!print_usage_flag)
\r
398 /* the user wants to display the usage of tesh */
\r
399 if(print_usage_flag)
\r
405 /* the user wants to display the semantic of the tesh file metacommands */
\r
406 if(print_readme_flag)
\r
412 /* load tesh files */
\r
416 /* use by the finalize function to known if it must display the tesh usage */
\r
420 {/* --jobs is not specified (use the default value) */
\r
421 jobs_nb = default_jobs_nb;
\r
423 else if(optional_jobs_nb == jobs_nb)
\r
424 {/* --jobs option is specified with no args (use one job per unit) */
\r
425 jobs_nb = fstreams_get_size(fstreams);
\r
428 if(jobs_nb > fstreams_get_size(fstreams))
\r
429 {/* --jobs = N is specified and N is more than the number of tesh files */
\r
431 WARN0("Number of requested jobs exceed the number of files to run");
\r
433 /* assume one job per file */
\r
434 jobs_nb = fstreams_get_size(fstreams);
\r
437 /* initialize the semaphore used to synchronize all the units */
\r
438 jobs_sem = xbt_os_sem_init(jobs_nb);
\r
440 /* initialize the semaphore used by the runner to wait for the end of all units */
\r
441 units_sem = xbt_os_sem_init(0);
\r
443 /* initialize the runner */
\r
444 if(runner_init(check_syntax_flag, timeout, fstreams) < 0)
\r
447 if(just_print_flag && silent_flag)
\r
450 if(just_print_flag && dry_run_flag)
\r
451 WARN0("mismatch in the syntax : --just-check-syntax and --just-display options at same time");
\r
453 /* run all the units */
\r
457 /* show the result of the units */
\r
458 if(summary_flag || dry_run_flag)
\r
459 runner_summarize();
\r
461 /* all the test are runned, destroy the runner */
\r
464 /* then, finalize tesh (release all the allocated memory and exits) */
\r
473 /* init -- initialize tesh : allocated all the objects needed by tesh to run
\r
474 * the tesh files specified in the command line.
\r
476 * return If successful the function returns zero. Otherwise the function returns
\r
477 * -1 and sets the global variable errno to the appropriate error code.
\r
486 char* suffix = strdup(".tesh");
\r
489 /* Windows specific : don't display the general-protection-fault message box and
\r
490 * the the critical-error-handler message box (instead the system send the error
\r
491 * to the calling process : tesh)
\r
493 prev_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
\r
495 struct sigaction act;
\r
496 /* Ignore pipe issues.
\r
497 * They will show up when we try to send data to dead buddies,
\r
498 * but we will stop doing so when we're done with provided input
\r
500 memset(&act,0, sizeof(struct sigaction));
\r
501 act.sa_handler = SIG_IGN;
\r
502 sigaction(SIGPIPE, &act, NULL);
\r
505 memset(&act,0, sizeof(struct sigaction));
\r
506 act.sa_handler = sig_abort_handler;
\r
507 sigaction(SIGABRT, &act, NULL);
\r
509 memset(&act,0, sizeof(struct sigaction));
\r
510 act.sa_handler = sig_int_handler;
\r
511 sigaction(SIGINT, &act, NULL);
\r
515 err_mutex = xbt_os_mutex_init();
\r
517 /* handle the abort signal */
\r
518 /*signal(SIGABRT, sig_abort_handler);*/
\r
520 /* handle the interrupt signal */
\r
521 /*signal(SIGINT, sig_int_handler);*/
\r
523 /* used to store the files to run */
\r
524 if(!(fstreams = fstreams_new(DEFAULT_FSTREAMS_CAPACITY, (void_f_pvoid_t)fstream_free)))
\r
526 ERROR1("(system error) %s", strerror(errno));
\r
530 /* register the current directory */
\r
531 if(!(buffer = getcwd(NULL, 0)))
\r
533 ERROR1("(system error) %s", strerror(errno));
\r
537 /* save the root directory */
\r
538 if(!(root_directory = directory_new(buffer)))
\r
540 ERROR1("(system error) %s", strerror(errno));
\r
546 /* this vector contains all the errors of the run */
\r
547 errors = xbt_dynar_new(sizeof(xerror_t), free);
\r
549 /* the directories to loads */
\r
550 if(!(directories = directories_new()))
\r
552 ERROR1("(system error) %s", strerror(errno));
\r
556 /* the include directories */
\r
557 include_dirs = xbt_dynar_new(sizeof(directory_t), (void_f_pvoid_t)directory_free);
\r
559 /* xbt logs option */
\r
560 if(!(logs = xbt_dynar_new(sizeof(char*), free_string)))
\r
562 ERROR1("(system error) %s", strerror(errno));
\r
566 /* the excluded files */
\r
567 if(!(excludes = excludes_new()))
\r
569 ERROR1("(system error) %s", strerror(errno));
\r
574 suffixes = xbt_dynar_new(sizeof(char*),free_string);
\r
576 /* register the default suffix ".tesh" */
\r
577 xbt_dynar_push(suffixes, &suffix);
\r
582 /* load -- load the tesh files to run */
\r
587 /* if the directories object is not empty load all the tesh files contained in
\r
588 * the directories specified in the command line (this tesh files must have the
\r
589 * a suffix specified in the suffixes object.
\r
591 if(!directories_is_empty(directories))
\r
592 directories_load(directories, fstreams, suffixes);
\r
594 /* if no tesh file has been specified in the command line try to load the default tesh file
\r
595 * teshfile from the current directory
\r
597 if(fstreams_is_empty(fstreams))
\r
599 struct stat buffer = {0};
\r
601 /* add the default tesh file if it exists in the current directory */
\r
602 if(!stat("teshfile", &buffer) && S_ISREG(buffer.st_mode))
\r
603 fstreams_add(fstreams, fstream_new(getcwd(NULL, 0), "teshfile"));
\r
606 /* excludes the files specified in the command line and stored in the excludes object */
\r
607 if(!excludes_is_empty(excludes) && !fstreams_is_empty(fstreams))
\r
609 /* check the files to excludes before */
\r
610 excludes_check(excludes, fstreams);
\r
612 /* exclude the specified tesh files */
\r
613 fstreams_exclude(fstreams, excludes);
\r
616 /* if the fstreams object is empty use the stdin */
\r
617 if(fstreams_is_empty(fstreams))
\r
618 fstreams_add(fstreams, fstream_new(NULL, "stdin"));
\r
620 /* load the tesh files (open them) */
\r
621 fstreams_load(fstreams);
\r
625 /* finalize -- cleanup all the allocated objects and display the tesh usage if needed */
\r
630 if(is_tesh_root && !summary_flag)
\r
635 xbt_dynar_foreach(errors, i, error)
\r
638 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));
\r
639 else if(!error->command && error->unit)
\r
640 fprintf(stderr, "[tesh/ERROR] %s : Unit `%s' - C%d (%s)\n", error->reason, error->unit, error->errcode, error_to_string(error->errcode));
\r
641 else if(!error->command && !error->unit && error->reason)
\r
642 fprintf(stderr, "[tesh/ERROR] %s : C%d (%s)\n", error->reason, error->errcode, error_to_string(error->errcode));
\r
643 else if(!error->command && !error->unit && !error->reason)
\r
644 fprintf(stderr, "[tesh/ERROR] C%d (%s)\n", error->errcode, error_to_string(error->errcode));
\r
649 /* delete vector of errors */
\r
651 xbt_dynar_free(&errors);
\r
653 xbt_os_mutex_destroy(err_mutex);
\r
655 /* delete the fstreams object */
\r
657 fstreams_free((void**)&fstreams);
\r
659 /* delete the excludes object */
\r
661 excludes_free((void**)&excludes);
\r
663 /* delete the directories object */
\r
665 directories_free((void**)&directories);
\r
667 /* delete the root directory object */
\r
669 directory_free((void**)&root_directory);
\r
671 /* delete the include directories object */
\r
673 xbt_dynar_free(&include_dirs);
\r
675 /* delete the list of tesh files suffixes */
\r
677 xbt_dynar_free(&suffixes);
\r
679 /* delete the xbt log options list */
\r
681 xbt_dynar_free(&logs);
\r
683 /* destroy the semaphore used to synchronize the units */
\r
685 xbt_os_sem_destroy(jobs_sem);
\r
687 /* destroy the semaphore used by the runner used to wait for the end of the units */
\r
689 xbt_os_sem_destroy(units_sem);
\r
691 /* exit from the xbt framework */
\r
694 /* Windows specific (restore the previouse error mode */
\r
696 SetErrorMode(prev_error_mode);
\r
699 if(!summary_flag && !dry_run_flag && !silent_flag && !just_print_flag && !print_version_flag && !print_usage_flag && is_tesh_root)
\r
702 INFO2("tesh terminated with exit code %d : %s",exit_code, "success");
\r
704 ERROR2("tesh terminated with exit code %d : %s",exit_code, error_to_string(exit_code));
\r
708 /* exit with the last error code */
\r
712 /* init_options -- initialize the options string */
\r
714 init_options (void)
\r
719 /* the function has been already called */
\r
720 if(optstring[0] != '\0')
\r
727 for (i = 0; opt_entries[i].c != '\0'; ++i)
\r
729 /* initialize the long name of the option*/
\r
730 longopts[i].name = (opt_entries[i].long_name == 0 ? "" : opt_entries[i].long_name);
\r
732 /* getopt_long returns the value of val */
\r
733 longopts[i].flag = 0;
\r
735 /* the short option */
\r
736 longopts[i].val = opt_entries[i].c;
\r
738 /* add the short option in the options string */
\r
739 *p++ = opt_entries[i].c;
\r
741 switch (opt_entries[i].type)
\r
743 /* if this option is used to set a flag or if the argument must be ignored
\r
744 * the option has no argument
\r
747 longopts[i].has_arg = no_argument;
\r
750 /* the option has an argument */
\r
756 if(opt_entries[i].optional_value != 0)
\r
760 longopts[i].has_arg = optional_argument;
\r
763 longopts[i].has_arg = required_argument;
\r
770 longopts[i].name = 0;
\r
773 /* process_command_line -- process the command line
\r
775 * param argc the number of the arguments contained by the command line.
\r
776 * param The array of C strings containing all the arguments of the command
\r
779 * return If successful, the function returns 0. Otherwise -1 is returned
\r
780 * and sets the global variable errno to indicate the error.
\r
782 * errors [ENOENT] A file name specified in the command line does not exist
\r
786 process_command_line(int argc, char** argv)
\r
788 register const struct s_optentry* entry;
\r
790 directory_t directory;
\r
793 /* initialize the options string of tesh */
\r
796 /* let the function getopt_long display the errors if any */
\r
799 /* set option index to zero */
\r
802 while (optind < argc)
\r
804 c = getopt_long(argc, argv, optstring, longopts, (int *) 0);
\r
808 /* end of the command line or "--". */
\r
813 /* no option specified, assume it's a tesh file to run */
\r
817 /* getpath returns -1 when the file to get the path doesn't exist */
\r
818 if(getpath(optarg, &path) < 0)
\r
822 if(ENOENT == errno)
\r
823 ERROR1("File %s does not exist", optarg);
\r
825 ERROR0("Insufficient memory is available to parse the command line : system error");
\r
830 /* get to the last / (if any) to get the short name of the file */
\r
831 delimiter = strrchr(optarg,'/');
\r
833 /* create a new file stream which represents the tesh file to run */
\r
834 fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
\r
838 /* if the list of all tesh files to run already contains this file
\r
839 * destroy it and display a warning, otherwise add it in the list.
\r
841 if(fstreams_contains(fstreams, fstream))
\r
843 fstream_free(&fstream);
\r
844 WARN1("File %s already specified to be run", optarg);
\r
847 fstreams_add(fstreams, fstream);
\r
855 /* unknown option, let getopt_long() displays the error */
\r
856 ERROR0("Command line processing failed : invalid command line");
\r
857 exit_code = EINVCMDLINE;
\r
862 for(entry = opt_entries; entry->c != '\0'; ++entry)
\r
867 switch (entry->type)
\r
871 ERROR0("Command line processing failed : internal error");
\r
872 exit_code = EPROCCMDLINE;
\r
878 /* set the flag to one */
\r
879 *(int*) entry->value = 1;
\r
883 /* string options */
\r
888 /* an option with a optional arg is specified use the entry->optional_value */
\r
889 optarg = (char*)entry->optional_value;
\r
891 else if (*optarg == '\0')
\r
893 /* a non optional argument is not specified */
\r
894 ERROR2("Option %c \"%s\"requires an argument",entry->c,entry->long_name);
\r
895 exit_code = ENOARG;
\r
899 /* --load-directory option */
\r
900 if(!strcmp(entry->long_name,"load-directory"))
\r
903 struct stat info = {0};
\r
904 if(stat(optarg, &info) || !S_ISDIR(info.st_mode))
\r
906 ERROR1("%s is not a directory",optarg);
\r
907 exit_code = ENOTDIR;
\r
914 if(translatepath(optarg, &path) < 0)
\r
918 if(ENOTDIR == errno)
\r
919 ERROR1("%s is not a directory",optarg);
\r
921 ERROR0("Insufficient memory is available to process the command line - system error");
\r
930 directory = directory_new(optarg);
\r
932 directory = directory_new(path);
\r
936 if(directories_contains(directories, directory))
\r
938 directory_free((void**)&directory);
\r
939 WARN1("Directory %s already specified to be load",optarg);
\r
942 directories_add(directories, directory);
\r
947 else if(!strcmp(entry->long_name,"directory"))
\r
950 struct stat info = {0};
\r
951 if(stat(optarg, &info) || !S_ISDIR(info.st_mode))
\r
953 ERROR1("%s is not a directory",optarg);
\r
954 exit_code = ENOTDIR;
\r
961 if(translatepath(optarg, &path) < 0)
\r
965 if(ENOTDIR == errno)
\r
966 ERROR1("%s is not a directory",optarg);
\r
968 ERROR0("Insufficient memory is available to process the command line - system error");
\r
975 char* buffer = getcwd(NULL, 0);
\r
979 if(!strcmp(buffer, optarg))
\r
980 WARN1("Already in the directory %s", optarg);
\r
981 else if(!print_directory_flag)
\r
982 INFO1("Entering directory \"%s\"",optarg);
\r
987 if(!strcmp(buffer, path))
\r
988 WARN1("Already in the directory %s", optarg);
\r
989 else if(!print_directory_flag)
\r
990 INFO1("Entering directory \"%s\"",path);
\r
1002 /* --suffix option */
\r
1003 else if(!strcmp(entry->long_name,"suffix"))
\r
1005 if(strlen(optarg) > MAX_SUFFIX)
\r
1007 ERROR1("Suffix %s too long",optarg);
\r
1008 exit_code = ESUFFIXTOOLONG;
\r
1012 if(optarg[0] == '.')
\r
1018 char suffix[MAX_SUFFIX + 2] = {0};
\r
1019 sprintf(suffix,".%s",optarg);
\r
1021 xbt_dynar_foreach(suffixes, i, cur)
\r
1023 if(!strcmp(suffix, cur))
\r
1031 WARN1("Suffix %s already specified to be used", optarg);
\r
1033 xbt_dynar_push(suffixes, &suffix);
\r
1041 xbt_dynar_foreach(suffixes, i, cur)
\r
1043 if(!strcmp(optarg, cur))
\r
1051 WARN1("Suffix %s already specified to be used", optarg);
\r
1053 xbt_dynar_push(suffixes, &optarg);
\r
1056 /* --file option */
\r
1057 else if(!strcmp(entry->long_name,"file"))
\r
1062 if(getpath(optarg, &path) < 0)
\r
1064 exit_code = errno;
\r
1066 if(ENOENT == errno)
\r
1067 ERROR1("File %s does not exist", optarg);
\r
1069 ERROR0("Insufficient memory is available to process the command line - system error");
\r
1074 delimiter = strrchr(optarg,'/');
\r
1076 fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
\r
1080 if(fstreams_contains(fstreams, fstream))
\r
1082 fstream_free(&fstream);
\r
1083 WARN1("File %s already specified to run", optarg);
\r
1086 fstreams_add(fstreams, fstream);
\r
1088 /* --include-dir option */
\r
1089 else if(!strcmp(entry->long_name,"include-dir"))
\r
1092 struct stat info = {0};
\r
1093 if(stat(optarg, &info) || !S_ISDIR(info.st_mode))
\r
1095 ERROR1("%s is not a directory",optarg);
\r
1096 exit_code = ENOTDIR;
\r
1103 if(translatepath(optarg, &path) < 0)
\r
1105 exit_code = errno;
\r
1107 if(ENOTDIR == errno)
\r
1108 ERROR1("%s is not a directory",optarg);
\r
1110 ERROR0("Insufficient memory is available to process the command line - system error");
\r
1121 directory = directory_new(optarg);
\r
1123 directory = directory_new(path);
\r
1127 xbt_dynar_foreach(include_dirs, i , cur)
\r
1129 if(!strcmp(cur->name, optarg))
\r
1138 directory_free((void**)&directory);
\r
1139 WARN1("Include directory %s already specified to be used",optarg);
\r
1143 xbt_dynar_push(include_dirs, &directory);
\r
1147 /* --exclude option */
\r
1148 else if(!strcmp(entry->long_name,"exclude"))
\r
1153 if(getpath(optarg, &path) < 0)
\r
1155 exit_code = errno;
\r
1157 if(ENOENT == errno)
\r
1158 ERROR1("file %s does not exist", optarg);
\r
1160 ERROR0("Insufficient memory is available to process the command line - system error");
\r
1165 delimiter = strrchr(optarg,'/');
\r
1167 fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
\r
1170 if(excludes_contains(excludes, fstream))
\r
1172 fstream_free(&fstream);
\r
1173 WARN1("File %s already specified to be exclude", optarg);
\r
1176 excludes_add(excludes, fstream);
\r
1179 /* --log option */
\r
1180 else if(!strcmp(entry->long_name,"log"))
\r
1182 xbt_dynar_push(logs, &optarg);
\r
1186 INFO1("Unexpected option %s", optarg);
\r
1193 /* strictly positve number options */
\r
1196 if ((NULL == optarg) && (argc > optind))
\r
1200 for (cp = argv[optind]; isdigit(cp[0]); ++cp)
\r
1202 optarg = argv[optind++];
\r
1205 /* the number option is specified */
\r
1206 if(NULL != optarg)
\r
1208 int i = atoi(optarg);
\r
1211 for (cp = optarg; isdigit(cp[0]); ++cp);
\r
1213 if (i < 1 || cp[0] != '\0')
\r
1215 ERROR2("Option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name);
\r
1216 exit_code = ENOTPOSITIVENUM;
\r
1220 *(int*)entry->value = i;
\r
1222 /* the number option is specified but has no arg, use the optional value*/
\r
1224 *(int*)entry->value = *(int*) entry->optional_value;
\r
1243 stream = exit_code ? stderr : stdout;
\r
1245 fprintf (stream, "Usage: tesh [options] [file] ...\n");
\r
1247 for (cpp = usage; *cpp; ++cpp)
\r
1248 fputs (*cpp, stream);
\r
1250 fprintf(stream, "\nReport bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>\n");
\r
1254 print_version(void)
\r
1256 /* TODO : display the version of tesh */
\r
1257 printf("Version :\n");
\r
1258 printf(" tesh version %s : Mini shell specialized in running test units by Martin Quinson \n", version);
\r
1259 printf(" and Malek Cherier\n");
\r
1260 printf(" Copyright (c) 2007, 2008 Martin Quinson, Malek Cherier\n");
\r
1261 printf(" All rights reserved\n");
\r
1262 printf(" This program is free software; you can redistribute it and/or modify it\n");
\r
1263 printf(" under the terms of the license (GNU LGPL) which comes with this package.\n\n");
\r
1265 if(!print_usage_flag)
\r
1266 printf("Report bugs to <martin.quinson@loria.fr | malek.cherier@loria.fr>");
\r
1270 print_readme(void)
\r
1273 char * line = NULL;
\r
1275 FILE* stream = fopen("README.txt", "r");
\r
1279 ERROR0("Unable to locate the README.txt file");
\r
1280 exit_code = EREADMENOTFOUND;
\r
1284 while(getline(&line, &len, stream) != -1)
\r
1285 printf("%s",line);
\r