/* $Id: popen.c,v 1.11 92/12/04 09:16:30 wolf Exp $
 *
 * Derived from the original!  Mod by Wolf & Kim
 *
 * Copyright (c) 1988 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software written by Ken Arnold and
 * published in UNIX Review, Vol. 6, No. 8.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

/* $Log:	popen.c,v $
 * Revision 1.11  92/12/04  09:16:30  wolf
 * Updates syslog defines
 * 
 * Revision 1.10  92/11/17  19:55:19  wolf
 * Changed Debug handling
 * 
 * Revision 1.9  92/06/28  19:31:35  wolf
 * Replaced wait with waitpid (Posix compliant) in order to avoid the
 * defuncts.
 * 
 * Revision 1.8  92/06/12  07:11:31  listserv
 * Initial 2.00 release
 * 
 */

#ifndef lint
static char sccsid[] = "@(#)popen.c	5.7 (Berkeley) 2/14/89";
#endif /* not lint */

#include "conf.h"
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef FAKESYSLOG
# include "fakesyslog.h"
#else
# include <syslog.h>
#endif

/*
 * Special version of popen which avoids call to shell.  This insures noone
 * may create a pipe to a hidden program as a side effect of a list or dir
 * command.
 */

#define WINSZ	6
static int win=0;

static int *pids=NULL;
static int fds;

extern int Debug;

void reapchild()
{
   while (waitpid(-1, NULL, WNOHANG) > 0)
	 win--;
}

void winInc(void)
{
   win++;
}

FILE * l_popen(char *program, char *type)
{
  register char *cp;
  FILE *iop;
  int argc, pdes[2], pid;
  char *argv[100];

  if (*type != 'r' && *type != 'w' || type[1])
    return(NULL);

  if (!pids) {
    if ((fds = sysconf(_SC_OPEN_MAX)) <= 0)
      return(NULL);
    if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
      return(NULL);
    memset((char *)pids, 0, fds * sizeof(int));
  }
  if (pipe(pdes) < 0)
    return(NULL);

  /* break up string into pieces */
  for (argc = 0, cp = program;; cp = NULL)
    if (!(argv[argc++] = strtok(cp, " \t\n")))
      break;

  iop = NULL;
  switch(pid = fork()) {
  case -1:			/* error */
    (void)close(pdes[0]);
    (void)close(pdes[1]);
    goto pfree;
    /* NOTREACHED */
  case 0:			/* child */
    if (*type == 'r') {
      if (pdes[1] != 1) {
	dup2(pdes[1], 1);
	dup2(pdes[1], 2);	/* stderr, too! */
	(void)close(pdes[1]);
      }
      (void)close(pdes[0]);
    } else {
      if (pdes[0] != 0) {
	dup2(pdes[0], 0);
	(void)close(pdes[0]);
      }
      (void)close(pdes[1]);
    }
    execv(argv[0], argv);
    _exit(1);
  }
  /* parent; assume fdopen can't fail...  */
  if (*type == 'r') {
    iop = fdopen(pdes[0], type);
    (void)close(pdes[1]);
  } else {
    iop = fdopen(pdes[1], type);
    (void)close(pdes[0]);
  }
  pids[fileno(iop)] = pid;
  win++;

pfree:	
  return(iop);
}

l_pclose(FILE *iop)
{
  register int fdes;

  /*
   * pclose returns -1 if stream is not associated with a
   * `popened' command, or, if already `pclosed'.
   */
  if (pids == 0 || pids[fdes = fileno(iop)] == 0)
    return(-1);
  (void)fclose(iop);
   if (Debug != 0)
     fprintf(stderr, "l_pclose: win is %d", win);
  while (win > WINSZ) sleep(30);
  pids[fdes] = 0;
  return(0);
}
