Logo Search packages:      
Sourcecode: vile version File versions

spawn.c

/*    Spawn:      various DOS access commands
 *          for MicroEMACS
 *
 * $Header: /usr/build/vile/vile/RCS/spawn.c,v 1.178 2003/05/25 20:21:34 tom Exp $
 *
 */

#ifdef _WIN32
# include    <process.h>
#endif

#include    "estruct.h"
#include    "edef.h"
#include    "nefunc.h"

#if SYS_UNIX && defined(SIGTSTP) && !DISP_X11
#define USE_UNIX_JOB_CTL 1
#else
#define USE_UNIX_JOB_CTL 0
#endif

#if SYS_VMS
#include <starlet.h>
#include <lib$routines.h>
extern int vms_system(char *);      /*FIXME: not the same as 'system()'? */
#endif

#if OPT_FINDPATH

typedef struct struct_findinfo {
    FINDCFG cfg;        /* findcfg mode info             */
    const char *dir_list;     /* findpath directory list       */
    int nonrecursive;         /* this find cmd is nonrecursive */
} FINDINFO;

static char *ck_find_cmd(char *cmd, int *allocd_storage, int prepend_bang);
static char *find_all_files(char *cmd, FINDINFO * pinfo, int prepend_bang);
static char *find_dirs_only(char *cmd, FINDINFO * pinfo, int prepend_bang);

static char *prev_findcmd;    /* last shell command created by the builtin
                         * find feature.  debug aid -- may go away
                         */
#endif

static int spawn1(int rerun, int pressret);

#if   SYS_VMS
#define EFN 0           /* Event flag.          */

#include    <ssdef.h>   /* Random headers.      */
#include    <stsdef.h>
#include    <descrip.h>
#include    <iodef.h>

extern int oldmode[3];        /* In "termio.c"        */
extern int newmode[3];        /* In "termio.c"        */
extern short iochan;          /* In "termio.c"        */
#endif

#if CC_NEWDOSCC && !CC_DJGPP  /* Typo, was NEWSDOSCC  */
#include    <process.h>
#endif

/*
 * Check all modification-times after executing a shell command
 */
#ifdef      MDCHK_MODTIME
#define     AfterShell()      check_visible_files_changed()
#else
#define     AfterShell()      TRUE
#endif

#if DISP_X11 && !SMALLER
static void
x_window_SHELL(const char *cmd)
{
    TBUFF *tmp = 0;

#ifdef HAVE_WAITPID
    int pid;

    if ((pid = fork()) > 0) {
      waitpid(pid, 0, 0);
    } else if (pid == 0) {
      if ((pid = fork()) == 0) {
#endif
          /*
           * We would use the -display option of xterm, but that
           * would get in the way of the user's ability to
           * customize $xshell.
           */
#ifdef HAVE_PUTENV
          static char *display_env;
          char *env = get_xdisplay();
          if (display_env != 0)
            free(display_env);
          display_env = typeallocn(char, 20 + strlen(env));
          lsprintf(display_env, "DISPLAY=%s", env);
          putenv(display_env);
#endif

          tmp = tb_scopy(&tmp, get_xshell());
          if (cmd != 0) {
            tb_unput(tmp);
            tmp = tb_sappend(&tmp, " ");
            tmp = tb_sappend(&tmp, get_xshellflags());
            tmp = tb_sappend(&tmp, " ");
            tmp = tb_sappend(&tmp, cmd);
            tmp = tb_append(&tmp, EOS);
          }
          TRACE(("executing '%s'\n", tb_values(tmp)));
          (void) system(tb_values(tmp));
          tb_free(&tmp);
#ifdef HAVE_WAITPID
      }
      _exit(0);
    }
#endif
}
#endif

/*
 * Create a subjob with a copy of the command interpreter in it. When the
 * command interpreter exits, mark the screen as garbage so that you do a full
 * repaint. The message at the start in VMS puts out a newline.
 * Under some (unknown) condition, you don't get one free when DCL starts up.
 */
/* ARGSUSED */
int
spawncli(int f GCC_UNUSED, int n GCC_UNUSED)
{
#undef OK_SPAWN

#if   SYS_UNIX
#define     OK_SPAWN
    ttclean(TRUE);
    file_stat(0, 0);
    kbd_openup();
#if   DISP_X11 && !SMALLER
    (void) x_window_SHELL((char *) 0);
#else
    (void) system_SHELL((char *) 0);
#endif
    kbd_openup();
    ttunclean();
    term.kopen();
    sgarbf = TRUE;
    return AfterShell();
#endif /* SYS_UNIX */

#if   SYS_VMS
#define     OK_SPAWN
    mlforce("[Starting DCL]\r\n");
    kbd_flush();        /* Ignore "ttcol".      */
    sgarbf = TRUE;
    return vms_system(NULL);  /* NULL => DCL.         */
#endif

#if   SYS_MSDOS || SYS_OS2 || SYS_WINNT
#define     OK_SPAWN
    bottomleft();
    kbd_flush();
    term.kclose();
    {
      char *shell = get_shell();
#if SYS_OS2
      /*
       * spawn it if we know it.  Some 3rd party command processors fail
       * if they system themselves (eg 4OS2).  CCM 24-MAR-94
       */
      spawnl(P_WAIT, shell, shell, NULL);
#else
#if SYS_WINNT
# if DISP_NTCONS
      w32_CreateProcess(shell, FALSE);
# else
      /*
       * This is winvile, in which case the editor _might_ have
       * been launched from the command line (which implies
       * direct access to an existing Win32 console environment)
       * or else was launched as a true Win32 app (has no console
       * env).  The latter case requires that CreateProcess() is
       * called in such a way that a console is guaranteed to be
       * allocated in the editor's behalf.
       *
       * The net result of the following call to w32_CreateProcess()
       * is that the spawned shell runs in its own process, while
       * winvile regains control immediately (i.e., there is no
       * wait for the spawned shell to exit).
       */
      w32_CreateProcess(shell, TRUE);
# endif
#else
      system(shell);
#endif
#endif
    }
    term.kopen();
    sgarbf = TRUE;
    return AfterShell();
#endif

#ifndef     OK_SPAWN
    mlforce("[This version of vile cannot spawn an interactive shell]");
    return FALSE;
#endif
}

#if USE_UNIX_JOB_CTL
int
bktoshell(int f, int n)       /* suspend and wait to wake up */
{
    int forced = (f && n == SPECIAL_BANG_ARG);  /* then it was :stop! */

    /* take care of autowrite */
    if (!forced && writeall(f, n, FALSE, TRUE, TRUE, FALSE) != TRUE)
      return FALSE;

    beginDisplay();
    ttclean(TRUE);
    file_stat(0, 0);

/* #define simulate_job_control_for_debug */
# ifdef simulate_job_control_for_debug
    rtfrmshell(SIGCONT);
    return TRUE;
# else
    (void) signal_pg(SIGTSTP);
    return TRUE;
# endif
}
#else
/* ARGSUSED */
int
bktoshell(int f GCC_UNUSED, int n GCC_UNUSED)
{
    mlforce("[Job control unavailable]");
    return FALSE;
}
#endif /* SIGTSTP */

/*ARGSUSED*/
SIGT
rtfrmshell(int ACTUAL_SIG_ARGS GCC_UNUSED)
{
#if USE_UNIX_JOB_CTL
    endofDisplay();
    kbd_openup();
    ttunclean();
    sgarbf = TRUE;
    setup_handler(SIGCONT, rtfrmshell);         /* suspend & restart */
    (void) update(TRUE);
#endif
#if defined(MDCHK_MODTIME)
    (void) check_visible_files_changed();
#endif
    SIGRET;
}

void
pressreturn(void)
{
    int c;
    int osgarbf;

    osgarbf = sgarbf;
    sgarbf = FALSE;
    mlforce("[Press return to continue]");
    sgarbf = osgarbf;
    /* loop for a CR, a space, or a : to do another named command */
    reading_msg_line = TRUE;
    while ((c = keystroke()) != '\r' &&
         c != '\n' &&
         c != ' ' &&
         !ABORTED(c)) {
      if (DefaultKeyBinding(c) == &f_namedcmd) {
          unkeystroke(c);
          break;
      }
    }
    kbd_erase_to_end(0);
    reading_msg_line = FALSE;
}

/* ARGSUSED */
int
respawn(int f, int n GCC_UNUSED)
{
    return spawn1(TRUE, !f);
}

/* ARGSUSED */
int
spawn(int f, int n GCC_UNUSED)
{
    return spawn1(FALSE, !f);
}

#define COMMON_SH_PROMPT (SYS_UNIX || SYS_VMS || SYS_MSDOS || SYS_OS2 || SYS_WINNT)

#if COMMON_SH_PROMPT
/*
 * Common function for prompting for shell/pipe command, and for recording the
 * last shell/pipe command so that we can support "!!" convention.
 *
 * Note that for 'capturecmd()', we must retain a leading "!".
 */
static int
ShellPrompt(TBUFF ** holds,
          char *result,
          int rerun)          /* TRUE/FALSE: spawn, -TRUE: capturecmd */
{
    register int s;
    register size_t len;
    static const char bang[] = SHPIPE_LEFT;
    BUFFER *bp;
    int cb = any_changed_buf(&bp), fix = (rerun != -TRUE);
    char save[NLINE], temp[NLINE], line[NLINE + 1];

    if ((len = tb_length(*holds)) != 0) {
      (void) strncpy(save, tb_values(*holds), len);
    }
    save[len] = EOS;

    /* if it doesn't start with '!', or if that's all it is */
    if (!isShellOrPipe(save) || save[1] == EOS)
      (void) strcpy(save, bang);

    (void) strcpy(line, save);
    if (rerun != TRUE) {
      if (cb != 0) {
          if (cb > 1) {
            (void) lsprintf(temp,
                        "Warning: %d modified buffers: %s",
                        cb, bang);
          } else {
            (void) lsprintf(temp,
                        "Warning: buffer \"%s\" is modified: %s",
                        bp->b_bname, bang);
          }
      } else {
          (void) lsprintf(temp, "%s%s",
                      rerun == -TRUE ? "" : ": ", bang);
      }

      if ((s = mlreply_no_bs(temp, line + 1, NLINE - 1)) != TRUE)
          return s;
    }
    if (line[1] == EOS)
      return FALSE;

    *holds = tb_scopy(holds, line);
    (void) strcpy(result, line + fix);
    return TRUE;
}
#endif

/*
 * Run a one-liner in a subjob. When the command returns, wait for a single
 * character to be typed, then mark the screen as garbage so a full repaint is
 * done.
 */
/* the #ifdefs have been totally separated, for readability */
static int
spawn1(int rerun, int pressret)
{
#if DISP_IBMPC
    int closed;
#endif
#if COMMON_SH_PROMPT
    register int s;
    char line[NLINE];         /* command line send to shell */

    if ((s = ShellPrompt(&tb_save_shell[0], line, rerun)) != TRUE)
      return s;
#endif /* COMMON_SH_PROMPT */

    /* take care of autowrite */
    if (writeall(FALSE, 1, FALSE, TRUE, TRUE, FALSE) != TRUE)
      return FALSE;

#if SYS_UNIX
#if DISP_X11
#if defined(HAVE_WAITPID) && !SMALLER
    (void) x_window_SHELL(line);
#else
    (void) system_SHELL(line);
#endif
#else
    ttclean(TRUE);
    file_stat(0, 0);

    (void) system_SHELL(line);

    ttunclean();
    if (pressret)
      pressreturn();
    term.open();
    term.kopen();
    term.flush();
    sgarbf = TRUE;
#endif /* DISP_X11 */
    return AfterShell();
#endif /* SYS_UNIX */

#if   SYS_VMS
    kbd_flush();
    s = vms_system(line);     /* Run the command.     */
    if (pressret) {
      term.putch('\r');
      term.putch('\n');
      term.flush();
      pressreturn();
    }
    sgarbf = TRUE;
    return (s);
#endif
#if   SYS_MSDOS || SYS_OS2 || SYS_WINNT
    kbd_erase_to_end(0);
    kbd_flush();
    term.kclose();
#if   DISP_IBMPC
    /* If we don't reset to 80x25, parts of the shell-output will go
     * astray.
     */
    closed = term.cols != 80 || term.rows != 25;
    if (closed)
      term.close();
#endif
#if SYS_WINNT
# if DISP_NTWIN
    w32_system_winvile(line, &pressret);
# else
    if (W32_SKIP_SHELL(line))
      pressret = FALSE;
    w32_system(line);
# endif
    w32_keybrd_reopen(pressret);
#else
    system(line);
    term.kopen();
    /* wait for return here if we are interactive */
    if (pressret) {
      pressreturn();
    }
#endif
#if   DISP_IBMPC
    /* Reopen the display _after_ the prompt, to keep the shell-output
     * in the same type of screen as the prompt.
     */
    if (closed)
      term.open();
#endif
#if DISP_NTCONS
    ntcons_reopen();
#endif
    sgarbf = TRUE;
    return AfterShell();
#endif
}

/*
 * Pipe a one line command into a window
 */
/* ARGSUSED */
int
capturecmd(int f, int n)
{
    int s;
#if SYS_UNIX || SYS_MSDOS || SYS_VMS || SYS_OS2 || SYS_WINNT
    int allocd_storage;
    BUFFER *bp;               /* pointer to buffer to zot */
    char line[NLINE];         /* command line send to shell */
    char *final_cmd;          /* possibly edited command line */

    /* get the command to pipe in */
    hst_init('!');
    s = ShellPrompt(&tb_save_shell[!global_g_val(GMDSAMEBANGS)], line, -TRUE);
    hst_flush();

    /* prompt ok? */
    if (s != TRUE)
      return s;

#if OPT_FINDPATH
    if ((final_cmd = ck_find_cmd(line, &allocd_storage, TRUE)) == NULL)
      return (FALSE);
#else
    allocd_storage = FALSE;
    final_cmd = line;
#endif

    if ((s = writeall(f, n, FALSE, FALSE, TRUE, FALSE)) == TRUE) {
      if ((s = ((bp = bfind(OUTPUT_BufName, 0)) != NULL)) == TRUE) {
          W_VALUES *save_wvals = save_window_modes(bp);

          if ((s = popupbuff(bp)) == TRUE) {
            ch_fname(bp, final_cmd);
            bp->b_active = FALSE;   /* force a re-read */
            if ((s = swbuffer_lfl(bp, FALSE, FALSE)) == TRUE)
                set_rdonly(bp, line, MDVIEW);
          }
          restore_window_modes(bp, save_wvals);
      }
    }
#if OPT_FINDPATH
    if (allocd_storage)
      (void) free(final_cmd);
#endif

#else /* ! SYS_UNIX */

    WINDOW *wp;               /* pointer to new window */
    BUFFER *bp;               /* pointer to buffer to zot */
    static char oline[NLINE]; /* command line send to shell */
    char line[NLINE];         /* command line send to shell */
    WINDOW *ocurwp;           /* save the current window during delete */

    static char filnam[NSTRING] = "command";

    /* get the command to pipe in */
    if ((s = mlreply("cmd: <", oline, NLINE)) != TRUE)
      return (s);

    (void) strcpy(line, oline);

    /* get rid of the command output buffer if it exists */
    if ((bp = find_b_name(OUTPUT_BufName)) != NULL) {
      /* try to make sure we are off screen */
      ocurwp = NULL;
      for_each_window(wp) {
          if (wp->w_bufp == bp) {
            if (curwp != wp) {
                ocurwp = curwp;
                curwp = wp;
            }
            delwind(FALSE, 1);
            if (ocurwp != NULL)
                curwp = ocurwp;
            break;
          }
      }
      if (zotbuf(bp) != TRUE)
          return (FALSE);
    }

    if (s != TRUE)
      return (s);

    /* split the current window to make room for the command output */
    if ((s = splitwind(FALSE, 1)) != TRUE)
      return (s);

    /* and read the stuff in */
    if ((s = getfile(filnam, FALSE)) != TRUE)
      return (s);

    /* overwrite its buffer name for consistency */
    set_bname(curbp, OUTPUT_BufName);

    /* make this window in VIEW mode, update buffer's mode lines */
    make_local_b_val(curwp->w_bufp, MDVIEW);
    set_b_val(curwp->w_bufp, MDVIEW, TRUE);
    curwp->w_flag |= WFMODE;

#if OPT_FINDERR
    set_febuff(OUTPUT_BufName);
#endif

    /* and get rid of the temporary file */
    unlink(filnam);
    s = AfterShell();
#endif /* SYS_UNIX */
    return (s);
}

#if SYS_UNIX || SYS_MSDOS || (SYS_OS2 && CC_CSETPP) || SYS_WINNT
/*
 * write_kreg_to_pipe() exists to facilitate execution of a Win32 thread.
 * All other host operating systems are simply victims.
 */
static void
write_kreg_to_pipe(void *writefp)
{
    FILE *fw;
    KILL *kp;                 /* pointer into kill register */

    fw = (FILE *) writefp;
    kregcirculate(FALSE);
    kp = kbs[ukb].kbufh;
    while (kp != NULL) {
      fwrite((char *) kp->d_chunk, 1, (size_t) KbSize(ukb, kp), fw);
      kp = kp->d_next;
    }
#if SYS_UNIX && ! TEST_DOS_PIPES
    (void) fflush(fw);
    (void) fclose(fw);
    ExitProgram(GOODEXIT);
    /* NOTREACHED */
#else
# if SYS_WINNT
    /*
     * If this function is invoked by a thread, then that thread (not
     * the parent process) must close write pipe.  We generalize this
     * function so that all Win32 execution environments (threaded or
     * not) use the same code.
     */
    (void) fflush(fw);
    (void) fclose(fw);
    if (!global_g_val(GMDW32PIPES))
      npflush();
# else
    npflush();                /* fake multi-processing */
# endif
#endif
}

#if OPT_SELECTIONS
/*
 * A corresponding function to write the current region from DOT to MK to a
 * pipe.
 */
static void
write_region_to_pipe(void *writefp)
{
    FILE *fw = (FILE *) writefp;
    LINEPTR last = setup_region();
    LINEPTR lp = DOT.l;

    while (lp != last) {
      fwrite((char *) lp->l_text, sizeof(char), (size_t) llength(lp), fw);
      putc('\n', fw);
      lp = lforw(lp);
    }
#if SYS_UNIX && ! TEST_DOS_PIPES
    (void) fflush(fw);
    (void) fclose(fw);
    ExitProgram(GOODEXIT);
    /* NOTREACHED */
#else
# if SYS_WINNT
    /*
     * If this function is invoked by a thread, then that thread (not
     * the parent process) must close write pipe.  We generalize this
     * function so that all Win32 execution environments (threaded or
     * not) use the same code.
     */
    (void) fflush(fw);
    (void) fclose(fw);
    if (!global_g_val(GMDW32PIPES))
      npflush();
# else
    npflush();                /* fake multi-processing */
# endif
#endif
}
#endif
#endif /* OPT_SELECTIONS */

/*
 * FUNCTION
 *   filterregion(void)
 *
 * DESCRIPTION
 *   Run a region through an external filter, replace region with the
 *   filter's output.
 *
 *   Architecturally, filterregion() is designed like so:
 *
 *       ------------  write pipe    ------------
 *       | vile     |--------------->| a filter |
 *       |          |                |          |
 *       |          |<---------------| ex:  fmt |
 *       ------------  read pipe     ------------
 *
 *   The idea here is to exec a filter (say, fmt) and then SIMULTANEOUSLY
 *   pump a potentially big wad of data down the write pipe while, AT THE
 *   SAME TIME, reading the filter's output.
 *
 *   The words in caps are the key, as they illustrate a need for separate
 *   vile processes:  a writer and a reader.  This is accomplished on a
 *   Unix host via the function softfork(), which calls fork().
 *
 *   For all other OSes, softfork() is a stub, which means that the
 *   entire filter operation runs single threaded.  This is a problem.
 *   Consider the following scenario on a host that doesn't support fork():
 *
 *   1) vile's !<region> command is used to exec vile-c-filt
 *   2) vile begins pushing a large <region> (say, 100 KB) down the
 *      write pipe
 *   3) the filter responds by pushing back an even larger response
 *   4) since vile hasn't finished the write pipe operation, the editor
 *      does not read any data from the read pipe.
 *   5) eventually, vile's read pipe buffer limit is reached and the
 *      filter blocks.
 *   6) when the filter blocks, it can't read data from vile and so the
 *      editor's write pipe buffer limit will trip as well, blocking vile.
 *
 *   That's process deadlock.
 *
 *   One workaround on a non-Unix host is the use of temp files, which
 *   uses this algorithm:
 *
 *      vile writes region to temp file,
 *      vile execs filter with stdin connected to temp file,
 *      vile reads filter's response.
 *
 *   This mechanism can be effected without error by a single process.
 *
 *   An alternative workaround simply creates two threads that simulate the
 *   effects of fork().  The first thread fills the write pipe, while the
 *   second thread consumes the read pipe.
 *
 * RETURNS
 *   Boolean, T -> all is well, F -> failure
 */

int
filterregion(void)
{
/* FIXME work on this for OS2, need inout_popen support, or named pipe? */
#if SYS_UNIX || SYS_MSDOS || (SYS_OS2 && CC_CSETPP) || SYS_WINNT
    static char oline[NLINE]; /* command line send to shell */
    char line[NLINE];         /* command line send to shell */
    FILE *fr, *fw;
    int s;

    /* get the filter name and its args */
    if ((s = mlreply_no_bs("!", oline, NLINE)) != TRUE)
      return (s);
    (void) strcpy(line, oline);
    if ((s = inout_popen(&fr, &fw, line)) != TRUE) {
      mlforce("[Couldn't open pipe or command]");
      return s;
    }

    if ((s = begin_kill()) != TRUE)
      return s;

    if (!softfork()) {
#if !(SYS_WINNT && defined(GMDW32PIPES))
      write_kreg_to_pipe(fw);
#else
      /* This is a Win32 environment with compiled Win32 pipe support. */
      if (global_g_val(GMDW32PIPES)) {
          /*
           * w32pipes mode enabled -- create child thread to blast
           * region to write pipe.
           */
          if (_beginthread(write_kreg_to_pipe, 0, fw) == (unsigned long) -1) {
            mlforce("[Can't create Win32 write pipe]");
            (void) fclose(fw);
            (void) npclose(fr);
            return (FALSE);
          }
      } else {
          /*
           * Single-threaded parent process writes region to pseudo
           * write pipe (temp file).
           */
          write_kreg_to_pipe(fw);
      }
#endif
    }
#if ! ((SYS_OS2 && CC_CSETPP) || SYS_WINNT)
    (void) fclose(fw);
#endif
    DOT.l = lback(DOT.l);
    s = ifile((char *) 0, TRUE, fr);
    npclose(fr);
    (void) firstnonwhite(FALSE, 1);
    (void) setmark();
    end_kill();
    return s;
#else
    mlforce("[Region filtering not available -- try buffer filtering]");
    return FALSE;
#endif
}

#if OPT_SELECTIONS
/*
 * Like filterregion, but opens a stream reading from a filter's output when
 * we will not modify the current buffer (e.g., syntax highlighting).
 */
int
open_region_filter(void)
{
/* FIXME work on this for OS2, need inout_popen support, or named pipe? */
#if SYS_UNIX || SYS_MSDOS || (SYS_OS2 && CC_CSETPP) || SYS_WINNT
    static char oline[NLINE]; /* command line send to shell */
    char line[NLINE];         /* command line send to shell */
    FILE *fr, *fw;
    int s;

    /* get the filter name and its args */
    if ((s = mlreply_no_bs("!", oline, NLINE)) != TRUE)
      return (s);
    (void) strcpy(line, oline);
    if ((s = inout_popen(&fr, &fw, line)) != TRUE) {
      mlforce("[Couldn't open pipe or command]");
      return s;
    }

    if (!softfork()) {
#if !(SYS_WINNT && defined(GMDW32PIPES))
      write_region_to_pipe(fw);
#else
      /* This is a Win32 environment with compiled Win32 pipe support. */
      if (global_g_val(GMDW32PIPES)) {
          /*
           * w32pipes mode enabled -- create child thread to blast
           * region to write pipe.
           */
          if (_beginthread(write_region_to_pipe, 0, fw) == (unsigned long) -1) {
            mlforce("[Can't create Win32 write pipe]");
            (void) fclose(fw);
            (void) npclose(fr);
            return (FALSE);
          }
      } else {
          /*
           * Single-threaded parent process writes region to pseudo
           * write pipe (temp file).
           */
          write_region_to_pipe(fw);
      }
#endif
    }
#if ! ((SYS_OS2 && CC_CSETPP) || SYS_WINNT)
    (void) fclose(fw);
#endif
    ffp = fr;
    count_fline = 0;
    return s;
#else
    mlforce("[Region filtering not available -- try buffer filtering]");
    return FALSE;
#endif
}
#endif /* OPT_SELECTIONS */

/*
 * filter a buffer through an external DOS program
 * this is obsolete, the filterregion code is better.
 */
/* ARGSUSED */
int
vile_filter(int f GCC_UNUSED, int n GCC_UNUSED)
{
#if !(SYS_UNIX||SYS_MSDOS || (SYS_OS2 && CC_CSETPP))  /* filterregion up above is better */
    register int s;           /* return status from CLI */
    register BUFFER *bp;      /* pointer to buffer to zot */
    static char oline[NLINE]; /* command line send to shell */
    char line[NLINE];         /* command line send to shell */
    char tnam[NFILEN];        /* place to store real file name */
    static char bname1[] = "fltinp";
#if   SYS_UNIX
    char *t;
#endif

    static char filnam1[] = "fltinp";
    static char filnam2[] = "fltout";

#if   SYS_VMS
    mlforce("[Not available under VMS]");
    return (FALSE);
#endif
    /* get the filter name and its args */
    if ((s = mlreply("cmd: |", oline, NLINE)) != TRUE)
      return (s);
    (void) strcpy(line, oline);

    /* setup the proper file names */
    bp = curbp;
    (void) strcpy(tnam, bp->b_fname);     /* save the original name */
    ch_fname(bp, bname1);     /* set it to our new one */

    /* write it out, checking for errors */
    if (writeout(filnam1, curbp, TRUE, TRUE) != TRUE) {
      mlforce("[Cannot write filter file]");
      ch_fname(bp, tnam);
      return (FALSE);
    }
#if   SYS_MSDOS || SYS_OS2 || SYS_WINNT
    (void) strcat(line, " <fltinp >fltout");
    bottomleft();
    term.kclose();
    system(line);
    term.kopen();
    sgarbf = TRUE;
    s = TRUE;
#endif
#if   SYS_UNIX
    bottomleft();
    ttclean(TRUE);
    file_stat(0, 0);
    if ((t = strchr(line, '|')) != 0) {
      char temp[NLINE];
      (void) strcpy(temp, t);
      (void) strcat(strcpy(t, " <fltinp"), temp);
    } else {
      (void) strcat(line, " <fltinp");
    }
    (void) strcat(line, " >fltout");
    system(line);
    ttunclean();
    term.flush();
    sgarbf = TRUE;
    s = TRUE;
#endif

    /* on failure, escape gracefully */
    if (s != TRUE || (readin(filnam2, FALSE, curbp, TRUE) == FALSE)) {
      mlforce("[Execution failed]");
      ch_fname(bp, tnam);
      unlink(filnam1);
      unlink(filnam2);
      return (s);
    }

    ch_fname(bp, tnam);       /* restore name */

    b_set_changed(bp);        /* flag it as changed */
    nounmodifiable(bp);       /* and it can never be "un-changed" */

    /* and get rid of the temporary file */
    unlink(filnam1);
    unlink(filnam2);
    return AfterShell();
#else
    mlforce("[Buffer filtering not available -- use filter operator]");
    return FALSE;
#endif
}

#if   SYS_VMS
/*
 * Run a command. The "cmd" is a pointer to a command string, or NULL if you
 * want to run a copy of DCL in the subjob (this is how the standard routine
 * lib$spawn works. You have to do weird stuff with the terminal on the way in
 * and the way out, because DCL does not want the channel to be in raw mode.
 */
int
vms_system(register char *cmd)
{
    struct dsc$descriptor cdsc;
    struct dsc$descriptor *cdscp;
    long status;
    long substatus;
    long iosb[2];

    status = sys$qiow(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
                  oldmode, sizeof(oldmode), 0, 0, 0, 0);
    if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
      return (FALSE);
    cdscp = NULL;       /* Assume DCL.          */
    if (cmd != NULL) {        /* Build descriptor.    */
      cdsc.dsc$a_pointer = cmd;
      cdsc.dsc$w_length = strlen(cmd);
      cdsc.dsc$b_dtype = DSC$K_DTYPE_T;
      cdsc.dsc$b_class = DSC$K_CLASS_S;
      cdscp = &cdsc;
    }
    status = lib$spawn(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0);
    if (status != SS$_NORMAL)
      substatus = status;
    status = sys$qiow(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
                  newmode, sizeof(newmode), 0, 0, 0, 0);
    if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
      return (FALSE);
    if ((substatus & STS$M_SUCCESS) == 0) /* Command failed.      */
      return (FALSE);
    return AfterShell();
}
#endif

#ifdef HAVE_PUTENV
int
set_envvar(int f GCC_UNUSED, int n GCC_UNUSED)
{
    static TBUFF *var, *val;
    char *both;
    int rc;

    if ((rc = mlreply2("Environment variable: ", &var)) == ABORT)
      return (rc);

    if ((rc = mlreply2("Value: ", &val)) == ABORT)
      return (rc);

    both = typeallocn(char, tb_length(var) + tb_length(val));

    lsprintf(both, "%s=%s", tb_values(var), tb_values(val));

    putenv(both);       /* this will leak.  i think it has to. */

    return TRUE;
}
#endif

#if OPT_FINDPATH

/*
 * FUNCTION
 *   parse_findcfg_mode(FINDCFG *pcfg, char *inputstr)
 *
 *   pcfg     - returned by reference -- what was found in inputstr
 *
 *   inputstr - what to parse
 *
 * DESCRIPTION
 *   find-cfg mode is a string that supports this syntax:
 *
 *      "[<recursive_token>][,<nonrecursive_token>[,<option>...]]"
 *
 *   where:
 *     <recursive_token>    := an ascii char that triggers a recursive find,
 *                             may not be taken from the character set defined
 *                             by isalpha().  To use ',' as a token, escape it
 *                             with '\'.
 *     <nonrecursive_token> := an ascii char that triggers a nonrecursive find,
 *                             may not be taken from the character set defined
 *                             by isalpha().  To use ',' as a token, escape it
 *                             with '\'.
 *
 *     <option>             := <dirs_only>|<follow>
 *
 *     <dirs_only>          := d
 *
 *     <follow>             := f
 *
 *   Example usage:
 *     se find-cfg="$,@"  ; '$' -> recursive find, '@' -> nonrecursive find
 *     se find-cfg="$,,d" ; '$' -> recursive find, the find operation should
 *                        ; only look for directories.
 *
 *   Note that an empty string disables find-cfg mode.
 *
 * RETURNS
 *   Boolean, T -> all is well
 */
int
parse_findcfg_mode(FINDCFG * pcfg, char *inputstr)
{
    char *cp;
    int i, rc = TRUE;

    memset(pcfg, 0, sizeof(*pcfg));
    pcfg->disabled = TRUE;    /* an assumption */
    cp = mktrimmed(inputstr);
    if (*cp == EOS)
      return (rc);

    /* handle first 2 tokens */
    for (i = 0; i < 2 && *cp; i++) {
      if (*cp != ',') {
          if (isAlpha(*cp)) {
            mlforce("[alphanumeric tokens not allowed]");
            return (FALSE);
          }
          if (*cp == '\\' && cp[1] == ',')
            cp++;       /* skip escape char */
          if (i == 0)
            pcfg->recur_token = *cp++;
          else
            pcfg->nonrecur_token = *cp++;
          cp = skip_blanks(cp);
          if (*cp) {
            if (*cp != ',') {
                mlforce("[invalid find-cfg syntax]");
                return (FALSE);
            }
          } else {
            pcfg->disabled = (!rc);
            return (rc);      /* end of string, all done */
          }
      }
      cp++;             /* skip token delimiter */
      cp = skip_blanks(cp);
      if (*cp == EOS) {
          mlforce("[invalid find-cfg syntax]");
          return (FALSE);
      }
    }

    /* options? */
    if (*cp) {
      while (*cp) {
          if (*cp == 'd')
            pcfg->dirs_only = TRUE;
          else if (*cp == 'f')
            pcfg->follow = TRUE;
          else {
            mlforce("[invalid find-cfg syntax]");
            rc = FALSE;
            break;
          }
          cp++;
      }
    }
    pcfg->disabled = (!rc);
    return (rc);
}

/*
 * Cruise through the user's shell command, stripping out unquoted tokens
 * that include wildcard characters.   Save each token in a vector.  Return
 * the remains of the shell command to the caller (or NULL if an error occurs).
 */
static char *
extract_wildcards(char *cmd, char ***vec, size_t *vecidx, const char *fnname)
{
    char **base, *cp, *anchor, buf[NFILEN * 2];
    int delim;
    size_t idx, len;

    idx = 0;
    len = 32;
    cp = cmd;
    base = castalloc(char *, len * sizeof(char *));
    if (base == NULL) {
      (void) no_memory(fnname);
      return (NULL);
    }
    while (*cp) {
      cp = anchor = skip_blanks(cp);
      if (*cp == '\'' || *cp == '"') {
          delim = *cp++;
          while (*cp) {
            if (*cp == '\\' && cp[1] == delim)
                cp += 2;
            else if (*cp == delim) {
                cp++;
                break;
            } else
                cp++;
          }
      } else {
          cp++;
          while (*cp && (!isSpace(*cp)))
            cp++;
          len = cp - anchor;
          strncpy(buf, anchor, len);
          buf[len] = EOS;
          if (string_has_wildcards(buf)) {
            memset(anchor, ' ', len);     /* blank out wildcard in cmd */
            if (idx >= len) {
                len *= 2;
                base = castrealloc(char *, base, sizeof(*base));
                if (base == NULL) {
                  (void) no_memory(fnname);
                  return (NULL);
                }
            }
            base[idx] = castalloc(char, len + 1);
            if (base[idx] == NULL) {
                (void) no_memory(fnname);
                return (NULL);
            }
            strcpy(base[idx++], buf);
          }
      }
    }

    /* all done */
    *vec = base;
    *vecidx = idx;
    return (cmd);
}

static void
free_vector(char ***vec, size_t vec_elements)
{
    char **base;
    ULONG i;

    base = *vec;
    for (i = 0; i < vec_elements; i++, base++)
      (void) free(*base);
    (void) free(*vec);
}

static char *
determine_quoted_delimiter(void)
{
    char *qdelim;

#if SYS_UNIX
    qdelim = "'";
#else
    {
      char *cp, buf[NFILEN];
      int unix_shell, shell_len;

      strcpy(buf, get_shell());
      cp = strrchr(buf, '.');
      if (cp)
          *cp = EOS;          /* trim file suffix */
      shell_len = strlen(buf);
      unix_shell = (shell_len >= 2 &&
                  toLower(buf[shell_len - 2]) == 's' &&
                  toLower(buf[shell_len - 1]) == 'h');
      if (unix_shell)
          qdelim = "'";
      else {
          /*
           * Assume a DOS-based shell, which generally honors double
           * quotes as argument delimiters (not single quotes).
           */

          qdelim = "\"";
      }
    }
#endif
    return (qdelim);
}

char *
last_findcmd(void)
{
    return (prev_findcmd);
}

/*
 * Add a string to the end of a cmd string, lengthening same if necessary.
 * Does not terminate cmd string.
 */
static int
add_token_to_cmd(char **cmd,
             size_t *cmdidx,
             size_t *cmdlen,
             char *token,
             const char *funcname)
{
    int rc = TRUE;
    size_t toklen = strlen(token);

    if (*cmdidx + toklen + 2 > *cmdlen) {
      char *tmp = *cmd;

      *cmdlen *= 2;
      tmp = castrealloc(char, tmp, *cmdlen);
      if (tmp == NULL) {
          (void) free(*cmd);
          rc = no_memory(funcname);
          return (rc);
      }
      *cmd = tmp;
    }
    strcpy(*cmd + *cmdidx, token);
    *cmdidx += toklen;
    (*cmd)[*cmdidx] = ' ';
    (*cmdidx)++;
    return (rc);
}

/*
 * FUNCTION
 *   ck_find_cmd(char *cmd, int *allocd_storage, int prepend_bang)
 *
 *   cmd            - user's original shell command.
 *
 *   allocd_storage - by ref, T -> the returned cmd string was allocated
 *                    on the heap.  Caller must free.
 *
 *   prepend_bang   - Boolean, T -> that if a modified shell command is
 *                    created, prepend it with '!'.
 *
 * DESCRIPTION
 *   If the user has enabled find-cfg mode and if the user's shell
 *   command contains a token that indicates that a find operation should
 *   be initiated, modify the user's shell command appropriately.
 *
 *   Example:
 *     :setv $findpath="."
 *     :se find-cfg="$"
 *     ^X-!$egrep -n FIXME *.[ch]
 *
 *   Resultant shell command on a Unix host (somewhat simplified):
 *     !find . -name '*.[ch]' -print | xargs egrep -n FIXME
 *
 *   Note that this example does not include the syntax used to filter out
 *   CVS/RCS directories and tags files.
 *
 * RETURNS
 *   Pointer to original user cmd if no find operation required, a newly
 *   synthesized cmd string, or NULL (failure case).
 */
static char *
ck_find_cmd(char *cmd, int *allocd_storage, int prepend_bang)
{
    char *cp, *cmdcopy;
    FINDINFO info;

    *allocd_storage = FALSE;
    if (!parse_findcfg_mode(&info.cfg, global_g_val_ptr(GVAL_FINDCFG))) {
      return (NULL);          /* bogus find-cfg value noted */
    }
    if (info.cfg.disabled)
      return (cmd);           /* find-cfg mode disabled */

    /*
     * don't munge vile's copy of the user's command line -- scrogs the
     * [Output] buffer name.
     */
    if ((cmdcopy = strmalloc(cmd)) == NULL) {
      (void) no_memory("ck_find_cmd");
      return (NULL);
    }
    cp = skip_blanks(cmdcopy);
    if (*cp == SHPIPE_LEFT[0])
      cp++;
    cp = skip_blanks(cp);
    if (*cp) {
      info.nonrecursive = (*cp == info.cfg.nonrecur_token);

      /* if specifying recursive or nonrecursive find syntax ... */
      if (info.nonrecursive || *cp == info.cfg.recur_token) {
          /*
           * user wants [non]recursive find syntax added to shell
           * command.
           */

          info.dir_list = get_findpath();
          if (info.dir_list[0] == EOS)
            info.dir_list = ".";
          if (info.cfg.dirs_only)
            cmd = find_dirs_only(cp + 1, &info, prepend_bang);
          else
            cmd = find_all_files(cp + 1, &info, prepend_bang);
          if (cmd) {
            /* keep a record of the cmd that's about to be spawned */

            if (prev_findcmd)
                (void) free(prev_findcmd);
            if ((prev_findcmd = strmalloc(cmd)) == NULL) {
                (void) free(cmd);
                cmd = NULL;
                no_memory("ck_find_cmd");
            } else {
                *allocd_storage = TRUE;
            }
          }
      }
    }
    (void) free(cmdcopy);
    return (cmd);
}

/*
 * FUNCTION
 *   find_dirs_only(char *cmd, FINDINFO *pinfo, int prepend_bang)
 *
 *   cmd          - user's input command, stripped of leading '!' and
 *                  find-cfg tokens.
 *
 *   pinfo        - contains info about the user's various "find" parameters.
 *
 *   prepend_bang - Boolean, T -> prepend '!' to beginning of shell cmd 
 *                  synthesized by this routine.
 *
 * DESCRIPTION
 *   The task:
 *
 *   - construct a shell command that looks like so:
 *
 *     [!]find <$findpath_dir_list> -type d -print | \
 *          egrep -v <RE that elides CVS & RCS dirs> | xargs cmdstr
 *
 *   - if executing on a host that supports case insensitive file names, 
 *     modify the above shell command to include option modifiers that as
 *     required.
 *
 *   - if a nonrecursive find is requested, add an appropriate option
 *     (i.e., -maxdepth 1) to the find cmdline (again, this syntax used may
 *     only be supported by the GNU version of find).
 *
 * RETURNS
 *   Pointer to newly formulated shell command (allocated on the heap) or
 *   NULL (failure).
 */
static char *
find_dirs_only(char *cmd, FINDINFO * pinfo, int prepend_bang)
{
    size_t i, outidx, outlen;
    const char *path, *fnname;
    char *rslt, *qdelim, buf[512];

    fnname = "find_dirs_only";
    outlen = 512;
    outidx = 0;
    rslt = castalloc(char, outlen);
    if (!rslt) {
      (void) no_memory(fnname);
      return (NULL);
    }
    if (prepend_bang) {
      *rslt = SHPIPE_LEFT[0];
      rslt[1] = EOS;
      outidx = 1;
    } else
      *rslt = '\0';
    strcat(rslt, "find ");
    outidx += sizeof("find ") - 1;
    path = pinfo->dir_list;

    /* add directory list to find command */
    while ((path = parse_pathlist(path, buf)) != NULL) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, buf, fnname))
          return (NULL);
    }

    /* worry about nonrecursive find */
    if (pinfo->nonrecursive) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-maxdepth 1", fnname))
          return (NULL);
    }

    /* follow symbolic links? */
    if (pinfo->cfg.follow) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-follow", fnname))
          return (NULL);
    }

    /* terminate find string with "-type d -print" */
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-type d -print", fnname))
      return (NULL);

    qdelim = determine_quoted_delimiter();

    /*
     * filter out RCS/CVS directories and tags files.  we make the 
     * assumption that the user's "find" creates filenames with
     * '/' as a path delimiter.
     *
     * ========================== FIXME ==================================
     * Note that this regular expression is simplistic and could be better
     * if it ensured that "RCS/CVS" are preceded by '/' or nothing.
     * ========================== FIXME ==================================
     */
    sprintf(buf,
          "| egrep -v%s %s(RCS|CVS)/%s",
#if SYS_UNIX
          "",
#else
          "i",
#endif
          qdelim,
          qdelim);
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, buf, fnname))
      return (NULL);

    /* finish off with xargs */
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, "| xargs", fnname))
      return (NULL);
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, cmd, fnname))
      rslt = NULL;
    else {
      rslt[outidx] = EOS;     /* terminate cmd string */
      if (outidx != 0) {
          char *cp;

          i = --outidx;
          cp = rslt + outidx;
          while (isSpace(*cp) && i != 0) {
            cp--;
            i--;
          }
          if (cp != rslt + outidx) {
            /* white space found at end of string, trim it. */

            cp[1] = EOS;
          }
      }
    }
    return (rslt);
}

/*
 * FUNCTION
 *   find_all_files(char *cmd, FINDINFO *pinfo, int prepend_bang)
 *
 *   cmd          - user's input command, stripped of leading '!' and
 *                  find-cfg tokens.
 *
 *   pinfo        - contains info about the user's various "find" parameters.
 *
 *   prepend_bang - Boolean, T -> prepend '!' to beginning of shell cmd 
 *                  synthesized by this routine.
 *
 * DESCRIPTION
 *   The task:
 *
 *   - scan all unquoted tokens in the user's command line.
 *
 *   - remove each unqoted token that includes shell wildcard chars.  Call the
 *     remainder of the user's input the "cmdstr".  Call the removed tokens
 *     the "wildvec".
 *
 *   - construct a shell command that looks like so:
 *
 *     [!]find <$findpath_dir_list> '(' -name wildvec[0] -o \
 *             -name wildvec[1] ... -o -name wildvec[n-1] ')' -print | \
 *          egrep -v <RE that elides CVS & RCS dirs and tags files> | \
 *          xargs cmdstr
 *
 *   - if executing on a host that supports case insensitive file names, 
 *     modify the above shell command to include option modifiers that as
 *     required.  Note that the find options used in this case (i.e., -iname)
 *     may only be supported by the GNU version of find. 
 *
 *   - if a nonrecursive find is requested, add an appropriate option
 *     (i.e., -maxdepth 1) to the find cmdline (again, this syntax used may
 *     only be supported by the GNU version of find).
 *
 * RETURNS
 *   Pointer to newly formulated shell command (allocated on the heap) or
 *   NULL (failure).
 */
static char *
find_all_files(char *cmd, FINDINFO * pinfo, int prepend_bang)
{
    size_t i, outidx, outlen, vecidx;
    const char *path, *fnname;
    char *xargstr, **vec, *rslt, *qdelim, buf[512];

    fnname = "find_all_files";
    if ((xargstr = extract_wildcards(cmd, &vec, &vecidx, fnname)) == NULL)
      return (NULL);
    if (vecidx == 0) {
      /* No wild cards were found on the command line.  No sense 
       * continuing.  Why?  With no wild cards, the find command 
       * will search for all files by default, this may or may not
       * be what the user wants.  It's certainly not what the user
       * wants if s/he types this by mistake:
       *
       *    !<token>egrep -n FIXME 8.c
       *
       * which is an obvious slip of the fingers on the keyboard.
       * If the user really wants to examine all files, s/he can type:
       *
       *    !<token>egrep -n FIXME *
       */

      free_vector(&vec, vecidx);
      mlforce("[unless \"d\" option is set, shell command must include at least one wildcard]");
      return (NULL);
    }
    outlen = 512;
    outidx = 0;
    rslt = castalloc(char, outlen);
    if (!rslt) {
      free_vector(&vec, vecidx);
      (void) no_memory(fnname);
      return (NULL);
    }
    if (prepend_bang) {
      *rslt = SHPIPE_LEFT[0];
      rslt[1] = EOS;
      outidx = 1;
    } else
      *rslt = '\0';
    strcat(rslt, "find ");
    outidx += sizeof("find ") - 1;
    path = pinfo->dir_list;

    /* add directory list to find command */
    while ((path = parse_pathlist(path, buf)) != NULL) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, buf, fnname)) {
          free_vector(&vec, vecidx);
          return (NULL);
      }
    }

    /* worry about nonrecursive find */
    if (pinfo->nonrecursive) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-maxdepth 1", fnname)) {
          free_vector(&vec, vecidx);
          return (NULL);
      }
    }

    /* follow symbolic links? */
    if (pinfo->cfg.follow) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-follow", fnname)) {
          free_vector(&vec, vecidx);
          return (NULL);
      }
    }

    if (vecidx > 1) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "'('", fnname)) {
          free_vector(&vec, vecidx);
          return (NULL);
      }
    }

    qdelim = determine_quoted_delimiter();

    /* add wildcards (if any) to find command */
    for (i = 0; i < vecidx; i++) {
      sprintf(buf,
            "%s-%sname %s%s%s",
            (i != 0) ? "-o " : "",
#if SYS_UNIX
            "",
#else
            "i",        /* find uses case insensitive filename search */
#endif
            qdelim,
            vec[i],
            qdelim);
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, buf, fnname)) {
          free_vector(&vec, vecidx);
          return (NULL);
      }
    }
    free_vector(&vec, vecidx);      /* don't need this anymore */

    if (vecidx > 1) {
      if (!add_token_to_cmd(&rslt, &outidx, &outlen, "')'", fnname))
          return (NULL);
    }

    /* terminate find string with "-print" (not needed for GNU find) */
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, "-print", fnname))
      return (NULL);

    /*
     * filter out RCS/CVS directories and tags files.  we make the 
     * assumption that the user's "find" creates filenames with
     * '/' as a path delimiter.
     *
     * ========================== FIXME ==================================
     * Note that this regular expression is simplistic and could be better
     * if it ensured that "RCS/CVS" are preceded by '/' or nothing.
     * ========================== FIXME ==================================
     */
    sprintf(buf,
          "| egrep -v%s %s((RCS|CVS)/|/%s$)%s",
#if SYS_UNIX
          "",
#else
          "i",
#endif
          qdelim,
#if SYS_UNIX
          "[Tt][Aa][Gg][Ss]",
#else
          "tags",
#endif
          qdelim);
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, buf, fnname))
      return (NULL);

    /* finish off with xargs */
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, "| xargs", fnname))
      return (NULL);
    if (!add_token_to_cmd(&rslt, &outidx, &outlen, xargstr, fnname))
      rslt = NULL;
    else {
      rslt[outidx] = EOS;     /* terminate cmd string */
      if (outidx != 0) {
          char *cp;

          i = --outidx;
          cp = rslt + outidx;
          while (isSpace(*cp) && i != 0) {
            cp--;
            i--;
          }
          if (cp != rslt + outidx) {
            /* white space found at end of string, trim it. */

            cp[1] = EOS;
          }
      }
    }
    return (rslt);
}

#endif /* OPT_FINDPATH */

Generated by  Doxygen 1.6.0   Back to index