xref: /netbsd/bin/csh/csh.c (revision 84e62ad1)
1 /* $NetBSD: csh.c,v 1.56 2022/09/15 11:35:06 martin Exp $ */
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)csh.c	8.2 (Berkeley) 10/12/93";
41 #else
42 __RCSID("$NetBSD: csh.c,v 1.56 2022/09/15 11:35:06 martin Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <sys/types.h>
47 #include <sys/ioctl.h>
48 #include <sys/stat.h>
49 
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <locale.h>
53 #include <paths.h>	/* should this be included in pathnames.h instead? */
54 #include <pwd.h>
55 #include <stdarg.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <time.h>
59 #include <unistd.h>
60 #include <vis.h>
61 
62 #include "csh.h"
63 #include "extern.h"
64 #include "pathnames.h"
65 #include "proc.h"
66 
67 /*
68  * C Shell
69  *
70  * Bill Joy, UC Berkeley, California, USA
71  * October 1978, May 1980
72  *
73  * Jim Kulp, IIASA, Laxenburg, Austria
74  * April 1980
75  *
76  * Christos Zoulas, Cornell University
77  * June, 1991
78  */
79 
80 FILE *cshin, *cshout, *csherr;
81 struct timespec time0;
82 struct rusage ru0;
83 struct varent shvhed, aliases;
84 Char HISTSUB;
85 int editing;
86 
87 int child;
88 int chkstop;
89 int didfds;
90 int doneinp;
91 int exiterr;
92 int haderr;
93 int havhash;
94 int intact;
95 int intty;
96 int justpr;
97 int loginsh;
98 int neednote;
99 int noexec;
100 int pjobs;
101 int setintr;
102 int timflg;
103 
104 Char *arginp;
105 Char *ffile;
106 int onelflg;
107 Char *shtemp;
108 
109 time_t chktim;
110 Char *doldol;
111 pid_t backpid;
112 gid_t egid, gid;
113 uid_t euid, uid;
114 int shpgrp;
115 int tpgrp;
116 
117 int opgrp;
118 
119 int SHIN;
120 int SHOUT;
121 int SHERR;
122 int OLDSTD;
123 
124 jmp_buf reslab;
125 
126 Char *gointr;
127 
128 sig_t parintr;
129 sig_t parterm;
130 
131 struct Bin B;
132 
133 struct Ain lineloc;
134 int cantell;
135 Char *lap;
136 struct whyle *whyles;
137 
138 struct wordent *alhistp,*alhistt;
139 
140 int AsciiOnly;
141 int gflag;
142 long pnleft;
143 Char *pargs;
144 Char *pargcp;
145 struct Hist Histlist;
146 struct wordent paraml;
147 int eventno;
148 int lastev;
149 Char HIST;
150 Char HISTSUB;
151 const char *bname;
152 Char *Vsav;
153 Char *Vdp;
154 Char *Vexpath;
155 char **Vt;
156 Char **evalvec;
157 Char *evalp;
158 Char *word_chars;
159 Char *STR_SHELLPATH;
160 #ifdef _PATH_BSHELL
161 Char *STR_BSHELL;
162 #endif
163 Char *STR_WORD_CHARS;
164 Char **STR_environ;
165 #ifdef EDIT
166 EditLine *el;
167 History *hi;
168 #endif
169 int editing;
170 
171 Char *dumphist[] = {STRhistory, STRmh, 0, 0};
172 Char *tildehist[] = {STRsource, STRmh, STRtildothist, 0};
173 
174 int nofile = 0;
175 int batch = 0;
176 int enterhist = 0;
177 int fast = 0;
178 int mflag = 0;
179 int nexececho = 0;
180 int nverbose = 0;
181 int prompt = 1;
182 int quitit = 0;
183 int reenter = 0;
184 
185 extern char **environ;
186 
187 static ssize_t readf(void *, void *, size_t);
188 static off_t seekf(void *, off_t, int);
189 static ssize_t writef(void *, const void *, size_t);
190 static int closef(void *);
191 static int srccat(Char *, Char *);
192 static int srcfile(const char *, int, int);
193 __dead static void phup(int);
194 static void srcunit(int, int, int);
195 static void mailchk(void);
196 #ifndef _PATH_DEFPATH
197 static Char **defaultpath(void);
198 #endif
199 
200 int
main(int argc,char * argv[])201 main(int argc, char *argv[])
202 {
203     struct sigaction oact;
204     Char *cp;
205     char *tcp, **tempv;
206     const char *ecp;
207     sigset_t nsigset;
208     int f;
209 
210     cshin = stdin;
211     cshout = stdout;
212     csherr = stderr;
213 
214     setprogname(argv[0]);
215     settimes();			/* Immed. estab. timing base */
216 
217     /*
218      * Initialize non constant strings
219      */
220 #ifdef _PATH_BSHELL
221     STR_BSHELL = SAVE(_PATH_BSHELL);
222 #endif
223 #ifdef _PATH_CSHELL
224     STR_SHELLPATH = SAVE(_PATH_CSHELL);
225 #endif
226     STR_environ = blk2short(environ);
227     environ = short2blk(STR_environ);	/* So that we can free it */
228     STR_WORD_CHARS = SAVE(WORD_CHARS);
229 
230     HIST = '!';
231     HISTSUB = '^';
232     word_chars = STR_WORD_CHARS;
233 
234     tempv = argv;
235     if (eq(str2short(tempv[0]), STRaout))	/* A.out's are quittable */
236 	quitit = 1;
237     uid = getuid();
238     gid = getgid();
239     euid = geteuid();
240     egid = getegid();
241     /*
242      * We are a login shell if: 1. we were invoked as -<something> and we had
243      * no arguments 2. or we were invoked only with the -l flag
244      */
245     loginsh = (**tempv == '-' && argc == 1) ||
246 	(argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' &&
247 	 tempv[1][2] == '\0');
248 
249     if (loginsh && **tempv != '-') {
250 	/*
251 	 * Mangle the argv space
252 	 */
253 	tempv[1][0] = '\0';
254 	tempv[1][1] = '\0';
255 	tempv[1] = NULL;
256 	for (tcp = *tempv; *tcp++;)
257 	    continue;
258 	for (tcp--; tcp >= *tempv; tcp--)
259 	    tcp[1] = tcp[0];
260 	*++tcp = '-';
261 	argc--;
262     }
263     if (loginsh)
264 	(void)time(&chktim);
265 
266     AsciiOnly = 1;
267 #ifdef NLS
268     (void)setlocale(LC_ALL, "");
269     {
270 	int k;
271 
272 	for (k = 0200; k <= 0377 && !Isprint(k); k++)
273 	    continue;
274 	AsciiOnly = k > 0377;
275     }
276 #else
277     AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
278 #endif				/* NLS */
279 
280     /*
281      * Move the descriptors to safe places. The variable didfds is 0 while we
282      * have only FSH* to work with. When didfds is true, we have 0,1,2 and
283      * prefer to use these.
284      */
285     initdesc();
286     /*
287      * XXX: This is to keep programs that use stdio happy.
288      *	    what we really want is freunopen() ....
289      *	    Closing cshin cshout and csherr (which are really stdin stdout
290      *	    and stderr at this point and then reopening them in the same order
291      *	    gives us again stdin == cshin stdout == cshout and stderr == csherr.
292      *	    If that was not the case builtins like printf that use stdio
293      *	    would break. But in any case we could fix that with memcpy and
294      *	    a bit of pointer manipulation...
295      *	    Fortunately this is not needed under the current implementation
296      *	    of stdio.
297      */
298     (void)fclose(cshin);
299     (void)fclose(cshout);
300     (void)fclose(csherr);
301     if (!(cshin  = funopen2((void *) &SHIN,  readf, writef, seekf, NULL,
302 	closef)))
303 	exit(1);
304     if (!(cshout = funopen2((void *) &SHOUT, readf, writef, seekf, NULL,
305 	closef)))
306 	exit(1);
307     if (!(csherr = funopen2((void *) &SHERR, readf, writef, seekf, NULL,
308 	closef)))
309 	exit(1);
310     (void)setvbuf(cshin,  NULL, _IOLBF, 0);
311     (void)setvbuf(cshout, NULL, _IOLBF, 0);
312     (void)setvbuf(csherr, NULL, _IOLBF, 0);
313 
314     /*
315      * Initialize the shell variables. ARGV and PROMPT are initialized later.
316      * STATUS is also munged in several places. CHILD is munged when
317      * forking/waiting
318      */
319     set(STRstatus, Strsave(STR0));
320 
321     if ((ecp = getenv("HOME")) != NULL)
322 	cp = quote(SAVE(ecp));
323     else
324 	cp = NULL;
325 
326     if (cp == NULL)
327 	fast = 1;		/* No home -> can't read scripts */
328     else
329 	set(STRhome, cp);
330     dinit(cp);			/* dinit thinks that HOME == cwd in a login
331 				 * shell */
332     /*
333      * Grab other useful things from the environment. Should we grab
334      * everything??
335      */
336     if ((ecp = getenv("LOGNAME")) != NULL ||
337 	(ecp = getenv("USER")) != NULL)
338 	set(STRuser, quote(SAVE(ecp)));
339     if ((ecp = getenv("TERM")) != NULL)
340 	set(STRterm, quote(SAVE(ecp)));
341 
342     /*
343      * Re-initialize path if set in environment
344      */
345     if ((ecp = getenv("PATH")) == NULL) {
346 #ifdef _PATH_DEFPATH
347 	importpath(str2short(_PATH_DEFPATH));
348 #else
349 	setq(STRpath, defaultpath(), &shvhed);
350 #endif
351     } else {
352 	importpath(str2short(ecp));
353     }
354 
355     set(STRshell, Strsave(STR_SHELLPATH));
356 
357     doldol = putn((int) getpid());	/* For $$ */
358     shtemp = Strspl(STRtmpsh, doldol);	/* For << */
359 
360     /*
361      * Record the interrupt states from the parent process. If the parent is
362      * non-interruptible our hand must be forced or we (and our children) won't
363      * be either. Our children inherit termination from our parent. We catch it
364      * only if we are the login shell.
365      */
366     /* parents interruptibility */
367     (void)sigaction(SIGINT, NULL, &oact);
368     parintr = oact.sa_handler;
369     (void)sigaction(SIGTERM, NULL, &oact);
370     parterm = oact.sa_handler;
371 
372     /* catch these all, login shell or not */
373     (void)signal(SIGHUP, phup);	/* exit processing on HUP */
374     (void)signal(SIGXCPU, phup);	/* ...and on XCPU */
375     (void)signal(SIGXFSZ, phup);	/* ...and on XFSZ */
376 
377     /*
378      * Process the arguments.
379      *
380      * Note that processing of -v/-x is actually delayed till after script
381      * processing.
382      *
383      * We set the first character of our name to be '-' if we are a shell
384      * running interruptible commands.  Many programs which examine ps'es
385      * use this to filter such shells out.
386      */
387     argc--, tempv++;
388     while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) {
389 	do
390 	    switch (*tcp++) {
391 	    case 0:		/* -	Interruptible, no prompt */
392 		prompt = 0;
393 		setintr = 1;
394 		nofile = 1;
395 		break;
396 	    case 'b':		/* -b	Next arg is input file */
397 		batch = 1;
398 		break;
399 	    case 'c':		/* -c	Command input from arg */
400 		if (argc == 1)
401 		    xexit(0);
402 		argc--, tempv++;
403 		arginp = SAVE(tempv[0]);
404 		prompt = 0;
405 		nofile = 1;
406 		break;
407 	    case 'e':		/* -e	Exit on any error */
408 		exiterr = 1;
409 		break;
410 	    case 'f':		/* -f	Fast start */
411 		fast = 1;
412 		break;
413 	    case 'i':		/* -i	Interactive, even if !intty */
414 		intact = 1;
415 		nofile = 1;
416 		break;
417 	    case 'm':		/* -m	read .cshrc (from su) */
418 		mflag = 1;
419 		break;
420 	    case 'n':		/* -n	Don't execute */
421 		noexec = 1;
422 		break;
423 	    case 'q':		/* -q	(Undoc'd) ... die on quit */
424 		quitit = 1;
425 		break;
426 	    case 's':		/* -s	Read from std input */
427 		nofile = 1;
428 		break;
429 	    case 't':		/* -t	Read one line from input */
430 		onelflg = 2;
431 		prompt = 0;
432 		nofile = 1;
433 		break;
434 	    case 'v':		/* -v	Echo hist expanded input */
435 		nverbose = 1;	/* ... later */
436 		break;
437 	    case 'x':		/* -x	Echo just before execution */
438 		nexececho = 1;	/* ... later */
439 		break;
440 	    case 'V':		/* -V	Echo hist expanded input */
441 		setNS(STRverbose);	/* NOW! */
442 		break;
443 	    case 'X':		/* -X	Echo just before execution */
444 		setNS(STRecho);	/* NOW! */
445 		break;
446 
447 	} while (*tcp);
448 	tempv++, argc--;
449     }
450 
451     if (quitit)			/* With all due haste, for debugging */
452 	(void)signal(SIGQUIT, SIG_DFL);
453 
454     /*
455      * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
456      * arguments the first of them is the name of a shell file from which to
457      * read commands.
458      */
459     if (nofile == 0 && argc > 0) {
460 	nofile = open(tempv[0], O_RDONLY);
461 	if (nofile < 0) {
462 	    child = 1;		/* So this doesn't return */
463 	    stderror(ERR_SYSTEM, tempv[0], strerror(errno));
464 	}
465 	ffile = SAVE(tempv[0]);
466 	/*
467 	 * Replace FSHIN. Handle /dev/std{in,out,err} specially
468 	 * since once they are closed we cannot open them again.
469 	 * In that case we use our own saved descriptors
470 	 */
471 	if ((SHIN = dmove(nofile, FSHIN)) < 0)
472 	    switch(nofile) {
473 	    case 0:
474 		SHIN = FSHIN;
475 		break;
476 	    case 1:
477 		SHIN = FSHOUT;
478 		break;
479 	    case 2:
480 		SHIN = FSHERR;
481 		break;
482 	    default:
483 		stderror(ERR_SYSTEM, tempv[0], strerror(errno));
484 		/* NOTREACHED */
485 	    }
486 	(void)ioctl(SHIN, FIOCLEX, NULL);
487 	prompt = 0;
488 	 /* argc not used any more */ tempv++;
489     }
490 
491     intty = isatty(SHIN);
492     intty |= intact;
493     if (intty || (intact && isatty(SHOUT))) {
494 	if (!batch && (uid != euid || gid != egid)) {
495 	    errno = EACCES;
496 	    child = 1;		/* So this doesn't return */
497 	    stderror(ERR_SYSTEM, "csh", strerror(errno));
498 	}
499     }
500     /*
501      * Decide whether we should play with signals or not. If we are explicitly
502      * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
503      * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
504      * Note that in only the login shell is it likely that parent may have set
505      * signals to be ignored
506      */
507     if (loginsh || intact || (intty && isatty(SHOUT)))
508 	setintr = 1;
509     settell();
510     /*
511      * Save the remaining arguments in argv.
512      */
513     setq(STRargv, blk2short(tempv), &shvhed);
514 
515     /*
516      * Set up the prompt.
517      */
518     if (prompt) {
519 	set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent));
520 	/* that's a meta-questionmark */
521 	set(STRprompt2, Strsave(STRmquestion));
522     }
523 
524     /*
525      * If we are an interactive shell, then start fiddling with the signals;
526      * this is a tricky game.
527      */
528     shpgrp = getpgrp();
529     opgrp = tpgrp = -1;
530     if (setintr) {
531 	**argv = '-';
532 	if (!quitit)		/* Wary! */
533 	    (void)signal(SIGQUIT, SIG_IGN);
534 	(void)signal(SIGINT, pintr);
535 	sigemptyset(&nsigset);
536 	(void)sigaddset(&nsigset, SIGINT);
537 	(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
538 	(void)signal(SIGTERM, SIG_IGN);
539 	if (quitit == 0 && arginp == 0) {
540 	    (void)signal(SIGTSTP, SIG_IGN);
541 	    (void)signal(SIGTTIN, SIG_IGN);
542 	    (void)signal(SIGTTOU, SIG_IGN);
543 	    /*
544 	     * Wait till in foreground, in case someone stupidly runs csh &
545 	     * dont want to try to grab away the tty.
546 	     */
547 	    if (isatty(FSHERR))
548 		f = FSHERR;
549 	    else if (isatty(FSHOUT))
550 		f = FSHOUT;
551 	    else if (isatty(OLDSTD))
552 		f = OLDSTD;
553 	    else
554 		f = -1;
555     retry:
556 	    if ((tpgrp = tcgetpgrp(f)) != -1) {
557 		if (tpgrp != shpgrp) {
558 		    sig_t old = signal(SIGTTIN, SIG_DFL);
559 		    (void)kill(0, SIGTTIN);
560 		    (void)signal(SIGTTIN, old);
561 		    goto retry;
562 		}
563 		opgrp = shpgrp;
564 		shpgrp = getpid();
565 		tpgrp = shpgrp;
566 		/*
567 		 * Setpgid will fail if we are a session leader and
568 		 * mypid == mypgrp (POSIX 4.3.3)
569 		 */
570 		if (opgrp != shpgrp)
571 		    if (setpgid(0, shpgrp) == -1)
572 			goto notty;
573 		/*
574 		 * We do that after we set our process group, to make sure
575 		 * that the process group belongs to a process in the same
576 		 * session as the tty (our process and our group) (POSIX 7.2.4)
577 		 */
578 		if (tcsetpgrp(f, shpgrp) == -1)
579 		    goto notty;
580 		(void)ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL);
581 	    }
582 	    if (tpgrp == -1) {
583 notty:
584 		(void)fprintf(csherr, "Warning: no access to tty (%s).\n",
585 			       strerror(errno));
586 		(void)fprintf(csherr, "Thus no job control in this shell.\n");
587 	    }
588 	}
589     }
590     if ((setintr == 0) && (parintr == SIG_DFL))
591 	setintr = 1;
592     (void)signal(SIGCHLD, pchild);	/* while signals not ready */
593 
594     /*
595      * Set an exit here in case of an interrupt or error reading the shell
596      * start-up scripts.
597      */
598     reenter = setexit();	/* PWP */
599     haderr = 0;			/* In case second time through */
600     if (!fast && reenter == 0) {
601 	/* Will have value(STRhome) here because set fast if don't */
602 	{
603 	    sig_t oparintr;
604 	    sigset_t osigset;
605 	    int osetintr;
606 
607 	    oparintr = parintr;
608 	    osetintr = setintr;
609 	    sigemptyset(&nsigset);
610 	    (void)sigaddset(&nsigset, SIGINT);
611 	    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
612 
613 	    setintr = 0;
614 	    parintr = SIG_IGN;	/* Disable onintr */
615 #ifdef _PATH_DOTCSHRC
616 	    (void)srcfile(_PATH_DOTCSHRC, 0, 0);
617 #endif
618 	    if (!fast && !arginp && !onelflg)
619 		dohash(NULL, NULL);
620 #ifdef _PATH_DOTLOGIN
621 	    if (loginsh)
622 		(void)srcfile(_PATH_DOTLOGIN, 0, 0);
623 #endif
624 	    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
625 	    setintr = osetintr;
626 	    parintr = oparintr;
627 	}
628 	(void)srccat(value(STRhome), STRsldotcshrc);
629 
630 	if (!fast && !arginp && !onelflg && !havhash)
631 	    dohash(NULL, NULL);
632 	/*
633 	 * Source history before .login so that it is available in .login
634 	 */
635 	if ((cp = value(STRhistfile)) != STRNULL)
636 	    tildehist[2] = cp;
637 	dosource(tildehist, NULL);
638         if (loginsh)
639 	      (void)srccat(value(STRhome), STRsldotlogin);
640     }
641 
642     /*
643      * Now are ready for the -v and -x flags
644      */
645     if (nverbose)
646 	setNS(STRverbose);
647     if (nexececho)
648 	setNS(STRecho);
649 
650     /*
651      * All the rest of the world is inside this call. The argument to process
652      * indicates whether it should catch "error unwinds".  Thus if we are a
653      * interactive shell our call here will never return by being blown past on
654      * an error.
655      */
656     process(setintr);
657 
658     /*
659      * Mop-up.
660      */
661     if (intty) {
662 	if (loginsh) {
663 	    (void)fprintf(cshout, "logout\n");
664 	    (void)close(SHIN);
665 	    child = 1;
666 	    goodbye();
667 	}
668 	else {
669 	    (void)fprintf(cshout, "exit\n");
670 	}
671     }
672     rechist();
673     exitstat();
674     /* NOTREACHED */
675 }
676 
677 void
untty(void)678 untty(void)
679 {
680     if (tpgrp > 0) {
681 	(void)setpgid(0, opgrp);
682 	(void)tcsetpgrp(FSHTTY, opgrp);
683     }
684 }
685 
686 void
importpath(Char * cp)687 importpath(Char *cp)
688 {
689     Char *dp, **pv;
690     int c, i;
691 
692     i = 0;
693     for (dp = cp; *dp; dp++)
694 	if (*dp == ':')
695 	    i++;
696     /*
697      * i+2 where i is the number of colons in the path. There are i+1
698      * directories in the path plus we need room for a zero terminator.
699      */
700     pv = xcalloc((size_t) (i + 2), sizeof(*pv));
701     dp = cp;
702     i = 0;
703     if (*dp)
704 	for (;;) {
705 	    if ((c = *dp) == ':' || c == 0) {
706 		*dp = 0;
707 		pv[i++] = Strsave(*cp ? cp : STRdot);
708 		if (c) {
709 		    cp = dp + 1;
710 		    *dp = ':';
711 		}
712 		else
713 		    break;
714 	    }
715 	    dp++;
716 	}
717     pv[i] = 0;
718     setq(STRpath, pv, &shvhed);
719 }
720 
721 /*
722  * Source to the file which is the catenation of the argument names.
723  */
724 static int
srccat(Char * cp,Char * dp)725 srccat(Char *cp, Char *dp)
726 {
727     Char *ep;
728     char *ptr;
729 
730     ep = Strspl(cp, dp);
731     ptr = short2str(ep);
732     free(ep);
733     return srcfile(ptr, mflag ? 0 : 1, 0);
734 }
735 
736 /*
737  * Source to a file putting the file descriptor in a safe place (> 2).
738  */
739 static int
srcfile(const char * f,int onlyown,int flag)740 srcfile(const char *f, int onlyown, int flag)
741 {
742     int unit;
743 
744     if ((unit = open(f, O_RDONLY)) == -1)
745 	return 0;
746     unit = dmove(unit, -1);
747 
748     (void) ioctl(unit, FIOCLEX, NULL);
749     srcunit(unit, onlyown, flag);
750     return 1;
751 }
752 
753 /*
754  * Source to a unit.  If onlyown it must be our file or our group or
755  * we don't chance it.	This occurs on ".cshrc"s and the like.
756  */
757 int insource;
758 
759 static void
srcunit(int unit,int onlyown,int hflg)760 srcunit(int unit, int onlyown, int hflg)
761 {
762     /* We have to push down a lot of state here */
763     /* All this could go into a structure */
764     struct whyle *oldwhyl;
765     struct Bin saveB;
766     sigset_t nsigset, osigset;
767     jmp_buf oldexit;
768     Char *oarginp, *oevalp, **oevalvec, *ogointr;
769     Char OHIST;
770     int oSHIN, oinsource, oldintty, oonelflg;
771     int oenterhist, otell;
772     /* The (few) real local variables */
773     int my_reenter;
774 
775     oSHIN = -1;
776     oldintty = intty;
777     oinsource = insource;
778     oldwhyl = whyles;
779     ogointr = gointr;
780     oarginp = arginp;
781     oevalp = evalp;
782     oevalvec = evalvec;
783     oonelflg = onelflg;
784     oenterhist = enterhist;
785     OHIST = HIST;
786     otell = cantell;
787 
788     if (unit < 0)
789 	return;
790     if (didfds)
791 	donefds();
792     if (onlyown) {
793 	struct stat stb;
794 
795 	if (fstat(unit, &stb) < 0) {
796 	    (void)close(unit);
797 	    return;
798 	}
799     }
800 
801     /*
802      * There is a critical section here while we are pushing down the input
803      * stream since we have stuff in different structures. If we weren't
804      * careful an interrupt could corrupt SHIN's Bin structure and kill the
805      * shell.
806      *
807      * We could avoid the critical region by grouping all the stuff in a single
808      * structure and pointing at it to move it all at once.  This is less
809      * efficient globally on many variable references however.
810      */
811     insource = 1;
812     getexit(oldexit);
813 
814     if (setintr) {
815 	sigemptyset(&nsigset);
816 	(void)sigaddset(&nsigset, SIGINT);
817 	(void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
818     }
819     /* Setup the new values of the state stuff saved above */
820     (void)memcpy(&saveB, &B, sizeof(B));
821     fbuf = NULL;
822     fseekp = feobp = fblocks = 0;
823     oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
824     intty = isatty(SHIN), whyles = 0, gointr = 0;
825     evalvec = 0;
826     evalp = 0;
827     enterhist = hflg;
828     if (enterhist)
829 	HIST = '\0';
830 
831     /*
832      * Now if we are allowing commands to be interrupted, we let ourselves be
833      * interrupted.
834      */
835     if (setintr)
836 	(void)sigprocmask(SIG_SETMASK, &osigset, NULL);
837     settell();
838 
839     if ((my_reenter = setexit()) == 0)
840 	process(0);				/* 0 -> blow away on errors */
841 
842     if (setintr)
843 	(void)sigprocmask(SIG_SETMASK, &osigset, NULL);
844     if (oSHIN >= 0) {
845 	int i;
846 
847 	/* We made it to the new state... free up its storage */
848 	for (i = 0; i < fblocks; i++)
849 	    free(fbuf[i]);
850 	free(fbuf);
851 
852 	/* Reset input arena */
853 	/* (note that this clears fbuf and fblocks) */
854 	(void)memcpy(&B, &saveB, sizeof(B));
855 
856 	(void)close(SHIN), SHIN = oSHIN;
857 	arginp = oarginp, onelflg = oonelflg;
858 	evalp = oevalp, evalvec = oevalvec;
859 	intty = oldintty, whyles = oldwhyl, gointr = ogointr;
860 	if (enterhist)
861 	    HIST = OHIST;
862 	enterhist = oenterhist;
863 	cantell = otell;
864     }
865 
866     resexit(oldexit);
867     /*
868      * If process reset() (effectively an unwind) then we must also unwind.
869      */
870     if (my_reenter)
871 	stderror(ERR_SILENT);
872     insource = oinsource;
873 }
874 
875 void
rechist(void)876 rechist(void)
877 {
878     Char buf[BUFSIZE], hbuf[BUFSIZE], *hfile;
879     int fp, ftmp, oldidfds;
880     struct varent *shist;
881 
882     if (!fast) {
883 	/*
884 	 * If $savehist is just set, we use the value of $history
885 	 * else we use the value in $savehist
886 	 */
887 	if ((shist = adrof(STRsavehist)) != NULL) {
888 	    if (shist->vec[0][0] != '\0')
889 		(void)Strcpy(hbuf, shist->vec[0]);
890 	    else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0')
891 		(void)Strcpy(hbuf, shist->vec[0]);
892 	    else
893 		return;
894 	}
895 	else
896   	    return;
897 
898   	if ((hfile = value(STRhistfile)) == STRNULL) {
899   	    hfile = Strcpy(buf, value(STRhome));
900   	    (void) Strcat(buf, STRsldthist);
901   	}
902 
903   	if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC,
904 	    0600)) == -1)
905   	    return;
906 
907 	oldidfds = didfds;
908 	didfds = 0;
909 	ftmp = SHOUT;
910 	SHOUT = fp;
911 	dumphist[2] = hbuf;
912 	dohist(dumphist, NULL);
913 	SHOUT = ftmp;
914 	(void)close(fp);
915 	didfds = oldidfds;
916     }
917 }
918 
919 void
goodbye(void)920 goodbye(void)
921 {
922     rechist();
923 
924     if (loginsh) {
925 	(void)signal(SIGQUIT, SIG_IGN);
926 	(void)signal(SIGINT, SIG_IGN);
927 	(void)signal(SIGTERM, SIG_IGN);
928 	setintr = 0;		/* No interrupts after "logout" */
929 	if (!(adrof(STRlogout)))
930 	    set(STRlogout, STRnormal);
931 #ifdef _PATH_DOTLOGOUT
932 	(void)srcfile(_PATH_DOTLOGOUT, 0, 0);
933 #endif
934 	if (adrof(STRhome))
935 	    (void)srccat(value(STRhome), STRsldtlogout);
936     }
937     exitstat();
938     /* NOTREACHED */
939 }
940 
941 __dead void
exitstat(void)942 exitstat(void)
943 {
944     Char *s;
945 #ifdef PROF
946     monitor(0);
947 #endif
948     /*
949      * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
950      * directly because we poke child here. Otherwise we might continue
951      * unwarrantedly (sic).
952      */
953     child = 1;
954     s = value(STRstatus);
955     xexit(s ? getn(s) : 0);
956     /* NOTREACHED */
957 }
958 
959 /*
960  * in the event of a HUP we want to save the history
961  */
962 static void
phup(int sig)963 phup(int sig)
964 {
965     rechist();
966 
967     /*
968      * We kill the last foreground process group. It then becomes
969      * responsible to propagate the SIGHUP to its progeny.
970      */
971     {
972 	struct process *pp, *np;
973 
974 	for (pp = proclist.p_next; pp; pp = pp->p_next) {
975 	    np = pp;
976 	    /*
977 	     * Find if this job is in the foreground. It could be that
978 	     * the process leader has exited and the foreground flag
979 	     * is cleared for it.
980 	     */
981 	    do
982 		/*
983 		 * If a process is in the foreground; we try to kill
984 		 * its process group. If we succeed, then the
985 		 * whole job is gone. Otherwise we keep going...
986 		 * But avoid sending HUP to the shell again.
987 		 */
988 		if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
989 		    kill(-np->p_jobid, SIGHUP) != -1) {
990 		    /* In case the job was suspended... */
991 		    (void)kill(-np->p_jobid, SIGCONT);
992 		    break;
993 		}
994 	    while ((np = np->p_friends) != pp);
995 	}
996     }
997     xexit(sig);
998     /* NOTREACHED */
999 }
1000 
1001 Char *jobargv[2] = {STRjobs, 0};
1002 
1003 /*
1004  * Catch an interrupt, e.g. during lexical input.
1005  * If we are an interactive shell, we reset the interrupt catch
1006  * immediately.  In any case we drain the shell output,
1007  * and finally go through the normal error mechanism, which
1008  * gets a chance to make the shell go away.
1009  */
1010 /* ARGSUSED */
1011 void
pintr(int notused)1012 pintr(int notused)
1013 {
1014     pintr1(1);
1015     /* NOTREACHED */
1016 }
1017 
1018 void
pintr1(int wantnl)1019 pintr1(int wantnl)
1020 {
1021     Char **v;
1022     sigset_t nsigset, osigset;
1023 
1024     sigemptyset(&nsigset);
1025     (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
1026     if (setintr) {
1027 	nsigset = osigset;
1028 	(void)sigdelset(&nsigset, SIGINT);
1029 	(void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
1030 	if (pjobs) {
1031 	    pjobs = 0;
1032 	    (void)fprintf(cshout, "\n");
1033 	    dojobs(jobargv, NULL);
1034 	    stderror(ERR_NAME | ERR_INTR);
1035 	}
1036     }
1037     (void)sigdelset(&osigset, SIGCHLD);
1038     (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
1039     (void)fpurge(cshout);
1040     (void)endpwent();
1041 
1042     /*
1043      * If we have an active "onintr" then we search for the label. Note that if
1044      * one does "onintr -" then we shan't be interruptible so we needn't worry
1045      * about that here.
1046      */
1047     if (gointr) {
1048 	gotolab(gointr);
1049 	timflg = 0;
1050 	if ((v = pargv) != NULL)
1051 	    pargv = 0, blkfree(v);
1052 	if ((v = gargv) != NULL)
1053 	    gargv = 0, blkfree(v);
1054 	reset();
1055     }
1056     else if (intty && wantnl) {
1057 	(void)fputc('\r', cshout);
1058 	(void)fputc('\n', cshout);
1059     }
1060     stderror(ERR_SILENT);
1061     /* NOTREACHED */
1062 }
1063 
1064 /*
1065  * Process is the main driving routine for the shell.
1066  * It runs all command processing, except for those within { ... }
1067  * in expressions (which is run by a routine evalav in sh.exp.c which
1068  * is a stripped down process), and `...` evaluation which is run
1069  * also by a subset of this code in sh.glob.c in the routine backeval.
1070  *
1071  * The code here is a little strange because part of it is interruptible
1072  * and hence freeing of structures appears to occur when none is necessary
1073  * if this is ignored.
1074  *
1075  * Note that if catch is not set then we will unwind on any error.
1076  * If an end-of-file occurs, we return.
1077  */
1078 static struct command *savet = NULL;
1079 
1080 void
process(int catch)1081 process(int catch)
1082 {
1083     struct command *t;
1084     jmp_buf osetexit;
1085     sigset_t nsigset;
1086 
1087     t = savet;
1088     savet = NULL;
1089     getexit(osetexit);
1090     for (;;) {
1091 	pendjob();
1092 	paraml.next = paraml.prev = &paraml;
1093 	paraml.word = STRNULL;
1094 	(void)setexit();
1095 	justpr = enterhist;	/* execute if not entering history */
1096 
1097 	/*
1098 	 * Interruptible during interactive reads
1099 	 */
1100 	if (setintr) {
1101 	    sigemptyset(&nsigset);
1102 	    (void)sigaddset(&nsigset, SIGINT);
1103 	    (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
1104 	}
1105 
1106 	/*
1107 	 * For the sake of reset()
1108 	 */
1109 	freelex(&paraml);
1110 	if (savet)
1111 	    freesyn(savet), savet = NULL;
1112 
1113 	if (haderr) {
1114 	    if (!catch) {
1115 		/* unwind */
1116 		doneinp = 0;
1117 		resexit(osetexit);
1118 		savet = t;
1119 		reset();
1120 	    }
1121 	    haderr = 0;
1122 	    /*
1123 	     * Every error is eventually caught here or the shell dies.  It is
1124 	     * at this point that we clean up any left-over open files, by
1125 	     * closing all but a fixed number of pre-defined files.  Thus
1126 	     * routines don't have to worry about leaving files open due to
1127 	     * deeper errors... they will get closed here.
1128 	     */
1129 	    closem();
1130 	    continue;
1131 	}
1132 	if (doneinp) {
1133 	    doneinp = 0;
1134 	    break;
1135 	}
1136 	if (chkstop)
1137 	    chkstop--;
1138 	if (neednote)
1139 	    pnote();
1140 	if (intty && prompt && evalvec == 0) {
1141 	    mailchk();
1142 #ifdef EDIT
1143 	    updateediting();
1144 #endif
1145 	    /*
1146 	     * If we are at the end of the input buffer then we are going to
1147 	     * read fresh stuff. Otherwise, we are rereading input and don't
1148 	     * need or want to prompt.
1149 	     */
1150 	    if (aret == F_SEEK && fseekp == feobp)
1151 		printprompt();
1152 	    (void)fflush(cshout);
1153 	}
1154 	if (seterr) {
1155 	    free(seterr);
1156 	    seterr = NULL;
1157 	}
1158 
1159 
1160 	/*
1161 	 * Echo not only on VERBOSE, but also with history expansion. If there
1162 	 * is a lexical error then we forego history echo.
1163 	 */
1164 	if ((lex(&paraml) && !seterr && intty) || adrof(STRverbose)) {
1165 	    int odidfds = didfds;
1166 	    fflush(csherr);
1167 	    didfds = 0;
1168 	    prlex(csherr, &paraml);
1169 	    fflush(csherr);
1170 	    didfds = odidfds;
1171 	}
1172 
1173 	/*
1174 	 * The parser may lose space if interrupted.
1175 	 */
1176 	if (setintr)
1177 	    (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
1178 
1179 	/*
1180 	 * Save input text on the history list if reading in old history, or it
1181 	 * is from the terminal at the top level and not in a loop.
1182 	 *
1183 	 * PWP: entry of items in the history list while in a while loop is done
1184 	 * elsewhere...
1185 	 */
1186 	if (enterhist || (catch && intty && !whyles))
1187 	    savehist(&paraml);
1188 
1189 	/*
1190 	 * Print lexical error messages, except when sourcing history lists.
1191 	 */
1192 	if (!enterhist && seterr)
1193 	    stderror(ERR_OLD);
1194 
1195 	/*
1196 	 * If had a history command :p modifier then this is as far as we
1197 	 * should go
1198 	 */
1199 	if (justpr)
1200 	    reset();
1201 
1202 	alias(&paraml);
1203 
1204 	/*
1205 	 * Parse the words of the input into a parse tree.
1206 	 */
1207 	savet = syntax(paraml.next, &paraml, 0);
1208 	if (seterr)
1209 	    stderror(ERR_OLD);
1210 
1211 	execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
1212 
1213 	/*
1214 	 * Made it!
1215 	 */
1216 	freelex(&paraml);
1217 	freesyn(savet), savet = NULL;
1218     }
1219     resexit(osetexit);
1220     savet = t;
1221 }
1222 
1223 void
1224 /*ARGSUSED*/
dosource(Char ** v,struct command * t)1225 dosource(Char **v, struct command *t)
1226 {
1227     Char buf[BUFSIZE], *f;
1228     int hflg;
1229 
1230     hflg = 0;
1231     v++;
1232     if (*v && eq(*v, STRmh)) {
1233 	if (*++v == NULL)
1234 	    stderror(ERR_NAME | ERR_HFLAG);
1235 	hflg++;
1236     }
1237     (void)Strcpy(buf, *v);
1238     f = globone(buf, G_ERROR);
1239     (void)strcpy((char *)buf, short2str(f));
1240     free(f);
1241     if (!srcfile((char *)buf, 0, hflg) && !hflg)
1242 	stderror(ERR_SYSTEM, (char *)buf, strerror(errno));
1243 }
1244 
1245 /*
1246  * Check for mail.
1247  * If we are a login shell, then we don't want to tell
1248  * about any mail file unless its been modified
1249  * after the time we started.
1250  * This prevents us from telling the user things he already
1251  * knows, since the login program insists on saying
1252  * "You have mail."
1253  */
1254 static void
mailchk(void)1255 mailchk(void)
1256 {
1257     struct stat stb;
1258     struct varent *v;
1259     Char **vp;
1260     time_t t;
1261     int cnt, intvl;
1262     int new;
1263 
1264     v = adrof(STRmail);
1265     if (v == 0)
1266 	return;
1267     (void)time(&t);
1268     vp = v->vec;
1269     cnt = blklen(vp);
1270     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1271     if (intvl < 1)
1272 	intvl = 1;
1273     if (chktim + intvl > t)
1274 	return;
1275     for (; *vp; vp++) {
1276 	if (stat(short2str(*vp), &stb) < 0)
1277 	    continue;
1278 	new = stb.st_mtime > time0.tv_sec;
1279 	if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1280 	    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
1281 	    (loginsh && !new))
1282 	    continue;
1283 	if (cnt == 1)
1284 	    (void)fprintf(cshout, "You have %smail.\n", new ? "new " : "");
1285 	else
1286 	    (void)fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
1287 			   vis_str(*vp));
1288     }
1289     chktim = t;
1290 }
1291 
1292 /*
1293  * Extract a home directory from the password file
1294  * The argument points to a buffer where the name of the
1295  * user whose home directory is sought is currently.
1296  * We write the home directory of the user back there.
1297  */
1298 int
gethdir(Char * home)1299 gethdir(Char *home)
1300 {
1301     struct passwd *pw;
1302     Char *h;
1303 
1304     /*
1305      * Is it us?
1306      */
1307     if (*home == '\0') {
1308 	if ((h = value(STRhome)) != NULL) {
1309 	    (void)Strcpy(home, h);
1310 	    return 0;
1311 	}
1312 	else
1313 	    return 1;
1314     }
1315 
1316     if ((pw = getpwnam(short2str(home))) != NULL) {
1317 	(void)Strcpy(home, str2short(pw->pw_dir));
1318 	return 0;
1319     }
1320     else
1321 	return 1;
1322 }
1323 
1324 /*
1325  * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
1326  * We also check if the shell has already changed the descriptor to point to
1327  * 0, 1, 2 when didfds is set.
1328  */
1329 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
1330 
1331 static ssize_t
readf(void * oreo,void * buf,size_t siz)1332 readf(void *oreo, void *buf, size_t siz)
1333 {
1334     return read(DESC(oreo), buf, siz);
1335 }
1336 
1337 
1338 static ssize_t
writef(void * oreo,const void * buf,size_t siz)1339 writef(void *oreo, const void *buf, size_t siz)
1340 {
1341     return write(DESC(oreo), buf, siz);
1342 }
1343 
1344 static off_t
seekf(void * oreo,off_t off,int whence)1345 seekf(void *oreo, off_t off, int whence)
1346 {
1347     return lseek(DESC(oreo), off, whence);
1348 }
1349 
1350 
1351 static int
closef(void * oreo)1352 closef(void *oreo)
1353 {
1354     return close(DESC(oreo));
1355 }
1356 
1357 
1358 /*
1359  * Print the visible version of a string.
1360  */
1361 int
vis_fputc(int ch,FILE * fp)1362 vis_fputc(int ch, FILE *fp)
1363 {
1364     char uenc[5];	/* 4 + NULL */
1365 
1366     if (ch & QUOTE)
1367 	return fputc(ch & TRIM, fp);
1368     /*
1369      * XXX: When we are in AsciiOnly we want all characters >= 0200 to
1370      * be encoded, but currently there is no way in vis to do that.
1371      */
1372     (void)vis(uenc, ch & TRIM, VIS_NOSLASH, 0);
1373     return (fputs(uenc, fp));
1374 }
1375 
1376 /*
1377  * Move the initial descriptors to their eventual
1378  * resting places, closing all other units.
1379  */
1380 void
initdesc(void)1381 initdesc(void)
1382 {
1383     didfds = 0;			/* 0, 1, 2 aren't set up */
1384     (void)ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL);
1385     (void)ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
1386     (void)ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL);
1387     (void)ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
1388     closem();
1389 }
1390 
1391 
1392 __dead void
1393 #ifdef PROF
done(int i)1394 done(int i)
1395 #else
1396 xexit(int i)
1397 #endif
1398 {
1399     untty();
1400     _exit(i);
1401     /* NOTREACHED */
1402 }
1403 
1404 #ifndef _PATH_DEFPATH
1405 static Char **
defaultpath(void)1406 defaultpath(void)
1407 {
1408     struct stat stb;
1409     Char **blk, **blkp;
1410     char *ptr;
1411 
1412     blkp = blk = xmalloc((size_t) sizeof(Char *) * 10);
1413 
1414 #define DIRAPPEND(a)  \
1415 	if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
1416 		*blkp++ = SAVE(ptr)
1417 #ifdef RESCUEDIR
1418     DIRAPPEND(RESCUEDIR);
1419 #endif
1420     DIRAPPEND(_PATH_BIN);
1421     DIRAPPEND(_PATH_USRBIN);
1422 
1423 #undef DIRAPPEND
1424 
1425 #if 0
1426     if (euid != 0 && uid != 0)
1427 	*blkp++ = Strsave(STRdot);
1428 #endif
1429 
1430     *blkp = NULL;
1431     return (blk);
1432 }
1433 #endif /* _PATH_DEFPATH */
1434 
1435 void
printprompt(void)1436 printprompt(void)
1437 {
1438     Char *cp;
1439 
1440     if (editing)
1441 	return;
1442 
1443     if (!whyles) {
1444 	for (cp = value(STRprompt); *cp; cp++)
1445 	    if (*cp == HIST)
1446 		(void)fprintf(cshout, "%d", eventno + 1);
1447 	    else {
1448 		if (*cp == '\\' && cp[1] == HIST)
1449 		    cp++;
1450 		(void)vis_fputc(*cp | QUOTE, cshout);
1451 	    }
1452     }
1453     else
1454 	/*
1455 	 * Prompt for forward reading loop body content.
1456 	 */
1457 	(void)fprintf(cshout, "? ");
1458     (void)fflush(cshout);
1459 }
1460 
1461 #ifdef EDIT
1462 char *
printpromptstr(EditLine * elx)1463 printpromptstr(EditLine *elx) {
1464     static char pbuf[1024];
1465     static char qspace[] = "? ";
1466     Char *cp;
1467     size_t i;
1468 
1469     if (whyles)
1470 	return qspace;
1471 
1472     i = 0;
1473     for (cp = value(STRprompt); *cp; cp++) {
1474 	if (i >= sizeof(pbuf))
1475 	    break;
1476 	if (*cp == HIST) {
1477 	    int r;
1478 	    r = snprintf(pbuf + i, sizeof(pbuf) - i, "%d", eventno + 1);
1479 	    if (r > 0)
1480 		i += (size_t)r;
1481 	} else
1482 	    pbuf[i++] = (char)*cp;
1483     }
1484     if (i >= sizeof(pbuf))
1485 	i = sizeof(pbuf) - 1;
1486     pbuf[i] = '\0';
1487     return pbuf;
1488 }
1489 #endif
1490