/*======================================================================
    pipe
    
    Initiate I/O to and from a process.  These functions are similar to 
    popen and pclose, but both an incoming stream and an output file are 
    provided.
   
 ====*/

#define WTR 1
#define RDR 0


/*----------------------------------------------------------------------
     Pipe a single character

  Args: command -- string to hand the shell
	outfile -- address of pointer containing file to receive output
	mode -- mode for type of shell, signal protection etc...
  Returns: pointer to alloc'd PIPE_S on success, NULL otherwise

  The outfile is either NULL, a pointer to a NULL value, or a pointer
  to the requested name for the output file.  In the pointer-to-NULL case
  the caller doesn't care about the name, but wants to see the pipe's
  results so we make one up.  It's up to the programmer to make sure
  the free storage containing the name is cleaned up.

  Mode bits serve several purposes.  PIPE_PROT means to protect the
  child from the usual nasty signals that might cause premature death.
  Otherwise, the default signals are set so the child can deal with 
  the nasty signals in its own way.  PIPE_USER means we're to try
  executing the command in the user's shell.  Right now we only look in
  the environment, but that may get more sophisticated later...
  PIPE_NOPIPE means we don't actually set up the pipe.  We just want
  to exec the command.
 ----*/
PIPE_S *
open_system_pipe(command, outfile, mode)
     char  *command;
     char **outfile;
     int    mode;
{
    PIPE_S *syspipe = NULL;
    char    shellpath[32], *shell;
    int     p[2];

    if (outfile && !*outfile)	/* asked for but not named?  name it */
      *outfile = temp_nam(NULL, "pine_p");

    if(!(mode & PIPE_NOPIPE))
      pipe(p);			/* allocate pipe */
    else{
	flush_status_messages();
	ClearScreen();
	fflush(stdout);
	Raw(0);
    }
    syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
    memset(syspipe, 0, sizeof(PIPE_S));

    if((syspipe->pid = vfork()) == 0){
 	/* reset child's handlers in requested fashion... */
	(void)signal(SIGINT,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
	(void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
	(void)signal(SIGHUP,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);

	if (outfile) {		/* connect output to our file */
	    int output = creat(*outfile, 0600);
	    dup2(output, 1);
	    dup2(output, 2);
	}

	if(!(mode & PIPE_NOPIPE)){
	    close(p[WTR]);		/*  connect process to pipe */
	    dup2(p[RDR], 0);
	    close(p[RDR]);
	}

	if(mode & PIPE_USER){
	    char *env, *sh;
	    if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
		shell = sh + 1;
		strcpy(shellpath, env);
	    }
	    else{
		shell = "csh";
		strcpy(shellpath, "/bin/csh");
	    }
	}
	else{
	    shell = "sh";
	    strcpy(shellpath, "/bin/sh");
	}

	execl(shellpath, shell, "-c", command, 0);
	exit(-1);
    }

    if(syspipe->pid > 0){
	/*
	 * reset handlers to make sure we don't exit prematurely...
	 */
	syspipe->isig = signal(SIGINT,  SIG_IGN);
	syspipe->qsig = signal(SIGQUIT, SIG_IGN);
	syspipe->hsig = signal(SIGHUP,  SIG_IGN);
	if(!(mode & PIPE_NOPIPE))
	  close(p[RDR]);
	syspipe->ifile = fdopen(p[WTR], "w");
	dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
    }
    else{
	if(mode & PIPE_NOPIPE){
	    Raw(1);
	    ClearScreen();
	    ps_global->mangled_screen = 1;
	}
	q_status_message1(1, 2, 3, "Error executing external command: %s",
			  error_description(errno));
	if(!(mode & PIPE_NOPIPE)){
	    close(p[WTR]);
	    close(p[RDR]);
	}
	fs_give((void **)&syspipe);
	if(outfile)
	  fs_give((void **)outfile);

	dprint(1, (debugfile, "CAN'T FORK FOR COMMAND: %s\n", command));
    }

    return(syspipe);
}



/*----------------------------------------------------------------------
    Close pipe previously allocated and wait for child's death

  Args: syspipe -- address of pointer to struct returned by open_system_pipe
  Returns: returns exit status of child or -1 if invalid syspipe
 ----*/
int
close_system_pipe(syspipe, mode)
PIPE_S **syspipe;
int      mode;
{
    int   status, r;
    FILE *f;

    if(!syspipe || !*syspipe)
      return(-1);

    if ((*syspipe)->ifile)
      fclose((*syspipe)->ifile);

    /*
     * Install c-client's universal reaper here once we've ironed out
     * with mrc what it should be...
     */
#ifdef HAVE_WAIT_UNION
    while((r=wait((union wait *)&status)) && r != -1 && r != (*syspipe)->pid);
#else  
    while((r=wait(&status)) && r != -1 && r != (*syspipe)->pid);
#endif

    /*
     * restore original handlers...
     */
    (void)signal(SIGINT,  (*syspipe)->isig);
    (void)signal(SIGHUP,  (*syspipe)->hsig);
    (void)signal(SIGQUIT, (*syspipe)->qsig);
    fs_give((void **)syspipe);

    /* restore screen */
    if(mode & PIPE_NOPIPE){
	Raw(1);
	ClearScreen();
	ps_global->mangled_screen = 1;
    }

    return(status);
}



/*----------------------------------------------------------------------
    Display the contents of the given file understood to be pipe output

  Args: filename -- name of file containing output
	title -- title to be used for screen displaying output
  Returns: none
 ----*/
void
display_system_pipe_output(filename, title)
     char *filename, *title ;
{
  FILE *f;
  char  buf[512] ;
  char *msg_p[4] ;
  int   msg_q ;

  if(name_file_size(filename) == 0L) 
    sleep(3);			/* Give the process time to do something */
  f = fopen(filename, "r");
  if(f != NULL){
    buf[0] = 0 ;
    unlink(filename);
    msg_p[msg_q=0] = buf ;
    while(fgets(msg_p[msg_q], sizeof(buf) - (msg_p[msg_q] - buf), f) 
	  && msg_q < 3) {
      msg_p[msg_q+1] = msg_p[msg_q]+strlen(msg_p[msg_q]) ;
      if (*(msg_p[++msg_q] - 1) == '\n')
	*(msg_p[msg_q] - 1) = '\0';
    }
    if (msg_q < 3) {
      if (*msg_p[0]) {
	int i ;
	for (i = 0 ; i < msg_q ; i++) 
	  q_status_message3(1, 1, 4, "%s %s: %s", title,
			    /*write_error ? "Error\007":*/ "Result", msg_p[i]);
      } else {
	q_status_message1(1,1,4, "%s command completed", title) ;
      }
    } else {
      scrolltool(f, title, NULL, AttachText, FileStar, NULL) ;
      ps_global->mangled_screen = 1 ;
    }
    fclose(f);
  } else {
    dprint(2, (debugfile, "Error reopening %s to get results: %s\n",
	       filename, error_description(errno)));
  }
}


