#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
#include <sys/types.h>
#include <signal.h>
#ifdef	HAVE_SYS_TIME_H
#ifdef	TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#include <ctype.h>

#include <x_types.h>
#include <utils.h>
#include <sysutils.h>

#define	PROC_MAX_TRIES	20
#define	MEM_MAX_TRIES	60


Int32
read_forced(int fd, UChar * data, Int32 num)
{
  Int32		i, n = 0;

  if(num < 1)
    return(0);

  while(num > 0){
    i = read(fd, data, num);

    if(i < 1){
	return(n > 0 ? n : i);
	break;
    }

    n += i;
    num -= i;
    data += i;
  }

  return(n);
}


Int32
write_forced(int fd, UChar * data, Int32 num)
{
  Int32		i, n = 0;

  if(num < 1)
    return(0);

  while(num > 0){
    i = write(fd, data, num);

    if(i < 1){
	return(n > 0 ? n : i);
	break;
    }

    n += i;
    num -= i;
    data += i;
  }

  return(n);
}


int			/* try 10 seconds to obtain a lock to a file */
set_wlock_forced(UChar * name)
{
  int		fd;
  struct flock	lockb;
  time_t	startt;

  fd = open(name, O_WRONLY | O_CREAT, 0600);
  if(fd < 0)
    return(-1);

  startt = time(NULL);

  forever{
    SETZERO(lockb);
    lockb.l_type = F_WRLCK;

    if(!fcntl(fd, F_SETLK, &lockb))
	return(fd);

    if(startt + 10 > time(NULL))
	break;

    ms_sleep((Int32)(drandom() * 200.0) + 100);
  }

  return(-1);
}

int			/* try n milliseconds to obtain a lock to a file */
set_wlock_timeout(UChar * name, Int32 timeout_ms)
{
  int		fd;
  Int32		remaining_ms;
  struct flock		lockb;
  struct timeval	tv, ntv, etv;
  Flag		last_try = NO;

  gettimeofday(&tv, NULL);
  etv.tv_usec = tv.tv_usec + 1000 * (timeout_ms % 1000);
  etv.tv_sec = tv.tv_sec + (timeout_ms / 1000);
  if(etv.tv_usec > 1000000){
    etv.tv_sec++;
    etv.tv_usec -= 1000000;
  }

  fd = open(name, O_WRONLY | O_CREAT, 0600);
  if(fd < 0)
    return(-1);

  forever{
    SETZERO(lockb);
    lockb.l_type = F_WRLCK;

    if(!fcntl(fd, F_SETLK, &lockb))
	return(fd);

    if(last_try)
	return(-1);

    remaining_ms = 1000 * (etv.tv_sec - tv.tv_sec) +
			(etv.tv_usec - tv.tv_usec) / 1000;
    if(remaining_ms > 300)
	remaining_ms = (Int32)(drandom() * 200.0) + 100;
    else
	last_try = YES;
	
    ms_sleep(remaining_ms);

    gettimeofday(&ntv, NULL);
    if(ntv.tv_sec < tv.tv_sec)
	ntv.tv_sec += 24 * 60 * 60;
    tv.tv_sec = ntv.tv_sec;
    tv.tv_usec = ntv.tv_usec;
  }

  return(-1);
}

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;

  proc_tries = 0;
  do{
    pid = fork();

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

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

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

    if(!pid)
	clr_timer();

  } while(pid < 0);

  return(pid);
}

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

void *
malloc_forced(size_t size)
{
  void	*newm;	/* uninitialized OK */
  Int32	i;

  for(i = MEM_MAX_TRIES; i >= 0; i--){
    newm = malloc(size);
    if(newm)
	break;

    ms_sleep(50 + (Int32)(200.0 * drandom()));
  }

  return(newm);
}

void *
realloc_forced(void * old, size_t size)
{
  void	*newm;	/* uninitialized OK */
  Int32	i;

  if(!old)
    return(malloc_forced(size));

  for(i = MEM_MAX_TRIES; i >= 0; i--){
    newm = realloc(old, size);
    if(newm)
	break;

    ms_sleep(50 + (Int32)(200.0 * drandom()));
  }

  return(newm);
}

