Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
runall.cpp
Go to the documentation of this file.
1
27#include <assert.h> /* for assert() */
28#include <errno.h> /* for errno */
29#include <stdlib.h> /* for exit(), free() */
30#include <string.h> /* for memcpy(), ... */
31#include <stdio.h> /* for FILE, fopen(), ... */
32
33#include <ctype.h> /* for isspace */
34#include <limits.h> /* for PATH_MAX */
35#include <sys/types.h>
36#include <sys/stat.h>
37#ifndef _WIN32
38# include <sys/wait.h> /* for WIFEXITED(), ... */
39#endif
40
41#include "cmdopt.h"
42#include "display.h"
43#include "exec.h"
44#include "output.h"
45#include "util.h"
46
47#include "target.h"
48
49#ifndef ENOENT
50# define ENOENT 2
51#endif /* ENOENT */
52
53#ifndef S_IXUSR
54# define S_IXUSR 0100
55#endif /* S_IXUSR */
56
57#ifndef S_IXGRP
58# define S_IXGRP 0010
59#endif /* S_IXGRP */
60
61#ifndef S_IXOTH
62# define S_IXOTH 0001
63#endif /* S_IXOTH */
64
65#if !defined (PATH_MAX) || PATH_MAX < 128 || 4096 < PATH_MAX
66 // deal with undefined, bogus, or excessive values
67# undef PATH_MAX
68# define PATH_MAX 1024
69#endif
70
96static char**
97merge_argv (const char* target, char* const argv [])
98{
99 size_t tlen;
100 char ** split;
101 unsigned i, arg_count = 0, spl_count = 0, wld_count = 0;
102
103 assert (0 != target);
104 assert (0 != argv);
105
106 tlen = strlen (target);
107 split = split_opt_string (target);
108
109 /* If the split of target only contains a single value, we may have a
110 bare executable name */
111 if (!split [1]) {
112 /* Check if last character in the target is whitespace */
113 if (isspace (target [tlen])) {
114 /* If it is, we've got a complex executable with no arguments.
115 Therfore, return it as is.
116 */
117 return split;
118 } /* Otherwise, it's a bare executable, so append argv */
119
120 /* Figure out how many arguments we've got in argv*/
121 for (/* none */; argv [arg_count]; ++arg_count);
122
123 /* reallocate memory for copying them, extending the buffer */
124 split = (char**)RW_REALLOC (split, (arg_count + 2) * sizeof (char*));
125
126 /* And copy the pointers */
127 for (i=0; i < arg_count; ++i)
128 split [i+1] = argv [i];
129
130 /* Then terminate the array*/
131 split [++i] = (char*)0;
132
133 return split;
134 } /* Otherwise, it's a complex executable with 1 or more arguments */
135
136 /* Figure out how many instances of '%x' we've got */
137 for (spl_count = 1; split [spl_count]; ++spl_count) {
138 if ('%' == split [spl_count][0] && 'x' == split [spl_count][1]
139 && '\0' == split [spl_count][2])
140 ++wld_count;
141 }
142
143 /* If we don't have any instances of '%x', we have a valid argv array,
144 so return it as it is.
145 */
146 if (0 == wld_count)
147 return split;
148
149 /* Now we need to determine how large the argv array is */
150 for (/* none */; argv [arg_count]; ++arg_count);
151
152 if (0 == arg_count) {
153 /* We want to shrink the array, removing the '%x' terms*/
154 unsigned found = 0;
155 for (i = 1; i <= spl_count; ++i) {
156 if (split [i] && '%' == split [i][0] && 'x' == split [i][1]
157 && '\0' == split [i][2])
158 ++found;
159 else
160 split [i - found] = split [i];
161 }
162 }
163 else if (1 == arg_count) {
164 /* We need to replace all the %x terms with argv [0] */
165
166 for (i = 1; i < spl_count; ++i) {
167 if ('%' == split [i][0] && 'x' == split [i][1]
168 && '\0' == split [i][2])
169 split [i] = argv [0];
170 }
171 }
172 else {
173 /* We need to resize the split array to hold the insertion (s) */
174 /* First, we realloc the array */
175 const unsigned new_len = spl_count + (arg_count - 1) * wld_count;
176 split = (char**)RW_REALLOC (split, sizeof (char**) * new_len);
177
178 /* Then we itterate backwards through the split array, transcribing
179 elements as we go. We have to go backwards, so we don't clobber
180 data in the process.
181 */
182 for (/* none */; wld_count; --spl_count) {
183 if (split [spl_count] && '%' == split [spl_count][0]
184 && 'x' == split [spl_count][1]
185 && '\0' == split [spl_count][2]) {
186 --wld_count;
187 for (i = arg_count; i; --i) {
188 split [spl_count + (arg_count - 1) * wld_count + i - 1] =
189 argv [i - 1];
190 }
191 }
192 else
193 split [spl_count + (arg_count - 1) * wld_count] =
194 split [spl_count];
195 }
196 }
197 return split;
198}
199
205#define READ_BUF_LEN 64
206
218static
219void count_warnings (const char* const target, unsigned* counter,
220 const char* const match)
221{
222 size_t path_len;
223 char* tmp_name;
224 FILE* data;
225
226 assert (0 != target);
227 assert (0 != counter);
228
229 if (0 == match)
230 return; /* Don't have a match string, so don't tally warnings */
231
232 path_len = strlen (target);
233 tmp_name = (char*)RW_MALLOC (path_len + 5);
234 memcpy (tmp_name, target, path_len + 1);
235 strcat (tmp_name,".log");
236
237 data = fopen (tmp_name, "r");
238 if (data) {
239 size_t pos = 0, limit = strlen (match);
240 while (!feof (data)) {
241 char buf [READ_BUF_LEN];
242 size_t nread; /* bytes read */
243
244 /* First, read a block from the file into the buffer */
245 nread = fread (buf, 1, sizeof buf, data);
246 if (ferror (data)) {
247 warn ("Error reading %s: %s\n", tmp_name, strerror (errno));
248 break;
249 }
250
251 /* Then, look for the search string within it. */
252 for (size_t i = 0; i < nread; ++i) {
253 if (buf [i] != match [pos])
254 pos = 0;
255 else if (++pos == limit) {
256 pos = 0;
257 ++*counter;
258 }
259 }
260 }
261
262 fclose (data);
263 }
264 else if (ENOENT != errno)
265 warn ("Error opening %s: %s\n", tmp_name, strerror (errno));
266
267 free (tmp_name);
268}
269
286static int
287check_target_ok (const char* target, struct target_status* status)
288{
289 struct stat file_info;
290 int exists = 1;
291 size_t path_len;
292 char* tmp_name;
293
294 assert (0 != target);
295 assert (0 != status);
296
297 path_len = strlen (target);
298
299#ifndef _WIN32
300 /* Otherwise, check for the .o file on non-windows systems */
301 tmp_name = (char*)RW_MALLOC (path_len + 3);
302 memcpy (tmp_name, target, path_len + 1);
303 strcat (tmp_name,".o");
304#else
305 /* Or the target\target.obj file on windows systems*/
306 {
307 const char* const target_name = get_target ();
308 size_t target_len = strlen (target_name);
309 size_t tmp_len = path_len + target_len - 2;
310 /* - 2 comes from removing 4 characters (extra .exe) and adding 2
311 characters (\ directory seperator and trailing null) */
312 tmp_name = (char*)RW_MALLOC (tmp_len);
313 memcpy (tmp_name, target, path_len - 4);
314 tmp_name [path_len - 4] = default_path_sep;
315 memcpy (tmp_name + path_len - 3, target_name, target_len);
316 tmp_name [tmp_len - 4] = 'o';
317 tmp_name [tmp_len - 3] = 'b';
318 tmp_name [tmp_len - 2] = 'j';
319 tmp_name [tmp_len - 1] = '\0';
320 }
321#endif /* _WIN32 */
322
323 count_warnings (target, &status->l_warn, "warning:");
324 count_warnings (tmp_name, &status->c_warn, "warning:");
325
326 if (0 > stat (target, &file_info)) {
327 if (ENOENT != errno) {
328 warn ("Error stating %s: %s\n", target, strerror (errno));
329 status->status = ST_SYSTEM_ERROR;
330 free (tmp_name);
331 return 0;
332 }
333 file_info.st_mode = 0; /* force mode on non-existant file to 0 */
334 exists = 0; /* note that it doesn't exist */
335 }
336
337 if (0 == (file_info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
338 /* This is roughly equivlent to the -x bash operator.
339 It checks if the file can be run, /not/ if we can run it
340 */
341
342#if 0 /* Disable .o target check as unused */
343 /* If target is a .o file, check if it exists */
344 if ('.' == target [path_len-1] && 'o' == target [path_len]) {
345 if (!exists)
346 status->status = ST_COMPILE;
347 free (tmp_name);
348 return 0;
349 }
350#endif
351
352 /* If the target exists, it doesn't have valid permissions */
353 if (exists) {
354 status->status = ST_EXECUTE_FLAG;
355 free (tmp_name);
356 return 0;
357 }
358
359 if (0 > stat (tmp_name, &file_info)) {
360 if (ENOENT != errno) {
361 warn ("Error stating %s: %s\n", tmp_name, strerror (errno));
362 status->status = ST_SYSTEM_ERROR;
363 }
364 else
365 status->status = ST_COMPILE;
366 }
367 else {
368 status->status = ST_LINK;
369 }
370
371 free (tmp_name);
372 return 0;
373 }
374 free (tmp_name);
375 return 1;
376}
377
392static const char*
393rw_basename (const char* path)
394{
395 const char *pos, *mark;
396
397 assert (0 != path);
398
399 for (mark = pos = path; '\0' != *pos; ++pos)
400#ifndef _WIN32
401 mark = (default_path_sep == *pos) ? pos + 1 : mark;
402#else
403 mark = (default_path_sep == *pos || '/' == *pos) ? pos + 1 : mark;
404#endif /* _WIN32 */
405
406 return mark;
407}
408
409static const char* target_name;
410
411const char* get_target ()
412{
413 return target_name;
414}
415
429static void
430run_target (struct target_status *summary,
431 const char *target,
432 const struct target_opts *target_template)
433{
434 struct target_opts options;
435 struct target_status results;
436
437 assert (0 != target);
438 assert (0 != target_template);
439 assert (0 != target_template->argv);
440
441 memcpy (&options, target_template, sizeof options);
442 memset (&results, 0, sizeof results);
443
444 /* create the argv array for this target */
445 options.argv = merge_argv (target, options.argv);
446
447 assert (0 != options.argv);
448 assert (0 != options.argv [0]);
449
450 target_name = rw_basename (options.argv [0]);
451
452 /* create the names of files to redirect stdin and stdout */
453 options.infname = input_name (options.data_dir, target_name);
454 options.outfname = output_name (options.argv [0]);
455
456 print_target (&options);
457
458 if (check_target_ok (options.argv [0], &results)) {
459 exec_file (&options, &results);
460 if (0 == results.exit && 0 == results.signaled)
461 parse_output (&options, &results);
462 }
463
464 print_status (&results);
465
466 if (summary) {
467 /* increment summary counters */
468 if (0 == results.signaled && results.exit)
469 ++summary->exit;
470
471 /* add cumulative times (when valid) */
472 if (results.usr_time != (clock_t)-1)
473 summary->usr_time += results.usr_time;
474
475 if (results.sys_time != (clock_t)-1)
476 summary->sys_time += results.sys_time;
477
478 if (results.wall_time != (clock_t)-1)
479 summary->wall_time += results.wall_time;
480
481 summary->signaled += results.signaled;
482 summary->c_warn += results.c_warn;
483 summary->l_warn += results.l_warn;
484 summary->t_warn += results.t_warn;
485
486 if ((unsigned)-1 != results.assert) {
487 /* increment assertion counters only when they're valid */
488 summary->assert += results.assert;
489 summary->failed += results.failed;
490 }
491 }
492
493 /* free data dynamically allocated for this target alone */
494 free (options.argv [0]);
495 free (options.argv);
496 free ((char*)options.infname);
497 free ((char*)options.outfname);
498}
499
500
512int
513main (int argc, char *argv [])
514{
515 struct target_opts target_template;
516 const char* exe_opts = "";
517 const char* const* const saved_argv = (const char* const*)argv;
518
519 exe_name = argv [0];
520
521 if (1 < argc && '-' == argv [1][0]) {
522 const int nopts =
523 eval_options (argc, argv, &target_template, &exe_opts);
524
525 if (0 > nopts)
526 return 1;
527
528 argc -= nopts;
529 argv += nopts;
530 }
531 else {
532 /* initialize data members */
533 memset (&target_template, 0, sizeof target_template);
534
535 --argc;
536 ++argv;
537 }
538
539 /* set the program output mode */
540 if (target_template.verbose)
542 else
544
545 if (0 < argc) {
546 struct target_status summary;
547
548 int i;
549
550 target_template.argv = split_opt_string (exe_opts);
551
552 assert (0 != target_template.argv);
553
554 /* print out the program's argv array in verbose mode */
555 print_header (target_template.verbose ? saved_argv : 0);
556
557 memset (&summary, 0, sizeof summary);
558
559 /* number of program's executed */
560 int progs_count = 0;
561
562 for (i = 0; i < argc; ++i) {
563 const char* target = argv [i];
564
565 if ('@' == target [0]) {
566 /* read targets from specified file */
567 const char* lst_name = target + 1;
568 FILE* lst = fopen (lst_name, "r");
569 if (0 == lst) {
570 warn ("Error opening %s: %s\n", lst_name, strerror (errno));
571 break;
572 }
573
574 while (!feof (lst)) {
575 char buf [PATH_MAX];
576 target = fgets (buf, int (sizeof buf), lst);
577
578 if (ferror (lst)) {
579 warn ("Error reading %s: %s\n", lst_name, strerror (errno));
580 break;
581 }
582
583 if (target) {
584 /* remove terminating newline character if present */
585 assert (buf == target);
586 if (char* pos = strchr (buf, '\n'))
587 *pos = '\0';
588 if (*target) {
589 ++progs_count;
590 run_target (&summary, target, &target_template);
591 }
592 }
593 }
594
595 fclose (lst);
596 }
597 else {
598 ++progs_count;
599 run_target (&summary, target, &target_template);
600 }
601 }
602
603 print_footer (progs_count, &summary);
604
605 if (target_template.argv [0])
606 free (target_template.argv [0]);
607 free (target_template.argv);
608 }
609
610 free (target_template.core);
611 free (target_template.cpu);
612 free (target_template.data);
613 free (target_template.fsize);
614 free (target_template.nofile);
615 free (target_template.stack);
616 free (target_template.as);
617
618 return 0;
619}
#define i
Definition 17.names.cpp:126
#define found
Definition 17.names.cpp:470
#define buf
Definition 17.names.cpp:452
#define pos
Definition 17.names.cpp:518
const char *const data[]
#define main
#define assert(expr)
Definition _cassert.h:40
#define FILE
void split(const std::string &text, const std::string &separators, std::list< std::string, std::allocator< std::string > > &words)
Definition alg2.cpp:40
A TOML path.
Definition path.hpp:209
void(* print_status)(const struct target_status *)
Definition display.cpp:393
void(* print_header)(const char *const[])
Definition display.cpp:391
void(* print_footer)(int, const struct target_status *)
Definition display.cpp:394
void set_output_format(enum OutputFmt format)
Definition display.cpp:371
void(* print_target)(const struct target_opts *)
Definition display.cpp:392
@ FMT_VERBOSE
Definition display.h:40
@ FMT_PLAIN
Definition display.h:38
void exec_file(const struct target_opts *options, struct target_status *result)
Definition exec.cpp:807
void parse_output(const struct target_opts *options, struct target_status *status)
Definition output.cpp:328
#define ENOENT
Definition runall.cpp:50
#define S_IXGRP
Definition runall.cpp:58
const char * get_target()
Definition runall.cpp:411
#define S_IXOTH
Definition runall.cpp:62
#define READ_BUF_LEN
Definition runall.cpp:205
#define PATH_MAX
Definition runall.cpp:68
#define S_IXUSR
Definition runall.cpp:54
rw_rlimit * cpu
Definition target.h:86
char ** argv
Definition target.h:76
rw_rlimit * fsize
Definition target.h:88
int verbose
Definition target.h:84
rw_rlimit * stack
Definition target.h:90
rw_rlimit * as
Definition target.h:91
rw_rlimit * data
Definition target.h:87
rw_rlimit * nofile
Definition target.h:89
rw_rlimit * core
Definition target.h:85
clock_t usr_time
Definition target.h:124
unsigned l_warn
Definition target.h:128
enum ProcessStatus status
Definition target.h:123
clock_t sys_time
Definition target.h:125
unsigned c_warn
Definition target.h:127
unsigned t_warn
Definition target.h:129
clock_t wall_time
Definition target.h:126
unsigned assert
Definition target.h:130
unsigned failed
Definition target.h:131
@ ST_LINK
Definition target.h:100
@ ST_COMPILE
Definition target.h:99
@ ST_SYSTEM_ERROR
Definition target.h:110
@ ST_EXECUTE_FLAG
Definition target.h:102
const char * exe_name
Definition cmdopt.cpp:55
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
char * input_name(const char *data_dir, const char *target)
Definition util.cpp:190
char * output_name(const char *target)
Definition util.cpp:227
void warn(const char *const format,...)
Definition util.cpp:58
#define RW_REALLOC(source, size)
Definition util.h:64
#define RW_MALLOC(size)
Definition util.h:51