1/* Header: /usr/src/games/warp/RCS/intrp.c,v 1.2 87/07/03 00:56:37 games Exp
2 *
3 * Revision 7.0.1.2  86/12/12  16:59:04  lwall
4 * Baseline for net release.
5 *
6 * Revision 7.0.1.1  86/10/16  10:51:43  lwall
7 * Added Damage.  Fixed random bugs.
8 *
9 * Revision 7.0  86/10/08  15:12:19  lwall
10 * Split into separate files.  Added amoebas and pirates.
11 *
12 */
13
14#include "EXTERN.h"
15#include "warp.h"
16#include "sig.h"
17#include "util.h"
18#include "term.h"
19#include "INTERN.h"
20#include "intrp.h"
21
22/* name of this host */
23    char *hostname;
24
25#ifdef TILDENAME
26static char *tildename = NULL;
27static char *tildedir = NULL;
28#endif
29
30static char *getrealname(uid_t);
31#ifdef CONDSUB
32static char *skipinterp(const char *, const char *);
33#endif
34
35__dead static void abort_interp(void);
36
37void
38intrp_init(char *tcbuf)
39{
40    /* get environmental stuff */
41
42    /* get home directory */
43
44    homedir = getenv("HOME");
45    if (homedir == NULL)
46	homedir = getenv("LOGDIR");
47
48    dotdir = getval("DOTDIR",homedir);
49
50    /* get login name */
51
52    logname = getenv("USER");
53    if (logname == NULL)
54	logname = getenv("LOGNAME");
55#ifdef GETLOGIN
56    if (logname == NULL)
57	logname = savestr(getlogin());
58#endif
59
60    /* get the real name of the person (%N) */
61    /* Must be done after logname is read in because BERKNAMES uses that */
62
63    strcpy(tcbuf,getrealname(getuid()));
64    realname = savestr(tcbuf);
65
66    /* name of this host (%H) */
67
68    gethostname(buf,sizeof buf);
69    hostname = savestr(buf);
70    if (strchr(hostname,'.'))
71	hostname = savestr(hostname);
72    else {
73	char hname[128];
74
75	strcpy(hname,hostname);
76	strcat(hname,MYDOMAIN);
77	hostname=savestr(hname);
78    }
79    warplib = savestr(filexp(WARPLIB));
80
81    if (scorespec)			/* that getwd below takes ~1/3 sec. */
82	return;				/* and we do not need it for -s */
83    (void) getcwd(tcbuf, sizeof(tcbuf));/* find working directory name */
84    origdir = savestr(tcbuf);		/* and remember it */
85}
86
87/* expand filename via %, ~, and $ interpretation */
88/* returns pointer to static area */
89/* Note that there is a 1-deep cache of ~name interpretation */
90
91char *
92filexp(const char *s)
93{
94    static char filename[CBUFLEN];
95    char scrbuf[CBUFLEN];
96    char *d;
97
98#ifdef DEBUGGING
99    if (debug & DEB_FILEXP)
100	printf("< %s\r\n",s);
101#endif
102    interp(filename, (sizeof filename), s);			/* interpret any % escapes */
103#ifdef DEBUGGING
104    if (debug & DEB_FILEXP)
105	printf("%% %s\r\n",filename);
106#endif
107    s = filename;
108    if (*s == '~') {	/* does destination start with ~? */
109	if (!*(++s) || *s == '/') {
110	    snprintf(scrbuf, sizeof(scrbuf), "%s%s",homedir,s);
111				/* swap $HOME for it */
112#ifdef DEBUGGING
113    if (debug & DEB_FILEXP)
114	printf("~ %s\r\n",scrbuf);
115#endif
116	    strcpy(filename,scrbuf);
117	}
118	else {
119#ifdef TILDENAME
120	    for (d=scrbuf; isalnum((unsigned char)*s); s++,d++)
121		*d = *s;
122	    *d = '\0';
123	    if (tildedir && strEQ(tildename,scrbuf)) {
124		strcpy(scrbuf,tildedir);
125		strcat(scrbuf, s);
126		strcpy(filename, scrbuf);
127#ifdef DEBUGGING
128		if (debug & DEB_FILEXP)
129		    printf("r %s %s\r\n",tildename,tildedir);
130#endif
131	    }
132	    else {
133		if (tildename) {
134		    free(tildename);
135		    free(tildedir);
136		}
137		tildedir = NULL;
138		tildename = savestr(scrbuf);
139		{
140		    struct passwd *pwd = getpwnam(tildename);
141
142		    snprintf(scrbuf, sizeof(scrbuf), "%s%s",pwd->pw_dir,s);
143		    tildedir = savestr(pwd->pw_dir);
144		    strcpy(filename,scrbuf);
145		    endpwent();
146		}
147	    }
148#else /* !TILDENAME */
149#ifdef VERBOSE
150	    IF(verbose)
151		fputs("~loginname not implemented.\r\n",stdout);
152	    ELSE
153#endif
154#ifdef TERSE
155		fputs("~login not impl.\r\n",stdout);
156#endif
157#endif
158	}
159    }
160    else if (*s == '$') {	/* starts with some env variable? */
161	d = scrbuf;
162	*d++ = '%';
163	if (s[1] == '{')
164	    strcpy(d,s+2);
165	else {
166	    *d++ = '{';
167	    for (s++; isalnum((unsigned char)*s); s++) *d++ = *s;
168				/* skip over token */
169	    *d++ = '}';
170	    strcpy(d,s);
171	}
172#ifdef DEBUGGING
173	if (debug & DEB_FILEXP)
174	    printf("$ %s\r\n",scrbuf);
175#endif
176	interp(filename, (sizeof filename), scrbuf);
177					/* this might do some extra '%'s but */
178					/* that is how the Mercedes Benz */
179    }
180#ifdef DEBUGGING
181    if (debug & DEB_FILEXP)
182	printf("> %s\r\n",filename);
183#endif
184    return filename;
185}
186
187#ifdef CONDSUB
188/* skip interpolations */
189
190static char *
191skipinterp(const char *pattern, const char *stoppers)
192{
193
194    while (*pattern && (!stoppers || !strchr(stoppers,*pattern))) {
195#ifdef DEBUGGING
196	if (debug & 8)
197	    printf("skipinterp till %s at %s\r\n",stoppers?stoppers:"",pattern);
198#endif
199	if (*pattern == '%' && pattern[1]) {
200	    switch (*++pattern) {
201	    case '{':
202		for (pattern++; *pattern && *pattern != '}'; pattern++)
203		    if (*pattern == '\\')
204			pattern++;
205		break;
206#ifdef CONDSUB
207	    case '(': {
208		pattern = skipinterp(pattern+1,"!=");
209		if (!*pattern)
210		    goto getout;
211		for (pattern++; *pattern && *pattern != '?'; pattern++)
212		    if (*pattern == '\\')
213			pattern++;
214		if (!*pattern)
215		    goto getout;
216		pattern = skipinterp(pattern+1,":)");
217		if (*pattern == ':')
218		    pattern = skipinterp(pattern+1,")");
219		break;
220	    }
221#endif
222#ifdef BACKTICK
223	    case '`': {
224		pattern = skipinterp(pattern+1,"`");
225		break;
226	    }
227#endif
228#ifdef PROMPTTTY
229	    case '"':
230		pattern = skipinterp(pattern+1,"\"");
231		break;
232#endif
233	    default:
234		break;
235	    }
236	    pattern++;
237	}
238	else {
239	    if (*pattern == '^' && pattern[1])
240		pattern += 2;
241	    else if (*pattern == '\\' && pattern[1])
242		pattern += 2;
243	    else
244		pattern++;
245	}
246    }
247getout:
248    return __UNCONST(pattern);			/* where we left off */
249}
250#endif
251
252static char *mygets(char *str, size_t n)
253{
254    char *ret;
255    size_t last;
256
257    if ((ret = fgets(str, n, stdin)) != NULL) {
258        last = strlen(str) - 1;
259
260        if (str[last] == '\n')
261            str[last] = '\0';
262    }
263
264    return ret;
265}
266
267/* interpret interpolations */
268
269char *
270dointerp(char *dest, size_t destsize, const char *pattern, const char *stoppers)
271{
272    char *s;
273    int i;
274    char scrbuf[512];
275    bool upper = false;
276    bool lastcomp = false;
277    int metabit = 0;
278
279    while (*pattern && (!stoppers || !strchr(stoppers,*pattern))) {
280#ifdef DEBUGGING
281	if (debug & 8)
282	    printf("dointerp till %s at %s\r\n",stoppers?stoppers:"",pattern);
283#endif
284	if (*pattern == '%' && pattern[1]) {
285	    upper = false;
286	    lastcomp = false;
287	    for (s=NULL; !s; ) {
288		switch (*++pattern) {
289		case '^':
290		    upper = true;
291		    break;
292		case '_':
293		    lastcomp = true;
294		    break;
295		case '{':
296		    pattern = cpytill(scrbuf,pattern+1,'}');
297		    if ((s = strchr(scrbuf,'-')) != NULL)
298			*s++ = '\0';
299		    else
300			s = nullstr;
301		    s = getval(scrbuf,s);
302		    break;
303#ifdef CONDSUB
304		case '(': {
305		    char rch;
306		    bool matched;
307
308		    pattern = dointerp(dest,destsize,pattern+1,"!=");
309		    rch = *pattern;
310		    if (rch == '!')
311			pattern++;
312		    if (*pattern != '=')
313			goto getout;
314		    pattern = cpytill(scrbuf,pattern+1,'?');
315		    if (!*pattern)
316			goto getout;
317		    if (*scrbuf == '^' && scrbuf[strlen(scrbuf)-1] == '$') {
318			scrbuf[strlen(scrbuf)-1] = '\0';
319			matched = strEQ(scrbuf+1,dest);
320		    }
321		    else
322			matched = instr(dest,scrbuf) != NULL;
323		    if (matched==(rch == '=')) {
324			pattern = dointerp(dest,destsize,pattern+1,":)");
325			if (*pattern == ':')
326			    pattern = skipinterp(pattern+1,")");
327		    }
328		    else {
329			pattern = skipinterp(pattern+1,":)");
330			if (*pattern == ':')
331			    pattern++;
332			pattern = dointerp(dest,destsize,pattern,")");
333		    }
334		    s = dest;
335		    break;
336		}
337#endif
338#ifdef BACKTICK
339		case '`': {
340		    FILE *pipefp;
341
342		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
343		    pipefp = popen(scrbuf,"r");
344		    if (pipefp != NULL) {
345			int len;
346
347			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
348			    pipefp);
349			scrbuf[len] = '\0';
350			pclose(pipefp);
351		    }
352		    else {
353			printf("\r\nCan't run %s\r\n",scrbuf);
354			*scrbuf = '\0';
355		    }
356		    for (s=scrbuf; *s; s++) {
357			if (*s == '\n') {
358			    if (s[1])
359				*s = ' ';
360			    else
361				*s = '\0';
362			}
363		    }
364		    s = scrbuf;
365		    break;
366		}
367#endif
368#ifdef PROMPTTTY
369		case '"':
370		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
371		    fputs(scrbuf,stdout);
372		    resetty();
373		    mygets(scrbuf, sizeof(scrbuf));
374		    crmode();
375		    raw();
376		    noecho();
377		    nonl();
378		    s = scrbuf;
379		    break;
380#endif
381		case '~':
382		    s = homedir;
383		    break;
384		case '.':
385		    s = dotdir;
386		    break;
387		case '$':
388		    s = scrbuf;
389		    snprintf(scrbuf, sizeof(scrbuf), "%d",getpid());
390		    break;
391		case 'H':			/* host name */
392		    s = hostname;
393		    break;
394		case 'L':			/* login id */
395		    s = logname;
396		    break;
397		case 'N':			/* full name */
398		    s = getval("NAME",realname);
399		    break;
400		case 'O':
401		    s = origdir;
402		    break;
403		case 'p':
404		    s = cwd;
405		    break;
406		case 'X':			/* warp library */
407		    s = warplib;
408		    break;
409		default:
410		    if (--destsize <= 0)
411			abort_interp();
412		    *dest++ = *pattern | metabit;
413		    s = nullstr;
414		    break;
415		}
416	    }
417	    if (!s)
418		s = nullstr;
419	    pattern++;
420	    if (upper || lastcomp) {
421		char *t;
422
423		if (s != scrbuf) {
424		    safecpy(scrbuf,s,(sizeof scrbuf));
425		    s = scrbuf;
426		}
427		if (upper || !(t=strrchr(s,'/')))
428		    t = s;
429		while (*t && !isalpha((unsigned char)*t)) {
430		    t++;
431		    *t = toupper((unsigned char)*t);
432		}
433	    }
434	    i = metabit;		/* maybe get into register */
435	    if (s == dest) {
436		while (*dest) {
437		    if (--destsize <= 0)
438			abort_interp();
439		    *dest++ |= i;
440		}
441	    }
442	    else {
443		while (*s) {
444		    if (--destsize <= 0)
445			abort_interp();
446		    *dest++ = *s++ | i;
447		}
448	    }
449	}
450	else {
451	    if (--destsize <= 0)
452		abort_interp();
453	    if (*pattern == '^' && pattern[1]) {
454		++pattern;			/* skip uparrow */
455		i = *pattern;		/* get char into a register */
456		if (i == '?')
457		    *dest++ = '\177' | metabit;
458		else if (i == '(') {
459		    metabit = 0200;
460		    destsize++;
461		}
462		else if (i == ')') {
463		    metabit = 0;
464		    destsize++;
465		}
466		else
467		    *dest++ = (i & 037) | metabit;
468		pattern++;
469	    }
470	    else if (*pattern == '\\' && pattern[1]) {
471		++pattern;			/* skip backslash */
472		i = *pattern;		/* get char into a register */
473
474		/* this used to be a switch but the if may save space */
475
476		if (i >= '0' && i <= '7') {
477		    i = 1;
478		    while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
479			i <<= 3;
480			i += *pattern++ - '0';
481		    }
482		    *dest++ = (i & 0377) | metabit;
483		    --pattern;
484		}
485		else if (i == 'b')
486		    *dest++ = '\b' | metabit;
487		else if (i == 'f')
488		    *dest++ = '\f' | metabit;
489		else if (i == 'n')
490		    *dest++ = '\n' | metabit;
491		else if (i == 'r')
492		    *dest++ = '\r' | metabit;
493		else if (i == 't')
494		    *dest++ = '\t' | metabit;
495		else
496		    *dest++ = i | metabit;
497		pattern++;
498	    }
499	    else
500		*dest++ = *pattern++ | metabit;
501	}
502    }
503    *dest = '\0';
504getout:
505    return __UNCONST(pattern);			/* where we left off */
506}
507
508void
509interp(char *dest, size_t destsize, const char *pattern)
510{
511    (void) dointerp(dest,destsize,pattern,NULL);
512#ifdef DEBUGGING
513    if (debug & DEB_FILEXP)
514	fputs(dest,stdout);
515#endif
516}
517
518/* get the person's real name from /etc/passwd */
519/* (string is overwritten, so it must be copied) */
520
521static char *
522getrealname(uid_t uid)
523{
524    char *s, *c;
525
526#ifdef PASSNAMES
527    struct passwd *pwd = getpwuid(uid);
528
529    s = pwd->pw_gecos;
530#ifdef BERKNAMES
531#ifdef BERKJUNK
532    while (*s && !isalnum(*s) && *s != '&') s++;
533#endif
534    if ((c = strchr(s, ',')) != NULL)
535	*c = '\0';
536    if ((c = strchr(s, ';')) != NULL)
537	*c = '\0';
538    s = cpytill(buf,s,'&');
539    if (*s == '&') {			/* whoever thought this one up was */
540	c = buf + strlen(buf);		/* in the middle of the night */
541	strcat(c,logname);		/* before the morning after */
542	strcat(c,s+1);
543	if (islower((unsigned char)*c))
544	    *c = toupper((unsigned char)*c);		/* gack and double gack */
545    }
546#else
547    if ((c = strchr(s, '(')) != NULL)
548	*c = '\0';
549    if ((c = strchr(s, '-')) != NULL)
550	s = c;
551    strcpy(buf,tmpbuf);
552#endif
553    endpwent();
554    return buf;				/* return something static */
555#else
556    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != NULL) {
557	fgets(buf,sizeof buf,tmpfp);
558	fclose(tmpfp);
559    }
560    else {
561	resetty();
562	printf("What is your name? ");
563	fgets(buf,(sizeof buf),stdin);
564	crmode();
565	raw();
566	noecho();
567	nonl();
568	if (fork())
569	    wait(0);
570	else {
571	    setgid(getgid());
572	    if ((tmpfp = fopen(filexp(FULLNAMEFILE),"w")) == NULL)
573		exit(1);
574	    fprintf(tmpfp, "%s\n", buf);
575	    fclose(tmpfp);
576	    exit(0);
577	}
578    }
579    buf[strlen(buf)-1] = '\0';
580    return buf;
581#endif
582}
583
584static void
585abort_interp(void)
586{
587    fputs("\r\n% interp buffer overflow!\r\n",stdout);
588    sig_catcher(0);
589}
590