/*
 * 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/cku196.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/cku196.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 */