Greetings all.

Attached is a patch altering source for the the exec utility so that it should build on windows systems. This patch does not alter the solution generation infrastructure to actually build the utility, but instead changes the source of the utility to make it compatible with windows.

Change log follows (and it's a bit long).

--Andrew Black

Log:
* util.h (rw_snprintf): Add macro hack to work around lack of snprintf definition on windows. * cmdopt.cpp: Alter #includes to include windows.h on windows in place of unistd.h. * cmdopt.cpp (eval_options): use Sleep() on windows in place of sleep(), signal() on windows in place of sigaction(). * cmdopt.cpp (split_opt_string): use '^' as escape character on windows in place of '\'.
        * exec.h (exec_attrs): Alter structure for windows builds.
* exec.cpp: Alter #includes to include windows.h and process.h on windows in place of unistd.h and sys/wait.h
        * exec.cpp (get_signame): Use rw_snprintf in place of snprintf.
* exec.cpp (handle_alrm, wait_for_child, open_input, replace_file, exec_file): Compile (existing) versions only on non-windows platforms. * exec.cpp (open_input, merge_argv, exec_file): Define new functions using windows native API, which will compile only on windows platforms. * output.cpp (check_example): Alter logic used to determine the location of the reference file to handle windows filenames. * runall.cpp (S_IXUSR, S_IXGRP, S_IXOTH): Define if not defined for windows. * runall.cpp (check_target_ok): Disable (unused) logic for output only targets. Alter compile check on windows systems to correctly locate .obj file.
        * runall.cpp (process_results): Handle windows result codes correctly.
* runall.cpp (rw_basename): Correct handling of escaping in path names, unix path token separators.
Index: util.h
===================================================================
--- util.h	(revision 430088)
+++ util.h	(working copy)
@@ -28,6 +28,15 @@
 #define RW_UTIL_H
 
 /**
+   Ugly workaround for windows non-definition of snprintf
+*/
+#ifndef _WIN32
+#  define rw_snprintf snprintf
+#else
+#  define rw_snprintf _snprintf
+#endif
+
+/**
    Generates a non-terminal error message on stderr.
 
    @param format printf () format string to display on stderr
Index: exec.cpp
===================================================================
--- exec.cpp	(revision 430088)
+++ exec.cpp	(working copy)
@@ -37,10 +37,15 @@
 #include <stdlib.h>
 #include <string.h> /* for str*, mem* */
 
-#include <unistd.h> /* for close, dup, exec, fork */
+#if !defined (_WIN32) && !defined (_WIN64)
+#  include <unistd.h> /* for close, dup, exec, fork */
+#  include <sys/wait.h>
+#else
+#  include <windows.h> /* for PROCESS_INFORMATION et al. */
+#  include <process.h> /* for CreateProcess et al. */
+#endif
 #include <sys/stat.h> /* for S_* */
 #include <sys/types.h>
-#include <sys/wait.h>
 
 #include "cmdopt.h"
 #include "util.h"
@@ -350,10 +355,11 @@
     }
 
     /* We've run out of known signal numbers, so use a default name */
-    snprintf (def, sizeof def, "SIG#%d", signo);
+    rw_snprintf (def, sizeof def, "SIG#%d", signo);
     return def;
 }
 
+#if !defined (_WIN32) && !defined (_WIN64)
 /**
    Callback used to set the alarm_timeout flag in response to recieving
    the signal SIGALRM
@@ -727,3 +733,226 @@
     /* parent */
     return wait_for_child (child_pid);
 }
+#else  /* _WIN{32,64} */
+/**
+   Opens an input file, based on exec_name, using the child_sa security setting
+
+   Takes an executable name and security setting, and tries to open an input 
+   file based on these variables and the value of the in_root global variable.
+   If a file is found in neither of two locations derived from these 
+   variables, or if in_root is a null string, it returns null.
+   If a file system error occurs when opening a file, INVALID_HANDLE_VALUE
+   is returned (and should be checked for).
+
+   Source file locations:
+     - [in_root]/manual/in/[exec_name].in
+     - [in_root]/tutorial/in/[exec_name].in
+
+   @param exec_name the name of executable being run
+   @param child_sa pointer to a SECURITY_ATTRIBUTES    structure
+   @returns the file descriptor of the opened file
+   @see in_root
+*/
+static HANDLE
+open_input (const char* exec_name, SECURITY_ATTRIBUTES* child_sa)
+{
+    const size_t root_len = strlen (in_root);
+    const size_t exec_len = strlen (exec_name) - 4; /* Remove trailing .exe */
+    const size_t out_len = root_len + exec_len + 17;
+    HANDLE intermit;
+    DWORD error;
+    char* const tmp_name = (char*)RW_MALLOC (out_len);
+
+    assert (0 != exec_name);
+    assert (0 != in_root);
+    assert (0 != child_sa);
+
+    if (!root_len) 
+        return 0;
+
+    /* Try in_root\manual\in\exec_name.in */
+    memcpy (tmp_name, in_root, root_len+1);
+    strcat (tmp_name, "\\manual\\in\\");
+    strncat (tmp_name, exec_name, exec_len);
+    strcat (tmp_name, ".in");
+
+    intermit = CreateFile(tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, 
+        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    error = GetLastError ();
+    /* If we found the file, return the descriptor */
+    if (INVALID_HANDLE_VALUE != intermit || (2 != error && 3 != error)) {
+        free (tmp_name);
+        return intermit;
+    }
+
+    /* Try in_root\tutorial\in\exec_name.in */
+    memcpy (tmp_name, in_root, root_len+1);
+    strcat (tmp_name, "\\tutorial\\in\\");
+    strncat (tmp_name, exec_name, exec_len);
+    strcat (tmp_name, ".in");
+    intermit = CreateFile(tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, 
+        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    /* If we didn't find the file, null out the handle to return */
+    error = GetLastError ();
+    if (INVALID_HANDLE_VALUE == intermit && (2 == error || 3 == error)) {
+        intermit = 0;
+    }
+
+    free (tmp_name);
+    return intermit;
+}
+
+/**
+   Convert an argv array into a string that can be passed to CreateProcess.
+
+   This method allocates memory which the caller is responsible for free ()ing.
+   The provided argv array is converted into a series of quoted strings.
+
+   @param argv argv array to convert
+   @return allocated array with converted contents
+*/
+static char*
+merge_argv (char** argv)
+{
+    size_t len = 0;
+    char** opts;
+    char* term;
+    char* merge;
+    char* pos;
+
+    assert (0 != argv);
+
+    for (opts = argv; *opts; ++opts) {
+        len += 3; /* for open ", close " and trailing space or null */
+        for (term = *opts; *term; ++term) {
+            ++len;
+            if ('"' == *term || '^' == *term)
+                ++len; /* Escape embedded "s and ^s*/
+        }
+    }
+
+    pos = merge = (char*)RW_MALLOC(len);
+    for (opts = argv; *opts; ++opts) {
+        *(pos++) = '"';
+        for (term = *opts; *term; ++term) {
+            if ('"' == *term || '^' == *term)
+                *(pos++) = '^';  /* Escape embedded "s and ^s*/
+            *(pos++) = *term;
+        }
+        *(pos++) = '"';
+        *(pos++) = ' ';
+    }
+    *(pos-1) = '\0'; /* convert trailing space to null */
+    return merge;
+}
+
+/**
+   Entry point to the child process (watchdog) subsystem.
+
+   This method creates a process using the windows CreateProcess API call.
+   The startup context for this process is based on the context of the exec
+   utility, but with the standard * file handles redirected.  The execution of
+   the process is monitored with the WaitForSingleObject API call.  If the 
+   process doesn't complete within the allowed timeout, it is then killed.  On 
+   Windows NT systems, a soft kill of the process (via the 
+   GenerateConsoleCtrlEvent API call) are first attempted attempted.  The 
+   process is then hard killed via the TerminateProcess API call.
+
+   @param exec_name name of the child executable
+   @param argv argv array for child process
+   @return structure describing how the child procees exited
+   @see wait_for_child ()
+*/
+struct exec_attrs 
+exec_file (char** argv)
+{
+    char* merged = merge_argv(argv);
+    PROCESS_INFORMATION child;
+    STARTUPINFO context;
+    SECURITY_ATTRIBUTES child_sa = /* SA for inheritable handle. */
+          {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+    struct exec_attrs status;
+
+    assert (0 != argv);
+
+    /* Borrow our startup info */
+    GetStartupInfo (&context);
+    context.dwFlags = STARTF_USESTDHANDLES;
+
+    /* Create I/O handles */
+    {
+        /* Input redirection */
+        context.hStdInput = open_input (target_name, &child_sa);
+        if (INVALID_HANDLE_VALUE == context.hStdInput)
+        { 
+            status.status = -1;
+            status.error = GetLastError ();
+            return status;
+        }
+
+        /* Output redirection */
+        const size_t exelen = strlen (argv [0]);
+        const size_t outlen = exelen + 5;
+        char* const tmp_name = (char*)RW_MALLOC (outlen);
+
+        memcpy (tmp_name, argv [0], exelen + 1);
+        strcat (tmp_name, ".out");
+
+        context.hStdOutput = CreateFile (tmp_name, GENERIC_WRITE, 
+                FILE_SHARE_WRITE, &child_sa, CREATE_ALWAYS, 
+                FILE_ATTRIBUTE_NORMAL, NULL);
+        if (INVALID_HANDLE_VALUE == context.hStdOutput)
+        { 
+            status.status = -1;
+            status.error = GetLastError ();
+            return status;
+        }
+        context.hStdError = context.hStdOutput;
+    }    
+
+    /* Create the child process */
+    CreateProcess (argv[0], merged, 0, 0, 1, 
+        DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, 0, 0, &context, &child);
+
+    /* Clean up handles */
+    CloseHandle(context.hStdInput);
+    CloseHandle(context.hStdOutput);
+
+    /* Clean up argument string*/
+    free (merged);
+
+    /* Wait for the child process to terminate */
+    if(WAIT_OBJECT_0 == WaitForSingleObject (child.hProcess, (timeout > 0) ? timeout * 1000 : INFINITE)) {
+        GetExitCodeProcess (child.hProcess, &status.status);
+        status.error = 0;
+        return status;
+    }    
+
+    {/* Try to soft kill child process group if it didn't terminate, but only on NT */
+        OSVERSIONINFO OSVer;
+        OSVer.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+        GetVersionEx (&OSVer);
+        if(VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) {
+            GenerateConsoleCtrlEvent (CTRL_C_EVENT, child.dwProcessId);
+            if(WAIT_OBJECT_0 == WaitForSingleObject (child.hProcess, 1000)) {
+                GetExitCodeProcess (child.hProcess, &status.status);
+                status.error = 1;
+                return status;
+            }
+            GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, child.dwProcessId);
+            if(WAIT_OBJECT_0 == WaitForSingleObject (child.hProcess, 1000)) {
+                GetExitCodeProcess (child.hProcess, &status.status);
+                status.error = 2;
+                return status;
+            }
+        }
+    }
+    /* Then hard kill the child process */
+    TerminateProcess (child.hProcess, 3);
+    GetExitCodeProcess (child.hProcess, &status.status);
+    status.error = 3;
+    return status;
+}
+#endif  /* _WIN{32,64} */
Index: cmdopt.cpp
===================================================================
--- cmdopt.cpp	(revision 430088)
+++ cmdopt.cpp	(working copy)
@@ -35,7 +35,11 @@
 #include <stdio.h> /* for *printf, fputs */
 #include <stdlib.h> /* for exit */
 #include <string.h> /* for str* */
-#include <unistd.h> /* for sleep */
+#if !defined (_WIN32) && !defined (_WIN64)
+#  include <unistd.h> /* for sleep */
+#else
+#  include <windows.h> /* for Sleep */
+#endif   /* _WIN{32,64} */
 
 #include "exec.h"
 #include "util.h"
@@ -304,7 +308,11 @@
                 if (optarg && *optarg) {
                     const long nsec = strtol (optarg, &end, 10);
                     if ('\0' == *end && 0 <= nsec && !errno) {
+#if !defined (_WIN32) && !defined (_WIN64)
                         sleep (nsec);
+#else
+                        Sleep (nsec * 1000);
+#endif
                         break;
                     }
                 }
@@ -330,12 +338,18 @@
                 if (optarg && *optarg) {
                     const long signo = get_signo (optarg);
                     if (0 <= signo) {
+#if !defined (_WIN32) && !defined (_WIN64)
                         struct sigaction act;
                         memset (&act, 0, sizeof act);
                         act.sa_handler = SIG_IGN;
                         if (0 > sigaction (signo, &act, 0))
                             terminate (1, "sigaction(%s, ...) failed: %s\n",
                                        get_signame (signo), strerror (errno));
+#else
+                        if (SIG_ERR == signal (signo, SIG_IGN))
+                            terminate (1, "sigaction(%s, ...) failed: %s\n",
+                                       get_signame (signo), strerror (errno));
+#endif   /* _WIN{32,64} */
                         break;
                     }
                 }
@@ -429,7 +443,11 @@
         }
         in_token = 1;
         switch (*pos) {
+#if !defined (_WIN32) && !defined (_WIN64)
         case '\\':
+#else
+        case '^':
+#endif   /* _WIN{32,64} */
             in_escape = 1;
             break;
         case '"':
Index: output.cpp
===================================================================
--- output.cpp	(revision 430088)
+++ output.cpp	(working copy)
@@ -233,8 +233,12 @@
 {
     struct stat file_info;
     const size_t root_len = strlen (in_root);
-    char* const ref_name = (char*)RW_MALLOC (root_len 
-                                             + strlen (target_name) + 19);
+#if !defined (_WIN32) && !defined (_WIN64)
+    const size_t cmp_len = strlen (target_name);
+#else
+    const size_t cmp_len = strlen (target_name) - 4; /* Remove trailing .exe */
+#endif   /* _WIN{32,64} */
+    char* const ref_name = (char*)RW_MALLOC (root_len + cmp_len + 19);
     FILE* reference;
 
     assert (0 != in_root);
@@ -244,8 +248,13 @@
 
     /* Try in_root/manual/out/target_name.out */
     memcpy (ref_name, in_root, root_len+1);
+#if !defined (_WIN32) && !defined (_WIN64)
     strcat (ref_name, "/manual/out/");
     strcat (ref_name, target_name);
+#else
+    strcat (ref_name, "\\manual\\out\\");
+    strncat (ref_name, target_name, cmp_len);
+#endif   /* _WIN{32,64} */
     strcat (ref_name, ".out");
 
     if (0 > stat (ref_name, &file_info)) {
@@ -260,8 +269,13 @@
         /* If that doesn't exist, try 
            in_root/tutorial/out/target_name.out */
         memcpy (ref_name, in_root, root_len+1);
+#if !defined (_WIN32) && !defined (_WIN64)
         strcat (ref_name, "/tutorial/out/");
         strcat (ref_name, target_name);
+#else
+        strcat (ref_name, "\\tutorial\\out\\");
+        strncat (ref_name, target_name, cmp_len);
+#endif   /* _WIN{32,64} */
         strcat (ref_name, ".out");
 
         if (0 > stat (ref_name, &file_info)) {
Index: exec.h
===================================================================
--- exec.h	(revision 430088)
+++ exec.h	(working copy)
@@ -28,8 +28,14 @@
 #define RW_EXEC_H
 
 struct exec_attrs {
+#if !defined (_WIN32) && !defined (_WIN64)
     int status;
     int killed;
+#else
+    /* AKA DWORD */
+    unsigned long status;
+    unsigned long error;
+#endif  /* _WIN{32,64} */
 };
 
 int get_signo (const char* signame);
Index: runall.cpp
===================================================================
--- runall.cpp	(revision 430088)
+++ runall.cpp	(working copy)
@@ -44,6 +44,18 @@
 #  define ENOENT 2
 #endif   // ENOENT
 
+#ifndef S_IXUSR
+#  define S_IXUSR 0100
+#endif   // S_IXUSR
+
+#ifndef S_IXGRP
+#  define S_IXGRP 0010
+#endif   // S_IXGRP
+
+#ifndef S_IXOTH
+#  define S_IXOTH 0001
+#endif   // S_IXOTH
+
 /**
    Utility function to rework the argv array
 
@@ -211,6 +223,7 @@
         const size_t path_len = strlen (target);
         char* tmp_name;
 
+#if 0
         /* If target is a .o file, check if it exists */
         if ('.' == target [path_len-1] && 'o' == target [path_len]) {
             if (exists)
@@ -219,18 +232,38 @@
                 puts ("  COMP");
             return 0;
         }
-            
+#endif
+
         /* If the target exists, it doesn't have valid permissions */
         if (exists) {
             puts (" XPERM");
             return 0;
         }
 
-        /* Otherwise, check for the .o file */
+#if !defined (_WIN32) && !defined (_WIN64)
+        /* Otherwise, check for the .o file on non-windows systems */
         tmp_name = (char*)RW_MALLOC (path_len + 3);
         memcpy (tmp_name, target, path_len + 1);
         strcat (tmp_name,".o");
-
+#else
+        /* Or the target\target.obj file on windows systems*/
+        {
+            size_t target_len = strlen (target_name);
+            size_t tmp_len = path_len + target_len - 2;
+                /* - 2 comes from removing 4 characters (extra .exe) and 
+                   adding 2 characters (\ directory seperator and trailing 
+                   null) */
+            tmp_name = (char*)RW_MALLOC (tmp_len);
+            memcpy (tmp_name, target, path_len - 4);
+            tmp_name [path_len - 4] = '\\';
+            tmp_name [path_len - 3] = '\0';
+            strcat (tmp_name, target_name);
+            tmp_name [tmp_len - 4] = 'o';
+            tmp_name [tmp_len - 3] = 'b';
+            tmp_name [tmp_len - 2] = 'j';
+            /* trailing null is tmp_len - 1 */
+        }
+#endif   /* _WIN{32,64} */
         if (0 > stat (tmp_name, &file_info)) {
             if (ENOENT != errno) {
                 warn ("Error stating %s: %s\n", tmp_name, strerror (errno));
@@ -281,6 +314,7 @@
     if (0 == result->status) {
         parse_output (target);
     } 
+#if !defined (_WIN32) && !defined (_WIN64)
     else if (WIFEXITED (result->status)) {
         const int retcode = WEXITSTATUS (result->status);
         switch (retcode) {
@@ -306,6 +340,14 @@
     else {
         printf ("(%d|%d)\n", result->status, result->killed);
     }
+#else
+    else if (-1 == result->status)
+        puts ("   I/O");
+    else if (result->error)
+        puts ("KILLED");
+    else
+        printf ("%6d\n", result->status);
+#endif   /* _WIN{32,64} */
 }
 
 /**
@@ -326,12 +368,19 @@
 rw_basename (const char* path)
 {
     const char *pos, *mark;
+    int escape = 0;
 
     assert (0 != path);
 
-    for (mark = pos = path; '\0' != *pos; ++pos)
-        mark = ('/' == *pos || '\\' == *pos) ? pos + 1 : mark;
-
+    for (mark = pos = path; '\0' != *pos; ++pos) {
+#if !defined (_WIN32) && !defined (_WIN64)
+        mark = (!escape && '/' == *pos) ? pos + 1 : mark;
+        escape = ('\\' == *pos && !escape);
+#else
+        mark = (!escape && ('/' == *pos || '\\' == *pos)) ? pos + 1 : mark;
+        escape = ('^' == *pos && !escape);
+#endif   /* _WIN{32,64} */
+    }
     return mark;
 }
 

Reply via email to