Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
cmdopt.cpp
Go to the documentation of this file.
1
27/* disable Compaq/HP C++ pure libc headers to allow POSIX symbols
28 such as SIGALRM or SIGKILL to be defined.*/
29#undef __PURE_CNAME
30
31#include <assert.h> /* for assert() */
32#include <ctype.h> /* for isspace */
33#include <errno.h> /* for errno */
34#include <signal.h> /* for raise, signal, SIG_IGN */
35#include <stdio.h> /* for *printf, fputs */
36#include <stdlib.h> /* for exit */
37#include <string.h> /* for str* */
38#ifndef _WIN32
39# include <unistd.h> /* for sleep */
40
41# if defined (_XOPEN_UNIX)
42# include <sys/resource.h> /* for struct rlimit, RLIMIT_CORE, ... */
43# endif
44
45#else
46# include <windows.h> /* for Sleep */
47#endif /* _WIN32 */
48
49#include "exec.h"
50#include "target.h"
51#include "util.h"
52
53#include "cmdopt.h"
54
55const char* exe_name;
57#ifndef _WIN32
58
59const char escape_code = '\\';
60const char default_path_sep = '/';
61const char suffix_sep = '.';
62const size_t exe_suffix_len = 0;
63
64#else /* Win32 */
65
66const char escape_code = '^';
67const char default_path_sep = '\\';
68const char suffix_sep = '.';
69const size_t exe_suffix_len = 4; /* strlen(".exe") == 4 */
70
71# ifndef _WIN32_WINNT
72# define _WIN32_WINNT 0x0500
73# endif
74
75# if _WIN32_WINNT >= 0x0500
76# define RLIMIT_AS
77# endif
78#endif
79
80static const char
81usage_text[] = {
82 "Usage: %s [OPTIONS] [targets]\n"
83 "\n"
84 " Treats each token in targets as the path to an executable. Each target\n"
85 " enumerated is executed, and the output is processed after termination.\n"
86 " If target prepended by '@' character, target is treated as text file\n"
87 " with list of targets (one target per line).\n"
88 " If the execution takes longer than a certain (configurable) amount of\n"
89 " time, the process is killed.\n"
90 "\n"
91 " -d dir Specify root directory for output reference files.\n"
92 " -h, -? Display usage information and exit.\n"
93 " -t seconds Set timeout before killing target (default is 10\n"
94 " seconds).\n"
95 " -x opts Specify command line options to pass to targets.\n"
96 " -- Terminate option processing and treat all arguments\n"
97 " that follow as targets.\n"
98 " --compat Use compatability mode test output parsing.\n"
99 " --nocompat Use standard test output parsing (default).\n"
100 " --help Display usage information and exit.\n"
101 " --exit=val Exit immediately with the specified return code.\n"
102 " --sleep=sec Sleep for the specified number of seconds.\n"
103 " --signal=sig Send itself the specified signal.\n"
104 " --ignore=sig Ignore the specified signal.\n"
105 " --ulimit=lim Set child process usage limits (see below).\n"
106 " --warn=alias Set compiler log warning pattern (see below).\n"
107 "\n"
108 " All short (single dash) options must be specified seperately.\n"
109 " If a short option takes a value, it may either be provided like\n"
110 " '-sval' or '-s val'.\n"
111 " If a long option take a value, it may either be provided like\n"
112 " '--option=value' or '--option value'.\n"
113 "\n"
114 " --ulimit sets limits on how much of a given resource or resorces\n"
115 " child processes are allowed to utilize. These limits take on two\n"
116 " forms, 'soft' and 'hard' limits. Options are specified in the form\n"
117 " 'resource:limit', where resource is a resource named below, and and\n"
118 " limit is a number, with value specifing the limit for the named\n"
119 " resource. If multiple limits are to be set, they can be specified\n"
120 " either in multiple instances of the --ulimit switch, or by specifying\n"
121 " additional limits in the same call by seperating the pairs with\n"
122 " commas. 'Soft' limits are specified by providing the resource name\n"
123 " in lowercase letters, while 'hard' limits are specified by providing\n"
124 " the resource name in uppercase letters. To set both limits, specify\n"
125 " the resource name in title case.\n"
126 "\n"
127 " --ulimit modes:\n"
128 " core Maximum size of core file, in bytes.\n"
129 " cpu Maximum CPU time, in seconds.\n"
130 " data Maximum data segment size, in bytes.\n"
131 " fsize Maximum size of generated files, in bytes.\n"
132 " nofile Maximum number of open file descriptors.\n"
133 " stack Maximum size of initial thread's stack, in bytes.\n"
134 " as Maximum size of available memory, in bytes.\n"
135 "\n"
136 " Note: Some operating systems lack support for some or all of the\n"
137 " ulimit modes. If a system is unable to limit a given property, a\n"
138 " warning message will be produced.\n"
139 "\n"
140 " --warn set the string used to parse compile and link logs. Rather\n"
141 " than specifying a search string, an alias code is provided,\n"
142 " coresponding to the output of a compiler and linker. Alias codes\n"
143 " are case sensitive.\n"
144 "\n"
145 " --warn modes:\n"
146 " acc HP aCC\n"
147 " cxx Compaq C++\n"
148 " eccp EDG eccp\n"
149 " gcc GNU gcc\n"
150 " icc Intel icc for Linux\n"
151 " mipspro SGI MIPSpro\n"
152 " sunpro Sun C++\n"
153 " vacpp IBM VisualAge C++\n"
154 " xlc IBM XLC++\n"
155};
156
157
163static void
164show_usage (int status)
165{
166 FILE* const where = status ? stderr : stdout;
167
168 assert (0 != exe_name);
169
170 fprintf (where, usage_text, exe_name);
171
172 exit (status);
173}
174
181static char*
182get_short_val (char* const* argv, int* idx)
183{
184 assert (0 != argv);
185 assert (0 != idx);
186
187 if ('\0' == argv [*idx][2])
188 return argv [++(*idx)];
189 else
190 return argv [*idx] + 2;
191}
192
200static char*
201get_long_val (char* const* argv, int* idx, size_t offset)
202{
203 assert (0 != argv);
204 assert (0 != idx);
205
206 if ('\0' == argv [*idx][offset])
207 return argv [++(*idx)];
208 else if ('=' == argv [*idx][offset])
209 return argv [*idx] + offset + 1;
210 else
211 return (char*)0;
212}
213
220static bool
221parse_limit_opts (const char* opts, struct target_opts* defaults)
222{
223 static const struct {
224 rw_rlimit** limit;
225 const char* name;
226 const char* caps;
227 const char* mixd;
228 size_t len;
229 } limits[] = {
230 {
231#ifdef RLIMIT_CORE
232 &defaults->core,
233#else
234 0,
235#endif /* RLIMIT_CORE */
236 "core", "CORE", "Core", 4 },
237 {
238#ifdef RLIMIT_CPU
239 &defaults->cpu,
240#else
241 0,
242#endif /* RLIMIT_CPU */
243 "cpu", "CPU", "Cpu", 3 },
244 {
245#ifdef RLIMIT_DATA
246 &defaults->data,
247#else
248 0,
249#endif /* RLIMIT_DATA */
250 "data", "DATA", "Data", 4 },
251 {
252#ifdef RLIMIT_FSIZE
253 &defaults->fsize,
254#else
255 0,
256#endif /* RLIMIT_FSIZE */
257 "fsize", "FSIZE", "Fsize", 5 },
258 {
259#ifdef RLIMIT_NOFILE
260 &defaults->nofile,
261#else
262 0,
263#endif /* RLIMIT_NOFILE */
264 "nofile", "NOFILE", "Nofile", 6 },
265 {
266#ifdef RLIMIT_STACK
267 &defaults->stack,
268#else
269 0,
270#endif /* RLIMIT_STACK */
271 "stack", "STACK", "Stack", 5 },
272 {
273#ifdef RLIMIT_AS
274 &defaults->as,
275#else
276 0,
277#endif /* RLIMIT_AS */
278 "as", "AS", "As", 2 },
279 { 0, 0, 0, 0, 0 }
280 };
281
282 const char* arg = opts;
283
284 assert (0 != opts);
285
286 while (arg && *arg) {
287
288 const size_t arglen = strlen (arg);
289
290 for (size_t i = 0; limits [i].name; ++i) {
291 if ( limits [i].len < arglen
292 && ( 0 == memcmp (limits [i].name, arg, limits [i].len)
293 || 0 == memcmp (limits [i].caps, arg, limits [i].len)
294 || 0 == memcmp (limits [i].mixd, arg, limits [i].len))
295 && ':' == arg [limits [i].len]) {
296
297 /* determine whether the hard limit and/or the soft limit
298 should be set. */
299 const bool hard = 0 != isupper (arg [0]);
300 const bool soft = 0 != islower (arg [1]);
301
302 arg += limits [i].len + 1;
303
304 if (!isdigit (*arg)) {
305 return 1;
306 }
307
308 char *end;
309 const long lim = strtol (arg, &end, 10);
310
311 arg = end;
312
313 if ('\0' != *arg && ',' != *arg)
314 break;
315
316 if (limits [i].limit) {
317 if (!*limits [i].limit) {
318 (*limits [i].limit) =
319 (rw_rlimit*)RW_MALLOC (sizeof (rw_rlimit));
320 (*limits [i].limit)->rlim_cur = RLIM_SAVED_CUR;
321 (*limits [i].limit)->rlim_max = RLIM_SAVED_MAX;
322 }
323 if (soft)
324 (*limits [i].limit)->rlim_cur = lim;
325
326 if (hard)
327 (*limits [i].limit)->rlim_max = lim;
328 }
329 else
330 warn ("Unable to process %s limit: Not supported\n",
331 limits [i].name);
332 break;
333 }
334 }
335
336 if (',' == *arg) {
337 ++arg;
338 }
339 else if ('\0' != *arg) {
340 return 1;
341 }
342 }
343 return 0;
344}
345
352static bool
353parse_warn_opts (const char* arg, struct target_opts* defaults)
354{
355 static const struct {
356 const char* name;
357 const char* pat;
358 } warn_set [] = {
359 { "acc", "Warning " },
360/*
361 { "cds", "UNKNOWN"},
362 { "como", "UNKNOWN"},
363*/
364 { "cxx", "Warning:"},
365 { "eccp", "warning:"},
366 { "gcc", "warning:"},
367 { "icc", "warning #"},
368 { "mipspro", "CC: WARNING"},
369 { "sunpro", "Warning:"},
370 { "vacpp", ": (W) "},
371 { "xlc", ": (W) "}, /* xlc and vacpp are synonyms. */
372 { 0, 0 }
373 };
374
375 assert (0 != arg);
376 assert (0 != defaults);
377
378 for (size_t i = 0; warn_set [i].name; ++i) {
379 if (0 == strcmp (warn_set [i].name, arg)) {
380
381 /* Set both compiler and linker warning string. */
382 defaults->c_warn = warn_set [i].pat;
383 defaults->l_warn = warn_set [i].pat;
384
385 return 0;
386 }
387 }
388
389 return 1;
390}
391
398static void
399bad_value (const char* opt, const char* val)
400{
401 assert (0 != opt);
402
403 terminate (1, "Bad argument for %s: %s\n", opt, val);
404}
405
411static void
412missing_value (const char* opt)
413{
414 assert (0 != opt);
415
416 terminate (1, "Missing argument for %s\n", opt);
417}
418
424static void
425bad_option (const char* opt)
426{
427 assert (0 != opt);
428
429 warn ("Unknown option: %s\n", opt);
430
431 show_usage (1);
432}
433
434int
435eval_options (int argc, char **argv, struct target_opts* defaults,
436 const char** exe_opts)
437{
438 const char opt_timeout[] = "-t";
439 const char opt_data_dir[] = "-d";
440 const char opt_t_flags[] = "-x";
441 const char opt_compat[] = "--compat";
442 const char opt_exit[] = "--exit";
443 const char opt_help[] = "--help";
444 const char opt_ignore[] = "--ignore";
445 const char opt_nocompat[] = "--nocompat";
446 const char opt_signal[] = "--signal";
447 const char opt_sleep[] = "--sleep";
448 const char opt_ulimit[] = "--ulimit";
449 const char opt_verbose[] = "--verbose";
450 const char opt_warn[] = "--warn";
451
452 int i;
453
454 assert (0 != argv);
455 assert (0 != defaults);
456
457 memset (defaults, 0, sizeof (target_opts));
458
459 /* The chain of preprocesor logic below initializes the defaults->c_warn
460 and defaults->l_warn values.
461 */
462#ifdef __GNUG__
463 parse_warn_opts ("Gcc", defaults);
464#elif defined (__HP_aCC)
465 parse_warn_opts ("Acc", defaults);
466#elif defined (__IBMCPP__)
467 parse_warn_opts ("Xlc", defaults);
468#elif defined (__SUNPRO_CC)
469 parse_warn_opts ("Sunpro", defaults);
470#elif defined (SNI)
471 parse_warn_opts ("Cds", defaults);
472#elif defined (__APOGEE__) /* EDG variant that doesn't define __EDG__. */
473 parse_warn_opts ("Como", defaults);
474
475/* The following are EDG variants, that define __EDG__ */
476#elif defined (__DECCXX)
477 parse_warn_opts ("Cxx", defaults);
478#elif defined (_SGI_COMPILER_VERSION)
479 parse_warn_opts ("Mipspro", defaults);
480#elif defined (__INTEL_COMPILER)
481 parse_warn_opts ("Icc", defaults);
482
483/* So we need to check for __EDG__ after we check for them. */
484#elif defined (__EDG__)
485 parse_warn_opts ("Eccp", defaults);
486#endif
487
488 if (1 == argc || '-' != argv [1][0])
489 return 1;
490
491 for (i = 1; i < argc && '-' == argv [i][0]; ++i) {
492
493 /* the name of the option being processed */
494 const char* optname = argv [i];
495
496 /* the option's argument, if any */
497 const char* optarg = 0;
498
499 char* end = 0;
500
501 switch (argv [i][1]) {
502 case '?': /* display help and exit with status of 0 */
503 case 'h':
504 show_usage (0);
505
506 case 'r':
507 ++i; /* Ignore -r option (makefile compat) */
508 break;
509
510 case 't': /* executable timeout in seconds */
511 optname = opt_timeout;
512 optarg = get_short_val (argv, &i);
513 if (optarg) {
514 if (!isdigit (*optarg))
515 bad_value (optname, optarg);
516
517 errno = 0;
518 defaults->timeout = unsigned (strtol (optarg, &end, 10));
519 if (*end || errno)
520 bad_value (optname, optarg);
521 }
522 else
523 missing_value (optname);
524
525 break;
526
527 case 'd': /* directory containing example reference files */
528 optname = opt_data_dir;
529 defaults->data_dir = get_short_val (argv, &i);
530 if (!defaults->data_dir)
531 missing_value (optname);
532 break;
533
534 case 'v': /* enable verbose mode */
535 optname = opt_verbose;
536 ++defaults->verbose;
537 break;
538
539 case 'x': /* command line options to pass to targets */
540 optname = opt_t_flags;
541 *exe_opts = get_short_val (argv, &i);
542 if (!*exe_opts)
543 missing_value (optname);
544 break;
545
546 case '-': /* long options */
547 {
548 const size_t arglen = strlen (argv [i]);
549
550 /* abort processing on --, eat token */
551 if ('\0' == argv [i][2])
552 return i+1;
553
554 if ( sizeof opt_compat - 1 == arglen
555 && !memcmp (opt_compat, argv [i], sizeof opt_compat)) {
556 /* enter compatibility mode */
557 defaults->compat = 1;
558 break;
559 }
560 else if ( sizeof opt_nocompat - 1 == arglen
561 && !memcmp (opt_nocompat, argv [i], sizeof opt_nocompat)) {
562 /* exit compatibility mode */
563 defaults->compat = 0;
564 break;
565 }
566 else if ( sizeof opt_exit - 1 <= arglen
567 && !memcmp (opt_exit, argv [i], sizeof opt_exit - 1)) {
568 /* exit immediately with the specified status */
569 optname = opt_exit;
570 optarg = get_long_val (argv, &i, sizeof opt_exit - 1);
571 if (optarg && *optarg) {
572 if (!isdigit (*optarg))
573 bad_value (optname, optarg);
574
575 errno = 0;
576 const long code = strtol (optarg, &end, 10);
577 if ('\0' == *end && !errno)
578 exit (int (code));
579 }
580 }
581 else if ( sizeof opt_help - 1 == arglen
582 && !memcmp (opt_help, argv [i], sizeof opt_help - 1)) {
583 /* display help and exit with status of 0 */
584 optname = opt_help;
585 show_usage (0);
586 break;
587 }
588 else if ( sizeof opt_sleep - 1 <= arglen
589 && !memcmp (opt_sleep, argv [i], sizeof opt_sleep - 1)) {
590 /* sleep for the specified number of seconds */
591 optname = opt_sleep;
592 optarg = get_long_val (argv, &i, sizeof opt_sleep - 1);
593 if (optarg && *optarg) {
594 if (!isdigit (*optarg))
595 bad_value (optname, optarg);
596
597 errno = 0;
598 const long nsec = strtol (optarg, &end, 10);
599 if ('\0' == *end && 0 <= nsec && !errno) {
600 rw_sleep (int (nsec));
601 break;
602 }
603 }
604 }
605 else if ( sizeof opt_signal - 1 <= arglen
606 && !memcmp (opt_signal, argv [i], sizeof opt_signal - 1)) {
607 /* send ourselves the specified signal */
608 optname = opt_signal;
609 optarg = get_long_val (argv, &i, sizeof opt_signal - 1);
610 if (optarg && *optarg) {
611 const int signo = get_signo (optarg);
612 if (0 <= signo) {
613 if (0 > raise (signo))
614 terminate (1, "raise(%s) failed: %s\n",
615 get_signame (signo), strerror (errno));
616 break;
617 }
618 }
619 }
620 else if ( sizeof opt_ignore - 1 <= arglen
621 && !memcmp (opt_ignore, argv [i], sizeof opt_ignore - 1)) {
622 /* ignore the specified signal */
623 optname = opt_ignore;
624 optarg = get_long_val (argv, &i, sizeof opt_ignore - 1);
625 if (optarg && *optarg) {
626 const int signo = get_signo (optarg);
627 if (0 <= signo) {
628 if (rw_signal (signo, 0 /* SIG_IGN */))
629 terminate (1, "rw_signal(%s, ...) failed: %s\n",
630 get_signame (signo), strerror (errno));
631 break;
632 }
633 }
634 }
635 else if ( sizeof opt_ulimit - 1 <= arglen
636 && !memcmp (opt_ulimit, argv [i], sizeof opt_ulimit - 1)) {
637 /* set child process resource utilization limits */
638 optname = opt_ulimit;
639 optarg = get_long_val (argv, &i, sizeof opt_ulimit - 1);
640 if (optarg && *optarg) {
641 if (!parse_limit_opts (optarg, defaults)) {
642 break;
643 }
644 }
645 }
646 else if ( sizeof opt_warn - 1 <= arglen
647 && !memcmp (opt_warn, argv [i], sizeof opt_warn - 1)) {
648 /* set compiler warning mode */
649 optname = opt_warn;
650 optarg = get_long_val (argv, &i, sizeof opt_warn - 1);
651 if (optarg && *optarg) {
652 if (!parse_warn_opts (optarg, defaults)) {
653 break;
654 }
655 }
656 }
657 /* fall through */
658 }
659 default:
660 if (optarg) {
661 if (*optarg)
662 bad_value (optname, optarg);
663 else
664 missing_value (optname);
665 }
666
667 if (argv [i])
668 bad_option (argv [i]);
669 else
670 missing_value (optname);
671 }
672 }
673
674 return i;
675}
676
677char**
678split_opt_string (const char* opts)
679{
680 char in_quote = 0;
681 int in_escape = 0;
682 int in_token = 0;
683 const char *pos;
684 char *target, *last;
685 char **table_pos, **argv;
686 size_t optlen;
687
688 assert (0 != opts);
689
690 optlen = strlen (opts);
691
692 if (0 == optlen) {
693 /* Alloc a an index array to hold the program name */
694 argv = (char**)RW_MALLOC (sizeof (char*));
695
696 /* And tie the two together */
697 argv [0] = (char*)0;
698 return argv;
699 }
700
701 table_pos = argv = (char**)RW_MALLOC ((optlen + 3) * sizeof (char*) / 2);
702 /* (strlen (opts)+3)/2 is overkill for the most situations, but it is just
703 large enough to handle the worst case scenario. The worst case is a
704 string similar to 'x y' or 'x y z', requiring array lengths of 4 and 5
705 respectively.
706 */
707
708 last = target = argv [0] = (char*)RW_MALLOC (optlen + 1);
709
710 /* Transcribe the contents, handling escaping and splitting */
711 for (pos = opts; *pos; ++pos) {
712 if (in_escape) {
713 *(target++) = *pos;
714 in_escape = 0;
715 continue;
716 }
717 if (isspace (*pos)) {
718 if (in_quote) {
719 *(target++) = *pos;
720 }
721 else {
722 if (in_token) {
723 *(target++) = '\0';
724 *(table_pos++) = last;
725 in_token = 0;
726 }
727 last = target;
728 }
729 continue;
730 }
731 in_token = 1;
732 switch (*pos) {
733 case escape_code:
734 in_escape = 1;
735 break;
736 case '"':
737 case '\'':
738 if (*pos == in_quote) {
739 in_quote = 0;
740 break;
741 }
742 else if (0 == in_quote) {
743 in_quote = *pos;
744 break;
745 }
746 /* intentionally falling through (in a quote and quote didn't
747 match opening quote.
748 */
749 default:
750 *(target++) = *pos;
751 }
752 }
753
754 if (in_token) { /* close and record the final token */
755 *(target++) = '\0';
756 *(table_pos++) = last;
757 }
758 *table_pos = (char*)0;/*And terminate the array*/
759
760 return argv;
761}
#define i
Definition 17.names.cpp:126
#define last
Definition 17.names.cpp:487
#define offset
Definition 17.names.cpp:508
#define len
Definition 17.names.cpp:488
#define val
Definition 17.names.cpp:570
#define pos
Definition 17.names.cpp:518
int opt_timeout
const int end
void terminate()
#define assert(expr)
Definition _cassert.h:40
#define stdout
#define FILE
#define stderr
const char * name
Definition codecvt1.cpp:72
#define RLIM_SAVED_CUR
Definition driver.cpp:61
#define RLIM_SAVED_MAX
Definition driver.cpp:65
int get_signo(const char *signame)
Definition exec.cpp:365
const char * get_signame(int signo)
Definition exec.cpp:397
rw_rlimit * cpu
Definition target.h:86
unsigned timeout
Definition target.h:77
const char * l_warn
Definition target.h:80
rw_rlimit * fsize
Definition target.h:88
int verbose
Definition target.h:84
rw_rlimit * stack
Definition target.h:90
const char * data_dir
Definition target.h:78
rw_rlimit * as
Definition target.h:91
rw_rlimit * data
Definition target.h:87
const char * c_warn
Definition target.h:79
int compat
Definition target.h:83
rw_rlimit * nofile
Definition target.h:89
rw_rlimit * core
Definition target.h:85
const char suffix_sep
Definition cmdopt.cpp:61
const char * exe_name
Definition cmdopt.cpp:55
const char escape_code
Definition cmdopt.cpp:59
const size_t exe_suffix_len
Definition cmdopt.cpp:62
char ** split_opt_string(const char *opts)
Definition cmdopt.cpp:678
const char default_path_sep
Definition cmdopt.cpp:60
int eval_options(int argc, char **argv, struct target_opts *defaults, const char **exe_opts)
Definition cmdopt.cpp:435
int arg
Definition locale.cpp:2806
void warn(const char *const format,...)
Definition util.cpp:58
int rw_signal(int signo, void(*func)(int))
Definition util.cpp:259
void rw_sleep(int seconds)
Definition util.cpp:244
#define RW_MALLOC(size)
Definition util.h:51