/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ /* Redir 2.1 Copyright (C) 1995-1999 DJ Delorie (dj@delorie.com) Redir is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Redir is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Here's the deal. We need to pass the command-line arguments to the child program *exactly* as we got them. This means we cannot allow any wildcard expansion, we need to retain any quote characters, and we need to disable response files processing. That's why _crt0_startup_flags are defined as they are, below, and that's why we define an empty __crt0_glob_function. In addition, we need to invoke the child program in the same way we were invoked: if they called us via `system', that's how the child should be invoked, and if they used `spawnXX', so should we. This is so the child will process quoted arguments and wildcards exactly as the caller wanted. */ int _crt0_startup_flags = (_CRT0_FLAG_DISALLOW_RESPONSE_FILES | _CRT0_FLAG_KEEP_QUOTES); char ** __crt0_glob_function(char *a) { return 0; } extern void *xmalloc(size_t), *xrealloc(void *, size_t); int time_it=0, display_exit_code=0; struct timeval startt, endt; int std_err_fid; FILE *std_err; int rv; static void usage(void) { /* ----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8 */ fprintf(stderr, "Redir 2.1 Copyright (C) 1995 - 1999 DJ Delorie (dj@delorie.com)\n"); fprintf(stderr, "Distribute freely. There is NO WARRANTY.\n"); fprintf(stderr, "This program is protected by the GNU General Public License.\n\n"); fprintf(stderr, "Usage: redir [-i file] [-o file] [-oa file] [-e file] [-ea file]\n"); fprintf(stderr, " [-eo] [-oe] [-x] [-t] command [args . . .]\n\n"); fprintf(stderr, " -i file redirect standard input from file\n"); fprintf(stderr, " -o file redirect standard output to file\n"); fprintf(stderr, " -oa file append standard output to file\n"); fprintf(stderr, " -e file redirect standard error to file\n"); fprintf(stderr, " -ea file append standard error to file\n"); fprintf(stderr, " -eo redirect standard error to standard output\n"); fprintf(stderr, " -oe redirect standard output to standard error\n"); fprintf(stderr, " -x print exit code\n"); fprintf(stderr, " -t print elapsed time\n"); fprintf(stderr, " command the program you want to run, with arguments\n\n"); fprintf(stderr, "Options are processed in the order they are encountered.\n\n"); exit(1); } static void fatal(const char *msg, const char *fn) { fprintf(std_err, msg, fn); fprintf(std_err, "\nThe error was: %s\n", strerror(errno)); exit(1); } static void unquote(const char *src, char *dst) { int quote=0; while ((quote || !isspace((unsigned char)*src)) && *src) { if (quote && *src == quote) { quote = 0; src++; } else if (!quote && (*src == '\'' || *src == '"')) { quote = *src; src++; } else if (*src == '\\' && strchr("'\"", src[1]) && src[1]) { src++; *dst++ = *src++; } else { *dst++ = *src++; } } *dst = '\0'; } static char * unquoted_argv(int argc, char *argv[], char *reuse) { char *new_arg; if (reuse) new_arg = (char *)xrealloc(reuse, strlen(argv[argc]) + 1); else new_arg = (char *)xmalloc(strlen(argv[argc]) + 1); unquote(argv[argc], new_arg); return new_arg; } extern char __PROXY[]; /* defined on crt1.c */ extern size_t __PROXY_LEN; static int run_program(int argc, char *argv[], int skip) { char doscmd[128]; char *tail = doscmd + 1, *tp = tail; size_t tail_len; int i = 0; /* Decide whether to invoke them with `spawn' or `system'. */ if (!getenv(__PROXY)) { movedata(_stubinfo->psp_selector, 128, _my_ds(), (int)doscmd, 128); tail_len = doscmd[0] & 0x7f; tail[tail_len] = '\0'; /* If the DOS command tail is "!proxy XXXX YYYY", then invoke via `spawn'. */ if (strstr(tail, __PROXY+1)) { while (isspace((unsigned char)*tp)) tp++; if (strncmp(tp, __PROXY+1, __PROXY_LEN-1)==0) { char *endarg; /* Paranoia: do we have at least three hex numbers after !proxy? */ endarg = tp + __PROXY_LEN - 1; do { tp = endarg; errno = 0; strtoul(tp, &endarg, 16); if (errno || endarg == tp) break; if (++i == 3) { gettimeofday(&startt, NULL); return spawnvp(P_WAIT, argv[skip], argv+skip); } } while (*endarg); } } /* The DOS command tail is the actual command line. Get past our own options we've already parsed, and pass the rest to the child via `system'. SKIP says how many argv[] elements to skip. */ for (tp = tail; skip--; argv++) { tp = strstr(tp, argv[1]); /* If, at some point, we don't find the next argv[] element, it's probably some disaster, because they all should be there. Instead of screaming bloody murder, we fall back on using argv[] from our `main', as the last resort. */ if (!tp) break; if (skip == 0) { /* We've come all the way to the child command line, invoke it. */ gettimeofday(&startt, NULL); return system(tp); } tp += strlen(argv[1]); /* get past this arg */ } } /* We need to recreate the original command line as a single string, from its breakdown in argv[]. */ for (tail_len = 0, i = skip; i < argc; i++) tail_len += strlen(argv[i]) + 1; /* +1 for the blank between args */ tp = tail = (char *)xmalloc(tail_len + 1); for (i = skip; i < argc; i++) { size_t len = strlen(argv[i]); memcpy(tp, argv[i], len); tp[len] = ' '; tp += len + 1; } tail[tail_len] = '\0'; gettimeofday(&startt, NULL); return system(tail); } int main(int argc, char **argv) { char *arg1 = NULL, *arg2 = NULL; int ac = argc; char **av = argv; /* Don't let us crash because some naughty program left the FPU in shambles. */ _clear87(); _fpreset(); if (argc < 2) usage(); std_err_fid = dup(1); std_err = fdopen(std_err_fid, "w"); /* We requested that the startup code retains any quote characters in the argv[] elements. So we need to unquote those that we process as we go. */ while (argc > 1 && (arg1 = unquoted_argv(1, argv, arg2))[0] == '-') { int temp; if (strcmp(arg1, "-i")==0 && argc > 2) { if ((temp = open(arg2 = unquoted_argv(2, argv, arg1), O_RDONLY, 0666)) < 0 || dup2(temp, 0) == -1) fatal("redir: attempt to redirect stdin from %s failed", arg2); close(temp); argc--; argv++; } else if (strcmp(arg1, "-o")==0 && argc > 2) { if ((temp = open(arg2 = unquoted_argv(2, argv, arg1), O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 || dup2(temp, 1) == -1) fatal("redir: attempt to redirect stdout to %s failed", arg2); close(temp); argc--; argv++; } else if (strcmp(arg1, "-oa")==0 && argc > 2) { if ((temp = open(arg2 = unquoted_argv(2, argv, arg1), O_WRONLY|O_APPEND|O_CREAT, 0666)) < 0 || dup2(temp, 1) == -1) fatal("redir: attempt to append stdout to %s failed", arg2); close(temp); argc--; argv++; } else if (strcmp(arg1, "-e")==0 && argc > 2) { if ((temp = open(arg2 = unquoted_argv(2, argv, arg1), O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 || dup2(temp, 2) == -1) fatal("redir: attempt to redirect stderr to %s failed", arg2); close(temp); argc--; argv++; } else if (strcmp(arg1, "-ea")==0 && argc > 2) { if ((temp = open(arg2 = unquoted_argv(2, argv, arg1), O_WRONLY|O_APPEND|O_CREAT, 0666)) < 0 || dup2(temp, 2) == -1) fatal("redir: attempt to append stderr to %s failed", arg2); close(temp); argc--; argv++; } else if (strcmp(arg1, "-eo")==0) { if (dup2(1,2) == -1) fatal("redir: attempt to redirect stderr to stdout failed", 0); } else if (strcmp(arg1, "-oe")==0) { if (dup2(2,1) == -1) fatal("redir: attempt to redirect stdout to stderr failed", 0); } else if (strcmp(arg1, "-x")==0) { display_exit_code = 1; } else if (strcmp(arg1, "-t")==0) { time_it = 1; } else usage(); argc--; argv++; } if (argc <= 1) { errno = EINVAL; fatal("Missing program name; aborting", ""); } /* We do NOT want `redir' to abort if the child is interrupted or crashes for any reason. */ _control87(0x033f, 0xffff); /* mask all numeric exceptions */ __djgpp_exception_toggle(); rv = run_program(ac, av, ac - argc + 1); __djgpp_exception_toggle(); _control87(0x033f, 0xffff); /* in case the child unmasked some */ _clear87(); /* clean up after the child, just in case */ _fpreset(); gettimeofday(&endt, NULL); if (rv < 0) fatal("Error attempting to run program %s", argv[1]); if (display_exit_code) { if ((rv & 255) == rv) fprintf(std_err, "Exit code: %d\n", rv & 255); else fprintf(std_err, "Exit code: %d (0x%04x)\n", rv & 255, rv); } if (time_it) { long min, sec, hour, elapsed_sec, elapsed_usec, msec; elapsed_sec = endt.tv_sec - startt.tv_sec; elapsed_usec = endt.tv_usec - startt.tv_usec; if (elapsed_usec < 0) { elapsed_sec -= 1; elapsed_usec += 1000000L; } msec = (elapsed_usec + 500) / 1000; sec = elapsed_sec % 60; min = (elapsed_sec / 60) % 60; hour = elapsed_sec / 3600; if (elapsed_sec > 59) fprintf(std_err, "Elapsed time: %ld.%03ld seconds (%ld:%02ld:%02ld.%03ld)\n", elapsed_sec, msec, hour, min, sec, msec); else fprintf(std_err, "Elapsed time: %ld.%03ld seconds\n", elapsed_sec, msec); } return rv; }