#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>
#include <netinet/in.h>
#include <netdb.h>
#ifdef	HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef	HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef	HAVE_SYS_TIME_H
#include <sys/time.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 */
#include <signal.h>
#include <sysutils.h>
#include <fileutil.h>

#ifndef	HAVE_STRERROR

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_t	rtimer = -1;

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

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

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

	rtimer = timer_create(CLOCK_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_nsec = timeval->it_value.tv_usec * 1000;
	timer_value.it_interval.tv_sec = timeval->it_interval.tv_sec;
	timer_value.it_interval.tv_nsec = timeval->it_interval.tv_usec * 1000;

	timer_ptr = &timer_value;
    }

    old_timer_ptr = (otimeval ? &old_timer_value : NULL);

    if(timer_settime(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_nsec / 1000;
	otimeval->it_interval.tv_sec = old_timer_value.it_interval.tv_sec;
	otimeval->it_interval.tv_usec = old_timer_value.it_interval.tv_nsec / 1000;
    }

    return(0);
}

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

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

    if(!timeval)
	return(0);

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

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

    if(timer_gettime(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_nsec / 1000;
	timeval->it_interval.tv_sec = timer_value.it_interval.tv_sec;
	timeval->it_interval.tv_usec = timer_value.it_interval.tv_nsec / 1000;
    }

    return(0);
}

#endif	/* !defined(HAVE_SETITIMER) */

#ifdef	_AIX
#ifndef	HAVE_TIMER_CREATE

#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	GETTIMEOFDAY	gettimeofday(&acttime, &actzone)

#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(AixPosixTimerStruct)

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

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_t	actid = 2783;

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

static struct itimerval		tmp_itimer, loc_itimer;

static struct timeval	acttime;
static struct timezone	actzone;

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;

  if(num_active_timers == 0)
    return;

  GETTIMEOFDAY;

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

static void
start_timers()
{
  int		i;

  if(!num_active_timers)
    return;

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

  do{
    i = 0;

    GETTIMEOFDAY;

    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);

  setitimer(ITIMER_REAL, &tmp_itimer, NULL);
}

static	char	handler_active = 0;

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

    handler_active = 1;

    STOP_TIMERS;

    process_timers();

    START_TIMERS;

    handler_active = 0;
  }
}


timer_t
timer_create(clockid_t id, struct sigevent *sigev)
{
  int		i;

  if(id != CLOCK_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, AixPosixTimerStruct, max_num_timers);
    else
	timers = NEWP(AixPosixTimerStruct, 1);

    if(active_timers)
	active_timers = RENEWP(active_timers, AixPosixTimerStruct,
					max_num_timers);
    else
	active_timers = NEWP(AixPosixTimerStruct, 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){
    org_handler = sigset(SIGALRM, handler);

    handler_installed = 1;
  }

  actid++;

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

  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_t 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_delete(timer_t 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_gettime(timer_t timerid, struct itimerspec *value)
{
  int	i;

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

	GETTIMEOFDAY;
	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_nsec = tmp_itimer.it_value.tv_usec * 1000;

	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_settime(
  timer_t		timerid,
  int			flags,
  struct itimerspec	*value,
  struct itimerspec	*ovalue)
{
  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_nsec / 1000;
  loc_itimer.it_value.tv_sec = value->it_value.tv_sec;
  loc_itimer.it_value.tv_usec = value->it_value.tv_nsec / 1000;

  /*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;

	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;

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

  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();

  START_TIMERS;
}

int
clock_gettime(clockid_t clock_id, struct timespec *tp)
{
  if(clock_id != CLOCK_REALTIME){
    errno = EINVAL;
    return(-1);
  }

  GETTIMEOFDAY;

  tp->tv_sec = acttime.tv_sec;
  tp->tv_nsec = acttime.tv_usec * 1000;

  return(0);
}

#endif	/* !defined(HAVE_TIMER_CREATE) */
#endif

#if !defined(WINDOWS_LIKE)

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

int	timerdone;

static void sig_handler(int sig)
{
  timerdone = 1;
}

void
ms_sleep(Int32 msecs)
{
  struct itimerval	timerval, otimerval;
  int			res;
  struct sigaction	siga, osiga;
  sigset_t		sigm, osigm;

  timerval.it_value.tv_sec = msecs / 1000;
  timerval.it_value.tv_usec = (msecs % 1000) * 1000;
  timerval.it_interval.tv_sec = 0;
  timerval.it_interval.tv_usec = 0;

  timerdone = 0;

  sigemptyset(&sigm);		/* get mask of blocked signals */
  sigaddset(&sigm, SIGALRM);		/* and unblock SIGALRM */
  sigprocmask(SIG_UNBLOCK, &sigm, &osigm);

  SETZERO(siga);		/* set signal handler, get old */
  siga.sa_handler = sig_handler;
  sigaction(SIGALRM, &siga, &osiga);

				/* set timer, get old value */
  res = setitimer(ITIMER_REAL, &timerval, &otimerval);

#if defined(HAVE_SIGHOLD) && defined(HAVE_SIGRELSE)

  while(1) {
    sighold(SIGALRM);
    if(timerdone)
	break;
    else
	sigpause(SIGALRM);
  }

  sigrelse(SIGALRM);

#else

  do{
    sigpause(SIGALRM);
  } while(!timerdone);

#endif

				/* restore old timer value */
  res = setitimer(ITIMER_REAL, &otimerval, NULL);
  sigaction(SIGALRM, &osiga, NULL);	/* signal handler */
  sigprocmask(SIG_SETMASK, &osigm, NULL);	/* and mask */
}


#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);
}

/* This is for stupid HP's HP-UX-10 header ioctl.h (Grrrrr HP !) */
#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

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);
}

int
open_tcpip_conn(UChar * hostname, UChar * servname, Int32 fallback_port)
{
  struct sockaddr_in	addr;
  struct hostent	*hp;
  struct servent	*se;
  struct linger	linger;
  int		sockfd;
  Int32		i, portnum;

  if(!servname && fallback_port < 0){
    errno = EINVAL;
    return(-1);
  }

  hp = gethostbyname(hostname);
  if(!hp)
    return(-2);

  if(!servname){
    portnum = fallback_port;
  }
  else{
    se = getservbyname(servname, "tcp");

    if(!se)
	return(-3);

    portnum = ntohs(se->s_port);
  }

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd < 0)
    return(-4);

  addr.sin_family = AF_INET;
  addr.sin_port = htons(0);
  addr.sin_addr.s_addr = htonl(0L);

  i = bind(sockfd, (struct sockaddr * ) (& addr), sizeof(addr));
  if(i){
    close(sockfd);
    return(-5);
  }

  memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
  addr.sin_port = htons(portnum);

  if(connect(sockfd, (struct sockaddr * ) (& addr), sizeof(addr)) < 0){
    close(sockfd);
    return(-6);
  }

  linger.l_onoff = 1;
  linger.l_linger = 60;
  i = setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &linger,
						sizeof (linger));

  return(sockfd);
}

Int32
set_tcpip_throughput(int sockfd)
{
    int			sock_optval, sockfd_socktype;
    Int32		sock_optlen[2], r, i;

    r = 0;

#if defined(IP_TOS) && defined(IPTOS_THROUGHPUT)
    sock_optval = IPTOS_THROUGHPUT;
    if( (i = setsockopt(sockfd, IPPROTO_IP, IP_TOS,
				(char *) &sock_optval, sizeof(int))) < 0)
	r -= 1;
#endif

	/* I use 32 Bit int for sock_optlen. See server.c for a *comment* */
    *((int *) &(sock_optlen[0])) = sizeof(int);
    i = getsockopt(sockfd, SOL_SOCKET, SO_TYPE,
			(char *) &sockfd_socktype, (int *) &(sock_optlen[0]));
    if(i){
	r -= 2;
	sockfd_socktype = -1;
    }

#ifdef	TCP_NODELAY
    sock_optval = 1;
    if(sockfd_socktype == SOCK_STREAM){
      if( (i = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
				(char *) &sock_optval, sizeof(int))) < 0)
	r -= 4;
    }
#endif

    return(r);
}

Int32
set_tcpip_keepalive(int sockfd)
{
    int			sock_optval;
    Int32		r, i;

    r = 0;

#ifdef	SO_KEEPALIVE
    sock_optval = 1;
    if( (i = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
				(char *) &sock_optval, sizeof(int))) < 0)
	r -= 1;
#endif

    return(r);
}

#endif	/* any PC-compiler */

/************ end of $RCSfile$ ******************/
