Greetings

Attached is the second version of the replacement runall script. In this version, I have tried to address the concerns raised by Martin in the previous review of the source.

One important bug fix made was to resolve the SEGV issue that was encountered on Solaris. The cause was an incorrect termination of the argv array used in the call to execv().

There are a fair number of directions this project could go from here. A few of them are as follows
* Add support for killing grandchild processes
* Add logic to makefiles to compile this script and use it rather than the runall.sh script * Split into multiple source files to make code more manageable (current length is ~1000 lines)
* Add self-test mode to verify operations
* Add support for compilation/execution on windows
        - Add support for windows process management
        - remove dependency on the diff utility
* Refine output analysis logic (see STDCXX-261)
* Test on additional platforms (including AIX, HPUX, IRIX, Tru64)

--Andrew Black
/************************************************************************
 *
 * runall.c - Unified runall replacement
 *
 * $Id$
 *
 ************************************************************************
 *
 * Copyright 2006 The Apache Software Foundation or its licensors,
 * as applicable.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 **************************************************************************/

#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <ctype.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/stat.h>

/*
 * Functions designated as 'extern' are intended to be used from 
 * outside a translation unit if/when this file is split into multiple
 * source files
 */

/*** watchdog structure ***/
struct exec_attrs {
    int status;
    int killed;
};

/*** parse_opts structure ***/

/* extern */ int timeout = 10;
/* extern */ char* exe_opts = "";
/* extern */ char* in_root = "";
/* extern */ char* exe_name;

/***** Utility function ******/

#define MALLOC_G(size)                             \
    guarded_malloc(size, __FILE__, __LINE__)

/* extern */ void
terminate ( const int state, const char* format, ... )
{
    va_list args;

    assert ( 0 != format );

    va_start ( args, format );
    vfprintf ( stderr, format, args );
    va_end ( args );

    exit ( state );
}

/* extern */ void*
guarded_malloc (const size_t size, const char* file, const unsigned line)
{
    void* const alloc = malloc(size);

    assert (0 != file);
    assert (0 < size);

    if ( 0 == alloc )
        terminate ( 1, "malloc(%lu) at line %u of %s failed: %s\n", 
                 (unsigned long)size, line, file, strerror (errno));

    return alloc;
}


/***** Begin watchdog logic ******/

static int
alarm_timeout;   /* set to a non-zero value when a non-zero timeout expires */

/* extern */ const char * 
get_signame (const int signo)
{
    static const struct {
        int         val;
        const char* str;
    } names[] = {

#undef SIGNAL
#define SIGNAL(val)   { SIG ## val, #val }

#ifdef SIGABRT
        SIGNAL (ABRT),
#endif   /* SIGABRT */
#ifdef SIGALRM
        SIGNAL (ALRM),
#endif   /* SIGALRM */
#ifdef SIGBUS
        SIGNAL (BUS),
#endif   /* SIGBUS */
#ifdef SIGCANCEL
        SIGNAL (CANCEL),
#endif   /* SIGCANCEL */
#ifdef SIGCHLD
        SIGNAL (CHLD),
#endif   /* SIGCHLD */
#ifdef SIGCKPT
        SIGNAL (CKPT),
#endif   /* SIGCKPT */
#ifdef SIGCLD
        SIGNAL (CLD),
#endif   /* SIGCLD */
#ifdef SIGCONT
        SIGNAL (CONT),
#endif   /* SIGCONT */
#ifdef SIGDIL
        SIGNAL (DIL),
#endif   /* SIGDIL */
#ifdef SIGEMT
        SIGNAL (EMT),
#endif   /* SIGEMT */
#ifdef SIGFPE
        SIGNAL (FPE),
#endif   /* SIGFPE */
#ifdef SIGFREEZE
        SIGNAL (FREEZE),
#endif   /* SIGFREEZE */
#ifdef SIGGFAULT
        SIGNAL (GFAULT),
#endif   /* SIGGFAULT */
#ifdef SIGHUP
        SIGNAL (HUP),
#endif   /* SIGHUP */
#ifdef SIGILL
        SIGNAL (ILL),
#endif   /* SIGILL */
#ifdef SIGINFO
        SIGNAL (INFO),
#endif   /* SIGINFO */
#ifdef SIGINT
        SIGNAL (INT),
#endif   /* SIGINT */
#ifdef SIGIO
        SIGNAL (IO),
#endif   /* SIGIO */
#ifdef SIGIOT
        SIGNAL (IOT),
#endif   /* SIGIOT */
#ifdef SIGK32
        SIGNAL (K32),
#endif   /* SIGK32 */
#ifdef SIGKILL
        SIGNAL (KILL),
#endif   /* SIGKILL */
#ifdef SIGLOST
        SIGNAL (LOST),
#endif   /* SIGLOST */
#ifdef SIGLWP
        SIGNAL (LWP),
#endif   /* SIGLWP */
#ifdef SIGPIPE
        SIGNAL (PIPE),
#endif   /* SIGPIPE */
#ifdef SIGPOLL
        SIGNAL (POLL),
#endif   /* SIGPOLL */
#ifdef SIGPROF
        SIGNAL (PROF),
#endif   /* SIGPROF */
#ifdef SIGPTINTR
        SIGNAL (PTINTR),
#endif   /* SIGPTINTR */
#ifdef SIGPTRESCHED
        SIGNAL (PTRESCHED),
#endif   /* SIGPTRESCHED */
#ifdef SIGPWR
        SIGNAL (PWR),
#endif   /* SIGPWR */
#ifdef SIGQUIT
        SIGNAL (QUIT),
#endif   /* SIGQUIT */
#ifdef SIGRESTART
        SIGNAL (RESTART),
#endif   /* SIGRESTART */
#ifdef SIGRESV
        SIGNAL (RESV),
#endif   /* SIGRESV */
#ifdef SIGSEGV
        SIGNAL (SEGV),
#endif   /* SIGSEGV */
#ifdef SIGSTKFLT
        SIGNAL (STKFLT),
#endif   /* SIGSTKFLT */
#ifdef SIGSTOP
        SIGNAL (STOP),
#endif   /* SIGSTOP */
#ifdef SIGSYS
        SIGNAL (SYS),
#endif   /* SIGSYS */
#ifdef SIGTERM
        SIGNAL (TERM),
#endif   /* SIGTERM */
#ifdef SIGTHAW
        SIGNAL (THAW),
#endif   /* SIGTHAW */
#ifdef SIGTRAP
        SIGNAL (TRAP),
#endif   /* SIGTRAP */
#ifdef SIGTSTP
        SIGNAL (TSTP),
#endif   /* SIGTSTP */
#ifdef SIGTTIN
        SIGNAL (TTIN),
#endif   /* SIGTTIN */
#ifdef SIGTTOU
        SIGNAL (TTOU),
#endif   /* SIGTTOU */
#ifdef SIGUNUSED
        SIGNAL (UNUSED),
#endif   /* SIGUNUSED */
#ifdef SIGURG
        SIGNAL (URG),
#endif   /* SIGURG */
#ifdef SIGUSR1
        SIGNAL (USR1),
#endif   /* SIGUSR1 */
#ifdef SIGUSR2
        SIGNAL (USR2),
#endif   /* SIGUSR2 */
#ifdef SIGVTALRM
        SIGNAL (VTALRM),
#endif   /* SIGVTALRM */
#ifdef SIGWAITING
        SIGNAL (WAITING),
#endif   /* SIGWAITING */
#ifdef SIGWINCH
        SIGNAL (WINCH),
#endif   /* SIGWINCH */
#ifdef SIGWINDOW
        SIGNAL (WINDOW),
#endif   /* SIGWINDOW */
#ifdef SIGXCPU
        SIGNAL (XCPU),
#endif   /* SIGXCPU */
#ifdef SIGXFSZ
        SIGNAL (XFSZ),
#endif   /* SIGXFSZ */
#ifdef SIGXRES
        SIGNAL (XRES),
#endif   /* SIGXRES */
        { -1, 0 }
    };

    size_t i;
    static char def[16];

    for (i = 0; i != sizeof names / sizeof *names; ++i) {
        if (names [i].val == signo) {
            return names [i].str;
        }
    }

    /* We've run out of known signal numbers, so use a default name */
    snprintf (def, sizeof(def), "SIG#%d", signo);
    return def;
}

static void
handle_alrm (const int signo)
{
    if (SIGALRM == signo)
        alarm_timeout = 1;
}

static struct exec_attrs
wait_for_child (const pid_t child_pid)
{
    static const int signals[] = {
        SIGHUP, SIGINT, SIGTERM, SIGKILL, SIGKILL
    };

    static const unsigned sigcount = sizeof (signals) / sizeof (int);

    struct exec_attrs state = {-1, 0};

    unsigned tryno = 0;

    assert ( 0 < child_pid );

    if (timeout>0)
        alarm (timeout);

    while (1) {
        const pid_t wait_pid = waitpid (child_pid, &state.status, 0);
        if (child_pid == wait_pid) {
            if (WIFEXITED (state.status) || WIFSIGNALED (state.status))
                break; /*we've got an exit state, so let's bail*/
        }
        else if ((pid_t)-1 == wait_pid && EINTR != errno) {
            /* waitpid() error */
            fprintf (stderr, "waitpid(%d) error: %s\n",
                     (int)child_pid, strerror (errno));
        }
        else if (alarm_timeout) {
            /* timeout elapsed, so send a signal to the child*/
            kill (child_pid, signals [tryno]);

            /* Record the signal used*/
            state.killed = signals [tryno];

            /* Step to the next signal */
            if ( ++tryno > sigcount ) {
                /* Still running, but we've run out of signals to try
                   Therefore, we'll set error flags and break out of 
                   the loop.
                */
                state.status = -1;
                state.killed = -1;
                fputs ("No more kill options, Bailing", stderr);
                break;
            }

            /* Reset the alarm */
            alarm_timeout = 0;
            alarm (1);
        }
    }

    /* Clear alarm */
    alarm (0);

    return state;
}

/**
 * @desc takes a directory strucure root and an executable name, and tries
 * to open an input file based on these parameters
 * @param root the root of the directory structure storing input files
 * @param exec_name the name of executable being run
 * @returns -1 if no file was opened, or the descriptor of the opened file
 **/

static int
open_input(const char* exec_name)
{
    const size_t root_len = strlen (in_root);
    int intermit = -1;

    assert (0 != exec_name);
    assert (0 != in_root);

    if (root_len) {
        const size_t out_len = root_len + strlen (exec_name) + 17;
    
        char* const tmp_name = (char*)MALLOC_G (out_len);

        /* Try in_root/manual/in/exec_name.in */
        memcpy (tmp_name, in_root, root_len+1);
        strcat (tmp_name, "/manual/in/");
        strcat (tmp_name, exec_name);
        strcat (tmp_name, ".in");
        intermit = open (tmp_name, O_RDONLY);
    
        /* If we opened the file, return the descriptor */
        if (0 <= intermit) {
            free (tmp_name);
            return intermit;
        }

        /* If the file exists (errno isn't ENOENT), exit */
        if (ENOENT != errno)
            terminate ( 1, "open(%s) failed: %s\n", tmp_name, 
                        strerror (errno));

        /* Try in_root/tutorial/in/exec_name.in */
        memcpy (tmp_name, in_root, root_len+1);
        strcat (tmp_name, "/tutorial/in/");
        strcat (tmp_name, exec_name);
        strcat (tmp_name, ".in");
        intermit = open (tmp_name, O_RDONLY);

        /* If we opened the file, return the descriptor */
        if (0 <= intermit) {
            free (tmp_name);
            return intermit;
        }

        /* If the file exists (errno isn't ENOENT), exit */
        if (-1 == intermit && ENOENT != errno)
            terminate ( 1, "open(%s) failed: %s\n", tmp_name, 
                        strerror (errno));

        free(tmp_name);
    }

    /* If we didn't find a source file, open /dev/null */

    intermit = open("/dev/null", O_RDONLY);

    /* If we opened the file, return the descriptor */
    if (0 <= intermit)
        return intermit;

    /* otherwise, print an error message and exit */
    terminate ( 1, "open(/dev/null) failed: %s\n", strerror (errno));
    return -1; /* silence a compiler warning */
}

static void
replace_file(const int source, const int dest, const char* name)
{
    int result;

    assert (source != dest);
    assert (0 <= source);
    assert (0 <= dest);
    assert (0 != name);

    result = dup2 (source, dest);
    if (-1 == result)
        terminate ( 1,  "redirecting to %s failed: %s\n", name, 
                    strerror (errno));

    result = close (source);
    if (-1 == result)
        terminate ( 1,  "closing source for %s redirection failed: %s\n", 
                    name, strerror (errno));
}

/* extern */ struct exec_attrs 
exec_file (const char* exec_name, char *const argv[])
{
    struct sigaction act;
    const pid_t child_pid = fork ();

    if (0 == child_pid) {   /* child */
        FILE* error_file;

        assert (0 != argv);
        assert (0 != argv[0]);
        assert (0 != exec_name);

        /* Cache stdout for use if execv() fails */
        {
            const int error_cache = dup (2);
            if (-1 == error_cache)
                terminate ( 1,  "Error duplicating stderr: %s\n", 
                            strerror (errno));

            error_file = fdopen (error_cache,"a");
            if (0 == error_file)
                terminate ( 1,  "Error opening file handle  from cloned "
                            "stderr file descriptor: %s\n", strerror (errno));
        }

        /* Redirect stdin */
        {
            const int intermit = open_input (exec_name);
            replace_file(intermit, 0, "stdin");
        }

        /* Redirect stdout */
        {
            const size_t exelen = strlen (argv[0]);
            const size_t outlen = exelen + 5;
            char* const tmp_name = (char*)MALLOC_G (outlen);
            int intermit;

            /* Redirect stdout */
            memcpy (tmp_name, argv[0], exelen + 1);
            strcat (tmp_name, ".out");
            intermit = open (tmp_name, O_WRONLY | O_CREAT | O_TRUNC, 
                             S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

            if (-1 == intermit)
                terminate( 1,  "Error opening %s for output redirection: "
                           "%s\n", tmp_name, strerror (errno));

            replace_file(intermit, 1, "stdout");

            free (tmp_name);
        }

        /* Redirect stderr */
        if (-1 == dup2(1, 2))
            terminate ( 1,  "Redirection of stderr to stdout failed: %s\n", 
                        strerror (errno));

        execv (argv [0], argv);

        fprintf (error_file, "execv (\"%s\", ...) error: %s\n",
                 argv [0], strerror (errno));

        exit (1);
    }

    /* parent */

    /* Set up alarm */
    alarm_timeout = 0;

    /* Need to use sigaction rather than signal due to linux glitch */
    memset (&act, 0, sizeof act);
    act.sa_handler = handle_alrm;
    sigaction (SIGALRM, &act, 0);
    
    return wait_for_child ( child_pid );
}

/***** Begin Option parsing ******/

/* extern */ void 
show_usage(const char* exec, const int status)
{
    assert (0 != exec);
    fprintf (stderr, "Usage: %s [OPTIONS] [targets]\n", exec);
    fputs ("\n"
           "  Treats each token in targets as the path to an executable.  "
           "Each target\n  enumerated is executed, and the output is "
           "processed after termination.  If\n  execution takes longer "
           "than a certain (configurable) period of time, the\n  process is "
           "killed\n\n", stderr);
    fputs ("    -d dir      Root directory for output reference files\n"
           "    -h, -?      Display usage information and exit\n"
           "    -t seconds  Watchdog timeout before killing target (default is "
"10 \n                seconds)\n"
           "    -x opts     Command line options to pass to targets\n"
           "    --          Terminate option processing and treat all "
           "following arguments\n                as targets\n", stderr);
    exit (status);
}

/* extern */ int 
eval_options (const int argc, /* const */ char* argv[])
{
    int i;

    char* val;

    assert(0 != argv);

    if ( 1 == argc || '-' != argv [1][0] )
        return 1;

    for (i = 1; i < argc && '-' == argv [i][0]; ++i) {

        switch (argv [i][1]) {
        case '?':
        case 'h':
            show_usage (argv [0], 0);
        case 'r':
            ++i; /* Ignore -r option (makefile compat) */
            break;
        case 't':
            if ( '\0' == argv [i][2] )
                val = argv [++i];
            else
                val = argv [i] + 2;

            timeout = atoi (val);
            break;
        case 'd':
            if ( '\0' == argv [i][2] )
                val = argv [++i];
            else
                val = argv [i] + 2;

            in_root = val;
            break;
        case 'x':
            if ( '\0' == argv [i][2] )
                val = argv [++i];
            else
                val = argv [i] + 2;

            exe_opts = val;
            break;
        case '-':
            /*const size_t arglen = strlen (argv [i]);*/

            /* abort processing on --, eat token */
            if ( '\0' == argv [i][2] )
                return i+1;

            /* Intentionally falling through */
        default:
            printf ("Unknown option: %s\n", argv [i]);
            show_usage(argv [0], 1);
        }
    }

    return i;
}

/* extern */ char**
split_child_opts()
{
    /* Need to turn the opts string into an argc/argv array pair.
       This logic isn't UTF-8 safe.
       Also, note that this doesn't support embeding redirection
       or piping into the arguments-we aren't a full shell after all
    */
    char in_quote = 0;
    int in_escape = 0;
    int in_token = 0;
    char *pos, *target, *last;
    char **table_pos, **argv;

    assert( 0 != exe_opts);

    if ( 0 == strlen (exe_opts)) {
        /* Alloc a an index array to hold the program name  */
        argv = (char**)MALLOC_G ( 2 * sizeof (char*) );

        /* And tie the two together */
        argv [1] = (char*)0;
        return argv;
    }

    table_pos = argv = (char**)MALLOC_G (
        (strlen (exe_opts) + 5) * sizeof (char*) / 2 );
    /* (strlen(exe_opts)+5)/2 is overkill for the most situations, 
       but it is just large enough to handle the worst case scenario
       (worst case is similar to 'x y' or 'x y z', requiring lengths
       of 4 and 5 respectively.)
    */

    last = target = argv[1] = (char *)MALLOC_G ( strlen (exe_opts) + 1 );

    /* Transcribe the contents, handling escaping and splitting */
    for (pos = exe_opts; *pos; ++pos) {
        if (in_escape) {
            *(target++) = *pos;
            in_escape = 0;
            continue;
        }
        if (isspace(*pos)) {
            if (in_quote) {
                *(target++) = *pos;
            }
            else {
                if (in_token) {
                    *(target++) = '\0';
                    *(++table_pos) = last;
                    in_token = 0;
                }
                last = target;
            }
            continue;
        }
        in_token = 1;
        switch (*pos) {
        case '\\':
            in_escape = 1;
            break;
        case '"':
        case '\'':
            if (*pos == in_quote) {
                in_quote = 0;
                break;
            }
            else if (0 == in_quote) {
                in_quote = *pos;
                break;
            }
            /* intentionally falling through (in a quote and quote didn't 
               match opening quote.
            */
        default:
            *(target++) = *pos;
        }
    }

    if (in_token) { /* close and record the final token */
        *(target++) = '\0';
        *(++table_pos) = last;
    }
    *(++table_pos) = (char*)0;/*And terminate the array*/

    return argv;
}

/***** Begin Main Loop ******/

/*
const char* log_level_names
*/

static void
check_test(const char* exec_name, const char* out_name)
{
    FILE* data = fopen (out_name, "r");
    unsigned r_lvl = 0, r_active = 0, r_total = 0;
    unsigned asserts = 0, failed = 0;
    int fmt_ok = 0;
    unsigned fsm = 0;
    char tok;

    assert ( 0 != exec_name );
    assert ( 0 != out_name );

    if (0 == data) {
        if (ENOENT != errno) {
            printf ("Error opening %s: %s\n", out_name, strerror (errno));
            return;
        }
        puts ("OUTPUT");
        return;
    }

    for (tok = fgetc (data); fsm < 6 && !feof (data); tok = fgetc (data)) {
        switch (tok) {
        case '\n':
            fsm = 1;
            break;
        case '#':
            fsm = ( 1 == fsm ) ? 2 : 0;
            break;
        case ' ':
            fsm = ( 2 == fsm ) ? 3 : ( ( 4 == fsm ) ? 5 : 0 );
            break;
        case '|':
            fsm = ( 3 == fsm ) ? 4 : 0;
            break;
        case '(':
            if ( 5 == fsm ) {
                fseek (data, -6, SEEK_CUR);
                fsm++;
                break;
            }
        default:
            fsm = 0;
        }
    }

    if (!feof(data)) {
        while (3 == fscanf(data, "# | (S%u) %*s | %u | %u | %*u%% |\n", 
                        &r_lvl, &r_active, &r_total)) {
            if (6 < r_lvl) {
                /* The 0.new tests produces errors, and are all 
                   expected to be active, so invert the total */
                if (8 == r_lvl && 0 == strcmp(exec_name,"0.new"))
                    r_active = r_total-r_active;
                failed += r_active;
                asserts += r_total;
                if (failed < r_active || asserts < r_total) {
                    puts(" OFLOW");
                    return;
                }
            }
            /* else if (1 < r_lvl) warning*/
            fmt_ok = 1;
        }
    }
    
    if (fmt_ok) {
        unsigned pcnt = 0;
        if (asserts) {
            pcnt = 100 * ( asserts - failed ) / asserts;
        }
        printf("     0 %6d %6d %5d%%\n", asserts, failed, pcnt);
    }
    else {
        puts("FORMAT");
    }

    fclose(data);
}

#define FILE_TEST(op, x)                                                \
    if (-1==(x))                                                        \
        terminate ( 1, op " failed: %s\n", strerror (errno) )

static void
check_example(const char* exec_name, const char* out_name)
{
    struct stat file_info;
    const size_t root_len = strlen(in_root);
    char* const ref_name = (char*)MALLOC_G(root_len 
                                           + strlen(exec_name) + 19);
    int state = -1;

    assert ( 0 != exec_name );
    assert ( 0 != out_name );

    /* Try in_root/manual/out/exec_name.out */
    memcpy (ref_name, in_root, root_len+1);
    strcat (ref_name, "/manual/out/");
    strcat (ref_name, exec_name);
    strcat (ref_name, ".out");

    if (0 > stat(ref_name, &file_info)) {
        if (ENOENT != errno) {
            printf("stat(%s) error: %s\n", ref_name, 
                   strerror (errno));
            free(ref_name);
            return;
        }
                        
        /* If that doesn't exist, try 
           in_root/tutorial/out/exec_name.out */
        memcpy (ref_name, in_root, root_len+1);
        strcat (ref_name, "/tutorial/out/");
        strcat (ref_name, exec_name);
        strcat (ref_name, ".out");

        if (0 > stat(ref_name, &file_info)) {
            if (ENOENT != errno) {
                printf("stat(%s) error: %s\n", ref_name, 
                       strerror (errno));
            }
            else {
                puts("OUTPUT");
            }
            free(ref_name);
            return;
        }
    }

    const pid_t child_pid = fork ();

    if (0 == child_pid) {   /* child */
        /* Cache stdout (hopefully) for use if execv() fails */
        int error_cache = dup(2);
        FILE* error_file;
        FILE_TEST("dup(stderr)", error_cache);

        FILE_TEST("close(stdin)",close(0));
        FILE_TEST("close(stdin)",close(1));
        FILE_TEST("close(stderr)",close(2));

        /* Todo: diff with --strip-trailing-cr on windows */
        execlp("diff", "diff", "-q", ref_name, out_name, (char *)0);

        if ((error_file = fdopen(error_cache,"a")))
            fprintf (error_file, "execlp(\"diff\", ...) error: %s\n",
                     strerror (errno));

        exit(2);
    }

    while (1) {
        const pid_t wait_pid = waitpid (child_pid, &state, 0);

        if (child_pid == wait_pid) {

            if (WIFEXITED (state)) {
                const int retcode = WEXITSTATUS (state);                
                switch ( retcode ) {
                case 0:
                    puts("     0");
                    break;
                case 1:
                    puts("OUTPUT");
                    break;
                default:
                    printf("diff returned %d\n", retcode);
                }
                break;
            }
            else if (WIFSIGNALED (state)) {
                printf("diff exited with %s\n", 
                       get_signame (WTERMSIG (state)));
                break;
            }
/*
            else if (WIFSTOPPED (state)) {
                printf ("process %d stopped\n", (int)child_pid);
            }
            else if (WIFCONTINUED (state)) {
                printf ("process %d continued\n", (int)child_pid);
            }
*/
        }
    }

    free(ref_name);
}

/* extern */ int
main (int argc, /* const */ char *argv[])
{
    exe_name = argv[0];

    if (1 < argc && '-' == argv [1][0]) {
        const int nopts = eval_options (argc, argv);

        if (0 > nopts)
            return 1;

        argc -= nopts;
        argv += nopts;
    }
    else {
        --argc;
        ++argv;
    }

    if (0 < argc) {
        int i;
        char** childargv = split_child_opts();

        assert ( 0 != childargv );
        puts("NAME               STATUS ASSRTS FAILED PERCNT");

        for ( i = 0; i < argc; ++i ) {
            struct exec_attrs status;
            struct stat file_info;
            const char* exec_name = basename (argv [i]);
            
            childargv [0] = argv [i];
            
            printf("%-18.18s ",exec_name);
            fflush(stdout);
            
            if (0 > stat (childargv[0], &file_info)) {
                if (ENOENT != errno) {
                    printf ("Stat error: %s\n", strerror (errno));
                    continue;
                }
                file_info.st_mode = 0; /* force mode on non-existant file to 0 */
            }
            if (0 == (file_info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
                /* This is roughly equivlent to the -x bash operator.
                   It checks if the file can be run, /not/ if we can run it
                */
                const size_t name_len = strlen ( exec_name );
                const size_t path_len = strlen ( argv [i] );
                char* tmp_name;

                /* If target is a .o file, it's good (since it built) */
                if ( '.' == exec_name [name_len-1] 
                     && 'o' == exec_name [name_len] ) {
                    puts ("     0");
                    continue;
                }

                /* Otherwise, check for the .o file */
                tmp_name = (char*)MALLOC_G ( path_len + 3 );
                memcpy (tmp_name, argv [i], path_len + 1);
                strcat (tmp_name,".o");

                if (0 > stat(tmp_name, &file_info)) {
                    if (ENOENT != errno) {
                        printf ("stat(%s) error: %s\n", tmp_name, 
                                strerror (errno));
                    }
                    else {
                        puts ("  COMP");
                    }
                }
                else {
                    puts ("  LINK");
                }
                
                free(tmp_name);
                continue;
            }

            status = exec_file (exec_name, childargv);

            if (0 == status.status) {
                char* const out_name = (char*)MALLOC_G (strlen( argv[i] ) + 5);
                strcpy (out_name, argv[i]);
                strcat (out_name,".out");

                if ( !in_root || !strlen (in_root) ) {
                    /* If there not an input directory, look at the 
                       assertion tags */
                    check_test (exec_name, out_name);
                }
                else{
                    /* Otherwise, diff against the output file */
                    check_example (exec_name, out_name);
                }
                free(out_name);
            } 
            else if (WIFEXITED (status.status)) {
                const int retcode = WEXITSTATUS (status.status);
                switch (retcode) {
                case 126:
                    puts (" EXIST");
                    break;
                case 127:
                    puts ("  EXEC");
                    break;
                default:
                    printf ("%6d\n", retcode);
                }
            }
            else if ( WIFSIGNALED (status.status) ) {
                printf("%6s\n", get_signame (WTERMSIG (status.status)));
            }
            else if ( -1 == status.status && -1 == status.killed ) {
                puts (" NKILL");
            }
            else {
                printf ("(%d|%d)\n", status.status, status.killed);
            }
        }

        if (childargv [1])
            free (childargv [1]);
        free (childargv);
    }

    return 0;
}

Reply via email to