root/games/warp/intrp.c
/* Header: /usr/src/games/warp/RCS/intrp.c,v 1.2 87/07/03 00:56:37 games Exp
 *
 * Revision 7.0.1.2  86/12/12  16:59:04  lwall
 * Baseline for net release.
 *
 * Revision 7.0.1.1  86/10/16  10:51:43  lwall
 * Added Damage.  Fixed random bugs.
 *
 * Revision 7.0  86/10/08  15:12:19  lwall
 * Split into separate files.  Added amoebas and pirates.
 *
 */

#include "EXTERN.h"
#include "warp.h"
#include "sig.h"
#include "util.h"
#include "term.h"
#include "INTERN.h"
#include "intrp.h"

/* name of this host */
    char *hostname;

#ifdef TILDENAME
static char *tildename = NULL;
static char *tildedir = NULL;
#endif

static char *getrealname(uid_t);
#ifdef CONDSUB
static char *skipinterp(const char *, const char *);
#endif

__dead static void abort_interp(void);

void
intrp_init(char *tcbuf)
{
    /* get environmental stuff */

    /* get home directory */

    homedir = getenv("HOME");
    if (homedir == NULL)
        homedir = getenv("LOGDIR");

    dotdir = getval("DOTDIR",homedir);

    /* get login name */

    logname = getenv("USER");
    if (logname == NULL)
        logname = getenv("LOGNAME");
#ifdef GETLOGIN
    if (logname == NULL)
        logname = savestr(getlogin());
#endif

    /* get the real name of the person (%N) */
    /* Must be done after logname is read in because BERKNAMES uses that */

    strcpy(tcbuf,getrealname(getuid()));
    realname = savestr(tcbuf);

    /* name of this host (%H) */

    gethostname(buf,sizeof buf);
    hostname = savestr(buf);
    if (strchr(hostname,'.'))
        hostname = savestr(hostname);
    else {
        char hname[128];

        strcpy(hname,hostname);
        strcat(hname,MYDOMAIN);
        hostname=savestr(hname);
    }
    warplib = savestr(filexp(WARPLIB));

    if (scorespec)                      /* that getwd below takes ~1/3 sec. */
        return;                         /* and we do not need it for -s */
    (void) getcwd(tcbuf, sizeof(tcbuf));/* find working directory name */
    origdir = savestr(tcbuf);           /* and remember it */
}

/* expand filename via %, ~, and $ interpretation */
/* returns pointer to static area */
/* Note that there is a 1-deep cache of ~name interpretation */

char *
filexp(const char *s)
{
    static char filename[CBUFLEN];
    char scrbuf[CBUFLEN];
    char *d;

#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
        printf("< %s\r\n",s);
#endif
    interp(filename, (sizeof filename), s);                     /* interpret any % escapes */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
        printf("%% %s\r\n",filename);
#endif
    s = filename;
    if (*s == '~') {    /* does destination start with ~? */
        if (!*(++s) || *s == '/') {
            snprintf(scrbuf, sizeof(scrbuf), "%s%s",homedir,s);
                                /* swap $HOME for it */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
        printf("~ %s\r\n",scrbuf);
#endif
            strcpy(filename,scrbuf);
        }
        else {
#ifdef TILDENAME
            for (d=scrbuf; isalnum((unsigned char)*s); s++,d++)
                *d = *s;
            *d = '\0';
            if (tildedir && strEQ(tildename,scrbuf)) {
                strcpy(scrbuf,tildedir);
                strcat(scrbuf, s);
                strcpy(filename, scrbuf);
#ifdef DEBUGGING
                if (debug & DEB_FILEXP)
                    printf("r %s %s\r\n",tildename,tildedir);
#endif
            }
            else {
                if (tildename) {
                    free(tildename);
                    free(tildedir);
                }
                tildedir = NULL;
                tildename = savestr(scrbuf);
                {
                    struct passwd *pwd = getpwnam(tildename);

                    snprintf(scrbuf, sizeof(scrbuf), "%s%s",pwd->pw_dir,s);
                    tildedir = savestr(pwd->pw_dir);
                    strcpy(filename,scrbuf);
                    endpwent();
                }
            }
#else /* !TILDENAME */
#ifdef VERBOSE
            IF(verbose)
                fputs("~loginname not implemented.\r\n",stdout);
            ELSE
#endif
#ifdef TERSE
                fputs("~login not impl.\r\n",stdout);
#endif
#endif
        }
    }
    else if (*s == '$') {       /* starts with some env variable? */
        d = scrbuf;
        *d++ = '%';
        if (s[1] == '{')
            strcpy(d,s+2);
        else {
            *d++ = '{';
            for (s++; isalnum((unsigned char)*s); s++) *d++ = *s;
                                /* skip over token */
            *d++ = '}';
            strcpy(d,s);
        }
#ifdef DEBUGGING
        if (debug & DEB_FILEXP)
            printf("$ %s\r\n",scrbuf);
#endif
        interp(filename, (sizeof filename), scrbuf);
                                        /* this might do some extra '%'s but */
                                        /* that is how the Mercedes Benz */
    }
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
        printf("> %s\r\n",filename);
#endif
    return filename;
}

#ifdef CONDSUB
/* skip interpolations */

static char *
skipinterp(const char *pattern, const char *stoppers)
{

    while (*pattern && (!stoppers || !strchr(stoppers,*pattern))) {
#ifdef DEBUGGING
        if (debug & 8)
            printf("skipinterp till %s at %s\r\n",stoppers?stoppers:"",pattern);
#endif
        if (*pattern == '%' && pattern[1]) {
            switch (*++pattern) {
            case '{':
                for (pattern++; *pattern && *pattern != '}'; pattern++)
                    if (*pattern == '\\')
                        pattern++;
                break;
#ifdef CONDSUB
            case '(': {
                pattern = skipinterp(pattern+1,"!=");
                if (!*pattern)
                    goto getout;
                for (pattern++; *pattern && *pattern != '?'; pattern++)
                    if (*pattern == '\\')
                        pattern++;
                if (!*pattern)
                    goto getout;
                pattern = skipinterp(pattern+1,":)");
                if (*pattern == ':')
                    pattern = skipinterp(pattern+1,")");
                break;
            }
#endif
#ifdef BACKTICK
            case '`': {
                pattern = skipinterp(pattern+1,"`");
                break;
            }
#endif
#ifdef PROMPTTTY
            case '"':
                pattern = skipinterp(pattern+1,"\"");
                break;
#endif
            default:
                break;
            }
            pattern++;
        }
        else {
            if (*pattern == '^' && pattern[1])
                pattern += 2;
            else if (*pattern == '\\' && pattern[1])
                pattern += 2;
            else
                pattern++;
        }
    }
getout:
    return __UNCONST(pattern);                  /* where we left off */
}
#endif

static char *mygets(char *str, size_t n)
{
    char *ret;
    size_t last;

    if ((ret = fgets(str, n, stdin)) != NULL) {
        last = strlen(str) - 1;

        if (str[last] == '\n')
            str[last] = '\0';
    }

    return ret;
}

/* interpret interpolations */

char *
dointerp(char *dest, size_t destsize, const char *pattern, const char *stoppers)
{
    char *s;
    int i;
    char scrbuf[512];
    bool upper = false;
    bool lastcomp = false;
    int metabit = 0;

    while (*pattern && (!stoppers || !strchr(stoppers,*pattern))) {
#ifdef DEBUGGING
        if (debug & 8)
            printf("dointerp till %s at %s\r\n",stoppers?stoppers:"",pattern);
#endif
        if (*pattern == '%' && pattern[1]) {
            upper = false;
            lastcomp = false;
            for (s=NULL; !s; ) {
                switch (*++pattern) {
                case '^':
                    upper = true;
                    break;
                case '_':
                    lastcomp = true;
                    break;
                case '{':
                    pattern = cpytill(scrbuf,pattern+1,'}');
                    if ((s = strchr(scrbuf,'-')) != NULL)
                        *s++ = '\0';
                    else
                        s = nullstr;
                    s = getval(scrbuf,s);
                    break;
#ifdef CONDSUB
                case '(': {
                    char rch;
                    bool matched;

                    pattern = dointerp(dest,destsize,pattern+1,"!=");
                    rch = *pattern;
                    if (rch == '!')
                        pattern++;
                    if (*pattern != '=')
                        goto getout;
                    pattern = cpytill(scrbuf,pattern+1,'?');
                    if (!*pattern)
                        goto getout;
                    if (*scrbuf == '^' && scrbuf[strlen(scrbuf)-1] == '$') {
                        scrbuf[strlen(scrbuf)-1] = '\0';
                        matched = strEQ(scrbuf+1,dest);
                    }
                    else
                        matched = instr(dest,scrbuf) != NULL;
                    if (matched==(rch == '=')) {
                        pattern = dointerp(dest,destsize,pattern+1,":)");
                        if (*pattern == ':')
                            pattern = skipinterp(pattern+1,")");
                    }
                    else {
                        pattern = skipinterp(pattern+1,":)");
                        if (*pattern == ':')
                            pattern++;
                        pattern = dointerp(dest,destsize,pattern,")");
                    }
                    s = dest;
                    break;
                }
#endif
#ifdef BACKTICK
                case '`': {
                    FILE *pipefp;

                    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
                    pipefp = popen(scrbuf,"r");
                    if (pipefp != NULL) {
                        int len;

                        len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
                            pipefp);
                        scrbuf[len] = '\0';
                        pclose(pipefp);
                    }
                    else {
                        printf("\r\nCan't run %s\r\n",scrbuf);
                        *scrbuf = '\0';
                    }
                    for (s=scrbuf; *s; s++) {
                        if (*s == '\n') {
                            if (s[1])
                                *s = ' ';
                            else
                                *s = '\0';
                        }
                    }
                    s = scrbuf;
                    break;
                }
#endif
#ifdef PROMPTTTY
                case '"':
                    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
                    fputs(scrbuf,stdout);
                    resetty();
                    mygets(scrbuf, sizeof(scrbuf));
                    crmode();
                    raw();
                    noecho();
                    nonl();
                    s = scrbuf;
                    break;
#endif
                case '~':
                    s = homedir;
                    break;
                case '.':
                    s = dotdir;
                    break;
                case '$':
                    s = scrbuf;
                    snprintf(scrbuf, sizeof(scrbuf), "%d",getpid());
                    break;
                case 'H':                       /* host name */
                    s = hostname;
                    break;
                case 'L':                       /* login id */
                    s = logname;
                    break;
                case 'N':                       /* full name */
                    s = getval("NAME",realname);
                    break;
                case 'O':
                    s = origdir;
                    break;
                case 'p':
                    s = cwd;
                    break;
                case 'X':                       /* warp library */
                    s = warplib;
                    break;
                default:
                    if (--destsize <= 0)
                        abort_interp();
                    *dest++ = *pattern | metabit;
                    s = nullstr;
                    break;
                }
            }
            if (!s)
                s = nullstr;
            pattern++;
            if (upper || lastcomp) {
                char *t;

                if (s != scrbuf) {
                    safecpy(scrbuf,s,(sizeof scrbuf));
                    s = scrbuf;
                }
                if (upper || !(t=strrchr(s,'/')))
                    t = s;
                while (*t && !isalpha((unsigned char)*t)) {
                    t++;
                    *t = toupper((unsigned char)*t);
                }
            }
            i = metabit;                /* maybe get into register */
            if (s == dest) {
                while (*dest) {
                    if (--destsize <= 0)
                        abort_interp();
                    *dest++ |= i;
                }
            }
            else {
                while (*s) {
                    if (--destsize <= 0)
                        abort_interp();
                    *dest++ = *s++ | i;
                }
            }
        }
        else {
            if (--destsize <= 0)
                abort_interp();
            if (*pattern == '^' && pattern[1]) {
                ++pattern;                      /* skip uparrow */
                i = *pattern;           /* get char into a register */
                if (i == '?')
                    *dest++ = '\177' | metabit;
                else if (i == '(') {
                    metabit = 0200;
                    destsize++;
                }
                else if (i == ')') {
                    metabit = 0;
                    destsize++;
                }
                else
                    *dest++ = (i & 037) | metabit;
                pattern++;
            }
            else if (*pattern == '\\' && pattern[1]) {
                ++pattern;                      /* skip backslash */
                i = *pattern;           /* get char into a register */

                /* this used to be a switch but the if may save space */

                if (i >= '0' && i <= '7') {
                    i = 1;
                    while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
                        i <<= 3;
                        i += *pattern++ - '0';
                    }
                    *dest++ = (i & 0377) | metabit;
                    --pattern;
                }
                else if (i == 'b')
                    *dest++ = '\b' | metabit;
                else if (i == 'f')
                    *dest++ = '\f' | metabit;
                else if (i == 'n')
                    *dest++ = '\n' | metabit;
                else if (i == 'r')
                    *dest++ = '\r' | metabit;
                else if (i == 't')
                    *dest++ = '\t' | metabit;
                else
                    *dest++ = i | metabit;
                pattern++;
            }
            else
                *dest++ = *pattern++ | metabit;
        }
    }
    *dest = '\0';
getout:
    return __UNCONST(pattern);                  /* where we left off */
}

void
interp(char *dest, size_t destsize, const char *pattern)
{
    (void) dointerp(dest,destsize,pattern,NULL);
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
        fputs(dest,stdout);
#endif
}

/* get the person's real name from /etc/passwd */
/* (string is overwritten, so it must be copied) */

static char *
getrealname(uid_t uid)
{
    char *s, *c;

#ifdef PASSNAMES
    struct passwd *pwd = getpwuid(uid);

    s = pwd->pw_gecos;
#ifdef BERKNAMES
#ifdef BERKJUNK
    while (*s && !isalnum(*s) && *s != '&') s++;
#endif
    if ((c = strchr(s, ',')) != NULL)
        *c = '\0';
    if ((c = strchr(s, ';')) != NULL)
        *c = '\0';
    s = cpytill(buf,s,'&');
    if (*s == '&') {                    /* whoever thought this one up was */
        c = buf + strlen(buf);          /* in the middle of the night */
        strcat(c,logname);              /* before the morning after */
        strcat(c,s+1);
        if (islower((unsigned char)*c))
            *c = toupper((unsigned char)*c);            /* gack and double gack */
    }
#else
    if ((c = strchr(s, '(')) != NULL)
        *c = '\0';
    if ((c = strchr(s, '-')) != NULL)
        s = c;
    strcpy(buf,tmpbuf);
#endif
    endpwent();
    return buf;                         /* return something static */
#else
    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != NULL) {
        fgets(buf,sizeof buf,tmpfp);
        fclose(tmpfp);
    }
    else {
        resetty();
        printf("What is your name? ");
        fgets(buf,(sizeof buf),stdin);
        crmode();
        raw();
        noecho();
        nonl();
        if (fork())
            wait(0);
        else {
            setgid(getgid());
            if ((tmpfp = fopen(filexp(FULLNAMEFILE),"w")) == NULL)
                exit(1);
            fprintf(tmpfp, "%s\n", buf);
            fclose(tmpfp);
            exit(0);
        }
    }
    buf[strlen(buf)-1] = '\0';
    return buf;
#endif
}

static void
abort_interp(void)
{
    fputs("\r\n% interp buffer overflow!\r\n",stdout);
    sig_catcher(0);
}