#include <conf.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#ifdef	HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef	HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef	HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <sys/types.h>
#include <signal.h>
#ifdef	HAVE_SYS_TIME_H
#ifdef	TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#include <x_types.h>
#include <fileutil.h>
#include <utils.h>
#include <sysutils.h>
#include <x_regex.h>
#include "backup.h"

static UChar	buf[50];

Int32
read_ext(int fd, UChar * buf, Int32 num, UChar mode)
{
  Int32		i, n = 0;
  fd_set	fds;
  sigset_t	sigs, osigs;
  int		e;
  UChar		options;

  options = mode & READ_OPTMASK;
  mode &= READ_MODEMASK;

  if(num < 1)
    return(0);

  while(num > 0){
    if(!(options & READ_NOSELECT)){
      do{
	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	i = select(fd + 1, &fds, NULL, NULL, NULL);
	if(!i)
	  continue;
	if(i < 0 && errno == EINTR){
	  errno = 0;
	  continue;
	}
      } while(i < 1);
    }

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGALRM);

    sigprocmask(SIG_BLOCK, &sigs, &osigs);

    i = read(fd, buf, num);
    e = errno;

    sigprocmask(SIG_SETMASK, &osigs, NULL);

    if(i < 1){
	if(e != EINTR){
	  switch(mode){
	    case READ_SPLIT:
		return(1);
		break;

	    case READ_FORCED:
		return(n);
		break;

	    case READ_UNINTR:
		return(i);
		break;
	  }
	}
	errno = 0;
	i = 0;
    }

    if(mode != READ_SPLIT)
	n += i;

    num -= i;
    buf += i;

    if(mode == READ_UNINTR && i > 0)
	return(n);
  }

  switch(mode){
    case READ_SPLIT:
	return(0);

    case READ_FORCED:
    case READ_UNINTR:
	return(n);
  }

  return(0);
}


Int32
write_ext(int fd, UChar * buf, Int32 num, UChar mode)
{
  Int32		i, n = 0;
  fd_set	fds;
  sigset_t	sigs, osigs;
  int		e;
  UChar		options;

  options = mode & WRITE_OPTMASK;
  mode &= WRITE_MODEMASK;

  if(num < 1)
    return(0);

  while(num > 0){
    if(!(options & WRITE_NOSELECT)){
      do{
	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	i = select(fd + 1, NULL, &fds, NULL, NULL);
	if(!i)
	  continue;
	if(i < 0 && errno == EINTR){
	  errno = 0;
	  continue;
	}
      } while(i < 1);
    }

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGALRM);

    sigprocmask(SIG_BLOCK, &sigs, &osigs);

    i = write(fd, buf, num);
    e = errno;

    sigprocmask(SIG_SETMASK, &osigs, NULL);

    if(i < 1){
      if(e != EINTR){
	switch(mode){
	  case WRITE_SPLIT:
	    return(1);
	    break;

	  case WRITE_UNINTR:
	    return(i);
	    break;

	  case WRITE_FORCED:
	    return(n);
	    break;
	}
      }

      errno = 0;
      i = 0;
    }

    if(mode != WRITE_SPLIT)
	n += i;

    num -= i;
    buf += i;

    if(mode == WRITE_UNINTR && i > 0)
	break;
  }

  switch(mode){
    case WRITE_SPLIT:
	return(0);
	break;

    case WRITE_UNINTR:
    case WRITE_FORCED:
	return(n);
	break;
  }

  return(0);
}

pid_t
waitpid_forced(pid_t pid, int * status, int options)
{
  int	p;

  forever{
    p = waitpid(pid, status, options);

    if(p == pid || (errno != EINTR
#ifdef	ERESTARTSYS
				 && errno != ERESTARTSYS
#endif
							))
	break;

    errno = 0;
  }

  return(p);
}

int
fork_forced()
{
  int		pid, proc_tries;
  sigset_t	sos, osos;

  proc_tries = 0;
  do{
    sigemptyset(&sos);
    sigaddset(&sos, SIGALRM);

    sigprocmask(SIG_BLOCK, &sos, &osos);

    pid = fork();

    if(pid < 0){
	if(errno == EAGAIN){
	  errno = 0;

	  sigprocmask(SIG_SETMASK, &osos, NULL);

	  proc_tries++;
	  if(proc_tries > PROC_MAX_TRIES){
	    return(pid);
	  }

	  ms_sleep(1000 * 1);
	}
	else{
	  return(pid);
	}
    }

    if(pid)
      sigprocmask(SIG_SETMASK, &osos, NULL);

  } while(pid < 0);

  return(pid);
}

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

      signal(SIGTERM, SIG_IGN);
      signal(SIGINT, SIG_IGN);

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

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

    fd = pp[1];
    *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];
    *pidp = pid;
  }

  return(fd);
}  

void
clr_timer()
{
  struct itimerval	itv;

  memset(&itv, 0, sizeof(itv));
  setitimer(ITIMER_REAL, &itv, NULL);

  signal(SIGALRM, SIG_DFL);
}

static Int32
fget_char_forced(FILE * fp, UChar * c)
{
  Int32		r;

  forever{
    r = fread(c, sizeof(UChar), 1, fp);
    if(r < 1){
      if(errno != EINTR){
	fclose(fp);
	return((Int32) EOF);
      }
      errno = 0;
      continue;
    }

    break;
  }

  return(1);
}

Int32
fscanwordq_forced(
   FILE         *fp,
   UChar         *string)
{
   Int32	i = 0, quote = 0;
   UChar 	a = '\0', prev;

   do{
	prev = a;
	if(fget_char_forced(fp, &a) < 1){
            fclose(fp);
            return((Int32) EOF);
        }
   } while(isspace(a));

   string[0] = a;

   if(a == '\\')
	i--;
   if(a == '\"'){
	quote = 1;
	i--;
   }

   do{
        i++;
	prev = a;
        if(fget_char_forced(fp, &a) < 1){
            fclose(fp);
	    string[i] = '\0';
            return(NO_ERROR);
        }
	if(a == '\\'){
	    if(prev != a){
		prev = a;
		if(fget_char_forced(fp, &a) < 1){
		    fclose(fp);
		    string[i] = '\0';
		    return(NO_ERROR);
		}
	    }
	}
	else{
	    do{
		if(a == '\"' && prev != '\\'){
		    prev = a;
		    quote = !quote;
        	    if(fget_char_forced(fp, &a) < 1){
			fclose(fp);
			string[i] = '\0';
			return(NO_ERROR);
        	    }
		}
	    } while(a == '\"' && prev != '\\');
	}
        string[i] = a;
	if(a == '\\')
	    a = '\0';
   } while(!isspace(a) || quote);

   string[i] = '\0';

   return(NO_ERROR);
}

UChar *
Real64_to_intstr(Real64 d, UChar * retstr)
{
  UChar		str[70];

  UChar		*cptr;
  Real64	p10, d_10;
  int		c;

  cptr = (retstr ? retstr : str);

  if(d >= 1.0e+40){
    sprintf(cptr, "%lg <integer representation not possible>",
				(double) d);
    return(retstr ? retstr : (UChar *) strdup(str));
  }

  d += 0.5;
  if(d < 1.0){
    strcpy(cptr, "0");
    return(retstr ? retstr : (UChar *) strdup(str));
  }

  d_10 = d / 10.0;
  for(p10 = 1.0; p10 <= d_10; p10 *= 10.0);

  while(p10 >= 1.0){
    c = (int)(d / p10);
    *(cptr++) = '0' + c;
    d -= (Real64) c * p10;
    p10 /= 10.0;
  }

  *cptr = '\0';

  return(retstr ? retstr : (UChar *) strdup(str));
}

UChar *
time_t_to_intstr(time_t d, UChar * retstr)
{
  UChar		str[70];

  UChar		*cptr;
  time_t	p10, d_10;
  int		c, n;

  p10 = (time_t) 1;
  d_10 = d / 10;
  for(n = 0; p10 <= d_10; n++)
    p10 = (p10 << 3) + (p10 << 1);

  cptr = (retstr ? retstr : str);
  while(p10 >= (time_t) 1){
    c = d / p10;
    *(cptr++) = '0' + c;
    d -= (time_t) c * p10;
    p10 /= 10;
  }

  *cptr = '\0';

  return(retstr ? retstr : (UChar *) strdup(str));
}

UChar *
size_t_to_intstr(size_t d, UChar * retstr)
{
  UChar		str[70];

  UChar		*cptr;
  size_t	p10, d_10;
  int		c, n;

  p10 = (size_t) 1;
  d_10 = d / 10;
  for(n = 0; p10 <= d_10; n++)
    p10 = (p10 << 3) + (p10 << 1);

  cptr = (retstr ? retstr : str);
  while(p10 >= (size_t) 1){
    c = d / p10;
    *(cptr++) = '0' + c;
    d -= (size_t) c * p10;
    p10 /= 10;
  }

  *cptr = '\0';

  return(retstr ? retstr : (UChar *) strdup(str));
}


UChar *
actimestr()
{
  time_t	actime;

  actime = time(NULL);
  strcpy(buf, ctime(&actime));

  while(buf[strlen(buf) - 1] == '\n')
    buf[strlen(buf) - 1] = '\0';

  return(buf);
}

#define	WS	"[ \t]+"
#define	OWS	"[ \t]*"
#define	MNAM	"[A-Za-z][A-Za-z][A-Za-z][A-Za-z]*"
#define	MNUM	"[01]?[0-9]"
#define	MDAY	"[0-3]?[0-9]"
#define	YEAR	"[0-9][0-9]*"
#define	AMDATE	MNUM "[/|\\]" MDAY "[/|\\]" YEAR
#define	EUDATE	MDAY OWS "\\([.]" OWS MNUM OWS "[.]?\\|[.]?" OWS		\
				MNAM OWS "[.]?\\)\\(" OWS YEAR "\\)"
#define	TIME	"[0-2]?[0-9]" OWS ":" OWS "[0-5]?[0-9]" OWS		\
				"\\(:" OWS "[0-5]?[0-9]\\)?"

static UChar	*monthnames[] = {
		"[Jj][Aa][Nn]", "[Ff][Ee][Bb]", "[Mm][Aa][Ee]?[Rr]",
		"[Aa][Pp][Rr]", "[Mm][Aa][YyIi]", "[Jj][Uu][Nn]",
		"[Jj][Uu][Ll]", "[Aa][Uu][Gg]", "[Ss][Ee][Pp]",
		"[Oo][CcKk][Tt]", "[Nn][Oo][Vv]", "[Dd][Ee][CcZz]",
	};
static UChar	*dateformats[] = {
		"^" OWS MNAM WS MNAM OWS MDAY WS TIME
			"\\(\\(" WS MNAM "\\)?" WS YEAR "\\)?" OWS "$",
		"^" OWS MNAM OWS MDAY WS TIME "\\(" WS YEAR "\\)?" OWS "$",
		"^" OWS MNAM OWS MDAY "\\(" WS YEAR "\\)?" OWS "$",
		"^" OWS MDAY OWS "\\([.]?" OWS MNAM OWS
			"\\|[.]" OWS MNUM OWS "\\)[.]?"
			"\\(" WS TIME "\\)?" OWS "$",
		"^" OWS TIME WS MDAY OWS "\\([.]?" OWS MNAM OWS
			"[.]?\\|[.]" OWS MNUM OWS "[.]?\\)" OWS "$",
		"^" OWS AMDATE "\\(" WS TIME "\\)?" OWS "$",
		"^" OWS EUDATE "\\(" WS TIME "\\)?" OWS "$",
		"^" OWS TIME WS EUDATE OWS "$",
		"^" OWS TIME WS AMDATE OWS "$",
		"^" OWS MNAM OWS MDAY WS YEAR WS TIME OWS "$",
		"^" OWS TIME OWS "$",
	};

static void
read_time(UChar ** str, int * hour, int * min, int * sec)
{
  int		j;
  UChar		*cp;

  sscanf(*str, "%d%n", hour, &j);
  *str = strchr(*str, ':') + 1;
  sscanf(*str, "%d%n", min, &j);
  *str += j;
  cp = strchr(*str, ':');
  if(cp){
    cp++;
    sscanf(cp, "%d%n", sec, &j);
    *str = cp + j;
  }
  *str = first_nospace(*str);
}

static Int32
read_month(UChar ** str, int * mon)
{
  int		i, n;
  Int32		end;

  if(isdigit(**str)){
    sscanf(*str, "%d%n", mon, &i);
    *str += i;
  }
  else{
    n = sizeof(monthnames) / sizeof(monthnames[0]);
    for(i = 0; i < n; i++){
	if(re_find_match_once(monthnames[i], *str, NULL, &end) == 0)
	  break;
    }
    if(i < n){
	*mon = (i % 12) + 1;
    }
    else{
	return(-1);
    }
    while(isalpha(**str))
	(*str)++;
  }

  return(0);
}

static struct tm *
actim()
{
  static struct tm	actm;
  time_t		actime;

  actime = time(NULL);
  memcpy(&actm, localtime(&actime), sizeof(actm));
  return(&actm);
}

static Int32
read_date(UChar ** str, int * day, int * mon, int * year, UChar am)
{
  int		j;

  sscanf(*str, "%d%n", day, &j);
  *str += j;
  while(!isalnum(**str))
    (*str)++;
  if(read_month(str, mon)){
    *day = *year = *mon = 0;
    return(-1);
  }
  if(am)
    memswap(day, mon, sizeof(int));

  while(!isdigit(**str))
    (*str)++;
  sscanf(*str, "%d%n", year, &j);
  *str = first_nospace(*str + j);
  if(*year < 100)
    *year += 1900 + (*year >= 70 ? 0 : 100);

  return(0);
}

time_t
time_from_datestr(UChar * datestr)
{
  Int32		nfmts;
  int		i, j, year, mon, day, hour, min, sec, fmt;
  struct tm	td, *atd;

  year = mon = day = hour = min = sec = 0;
  fmt = -1;

  nfmts = sizeof(dateformats) / sizeof(dateformats[0]);
  for(i = 0; i < nfmts; i++){
    if(re_find_match_once(dateformats[i], datestr, NULL, NULL) == 0){
	fmt = i;
	break;
    }
  }
  if(fmt == -1)
    return((time_t) -1);

  datestr = first_nospace(datestr);

  SETZERO(td);

  switch(fmt){
    case 0:
	while(!isspace(*datestr))
	  datestr++;
	datestr = first_nospace(datestr);

    case 1:
    case 2:
	if(read_month(&datestr, &mon))
	  return((time_t) -1);

	sscanf(datestr, "%d%n", &day, &j);
	datestr = first_nospace(datestr + j);
	if(fmt == 1 || fmt == 0){
	  read_time(&datestr, &hour, &min, &sec);
	}

	if(*datestr){
	  if(fmt == 0){
	    while(!isdigit(*datestr) && *datestr)
		datestr++;
	  }

	  sscanf(datestr, "%d", &year);
	  if(year < 100)
	    year += 1900 + (year >= 70 ? 0 : 100);
	}
	else{
	  atd = actim();
	  year = atd->tm_year + 1900;
	}
	break;

    case 5:
    case 6:
	if(read_date(&datestr, &day, &mon, &year, fmt == 5))
	  return((time_t) -1);

	if(! *datestr)
	  break;

    case 7:
    case 8:
	read_time(&datestr, &hour, &min, &sec);

	if(fmt == 7 || fmt == 8)
	  if(read_date(&datestr, &day, &mon, &year, fmt == 8))
	    return((time_t) -1);

	break;

    case 3:
	sscanf(datestr, "%d%n", &day, &i);
	datestr += i;
	while(!isalnum(*datestr))
	  datestr++;

	if(read_month(&datestr, &mon))
	  return((time_t) -1);

	datestr = first_nospace(datestr);

	if(*datestr){
	  while(!isdigit(*datestr) && *datestr)
	    datestr++;
	  if(*datestr)
	    read_time(&datestr, &hour, &min, &sec);
	}

    case 4:
    case 10:
	atd = actim();
	year = atd->tm_year + 1900;

	if(fmt == 3)
	  break;

	read_time(&datestr, &hour, &min, &sec);

	if(fmt == 10){
	  mon = atd->tm_mon + 1;
	  day = atd->tm_mday;
	  break;
	}

	sscanf(datestr, "%d%n", &day, &i);
	datestr += i;
	while(!isalnum(*datestr))
	  datestr++;

    case 9:
	if(read_month(&datestr, &mon))
	  return((time_t) -1);

	if(fmt == 4)
	  break;

	sscanf(datestr, "%d%n", &day, &i);
	datestr += i;

	sscanf(datestr, "%d%n", &year, &i);
	datestr += i;
	if(year < 100)
	  year += 1900 + (year >= 70 ? 0 : 100);

	read_time(&datestr, &hour, &min, &sec);
	break;
  }

  td.tm_hour = hour;
  td.tm_min = min;
  td.tm_sec = sec;
  td.tm_mday = day;
  td.tm_mon = mon - 1;
  td.tm_year = year - 1900;
  td.tm_isdst = -1;

  return(mktime(&td));
}

time_t
strint2time(UChar * str)
{
  time_t	t;

  t = (time_t) 0;

  str = first_nospace(str);
  if(!isdigit(*str))
    return((time_t) -1);

  while(*str && isdigit(*str))
    t = (t << 3) + (t << 1) + (*(str++) - '0');

  return(t);
}

int
connect_afbu_server(UChar * servername, UChar * servname, Int32 portnum)
{
  int	sockfd;

  sockfd = open_tcpip_conn(servername, servname, portnum);

  if(sockfd < 0)
    return(sockfd);

  set_tcpip_throughput(sockfd);

  set_tcpip_keepalive(sockfd);

  return(sockfd);
}

