#define I_SYS
#define I_ERRNO
#define I_GETOPT
#define I_STRING
#define I_SOCKET
#define I_IOCTL
#define I_STAT
#define I_SIGNAL
#define I_TIME
#define I_WAIT
#define I_CTYPE
#include "includes.h"

#include "debug.h"


/*
 *
 * main file. Calls everything else. ;)
 * 
 * basically consists of select() and then calls things based on the select().
 *
 */
/*------------------------------------------------------------------------*/
/* truely global vars. Used everywhere. */
unsigned long current_time = 0;
int do_shutdown	 = 0;
int term_debug = 0;
int compressing = 1;
int onetime_term_socket = -1;
/*------------------------------------------------------------------------*/
/* Various global variables. Note that most of these vars are local to    */
/* this file. Where possible (i.e. where it doesn't impinge efficency) 	  */
/* this is enforced */
int remote = 0;

struct Client clients[MAX_CLIENTS];
int num_clients = 0;
int baudrate =   2400;
int auto_retrain = 0; /* Don't automatically adjust timing values */
int bytes_left = 1024; /* bytes availiable to send this second.. */
int fudge_flow = 0; /* should we generate periodic control-Q's */
int byte_shift = 0; /* So we can map less frequently uses section down to */
                    /* 0-32 */
int window_size_max = 3; /* A convenient number. 9600 baud users should probly */
				/* go to 5. */
int window_size = 1; /* Don't change this */
unsigned long packet_timeout = 70; /* wait 3.5 seconds for a packet to time out */
static int do_forceing = 1; /* weather or not to automatically transmit packets */
				/* if the window is full, and the out */
				/* buffer is empty. */
int write_noise = 0; /* whether we should print out all the the serial stuff */
				/* we get that we don't understand.. */

int seven_bit_in = 0;		/* Are we on a line that ignores the */
				/* top bit. */
int stop_bits = 2;		/* # of stop bits being used */
static long unsigned delay = 20000;
				/* This is a 20000 micro-second delay to decrease */ 
				/* cpu usage. */

int in_mask = 255;
int seven_bit_out = 0;
int out_mask = 255;
int breakout_char = '0';

int stat_modem_in = 0,
  stat_modem_out = 0,
  stat_cooked_in = 0,
  stat_cooked_out = 0,
  stat_rare_out = 0;

#ifdef SHAREDIR
char *share_p;
#endif
char *home_p;

int rshtype = 1;  /* Default to allow rsh and use login shell */

static char ownroot[256] = "";
char escapes[256];
char ignores[256];

int modem_in = 0, modem_out = 1;
/*------------------------------------------------------------------------*/
/* module function prototypes */

void main_loop(int);
void main_init(void);
void do_link_in(void);
void do_link_out(void);
void clear_buffers(struct Client *cl);
/*------------------------------------------------------------------------*/
/* main. */

void main_init(void) {
  int i;

  for (i = 0; i < MAX_CLIENTS;++i) {
    clients[i].fd = -1;
    clients[i].state = -1;
    clients[i].in_buff.data = NULL;
    clients[i].out_buff.data = NULL;
    clients[i].in_buff.alloced = 0;
    clients[i].out_buff.alloced = 0;
  }
}
				/* In case of sudden death, do some */
				/* minimal clean up. */
void sig_quit(int dummy) {
  set_block(0);
  set_block(1);
  terminal_restore(0);
  terminal_restore(1);
  set_block(modem_in);
  set_block(modem_out);
  exit(0);
}
				/* Drop a core. */
void sig_core(int dummy) {
  set_block(0);
  set_block(1);
  terminal_restore(0);
  terminal_restore(1);
  set_block(modem_in);
  set_block(modem_out);
  abort();
}
				/* We ignore this signal. There was */
				/* some problem with it on linux. A */
				/* bit odd, and I can't track it down. */
				/* I suspect it is a bad shell. So we */
				/* just ignore it. */
void sig_ignore(int dummy) {
  signal(SIGALRM, sig_ignore);
  signal(SIGPIPE, sig_ignore);
}

void sig_child(int dummy) {
  int p, i;
#if defined(SVR4) || defined(DYNIXPTX)
  int stat_loc;

  p = waitpid((pid_t)-1, &stat_loc, WNOHANG);
#else
  p = wait3(0, WNOHANG, 0);
#endif /* SVR4 */
#ifndef SYSV
  signal(SIGCHLD, sig_child);
#endif /* SYSV */

  if (p < 1) return;
  DEBUG_MAIN(stderr, "%s:sig child kicked\n", term_server);
  for (i = 0; i < MAX_CLIENTS;++i) {
    if (clients[i].pid == p && clients[i].state > 0) {
#ifdef __hpux  /* ++kay: let term read until EOF */
      clients[i].state = 2;
#endif
      return;
    }
  }
  /* Hmm. child that we don't know about died!?!?! */
  if (dummy)
    dummy = 0;
}

void read_rc(char *p) {
  FILE *f = NULL;
  char file[256], *path = NULL;
  static int Chdir[256];
  static int seven_bit=0, login_type = 1;
#define INT_TYPE 1
#define CHAR_TYPE 2
#define RANGE_TYPE 3
#define LONG_TYPE 4
  static char int_type=INT_TYPE, char_type=CHAR_TYPE, range_type=RANGE_TYPE,
	long_type=LONG_TYPE;
#define RC_OPTIONS_SIZE 69
  int m, n;
  static char *rc_options[RC_OPTIONS_SIZE] = {
	"compress",	& int_type,	(char *) &compressing,
	"breakout",	& int_type,	(char *) &breakout_char,
	"remote",	& int_type,	(char *) &remote,
	"chdir",	& char_type,	(char *) Chdir,
	"escape",	& range_type,	(char *) escapes,
	"ignore",	& range_type,	(char *) ignores,
	"baudrate",	& int_type,	(char *) &baudrate,
	"retrain",	& int_type,	(char *) &auto_retrain,
	"shift",	& int_type,	(char *) &byte_shift,
	"window",	& int_type,	(char *) &window_size_max,
	"timeout",	& int_type,	(char *) &packet_timeout,
	"force",	& int_type,	(char *) &do_forceing,
	"noise",	& int_type,	(char *) &write_noise,	
	"sevenbit",	& int_type,	(char *) &seven_bit,	
	"seven_out",	& int_type,	(char *) &seven_bit_out,
	"seven_in",	& int_type,	(char *) &seven_bit_in,
	"flowcontrol",	& int_type,	(char *) &fudge_flow,
	"login",	& int_type,	(char *) &login_type,	
	"denyrsh",	& int_type,	(char *) &rshtype,
	"chroot",	& char_type,	(char *) ownroot,
	"nonblock",	& int_type,	(char *) &use_nonblock,
	"delay",	& long_type,	(char *) &delay,
	"stopbits",	& int_type,	(char *) &stop_bits
      };

  home_p = getenv( "TERMDIR" );
  if (!home_p) home_p = getenv( "HOME" );

#ifdef SHAREDIR
#ifndef USERSHARE
  share_p = getenv( "TERMSHARE" );
#endif
  if (!share_p) share_p = SHAREDIR ":/usr/lib/term:/etc" ;

  set_share_mode();
  if(share == -1){
#ifndef USERSHARE
    share = (getgid() != getegid());
#else
    share = 2;
#endif
    set_share_mode();
  }

#ifndef USERSHARE
  if(!share){
#endif
#endif
    setgid(getgid());
    setuid(getuid());
#ifdef SHAREDIR
#ifndef USERSHARE
  }
#endif

  if(share || ! home_p)
    path = share_p;
  else
#endif
    path = home_p;

#if defined(_POSIX_SAVED_IDS) && defined(SHAREDIR)
  if (share && savedeid >= 0) {
#ifdef USERSHARE
    setuid(savedeid);
#else
    setgid(savedeid);
#endif
  }
#endif
  for(;path != NULL; path = strchr(path,':')){
    if (*path == ':') path++;
    strcpy(file,path);
    if(strchr(file,':') != NULL )
       *strchr(file,':') = '\0';

#ifdef SHAREDIR
    if(! share && home_p != NULL) 
#endif
      strcat(file, "/.term");

    strcat(file, "/termrc");

    if (p != NULL) if ( p[0] )
      sprintf(&file[strlen(file)], ".%s",p);
    if (! eaccess(file, R_OK)) break;
  };
  if (path != NULL) {
    fprintf(stderr,"Reading file:  %s\r\n",file);
    f = fopen(file, "r");
  }

#if defined(_POSIX_SAVED_IDS) && defined(SHAREDIR)
  if (share) {
#ifdef USERSHARE
    setuid(getuid());
#else
    setgid(getgid());
#endif
  }
#endif
  if (path == NULL) return;

  if (!f) return;
  
  Chdir[0] = '\0';
  while (!feof(f)) {
    char line[256];

    fgets(line,	256,	f);
				/* skip blank lines + comments. */
    if (!line[0] || line[0] == '\n' || line[0] == '#')
      continue;
			

    for(m = 0;m < RC_OPTIONS_SIZE;m += 3)
      if ((n = strlen((char *) rc_options[m])) < strlen(line))
        if (! strncmp((char *) rc_options[m], line, n)  
            && isspace(line[n])) {

      while (isspace(line[++n]));
 
      switch ( (int) *rc_options[m+1] ){
        case CHAR_TYPE:
        {
          int i;
          for(i=0;i<256 && ! isspace(line[n]) && line[n];++i){
            n++;
            if ( (rc_options[m+2][i] = line[n]) == '\\' && line[n+1] )
              rc_options[m+2][i] = line[n++];
          };
          break;
        }
        case RANGE_TYPE:
        {
          char *p;
          int i,j;
      
          if(strchr(&line[n],'#'))
            *strchr(&line[n],'#') = '\0';
          i = atoi(&line[n]);	/* get the number following. */
          if (i < 0 || i > 255) {	/* check for sanity. */
            fprintf(stderr, "Invalid escape/ignore %d in termrc\n", i);
            continue;
          }
          if ((p = strchr(&line[n], '-')) != NULL) { /* See if this is a */
  					     /* range.. */
            while (isspace(*++p));	/* skip whitespace. Note that it */
				/* automatically skips the '-'. */
            j = atoi(p);		/* if it is, then get the second number. */
            if (j < 0 || j > 255) {	/* sanity check again. */
              fprintf(stderr, "Invalid range limit %d in termrc\n", j);
              continue;
            }
            for (;i != j; i = (i+1) & 255) /* Ok. mark all the characters */
				/* in the range as being escaped. */
              rc_options[m+2][i] = '\1';   
          }
          else rc_options[m+2][i] = '\1';	/* else just set the one character as */
				/* to be escaped. */
          break;
        }

        case INT_TYPE:
        {
          int *o;

	/* This reads an integer with "on" == 1, "off" == 0, and default == 1 */

          o = (int *) rc_options[m+2];
          if (isdigit(line[n]))
            *o = atoi(&line[n]);
          else
            *o = !!strncmp(&line[n], "off", 3);
          break;
        }
        case LONG_TYPE:
	{
	  if(strchr(&line[n],'#'))
	    *strchr(&line[n],'#') = '\0';
	  sscanf(&line[n],"%lu",(unsigned long *) rc_options[m+2]); 
          break;
        }
      };
      break;	
    };
    if (m == RC_OPTIONS_SIZE){
      if(! strncmp("remote",line,6)){
         remote = 1;
      }else{
        fprintf(stderr, "Unrecognized line in %s\r\n",file);
        fprintf(stderr, "\t%s\r\n",line);
      }
    }
  }
  if(f != NULL) fclose(f);

  if (Chdir[0] != '\0')
    chdir((char *) Chdir);
  if (baudrate < 300 && baudrate)
    baudrate = 300;
  if (fudge_flow < 0)
    fudge_flow = 0;
  if (! login_type)
    rshtype = -1;
  if (byte_shift < 0 || byte_shift > 255)
    byte_shift = 0;
  if (packet_timeout < 10 || packet_timeout > 1000){
    auto_retrain = 1;
    packet_timeout = 70;
  } 
  if(window_size_max < 1 || window_size_max > N_PACKETS / 2){
    window_size_max = N_PACKETS / 2;
    auto_retrain = 1;
  }

  if(seven_bit){
    seven_bit_in = 1;
    seven_bit_out = 1;
  }

  if ( seven_bit_out ) {
    extern int tok_byte_mask_out, tok_byte_width_out;
    out_mask = 127;
    tok_byte_mask_out = 127;
    tok_byte_width_out = 7;
  }

  if (seven_bit_in) {
    extern int tok_byte_mask_in, tok_byte_width_in;
    in_mask = 127;
    tok_byte_mask_in = 127;
    tok_byte_width_in = 7;
  }
#if 0 /* Usefull for debugging */
  for(m = 0;m < RC_OPTIONS_SIZE;m += 3)
    switch ( (int) *rc_options[m+1] ){
    case INT_TYPE:
    {
      int *o;
      o = (int *) rc_options[m+2];
      fprintf(stderr,"%s %d\r\n",rc_options[m],*o);
      break;
    }
  }
#endif
}

void main(int argc, char *argv[]) 
{
  extern char *optarg;
  extern int optind, opterr;
  
  int s, c, i;
  char *p, protected_directory[109];
#ifdef SHAREDIR
  char symbolic_link[109];
  int umask_old;
#endif
  char sock_unix[109];

  fprintf(stderr, "Term version: %s\r\n",str_version(VERSION));

  /* initalize character escapeing. */
  for (i = 0; i < 256;i++) {
    ignores[i] = 0;
    escapes[i] = 0;
  }

  escapes['^'] = 1;

  main_init();

#ifdef SHAREDIR
  while ((c = getopt (argc, argv, "Aac:d:f:l:n:ors:t:v:w:P:S:")) !=EOF)
    switch(c) {
    case 'S':
      share =(!strcmp(optarg,"on"));
      break;
  };optind=1;   
#endif

  if (!term_server) {
    term_server = "";
  }

  /* read in the termrc file. */
  read_rc(0);

#ifdef SHAREDIR
  if(share == 1)
    fprintf(stderr, "Using shared mode.\r\n");
#endif

  /* then check env variables. */
  setbuf(stderr, 0);
  if (getenv("BAUDRATE")) {
    baudrate = atoi(getenv("BAUDRATE"));
    if ( baudrate < 300 && baudrate)
      baudrate = 300;
  }
  /* Then check command line options */
  /*
    The code to parse the command line was written by the
    one and only Muhammad Saggaf. If you have any question
    about Linux, networking, Unix, or life in general, 
    don't ask him mate! :). Chances are he doesn't know the 
    answer.
    */
  
  opterr = 0;
  
  while ((c = getopt (argc, argv, "Aac:d:f:l:n:ors:t:v:w:P:S:")) !=EOF)
    switch(c) {
    case 'A':
      auto_retrain = 1;
      break;
    case 'a':
      {
	extern int tok_byte_mask_out, tok_byte_width_out,
	  tok_byte_mask_in, tok_byte_width_in;
	tok_byte_mask_out = tok_byte_mask_in = 127;
	tok_byte_width_out = tok_byte_width_in = 7;

	seven_bit_in = seven_bit_out = 1;
	in_mask = out_mask = 127;
	break;
      }
    case 'f': 
      fudge_flow = atoi(optarg);
      if (fudge_flow < 0) fudge_flow = 0;
      break;
    case 'l':
      i = open(optarg, O_RDWR | O_CREAT | O_TRUNC, 0644);
      if (i < 0) {
	perror("open");
	fprintf(stderr, "Unable to open log file %s\r\n", optarg);
	break;
      }
      if (i != 2) {
	close(2);		/* Just to make sure.. */
	dup2(i, 2);
	close(i);
      }
      break;
    case 'o': do_forceing = 1; break;      
    case 'r': remote = 1; break;
    case 't':
      packet_timeout = atoi(optarg);
      if (packet_timeout < 10 || packet_timeout > 1000) {
	fprintf(stderr, "Invalid timeout value (%lu).\r\n", packet_timeout);
	packet_timeout = 70;
      }
      break;
    case 'v':

				/* add a device to the list. */
      i = open(optarg, O_RDWR);
      if (i < 0) {
	perror("open");
	fprintf(stderr,"Unable to open modem device %s\r\n", optarg);
	break;
      }
      if(modem_in>=0) close(modem_in);
      if(modem_out>=0) close(modem_out);
      modem_in = i;
      modem_out = i;
      break;
    case 'w':
      window_size_max = atoi(optarg);
      if (window_size_max < 1 || window_size_max > N_PACKETS / 2)  {
	fprintf(stderr, "Invalid window size %d\r\n", window_size_max);
	window_size_max = 2;
      }
      break;
    case 'd': 
      term_debug = atoi(optarg);
      fprintf(stderr, "Debugging = %x\n", term_debug);
      break;
    case 'c':
      if (!strcmp(optarg, "off"))
	compressing = 0;
      break;
    case 'n':
      if (!strcmp(optarg, "on"))
	write_noise = 1;
      else write_noise = 0;
      break;
    case 's':
      baudrate = atoi(optarg);
      if (baudrate < 300 && baudrate) {
	fprintf(stderr, "baudrate set too low. Reset to 300\r\n");
	baudrate = 300;
      }
      break;
    case 'S':
      break;
    default:
      fprintf(stderr, "unrecognized or incomplete argument '%s'. Exiting\r\n",  argv[optind-1]);
      exit(-1);
    }
  
  if (optind < argc)
    term_server = argv[optind];
  
  for (s = 0; s < MAX_CLIENTS;++s) {
    clients[s].fd = -1;
  }

  if (!term_server) {
    term_server = "";
  }
				/* Read in a specific termrc. */
  if (term_server[0])
    read_rc(term_server);

#ifdef SHAREDIR
  if(!share){
#endif
    if(!home_p) home_p = "/tmp";
    for(p = home_p;p != NULL; p = strchr(p,':')){
      if (*p == ':') p++;
      strcpy(protected_directory,p);
      if (strchr(protected_directory,':') != NULL)
        *strchr(protected_directory, ':') = '\0';
      strcat(protected_directory,"/.term");
      if (access(protected_directory, X_OK & W_OK) < 0) continue;
      break;
    };
    if (p == NULL) {
      fprintf(stderr, "Failed to find term private directory.\r\n");
      exit(1);
    }
    mkdir(protected_directory, 0700);
    sprintf(sock_unix,"%s/socket%s",protected_directory,term_server);
#ifdef SHAREDIR
  }else{
#ifdef _POSIX_SAVED_IDS
    if (savedeid >= 0) 
#ifdef USERSHARE
      setuid(savedeid);
#else
      setgid(savedeid);
#endif
#endif
/* set the protection to be explicitly what I define below */
    umask_old=umask(0);

    for(p = share_p;p != NULL; p = strchr(p,':')){
      if (*p == ':') p++;
      strcpy(protected_directory,p);
      if (strchr(protected_directory,':') != NULL)
        *strchr(protected_directory, ':') = '\0';
      if (eaccess(protected_directory, X_OK & W_OK) < 0) continue;
      break;
    };
    if (p == NULL) {
      fprintf(stderr, "Failed to find term shared directory.\r\n");
      exit(1);
    }

/* If the needed directories don't exist, create them */
    strcat(protected_directory,"/tmp");
    mkdir(protected_directory,0777);

/* It seems that the protection on sock_unix is ignored, so we need a 
   a protected directory. */
    sprintf(protected_directory,"%s/%s%s", protected_directory,"private",term_server);
    sprintf(sock_unix,"%s/socket",protected_directory);

/* Remove any old sockets */
    unlink(sock_unix);

/* Create a link to "." as ".term" for backwards compatability */
    sprintf(symbolic_link,"%s/.term",share_p);
    symlink("./tmp/private",symbolic_link);

/* Remove and recreate the private directory, so we can be sure we can see it */
    rmdir(protected_directory);
#ifndef USERSHARE
    mkdir(protected_directory, 0770|S_ISGID);
#else
    mkdir(protected_directory, 0700|S_ISUID);
#endif
  
/* Restore the mask to what the user wants to use with tupload. */
    umask(umask_old);
  }
#endif
  
  if ((s = bind_unix(sock_unix)) < 0) {
    exit(1);
  }

#ifdef SHAREDIR
  setuid(getuid());
  setgid(getgid());
#endif

  /* init modules */
  
  serial_init();
  compress_init();
  update_time();
  
#ifdef SYSV
  sigset(SIGCHLD, sig_child);
#else
  signal(SIGCHLD, sig_child);
#endif /* SYSV */
  signal(SIGHUP, sig_quit);
  signal(SIGPIPE, sig_ignore);
  signal(SIGINT, sig_quit);
  signal(SIGQUIT, sig_quit);
  signal(SIGIOT, sig_core);
  signal(SIGSEGV, sig_core);
  signal(SIGALRM, sig_ignore);

  terminal_save(0);
  set_nonblock(s);
  set_nonblock(modem_in);
  set_nonblock(modem_out);
  terminal_raw(modem_in);
  terminal_raw(modem_out);

  if (ownroot[0]) {
    if (!chroot(ownroot))
      perror("chroot");
  }
  
  main_loop(s);

  set_block(modem_in);
  set_block(modem_out);
  terminal_restore(0);
  terminal_restore(1);

  set_block(s);
}

/*-----------------------------------------------------------------------*/
void check_client(int cl, int ret) {
  DEBUG_MAIN(stderr, "%s: termerrno == %d\n", term_server, termerrno);
  if (!termerrno) return;
  if (clients[cl].state == 3 ) {
    DEBUG_FP(stderr, "%s:truncating out_buff\n", term_server);
    clients[cl].out_buff.size = 0;
    clients[cl].out_buff.start = clients[cl].out_buff.end;
    return;
  }
#if 0
  if (ret < 0)
    perror("client gave this");
#endif
				/* Ok. Close the descriptor. */
  if(clients[cl].fd>=0) close(clients[cl].fd);
  clients[cl].fd = -1;
				/* And go to state 2. */
  clients[cl].state = 2;
}

int new_client(void) {
  int j;

  for (j = remote; j < MAX_CLIENTS;j+=2)
    if (clients[j].fd < 0 && clients[j].state < 0) break;

  if (j == MAX_CLIENTS) return -1; /* not maximum clients */

  DEBUG_FP(stderr, "%s: new client %d.\n", term_server, j);
  
  clear_buffers(&clients[j]);
  
  clients[j].type = T_SMART | T_RDFILE | T_WRFILE;
  clients[j].dump_count = 0;
  clients[j].cl_type = CL_SOCKET;
  clients[j].compress = compressing;
  clients[j].state = 1;
  clients[j].c_state = 0;
  clients[j].number = j;
  clients[j].priority = 0;
  clients[j].name[0] = 0;
  return j;
}
/*------------------------------------------------------------------------*/
/* Main loop. Hangs around waiting for things to get ready, and calls to  */
/* approroprite routines. 						  */

void main_loop(int socket) {
  struct timeval timeout;
  fd_set reads, writes, excepts;
  int i, j, k, empty = 1, csocket = -1;
  int max = 0;

  while (!do_shutdown) {

/* If the serial out buffer is empty, try and put something in it */
    if (bytes_left && !serial_out.size) {
      DEBUG_FP(stderr, "%s: doing serial out\n",
	       term_server);
      do_serial_out(0);
      if (!serial_out.size)
	do_link_out();
      if (!serial_out.size) 
	do_serial_out(0);
      DEBUG_FP(stderr, "%s:  serial out size %d\n",
	       term_server, serial_out.size);
    }
    
/* Set up client stuff */
/* We select to read if: */
/*	The input buffer is empty */
/* We select to write if: */
/*      The output buffer is not empty */
/*      We're waiting for a connection to complete (state == 5) */

    FD_ZERO(&reads);
    FD_ZERO(&writes);
    FD_ZERO(&excepts);
    
    if (p_in_num && empty)
      do_link_in();
    
    for (i = 0; i < MAX_CLIENTS;++i) {
				/* If it's closeing down, and the */
				/* buffers are empty, then kill it. */
      if ((clients[i].state == 3 || clients[i].state == 4) &&
	  !clients[i].in_buff.size && !clients[i].out_buff.size) {
	DEBUG_FP(stderr, "%s:real close %d %d\n", term_server , i,
		 clients[i].state);
	if(clients[i].fd>0) close(clients[i].fd);
	clients[i].fd = -1;
	if (clients[i].state == 3)
	  clients[i].state = -1; 
	else clients[i].state = 1;
	continue;
      }
				/* If it's a file, we don't need to */
				/* select() on it. */
      if (clients[i].cl_type == CL_FILE) continue;
      if (clients[i].fd < 0) continue; /* If it's not a file, and */
				       /* there is no fd, then no */
				       /* select(). */
      if (!clients[i].in_buff.size && (clients[i].type & T_RDFILE) &&
	  clients[i].state == 1) { 
	FD_SET(clients[i].fd, &reads);
      }
      if ((clients[i].out_buff.size && (clients[i].type & T_WRFILE)) ||
	  (clients[i].state == 5)) {
	FD_SET(clients[i].fd, &writes);
      }
      FD_SET(clients[i].fd, &excepts);
      if (max < clients[i].fd) max = clients[i].fd;
    }
    
/* Select for socket and modem */
/* We select read for socket if we aren't at the maxmimum number */
/* of clients */
    if (num_clients < MAX_CLIENTS) {
      FD_SET(socket, &reads);
      if (max < socket) max = socket;
      if(onetime_term_socket >=0){
        FD_SET(onetime_term_socket, &reads);
        if (max < onetime_term_socket) max = onetime_term_socket;
      };
    }

/* We select for read on the modem if the serial in buffer is empty */
    if (!serial_in.size) {
      FD_SET(modem_in, &reads);
      if (max < modem_in) max = modem_in;
    }

/* Now if there is anything in the serial out buffer, select for writing */

    if (serial_out.size && bytes_left) {
      DEBUG_FP(stderr, "%s: dso: fd set\n",
	       term_server);
      FD_SET(modem_out, &writes);
      if (max < modem_out) max = modem_out;
    }

				/* This is only for my own purposes. */

/* This is intended as a short delay to help decrease CPU usage. */
    timeout.tv_sec = 0;
    timeout.tv_usec = (unsigned long int) delay;
    select(0, NULL, NULL, NULL, &timeout);

/* Set the timeout value for select(). */
    timeout.tv_sec = 0;
    timeout.tv_usec = 500000; /* 0.5 seconds */

/* do select() */	
    if (select(max+1, &reads, &writes, &excepts, &timeout) < 0) {
				/* This is perfectly normal. Things */
				/* that send signals will cause select */
				/* to exit with an error. */
#if 0
      perror("select");
#endif
      continue;
    }	

/* Update current_time. This is maintained in 20th s of a second */
    update_time();

/* start checking to see whats ready and whats not */
/* Can we read from modem  ?? */
    if (!serial_in.size && FD_ISSET(modem_in , &reads)) {
      j = read_into_buff(modem_in, &serial_in, 0);
      if ( j < 0 && errno != ERR_BLOCK) {
	perror("read from modem");
	return;
      }
      else stat_modem_in += j;
    }
    
    if (serial_in.size) 
      do_serial_in();

/* Ok. Can we write to modem ??? */
    if (FD_ISSET(modem_out, &writes) && bytes_left) {
      
      int t = bytes_left;
      if (t > serial_out.size)
	t = serial_out.size;
      DEBUG_FP(stderr, "%s: dso: write from buff\n",
	       term_server);
      j = write_from_buff(modem_out, &serial_out, t);
      if (j < 1 && termerrno != 1) {
	perror("write to modem");
	return;
      }	  
      else {
	stat_modem_out += j;
	if (baudrate)
	  bytes_left -= j;
      }
    }

/* test for new client */

    for(k=0;k<2;k++){
      switch (k){
        case 0:
          csocket = onetime_term_socket;
          break;
        case 1:
        default:
          csocket = socket;
          break;
      };
      if(csocket<0) continue;
      if (FD_ISSET(csocket, &reads)) { /* try for a connect. */

#if defined(NO_UNIX_DOMAIN) || defined(TERM_NFS_DIR)
        struct sockaddr_in dummy;
#else
        struct sockaddr_un dummy;
#endif

#if defined(STREAMS_PIPE) || defined(X_STREAMS_PIPE)
 	i = CheckClientConnection(csocket);
	if (i != -2) {			/* we have a streams pipe */
		if (i == -1) {
			fprintf(stderr, "can't add client\n");
			continue;
		} /* else fall through to add new client after "accept ..." */
	} else 		/* not a streams pipe */
#endif
        {
          int din = sizeof(dummy);
          i = accept(csocket , (struct sockaddr *) &dummy, &din);
        };
        if(!k){
          onetime_term_socket = -1;
          close(csocket);
        };
        
        if (i >= 0) { /* a new client */
	  j = new_client();
	  clients[j].fd = i;
	  clients[j].cl_type = CL_SOCKET;
	  set_nonblock(i);
        }
        else if (termerrno != 1) {
#if 0
	  perror("accept");
#endif
        }
      }
    };
/* test for data being read from clients */
    for (i = 0; i < MAX_CLIENTS;++i) {
      if (clients[i].fd < 0) continue;
      if (clients[i].in_buff.size) continue;
      switch (clients[i].cl_type) {
      case CL_CHILD:		/* fall through */
      case CL_SOCKET:		/* fall through */
#if defined(STREAMS_PIPE) || defined(X_STREAMS_PIPE)
      case CL_SPIPE:
#endif
	if (!FD_ISSET(clients[i].fd, &reads) &&
	    !FD_ISSET(clients[i].fd, &excepts))
	  continue;
				/* Fall through. */
      case CL_FILE:		
	if (!(clients[i].type & T_RDFILE)) continue;
	j = read_into_buff(clients[i].fd, &clients[i].in_buff, 0); 
	DEBUG_FP(stderr, "%s:read %d bytes from client %d\n",
		 term_server , j, i);
				/* Did this client start sending?? */
	DEBUG_FP(stderr, "%s:j %d, inf_buff.size %d\n",
		 term_server , j, clients[i].in_buff.size);

				/* Hmmm. Something errored. Lets take */
				/* a look... */
	if (j <= 0)
	  check_client(i, j);
	if (termerrno == 1)
	  check_client(i, j);
	break;
      case CL_BOUND:
	if (!FD_ISSET(clients[i].fd, &reads)) continue;
	{
	  un_char num[10]; int k;
				/* We have an accept ready... */
	  clients[i].type &= ~T_RDFILE; /* Don't try reading it untill */
				/* the accept is done. */
	  sprintf((char *) num, "%d", i);
	  for (k =0 ;num[k];++k)
	    add_to_buffer(&clients[i].in_buff, num[k]);
	  add_to_buffer(&clients[i].in_buff, 0);
	}
	break;
      }
    } /* for clients loop */
    
#ifdef CONN_NONBLOCK
/* Test pending connections. */
    for (i = 0; i < MAX_CLIENTS; ++i) {
      if (clients[i].state == 5) {
	char	dummy;

	if (FD_ISSET(clients[i].fd, &writes)) {
	  ret_ok(&clients[i], 0);	/* safe to call from here? */
	  clients[i].state = 1;
	}
	else if (read(clients[i].fd, &dummy, 0) < 0) {
	  switch (errno) {
          case ERR_BLOCK:
#ifdef EINPROGRESS
	  case EINPROGRESS:
#endif
	  case ENOTCONN:	/* no status, but maybe timed out */
	    if (current_time >= clients[i].timeout) {
	      errno = ETIMEDOUT;
	      ret_fail(&clients[i], 0, 1, "connect() timed out");
	      perror("async connect()");
	      clients[i].state = 2;
	    }
	    break;
	  default:
	    ret_fail(&clients[i], 0, 1, "connect() timed out");
	    perror("async connect()");
	    clients[i].state = 2;
	    break;
	  }
	}
      }
    }
#endif

/* test for data being send to clients */
    empty = 1;
    for (i = 0; i < MAX_CLIENTS;++i) {
      if (clients[i].fd < 0) continue;
      if (!clients[i].out_buff.size) continue;
      switch (clients[i].cl_type) {
      case CL_CHILD:
      case CL_SOCKET:
#if defined(STREAMS_PIPE) || defined(X_STREAMS_PIPE)
      case CL_SPIPE:
#endif
	if  (!FD_ISSET(clients[i].fd, &writes)) continue;
      case CL_FILE:
	if (!(clients[i].type & T_WRFILE)) continue;
				/* something there! :) */
	j = write_from_buff(clients[i].fd, &clients[i].out_buff,0);
	DEBUG_FP(stderr, "%s:write %d bytes to client %d\n", 
		 term_server, j, i);
	if (j <= 0)
	  check_client(i, j);
	else stat_cooked_in += j;
	break;
      case CL_BOUND:
	break;
      }				/* switch */
    }				/* for clients loop */
    
  } /* while loop */
  
} /* function */

