Logo Search packages:      
Sourcecode: af version File versions  Download package

exec.c

/* Exec.c - Af routines to execute external programs.
   Copyright (C) 1990 - 2003 Malc Arnold.

   This program 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.

   This program 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include "af.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include STRING_HDR

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */

/****************************************************************************/
/* RCS info */

#ifndef lint
static char *RcsId = "$Id: exec.c,v 2.4 2003/10/27 23:16:20 malc Exp $";
#endif /* ! lint */

/****************************************************************************/
/* Global function declarations */

extern char *xrealloc(), *xstrdup(), *vstrcat();
extern char *getenv(), *strerror(), *get_vtext();
extern char *utos();
extern int pclose(), kill(), killpg(), wait();
extern int isatty(), strncasecmp(), get_key();
extern unsigned alarm();
extern void _exit(), free(), msg(), msgl(), emsgl();
extern void clearmsg(), typeout(), vtredraw();
extern void tclear(), init_tmodes(), end_tmodes();
extern RETSIGTYPE (*signal())();
extern FILE *popen();

/* Local function declarations */

int shellout(), close_pipe();
FILE *open_pipe();
void reset_signals();
static void init_escape(), end_escape();

/****************************************************************************/
/* Import the system error number */

extern int errno;

/****************************************************************************/
/* Import the user quit flag from commands.c */

extern int user_quit;

/****************************************************************************/
/* The number of active child processes */

static int no_of_children = 0;

/****************************************************************************/
FILE *open_pipe(cmd, mode, interactive, ofp)
char *cmd, *mode;
int interactive;
FILE *ofp;
{
      /* Open a pipe in the given mode to run the named command */

      int fds[2];
      FILE *fp = NULL;

      /* Set up the file descriptors for the pipe */

      if (pipe(fds) < 0) {
            return(NULL);
      }

      /* Reset the terminal before starting an interactive process */

      if (interactive) {
            init_escape();
      }

      /* Now we fork */

      switch (fork()) {
      case -1:
            return(NULL);
      case 0:
            /* Redirect the file descriptors */

            switch (*mode) {
            case 'r':
                  (void) close(fds[0]);
                  if (ofp != NULL) {
                        (void) dup2(fileno(ofp), 0);
                  } else {
                        (void) close(0);
                  }
                  (void) dup2(fds[1], 1);
                  (void) dup2(fds[1], 2);
                  break;
            case 'w':
                  (void) close(fds[1]);
                  (void) dup2(fds[0], 0);
                  if (ofp != NULL) {
                        (void) dup2(fileno(ofp), 1);
                        (void) dup2(fileno(ofp), 2);
                  }
                  break;
            }

            /* Set up signal handlers and process group */

            reset_signals();
            if (!interactive) {
                  (void) setpgid(getpid(), getpid());
            }

            /* Execute the command or abort */

            (void) execlp(DEFSHELL, DEFSHELL,
                        SHELLARG, cmd, NULL);
            _exit(errno);
      default:
            /* Connect a file pointer to the pipe */

            switch (*mode) {
            case 'r':
                  (void) close(fds[1]);
                  fp = fdopen(fds[0], "r");
                  break;
            case 'w':
                  (void) close(fds[0]);
                  fp = fdopen(fds[1], "w");
                  break;
            }
      }

      /* Return the file pointer */

      return(fp);
}
/****************************************************************************/
int close_pipe(fp, interactive, keypress)
FILE *fp;
int interactive, keypress;
{
      /* Close a pipe, possibly pausing for a key press */

      int status = 0;

      /* Wait for the command to terminate */

      (void) fclose(fp);
      (void) wait(&status);

      /* Clean up shell escapes after interactive processes */

      end_escape(interactive, keypress, interactive);

      /* And return the command's status */

      return(status);
}
/****************************************************************************/
int shellout(cmd, interactive, async, ifp, ofp)
char *cmd;
int interactive, async;
FILE *ifp, *ofp;
{
      /* Run a command in a subshell, or shell out if cmd is NULL */

      static char *defshell = DEFSHELL;
      char *shell;
      int status;
      RETSIGTYPE (*old_quit)(), (*old_term)();
      RETSIGTYPE (*old_tstp)();

      /* We shell out to $SHELL or use DEFSHELL */

      if (cmd != NULL || (shell = getenv(SHELL)) == NULL) {
            shell = defshell;
      }

      /* Restore the terminal modes before starting the shell */

      if (ofp == NULL) {
            init_escape();
      }

      /* Now we fork */

      switch(fork()) {
      case -1:
            /* Failed; report the reason */

            return(-1);
      case 0:
            /* Redirect stdin and stdout as required */

            if (ifp != NULL) {
                  (void) dup2(fileno(ifp), fileno(stdin));
            }
            if (ofp != NULL) {
                  (void) dup2(fileno(ofp), fileno(stdout));
                  (void) dup2(fileno(ofp), fileno(stderr));
            }

            /* Reset the signal mask and handlers */

            reset_signals();

            /* Execute the shell or shell command */

            if (cmd != NULL) {
                  (void) execlp(shell, shell, SHELLARG, cmd, NULL);
            } else {
                  (void) execlp(shell, shell, NULL);
            }

            /* Something horrible has happened */

            _exit(FATAL_RETURN);
      default:
            /* Allow SIGTSTP but ignore SIGQUIT and SIGTERM */

#ifdef HAVE_JOBCONTROL
            old_tstp = signal(SIGTSTP, SIG_DFL);
#endif /* HAVE_JOBCONTROL */
            old_quit = signal(SIGQUIT, SIG_IGN);
            old_term = signal(SIGTERM, SIG_IGN);

            /* Wait for the child or note it's existence */

            if (!async) {
                  /* Wait for the child */

                  (void) wait(&status);
            } else {
                  /* Update the child count */

                  no_of_children++;
            }

            /* Reset the SIGTSTP and SIGTERM handling */

#ifdef HAVE_JOBCONTROL
            (void) signal(SIGTSTP, old_tstp);
#endif /* HAVE_JOBCONTROL */
            (void) signal(SIGTERM, old_quit);
            (void) signal(SIGTERM, old_term);
      }

      /* Reset the terminal modes for af */

      end_escape(ofp == NULL, interactive && !RETURNFATAL(status), TRUE);

      /* Print the return value of the command? */

      if (!RETURNFATAL(status) && interactive) {
            printf("--------\nCommand %s %d; ",
                   (!RETURNSIG(status)) ? "returned" :
                   "terminated due to signal", RETURNVAL(status));
      }

      /* Handle a fatal error executing the command or shell */

      return((RETURNFATAL(status)) ? -1 :
             (RETURNVAL(status) | RETURNSIG(status)));
}
/****************************************************************************/
int wait_for_children(text)
char *text;
{
      /* Wait for all child processes to exit */

      int status, worst = 0;
      
      /* Check that there are children to wait for */

      if (!no_of_children) {
            /* Waited for no children ok */

            return(0);
      }

      /* Let the user know what's happening */

      msg(text);

      /* Loop while there are children outstanding */

      while (no_of_children > 0) {
            /* Wait for the next child */

            (void) wait(&status);

            /* Extract the worst return status */

            if (RETURNFATAL(status) ||
                RETURNVAL(status) && !RETURNVAL(worst)) {
                  worst = status;
            }

            /* That's one less to wait for */

            no_of_children--;
      }

      /* Confirm we've found all the children */

      msgl(text, " Done", NULL);

      /* Now return the worst status of the bunch */

      return((RETURNFATAL(status)) ? -1 :
             (RETURNVAL(status) | RETURNSIG(status)));
}
/****************************************************************************/
int runcmd(cmd, verbose, ifp)
char *cmd;
int verbose;
FILE *ifp;
{
      /* Run a command in a subshell displaying output via typeout */

      char buf[BUFSIZ];
      int status;
      FILE *fp;

      /* Open the pipe for reading */

      if ((fp = open_pipe(cmd, "r", FALSE, ifp)) == NULL) {
            /* Error starting the command */

            emsgl("Can't start process ", cmd,
                  ": ", strerror(errno), NULL);
            return(-1);
      }

      /* Now display each line of output to typeout */

      while (!user_quit && fgets(buf, BUFSIZ, fp) != NULL) {
            typeout(buf);
      }

      /* Close the pipe and gather status */

      status = close_pipe(fp, FALSE, FALSE);

      /* Show the return value of the command */

      if (verbose) {
            typeout("--------\nCommand ");
            typeout((!RETURNSIG(status)) ? "returned "
                  : "terminated due to signal ");
            typeout(utos((unsigned) RETURNVAL(status)));
      }

      /* End the typeout */

      typeout(NULL);

      /* And return the command's status */

      return((RETURNFATAL(status)) ? -1 :
             (RETURNVAL(status) | RETURNSIG(status)));
}
/****************************************************************************/
char *syscmd(cmd)
char *cmd;
{
      /* Run a command and return the first line of output */

      char buf[BUFSIZ];
      char *output;
      int status;
      FILE *fp;

      /* Open the pipe for reading */

      if ((fp = open_pipe(cmd, "r", FALSE, NULL)) == NULL) {
            /* Error starting the command */

            emsgl("Can't start process ", cmd,
                  ": ", strerror(errno), NULL);
            return(NULL);
      }

      /* Default the output to the empty string */

      output = xstrdup("");

      /* Now collect the output of the command */

      while (fgets(buf, BUFSIZ, fp) != NULL) {
            /* Add the line to the output */

            output = xrealloc(output, strlen(output) + strlen(buf) + 1);
            (void) strcat(output, buf);
      }

      /* Close the pipe and check the status */

      status = close_pipe(fp, FALSE, FALSE);

      /* Check for an error executing the command */

      if (!strlen(output) && (RETURNSIG(status) || RETURNVAL(status))) {
            /* Set the output to the null string */

            free(output);
            output = NULL;
      }

      /* Return the command's output */

      return(output);
}
/****************************************************************************/
#ifdef HAVE_JOBCONTROL
void pause_af()
{
      /* Put af into the background (using a SIGTSTP) */

      /* Reset the terminal modes before suspending */

      init_escape();

      /* Stop the process while not connected to a terminal */

      do {
            (void) kill(0, SIGSTOP);
      } while (!isatty(fileno(stdin)));

      /* And restore the terminal modes */

      end_escape(TRUE, FALSE, TRUE);
      return;
}
#endif /* HAVE_JOBCONTROL */
/****************************************************************************/
int edit_file(filnam)
char *filnam;
{
      /* Execute an editor on filnam */

      char *cmd, *prog, *defprog = DEFEDITOR;
      int status;

      /* What editor do we want to use? */

      if ((prog = get_vtext(V_EDITOR)) == NULL
          && (prog = getenv(VISUAL)) == NULL
          && (prog = getenv(EDITOR)) == NULL) {
            /* Give up and use the default */

            prog = defprog;
      }

      /* Build the argument list and edit the file */

      cmd = vstrcat(prog, " ", filnam, NULL);
      status = shellout(cmd, FALSE, FALSE, NULL, NULL);

      /* Clean up and return */

      free(cmd);
      return(status);
}
/****************************************************************************/
void reset_signals()
{
      /* Reset the signal handlers to their default state */

      sigset_t sigset;

      /* Clear the signal mask */

      (void) sigemptyset(&sigset);
      (void) sigprocmask(SIG_SETMASK, &sigset, NULL);

      /* Clear the alarm timer */

      (void) alarm(0);

      /* And default the signal handlers */

      (void) signal(SIGINT, SIG_DFL);
      (void) signal(SIGQUIT, SIG_DFL);
      (void) signal(SIGTERM, SIG_DFL);

      (void) signal(SIGALRM, SIG_DFL);
      (void) signal(SIGPIPE, SIG_DFL);

#ifdef HAVE_JOBCONTROL
      (void) signal(SIGTSTP, SIG_DFL);
#endif /* HAVE_JOBCONTROL */

      return;
}
/****************************************************************************/
static void init_escape()
{
      /* Set up for a shell escape */

      clearmsg();
      end_tmodes();

      return;
}
/****************************************************************************/
static void end_escape(restore, keypress, redraw)
int restore, keypress, redraw;
{
      /* Restart after a shell escape */

      /* Set the terminal modes back to af state if required */

      if (restore) {
            init_tmodes(FALSE);
      }

      /* Wait for a key if required */

      if (keypress) {
            (void) fputs("Press a key", stdout);
            (void) fflush(stdout);
            (void) get_key();
      }

      /* Redraw the screen if required */

      if (redraw) {
            tclear();
            vtredraw();
            clearmsg();
      }
      return;
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index