Greetings all.

Attached is a patch plus a pair of new files (to be placed in the util subdirectory) to create a display subsystem for the exec utility. This is a preparatory patch for other changes related to displaying the results.

It may make sense to rename the existing output subsystem (as parser?) and call the new subsystem 'output'.

--Andrew Black

Log:
        * GNUmakefile.bin: Add display.o to link objects for exec utility
        * display.h: Define display subsystem
* display.cpp: Implement initial display subsystem, equivalent to existing output.
        * output.h (parse_output): Alter signature to support display subsystem
        * output.cpp: Include display.h, Update comments.
(check_test, check_compat_test, check_example, parse_output): Alter signatures to support display subsystem, replace I/O calls with updates to target_status structure.
        * runall.cpp: Same as above.
          (check_target_ok, process_results): Same as above.
(run_target): Add storage structure for display subsystem, calls into display subsystem.
          (main): Add calls into display subsystem.
/************************************************************************
 *
 * display.cpp - Definitions of the result display subsystem
 *
 * $Id$
 *
 ************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you 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 <stdio.h>      /* for fflush(), printf(), puts(), ... */

#include "cmdopt.h" /* for target_name -should this be moved? */
#include "exec.h" /* for get_signame */

#include "display.h"

enum display_modes output_fmt = DISPLAY_PLAIN;

void set_header (const char* format);

void print_header ()
{
    puts ("NAME                      STATUS ASSERTS FAILED PERCNT");
}

void begin_row ()
{
    printf ("%-25.25s ", target_name);
    fflush (stdout);
}

void set_progress (int time);
#if 0
{
    if (DISPLAY_XTERM == output_fmt) {
        if (0 <= time)
            /* print time */;
        else
            /* print get_signame (-time) */;
    }
}
#endif

void end_row (const struct target_status* status)
{
    if (status->status) /* if status (message) is set, print it */
        printf ("%6s\n", status->status);
    else if (status->exit) /* if exit (status) is non-zero, print it */
        printf ("%6d\n", status->exit);
    else if (!status->assert) /* if the assertion count is 0, it's an example */
        puts ("     0");
    else {
        unsigned pcnt = 0;
        if (status->assert) {
            pcnt = 100 * (status->assert - status->failed) / status->assert;
        }
        printf ("     0 %7u %6u %5d%%\n", status->assert, status->failed, pcnt);
    }
}

void print_footer ()
{
    switch (output_fmt) {
    case DISPLAY_HTML:
        break;
    default:
        break;
    }
}
/************************************************************************
 *
 * exec.h - Interface declaration for the result display subsystem
 *
 * $Id$
 *
 ************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you 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.
 * 
 **************************************************************************/

#ifndef RW_DISPLAY_H
#define RW_DISPLAY_H

/**
   Output format mode enumeration.

   Used to determine the mode used in producing output.
*/
extern enum display_modes {
    DISPLAY_PLAIN = 0, /**< plain text output, intended for files. */
    DISPLAY_XTERM, /**< xterm colored output. */
    DISPLAY_INDEP, /**< Non-targeted formated output. */
    DISPLAY_HTML /**< HTML formated output. */
} output_fmt;

/**
   Structure encapsulating the results extracted from a run.
*/
struct target_status {
    int exit; /**< exit code for process */
    const char* status; /**< Name for process status */
    unsigned warn; /**< Number of (test) warnings */
    unsigned assert; /**< Number of (test) assertions */
    unsigned failed; /**< Number of failed (test) assertions */
};

/**
   Sets the column order and widths (if applicable) for output.

   @todo describe format string format

   @param format custom format string for output.
*/
void set_header (const char* format);

/**
   Prints the table preamble formatting, followed by the formatted header row.
*/
void print_header ();

/**
   Prints the formatted header column for a target row.

   This method uses the target_name global (defined in cmdopt.h), and flushes
   stdout after printing.

   @see target_name
*/
void begin_row ();

/**
   Updates the display of a (non-final) status indicator

   This function is only useful when output_fmt == DISPLAY_XTERM.

   @param time 0 > time indicates -signal number, otherwise time remaining
*/
void set_progress (int time);

/**
   Prints the formatted results for a target row.

   @param status describes the results of the run
*/
void end_row (const struct target_status* status);


/**
   Prints the closing formatting for the table.
*/
void print_footer ();

#endif   // RW_DISPLAY_H
Index: etc/config/GNUmakefile.bin
===================================================================
--- etc/config/GNUmakefile.bin	(revision 433677)
+++ etc/config/GNUmakefile.bin	(working copy)
@@ -54,7 +54,7 @@
 	@$(MAKE) -C $(LIBDIR) 
 
 # link the run utility
-exec: runall.o cmdopt.o output.o util.o exec.o
+exec: runall.o cmdopt.o output.o util.o exec.o display.o
 	@echo "$(LD) $^ -o $@ $(LDFLAGS) $(LDLIBS)" >> $(LOGFILE)
 	$(LD) $^ -o $@ $(LDFLAGS) $(LDLIBS) $(TEEOPTS)
 
Index: util/output.h
===================================================================
--- util/output.h	(revision 436991)
+++ util/output.h	(working copy)
@@ -28,6 +28,6 @@
 #define OUTPUT_H
 
 void
-parse_output (const char* target);
+parse_output (const char* target, struct target_status* status);
 
 #endif   // OUTPUT_H
Index: util/output.cpp
===================================================================
--- util/output.cpp	(revision 436991)
+++ util/output.cpp	(working copy)
@@ -35,6 +35,7 @@
 #include <sys/stat.h>
 
 #include "cmdopt.h"
+#include "display.h"
 #include "util.h"
 
 #include "output.h"
@@ -49,27 +50,23 @@
    
    This method tries to open out_name.  If this succedes, it then searches
    the file for a result summary as produced by the rwtest driver.
-   If a result summary is found, it then parses the result and displays the
-   result of the parsing.  Otherwise, and appropriate error messages is 
-   displayed.
+   If a result summary is found, it then parses it and fills the status 
+   structure with the results of the parsing.  Otherwise, status->status is 
+   set to the error state.
 
-   Output messages produced:
-     - OUTPUT\n
-       Output file couldn't be opened
+   Error states:
      - OFLOW\n
        Assertions counted overflows the (internal)storage variable
      - FORMAT\n
        Result summary can't be found in output file
-     - 0 [assertion count] [failed assertion count] [pass rate]\n
-       Parsed results
 
    @param data pointer to file structure for output file being parsed
+   @param status status object to record results in.
 */
 static void
-check_test (FILE* data)
+check_test (FILE* data, struct target_status* status)
 {
     unsigned r_lvl = 0, r_active = 0, r_total = 0;
-    unsigned asserts = 0, failed = 0;
     int fmt_ok = 0;
     unsigned fsm = 0;
     char tok;
@@ -110,10 +107,10 @@
                    expected to be active, so invert the total */
                 if (8 == r_lvl && 0 == strcmp (target_name, "0.new"))
                     r_active = r_total-r_active;
-                failed += r_active;
-                asserts += r_total;
-                if (failed < r_active || asserts < r_total) {
-                    puts (" OFLOW");
+                status->failed += r_active;
+                status->assert += r_total;
+                if (status->failed < r_active || status->assert < r_total) {
+                    status->status = "OFLOW";
                     return;
                 }
             }
@@ -122,16 +119,8 @@
         }
     }
     
-    if (fmt_ok) {
-        unsigned pcnt = 0;
-        if (asserts) {
-            pcnt = 100 * (asserts - failed) / asserts;
-        }
-        printf ("     0 %7d %6d %5d%%\n", asserts, failed, pcnt);
-    }
-    else {
-        puts ("FORMAT");
-    }
+    if (!fmt_ok)
+        status->status = "FORMAT";
 }
 
 /**
@@ -145,9 +134,8 @@
    @see check_test ()
 */
 static void
-check_compat_test (FILE* data)
+check_compat_test (FILE* data, struct target_status* status)
 {
-    unsigned asserts = 0, failed = 0;
     int read = 0;
     unsigned fsm = 0;
     char tok;
@@ -179,19 +167,12 @@
 
     if (!feof (data)) { /* leading "## A" eaten above */
         read = fscanf (data, "ssertions = %u\n## FailedAssertions = %u",
-                        &asserts, &failed);
+                        &status->assert, &status->failed);
     }
 
-    if (2 == read) {
-        unsigned pcnt = 0;
-        if (asserts) {
-            pcnt = 100 * (asserts - failed) / asserts;
-        }
-        printf ("     0 %7d %6d %5d%%\n", asserts, failed, pcnt);
+    if (2 != read) {
+        status->status = "FORMAT";
     }
-    else {
-        puts ("FORMAT");
-    }
 }
 
 /**
@@ -205,31 +186,31 @@
    Parses output file out_name for the example target_name.
    
    This method tries to compare out_name against a reference output file and
-   displays a result code.  The reference file is determined by target_name and
-   the in_root global variable.
-   This method relies on the POSIX diff utility at this time.  This dependancy
-   needs to be removed, though doing so will require a rewrite of the method.
+   reports the result of the comparison.  If the comparison fails to result in
+   a match, status->status is set to the appropriate error state.  The 
+   reference file name is generated by the reference_name function.
 
    Reference file locations:
      - [in_root]/manual/out/[target_name].out
      - [in_root]/tutorial/out/[target_name].out
 
-   Output messages produced:
+   Error states:
      - NOREF\n
        No reference file could be found
      - OUTPUT\n
        Output file differs from the reference file
-     - 0\n
-       Output file matches the reference file (check passes)
+     - BADREF\n
+       Error accessing reference file
 
    @todo add logic to handle differing line endings (CR vs LF vs CRLF)
 
-   @param output_name the name of the output file
-   @param data pointer to file structure for output file being parsed
-   @see in_root
+   @param out_name the name of the output file
+   @param fout pointer to file structure for output file being parsed
+   @param status status object to record results in.
+   @see reference_name
 */
 static void
-check_example (char* const out_name, FILE* fout)
+check_example (char* const out_name, FILE* fout, struct target_status* status)
 {
     struct stat file_info;
     char* ref_name;
@@ -246,7 +227,7 @@
         if (ENOENT != errno) {
             warn ("stat (%s) error: %s\n", exe_name, ref_name, 
                   strerror (errno));
-            puts ("BADREF");
+            status->status = "BADREF";
             free (ref_name);
             return;
         }
@@ -260,10 +241,10 @@
             if (ENOENT != errno) {
                 warn ("stat (%s) error: %s\n", exe_name, ref_name, 
                       strerror (errno));
-                puts ("BADREF");
+                status->status = "BADREF";
             }
             else
-                puts (" NOREF");
+                status->status = "NOREF";
 
             free (ref_name);
             return;
@@ -276,7 +257,7 @@
         int cache = errno; /* caching errno, as puts could alter it */
         if (ENOENT != cache)
             warn ("Error opening %s: %s\n", ref_name, strerror (cache));
-        puts ("BADREF");
+        status->status = "BADREF";
     }
     else {
         int match = 1;   /* do the two files match? */
@@ -317,10 +298,8 @@
             }
         }
 
-        if (match)
-            puts ("     0");
-        else
-            puts ("OUTPUT");
+        if (!match)
+            status->status = "OUTPUT";
 
         fclose (fref);
     }
@@ -328,13 +307,17 @@
 }
 
 /**
-   Umbrella (dispatch) function to analyse the (saved) output of target
+   Umbrella (dispatch) function to analyse the (saved) output of target.
 
+   This file opens the saved output for parsing.  If this fails, 
+   status->status is set to an error state of "OUTPUT".
+
    @param target the path to the executable that generated the output file 
    being parsed.
+   @param status status object to record results in.
 */
 void
-parse_output (const char* target)
+parse_output (const char* target, struct target_status* status)
 {
     char* out_name;
     FILE* data;
@@ -347,20 +330,20 @@
     if (0 == data) {
         if (ENOENT != errno)
             warn ("Error opening %s: %s\n", out_name, strerror (errno));
-        puts ("OUTPUT");
+        status->status = "OUTPUT";
     }
     else {
         if (!strlen (in_root)) {
             /* If there is not an input directory, look at the assertion tags */
 
             if (!compat)
-                check_test (data);
+                check_test (data, status);
             else
-                check_compat_test (data);
+                check_compat_test (data, status);
         }
         else {
             /* Otherwise, diff against the output file */
-            check_example (out_name, data);
+            check_example (out_name, data, status);
         }
         fclose (data);
     }
Index: util/runall.cpp
===================================================================
--- util/runall.cpp	(revision 436991)
+++ util/runall.cpp	(working copy)
@@ -26,7 +26,6 @@
 
 #include <assert.h>     /* for assert() */
 #include <errno.h>      /* for errno */
-#include <stdio.h>      /* for fflush(), printf(), puts(), ... */
 #include <stdlib.h>     /* for exit(), free() */
 #include <string.h>     /* for memcpy(), ... */
 
@@ -40,6 +39,7 @@
 #endif
 
 #include "cmdopt.h"
+#include "display.h"
 #include "exec.h"
 #include "output.h"
 #include "util.h"
@@ -195,10 +195,12 @@
    This method checks to see if target exists and is theoretically executable.
    A special case is the situation where the target name ends in .o, 
    indicating that the target only needed to compile, and doesn't need to
-   be run.  If there are problems, or the target is a .o file, a result is 
-   displayed and 0 is returned.
+   be run.  If there are problems, or the target is a .o file, status->status 
+   is set with an error state and 0 is returned.
 
-   Output messages produced:
+   Error states:
+     - ERROR\n
+       System error (during stat operation)
      - COMP\n
        Target failed to compile
      - LINK\n
@@ -209,10 +211,11 @@
        Target .o file exists (check passes)
 
    @param target the path to the executable to check
+   @param status status object to record results in.
    @return 1 if valid target to run, 0 otherwise.
 */
 static int
-check_target_ok (const char* target)
+check_target_ok (const char* target, struct target_status* status)
 {
     struct stat file_info;
     int exists = 1;
@@ -220,7 +223,7 @@
     if (0 > stat (target, &file_info)) {
         if (ENOENT != errno) {
             warn ("Error stating %s: %s\n", target, strerror (errno));
-            puts ("ERROR");
+            status->status = "ERROR";
             return 0;
         }
         file_info.st_mode = 0; /* force mode on non-existant file to 0 */
@@ -236,17 +239,15 @@
 #if 0 /* Disable .o target check as unused */
         /* If target is a .o file, check if it exists */
         if ('.' == target [path_len-1] && 'o' == target [path_len]) {
-            if (exists)
-                puts ("     0");
-            else
-                puts ("  COMP");
+            if (!exists)
+                status->status = "COMP";
             return 0;
         }
 #endif
             
         /* If the target exists, it doesn't have valid permissions */
         if (exists) {
-            puts (" XPERM");
+            status->status = "XPERM";
             return 0;
         }
 
@@ -277,13 +278,13 @@
         if (0 > stat (tmp_name, &file_info)) {
             if (ENOENT != errno) {
                 warn ("Error stating %s: %s\n", tmp_name, strerror (errno));
-                puts ("ERROR");
+                status->status = "ERROR";
             }
             else
-                puts ("  COMP");
+                status->status = "COMP";
         }
         else {
-            puts ("  LINK");
+            status->status = "LINK";
         }
                 
         free (tmp_name);
@@ -298,67 +299,78 @@
    This method examines the content of result, dispatching the processing
    to check_test () or check_example () if target had a return code of 0.
    Otherwise, this method determines why target terminated, based on the
-   return code and outputs that reason.
+   return code and sets status->status or status->exit to the error state.
 
-   Output messages produced:
+   Unix Error states:
      - EXIST\n
        Return code 126 (target doesn't exist?)
      - EXEC\n
        Return code 127 (target couldn't be run?)
      - NKILL\n
        Couldn't kill the child executable
-     - n\n
-       Child exited with the non-zero return code n
      - name\n
        Child terminated upon recieving the signal SIGname
+     - UNKN \n
+       Child process subsystem returned in an unknown state.
 
+   Windows Error states:
+     - I/O\n
+       System error during child process execution.
+     - KILLED\n
+       Child process killed by the exec utility.
+     - SEGV\n
+       Child process terminated in a segfault
+
    @param target the path to the executable that was run
-   @param exec_name the short name of the executable
    @param result the return code data from running the target
+   @param status status object to record results in.
    @see check_test ()
    @see check_example ()
 */
 static void
-process_results (const char* target, const struct exec_attrs* result)
+process_results (const char* target, const struct exec_attrs* result, 
+                 struct target_status* status)
 {
     if (0 == result->status) {
-        parse_output (target);
+        parse_output (target, status);
     } 
 #if !defined (_WIN32) && !defined (_WIN64)
     else if (WIFEXITED (result->status)) {
         const int retcode = WEXITSTATUS (result->status);
         switch (retcode) {
         case 126:
-            puts (" EXIST");
+            status->status = "EXIST";
             break;
         case 127:
-            puts ("  EXEC");
+            status->status = "EXEC";
             break;
         default:
-            printf ("%6d\n", retcode);
+            status->exit = retcode;
         }
     }
     else if (WIFSIGNALED (result->status)) {
-        printf ("%6s\n", get_signame (WTERMSIG (result->status)));
+        status->status = get_signame (WTERMSIG (result->status));
     }
     else if (WIFSTOPPED (result->status)) {
-        printf ("%6s\n", get_signame (WSTOPSIG (result->status)));
+        status->status = get_signame (WSTOPSIG (result->status));
     }
     else if (-1 == result->status && -1 == result->killed) {
-        puts (" NKILL");
+        status->status = "NKILL";
     }
     else {
-        printf ("(%d|%d)\n", result->status, result->killed);
+        warn ("Unknown result status: %d, %d\n", result->status, 
+              result->killed);
+        status->status = "UNKN";
     }
 #else
     else if (-1 == result->status)
-        puts ("   I/O");
+        status->status = "I/O";
     else if (result->error)
-        puts ("KILLED");
+        status->status = "KILLED";
     else if (STATUS_ACCESS_VIOLATION == result->status)
-        puts ("  SEGV");
+        status->status = "SEGV";
     else
-        printf ("%6d\n", result->status);
+        status->exit = result->status;
 #endif   /* _WIN{32,64} */
 }
 
@@ -410,6 +422,7 @@
 run_target (char* target, char** argv)
 {
     char** childargv;
+    struct target_status results;
 
     assert (0 != target);
     assert (0 != argv);
@@ -421,14 +434,16 @@
 
     target_name = rw_basename (childargv [0]);
 
-    printf ("%-25.25s ", target_name);
-    fflush (stdout);
+    begin_row ();
+    memset (&results, 0, sizeof results);
 
-    if (check_target_ok (childargv [0])) {
+    if (check_target_ok (childargv [0], &results)) {
         struct exec_attrs status = exec_file (childargv);
-        process_results (childargv [0], &status);
+        process_results (childargv [0], &status, &results);
     }
 
+    end_row (&results);
+
     free (childargv [0]);
     free (childargv);
 }
@@ -468,12 +483,15 @@
         char** childargv = split_opt_string (exe_opts);
 
         assert (0 != childargv);
-        puts ("NAME                      STATUS ASSERTS FAILED PERCNT");
 
+        print_header ();
+
         for (i = 0; i < argc; ++i) {
             run_target (argv [i], childargv);
         }
 
+        print_footer ();
+
         if (childargv [0])
             free (childargv [0]);
         free (childargv);

Reply via email to