/* $Id: intrp.c,v 3.0 1992/02/01 03:09:32 davison Trn $
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The authors make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "util2.h"
#include "search.h"
#include "cache.h"
#include "bits.h"
#include "head.h"
#include "trn.h"
#include "artsrch.h"
#include "ng.h"
#include "ngdata.h"
#include "respond.h"
#include "rcstuff.h"
#include "artio.h"
#include "init.h"
#include "term.h"
#include "final.h"
#include "rthread.h"
#include "rt-select.h"
#include "rt-util.h"
#include "nntp.h"
#include "INTERN.h"
#include "intrp.h"

static char * regexp_specials = "^$.*[\\/?%";

char orgname[] = ORGNAME;

#ifdef HAS_UNAME
#include <sys/utsname.h>
struct utsname utsn;
#endif

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

#ifdef CONDSUB
COMPEX cond_compex;
char *skipinterp _((char *,char *));
#endif

static void abort_interp _((void));

void
intrp_init(tcbuf)
char *tcbuf;
{
    char *getlogin();

#ifdef CONDSUB
    init_compex(&cond_compex);
#endif
    
    /* get environmental stuff */

#ifdef NEWS_ADMIN
    {
#ifdef HAS_GETPWENT
	struct passwd *getpwnam();
	struct passwd *pwd = getpwnam(NEWS_ADMIN);

	if (pwd != NULL)
	    newsuid = pwd->pw_uid;
#else
#ifdef TILDENAME
	char tildenews[2+sizeof NEWS_ADMIN];
	strcpy(tildenews, "~");
	strcat(tildenews, NEWS_ADMIN);
	(void) filexp(tildenews);
#else
	??? "Define either HAS_GETPWENT or TILDENAME to get NEWS_ADMIN"
#endif  /* TILDENAME */
#endif	/* HAS_GETPWENT */
    }
#endif	/* NEWS_ADMIN */
    /* get home directory */

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

    dotdir = getval("DOTDIR",homedir);

    /* get login name */

    loginName = getenv("USER");
    if (loginName == Nullch) {
	loginName = getenv("LOGNAME");
#ifdef GETLOGIN
	if (loginName == Nullch)
	    loginName = savestr(getlogin());
#endif
    }

    spool = savestr(filexp(NEWSSPOOL));	/* usually /usr/spool/news */
    threaddir = filexp(THREAD_DIR);
    if (strEQ(threaddir,spool))
	threaddir = spool;
    else
	threaddir = savestr(threaddir);
    overviewdir = filexp(OVERVIEW_DIR);
    if (strEQ(overviewdir,spool))
	overviewdir = spool;
    else
	overviewdir = savestr(overviewdir);

#ifdef NEWS_ADMIN
    /* if this is the news admin then load his UID into newsuid */

    if (strEQ(loginName,NEWS_ADMIN))
	newsuid = getuid();
#endif

    if (checkflag)			/* that getwd below takes ~1/3 sec. */
	return;				/* and we do not need it for -c */
    getwd(tcbuf);			/* find working directory name */
    origdir = savestr(tcbuf);		/* and remember it */

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

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

    /* name of header file (%h) */

    headname = savestr(filexp(HEADNAME));

    /* host name that goes in postings (%H) */

    phostname = PHOSTNAME;
    if (*phostname == '/' || *phostname == '~') {
	phostname = filexp(phostname);
	if ((tmpfp = fopen(phostname,"r")) == NULL) {
	    printf("Warning: Couldn't open %s to determine hostname!\n",
		   phostname); 
	    sig_catcher(0);
	}
	fgets(buf, sizeof(buf), tmpfp);
	if (buf[strlen(buf)-1] == '\n')
	    buf[strlen(buf)-1] = 0;
	fclose(tmpfp);
	phostname = savestr(buf);
    }
    else {
#ifdef HAS_GETHOSTNAME
	gethostname(buf,sizeof buf);
#else
# ifdef HAS_UNAME
	/* get sysname */
	uname(&utsn);
	strcpy(buf,utsn.nodename);
# else
#  ifdef PHOSTCMD
       {
	FILE *popen();
	FILE *pipefp = popen(PHOSTCMD,"r");
	
	if (pipefp == Nullfp) {
	    printf("Can't find hostname\n");
	    sig_catcher(0);
	}
	fgets(buf,sizeof buf,pipefp);
	buf[strlen(buf)-1] = '\0';	/* wipe out newline */
	pclose(pipefp);
       }
#  else
	*buf = '\0';
#  endif /* PHOSTCMD */
# endif /* HAS_UNAME */
#endif /* HAS_GETHOSTNAME */
	if (*buf) {
	    if (*phostname == '.' && phostname[1]) {
		strcat(buf,phostname);
	    }
	    phostname = savestr(buf);
	}
    }
}

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

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

#ifdef DEBUG
    if (debug & DEB_FILEXP)
	printf("< %s\n",s) FLUSH;
#endif
    interp(filename, (sizeof filename), s);	
					/* interpret any % escapes */
#ifdef DEBUG
    if (debug & DEB_FILEXP)
	printf("%% %s\n",filename) FLUSH;
#endif
    s = filename;
    if (*s == '~') {	/* does destination start with ~? */
	if (!*(++s) || *s == '/') {
	    sprintf(scrbuf,"%s%s",homedir,s);
				/* swap $HOME for it */
#ifdef DEBUG
    if (debug & DEB_FILEXP)
	printf("~ %s\n",scrbuf) FLUSH;
#endif
	    strcpy(filename,scrbuf);
	}
	else {
#ifdef TILDENAME
	    for (d=scrbuf; isalnum(*s); s++,d++)
		*d = *s;
	    *d = '\0';
	    if (tildedir && strEQ(tildename,scrbuf)) {
		strcpy(scrbuf,tildedir);
		strcat(scrbuf, s);
		strcpy(filename, scrbuf);
#ifdef DEBUG
		if (debug & DEB_FILEXP)
		    printf("r %s %s\n",tildename,tildedir) FLUSH;
#endif
	    }
	    else {
		if (tildename) {
		    free(tildename);
		    free(tildedir);
		}
		tildedir = Nullch;
		tildename = savestr(scrbuf);
#ifdef HAS_GETPWENT	/* getpwnam() is not the paragon of efficiency */
		{
#ifdef notdef
		    struct passwd *getpwnam _((char*));
#endif
		    struct passwd *pwd = getpwnam(tildename);
		    if (pwd == NULL) {
			printf("%s is an unknown user. Using default.\n",tildename) FLUSH;
			return Nullch;
		    }
		    sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
		    tildedir = savestr(pwd->pw_dir);
		    strcpy(filename,scrbuf);
		    endpwent();
		}
#else			/* this will run faster, and is less D space */
		{	/* just be sure LOGDIRFIELD is correct */
		    FILE *pfp = fopen("/etc/passwd","r");
		    char tmpbuf[512];
		    int i;
		    
		    if (pfp == Nullfp) {
			printf(cantopen,"passwd") FLUSH;
			sig_catcher(0);
		    }
		    while (fgets(tmpbuf,512,pfp) != Nullch) {
			d = cpytill(scrbuf,tmpbuf,':');
#ifdef DEBUG
			if (debug & DEB_FILEXP)
			    printf("p %s\n",tmpbuf) FLUSH;
#endif
			if (strEQ(scrbuf,tildename)) {
			    for (i=LOGDIRFIELD-2; i; i--) {
				if (d)
				    d = index(d+1,':');
			    }
			    if (d) {
				cpytill(scrbuf,d+1,':');
				tildedir = savestr(scrbuf);
				strcat(scrbuf,s);
				strcpy(filename,scrbuf);
			    }
			    break;
			}
		    }
		    fclose(pfp);
		}
#endif
	    }
#else /* !TILDENAME */
#ifdef VERBOSE
	    IF(verbose)
		fputs("~loginname not implemented.\n",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("~login not impl.\n",stdout) FLUSH;
#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(*s); s++) *d++ = *s;
				/* skip over token */
	    *d++ = '}';
	    strcpy(d,s);
	}
#ifdef DEBUG
	if (debug & DEB_FILEXP)
	    printf("$ %s\n",scrbuf) FLUSH;
#endif
	interp(filename, (sizeof filename), scrbuf);
					/* this might do some extra '%'s but */
					/* that is how the Mercedes Benz */
    }
#ifdef DEBUG
    if (debug & DEB_FILEXP)
	printf("> %s\n",filename) FLUSH;
#endif
    return filename;
}

#ifdef CONDSUB
/* skip interpolations */

char *
skipinterp(pattern,stoppers)
register char *pattern;
char *stoppers;
{

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUG
	if (debug & DEB_INTRP)
	    printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	switch_again:
	    switch (*++pattern) {
	    case '^':
	    case '_':
	    case '\\':
	    case '\'':
	    case '>':
	    case ')':
		goto switch_again;
	    case ':':
		pattern++;
		while (*pattern
		 && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
		    pattern++;
		}
		pattern--;
		goto switch_again;
	    case '{':
		for (pattern++; *pattern && *pattern != '}'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		break;
	    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 pattern;			/* where we left off */
}
#endif

/* interpret interpolations */

char *
dointerp(dest,destsize,pattern,stoppers)
register char *dest;
register int destsize;
register char *pattern;
char *stoppers;
{
    char *subj_buf = Nullch;
    char *ngs_buf = Nullch;
    char *refs_buf = Nullch;
    char *artid_buf = Nullch;
    char *reply_buf = Nullch;
    char *from_buf = Nullch;
    char *path_buf = Nullch;
    char *follow_buf = Nullch;
    char *dist_buf = Nullch;
    char *line_buf = Nullch;
    char *line_split = Nullch;
    char *orig_dest = dest;
    register char *s, *h;
    register int i;
    char scrbuf[512];
    char spfbuf[512];
    static char *input_str = Nullch;
    static int input_siz = 0;
    bool upper = FALSE;
    bool lastcomp = FALSE;
    bool re_quote = FALSE;
    int tick_quote = 0;
    bool address_parse = FALSE;
    bool comment_parse = FALSE;
    bool proc_sprintf = FALSE;
    int metabit = 0;

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUG
	if (debug & DEB_INTRP)
	    printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	    upper = FALSE;
	    lastcomp = FALSE;
	    re_quote = FALSE;
	    tick_quote = 0;
	    address_parse = FALSE;
	    comment_parse = FALSE;
	    proc_sprintf = FALSE;
	    for (s=Nullch; !s; ) {
		switch (*++pattern) {
		case '^':
		    upper = TRUE;
		    break;
		case '_':
		    lastcomp = TRUE;
		    break;
		case '\\':
		    re_quote = TRUE;
		    break;
		case '\'':
		    tick_quote++;
		    break;
		case '>':
		    address_parse = TRUE;
		    break;
		case ')':
		    comment_parse = TRUE;
		    break;
		case ':':
		    proc_sprintf = TRUE;
		    h = spfbuf;
		    *h++ = '%';
		    pattern++;	/* Skip over ':' */
		    while (*pattern
		     && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
			*h++ = *pattern++;
		    }
		    *h++ = 's';
		    *h++ = '\0';
		    pattern--;
		    break;
		case '/':
#ifdef ARTSEARCH
		    s = scrbuf;
		    if (!index("/?g",pattern[-2]))
			*s++ = '/';
		    strcpy(s,lastpat);
		    s += strlen(s);
		    if (pattern[-2] != 'g') {
			if (index("/?",pattern[-2]))
			    *s++ = pattern[-2];
			else
			    *s++ = '/';
			if (art_doread)
			    *s++ = 'r';
			if (art_howmuch != ARTSCOPE_SUBJECT) {
			    *s++ = scopestr[art_howmuch];
			    if (art_howmuch == ARTSCOPE_ONEHDR) {
				safecpy(s,art_srchhdr,
					(sizeof scrbuf) - (s-scrbuf));
				s = index(s,':') + 1;
				if (!s)
				    s = scrbuf+(sizeof scrbuf)-1;
			    }
			}
		    }
		    *s = '\0';
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case '{':
		    pattern = cpytill(scrbuf,pattern+1,'}');
		    if (s = index(scrbuf,'-'))
			*s++ = '\0';
		    else
			s = nullstr;
		    s = getval(scrbuf,s);
		    break;
		case '[':
		    if (in_ng) {
			pattern = cpytill(scrbuf,pattern+1,']');
			i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
			if (line_buf)
			    free(line_buf);
			s = line_buf = fetchlines(art,i);
		    }
		    else
			s = nullstr;
		    break;
#ifdef CONDSUB
		case '(': {
		    COMPEX *oldbra_compex = bra_compex;
		    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;
		    s = scrbuf;
		    h = spfbuf;
		    proc_sprintf = FALSE;
		    do {
			switch (*s) {
			case '^':
			    *h++ = '\\';
			    break;
			case '\\':
			    *h++ = '\\';
			    *h++ = '\\';
			    break;
			case '%':
			    proc_sprintf = TRUE;
			    break;
			}
			*h++ = *s;
		    } while (*s++);
		    if (proc_sprintf) {
			dointerp(scrbuf,sizeof scrbuf,spfbuf,Nullch);
			proc_sprintf = FALSE;
		    }
		    if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
			printf("%s: %s\n",scrbuf,s) FLUSH;
			pattern += strlen(pattern);
			free_compex(&cond_compex);
			goto getout;
		    }
		    matched = (execute(&cond_compex,dest) != Nullch);
		    if (getbracket(&cond_compex, 0)) /* were there brackets? */
			bra_compex = &cond_compex;
		    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;
		    bra_compex = oldbra_compex;
		    free_compex(&cond_compex);
		    break;
		}
#endif
#ifdef BACKTICK
		case '`': {
		    FILE *pipefp, *popen();

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

			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
			    pipefp);
			scrbuf[len] = '\0';
			pclose(pipefp);
		    }
		    else {
			printf("\nCan't run %s\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) FLUSH;
		    resetty();
		    fgets(scrbuf, sizeof scrbuf, stdin);
		    noecho();
		    crmode();
		    i = strlen(scrbuf);
		    if (scrbuf[i-1] == '\n') {
			scrbuf[--i] = '\0';
		    }
		    growstr(&input_str, &input_siz, i+1);
		    safecpy(input_str, scrbuf, i+1);
		    s = input_str;
		    break;
#endif
		case '~':
		    s = homedir;
		    break;
		case '.':
		    s = dotdir;
		    break;
		case '$':
		    s = scrbuf;
		    sprintf(s,"%ld",our_pid);
		    break;
		case '#':
		    s = scrbuf;
		    sprintf(s,"%d",perform_cnt);
		    break;
		case '?':
		    s = " ";
		    line_split = dest;
		    break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
#ifdef CONDSUB
		    s = getbracket(bra_compex,*pattern - '0');
#else
		    s = nullstr;
#endif
		    break;
		case 'a':
		    if (in_ng) {
			s = scrbuf;
			sprintf(s,"%ld",(long)art);
		    }
		    else
			s = nullstr;
		    break;
		case 'A':
		    if (in_ng) {
#ifdef LINKART
			s = linkartname;  /* so Eunice people get right file */
#else
			s = scrbuf;
#ifdef USE_NNTP
			if (artopen(art))
			    nntp_finishbody(FB_SILENT);
			sprintf(s,"%s/%s",spool,nntp_artname());
#else
			sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
#endif
#endif
		    }
		    else
			s = nullstr;
		    break;
		case 'b':
		    s = savedest;
		    break;
		case 'B':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)savefrom);
		    break;
		case 'c':
		    s = ngdir;
		    break;
		case 'C':
		    s = ngname;
		    break;
		case 'd':
		    s = scrbuf;
		    sprintf(s,"%s/%s",spool,ngdir);
		    break;
		case 'D':
		    if (in_ng)
			s = dist_buf = fetchlines(art,DIST_LINE);
		    else
			s = nullstr;
		    break;
		case 'e':
		    s = (extractprog ? extractprog : "-");
		    break;
		case 'E':
		    s = extractdest;
		    break;
		case 'f':			/* from line */
		    if (in_ng) {
			parseheader(art);
			if (htype[REPLY_LINE].ht_minpos >= 0 && !comment_parse) {
						/* was there a reply line? */
			    if (!(s=reply_buf))
				s = reply_buf = fetchlines(art,REPLY_LINE);
			}
			else if (!(s = from_buf))
			    s = from_buf = fetchlines(art,FROM_LINE);
		    }
		    else
			s = nullstr;
		    break;
		case 'F':
		    if (in_ng) {
			parseheader(art);
			if (htype[FOLLOW_LINE].ht_minpos >= 0)
					/* is there a Followup-To line? */
			    s = follow_buf = fetchlines(art,FOLLOW_LINE);
			else 
			    s = ngs_buf = fetchlines(art,NGS_LINE);
		    }
		    else
			s = nullstr;
		    break;
		case 'h':			/* header file name */
		    s = headname;
		    break;
		case 'H':			/* host name in postings */
		    s = phostname;
		    break;
		case 'i':
		    if (in_ng) {
			if (!(s=artid_buf))
			    s = artid_buf = fetchlines(art,MESSID_LINE);
			if (*s && *s != '<') {
			    sprintf(scrbuf,"<%s>",artid_buf);
			    s = scrbuf;
			}
		    }
		    else
			s = nullstr;
		    break;
		case 'I':			/* ref article indicator */
		    s = scrbuf;
		    sprintf(scrbuf,"'%s'",indstr);
		    break;
		case 'l':			/* rn library */
#ifdef NEWS_ADMIN
		    s = newsadmin;
#else
		    s = "???";
#endif
		    break;
		case 'L':			/* login id */
		    s = loginName;
		    break;
		case 'm':		/* current mode */
		    s = scrbuf;
		    *s = mode;
		    s[1] = '\0';
		    break;
		case 'M':
		    sprintf(scrbuf,"%ld",(long)dmcount);
		    s = scrbuf;
		    break;
		case 'n':			/* newsgroups */
		    if (in_ng)
			s = ngs_buf = fetchlines(art,NGS_LINE);
		    else
			s = nullstr;
		    break;
		case 'N':			/* full name */
		    s = getval("NAME",realname);
		    break;
		case 'o':			/* organization */
#ifdef IGNOREORG
		    s = getval("NEWSORG",orgname); 
#else
		    s = getenv("NEWSORG");
		    if (s == Nullch) 
			s = getval("ORGANIZATION",orgname); 
#endif
		    s = filexp(s);
#ifdef ORGFILE
		    if (*s == '/') {
			FILE *ofp = fopen(s,"r");

			if (ofp) {
			    if (fgets(scrbuf,sizeof scrbuf,ofp) == NULL)
			    	*scrbuf = '\0';
			    fclose(ofp);
			    s = scrbuf+strlen(scrbuf)-1;
			    if (*scrbuf && *s == '\n')
				*s = '\0';
			    s = scrbuf;
			}
			else
			    s = nullstr;
		    }
#endif
		    break;
		case 'O':
		    s = origdir;
		    break;
		case 'p':
		    s = cwd;
		    break;
		case 'P':
		    s = spool;
		    break;
		case 'q':
		    s = input_str;
		    break;
		case 'r':
		    if (in_ng) {
			parseheader(art);
			if (htype[REFS_LINE].ht_minpos >= 0) {
			    int trial_size=sizeof(scrbuf);
                            char * trial_buf=0;

			    refs_buf = fetchlines(art,REFS_LINE);
			    do {
			      if (trial_buf != 0) trial_size = trial_size*2;
			      free(trial_buf);
			      trial_buf = malloc(trial_size);
			    } while (refscpy(trial_buf,trial_size,refs_buf) < 1);
			    if (strlen(trial_buf)+1 < (sizeof scrbuf)) {
			      strcpy(scrbuf, trial_buf);
			    }
			    else {
			      reducerefs(scrbuf,(sizeof scrbuf),trial_buf);
			    }

			    free(trial_buf);			
			}
			else
			    *scrbuf = '\0';
			s = rindex(scrbuf,'<');
		    }
		    else
			s = nullstr;
		    break;
		case 'R':
		    if (!in_ng) {
			s = nullstr;
			break;
		    }
		    parseheader(art);
		    if (htype[REFS_LINE].ht_minpos >= 0) {
  		        int trial_size=sizeof(scrbuf);
		        char * trial_buf=0;

			refs_buf = fetchlines(art,REFS_LINE);

			do {
			  if (trial_buf != 0) trial_size = trial_size*2;
			  free(trial_buf);
			  trial_buf = malloc(trial_size);
			} while (refscpy(trial_buf,trial_size,refs_buf) < 1);
			reducerefs(scrbuf,(sizeof scrbuf),trial_buf);
			free(trial_buf);
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,MESSID_LINE);
		    if (artid_buf[0] == '<')
			safecat(scrbuf,artid_buf,sizeof(scrbuf));
		    else if (artid_buf[0]) {
			char tmpbuf[64];
    
			sprintf(tmpbuf,"<%s>",artid_buf);
			safecat(scrbuf,tmpbuf,sizeof(scrbuf));
		    }
		    s = scrbuf;
		    break;
		case 's':
		    if (!in_ng) {
			s = nullstr;
			break;
		    }
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE);
						/* get subject handy */
		    while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    if (h = instr(s,"- (nf", TRUE))
			*h = '\0';
		    break;
		case 'S':
		    if (!in_ng) {
			s = nullstr;
			break;
		    }
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE);
						/* get subject handy */
		    if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    break;
		case 't':
		case 'T':
		    if (!in_ng) {
			s = nullstr;
			break;
		    }
		    parseheader(art);
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
					/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    else
			s = "noname";
		    if (*pattern == 'T') {
			if (htype[PATH_LINE].ht_minpos >= 0) {
					/* should we substitute path? */
			    s = path_buf = fetchlines(art,PATH_LINE);
			}
			i = strlen(phostname);
			if (strnEQ(phostname,s,i) && s[i] == '!')
			    s += i + 1;
		    }
		    address_parse = TRUE;	/* just the good part */
		    break;
		case 'u':
		    sprintf(scrbuf,"%ld",(long)toread[ng]);
		    s = scrbuf;
		    break;
		case 'U': {
		    int unseen;

		    if (!in_ng) {
			s = nullstr;
			break;
		    }
		    unseen = (art <= lastart) && !was_read(art);
		    if (selected_only) {
			int selected;

			selected = (curr_artp->flags & AF_SEL);
			sprintf(scrbuf,"%ld",
				(long)selected_count - (selected && unseen));
		    }
		    else
			sprintf(scrbuf,"%ld",(long)toread[ng] - unseen);
		    s = scrbuf;
		    break;
		}
		case 'v': {
		    int selected, unseen;

		    if (in_ng) {
			selected = (curr_artp->flags & AF_SEL);
			unseen = (art <= lastart) && !was_read(art);
			sprintf(scrbuf,"%ld",(long)toread[ng] - selected_count
						- (!selected && unseen));
			s = scrbuf;
		    }
		    else
			s = nullstr;
		    break;
		}
		case 'W':
		    s = threaddir;
		    break;
		case 'x':			/* news library */
		    s = lib;
		    break;
		case 'X':			/* rn library */
		    s = rnlib;
		    break;
		case 'y':
		    s = tmpdir;
		    break;
		case 'z':
		    if (!in_ng) {
			s = nullstr;
			break;
		    }
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
#endif
		    if (stat(s,&filestat) < 0)
			filestat.st_size = 0L;
		    sprintf(scrbuf,"%5ld",(long)filestat.st_size);
		    s = scrbuf;
		    break;
		case 'Z':
		    sprintf(scrbuf,"%ld",(long)selected_count);
		    s = scrbuf;
		    break;
		default:
		    if (--destsize <= 0)
			abort_interp();
		    *dest++ = *pattern | metabit;
		    s = nullstr;
		    break;
		}
	    }
	    if (!s)
		s = nullstr;
	    if (proc_sprintf) {
		sprintf(scrbuf,spfbuf,s);
		s = scrbuf;
	    }
	    pattern++;
	    if (upper || lastcomp) {
		char *t;

		if (s != scrbuf) {
		    safecpy(scrbuf,s,(sizeof scrbuf));
		    s = scrbuf;
		}
		if (upper || !(t=rindex(s,'/')))
		    t = s;
		while (*t && !isalpha(*t))
		    t++;
		if (islower(*t))
		    *t = toupper(*t);
	    }
	    /* Do we have room left? */
	    i = strlen(s);
	    if (destsize <= i)
		abort_interp();
	    destsize -= i;	/* adjust the size now. */

	    /* A maze of twisty little conditions, all alike... */
	    if (address_parse || comment_parse) {
		if (s != scrbuf) {
		    safecpy(scrbuf,s,(sizeof scrbuf));
		    s = scrbuf;
		}
		if (address_parse) {
		    if ((h=index(s,'<')) != Nullch) { /* grab the good part */
			s = h+1;
			if ((h=index(s,'>')) != Nullch)
			    *h = '\0';
		    } else if ((h=index(s,'(')) != Nullch) {
			while (h-- != s && *h == ' ')
			    ;
			h[1] = '\0';		/* or strip the comment */
		    }
		} else {
		    if (!(s = extract_name(s)))
			s = nullstr;
		}
	    }
	    if (metabit) {
		/* set meta bit while copying. */
		i = metabit;		/* maybe get into register */
		if (s == dest) {
		    while (*dest)
			*dest++ |= i;
		} else {
		    while (*s)
			*dest++ = *s++ | i;
		}
	    } else if (re_quote || tick_quote) {
		/* put a backslash before regexp specials while copying. */
		if (s == dest) {
		    /* copy out so we can copy in. */
		    safecpy(scrbuf, s, sizeof scrbuf);
		    s = scrbuf;
		    if (i > sizeof scrbuf)	/* we truncated, ack! */
			abort_interp();
		}
		while (*s) {
		    if ((re_quote && index(regexp_specials, *s))
		     || (tick_quote == 2 && *s == '"')) {
			if (--destsize <= 0)
			    abort_interp();
			*dest++ = '\\';
		    }
		    else if (re_quote && *s == ' ' && s[1] == ' ') {
			*dest++ = ' ';
			*dest++ = '*';
			while (*++s == ' ') ;
			continue;
		    }
		    else if (tick_quote && *s == '\'') {
			if ((destsize -= 3) <= 0)
			    abort_interp();
			*dest++ = '\'';
			*dest++ = '\\';
			*dest++ = '\'';
		    }
		    *dest++ = *s++;
		}
	    } else {
		/* straight copy. */
		if (s == dest) {
		    dest += i;
		} else {
		    while (*s)
			*dest++ = *s++;
		}
	    }
	}
	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';
    if (line_split != Nullch)
	if ((int)strlen(orig_dest) > 79)
	    *line_split = '\n';
getout:
    if (subj_buf != Nullch)	/* return any checked out storage */
	free(subj_buf);
    if (ngs_buf != Nullch)
	free(ngs_buf);
    if (refs_buf != Nullch)
	free(refs_buf);
    if (artid_buf != Nullch)
	free(artid_buf);
    if (reply_buf != Nullch)
	free(reply_buf);
    if (from_buf != Nullch)
	free(from_buf);
    if (path_buf != Nullch)
	free(path_buf);
    if (follow_buf != Nullch)
	free(follow_buf);
    if (dist_buf != Nullch)
	free(dist_buf);
    if (line_buf != Nullch)
	free(line_buf);
    return pattern;			/* where we left off */
}

void
interp(dest,destsize,pattern)
char *dest;
int destsize;
char *pattern;
{
    dointerp(dest,destsize,pattern,Nullch);
#ifdef DEBUG
    if (debug & DEB_FILEXP)
	fputs(dest,stdout);
#endif
}

/* copy a references line, normalizing as we go */

int
refscpy(dest,destsize,src)
register char *dest, *src;
register int destsize;
{
    register char *dot, *at, *beg;
    char tmpbuf[64];
    
    while (*src) {
	if (*src != '<') {
	    if (--destsize <= 0)
		break;
	    *dest++ = '<';
	    at = dot = Nullch;
	    beg = src;
	    while (*src && *src != ' ' && *src != ',') {
		if (*src == '.')
		    dot = src;
		else if (*src == '@')
		    at = src;
		if (--destsize <= 0)
		    break;
		*dest++ = *src++;
	    }
	    if (destsize <= 0)
		break;
	    if (dot && !at) {
		int len;

		*dest = *dot++ = '\0';
		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
		len = strlen(tmpbuf);
		if (destsize > len) {
		    strcpy(dest,tmpbuf);
		    dest = dest + len;
		    destsize -= len;
		}
	    }
	    if (--destsize <= 0)
		break;
	    *dest++ = '>';
	}
	else {
	    while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
	    if (destsize <= 0)
		break;
	}
	while (*src == ' ' || *src == '\t' || *src == '\n' || *src == ',')
	    src++;
	if (*src && --destsize > 0)
	    *dest++ = ' ';
    }
    *dest = '\0';
    return(destsize);
} 

/* reduce a references line */
/* note, modifies src */
void
reducerefs(dest,destsize,src)
register char *dest, *src;
register int destsize;
{
  char * s;
  char * h;
  /* no more than 3 prior references PLUS the
  ** root article allowed, including the one
  ** concatenated below */
  if ((s = rindex(src,'<')) > src) {
    *s = '\0';
    h = rindex(src,'<');
    *s = '<';
    if (h > src) {
      s = index(src+1,'<');
      if (s < h)
	strcpy(s,h);
    }
  }
  if (strlen(src) < destsize) {
    strcpy(dest,src);
  }
  else {
    if ((s = rindex(src,'<')) > src) {
      if (strlen(s) < destsize) {
	strcpy(dest,s);
      }
      else {
	*dest = '\0';
      }
    }
    else {
      *dest = '\0';
    }
  }
}

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

char *
getrealname(uid)
long uid;
{
    char *s, *c;

#ifdef PASSNAMES
#ifdef HAS_GETPWENT
#ifdef notdef
    struct passwd *getpwuid _((uid_t));
#endif
    struct passwd *pwd = getpwuid(uid);

    if (!pwd)
	return nullstr;

    s = pwd->pw_gecos;
#else
    char tmpbuf[512];
    int i;

    getpw(uid, tmpbuf);
    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
	if (s)
	    s = index(s,':')+1;
    }
    if (!s)
	return nullstr;
    cpytill(tmpbuf,s,':');
    s = tmpbuf;
#endif
#ifdef BERKNAMES
#ifdef BERKJUNK
    while (*s && !isalnum(*s) && *s != '&') s++;
#endif
    if ((c = index(s, ',')) != Nullch)
	*c = '\0';
    if ((c = index(s, ';')) != Nullch)
	*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,loginName);		/* before the morning after */
	strcat(c,s+1);
	if (islower(*c))
	    *c = toupper(*c);		/* gack and double gack */
    }
#else
    if ((c = index(s, '(')) != Nullch)
	*c = '\0';
    if ((c = index(s, '-')) != Nullch)
	s = c;
    strcpy(buf,s);
#endif
#ifdef HAS_GETPWENT
    endpwent();
#endif
    return buf;				/* return something static */
#else
    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
	fgets(buf,sizeof buf,tmpfp);
	fclose(tmpfp);
	buf[strlen(buf)-1] = '\0';
	return buf;
    }
    return "PUT YOUR NAME HERE";
#endif
}

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


