#include <conf.h>
#include <unistd.h>
#include <x_defs.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifdef	HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef  HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#ifdef	HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#ifdef  HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#ifdef	HAVE_TIME_H
#ifdef	TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#ifdef HAVE_TERMIO_H
# include <termio.h>
#else
# include <termios.h>
#endif /* HAVE_TERMIO_H */
#ifdef	HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef	HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#include <signal.h>
#include <grp.h>
#include <sysutils.h>
#include <fileutil.h>

#define	GETOUT		{ goto getout; }
#define	CLEANUP		{ goto cleanup; }
#define	CLEANUPR(x)	{ r = x; goto cleanup; }

#ifndef	HAVE_STRERROR
#include <errno.h>

#ifndef	HAVE_SYS_ERRLIST_DEF
extern	char *sys_errlist[];
#endif

char *
strerror(int no)
{
  return(sys_errlist[no]);

#if 0
  static char	*msg = "Unknown error (no sys_errlist available)";
  return(msg);
#endif
}

#endif

#ifndef	HAVE_SETITIMER
#include <errno.h>

static Timer	rtimer = -1;

int
setitimer(
    int			which,
    struct itimerval	*timeval,
    struct itimerval	*otimeval)
{
    struct Signal_Event	evp;
    struct itimerval	timer_value, old_timer_value, *timer_ptr, *old_timer_ptr;

    if(which != ITIMER_REAL)
	return(EINVAL);

    if(rtimer == -1){
	evp.sigev_signum = SIGALRM;

	rtimer = timer_make(CLOCK_TYPE_REALTIME, &evp);
	if(rtimer == -1)
	    return(-1);
    }

    timer_ptr = NULL;

    if(timeval){
	timer_value.it_value.tv_sec = timeval->it_value.tv_sec;
	timer_value.it_value.tv_usec = timeval->it_value.tv_usec;
	timer_value.it_interval.tv_sec = timeval->it_interval.tv_sec;
	timer_value.it_interval.tv_usec = timeval->it_interval.tv_usec;

	timer_ptr = &timer_value;
    }

    old_timer_ptr = (otimeval ? &old_timer_value : NULL);

    if(timer_set(rtimer, 0, timer_ptr, old_timer_ptr) != 0)
	return(-1);

    if(otimeval){
	otimeval->it_value.tv_sec = old_timer_value.it_value.tv_sec;
	otimeval->it_value.tv_usec = old_timer_value.it_value.tv_usec;
	otimeval->it_interval.tv_sec = old_timer_value.it_interval.tv_sec;
	otimeval->it_interval.tv_usec = old_timer_value.it_interval.tv_usec;
    }

    return(0);
}

int
getitimer(
    int			which,
    struct itimerval	*timeval)
{
    struct Signal_Event	evp;
    struct itimerval	timer_value;

    if(which != ITIMER_REAL){
	errno = EINVAL;
	return(-1);
    }

    if(!timeval)
	return(0);

    if(rtimer == -1){
	evp.sigev_signum = SIGALRM;

	rtimer = timer_make(CLOCK_REALTIME, &evp);
	if(rtimer != -1)
	    return(-1);
    }

    if(timer_get(rtimer, &timer_value) != 0)
	return(-1);

    if(timeval){
	timeval->it_value.tv_sec = timer_value.it_value.tv_sec;
	timeval->it_value.tv_usec = timer_value.it_value.tv_usec;
	timeval->it_interval.tv_sec = timer_value.it_interval.tv_sec;
	timeval->it_interval.tv_usec = timer_value.it_interval.tv_usec;
    }

    return(0);
}

#endif	/* !defined(HAVE_SETITIMER) */


#include <utils.h>
#include <x_timer.h>
#include <errno.h>

#define	STOP_TIMERS	setitimer(ITIMER_REAL, &itimer_null, NULL)
#define	START_TIMERS	start_timers()

#define	ADD_TIME(dest, t1, t2)	{ (dest).tv_sec = (t1).tv_sec + (t2).tv_sec; \
				  (dest).tv_usec = \
					(t1).tv_usec + (t2).tv_usec; \
				  if((dest).tv_usec >= 1000000){ \
				    (dest).tv_usec -= 1000000; \
				    (dest).tv_sec++; } }
#define	SUB_TIME(dest, t1, t2)	{ if((t1).tv_usec < (t2).tv_usec){ \
				    (dest).tv_sec = \
					(t1).tv_sec - (t2).tv_sec - 1;\
				    (dest).tv_usec = (t1).tv_usec + 1000000 \
							- (t2).tv_usec; } \
				  else{ \
				    (dest).tv_sec = \
				    (t1).tv_sec - (t2).tv_sec; \
				    (dest).tv_usec = (t1).tv_usec - (t2).tv_usec;}}
#define	CP_TIME(dest, src)	{ (dest).tv_usec = (src).tv_usec; \
				  (dest).tv_sec = (src).tv_sec; }
#define	IS_LATER(now, timer)	((now).tv_sec > (timer).tv_sec ? 1 : \
				  ((now).tv_sec < (timer).tv_sec ? 0 : \
				    ((now).tv_usec >= (timer).tv_usec ? 1 : 0)))
#define	TIMERSIZE	sizeof(TimerDataStruct)

typedef struct _aix_posix_timer {
  int			timer_id;
  struct timeval	exp_time;
  struct timeval	interval;
  signal_t		signal;
  char			repeat;
}	TimerDataStruct;

static	void	(*org_handler)() = SIG_DFL;
static	char	handler_installed = 0;

static	char	timer_signal = 0;

static	int	num_active_timers = 0;
static	int	num_timers = 0;
static	int	max_num_timers = 0;

static	Timer	actid = 2783;

static	TimerDataStruct	*active_timers = NULL;
static	TimerDataStruct	*timers = NULL;
static	TimerDataStruct	tmp_timer;

static struct itimerval		tmp_itimer, loc_itimer;

static struct itimerval itimer_null = { { (int) 0, (int) 0 },
					{ (int) 0, (int) 0 } };

static void
exec_timer(int * act_timer_idx_ptr)
{
  int	i, j, act_timer_idx, sigfl;

  act_timer_idx = *act_timer_idx_ptr;

  sigfl = active_timers[act_timer_idx].signal;

  if(active_timers[act_timer_idx].repeat){
    ADD_TIME(active_timers[act_timer_idx].exp_time,
		active_timers[act_timer_idx].exp_time,
		active_timers[act_timer_idx].interval);

    for(i = act_timer_idx + 1; i < num_active_timers; i++)
	if(IS_LATER(active_timers[i].exp_time,
			active_timers[act_timer_idx].exp_time))
	  break;

    if(i > act_timer_idx + 1){
	memcpy(&tmp_timer, &active_timers[act_timer_idx], TIMERSIZE);
	for(j = act_timer_idx; j < i - 1; j++)
	  memcpy(&active_timers[j], &active_timers[j + 1], TIMERSIZE);
	memcpy(&active_timers[i - 1], &tmp_timer, TIMERSIZE);

	(*act_timer_idx_ptr)--;
    }
  }
  else{
    (*act_timer_idx_ptr)--;
    num_active_timers--;
    for(i = act_timer_idx; i < num_active_timers; i++)
	memcpy(active_timers + i, active_timers + i + 1, TIMERSIZE);
  }

  timer_signal = 1;
  kill(getpid(), sigfl);
  timer_signal = 0;
}

static	void
process_timers()
{
  int		i;
  struct timeval	acttime;
  struct timezone	actzone;


  if(num_active_timers == 0)
    return;

  gettimeofday(&acttime, &actzone);

  for(i = 0; i < num_active_timers; i++){
    if(IS_LATER(acttime, active_timers[i].exp_time)){
	exec_timer(&i);
    }
    else
	break;
  }
}

static int
start_timers()
{
  int		i;
  struct timeval	acttime;
  struct timezone	actzone;

  if(!num_active_timers)
    return(0);

  tmp_itimer.it_interval.tv_sec = 0;
  tmp_itimer.it_interval.tv_usec = 0;

  do{
    i = 0;

    gettimeofday(&acttime, &actzone);

    SUB_TIME(tmp_itimer.it_value, active_timers[0].exp_time, acttime);

    if((tmp_itimer.it_value.tv_sec == 0 && tmp_itimer.it_value.tv_usec == 0)
			|| tmp_itimer.it_value.tv_sec < 0){
	exec_timer(&i);
	i = 1;
    }
  } while(i);

  return(setitimer(ITIMER_REAL, &tmp_itimer, NULL));
}

static	char	handler_active = 0;

static void
handler(signal_t sig)
{
  if(timer_signal){
    if(org_handler != SIG_DFL)
	org_handler(sig);
  }
  else{
    while(handler_active);

    handler_active = 1;

    STOP_TIMERS;

    process_timers();

    START_TIMERS;

    handler_active = 0;
  }
}


Timer
timer_make(ClockID id, struct Signal_Event *sigev)
{
  struct sigaction	siga, osiga;

  if(id != CLOCK_TYPE_REALTIME){
    errno = EINVAL;
    return(-1);
  }

  STOP_TIMERS;

  num_timers++;

  if(num_timers > max_num_timers){
    max_num_timers = num_timers;

    if(timers)
	timers = RENEWP(timers, TimerDataStruct, max_num_timers);
    else
	timers = NEWP(TimerDataStruct, 1);

    if(active_timers)
	active_timers = RENEWP(active_timers, TimerDataStruct,
					max_num_timers);
    else
	active_timers = NEWP(TimerDataStruct, 1);

    if(!timers && ! active_timers){
	errno = ENOMEM;
	return(-1);
    }
  }

  process_timers();

  START_TIMERS;

  tmp_itimer.it_interval.tv_sec = 0;
  tmp_itimer.it_interval.tv_usec = 0;

  if(! handler_installed){
    SETZERO(siga);
    siga.sa_handler = handler;
    sigaction(SIGALRM, &siga, &osiga);

    org_handler = osiga.sa_handler;

    handler_installed = 1;
  }

  actid++;

  timers[num_timers - 1].timer_id = actid;
  timers[num_timers - 1].signal = sigev->sigev_signum;

  return(actid);
}

static void
clear_timer(int idx)
{
  num_active_timers--;

  for(; idx < num_active_timers; idx++)
    memcpy(&active_timers[idx], &active_timers[idx + 1], TIMERSIZE);
}

static int
remove_timer(Timer timerid)
{
  int	i;

  for(i = 0; i < num_timers; i++){
    if(timerid == timers[i].timer_id){
	num_timers--;
	for(; i < num_timers; i++)
	  memcpy(&timers[i], &timers[i + 1], TIMERSIZE);
	return(0);
    }
  }

  errno = EINVAL;

  return(-1);
}

int
timer_del(Timer timerid)
{
  int	i, res;

  res = -1;
  if(num_active_timers > 0){
    for(i = 0; i < num_active_timers; i++){
	if(active_timers[i].timer_id == timerid){
	  STOP_TIMERS;

	  clear_timer(i);

	  START_TIMERS;

	  res = remove_timer(timerid);
	}
    }
  }

  return(res);
}

int
timer_get(Timer timerid, struct itimerval *value)
{
  int	i;
  struct timeval	acttime;
  struct timezone	actzone;

  for(i = 0; i < num_active_timers; i++){
    if(timerid == active_timers[i].timer_id){
	if(active_timers[i].repeat){
	  value->it_interval.tv_usec =
			active_timers[i].interval.tv_usec;
	  value->it_interval.tv_sec =
			active_timers[i].interval.tv_sec;
	}
	else{
	  value->it_interval.tv_sec = 0;
	  value->it_interval.tv_usec = 0;
	}

	gettimeofday(&acttime, &actzone);
	SUB_TIME(tmp_itimer.it_value, active_timers[i].exp_time, acttime);
	if(tmp_itimer.it_value.tv_sec < 0){
	  tmp_itimer.it_value.tv_sec = 0;
	  tmp_itimer.it_value.tv_usec = 0;
	}
	value->it_value.tv_sec = tmp_itimer.it_value.tv_sec;
	value->it_value.tv_usec = tmp_itimer.it_value.tv_usec;

	return(0);
    }
  }

  for(i = 0; i < num_timers; i++)
    if(timerid == timers[i].timer_id)
	break;

  if(i < num_timers)
    return(0);

  return(-1);
}

int
timer_set(
  Timer		timerid,
  int		flags,
  struct itimerval	*value,
  struct itimerval	*ovalue)
{
  struct timeval	acttime;
  struct timezone	actzone;
  int	i, j, tnum;

  for(tnum = 0; tnum < num_timers; tnum++)
    if(timerid == timers[tnum].timer_id)
	break;

  if(tnum >= num_timers){
    errno = EINVAL;
    return(-1);
  };

  memset(&tmp_itimer, 0, sizeof(tmp_itimer));

  loc_itimer.it_interval.tv_sec = value->it_interval.tv_sec;
  loc_itimer.it_interval.tv_usec = value->it_interval.tv_usec;
  loc_itimer.it_value.tv_sec = value->it_value.tv_sec;
  loc_itimer.it_value.tv_usec = value->it_value.tv_usec;

  /*if(!loc_itimer.it_value.tv_sec && !loc_itimer.it_value.tv_usec &&
	(loc_itimer.it_interval.tv_sec || loc_itimer.it_interval.tv_usec)){
    loc_itimer.it_value.tv_sec = loc_itimer.it_interval.tv_sec;
    loc_itimer.it_value.tv_usec = loc_itimer.it_interval.tv_usec;
  }*/

  STOP_TIMERS;

  for(i = 0; i < num_active_timers; i++)
    if(active_timers[i].timer_id == timerid){
	gettimeofday(&acttime, &actzone);

	SUB_TIME(tmp_itimer.it_value, active_timers[i].exp_time, acttime);
	if(active_timers[i].repeat)
	  CP_TIME(tmp_itimer.it_interval, active_timers[i].interval);

	if(tmp_itimer.it_value.tv_sec < 0){
	  tmp_itimer.it_value.tv_sec = 0;
	  tmp_itimer.it_value.tv_usec = 0;
	}

	clear_timer(i);
	break;
    }

  gettimeofday(&acttime, &actzone);

  if(ovalue){
    ovalue->it_interval.tv_sec = tmp_itimer.it_interval.tv_sec;
    ovalue->it_interval.tv_usec = tmp_itimer.it_interval.tv_usec;
    ovalue->it_value.tv_sec = tmp_itimer.it_value.tv_sec;
    ovalue->it_value.tv_usec = tmp_itimer.it_value.tv_usec;
  }

  tmp_timer.timer_id = timerid;

  tmp_timer.repeat = !(loc_itimer.it_interval.tv_sec == 0
				&& loc_itimer.it_interval.tv_usec == 0);

  tmp_timer.signal = timers[tnum].signal;

  tmp_timer.interval.tv_sec = loc_itimer.it_interval.tv_sec;
  tmp_timer.interval.tv_usec = loc_itimer.it_interval.tv_usec;

  if(flags & TIMER_ADDREL){
    ADD_TIME(tmp_timer.exp_time, loc_itimer.it_value, tmp_itimer.it_value);
  }
  else{
    if(flags & TIMER_ABSTIME){
      CP_TIME(tmp_timer.exp_time, loc_itimer.it_value);
    }
    else{
      ADD_TIME(tmp_timer.exp_time, loc_itimer.it_value, acttime);
    }
  }

  if(loc_itimer.it_value.tv_sec || loc_itimer.it_value.tv_usec
	|| loc_itimer.it_interval.tv_sec || loc_itimer.it_interval.tv_usec){
    for(i = 0; i < num_active_timers; i++)
      if(IS_LATER(active_timers[i].exp_time, tmp_timer.exp_time))
	  break;

    for(j = num_active_timers; j > i; j--)
      memcpy(&active_timers[j], &active_timers[j - 1], TIMERSIZE);

    memcpy(&active_timers[i], &tmp_timer, TIMERSIZE);

    num_active_timers++;
  }

  process_timers();

  return(START_TIMERS);
}

int
clock_get(ClockID clock_id, struct timeval *tp)
{
  struct timeval	acttime;
  struct timezone	actzone;

  if(clock_id != CLOCK_TYPE_REALTIME){
    errno = EINVAL;
    return(-1);
  }

  gettimeofday(&acttime, &actzone);

  tp->tv_sec = acttime.tv_sec;
  tp->tv_usec = acttime.tv_usec;

  return(0);
}


#if !defined(WINDOWS_LIKE)

#include <signal.h>
#include <unistd.h>
#include <x_types.h>


#ifndef	NSIG
#ifdef	_SIGMAX
#define	NSIG	_SIGMAX
#else
#ifdef	SIGMAX
#define	NSIG	SIGMAX
#else
#define	NSIG	32
#endif
#endif
#endif

#ifdef HAVE_TERMIO_H
static struct termio	org_ttyattr;
#else
static struct termios	org_ttyattr;
#endif
static void		(*org_sighandlers[NSIG])(int);
static UChar		signals_set = 0;

static void
reset_signals()
{
  Int32	i;

#ifdef HAVE_TERMIO_H
  ioctl(0, TCSETA, &org_ttyattr);
#else
  ioctl(0, TIOCSETA, &org_ttyattr);
#endif

  if(!signals_set)
    return;

  for(i = 0; i < NSIG; i++)
    signal(i, org_sighandlers[i]);

  signals_set = 0;
}

static void
ttsighandl(int sig)
{
  reset_signals();

  if(org_sighandlers[sig])
    org_sighandlers[sig](sig);
  else
    exit(1);
}

Int32
getinchr(UChar * c, Uns32 timeout)
{
#ifdef HAVE_TERMIO_H
  struct termio		new_ttyattr;
#else
  struct termios	new_ttyattr;
#endif
  Int32		i;
  char		a;
  Uns32		ltime;
  char		stime;

  if(c == NULL)
    return(EINVAL);

  stime = (char)(timeout & 63);
  ltime = timeout / 64 + 1;

#ifdef HAVE_TERMIO_H
  if(ioctl(0, TCGETA, &org_ttyattr))
#else
  if(ioctl(0, TIOCGETA, &org_ttyattr))
#endif
    return(-1);

  memcpy(&new_ttyattr, &org_ttyattr, sizeof(org_ttyattr));
  if(timeout){
    new_ttyattr.c_cc[VMIN] = '\0';
  }
  else{
    new_ttyattr.c_cc[VMIN] = '\001';
    new_ttyattr.c_cc[VTIME] = '\0';
  }
  new_ttyattr.c_lflag &= ~(ICANON | ECHO);

  for(i = 0; i < NSIG; i++)
    org_sighandlers[i] = signal(i, ttsighandl);
  signals_set = 1;


  do{
    if(timeout){
      if(ltime > 1){
	new_ttyattr.c_cc[VTIME] = (char) 64;
      }
      else{
	new_ttyattr.c_cc[VTIME] = stime;
      }
      ltime--;
    }

#ifdef HAVE_TERMIO_H
    if(ioctl(0, TCSETA, &new_ttyattr)){
#else
    if(ioctl(0, TIOCSETA, &new_ttyattr)){
#endif
      reset_signals();
      return(-1);
    }

    i = read(0, &a, 1);
  } while(timeout ? (ltime > 0 && i < 1) : (i < 1));

  reset_signals();

  if(i > 0){
    *c = a;
    return(1);
  }

  return(0);
}

Flag
is_a_tty(int fd)
{
#ifdef HAVE_TERMIO_H
  struct termio		ttyattrs;
#else
  struct termios	ttyattrs;
#endif

#ifdef HAVE_TERMIO_H
  if(! ioctl(0, TCGETA, &ttyattrs))
#else
  if(! ioctl(0, TIOCGETA, &ttyattrs))
#endif
    return(YES);

#if	0
  if(errno == EBADF || errno == EINVAL)
    return(DONT_KNOW);
#endif

  return(NO);
}

#define	NULLDEV	"/dev/null"

Int32
free_fds()
{
  int	*fds = NULL, *newfds = NULL, fd;
  Int32	num_fds = 0;

  while((fd = open(NULLDEV, O_RDONLY)) >= 0){
    newfds = ZSRENEWP(fds, int, num_fds + 1, num_fds);

    if(!newfds){
	close(fd);
	break;
    }
    fds = newfds;

    fds[num_fds] = fd;

    num_fds++;
  }

  for(fd = 0; fd < num_fds; fd++)
    close(fds[fd]);

  free(fds);

  return(num_fds);
}


/* This is for stupid HP's HP-UX-10 header ioctl.h (Grrrrr HP !) */
#ifdef	hpux
#ifndef	TIOCNOTTY
#define	notdef	1
#undef	_SYS_IOCTL_INCLUDED
#include <sys/ioctl.h>
#ifndef	TIOCNOTTY
#warning Definition of macro TIOCNOTTY cannot be found !
#endif
#endif
#endif

int
detach_from_tty()
{
  int	fd, r;

  r = 1;

  if((fd = open("/dev/tty", O_RDWR)) >= 0){
    r = ioctl(fd, TIOCNOTTY);
    close(fd);
  }

  return(r);
}

void			/* the super-safe generalized vsyslog */
gvsyslog(
  UChar		*ident,
  int		options,
  int		facility,
  int		prio,
  UChar		*fmt,
  va_list	args)
{
  openlog(ident, options, facility);

#ifdef	HAVE_VSYSLOG

  vsyslog(prio, fmt, args);

#else
#ifdef	HAVE_VSNPRINTF
  {
    UChar	lbuf[10000];	/* we just hope, there is nothing important */
					/* after 9999 characters any more */
    vsnprintf(lbuf, 9998, fmt, args);
    lbuf[9999] = '\0';
    syslog(prio, "%s", lbuf);
  }
#else
  {
    UChar	*line;
    int		fd;
    FILE	*fp;

    fd = tmp_file(NULL);
    if(fd < 0)
	return;
    fp = fdopen(fd, "r+");
    if(!fp){
	close(fd);
	return;
    }
    vfprintf(fp, fmt, args);
    fseek(fp, 0, SEEK_SET);
    while(!feof(fp)){
	line = fget_alloc_str(fp);
	if(!line)
	  continue;
	syslog(prio, "%s", line);
	free(line);
    }
    fclose(fp);
  }
#endif
#endif

  closelog();
}

#if	defined(unix) || defined(_AIX)

int
open_to_pipe(
  UChar		*prog,
  UChar		*filename,
  UChar		fds,
  int		*pidp,
  int		mode)
{
  int		fd = -1, pid, pp[2], i;
  char		**progargv;

  if(!filename && !prog)
    return -1;

  if(filename){
    fd = open(filename, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode);
    if(fd < 0)
      return(-1);
  }

  if(prog){
    i = pipe(pp);

    if(i){
      if(filename)
	close(fd);
      return(-1);
    }

    pid = fork_forced();
    if(pid < 0){
      if(filename)
	close(fd);
      return(-1);
    }
    if(!pid){	/* child */
      if( (i = cmd2argvqf(&progargv, prog)) )
	exit(i);

      close(pp[1]);
      if(filename){
	if(fds & 1)
	  dup2(fd, 1);
	if(fds & 2)
	  dup2(fd, 2);
      }
      dup2(pp[0], 0);

      execvp(progargv[0], progargv + 1);
      exit(errno);
    }

    close(pp[0]);
    if(filename)
      close(fd);

    fd = pp[1];
    if(pidp)
	*pidp = pid;
  }

  return(fd);
}  

int
open_from_pipe(UChar * prog, UChar * filename, UChar fds, int * pidp)
{
  int		fd = -1, pid, pp[2], i;
  char		**progargv;

  if(!filename && !prog)
    return -1;

  if(filename){
    fd = open(filename, O_RDONLY | O_BINARY);
    if(fd < 0)
	return(-1);
  }

  if(prog){
    i = pipe(pp);

    if(i){
      if(filename)
	close(fd);
      return(-1);
    }

    pid = fork_forced();
    if(pid < 0){
      if(filename)
	close(fd);
      return(-1);
    }
    if(!pid){	/* child */
      close(pp[0]);

      if( (i = cmd2argvqf(&progargv, prog)) )
	exit(i);

      if(filename){
	dup2(fd, 0);
      }
      if(fds & 1)
	dup2(pp[1], 1);
      if(fds & 2)
	dup2(pp[1], 2);

      execvp(progargv[0], progargv + 1);
      exit(errno);
    }

    close(pp[1]);
    if(filename)
      close(fd);

    fd = pp[0];
    if(pidp)
	*pidp = pid;
  }

  return(fd);
}  

Int32
open_from_to_pipe(UChar * prog, int * pipes, UChar fds, int * pidp)
{
  int		pid, topp[2], frompp[2], i, j;
  char		**progargv;

  if(!prog)
    return -1;

  i = pipe(topp);
  j = pipe(frompp);

  if(i || j){
    if(i){
	close(topp[0]);
	close(topp[1]);
    }
    if(j){
	close(frompp[0]);
	close(frompp[1]);
    }
    return(-1);
  }

  pid = fork_forced();
  if(pid < 0){
    return(-1);
  }
  if(!pid){	/* child */
    close(frompp[0]);
    close(topp[1]);

    if( (i = cmd2argvqf(&progargv, prog)) )
	exit(i);

    dup2(topp[0], 0);
    if(fds & 1)
	dup2(frompp[1], 1);
    if(fds & 2)
	dup2(frompp[1], 2);

    execvp(progargv[0], progargv + 1);
    exit(errno);
  }

  close(frompp[1]);
  close(topp[0]);

  if(pidp)
    *pidp = pid;
  if(pipes){
    pipes[0] = frompp[0];
    pipes[1] = topp[1];
  }

  return(0);
}

int
open_to_pipe_sigblk(
  UChar		*prog,
  UChar		*filename,
  UChar		fds,
  int		*pidp,
  int		mode,
  sigset_t	*sigs)
{
  int		r;
  sigset_t	nsigs, osigs;

  if(prog){
    COPYVAL(nsigs, *sigs);	/* in case it's modified by sigprocmask */
    sigprocmask(SIG_SETMASK, &nsigs, &osigs);
  }

  r = open_to_pipe(prog, filename, fds, pidp, mode);

  if(prog)
    sigprocmask(SIG_SETMASK, &osigs, NULL);

  return(r);
}

int
open_from_pipe_sigblk(
  UChar		*prog,
  UChar		*filename,
  UChar		fds,
  int		*pidp,
  sigset_t	*sigs)
{
  int		r;
  sigset_t	nsigs, osigs;

  if(prog){
    COPYVAL(nsigs, *sigs);	/* in case it's modified by sigprocmask */
    sigprocmask(SIG_SETMASK, &nsigs, &osigs);
  }

  r = open_from_pipe(prog, filename, fds, pidp);

  if(prog)
    sigprocmask(SIG_SETMASK, &osigs, NULL);

  return(r);
}

Int32
open_from_to_pipe_sigblk(
  UChar		*prog,
  int		*pipes,
  UChar		fds,
  int		*pidp,
  sigset_t	*sigs)
{
  int		r;
  sigset_t	nsigs, osigs;

  if(prog){
    COPYVAL(nsigs, *sigs);	/* in case it's modified by sigprocmask */
    sigprocmask(SIG_SETMASK, &nsigs, &osigs);
  }

  r = open_from_to_pipe(prog, pipes, fds, pidp);

  if(prog)
    sigprocmask(SIG_SETMASK, &osigs, NULL);

  return(r);
}

int
fd_system_fork(char * prnam, int * fds)
{
  int	infd[2], outfd[2], errfd[2], i, flags;
  char	*the_shell;

  if(!fds || !prnam){
    errno = EINVAL;
    return(-1);
  }

  if(!(the_shell = getenv("SHELL")))
    the_shell = "/bin/sh";

  i = pipe(infd);
  if(i)
    return(-1);
  i = pipe(outfd);
  if(i)
    return(-1);
  i = pipe(errfd);
  if(i)
    return(-1);

  i = fork();
  if(i < 0)
    return(-1);

  if(i == 0){			/* child */
    if(fds[0])
      dup2(infd[0], 0);
    else
      close(infd[0]);
    if(fds[1])
      dup2(outfd[1], 1);
    else
      close(outfd[1]);
    if(fds[2])
      dup2(errfd[1], 2);
    else
      close(errfd[1]);

    close(infd[1]);
    close(outfd[0]);
    close(errfd[0]);

    flags = fcntl(1, F_GETFL);
    fcntl(1, F_SETFL, flags | O_SYNC);
    flags = fcntl(2, F_GETFL);
    fcntl(2, F_SETFL, flags | O_SYNC);

    execl(the_shell, the_shell, "-c", prnam, NULL);

    fprintf(stderr, T_("Error: could not start subprocess \"%s\".\n"),
			prnam);

    exit(1);
  }

  if(fds[0])
    fds[0] = infd[1];
  else
    close(infd[1]);
  if(fds[1])
    fds[1] = outfd[0];
  else
    close(outfd[0]);
  if(fds[2])
    fds[2] = errfd[0];
  else
    close(errfd[0]);

  close(infd[0]);
  close(outfd[1]);
  close(errfd[1]);

  return(i);
}

int
fp_system_fork(char * pnam, FILE ** fps)
{
  int	fds[3], pid;

  if(!fps || !pnam){
    errno = EINVAL;
    return(-1);
  }

  pid = fd_system_fork(pnam, fds);

  if(pid < 1)
    return(pid);

  fps[0] = fdopen(fds[0], "w");
  fps[1] = fdopen(fds[1], "r");
  fps[2] = fdopen(fds[2], "r");

  if(!fps[0] || !fps[1] || !fps[2]){
    if(fps[0])
	fclose(fps[0]);
    if(fps[1])
	fclose(fps[1]);
    if(fps[2])
	fclose(fps[2]);

    return(-1);
  }

  return(pid);
}

int
fdpopen(char * cmd, int mode, int * pidp)
{
  int	fds[3], pid, rfd;

  rfd = -1;

  switch(mode){
   case O_WRONLY:
    fds[0] = 1;
    fds[1] = 0;
    fds[2] = 0;

    pid = fd_system_fork(cmd, fds);
    if(pid < 0)
	return(-1);

    rfd = fds[0];
    break;

   case O_RDONLY:
    fds[0] = 0;
    fds[1] = 1;
    fds[2] = 0;

    pid = fd_system_fork(cmd, fds);
    if(pid < 0)
	return(-1);

    rfd = fds[1];
    break;

   default:
    errno = EINVAL;
    return(-1);
  }

  if(pidp)
    *pidp = pid;

  return(rfd);
}

#endif	/* defined(unix) */

Int32
bytes_free_real_mem_pag(Int32 chunksize, Int32 maxsize)
{
  Real64	avg_duration, total_duration, timediff;
  Int32		nmeasured, newsize;
  Int32		allocated = 0;
  UChar		*mem = NULL, *newmem;
  time_t	start_time, end_time;
  struct timeval	start_tod, end_tod;

  avg_duration = total_duration = 0.0;
  nmeasured = 0;

  if(maxsize / chunksize < 8)
    maxsize = chunksize * 8;

  forever{
    do{
	start_time = time(NULL);
	gettimeofday(&start_tod, NULL);
    }while(start_time != time(NULL));

    if((newsize = allocated + chunksize) > maxsize)
	break;

    newmem = ZRENEWP(mem, UChar, newsize);
    if(!newmem)
	break;

    mem = newmem;
    memset(mem + allocated, 0x5a, chunksize);

    do{
	end_time = time(NULL);
	gettimeofday(&end_tod, NULL);
    }while(end_time != time(NULL));

    timediff = (Real64) (end_tod.tv_sec - start_tod.tv_sec) +
		(Real64) (end_tod.tv_usec - start_tod.tv_usec) / 1000000.0;
    if(timediff < 0.0)
	timediff += 86400.0;

    if(nmeasured == 0){
	avg_duration = total_duration =  timediff;
    }
    else{
	if(timediff > avg_duration * 5.0 && nmeasured >= 8)
	  break;
	total_duration += timediff;
	avg_duration = total_duration / (Real64) nmeasured;
    }
	
    nmeasured++;
    allocated = newsize;
  }

  ZFREE(mem);

  return(allocated);
}



#ifndef _WIN32	/* here we believe in the maximum thinkable BS occuring */

Int32
get_fs_space(UChar * path, Real64 * size)
{
#ifndef	HAVE_STATVFS
  struct statfs	statfsb;
#else
  struct statvfs	statfsb;
#endif

  if(!size){
    errno = EINVAL;
    return(-1);
  }

#ifdef	HAVE_STATVFS		/* sees to be well-defined */
	  if(statvfs(path, &statfsb) == -1)
#else
#if defined(_AIX) || defined(linux) || defined(hpux) || defined(__FreeBSD__) || defined(__OpenBSD__)
	  if(statfs(path, &statfsb) == -1)
#else
#if defined(sun)
	  if(statfs(path, &statfsb, sizeof(statfsb), 0) == -1)
#else
#if defined(sgi)
	  if(statfs(path, &statfsb, sizeof(statfsb), 0) == -1)
#else
#if defined(__osf__)
	  if(statfs(path, &statfsb, sizeof(statfsb)) == -1)
#else
#if defined(bioware_4)
	  if(statfs(path, &statfsb) == -1)
#else
#error	undefined architecture
#endif
#endif
#endif
#endif
#endif
#endif
	    return(-1);

#ifdef	HAVE_STATVFS
  if(getuid())
    *size = (Real64) statfsb.f_bsize * (Real64) statfsb.f_bavail;
  else
#endif
    *size = (Real64) statfsb.f_bsize * (Real64) statfsb.f_bfree;

  return(0);
}

#else	/* defined(_WIN32) */

Int32
get_fs_space(UChar * path, Real64 * size)
{
  if(size)
    *size = 10000000.0;

  return(0);
}

#endif	/* ifelse defined(_WIN32) */

int
set_eff_ugids(uid_t uid, gid_t gid, int ngids, gid_t * gids)
{
  int		r = 0, ncgids = 0;
  uid_t		ceuid;
  gid_t		cegid, *cgids = NULL;

  if(ngids > 0 && gids){
    if(get_groups(&ncgids, &cgids))
	return(-1);

    r = setgroups(ngids, gids);
    if(r)
	CLEANUPR(r);
  }

  ceuid = cegid = -1;

  if(gid != -1){
    cegid = getegid();

    r =

#ifdef	HAVE_SETEGID
	setegid(gid);
#else
#ifdef	HAVE_SETREGID
	setregid(-1, gid);
#else
#ifdef	HAVE_SETRESGID
	setresgid(-1, gid, -1);
#else
#error	No function to set the effective group id
#endif
#endif
#endif

    if(r){
	set_eff_ugids(-1, -1, ncgids, cgids);
	CLEANUPR(r);
    }
  }

  if(uid != -1){
    ceuid = geteuid();

    r =

#ifdef	HAVE_SETEUID
	seteuid(uid);
#else
#ifdef	HAVE_SETREUID
	setreuid(-1, uid);
#else
#ifdef	HAVE_SETRESUID
	setresuid(-1, uid, -1);
#else
#error	No function to set the effective user id
#endif
#endif
#endif

    if(r){
	set_eff_ugids(-1, cegid, ncgids, cgids);
	CLEANUPR(r);
    }
  }

 cleanup:
  ZFREE(cgids);

  return(r);
}

int
get_groups(int * ngids, gid_t ** gids)
{
  int		ngs;
  gid_t		*gs, g;

  ngs = getgroups(0, &g);
  if(!gids){
    if(ngids)
	*ngids = ngs;
    return(0);
  }

  gs = NEWP(gid_t, ngs);
  if(!gs)
    return(-1);

  getgroups(ngs, gs);

  *gids = gs;
  if(ngids)
    *ngids = ngs;

  return(0);
}

#endif	/* any PC-compiler */

/************ end of $RCSfile: sysutils.c,v $ ******************/
