/*

$Header: utils.c[1.14] Wed Sep  9 16:34:03 1992 nickel@cs.tu-berlin.de proposed $
This file is part of socket(1).
Copyright (C) 1992 by Juergen Nickelsen <nickel@cs.tu-berlin.de>
Please read the file COPYRIGHT for further details.

*/

#ifdef sgi
#define _BSD_SIGNALS
#define SIG_HANDLER_RET int
#else /* !sgi */
#define SIG_HANDLER_RET void
#endif

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifdef ISC
#define WNOHANG 1
#else
#include <sys/resource.h>
#endif
#include "globals.h"


/* Signal handler, print message and exit */
SIG_HANDLER_RET exitsig(sig)
int sig ;
{
    if (sig != SIGUSR1) {
	fprintf(stderr, "\n%s occured, exiting\n", sys_siglist[sig]) ;
    }
    exit(-sig) ;
}

/* Give usage message */
void usage()
{
    static char ustring[] =
#ifndef __riscos__
	"Usage: %s [-bcflqrvw] [-p prog] {-s | host} port\n\n" ;
#else
	"Usage: %s [-clqrvw] {-s | host} port\n\n" ;
#endif

    fprintf(stderr, ustring, progname) ;
    fprintf(stderr, "General options:\n");
    fprintf(stderr, "  -s        start %s in server mode\n", progname);
    fprintf(stderr, "  host      start %s in client mode and connect to host\n", progname);
    fprintf(stderr, "  port      use port to listen/connect\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Additional options:\n");
#ifndef __riscos__
    fprintf(stderr, "  -b        put into background mode\n");
#endif
    fprintf(stderr, "  -c        CRLF conversion\n");
#ifndef __riscos__
    fprintf(stderr, "  -f        forking child\n");
#endif
    fprintf(stderr, "  -l        loop server when client quits\n");
    fprintf(stderr, "  -q        quit after end-of-file\n");
#ifndef __riscos__
    fprintf(stderr, "  -p prog   execute prog and pipe through it\n");
#else
    fprintf(stderr, "  -p prog   execute C prog and pipe through it\n");
    fprintf(stderr, "  -P prog   execute non-C prog and pipe through it\n");
#endif
    fprintf(stderr, "  -r        read from socket only\n");
    fprintf(stderr, "  -v        be verbose\n");
    fprintf(stderr, "  -w        write to socket only\n");
    fprintf(stderr, "\n");
}

/* perror with progname */
void perror2(s)
char *s ;
{
    fprintf(stderr, "%s: ", progname) ;
    perror(s) ;
}

/* is s a number? */
int is_number(s)
char *s ;
{
    while (*s) {
	if (*s < '0' || *s > '9') {
	    return 0 ;
	}
	s++ ;
    }
    return 1 ;
}

/* set up signal handling. All except TSTP, CONT, CLD, and QUIT
 * are caught with exitsig(). */
init_signals()
{
    int i ;
#if (defined(SIG_SETMASK) && !defined(__riscos__))
  /* only with BSD signals and not for RISC OS */
    static struct sigvec svec = { exitsig, ~0, 0 } ;
#endif

    initialize_siglist() ;	/* shamelessly stolen from BASH */

    for (i = 0; i < NSIG; i++) {
	switch (i) {
#ifdef SIGTSTP
	  case SIGTSTP:
	  case SIGTTOU:
	  case SIGTTIN:
	  case SIGSTOP:
	  case SIGCONT:
	    continue ;
#endif
#if !defined (SIGCHLD) && defined (SIGCLD)
#define SIGCHLD SIGCLD
#endif
#ifdef SIGCHLD
	  case SIGCHLD:
	    continue ;
#endif
#ifdef SIGWINCH
	  case SIGWINCH:	/* it is ridiculous to exit on WINCH */
	    continue ;
#endif
	  case SIGQUIT:		/* if the user wants a core dump, */
	    continue ;		/* they can have it. */
	  default:
#if (defined(SIG_SETMASK) && !defined(__riscos__))
	    sigvec(i, &svec, NULL) ;
#else
	    signal(i, exitsig) ;
#endif
	}
    }
}

/* connect stdin with prog's stdout/stderr and stdout
 * with prog's stdin. */
void open_pipes(prog)
char *prog ;
{
#ifndef __riscos__
    int from_cld[2] ;		/* from child process */
    int to_cld[2] ;		/* to child process */

    /* create pipes */
    if (pipe(from_cld) == -1) {
	perror2("pipe") ;
	exit(errno) ;
    }
    if (pipe(to_cld) == -1) {
	perror2("pipe") ;
	exit(errno) ;
    }

    /* for child process */
    switch (fork()) {
      case 0:			/* this is the child process */
	/* connect stdin to pipe */
	close(0) ;
	close(to_cld[1]) ;
	dup2(to_cld[0], 0) ;
	close(to_cld[0]) ;
	/* connect stdout to pipe */
	close(1) ;
	close(from_cld[0]) ;
	dup2(from_cld[1], 1) ;
	/* connect stderr to pipe */
	close(2) ;
	dup2(from_cld[1], 2) ;
	close(from_cld[1]) ;
	/* call program via sh */
	execl("/bin/sh", "sh", "-c", prog, NULL) ;
	perror2("exec /bin/sh") ;
	/* terminate parent silently */
	kill(getppid(), SIGUSR1) ;
	exit(255) ;
      case -1:
	perror2("fork") ;	/* fork failed */
	exit(errno) ;
      default:			/* parent process */
	/* connect stderr to pipe */
	close(0) ;
	close(from_cld[1]) ;
	dup2(from_cld[0], 0) ;
	close(from_cld[0]) ;
	/* connect stderr to pipe */
	close(1) ;
	close(to_cld[0]) ;
	dup2(to_cld[1], 1) ;
	close(to_cld[1]) ;
    }
#else
    FILE *inpipe, *outpipe;
    if ((outpipe = fopen("Pipe:$.outpipe", "wb")) == NULL) {
      fprintf(stderr, "Couldn't create output pipe file!\n");
      exit(1);
    }
    if ((inpipe = fopen("Pipe:$.inpipe", "wb")) == NULL) {
      fprintf(stderr, "Couldn't create input pipe file!\n");
      exit(1);
    }
    fclose(inpipe);
    if ((inpipe = fopen("Pipe:$.inpipe", "rb")) == NULL) {
      fprintf(stderr, "Couldn't read input pipe file!\n");
      exit(1);
    }
    /* connect stdin to pipe */
    fclose(0);
    dup2(fileno(outpipe), 0);
    fclose(outpipe);
    /* stdin = outpipe; */
    /* connect stdout to pipe */
    fclose(1);
    dup2(fileno(inpipe), 1);
    fclose(inpipe);
    /* stdout = inpipe; */
    /* call program via sh */
    if (not_c)
      execl(prog, "<123> < Pipe:$.outpipe }<123> > Pipe:$.inpipe }", NULL);
    else
      execl(prog, "< Pipe:$.outpipe > Pipe:$.inpipe 2>&1", NULL);
#endif
}

/* remove zombie child processes */
void wait_for_children()
{
    int wret, status ;
#ifndef ISC
    struct rusage rusage ;
#endif

    /* Just do a wait, forget result */
#ifndef ISC
    while ((wret = wait3(&status, WNOHANG, &rusage)) > 0) ;
#else
    while ((wret = waitpid(-1, &status, WNOHANG)) > 0) ;
#endif
}

/* expand LF characters to CRLF and adjust *sizep */
void add_crs(from, to, sizep)
char *from, *to ;		/* *from is copied to *to */
int *sizep ;
{
    int countdown ;		/* counter */

    countdown = *sizep ;
    while (countdown) {
	if (*from == '\n') {
	    *to++ = '\r' ;
	    (*sizep)++ ;
	}
	*to++ = *from++ ;
	countdown-- ;
    }
}

/* strip CR characters from buffer and adjust *sizep */
void strip_crs(from, to, sizep)
char *from, *to ;		/* *from is copied to *to */
int *sizep ;
{

    int countdown ;		/* counter */

    countdown = *sizep ;
    while (countdown) {
	if (*from == '\r') {
	    from++ ;
	    (*sizep)-- ;
	} else {
	    *to++ = *from++ ;
	}
	countdown-- ;
    }
}

#define NULL_DEVICE "/dev/null"

/* put yourself in the background */
void background()
{
#ifndef __riscos__
    int child_pid ;		/* PID of child process */
    int nulldev_fd ;		/* file descriptor for null device */

    child_pid = fork() ;
    switch (child_pid) {
      case -1:
	perror2("fork") ;
	exit(1) ;
      case 0:
#ifdef NOSETSID
	ioctl(0, TIOCNOTTY, 0) ;
#else
	setsid() ;
#endif
	chdir("/") ;
	if ((nulldev_fd = open(NULL_DEVICE, O_RDWR, 0)) != -1) {
	    int i ;

	    for (i = 0; i < 3; i++) {
		if (isatty(i)) {
		    dup2(nulldev_fd, i) ;
		}
	    }
	    close(nulldev_fd) ;
	}
	break ;
      default:
	exit(0) ;
    }
#endif
}
