Return-Path: Delivered-To: apmail-incubator-stdcxx-dev-archive@www.apache.org Received: (qmail 85789 invoked from network); 10 Aug 2006 20:31:09 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 10 Aug 2006 20:31:09 -0000 Received: (qmail 32092 invoked by uid 500); 10 Aug 2006 20:31:09 -0000 Delivered-To: apmail-incubator-stdcxx-dev-archive@incubator.apache.org Received: (qmail 32080 invoked by uid 500); 10 Aug 2006 20:31:08 -0000 Mailing-List: contact stdcxx-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: stdcxx-dev@incubator.apache.org Delivered-To: mailing list stdcxx-dev@incubator.apache.org Received: (qmail 32068 invoked by uid 99); 10 Aug 2006 20:31:08 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 10 Aug 2006 13:31:08 -0700 X-ASF-Spam-Status: No, hits=0.0 required=10.0 tests= X-Spam-Check-By: apache.org Received-SPF: neutral (asf.osuosl.org: local policy) Received: from [208.30.140.160] (HELO moroha.quovadx.com) (208.30.140.160) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 10 Aug 2006 13:31:00 -0700 Received: from [10.70.3.48] ([10.70.3.48]) by moroha.quovadx.com (8.13.6/8.13.4) with ESMTP id k7AKUVh6008508 for ; Thu, 10 Aug 2006 20:30:31 GMT Message-ID: <44DB9766.4030004@roguewave.com> Date: Thu, 10 Aug 2006 14:30:30 -0600 From: Andrew Black User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060720 SeaMonkey/1.0.3 MIME-Version: 1.0 To: stdcxx-dev@incubator.apache.org Subject: Re: [patch] exec utility windows port References: <44DA6A90.50006@roguewave.com> <44DA7D4D.7080100@roguewave.com> In-Reply-To: <44DA7D4D.7080100@roguewave.com> Content-Type: multipart/mixed; boundary="------------030101000307080503000408" X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N --------------030101000307080503000408 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Greetings Attached is a reworked version of the patch with the requested changes included. Unfortunately, the change log has changed enough that a new one is required. If it is decided that the windows file structure is to be altered (with regards to the location of object files), this patch will need to be altered slightly. One that was asked was 'Why can't we use backslash as the escape character on Windows?' My answer is that the character is used as a path separator, and it would be impossible to disambiguate the conflicting uses. I use ^ as the escape character on windows, as that's the escape character used elsewhere in windows. Another question that was asked was about the block I #if 0 ed out. This change was intentional. The block in question is/was used to check compile only targets, but we don't have any of those at this time. IF this check were to be kept, it would need platform specific logic to handle the differences in suffixes for object files between windows and unix. --Andrew Black Log: * util.h (rw_snprintf): Add macro hack to work around lack of snprintf definition on windows. * util.h (reference_name, output_name): Declare functions to generate the names for reference and output files respectively. * util.cpp (reference_name, output_name): Define above. * cmdopt.h (escape_code, default_path_sep, suffix_len): Declare platform specific constants related to the file system. * cmdopt.cpp (escape_code, default_path_sep, suffix_len): Define above, conditional on platform. * cmdopt.cpp: Alter #includes to include windows.h on windows in place of unistd.h. * cmdopt.cpp (rw_sleep, rw_signal): Define platform abstraction functions for sleep and signal/sigaction, conditional on platform. * cmdopt.cpp (eval_options): use use rw_sleep() in place of sleep(), rw_signal() in place of sigaction(). * cmdopt.cpp (split_opt_string): use escape_code as escape character 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, exec_file): Alter (existing) version to use reference_name, output_name respectively. * 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): Use reference_name to determine reference file location. * 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): Use default_path_sep as separator for non-windows builds. --------------030101000307080503000408 Content-Type: text/x-patch; name="winport2.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="winport2.diff" 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 +*/ +#if !defined (_WIN32) && !defined (_WIN64) +# 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 @@ -52,4 +61,24 @@ void* guarded_realloc (void* source, const size_t size, const char* const file, const unsigned line); +/** + Generates the name of a reference (input/output) file, based on dir and mode. + + This function allocates memory which is to be freed by the caller. + + @param dir example subdirectory to reference + @param mode type of file to generate name for (should be 'in' or 'out') + @return translation of 'in_root/dir/mode/target_name.mode' +*/ +char* reference_name(const char* dir, const char* mode); + +/** + Generates the name of the output file for the executable target. + + This function allocates memory which is to be freed by the caller. + + @param path of target to generate output name for + @return translation of 'target.out' +*/ +char* output_name(const char* target); #endif // RW_UTIL_H Index: exec.cpp =================================================================== --- exec.cpp (revision 430088) +++ exec.cpp (working copy) @@ -37,10 +37,15 @@ #include #include /* for str*, mem* */ -#include /* for close, dup, exec, fork */ +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for close, dup, exec, fork */ +# include +#else +# include /* for PROCESS_INFORMATION et al. */ +# include /* for CreateProcess et al. */ +#endif #include /* for S_* */ #include -#include #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 @@ -542,15 +548,10 @@ assert (0 != in_root); if (root_len) { - const size_t out_len = root_len + strlen (exec_name) + 17; - - char* const tmp_name = (char*)RW_MALLOC (out_len); + char* tmp_name; /* 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"); + tmp_name = reference_name("manual", "in"); intermit = open (tmp_name, O_RDONLY); /* If we opened the file, return the descriptor */ @@ -565,10 +566,8 @@ 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"); + free (tmp_name); + tmp_name = reference_name("tutorial", "in"); intermit = open (tmp_name, O_RDONLY); /* If we opened the file, return the descriptor */ @@ -683,14 +682,9 @@ /* Redirect stdout */ { - const size_t exelen = strlen (argv [0]); - const size_t outlen = exelen + 5; - char* const tmp_name = (char*)RW_MALLOC (outlen); + char* const tmp_name = output_name(argv [0]); 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); @@ -727,3 +721,216 @@ /* 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); + HANDLE intermit; + DWORD error; + char* tmp_name; + + 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 */ + tmp_name = reference_name("manual", "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 */ + free (tmp_name); + tmp_name = reference_name("tutorial", "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 */ + { + /* Output redirection */ + char* const tmp_name = output_name(argv [0]); + + 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; + free (tmp_name); + + /* Input redirection */ + context.hStdInput = open_input (target_name, &child_sa); + if (INVALID_HANDLE_VALUE == context.hStdInput) + { + status.status = -1; + status.error = GetLastError (); + return status; + } + } + + /* 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 /* for *printf, fputs */ #include /* for exit */ #include /* for str* */ -#include /* for sleep */ +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for sleep */ +#else +# include /* for Sleep */ +#endif /* _WIN{32,64} */ #include "exec.h" #include "util.h" @@ -84,6 +88,43 @@ " '--option=value' or '--option value'.\n" }; +#if !defined (_WIN32) && !defined (_WIN64) +const char escape_code = '\\'; +const char default_path_sep = '//'; +const size_t exe_suffix_len = 0; + +static void +rw_sleep (int seconds) +{ + sleep (seconds); +} + +static int +rw_signal (int signo, void (*func)(int)) +{ + struct sigaction act; + memset (&act, 0, sizeof act); + act.sa_handler = func; + return 0 > sigaction (signo, &act, 0); +} +#else +const char escape_code = '^'; +const char default_path_sep = '\\'; +const size_t exe_suffix_len = 4; /* strlen(".exe") == 4 */ + +static void +rw_sleep (int seconds) +{ + Sleep (seconds * 1000); +} + +static int +rw_signal (int signo, void (*func)(int)) +{ + return SIG_ERR == signal (signo, func); +} +#endif + /** Display command line switches for program and terminate. @@ -304,7 +345,7 @@ if (optarg && *optarg) { const long nsec = strtol (optarg, &end, 10); if ('\0' == *end && 0 <= nsec && !errno) { - sleep (nsec); + rw_sleep (nsec); break; } } @@ -330,11 +371,8 @@ if (optarg && *optarg) { const long signo = get_signo (optarg); if (0 <= signo) { - 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", + if (rw_signal (signo, SIG_IGN)) + terminate (1, "rw_signal(%s, ...) failed: %s\n", get_signame (signo), strerror (errno)); break; } @@ -429,7 +467,7 @@ } in_token = 1; switch (*pos) { - case '\\': + case escape_code: in_escape = 1; break; case '"': Index: output.cpp =================================================================== --- output.cpp (revision 430088) +++ output.cpp (working copy) @@ -233,8 +233,7 @@ { 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); + char* ref_name; FILE* reference; assert (0 != in_root); @@ -243,10 +242,7 @@ assert (0 != output); /* Try in_root/manual/out/target_name.out */ - memcpy (ref_name, in_root, root_len+1); - strcat (ref_name, "/manual/out/"); - strcat (ref_name, target_name); - strcat (ref_name, ".out"); + ref_name = reference_name("manual", "out"); if (0 > stat (ref_name, &file_info)) { if (ENOENT != errno) { @@ -259,10 +255,8 @@ /* If that doesn't exist, try in_root/tutorial/out/target_name.out */ - memcpy (ref_name, in_root, root_len+1); - strcat (ref_name, "/tutorial/out/"); - strcat (ref_name, target_name); - strcat (ref_name, ".out"); + free (ref_name); + ref_name = reference_name("tutorial", "out"); if (0 > stat (ref_name, &file_info)) { if (ENOENT != errno) { @@ -344,15 +338,11 @@ void parse_output (const char* target) { - const size_t path_len = strlen (target); - char* const out_name = (char*)RW_MALLOC (path_len + 5); + char* const out_name = output_name(target); FILE* data; assert (0 != target); - memcpy (out_name, target, path_len + 1); - strcat (out_name,".out"); - data = fopen (out_name, "r"); if (0 == data) { Index: util.cpp =================================================================== --- util.cpp (revision 430088) +++ util.cpp (working copy) @@ -122,3 +122,53 @@ return alloc; } + +char* +reference_name(const char* dir, const char* mode) +{ + const size_t root_len = strlen (in_root); + const size_t cmp_len = strlen (target_name) - exe_suffix_len; + const size_t dir_len = strlen (dir); + const size_t mode_len = strlen (mode); + char* const ref_name = (char*)RW_MALLOC (root_len + cmp_len + dir_len + + mode_len * 2 + 5); + /* 4 comes from 3 path seperator characters the suffix seperator, and a + trailing null */ + char* tail = ref_name; + + assert (0 != in_root); + assert (0 != target_name); + assert (0 != dir); + assert (0 != mode); + + memcpy (tail, in_root, root_len); + tail += root_len; + *(tail++) = default_path_sep; + memcpy (tail , dir, dir_len); + tail += dir_len; + *(tail++) = default_path_sep; + memcpy (tail , mode, mode_len); + tail += mode_len; + *(tail++) = default_path_sep; + memcpy (tail , target_name, cmp_len); + tail += cmp_len; + *(tail++) = '.'; + memcpy (tail , mode, mode_len); + tail += mode_len; + *(tail) = '\0'; + + return ref_name; +} + +char* +output_name(const char* target) +{ + const char* suffix = ".out"; + const size_t sfx_len = strlen (suffix); + const size_t exe_len = strlen (target) - exe_suffix_len; + char* const tmp_name = (char*)RW_MALLOC (exe_len + sfx_len + 1); + + memcpy (tmp_name, target, exe_len); + memcpy (tmp_name + exe_len, ".out", sfx_len + 1); + return tmp_name; +} 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: cmdopt.h =================================================================== --- cmdopt.h (revision 430088) +++ cmdopt.h (working copy) @@ -34,6 +34,9 @@ extern const char* in_root; extern const char* exe_name; extern const char* target_name; /**< Alias for current target name. */ +extern const char escape_code; /**< Escape character used in paths. */ +extern const char default_path_sep; /**< Primary path seperator */ +extern const size_t exe_suffix_len; /**< Length of executable suffix. */ void show_usage (int status); 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 /* 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) @@ -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} */ } /** @@ -329,9 +371,13 @@ assert (0 != path); - for (mark = pos = path; '\0' != *pos; ++pos) + for (mark = pos = path; '\0' != *pos; ++pos) { +#if !defined (_WIN32) && !defined (_WIN64) + mark = (default_path_sep == *pos) ? pos + 1 : mark; +#else mark = ('/' == *pos || '\\' == *pos) ? pos + 1 : mark; - +#endif /* _WIN{32,64} */ + } return mark; } --------------030101000307080503000408--