/*
  Copyright (C) 1985, 1996, Trustees of Columbia University in the City of New
  York.  The C-Kermit software may not be, in whole or in part, licensed or
  sold for profit as a software product itself, nor may it be included in or
  distributed with commercial products or otherwise distributed by commercial
  concerns to their clients or customers without written permission of the
  Office of Kermit Development and Distribution, Columbia University.  This
  copyright notice must not be removed, altered, or obscured.
*/

/*
 * ckustr.c - string extraction/restoration routines
*/

#include <stdio.h>
#include <sysexits.h>
#include <varargs.h>
#include <paths.h>

/*
  STR_FILE must be defined as a quoted string on the cc command line,
  for example:

  	-DSTR_FILE=\\\"/usr/local/lib/kermit5.sr\\\"

  This is the file where the strings go, and where C-Kermit looks for them
  at runtime.
*/

#ifdef STR_FILE
char	*StringFile = STR_FILE;
#else
char	*StringFile = "/usr/local/lib/kermit5.sr";
#endif /* STR_FILE */

/*
 * If _PATH_CTIMED is defined (in <paths.h>) then use that definition.  2.11BSD
 * has this defined but 2.10BSD and other systems do not.
*/

#ifndef _PATH_CTIMED
#define	_PATH_CTIMED STR_CTIMED
#endif

extern int errno;
static int strfile = -1, ourpid = 0;

#define BUFLEN 256

errprep(offset, buf)
unsigned short offset;
char *buf;
{
register int pid = getpid();

	if (pid != ourpid) {
		ourpid = pid;
		if (strfile >= 0) {
			close(strfile);
			strfile = -1;
		}
	}
	if (strfile < 0) {
	        char *p, *getenv();
		if (p = getenv("KSTR"))
		  if (strlen(p))
		    StringFile = p;
		strfile = open(StringFile, 0);
		if (strfile < 0) {
oops:
			fprintf(stderr, "Cannot find %s\r\n", StringFile);
			exit(EX_OSFILE);
		}
	}
	if (lseek(strfile, (long) offset, 0) < 0
			|| read(strfile, buf, BUFLEN) <= 0)
		goto oops;
}

/* extracted string front end for printf() */
/*VARARGS1*/
strprerror(fmt, va_alist)
	int fmt;
	va_dcl
{
	va_list	ap;
	char buf[BUFLEN];

	errprep(fmt, buf);
	va_start(ap);
	vprintf(buf, ap);
	va_end(ap);
}

/* extracted string front end for sprintf() */
/*VARARGS1*/
strsrerror(fmt, obuf, va_alist)
	int fmt;
	char *obuf;
	va_dcl
{
	char buf[BUFLEN];
	va_list	ap;

	errprep(fmt, buf);
	va_start(ap);
	vsprintf(obuf, buf, ap);
	va_end(ap);
}

/* extracted string front end for fprintf() */
/*VARARGS1*/
strfrerror(fmt, fd, va_alist)
	int fmt;
	FILE *fd;
	va_dcl
{
	va_list	ap;
	char buf[BUFLEN];

	errprep(fmt, buf);
	va_start(ap);
	vfprintf(fd, buf, ap);
	va_end(ap);
}

/* extracted string front end for perror() */
strperror(fmt)
	int fmt;
{
	char buf[BUFLEN];
	register int saverr = errno;

	errprep(fmt, buf);
	errno = saverr;
	perror(buf);
}

perror(str)
	char	*str;
	{

	printf("%s: errno %d\n", str, errno);
	}

/*
 * The following is needed _only_ on systems which do not have the C library
 * stubs for the ctime() and getpw*() functions.  In 2.11BSD these are
 * present in the libstubs.a library and accessed via "-lstubs" at link time.
 *
 * 2.10BSD's cpp has the BSD2_10 symbol builtin.  Other systems without 
 * libstubs.a will need to define (via a -D option in CFLAGS) 'BSD2_10'.
*/

#ifdef	BSD2_10

#include <sys/types.h>
#include <sys/time.h>
#include <pwd.h>
#include <utmp.h>

#define	SEND_FD	W[1]
#define	RECV_FD	R[0]

#define	CTIME	1
#define	ASCTIME	2
#define	TZSET	3
#define	LOCALTIME 4
#define	GMTIME	5
#define	OFFTIME	6

#define GETPWENT        7
#define GETPWNAM        8
#define GETPWUID        9
#define SETPASSENT      10
#define ENDPWENT        11

	static	int	R[2], W[2], inited;
	static	char	result[256 + 4];
	static	struct	tm	tmtmp;
	static	struct	passwd	_pw, *getandfixpw();

char	*
ctime(t)
	time_t	*t;
	{
	u_char	fnc = CTIME;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, t, sizeof (*t));
	getb(RECV_FD, result, 26);
	return(result);
	}

char	*
asctime(tp)
	struct	tm	*tp;
	{
	u_char	fnc = ASCTIME;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, tp, sizeof (*tp));
	getb(RECV_FD, result, 26);
	return(result);
	}

void
tzset()
	{
	u_char	fnc = TZSET;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	}

struct	tm *
localtime(tp)
	time_t	*tp;
	{
	u_char	fnc = LOCALTIME;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, tp, sizeof (*tp));
	getb(RECV_FD, &tmtmp, sizeof tmtmp);
	getb(RECV_FD, result, 24);
	tmtmp.tm_zone = result;
	return(&tmtmp);
	}

struct	tm *
gmtime(tp)
	time_t	*tp;
	{
	u_char	fnc = GMTIME;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, tp, sizeof (*tp));
	getb(RECV_FD, &tmtmp, sizeof tmtmp);
	getb(RECV_FD, result, 24);
	tmtmp.tm_zone = result;
	return(&tmtmp);
	}

struct	tm *
offtime(clock, offset)
	time_t	*clock;
	long	offset;
	{
	u_char	fnc = OFFTIME;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, clock, sizeof (*clock));
	write(SEND_FD, &offset, sizeof offset);
	getb(RECV_FD, &tmtmp, sizeof tmtmp);
	tmtmp.tm_zone = "";
	return(&tmtmp);
	}

struct passwd *
getpwent()
	{
	u_char	fnc = GETPWENT;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	return(getandfixpw());
	}

struct	passwd *
getpwnam(nam)
	char	*nam;
	{
	u_char	fnc = GETPWNAM;
	char	lnam[UT_NAMESIZE + 1];
	int	len;

	len = strlen(nam);
	if	(len > UT_NAMESIZE)
		len = UT_NAMESIZE;
	bcopy(nam, lnam, len);
	lnam[len] = '\0';

	sewer();
	write(SEND_FD, &fnc, 1);
	write(SEND_FD, &len, sizeof (int));
	write(SEND_FD, lnam, len);
	return(getandfixpw());
	}

struct	passwd	*
getpwuid(uid)
	uid_t	uid;
	{
	u_char	fnc = GETPWUID;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, &uid, sizeof (uid_t));
	return(getandfixpw());
	}

setpwent()
	{
	return(setpassent(0));
	}

setpassent(stayopen)
	int	stayopen;
	{
	u_char	fnc = SETPASSENT;
	int	sts;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	write(SEND_FD, &stayopen, sizeof (int));
	getb(RECV_FD, &sts, sizeof (int));
	return(sts);
	}

void
endpwent()
	{
	u_char	fnc = ENDPWENT;

	sewer();
	write(SEND_FD, &fnc, sizeof fnc);
	return;
	}

/* setpwfile() is deprecated */
void
setpwfile(file)
	char	*file;
	{
	return;
	}

struct passwd *
getandfixpw()
	{
	short	sz;

	getb(RECV_FD, &sz, sizeof (int));
	if	(sz == 0)
		return(NULL);
	getb(RECV_FD, &_pw, sizeof (_pw));
	getb(RECV_FD, result, sz);
	_pw.pw_name += (int)result;
	_pw.pw_passwd += (int)result;
	_pw.pw_class += (int)result;
	_pw.pw_gecos += (int)result;
	_pw.pw_dir += (int)result;
	_pw.pw_shell += (int)result;
	return(&_pw);
	}

getb(f, p, n)
	register int f, n;
	register char *p;
	{
	int	i;

	while	(n)
		{
		i = read(f, p, n);
		if	(i <= 0)
			return;
		p += i;
		n -= i;
		}
	}

sewer()
	{
	register int	pid, ourpid = getpid();

	if	(inited == ourpid)
		return;
	if	(inited)
		{
		close(SEND_FD);
		close(RECV_FD);
		}
	pipe(W);
	pipe(R);
	pid = vfork();
	if	(pid == 0)
		{			/* child */
		alarm(0);		/* cancel alarms */
		dup2(W[0], 0);		/* parent write side to our stdin */
		dup2(R[1], 1);		/* parent read side to our stdout */
		close(SEND_FD);		/* copies made, close the... */
		close(RECV_FD);		/* originals now */
		execl(_PATH_CTIMED, "ctimed", 0);
		_exit(EX_OSFILE);
		}
	if	(pid == -1)
		abort();		/* nothing else really to do */
	close(W[0]);			/* close read side of SEND channel */
	close(R[1]);			/* close write side of RECV channel */
	inited = ourpid;		/* don't do this again in this proc */
	}

XXctime()
	{

	if	(SEND_FD)
		close(SEND_FD);
	if	(RECV_FD)
		close(RECV_FD);
	SEND_FD = RECV_FD = 0;
	inited = 0;
	}
#endif	/* BSD2_10 */
