"Fossies" - the Fresh Open Source Software Archive 
Member "tcsh-6.22.03/sh.c" (18 Nov 2020, 63662 Bytes) of package /linux/misc/tcsh-6.22.03.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "sh.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
6.22.02_vs_6.22.03.
1 /*
2 * sh.c: Main shell routines
3 */
4 /*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 #define EXTERN /* Intern */
33 #include "sh.h"
34
35 #ifndef lint
36 char copyright[] =
37 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
38 All rights reserved.\n";
39 #endif /* not lint */
40
41 #include "tc.h"
42 #include "ed.h"
43 #include "tw.h"
44
45 extern int MapsAreInited;
46 extern int NLSMapsAreInited;
47
48 /*
49 * C Shell
50 *
51 * Bill Joy, UC Berkeley, California, USA
52 * October 1978, May 1980
53 *
54 * Jim Kulp, IIASA, Laxenburg, Austria
55 * April 1980
56 *
57 * Filename recognition added:
58 * Ken Greer, Ind. Consultant, Palo Alto CA
59 * October 1983.
60 *
61 * Karl Kleinpaste, Computer Consoles, Inc.
62 * Added precmd, periodic/tperiod, prompt changes,
63 * directory stack hack, and login watch.
64 * Sometime March 1983 - Feb 1984.
65 *
66 * Added scheduled commands, including the "sched" command,
67 * plus the call to sched_run near the precmd et al
68 * routines.
69 * Upgraded scheduled events for running events while
70 * sitting idle at command input.
71 *
72 * Paul Placeway, Ohio State
73 * added stuff for running with twenex/inputl 9 Oct 1984.
74 *
75 * ported to Apple Unix (TM) (OREO) 26 -- 29 Jun 1987
76 */
77
78 jmp_buf_t reslab IZERO_STRUCT;
79 struct wordent paraml IZERO_STRUCT;
80
81 static const char tcshstr[] = "tcsh";
82
83 struct sigaction parintr; /* Parents interrupt catch */
84 struct sigaction parterm; /* Parents terminate catch */
85
86 #ifdef TESLA
87 int do_logout = 0;
88 #endif /* TESLA */
89
90
91 int use_fork = 0; /* use fork() instead of vfork()? */
92
93 /*
94 * Magic pointer values. Used to specify other invalid conditions aside
95 * from null.
96 */
97 static Char INVCHAR;
98 Char *INVPTR = &INVCHAR;
99 Char **INVPPTR = &INVPTR;
100
101 static int fast = 0;
102 static int mflag = 0;
103 static int prompt = 1;
104 int enterhist = 0;
105 int tellwhat = 0;
106 time_t t_period;
107 Char *ffile = NULL;
108 int dolzero = 0;
109 int insource = 0;
110 int exitset = 0;
111 static time_t chktim; /* Time mail last checked */
112 char *progname;
113 int tcsh;
114
115 /*
116 * This preserves the input state of the shell. It is used by
117 * st_save and st_restore to manupulate shell state.
118 */
119 struct saved_state {
120 int insource;
121 int OLDSTD;
122 int SHIN;
123 int SHOUT;
124 int SHDIAG;
125 int intty;
126 struct whyle *whyles;
127 Char *gointr;
128 Char *arginp;
129 Char *evalp;
130 Char **evalvec;
131 Char *alvecp;
132 Char **alvec;
133 int onelflg;
134 int enterhist;
135 Char **argv;
136 Char **av;
137 Char HIST;
138 int cantell;
139 struct Bin B;
140 int justpr;
141 };
142
143 static int srccat (Char *, Char *);
144 #ifndef WINNT_NATIVE
145 static int srcfile (const char *, int, int, Char **);
146 #else
147 int srcfile (const char *, int, int, Char **);
148 #endif /*WINNT_NATIVE*/
149 static void srcunit (int, int, int, Char **);
150 static void mailchk (void);
151 #ifndef _PATH_DEFPATH
152 static Char **defaultpath (void);
153 #endif
154 static void record (void);
155 static void st_save (struct saved_state *, int, int,
156 Char **, Char **);
157 static void st_restore (void *);
158
159 int main (int, char **);
160
161 #ifndef LOCALEDIR
162 #define LOCALEDIR "/usr/share/locale"
163 #endif
164
165 #ifdef NLS_CATALOGS
166 static void
167 add_localedir_to_nlspath(const char *path)
168 {
169 static const char msgs_LOC[] = "/%L/LC_MESSAGES/%N.cat";
170 static const char msgs_lang[] = "/%l/LC_MESSAGES/%N.cat";
171 char *old;
172 char *new, *new_p;
173 size_t len;
174 int add_LOC = 1;
175 int add_lang = 1;
176 char trypath[MAXPATHLEN];
177 struct stat st;
178
179 if (path == NULL)
180 return;
181
182 (void) xsnprintf(trypath, sizeof(trypath), "%s/C/LC_MESSAGES/tcsh.cat",
183 path);
184 if (stat(trypath, &st) == -1)
185 return;
186
187 if ((old = getenv("NLSPATH")) != NULL)
188 len = strlen(old) + 1; /* don't forget the colon. */
189 else
190 len = 0;
191
192 len += 2 * strlen(path) +
193 sizeof(msgs_LOC) + sizeof(msgs_lang); /* includes the extra colon */
194
195 new = new_p = xcalloc(len, 1);
196
197 if (old != NULL) {
198 size_t pathlen = strlen(path);
199 char *old_p;
200
201 (void) xsnprintf(new_p, len, "%s", old);
202 new_p += strlen(new_p);
203 len -= new_p - new;
204
205 /* Check if the paths we try to add are already present in NLSPATH.
206 If so, note it by setting the appropriate flag to 0. */
207 for (old_p = old; old_p; old_p = strchr(old_p, ':'),
208 old_p = old_p ? old_p + 1 : NULL) {
209 if (strncmp(old_p, path, pathlen) != 0)
210 continue;
211 if (strncmp(old_p + pathlen, msgs_LOC, sizeof(msgs_LOC) - 1) == 0)
212 add_LOC = 0;
213 else if (strncmp(old_p + pathlen, msgs_lang,
214 sizeof(msgs_lang) - 1) == 0)
215 add_lang = 0;
216 }
217 }
218
219 /* Add the message catalog paths not already present to NLSPATH. */
220 if (add_LOC || add_lang)
221 (void) xsnprintf(new_p, len, "%s%s%s%s%s%s",
222 old ? ":" : "",
223 add_LOC ? path : "", add_LOC ? msgs_LOC : "",
224 add_LOC && add_lang ? ":" : "",
225 add_lang ? path : "", add_lang ? msgs_lang : "");
226
227 tsetenv(STRNLSPATH, str2short(new));
228 free(new);
229 }
230 #endif
231
232 int
233 main(int argc, char **argv)
234 {
235 int batch = 0;
236 volatile int nexececho = 0;
237 int nofile = 0;
238 volatile int nverbose = 0;
239 volatile int rdirs = 0;
240 volatile int exitcode = 0;
241 int quitit = 0;
242 Char *cp;
243 #ifdef AUTOLOGOUT
244 Char *cp2;
245 #endif
246 char *tcp, *ttyn;
247 int f, reenter;
248 char **tempv;
249 static const char *targinp = NULL;
250 int osetintr;
251 struct sigaction oparintr;
252
253 #ifdef WINNT_NATIVE
254 nt_init();
255 #endif /* WINNT_NATIVE */
256
257 (void)memset(&reslab, 0, sizeof(reslab));
258 #if defined(NLS_CATALOGS) && defined(LC_MESSAGES)
259 (void) setlocale(LC_MESSAGES, "");
260 #endif /* NLS_CATALOGS && LC_MESSAGES */
261
262 #ifdef NLS
263 # ifdef LC_CTYPE
264 (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
265 # endif /* LC_CTYPE */
266 #endif /* NLS */
267
268 STR_environ = blk2short(environ);
269 environ = short2blk(STR_environ); /* So that we can free it */
270
271 #ifdef NLS_CATALOGS
272 add_localedir_to_nlspath(LOCALEDIR);
273 #endif
274
275 nlsinit();
276 initlex(¶ml);
277
278 #ifdef MALLOC_TRACE
279 mal_setstatsfile(fdopen(dmove(xopen("/tmp/tcsh.trace",
280 O_WRONLY|O_CREAT|O_LARGEFILE, 0666), 25), "w"));
281 mal_trace(1);
282 #endif /* MALLOC_TRACE */
283
284 #if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX)
285 # ifdef _SC_CLK_TCK
286 clk_tck = (clock_t) sysconf(_SC_CLK_TCK);
287 # else /* ! _SC_CLK_TCK */
288 # ifdef CLK_TCK
289 clk_tck = CLK_TCK;
290 # else /* !CLK_TCK */
291 clk_tck = HZ;
292 # endif /* CLK_TCK */
293 # endif /* _SC_CLK_TCK */
294 #endif /* !BSDTIMES && POSIX */
295
296 settimes(); /* Immed. estab. timing base */
297 #ifdef TESLA
298 do_logout = 0;
299 #endif /* TESLA */
300
301 /*
302 * Make sure we have 0, 1, 2 open
303 * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr)
304 */
305 {
306 do
307 if ((f = xopen(_PATH_DEVNULL, O_RDONLY|O_LARGEFILE)) == -1 &&
308 (f = xopen("/", O_RDONLY|O_LARGEFILE)) == -1)
309 exit(1);
310 while (f < 3);
311 xclose(f);
312 }
313
314 osinit(); /* Os dependent initialization */
315
316
317 {
318 char *t;
319
320 t = strrchr(argv[0], '/');
321 #ifdef WINNT_NATIVE
322 {
323 char *s = strrchr(argv[0], '\\');
324 if (s)
325 t = s;
326 }
327 #endif /* WINNT_NATIVE */
328 t = t ? t + 1 : argv[0];
329 if (*t == '-') t++;
330 progname = strsave((t && *t) ? t : tcshstr); /* never want a null */
331 tcsh = strncmp(progname, tcshstr, sizeof(tcshstr) - 1) == 0;
332 }
333
334 /*
335 * Initialize non constant strings
336 */
337 #ifdef _PATH_BSHELL
338 STR_BSHELL = SAVE(_PATH_BSHELL);
339 #endif
340 #ifdef _PATH_TCSHELL
341 STR_SHELLPATH = SAVE(_PATH_TCSHELL);
342 #else
343 # ifdef _PATH_CSHELL
344 STR_SHELLPATH = SAVE(_PATH_CSHELL);
345 # endif
346 #endif
347 STR_WORD_CHARS = SAVE(WORD_CHARS);
348 STR_WORD_CHARS_VI = SAVE(WORD_CHARS_VI);
349
350 HIST = '!';
351 HISTSUB = '^';
352 PRCH = tcsh ? '>' : '%'; /* to replace %# in $prompt for normal users */
353 PRCHROOT = '#'; /* likewise for root */
354 word_chars = STR_WORD_CHARS;
355 bslash_quote = 0; /* PWP: do tcsh-style backslash quoting? */
356 anyerror = 1; /* for compatibility */
357 setcopy(STRanyerror, STRNULL, VAR_READWRITE);
358
359 /* Default history size to 100 */
360 setcopy(STRhistory, str2short("100"), VAR_READWRITE);
361 sethistory(100);
362
363 tempv = argv;
364 ffile = SAVE(tempv[0]);
365 dolzero = 0;
366 if (eq(ffile, STRaout)) /* A.out's are quittable */
367 quitit = 1;
368 uid = getuid();
369 gid = getgid();
370 euid = geteuid();
371 egid = getegid();
372 /*
373 * We are a login shell if: 1. we were invoked as -<something> with
374 * optional arguments 2. or we were invoked only with the -l flag
375 */
376 loginsh = (**tempv == '-') || (argc == 2 &&
377 tempv[1][0] == '-' && tempv[1][1] == 'l' &&
378 tempv[1][2] == '\0');
379 #ifdef _VMS_POSIX
380 /* No better way to find if we are a login shell */
381 if (!loginsh) {
382 loginsh = (argc == 1 && getppid() == 1);
383 **tempv = '-'; /* Avoid giving VMS an acidic stomach */
384 }
385 #endif /* _VMS_POSIX */
386
387 if (loginsh && **tempv != '-') {
388 char *argv0;
389
390 /*
391 * Mangle the argv space
392 */
393 tempv[1][0] = '\0';
394 tempv[1][1] = '\0';
395 tempv[1] = NULL;
396 argv0 = strspl("-", *tempv);
397 *tempv = argv0;
398 argc--;
399 }
400 if (loginsh) {
401 (void) time(&chktim);
402 setNS(STRloginsh);
403 }
404
405 NoNLSRebind = getenv("NOREBIND") != NULL;
406 #ifdef NLS
407 # ifdef SETLOCALEBUG
408 dont_free = 1;
409 # endif /* SETLOCALEBUG */
410 (void) setlocale(LC_ALL, "");
411 # ifdef LC_COLLATE
412 (void) setlocale(LC_COLLATE, "");
413 # endif
414 # ifdef SETLOCALEBUG
415 dont_free = 0;
416 # endif /* SETLOCALEBUG */
417 # ifdef STRCOLLBUG
418 fix_strcoll_bug();
419 # endif /* STRCOLLBUG */
420
421 /*
422 * On solaris ISO8859-1 contains no printable characters in the upper half
423 * so we need to test only for MB_CUR_MAX == 1, otherwise for multi-byte
424 * locales we are always AsciiOnly == 0.
425 */
426 if (MB_CUR_MAX == 1) {
427 int k;
428
429 for (k = 0200; k <= 0377 && !isprint(CTL_ESC(k)); k++)
430 continue;
431 AsciiOnly = k > 0377;
432 } else
433 AsciiOnly = 0;
434 #else
435 AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
436 #endif /* NLS */
437 if (MapsAreInited && !NLSMapsAreInited)
438 ed_InitNLSMaps();
439 ResetArrowKeys();
440
441 /*
442 * Initialize for periodic command intervals. Also, initialize the dummy
443 * tty list for login-watch.
444 */
445 (void) time(&t_period);
446 #ifndef HAVENOUTMP
447 initwatch();
448 #endif /* !HAVENOUTMP */
449
450 #if defined(alliant)
451 /*
452 * From: Jim Pace <jdp@research.att.com>
453 * tcsh does not work properly on the alliants through an rlogin session.
454 * The shell generally hangs. Also, reference to the controlling terminal
455 * does not work ( ie: echo foo > /dev/tty ).
456 *
457 * A security feature was added to rlogind affecting FX/80's Concentrix
458 * from revision 5.5.xx upwards (through 5.7 where this fix was implemented)
459 * This security change also affects the FX/2800 series.
460 * The security change to rlogind requires the process group of an rlogin
461 * session become disassociated with the tty in rlogind.
462 *
463 * The changes needed are:
464 * 1. set the process group
465 * 2. reenable the control terminal
466 */
467 if (loginsh && isatty(SHIN)) {
468 ttyn = ttyname(SHIN);
469 xclose(SHIN);
470 SHIN = xopen(ttyn, O_RDWR|O_LARGEFILE);
471 shpgrp = getpid();
472 (void) ioctl (SHIN, TIOCSPGRP, (ioctl_t) &shpgrp);
473 (void) setpgid(0, shpgrp);
474 }
475 #endif /* alliant */
476
477 /*
478 * Move the descriptors to safe places. The variable didfds is 0 while we
479 * have only FSH* to work with. When didfds is true, we have 0,1,2 and
480 * prefer to use these.
481 */
482 initdesc();
483
484 cdtohome = 1;
485 setv(STRcdtohome, SAVE(""), VAR_READWRITE);
486
487 /*
488 * Get and set the tty now
489 */
490 if ((ttyn = ttyname(SHIN)) != NULL) {
491 /*
492 * Could use rindex to get rid of other possible path components, but
493 * hpux preserves the subdirectory /pty/ when storing the tty name in
494 * utmp, so we keep it too.
495 */
496 if (strncmp(ttyn, "/dev/", 5) == 0)
497 setv(STRtty, cp = SAVE(ttyn + 5), VAR_READWRITE);
498 else
499 setv(STRtty, cp = SAVE(ttyn), VAR_READWRITE);
500 }
501 else
502 setv(STRtty, cp = SAVE(""), VAR_READWRITE);
503
504 /*
505 * Initialize the shell variables. ARGV and PROMPT are initialized later.
506 * STATUS is also munged in several places. CHILD is munged when
507 * forking/waiting
508 */
509
510 /*
511 * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and
512 * on shells running as root. Out of these, autologout should NOT be set
513 * for any psudo-terminals (this catches most window systems) and not for
514 * any terminal running X windows.
515 *
516 * At Ohio State, we have had problems with a user having his X session
517 * drop out from under him (on a Sun) because the shell in his master
518 * xterm timed out and exited.
519 *
520 * Really, this should be done with a program external to the shell, that
521 * watches for no activity (and NO running programs, such as dump) on a
522 * terminal for a long peroid of time, and then SIGHUPS the shell on that
523 * terminal.
524 *
525 * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things
526 * allways first check to see if loginsh or really root, then do things
527 * with ttyname()
528 *
529 * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the
530 * value of cp before using it! ("root can rsh too")
531 *
532 * PWP: keep the nested ifs; the order of the tests matters and a good
533 * (smart) C compiler might re-arange things wrong.
534 */
535 #ifdef AUTOLOGOUT
536 # ifdef convex
537 if (uid == 0)
538 /* root always has a 15 minute autologout */
539 setcopy(STRautologout, STRrootdefautologout, VAR_READWRITE);
540 else
541 if (loginsh)
542 /* users get autologout set to 0 */
543 setcopy(STRautologout, STR0, VAR_READWRITE);
544 # else /* convex */
545 if (loginsh || (uid == 0)) {
546 if (*cp) {
547 /* only for login shells or root and we must have a tty */
548 if (((cp2 = Strrchr(cp, (Char) '/')) != NULL) &&
549 (Strncmp(cp, STRptssl, 3) != 0)) {
550 cp2 = cp2 + 1;
551 }
552 else
553 cp2 = cp;
554 if (!(((Strncmp(cp2, STRtty, 3) == 0) && Isalpha(cp2[3])) ||
555 Strstr(cp, STRptssl) != NULL)) {
556 if (getenv("DISPLAY") == NULL) {
557 /* NOT on X window shells */
558 setcopy(STRautologout, STRdefautologout, VAR_READWRITE);
559 }
560 }
561 }
562 }
563 # endif /* convex */
564 #endif /* AUTOLOGOUT */
565
566 sigset_interrupting(SIGALRM, queue_alrmcatch);
567
568 setcopy(STRstatus, STR0, VAR_READWRITE);
569
570 /*
571 * get and set machine specific environment variables
572 */
573 getmachine();
574
575
576 /*
577 * Publish the selected echo style
578 */
579 #if ECHO_STYLE != BSD_ECHO
580 if (tcsh) {
581 # if ECHO_STYLE == NONE_ECHO
582 setcopy(STRecho_style, STRnone, VAR_READWRITE);
583 # endif /* ECHO_STYLE == NONE_ECHO */
584 # if ECHO_STYLE == SYSV_ECHO
585 setcopy(STRecho_style, STRsysv, VAR_READWRITE);
586 # endif /* ECHO_STYLE == SYSV_ECHO */
587 # if ECHO_STYLE == BOTH_ECHO
588 setcopy(STRecho_style, STRboth, VAR_READWRITE);
589 # endif /* ECHO_STYLE == BOTH_ECHO */
590 } else
591 #endif /* ECHO_STYLE != BSD_ECHO */
592 setcopy(STRecho_style, STRbsd, VAR_READWRITE);
593
594 /*
595 * increment the shell level.
596 */
597 shlvl(1);
598
599 #ifdef __ANDROID__
600 /* On Android, $HOME either isn't set or set to /data, a R/O location.
601 Check for the environment variable EXTERNAL_STORAGE, which contains
602 the mount point of the external storage (SD card, mostly). If
603 EXTERNAL_STORAGE isn't set fall back to "/sdcard". Eventually
604 override $HOME so the environment is on the same page. */
605 if (((tcp = getenv("HOME")) != NULL && strcmp (tcp, "/data") != 0)
606 || (tcp = getenv("EXTERNAL_STORAGE")) != NULL) {
607 cp = quote(SAVE(tcp));
608 } else
609 cp = quote(SAVE("/sdcard"));
610 tsetenv(STRKHOME, cp);
611 #else
612 if ((tcp = getenv("HOME")) != NULL)
613 cp = quote(SAVE(tcp));
614 else
615 cp = NULL;
616 #endif
617
618 if (cp == NULL)
619 fast = 1; /* No home -> can't read scripts */
620 else
621 setv(STRhome, cp, VAR_READWRITE);
622
623 dinit(cp); /* dinit thinks that HOME == cwd in a login
624 * shell */
625 /*
626 * Grab other useful things from the environment. Should we grab
627 * everything??
628 */
629 {
630 char *cln, *cus, *cgr;
631 struct passwd *pw;
632 struct group *gr;
633
634
635 #ifdef apollo
636 int oid = getoid();
637
638 setv(STRoid, Itoa(oid, 0, 0), VAR_READWRITE);
639 #endif /* apollo */
640
641 setv(STReuid, Itoa(euid, 0, 0), VAR_READWRITE);
642 if ((pw = xgetpwuid(euid)) == NULL)
643 setcopy(STReuser, STRunknown, VAR_READWRITE);
644 else
645 setcopy(STReuser, str2short(pw->pw_name), VAR_READWRITE);
646
647 setv(STRuid, Itoa(uid, 0, 0), VAR_READWRITE);
648
649 setv(STRgid, Itoa(gid, 0, 0), VAR_READWRITE);
650
651 cln = getenv("LOGNAME");
652 cus = getenv("USER");
653 if (cus != NULL)
654 setv(STRuser, quote(SAVE(cus)), VAR_READWRITE);
655 else if (cln != NULL)
656 setv(STRuser, quote(SAVE(cln)), VAR_READWRITE);
657 else if ((pw = xgetpwuid(uid)) == NULL)
658 setcopy(STRuser, STRunknown, VAR_READWRITE);
659 else
660 setcopy(STRuser, str2short(pw->pw_name), VAR_READWRITE);
661 if (cln == NULL)
662 tsetenv(STRLOGNAME, varval(STRuser));
663 if (cus == NULL)
664 tsetenv(STRKUSER, varval(STRuser));
665
666 cgr = getenv("GROUP");
667 if (cgr != NULL)
668 setv(STRgroup, quote(SAVE(cgr)), VAR_READWRITE);
669 else if ((gr = xgetgrgid(gid)) == NULL)
670 setcopy(STRgroup, STRunknown, VAR_READWRITE);
671 else
672 setcopy(STRgroup, str2short(gr->gr_name), VAR_READWRITE);
673 if (cgr == NULL)
674 tsetenv(STRKGROUP, varval(STRgroup));
675 }
676
677 /*
678 * HOST may be wrong, since rexd transports the entire environment on sun
679 * 3.x Just set it again
680 */
681 {
682 char cbuff[MAXHOSTNAMELEN];
683
684 if (gethostname(cbuff, sizeof(cbuff)) >= 0) {
685 cbuff[sizeof(cbuff) - 1] = '\0'; /* just in case */
686 tsetenv(STRHOST, str2short(cbuff));
687 }
688 else
689 tsetenv(STRHOST, STRunknown);
690 }
691
692
693 #ifdef REMOTEHOST
694 /*
695 * Try to determine the remote host we were logged in from.
696 */
697 remotehost();
698 #endif /* REMOTEHOST */
699
700 #ifdef apollo
701 if ((tcp = getenv("SYSTYPE")) == NULL)
702 tcp = "bsd4.3";
703 tsetenv(STRSYSTYPE, quote(str2short(tcp)));
704 #endif /* apollo */
705
706 /*
707 * set editing on by default, unless running under Emacs as an inferior
708 * shell.
709 * We try to do this intelligently. If $TERM is available, then it
710 * should determine if we should edit or not. $TERM is preserved
711 * across rlogin sessions, so we will not get confused if we rlogin
712 * under an emacs shell. Another advantage is that if we run an
713 * xterm under an emacs shell, then the $TERM will be set to
714 * xterm, so we are going to want to edit. Unfortunately emacs
715 * does not restore all the tty modes, so xterm is not very well
716 * set up. But this is not the shell's fault.
717 * Also don't edit if $TERM == wm, for when we're running under an ATK app.
718 * Finally, emacs compiled under terminfo, sets the terminal to dumb,
719 * so disable editing for that too.
720 *
721 * Unfortunately, in some cases the initial $TERM setting is "unknown",
722 * "dumb", or "network" which is then changed in the user's startup files.
723 * We fix this by setting noediting here if $TERM is unknown/dumb and
724 * if noediting is set, we switch on editing if $TERM is changed.
725 */
726 if ((tcp = getenv("TERM")) != NULL) {
727 setv(STRterm, quote(SAVE(tcp)), VAR_READWRITE);
728 noediting = strcmp(tcp, "unknown") == 0 || strcmp(tcp, "dumb") == 0 ||
729 strcmp(tcp, "network") == 0;
730 editing = strcmp(tcp, "emacs") != 0 && strcmp(tcp, "wm") != 0 &&
731 !noediting;
732 }
733 else {
734 noediting = 0;
735 editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0);
736 }
737
738 /*
739 * The 'edit' variable is either set or unset. It doesn't
740 * need a value. Making it 'emacs' might be confusing.
741 */
742 if (editing)
743 setNS(STRedit);
744
745
746 /*
747 * still more mutability: make the complete routine automatically add the
748 * suffix of file names...
749 */
750 setNS(STRaddsuffix);
751
752 /*
753 * Compatibility with tcsh >= 6.12 by default
754 */
755 setNS(STRcsubstnonl);
756
757 /*
758 * Random default kill ring size
759 */
760 setcopy(STRkillring, str2short("30"), VAR_READWRITE);
761
762 /*
763 * Re-initialize path if set in environment
764 */
765 if ((tcp = getenv("PATH")) == NULL)
766 #ifdef _PATH_DEFPATH
767 importpath(str2short(_PATH_DEFPATH));
768 #else /* !_PATH_DEFPATH */
769 setq(STRpath, defaultpath(), &shvhed, VAR_READWRITE);
770 #endif /* _PATH_DEFPATH */
771 else
772 /* Importpath() allocates memory for the path, and the
773 * returned pointer from SAVE() was discarded, so
774 * this was a memory leak.. (sg)
775 *
776 * importpath(SAVE(tcp));
777 */
778 importpath(str2short(tcp));
779
780
781 {
782 /* If the SHELL environment variable ends with "tcsh", set
783 * STRshell to the same path. This is to facilitate using
784 * the executable in environments where the compiled-in
785 * default isn't appropriate (sg).
786 */
787
788 size_t sh_len = 0;
789
790 if ((tcp = getenv("SHELL")) != NULL) {
791 sh_len = strlen(tcp);
792 if ((sh_len >= 5 && strcmp(tcp + (sh_len - 5), "/tcsh") == 0) ||
793 (!tcsh && sh_len >= 4 && strcmp(tcp + (sh_len - 4), "/csh") == 0))
794 setv(STRshell, quote(SAVE(tcp)), VAR_READWRITE);
795 else
796 sh_len = 0;
797 }
798 if (sh_len == 0)
799 setcopy(STRshell, STR_SHELLPATH, VAR_READWRITE);
800 }
801
802 #ifdef _OSD_POSIX /* BS2000 needs this variable set to "SHELL" */
803 if ((tcp = getenv("PROGRAM_ENVIRONMENT")) == NULL)
804 tcp = "SHELL";
805 tsetenv(STRPROGRAM_ENVIRONMENT, quote(str2short(tcp)));
806 #endif /* _OSD_POSIX */
807
808 #ifdef COLOR_LS_F
809 if ((tcp = getenv("LS_COLORS")) != NULL)
810 parseLS_COLORS(str2short(tcp));
811 if ((tcp = getenv("LSCOLORS")) != NULL)
812 parseLSCOLORS(str2short(tcp));
813 #endif /* COLOR_LS_F */
814
815 mainpid = getpid();
816 doldol = putn((tcsh_number_t)mainpid); /* For $$ */
817 #ifdef WINNT_NATIVE
818 {
819 char *tmp;
820 Char *tmp2;
821 if ((tmp = getenv("TMP")) != NULL) {
822 tmp = xasprintf("%s/%s", tmp, "sh");
823 tmp2 = SAVE(tmp);
824 xfree(tmp);
825 }
826 else {
827 tmp2 = SAVE("");
828 }
829 shtemp = Strspl(tmp2, doldol); /* For << */
830 xfree(tmp2);
831 }
832 #else /* !WINNT_NATIVE */
833 #ifdef HAVE_MKSTEMP
834 {
835 const char *tmpdir = getenv ("TMPDIR");
836 if (!tmpdir)
837 tmpdir = "/tmp";
838 shtemp = Strspl(SAVE(tmpdir), SAVE("/sh" TMP_TEMPLATE)); /* For << */
839 }
840 #else /* !HAVE_MKSTEMP */
841 shtemp = Strspl(STRtmpsh, doldol); /* For << */
842 #endif /* HAVE_MKSTEMP */
843 #endif /* WINNT_NATIVE */
844
845 /*
846 * Record the interrupt states from the parent process. If the parent is
847 * non-interruptible our hand must be forced or we (and our children) won't
848 * be either. Our children inherit termination from our parent. We catch it
849 * only if we are the login shell.
850 */
851 sigaction(SIGINT, NULL, &parintr);
852 sigaction(SIGTERM, NULL, &parterm);
853
854
855 #ifdef TCF
856 /* Enable process migration on ourselves and our progeny */
857 (void) signal(SIGMIGRATE, SIG_DFL);
858 #endif /* TCF */
859
860 /*
861 * dspkanji/dspmbyte autosetting
862 */
863 /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
864 #if defined(DSPMBYTE)
865 #if defined(NLS) && defined(LC_CTYPE)
866 if (((tcp = setlocale(LC_CTYPE, NULL)) != NULL || (tcp = getenv("LANG")) != NULL) && !adrof(CHECK_MBYTEVAR))
867 #else
868 if ((tcp = getenv("LANG")) != NULL && !adrof(CHECK_MBYTEVAR))
869 #endif
870 {
871 autoset_dspmbyte(str2short(tcp));
872 }
873 #if defined(WINNT_NATIVE)
874 else if (!adrof(CHECK_MBYTEVAR))
875 nt_autoset_dspmbyte();
876 #endif /* WINNT_NATIVE */
877 #endif
878 #if defined(AUTOSET_KANJI)
879 # if defined(NLS) && defined(LC_CTYPE)
880 if (setlocale(LC_CTYPE, NULL) != NULL || getenv("LANG") != NULL)
881 # else
882 if (getenv("LANG") != NULL)
883 # endif
884 autoset_kanji();
885 #endif /* AUTOSET_KANJI */
886 fix_version(); /* publish the shell version */
887
888 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
889 xprintf("%S\n", varval(STRversion));
890 xexit(0);
891 }
892 if (argc > 1 && strcmp(argv[1], "--help") == 0) {
893 xprintf("%S\n\n", varval(STRversion));
894 xprintf("%s", CGETS(11, 8, HELP_STRING));
895 xexit(0);
896 }
897 /*
898 * Process the arguments.
899 *
900 * Note that processing of -v/-x is actually delayed till after script
901 * processing.
902 *
903 * We set the first character of our name to be '-' if we are a shell
904 * running interruptible commands. Many programs which examine ps'es
905 * use this to filter such shells out.
906 */
907 argc--, tempv++;
908 while (argc > 0 && (tcp = tempv[0])[0] == '-' &&
909 *++tcp != '\0' && !batch) {
910 do
911 switch (*tcp++) {
912
913 case 0: /* - Interruptible, no prompt */
914 prompt = 0;
915 setintr = 1;
916 nofile = 1;
917 break;
918
919 case 'b': /* -b Next arg is input file */
920 batch = 1;
921 break;
922
923 case 'c': /* -c Command input from arg */
924 if (argc == 1)
925 xexit(0);
926 argc--, tempv++;
927 #ifdef M_XENIX
928 /* Xenix Vi bug:
929 it relies on a 7 bit environment (/bin/sh), so it
930 pass ascii arguments with the 8th bit set */
931 if (!strcmp(argv[0], "sh"))
932 {
933 char *p;
934
935 for (p = tempv[0]; *p; ++p)
936 *p &= ASCII;
937 }
938 #endif
939 targinp = tempv[0];
940 prompt = 0;
941 nofile = 1;
942 break;
943 case 'd': /* -d Load directory stack from file */
944 rdirs = 1;
945 break;
946
947 #ifdef apollo
948 case 'D': /* -D Define environment variable */
949 {
950 Char *dp;
951
952 cp = str2short(tcp);
953 if (dp = Strchr(cp, '=')) {
954 *dp++ = '\0';
955 tsetenv(cp, dp);
956 }
957 else
958 tsetenv(cp, STRNULL);
959 }
960 *tcp = '\0'; /* done with this argument */
961 break;
962 #endif /* apollo */
963
964 case 'e': /* -e Exit on any error */
965 exiterr = 1;
966 break;
967
968 case 'f': /* -f Fast start */
969 fast = 1;
970 break;
971
972 case 'i': /* -i Interactive, even if !intty */
973 intact = 1;
974 nofile = 1;
975 break;
976
977 case 'm': /* -m read .cshrc (from su) */
978 mflag = 1;
979 break;
980
981 case 'n': /* -n Don't execute */
982 noexec = 1;
983 break;
984
985 case 'q': /* -q (Undoc'd) ... die on quit */
986 quitit = 1;
987 break;
988
989 case 's': /* -s Read from std input */
990 nofile = 1;
991 break;
992
993 case 't': /* -t Read one line from input */
994 onelflg = 2;
995 prompt = 0;
996 nofile = 1;
997 break;
998
999 case 'v': /* -v Echo hist expanded input */
1000 nverbose = 1; /* ... later */
1001 break;
1002
1003 case 'x': /* -x Echo just before execution */
1004 nexececho = 1; /* ... later */
1005 break;
1006
1007 case 'V': /* -V Echo hist expanded input */
1008 setNS(STRverbose); /* NOW! */
1009 break;
1010
1011 case 'X': /* -X Echo just before execution */
1012 setNS(STRecho); /* NOW! */
1013 break;
1014
1015 case 'F':
1016 /*
1017 * This will cause children to be created using fork instead of
1018 * vfork.
1019 */
1020 use_fork = 1;
1021 break;
1022
1023 case ' ':
1024 case '\t':
1025 case '\r':
1026 case '\n':
1027 /*
1028 * for O/S's that don't do the argument parsing right in
1029 * "#!/foo -f " scripts
1030 */
1031 break;
1032
1033 default: /* Unknown command option */
1034 exiterr = 1;
1035 stderror(ERR_TCSHUSAGE, tcp-1, progname);
1036 break;
1037
1038 } while (*tcp);
1039 tempv++, argc--;
1040 }
1041
1042 if (quitit) /* With all due haste, for debugging */
1043 (void) signal(SIGQUIT, SIG_DFL);
1044
1045 /*
1046 * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
1047 * arguments the first of them is the name of a shell file from which to
1048 * read commands.
1049 */
1050 if (nofile == 0 && argc > 0) {
1051 nofile = xopen(tempv[0], O_RDONLY|O_LARGEFILE);
1052 if (nofile < 0) {
1053 child = 1; /* So this ... */
1054 /* ... doesn't return */
1055 stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1056 }
1057 xfree(ffile);
1058 dolzero = 1;
1059 ffile = SAVE(tempv[0]);
1060 /*
1061 * Replace FSHIN. Handle /dev/std{in,out,err} specially
1062 * since once they are closed we cannot open them again.
1063 * In that case we use our own saved descriptors
1064 */
1065 if ((SHIN = dmove(nofile, FSHIN)) < 0)
1066 switch(nofile) {
1067 case 0:
1068 SHIN = FSHIN;
1069 break;
1070 case 1:
1071 SHIN = FSHOUT;
1072 break;
1073 case 2:
1074 SHIN = FSHDIAG;
1075 break;
1076 default:
1077 stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1078 break;
1079 }
1080 (void) close_on_exec(SHIN, 1);
1081 prompt = 0;
1082 /* argc not used any more */ tempv++;
1083 }
1084
1085 /*
1086 * Call to closem() used to be part of initdesc(). Now called below where
1087 * the script name argument has become stdin. Kernel may have used a file
1088 * descriptor to hold the name of the script (setuid case) and this name
1089 * mustn't be lost by closing the fd too soon.
1090 */
1091 closem();
1092
1093 /*
1094 * Consider input a tty if it really is or we are interactive. but not for
1095 * editing (christos)
1096 */
1097 if (!(intty = isatty(SHIN))) {
1098 if (adrof(STRedit))
1099 unsetv(STRedit);
1100 editing = 0;
1101 }
1102 intty |= intact;
1103 #ifndef convex
1104 if (intty || (intact && isatty(SHOUT))) {
1105 if (!batch && (uid != euid || gid != egid)) {
1106 errno = EACCES;
1107 child = 1; /* So this ... */
1108 /* ... doesn't return */
1109 stderror(ERR_SYSTEM, progname, strerror(errno));
1110 }
1111 }
1112 #endif /* convex */
1113 isoutatty = isatty(SHOUT);
1114 isdiagatty = isatty(SHDIAG);
1115 /*
1116 * Decide whether we should play with signals or not. If we are explicitly
1117 * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
1118 * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
1119 * Note that in only the login shell is it likely that parent may have set
1120 * signals to be ignored
1121 */
1122 if (loginsh || intact || (intty && isatty(SHOUT)))
1123 setintr = 1;
1124 settell();
1125 /*
1126 * Save the remaining arguments in argv.
1127 */
1128 setq(STRargv, blk2short(tempv), &shvhed, VAR_READWRITE);
1129
1130 /*
1131 * Set up the prompt.
1132 */
1133 if (prompt) {
1134 setcopy(STRprompt, STRdefprompt, VAR_READWRITE);
1135 /* that's a meta-questionmark */
1136 setcopy(STRprompt2, STRmquestion, VAR_READWRITE);
1137 setcopy(STRprompt3, STRKCORRECT, VAR_READWRITE);
1138 }
1139
1140 /*
1141 * If we are an interactive shell, then start fiddling with the signals;
1142 * this is a tricky game.
1143 */
1144 shpgrp = mygetpgrp();
1145 opgrp = tpgrp = -1;
1146 if (setintr) {
1147 struct sigaction osig;
1148
1149 **argv = '-';
1150 if (!quitit) /* Wary! */
1151 (void) signal(SIGQUIT, SIG_IGN);
1152 pintr_disabled = 1;
1153 sigset_interrupting(SIGINT, queue_pintr);
1154 (void) signal(SIGTERM, SIG_IGN);
1155
1156 /*
1157 * No reason I can see not to save history on all these events..
1158 * Most usual occurrence is in a window system, where we're not a login
1159 * shell, but might as well be... (sg)
1160 * But there might be races when lots of shells exit together...
1161 * [this is also incompatible].
1162 * We have to be mre careful here. If the parent wants to
1163 * ignore the signals then we leave them untouched...
1164 * We also only setup the handlers for shells that are trully
1165 * interactive.
1166 */
1167 sigaction(SIGHUP, NULL, &osig);
1168 if (loginsh || osig.sa_handler != SIG_IGN)
1169 /* exit processing on HUP */
1170 sigset_interrupting(SIGHUP, queue_phup);
1171 #ifdef SIGXCPU
1172 sigaction(SIGXCPU, NULL, &osig);
1173 if (loginsh || osig.sa_handler != SIG_IGN)
1174 /* exit processing on XCPU */
1175 sigset_interrupting(SIGXCPU, queue_phup);
1176 #endif
1177 #ifdef SIGXFSZ
1178 sigaction(SIGXFSZ, NULL, &osig);
1179 if (loginsh || osig.sa_handler != SIG_IGN)
1180 /* exit processing on XFSZ */
1181 sigset_interrupting(SIGXFSZ, queue_phup);
1182 #endif
1183
1184 if (quitit == 0 && targinp == 0) {
1185 #ifdef SIGTSTP
1186 (void) signal(SIGTSTP, SIG_IGN);
1187 #endif
1188 #ifdef SIGTTIN
1189 (void) signal(SIGTTIN, SIG_IGN);
1190 #endif
1191 #ifdef SIGTTOU
1192 (void) signal(SIGTTOU, SIG_IGN);
1193 #endif
1194 /*
1195 * Wait till in foreground, in case someone stupidly runs csh &
1196 * dont want to try to grab away the tty.
1197 */
1198 if (isatty(FSHDIAG))
1199 f = FSHDIAG;
1200 else if (isatty(FSHOUT))
1201 f = FSHOUT;
1202 else if (isatty(OLDSTD))
1203 f = OLDSTD;
1204 else
1205 f = -1;
1206
1207 #ifdef NeXT
1208 /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */
1209 if (f != -1 && shpgrp == 0) {
1210 shpgrp = getpid();
1211 (void) setpgid(0, shpgrp);
1212 (void) tcsetpgrp(f, shpgrp);
1213 }
1214 #endif /* NeXT */
1215 #ifdef BSDJOBS /* if we have tty job control */
1216 if (f != -1 && grabpgrp(f, shpgrp) != -1) {
1217 /*
1218 * Thanks to Matt Day for the POSIX references, and to
1219 * Paul Close for the SGI clarification.
1220 */
1221 if (setdisc(f) != -1) {
1222 opgrp = shpgrp;
1223 shpgrp = getpid();
1224 tpgrp = shpgrp;
1225 if (tcsetpgrp(f, shpgrp) == -1) {
1226 /*
1227 * On hpux 7.03 this fails with EPERM. This happens on
1228 * the 800 when opgrp != shpgrp at this point. (we were
1229 * forked from a non job control shell)
1230 * POSIX 7.2.4, says we failed because the process
1231 * group specified did not belong to a process
1232 * in the same session with the tty. So we set our
1233 * process group and try again.
1234 */
1235 if (setpgid(0, shpgrp) == -1) {
1236 xprintf("setpgid:");
1237 goto notty;
1238 }
1239 if (tcsetpgrp(f, shpgrp) == -1) {
1240 xprintf("tcsetpgrp:");
1241 goto notty;
1242 }
1243 }
1244 /*
1245 * We check the process group now. If it is the same, then
1246 * we don't need to set it again. On hpux 7.0 on the 300's
1247 * if we set it again it fails with EPERM. This is the
1248 * correct behavior according to POSIX 4.3.3 if the process
1249 * was a session leader .
1250 */
1251 else if (shpgrp != mygetpgrp()) {
1252 if(setpgid(0, shpgrp) == -1) {
1253 xprintf("setpgid:");
1254 goto notty;
1255 }
1256 }
1257 #ifdef IRIS4D
1258 /*
1259 * But on irix 3.3 we need to set it again, even if it is
1260 * the same. We do that to tell the system that we
1261 * need BSD process group compatibility.
1262 */
1263 else
1264 (void) setpgid(0, shpgrp);
1265 #endif
1266 (void) close_on_exec(dcopy(f, FSHTTY), 1);
1267 }
1268 else
1269 tpgrp = -1;
1270 }
1271 if (tpgrp == -1) {
1272 notty:
1273 xprintf(CGETS(11, 1, "Warning: no access to tty (%s).\n"),
1274 strerror(errno));
1275 xprintf("%s",
1276 CGETS(11, 2, "Thus no job control in this shell.\n"));
1277 /*
1278 * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't
1279 * have access to tty, disable editing too
1280 */
1281 if (adrof(STRedit))
1282 unsetv(STRedit);
1283 editing = 0;
1284 }
1285 #else /* BSDJOBS */ /* don't have job control, so frotz it */
1286 tpgrp = -1;
1287 #endif /* BSDJOBS */
1288 }
1289 }
1290 if (setintr == 0 && parintr.sa_handler == SIG_DFL)
1291 setintr = 1;
1292
1293 /*
1294 * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the
1295 * handler is installed with signal(2) or sigset(2). sigaction(2) must
1296 * be used instead.
1297 *
1298 * David Dawes (dawes@physics.su.oz.au) Sept 1991
1299 */
1300 sigset_interrupting(SIGCHLD, queue_pchild);
1301
1302 if (intty && !targinp)
1303 (void) ed_Setup(editing);/* Get the tty state, and set defaults */
1304 /* Only alter the tty state if editing */
1305
1306 /*
1307 * Set an exit here in case of an interrupt or error reading the shell
1308 * start-up scripts.
1309 */
1310 osetintr = setintr;
1311 oparintr = parintr;
1312 (void)cleanup_push_mark(); /* There is no outer handler */
1313 if (setexit() != 0) /* PWP */
1314 reenter = 1;
1315 else
1316 reenter = 0;
1317 exitset++;
1318 haderr = 0; /* In case second time through */
1319 if (!fast && reenter == 0) {
1320 /* Will have varval(STRhome) here because set fast if don't */
1321 {
1322 pintr_disabled++;
1323 cleanup_push(&pintr_disabled, disabled_cleanup);
1324 setintr = 0;/*FIXRESET:cleanup*/
1325 /* onintr in /etc/ files has no effect */
1326 parintr.sa_handler = SIG_IGN;/*FIXRESET: cleanup*/
1327 #ifdef LOGINFIRST
1328 #ifdef _PATH_DOTLOGIN
1329 if (loginsh)
1330 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1331 #endif
1332 #endif
1333
1334 #ifdef _PATH_DOTCSHRC
1335 (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL);
1336 #endif
1337 if (!targinp && !onelflg && !havhash)
1338 dohash(NULL,NULL);
1339 #ifndef LOGINFIRST
1340 #ifdef _PATH_DOTLOGIN
1341 if (loginsh)
1342 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1343 #endif
1344 #endif
1345 cleanup_until(&pintr_disabled);
1346 setintr = osetintr;
1347 parintr = oparintr;
1348 }
1349 #ifdef LOGINFIRST
1350 if (loginsh)
1351 (void) srccat(varval(STRhome), STRsldotlogin);
1352 #endif
1353 /* upward compat. */
1354 if (!srccat(varval(STRhome), STRsldottcshrc))
1355 (void) srccat(varval(STRhome), STRsldotcshrc);
1356
1357 if (!targinp && !onelflg && !havhash)
1358 dohash(NULL,NULL);
1359
1360 /*
1361 * Source history before .login so that it is available in .login
1362 */
1363 loadhist(NULL, 0);
1364 #ifndef LOGINFIRST
1365 if (loginsh)
1366 (void) srccat(varval(STRhome), STRsldotlogin);
1367 #endif
1368 if (loginsh || rdirs)
1369 loaddirs(NULL);
1370 }
1371 /* Reset interrupt flag */
1372 setintr = osetintr;
1373 parintr = oparintr;
1374 exitset--;
1375
1376 /* Initing AFTER .cshrc is the Right Way */
1377 if (intty && !targinp) { /* PWP setup stuff */
1378 ed_Init(); /* init the new line editor */
1379 #ifdef SIG_WINDOW
1380 check_window_size(1); /* mung environment */
1381 #endif /* SIG_WINDOW */
1382 }
1383
1384 /*
1385 * Now are ready for the -v and -x flags
1386 */
1387 if (nverbose)
1388 setNS(STRverbose);
1389 if (nexececho)
1390 setNS(STRecho);
1391
1392
1393 if (targinp) {
1394 /* If this -c command caused an error before, skip processing */
1395 if (reenter && arginp) {
1396 exitcode = 1;
1397 goto done;
1398 }
1399
1400 arginp = SAVE(targinp);
1401 /*
1402 * we put the command into a variable
1403 */
1404 if (arginp != NULL)
1405 setv(STRcommand, quote(Strsave(arginp)), VAR_READWRITE);
1406
1407 /*
1408 * * Give an error on -c arguments that end in * backslash to
1409 * ensure that you don't make * nonportable csh scripts.
1410 */
1411 {
1412 int count;
1413
1414 cp = Strend(arginp);
1415 count = 0;
1416 while (cp > arginp && *--cp == '\\')
1417 ++count;
1418 if ((count & 1) != 0) {
1419 exiterr = 1;
1420 stderror(ERR_ARGC);
1421 }
1422 }
1423 }
1424 /*
1425 * All the rest of the world is inside this call. The argument to process
1426 * indicates whether it should catch "error unwinds". Thus if we are a
1427 * interactive shell our call here will never return by being blown past on
1428 * an error.
1429 */
1430 process(setintr);
1431
1432 done:
1433 /*
1434 * Mop-up.
1435 */
1436 /* Take care of these (especially HUP) here instead of inside flush. */
1437 handle_pending_signals();
1438 if (intty) {
1439 if (loginsh) {
1440 xprintf("logout\n");
1441 xclose(SHIN);
1442 child = 1;
1443 #ifdef TESLA
1444 do_logout = 1;
1445 #endif /* TESLA */
1446 goodbye(NULL, NULL);
1447 }
1448 else {
1449 xprintf("exit\n");
1450 }
1451 }
1452 record();
1453 exitstat();
1454 return exitcode;
1455 }
1456
1457 void
1458 untty(void)
1459 {
1460 #ifdef BSDJOBS
1461 if (tpgrp > 0 && opgrp != shpgrp) {
1462 (void) setpgid(0, opgrp);
1463 (void) tcsetpgrp(FSHTTY, opgrp);
1464 (void) resetdisc(FSHTTY);
1465 }
1466 #endif /* BSDJOBS */
1467 }
1468
1469 void
1470 importpath(Char *cp)
1471 {
1472 size_t i = 0;
1473 Char *dp;
1474 Char **pv;
1475 int c;
1476
1477 for (dp = cp; *dp; dp++)
1478 if (*dp == PATHSEP)
1479 i++;
1480 /*
1481 * i+2 where i is the number of colons in the path. There are i+1
1482 * directories in the path plus we need room for a zero terminator.
1483 */
1484 pv = xcalloc(i + 2, sizeof(Char *));
1485 dp = cp;
1486 i = 0;
1487 if (*dp)
1488 for (;;) {
1489 if ((c = *dp) == PATHSEP || c == 0) {
1490 *dp = 0;
1491 pv[i++] = Strsave(*cp ? cp : STRdot);
1492 if (c) {
1493 cp = dp + 1;
1494 *dp = PATHSEP;
1495 }
1496 else
1497 break;
1498 }
1499 #ifdef WINNT_NATIVE
1500 else if (*dp == '\\')
1501 *dp = '/';
1502 #endif /* WINNT_NATIVE */
1503 dp++;
1504 }
1505 pv[i] = 0;
1506 cleanup_push(pv, blk_cleanup);
1507 setq(STRpath, pv, &shvhed, VAR_READWRITE);
1508 cleanup_ignore(pv);
1509 cleanup_until(pv);
1510 }
1511
1512 /*
1513 * Source to the file which is the catenation of the argument names.
1514 */
1515 static int
1516 srccat(Char *cp, Char *dp)
1517 {
1518 if (cp[0] == '/' && cp[1] == '\0')
1519 return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
1520 else {
1521 Char *ep;
1522 char *ptr;
1523 int rv;
1524
1525 #ifdef WINNT_NATIVE
1526 ep = Strend(cp);
1527 if (ep != cp && ep[-1] == '/' && dp[0] == '/') /* silly win95 */
1528 dp++;
1529 #endif /* WINNT_NATIVE */
1530
1531 ep = Strspl(cp, dp);
1532 cleanup_push(ep, xfree);
1533 ptr = short2str(ep);
1534
1535 rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
1536 cleanup_until(ep);
1537 return rv;
1538 }
1539 }
1540
1541 /*
1542 * Source to a file putting the file descriptor in a safe place (> 2).
1543 */
1544 #ifndef WINNT_NATIVE
1545 static int
1546 #else
1547 int
1548 #endif /*WINNT_NATIVE*/
1549 srcfile(const char *f, int onlyown, int flag, Char **av)
1550 {
1551 int unit;
1552
1553 if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1)
1554 return 0;
1555 cleanup_push(&unit, open_cleanup);
1556 unit = dmove(unit, -1);
1557 cleanup_ignore(&unit);
1558 cleanup_until(&unit);
1559
1560 (void) close_on_exec(unit, 1);
1561 srcunit(unit, onlyown, flag, av);
1562 return 1;
1563 }
1564
1565
1566 /*
1567 * Save the shell state, and establish new argument vector, and new input
1568 * fd.
1569 */
1570 static void
1571 st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
1572 {
1573 st->insource = insource;
1574 st->SHIN = SHIN;
1575 /* Want to preserve the meaning of "source file >output".
1576 * Save old descriptors, move new 0,1,2 to safe places and assign
1577 * them to SH* and let process() redo 0,1,2 from them.
1578 *
1579 * The macro returns true if d1 and d2 are good and they point to
1580 * different things. If you don't avoid saving duplicate
1581 * descriptors, you really limit the depth of "source" recursion
1582 * you can do because of all the open file descriptors. -IAN!
1583 */
1584 #define NEED_SAVE_FD(d1,d2) \
1585 (fstat(d1, &s1) != -1 && fstat(d2, &s2) != -1 \
1586 && (s1.st_ino != s2.st_ino || s1.st_dev != s2.st_dev) )
1587
1588 st->OLDSTD = st->SHOUT = st->SHDIAG = -1;/* test later to restore these */
1589 if (didfds) {
1590 struct stat s1, s2;
1591 if (NEED_SAVE_FD(0,OLDSTD)) {
1592 st->OLDSTD = OLDSTD;
1593 OLDSTD = dmove(0, -1);
1594 (void)close_on_exec(OLDSTD, 1);
1595 }
1596 if (NEED_SAVE_FD(1,SHOUT)) {
1597 st->SHOUT = SHOUT;
1598 SHOUT = dmove(1, -1);
1599 (void)close_on_exec(SHOUT, 1);
1600 }
1601 if (NEED_SAVE_FD(2,SHDIAG)) {
1602 st->SHDIAG = SHDIAG;
1603 SHDIAG = dmove(2, -1);
1604 (void)close_on_exec(SHDIAG, 1);
1605 }
1606 donefds();
1607 }
1608
1609 st->intty = intty;
1610 st->whyles = whyles;
1611 st->gointr = gointr;
1612 st->arginp = arginp;
1613 st->evalp = evalp;
1614 st->evalvec = evalvec;
1615 st->alvecp = alvecp;
1616 st->alvec = alvec;
1617 st->onelflg = onelflg;
1618 st->enterhist = enterhist;
1619 st->justpr = justpr;
1620 if (hflg)
1621 st->HIST = HIST;
1622 else
1623 st->HIST = '\0';
1624 st->cantell = cantell;
1625 cpybin(st->B, B);
1626
1627 /*
1628 * we can now pass arguments to source.
1629 * For compatibility we do that only if arguments were really
1630 * passed, otherwise we keep the old, global $argv like before.
1631 */
1632 if (av != NULL && *av != NULL) {
1633 struct varent *vp;
1634 if ((vp = adrof(STRargv)) != NULL && vp->vec != NULL)
1635 st->argv = saveblk(vp->vec);
1636 else
1637 st->argv = NULL;
1638 setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE);
1639 }
1640 else
1641 st->argv = NULL;
1642 st->av = av;
1643
1644 SHIN = unit; /* Do this first */
1645
1646 /* Establish new input arena */
1647 {
1648 fbuf = NULL;
1649 fseekp = feobp = fblocks = 0;
1650 settell();
1651 }
1652
1653 arginp = 0;
1654 onelflg = 0;
1655 intty = isatty(SHIN);
1656 whyles = 0;
1657 gointr = 0;
1658 evalvec = 0;
1659 evalp = 0;
1660 alvec = al;
1661 alvecp = 0;
1662 enterhist = hflg;
1663 if (enterhist)
1664 HIST = '\0';
1665 insource = 1;
1666 }
1667
1668
1669 /*
1670 * Restore the shell to a saved state
1671 */
1672 static void
1673 st_restore(void *xst)
1674 {
1675 struct saved_state *st;
1676
1677 st = xst;
1678 if (st->SHIN == -1)
1679 return;
1680
1681 /* Reset input arena */
1682 {
1683 int i;
1684 Char** nfbuf = fbuf;
1685 int nfblocks = fblocks;
1686
1687 fblocks = 0;
1688 fbuf = NULL;
1689 for (i = 0; i < nfblocks; i++)
1690 xfree(nfbuf[i]);
1691 xfree(nfbuf);
1692 }
1693 cpybin(B, st->B);
1694
1695 xclose(SHIN);
1696
1697 insource = st->insource;
1698 SHIN = st->SHIN;
1699 if (st->OLDSTD != -1)
1700 xclose(OLDSTD), OLDSTD = st->OLDSTD;
1701 if (st->SHOUT != -1)
1702 xclose(SHOUT), SHOUT = st->SHOUT;
1703 if (st->SHDIAG != -1)
1704 xclose(SHDIAG), SHDIAG = st->SHDIAG;
1705 arginp = st->arginp;
1706 onelflg = st->onelflg;
1707 evalp = st->evalp;
1708 evalvec = st->evalvec;
1709 alvecp = st->alvecp;
1710 alvec = st->alvec;
1711 intty = st->intty;
1712 whyles = st->whyles;
1713 gointr = st->gointr;
1714 if (st->HIST != '\0')
1715 HIST = st->HIST;
1716 enterhist = st->enterhist;
1717 cantell = st->cantell;
1718 justpr = st->justpr;
1719
1720 if (st->argv != NULL)
1721 setq(STRargv, st->argv, &shvhed, VAR_READWRITE);
1722 else if (st->av != NULL && *st->av != NULL && adrof(STRargv) != NULL)
1723 unsetv(STRargv);
1724 }
1725
1726 /*
1727 * Source to a unit. If onlyown it must be our file or our group or
1728 * we don't chance it. This occurs on ".cshrc"s and the like.
1729 */
1730 static void
1731 srcunit(int unit, int onlyown, int hflg, Char **av)
1732 {
1733 struct saved_state st;
1734
1735 st.SHIN = -1; /* st_restore checks this */
1736
1737 if (unit < 0)
1738 return;
1739
1740 if (onlyown) {
1741 struct stat stb;
1742
1743 if (fstat(unit, &stb) < 0) {
1744 xclose(unit);
1745 return;
1746 }
1747 }
1748
1749 /* Does nothing before st_save() because st.SHIN == -1 */
1750 cleanup_push(&st, st_restore);
1751 if (setintr) {
1752 pintr_disabled++;
1753 cleanup_push(&pintr_disabled, disabled_cleanup);
1754 }
1755
1756 /* Save the current state and move us to a new state */
1757 st_save(&st, unit, hflg, NULL, av);
1758
1759 /*
1760 * Now if we are allowing commands to be interrupted, we let ourselves be
1761 * interrupted.
1762 */
1763 if (setintr) {
1764 cleanup_until(&pintr_disabled);
1765 pintr_disabled++;
1766 cleanup_push(&pintr_disabled, disabled_cleanup);
1767 }
1768
1769 process(0); /* 0 -> blow away on errors */
1770
1771 /* Restore the old state */
1772 cleanup_until(&st);
1773 }
1774
1775
1776 /*ARGSUSED*/
1777 void
1778 goodbye(Char **v, struct command *c)
1779 {
1780 USE(v);
1781 USE(c);
1782 record();
1783
1784 if (loginsh) {
1785 size_t omark;
1786 sigset_t set;
1787
1788 sigemptyset(&set);
1789 signal(SIGQUIT, SIG_IGN);
1790 sigaddset(&set, SIGQUIT);
1791 sigprocmask(SIG_UNBLOCK, &set, NULL);
1792 signal(SIGINT, SIG_IGN);
1793 sigaddset(&set, SIGINT);
1794 signal(SIGTERM, SIG_IGN);
1795 sigaddset(&set, SIGTERM);
1796 signal(SIGHUP, SIG_IGN);
1797 sigaddset(&set, SIGHUP);
1798 sigprocmask(SIG_UNBLOCK, &set, NULL);
1799 phup_disabled = 1;
1800 setintr = 0; /* No interrupts after "logout" */
1801 /* Trap errors inside .logout */
1802 omark = cleanup_push_mark();
1803 if (setexit() == 0) {
1804 if (!(adrof(STRlogout)))
1805 setcopy(STRlogout, STRnormal, VAR_READWRITE);
1806 #ifdef _PATH_DOTLOGOUT
1807 (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1808 #endif
1809 if (adrof(STRhome))
1810 (void) srccat(varval(STRhome), STRsldtlogout);
1811 #ifdef TESLA
1812 do_logout = 1;
1813 #endif /* TESLA */
1814 }
1815 cleanup_pop_mark(omark);
1816 }
1817 exitstat();
1818 }
1819
1820 void
1821 exitstat(void)
1822 {
1823 #ifdef PROF
1824 _mcleanup();
1825 #endif
1826 /*
1827 * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1828 * directly because we poke child here. Otherwise we might continue
1829 * unwarrantedly (sic).
1830 */
1831 child = 1;
1832
1833 xexit(getn(varval(STRstatus)));
1834 }
1835
1836 /*
1837 * in the event of a HUP we want to save the history
1838 */
1839 void
1840 phup(void)
1841 {
1842 static int again = 0;
1843 if (again++)
1844 return;
1845
1846 if (loginsh) {
1847 setcopy(STRlogout, STRhangup, VAR_READWRITE);
1848 #ifdef _PATH_DOTLOGOUT
1849 (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1850 #endif
1851 if (adrof(STRhome))
1852 (void) srccat(varval(STRhome), STRsldtlogout);
1853 }
1854
1855 record();
1856
1857 #ifdef POSIXJOBS
1858 /*
1859 * We kill the last foreground process group. It then becomes
1860 * responsible to propagate the SIGHUP to its progeny.
1861 */
1862 {
1863 struct process *pp, *np;
1864
1865 for (pp = proclist.p_next; pp; pp = pp->p_next) {
1866 np = pp;
1867 /*
1868 * Find if this job is in the foreground. It could be that
1869 * the process leader has exited and the foreground flag
1870 * is cleared for it.
1871 */
1872 do
1873 /*
1874 * If a process is in the foreground we try to kill
1875 * it's process group. If we succeed, then the
1876 * whole job is gone. Otherwise we keep going...
1877 * But avoid sending HUP to the shell again.
1878 */
1879 if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) {
1880 np->p_flags &= ~PHUP;
1881 if (killpg(np->p_jobid, SIGHUP) != -1) {
1882 /* In case the job was suspended... */
1883 #ifdef SIGCONT
1884 (void) killpg(np->p_jobid, SIGCONT);
1885 #endif
1886 break;
1887 }
1888 }
1889 while ((np = np->p_friends) != pp);
1890 }
1891 }
1892 #endif /* POSIXJOBS */
1893
1894 xexit(SIGHUP);
1895 }
1896
1897 static Char *jobargv[2] = {STRjobs, 0};
1898
1899 /*
1900 * Catch an interrupt, e.g. during lexical input.
1901 * If we are an interactive shell, we reset the interrupt catch
1902 * immediately. In any case we drain the shell output,
1903 * and finally go through the normal error mechanism, which
1904 * gets a chance to make the shell go away.
1905 */
1906 int just_signaled; /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1907
1908 void
1909 pintr(void)
1910 {
1911 just_signaled = 1;
1912 pintr1(1);
1913 }
1914
1915 void
1916 pintr1(int wantnl)
1917 {
1918 if (setintr) {
1919 if (pjobs) {
1920 pjobs = 0;
1921 xputchar('\n');
1922 dojobs(jobargv, NULL);
1923 stderror(ERR_NAME | ERR_INTR);
1924 }
1925 }
1926 /* MH - handle interrupted completions specially */
1927 {
1928 if (InsideCompletion)
1929 stderror(ERR_SILENT);
1930 }
1931 /* JV - Make sure we shut off inputl */
1932 {
1933 (void) Cookedmode();
1934 GettingInput = 0;
1935 if (evalvec)
1936 doneinp = 1;
1937 }
1938 drainoline();
1939 #ifdef HAVE_GETPWENT
1940 (void) endpwent();
1941 #endif
1942
1943 /*
1944 * If we have an active "onintr" then we search for the label. Note that if
1945 * one does "onintr -" then we shan't be interruptible so we needn't worry
1946 * about that here.
1947 */
1948 if (gointr) {
1949 gotolab(gointr);
1950 reset();
1951 }
1952 else if (intty && wantnl) {
1953 if (editing) {
1954 /*
1955 * If we are editing a multi-line input command, and move to
1956 * the beginning of the line, we don't want to trash it when
1957 * we hit ^C
1958 */
1959 PastBottom();
1960 ClearLines();
1961 ClearDisp();
1962 }
1963 else {
1964 /* xputchar('\n'); *//* Some like this, others don't */
1965 (void) putraw('\r');
1966 (void) putraw('\n');
1967 }
1968 }
1969 stderror(ERR_SILENT);
1970 }
1971
1972 /*
1973 * Process is the main driving routine for the shell.
1974 * It runs all command processing, except for those within { ... }
1975 * in expressions (which is run by a routine evalav in sh.exp.c which
1976 * is a stripped down process), and `...` evaluation which is run
1977 * also by a subset of this code in sh.glob.c in the routine backeval.
1978 *
1979 * The code here is a little strange because part of it is interruptible
1980 * and hence freeing of structures appears to occur when none is necessary
1981 * if this is ignored.
1982 *
1983 * Note that if catch is not set then we will unwind on any error.
1984 * If an end-of-file occurs, we return.
1985 */
1986 void
1987 process(int catch)
1988 {
1989 jmp_buf_t osetexit;
1990 /* PWP: This might get nuked by longjmp so don't make it a register var */
1991 size_t omark;
1992 volatile int didexitset = 0;
1993
1994 getexit(osetexit);
1995 omark = cleanup_push_mark();
1996 for (;;) {
1997 struct command *t;
1998 int hadhist, old_pintr_disabled;
1999
2000 (void)setexit();
2001 if (didexitset == 0) {
2002 exitset++;
2003 didexitset++;
2004 }
2005 pendjob();
2006
2007 justpr = enterhist; /* execute if not entering history */
2008
2009 if (haderr) {
2010 if (!catch) {
2011 /* unwind */
2012 doneinp = 0;
2013 cleanup_pop_mark(omark);
2014 resexit(osetexit);
2015 reset();
2016 }
2017 haderr = 0;
2018 /*
2019 * Every error is eventually caught here or the shell dies. It is
2020 * at this point that we clean up any left-over open files, by
2021 * closing all but a fixed number of pre-defined files. Thus
2022 * routines don't have to worry about leaving files open due to
2023 * deeper errors... they will get closed here.
2024 */
2025 closem();
2026 continue;
2027 }
2028 if (doneinp) {
2029 doneinp = 0;
2030 break;
2031 }
2032 if (chkstop)
2033 chkstop--;
2034 if (neednote)
2035 pnote();
2036 if (intty && prompt && evalvec == 0) {
2037 just_signaled = 0;
2038 mailchk();
2039 /*
2040 * Watch for logins/logouts. Next is scheduled commands stored
2041 * previously using "sched." Then execute periodic commands.
2042 * Following that, the prompt precmd is run.
2043 */
2044 #ifndef HAVENOUTMP
2045 watch_login(0);
2046 #endif /* !HAVENOUTMP */
2047 sched_run();
2048 period_cmd();
2049 precmd();
2050 /*
2051 * If we are at the end of the input buffer then we are going to
2052 * read fresh stuff. Otherwise, we are rereading input and don't
2053 * need or want to prompt.
2054 */
2055 if (fseekp == feobp && aret == TCSH_F_SEEK)
2056 printprompt(0, NULL);
2057 flush();
2058 setalarm(1);
2059 }
2060 if (seterr) {
2061 xfree(seterr);
2062 seterr = NULL;
2063 }
2064
2065 /*
2066 * Interruptible during interactive reads
2067 */
2068 if (setintr)
2069 pintr_push_enable(&old_pintr_disabled);
2070 freelex(¶ml);
2071 hadhist = lex(¶ml);
2072 if (setintr)
2073 cleanup_until(&old_pintr_disabled);
2074 cleanup_push(¶ml, lex_cleanup);
2075
2076 /*
2077 * Echo not only on VERBOSE, but also with history expansion. If there
2078 * is a lexical error then we forego history echo.
2079 * Do not echo if we're only entering history (source -h).
2080 */
2081 if ((hadhist && !seterr && intty && !tellwhat && !Expand && !whyles) ||
2082 (!enterhist && adrof(STRverbose)))
2083 {
2084 int odidfds = didfds;
2085 haderr = 1;
2086 didfds = 0;
2087 prlex(¶ml);
2088 flush();
2089 haderr = 0;
2090 didfds = odidfds;
2091 }
2092 (void) alarm(0); /* Autologout OFF */
2093 alrmcatch_disabled = 1;
2094
2095 /*
2096 * Save input text on the history list if reading in old history, or it
2097 * is from the terminal at the top level and not in a loop.
2098 *
2099 * PWP: entry of items in the history list while in a while loop is done
2100 * elsewhere...
2101 */
2102 if (enterhist || (catch && intty && !whyles && !tellwhat && !arun))
2103 savehist(¶ml, enterhist > 1);
2104
2105 if (Expand && seterr)
2106 Expand = 0;
2107
2108 /*
2109 * Print lexical error messages, except when sourcing history lists.
2110 */
2111 if (!enterhist && seterr)
2112 stderror(ERR_OLD);
2113
2114 /*
2115 * If had a history command :p modifier then this is as far as we
2116 * should go
2117 */
2118 if (justpr)
2119 goto cmd_done;
2120
2121 /*
2122 * If had a tellwhat from twenex() then do
2123 */
2124 if (tellwhat) {
2125 (void) tellmewhat(¶ml, NULL);
2126 goto cmd_done;
2127 }
2128
2129 alias(¶ml);
2130
2131 #ifdef BSDJOBS
2132 /*
2133 * If we are interactive, try to continue jobs that we have stopped
2134 */
2135 if (prompt)
2136 continue_jobs(¶ml);
2137 #endif /* BSDJOBS */
2138
2139 /*
2140 * Check to see if the user typed "rm * .o" or something
2141 */
2142 if (prompt)
2143 rmstar(¶ml);
2144 /*
2145 * Parse the words of the input into a parse tree.
2146 */
2147 t = syntax(paraml.next, ¶ml, 0);
2148 /*
2149 * We cannot cleanup push here, because cd /blah; echo foo
2150 * would rewind t on the chdir error, and free the rest of the command
2151 */
2152 if (seterr) {
2153 freesyn(t);
2154 stderror(ERR_OLD);
2155 }
2156
2157 postcmd();
2158 /*
2159 * Execute the parse tree From: Michael Schroeder
2160 * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
2161 */
2162 execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
2163 freesyn(t);
2164
2165 /*
2166 * Made it!
2167 */
2168 #ifdef SIG_WINDOW
2169 if (windowchg || (catch && intty && !whyles && !tellwhat)) {
2170 (void) check_window_size(0); /* for window systems */
2171 }
2172 #endif /* SIG_WINDOW */
2173 setcopy(STR_, InputBuf, VAR_READWRITE | VAR_NOGLOB);
2174 cmd_done:
2175 if (cleanup_reset())
2176 cleanup_until(¶ml);
2177 else
2178 haderr = 1;
2179 }
2180 cleanup_pop_mark(omark);
2181 resexit(osetexit);
2182 exitset--;
2183 handle_pending_signals();
2184 }
2185
2186 /*ARGSUSED*/
2187 void
2188 dosource(Char **t, struct command *c)
2189 {
2190 Char *f;
2191 int hflg = 0;
2192 char *file;
2193
2194 USE(c);
2195 t++;
2196 if (*t && eq(*t, STRmh)) {
2197 if (*++t == NULL)
2198 stderror(ERR_NAME | ERR_HFLAG);
2199 hflg++;
2200 }
2201 else if (*t && eq(*t, STRmm)) {
2202 if (*++t == NULL)
2203 stderror(ERR_NAME | ERR_MFLAG);
2204 hflg = 2;
2205 }
2206
2207 f = globone(*t++, G_ERROR);
2208 file = strsave(short2str(f));
2209 cleanup_push(file, xfree);
2210 xfree(f);
2211 t = glob_all_or_error(t);
2212 cleanup_push(t, blk_cleanup);
2213 if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
2214 stderror(ERR_SYSTEM, file, strerror(errno));
2215 cleanup_until(file);
2216 }
2217
2218 /*
2219 * Check for mail.
2220 * If we are a login shell, then we don't want to tell
2221 * about any mail file unless its been modified
2222 * after the time we started.
2223 * This prevents us from telling the user things he already
2224 * knows, since the login program insists on saying
2225 * "You have mail."
2226 */
2227
2228 /*
2229 * The AMS version.
2230 * This version checks if the file is a directory, and if so,
2231 * tells you the number of files in it, otherwise do the old thang.
2232 * The magic "+1" in the time calculation is to compensate for
2233 * an AFS bug where directory mtimes are set to 1 second in
2234 * the future.
2235 */
2236
2237 static void
2238 mailchk(void)
2239 {
2240 struct varent *v;
2241 Char **vp;
2242 time_t t;
2243 int intvl, cnt;
2244 struct stat stb;
2245 int new;
2246
2247 v = adrof(STRmail);
2248 if (v == NULL || v->vec == NULL)
2249 return;
2250 (void) time(&t);
2251 vp = v->vec;
2252 cnt = blklen(vp);
2253 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
2254 if (intvl < 1)
2255 intvl = 1;
2256 if (chktim + intvl > t)
2257 return;
2258 for (; *vp; vp++) {
2259 char *filename = short2str(*vp);
2260 char *mboxdir = filename;
2261
2262 if (stat(filename, &stb) < 0)
2263 continue;
2264 #if defined(BSDTIMES) || defined(_SEQUENT_)
2265 new = stb.st_mtime > time0.tv_sec;
2266 #else
2267 new = stb.st_mtime > seconds0;
2268 #endif
2269 if (S_ISDIR(stb.st_mode)) {
2270 DIR *mailbox;
2271 int mailcount = 0;
2272 char *tempfilename;
2273 struct stat stc;
2274
2275 tempfilename = xasprintf("%s/new", filename);
2276
2277 if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) {
2278 /*
2279 * "filename/new" exists and is a directory; you are
2280 * using Qmail.
2281 */
2282 stb = stc;
2283 #if defined(BSDTIMES) || defined(_SEQUENT_)
2284 new = stb.st_mtime > time0.tv_sec;
2285 #else
2286 new = stb.st_mtime > seconds0;
2287 #endif
2288 mboxdir = tempfilename;
2289 }
2290
2291 if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) {
2292 xfree(tempfilename);
2293 continue;
2294 }
2295
2296 mailbox = opendir(mboxdir);
2297 xfree(tempfilename);
2298 if (mailbox == NULL)
2299 continue;
2300
2301 /* skip . and .. */
2302 if (!readdir(mailbox) || !readdir(mailbox)) {
2303 (void)closedir(mailbox);
2304 continue;
2305 }
2306
2307 while (readdir(mailbox))
2308 mailcount++;
2309
2310 (void)closedir(mailbox);
2311 if (mailcount == 0)
2312 continue;
2313
2314 if (cnt == 1)
2315 xprintf(CGETS(11, 3, "You have %d mail messages.\n"),
2316 mailcount);
2317 else
2318 xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"),
2319 mailcount, filename);
2320 }
2321 else {
2322 char *type;
2323
2324 if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime ||
2325 (stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
2326 (loginsh && !new))
2327 continue;
2328 type = strsave(new ? CGETS(11, 6, "new ") : "");
2329 cleanup_push(type, xfree);
2330 if (cnt == 1)
2331 xprintf(CGETS(11, 5, "You have %smail.\n"), type);
2332 else
2333 xprintf(CGETS(11, 7, "You have %smail in %s.\n"), type, filename);
2334 cleanup_until(type);
2335 }
2336 }
2337 chktim = t;
2338 }
2339
2340 /*
2341 * Extract a home directory from the password file
2342 * The argument points to a buffer where the name of the
2343 * user whose home directory is sought is currently.
2344 * We return home directory of the user, or NULL.
2345 */
2346 Char *
2347 gethdir(const Char *home)
2348 {
2349 Char *h;
2350
2351 /*
2352 * Is it us?
2353 */
2354 if (*home == '\0') {
2355 if ((h = varval(STRhome)) != STRNULL)
2356 return Strsave(h);
2357 else
2358 return NULL;
2359 }
2360
2361 /*
2362 * Look in the cache
2363 */
2364 if ((h = gettilde(home)) == NULL)
2365 return NULL;
2366 else
2367 return Strsave(h);
2368 }
2369
2370 /*
2371 * Move the initial descriptors to their eventual
2372 * resting places, closing all other units.
2373 */
2374 void
2375 initdesc(void)
2376 {
2377 #ifdef NLS_BUGS
2378 #ifdef NLS_CATALOGS
2379 nlsclose();
2380 #endif /* NLS_CATALOGS */
2381 #endif /* NLS_BUGS */
2382
2383
2384 didfds = 0; /* 0, 1, 2 aren't set up */
2385 (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1);
2386 (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1);
2387 (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1);
2388 (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1);
2389 #ifndef CLOSE_ON_EXEC
2390 didcch = 0; /* Havent closed for child */
2391 #endif /* CLOSE_ON_EXEC */
2392 if (SHDIAG >= 0)
2393 isdiagatty = isatty(SHDIAG);
2394 else
2395 isdiagatty = 0;
2396 if (SHDIAG >= 0)
2397 isoutatty = isatty(SHOUT);
2398 else
2399 isoutatty = 0;
2400 #ifdef NLS_BUGS
2401 #ifdef NLS_CATALOGS
2402 nlsinit();
2403 #endif /* NLS_CATALOGS */
2404 #endif /* NLS_BUGS */
2405 }
2406
2407
2408 void
2409 #ifdef PROF
2410 done(int i)
2411 #else
2412 xexit(int i)
2413 #endif
2414 {
2415 #ifdef TESLA
2416 if (loginsh && do_logout) {
2417 /* this is to send hangup signal to the develcon */
2418 /* we toggle DTR. clear dtr - sleep 1 - set dtr */
2419 /* ioctl will return ENOTTY for pty's but we ignore it */
2420 /* exitstat will run after disconnect */
2421 /* we sleep for 2 seconds to let things happen in */
2422 /* .logout and rechist() */
2423 #ifdef TIOCCDTR
2424 (void) sleep(2);
2425 (void) ioctl(FSHTTY, TIOCCDTR, NULL);
2426 (void) sleep(1);
2427 (void) ioctl(FSHTTY, TIOCSDTR, NULL);
2428 #endif /* TIOCCDTR */
2429 }
2430 #endif /* TESLA */
2431
2432 {
2433 struct process *pp, *np;
2434 pid_t mypid = getpid();
2435 /* Kill all processes marked for hup'ing */
2436 for (pp = proclist.p_next; pp; pp = pp->p_next) {
2437 np = pp;
2438 do
2439 if ((np->p_flags & PHUP) && np->p_jobid != shpgrp &&
2440 np->p_parentid == mypid) {
2441 if (killpg(np->p_jobid, SIGHUP) != -1) {
2442 /* In case the job was suspended... */
2443 #ifdef SIGCONT
2444 (void) killpg(np->p_jobid, SIGCONT);
2445 #endif
2446 break;
2447 }
2448 }
2449 while ((np = np->p_friends) != pp);
2450 }
2451 }
2452 untty();
2453 #ifdef NLS_CATALOGS
2454 /*
2455 * We need to call catclose, because SVR4 leaves symlinks behind otherwise
2456 * in the catalog directories. We cannot close on a vforked() child,
2457 * because messages will stop working on the parent too.
2458 */
2459 if (child == 0)
2460 nlsclose();
2461 #endif /* NLS_CATALOGS */
2462 #ifdef WINNT_NATIVE
2463 nt_cleanup();
2464 #endif /* WINNT_NATIVE */
2465 _exit(i);
2466 }
2467
2468 #ifndef _PATH_DEFPATH
2469 static Char **
2470 defaultpath(void)
2471 {
2472 char *ptr;
2473 Char **blk, **blkp;
2474 struct stat stb;
2475
2476 blkp = blk = xmalloc(sizeof(Char *) * 10);
2477
2478 #ifndef NODOT
2479 # ifndef DOTLAST
2480 *blkp++ = Strsave(STRdot);
2481 # endif
2482 #endif
2483
2484 #define DIRAPPEND(a) \
2485 if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
2486 *blkp++ = SAVE(ptr)
2487
2488 #ifdef _PATH_LOCAL
2489 DIRAPPEND(_PATH_LOCAL);
2490 #endif
2491
2492 #ifdef _PATH_USRUCB
2493 DIRAPPEND(_PATH_USRUCB);
2494 #endif
2495
2496 #ifdef _PATH_USRBSD
2497 DIRAPPEND(_PATH_USRBSD);
2498 #endif
2499
2500 #ifdef _PATH_BIN
2501 DIRAPPEND(_PATH_BIN);
2502 #endif
2503
2504 #ifdef _PATH_USRBIN
2505 DIRAPPEND(_PATH_USRBIN);
2506 #endif
2507
2508 #undef DIRAPPEND
2509
2510 #ifndef NODOT
2511 # ifdef DOTLAST
2512 *blkp++ = Strsave(STRdot);
2513 # endif
2514 #endif
2515 *blkp = NULL;
2516 return (blk);
2517 }
2518 #endif
2519
2520 static void
2521 record(void)
2522 {
2523 static int again = 0;
2524 int ophup_disabled;
2525
2526 if (again++)
2527 return;
2528
2529 ophup_disabled = phup_disabled;
2530 phup_disabled = 1;
2531 if (!fast) {
2532 recdirs(NULL, adrof(STRsavedirs) != NULL);
2533 rechist(NULL, adrof(STRsavehist) != NULL);
2534 }
2535 displayHistStats("Exiting"); /* no-op unless DEBUG_HIST */
2536 phup_disabled = ophup_disabled;
2537 }
2538
2539 /*
2540 * Grab the tty repeatedly, and give up if we are not in the correct
2541 * tty process group.
2542 */
2543 int
2544 grabpgrp(int fd, pid_t desired)
2545 {
2546 struct sigaction old;
2547 pid_t pgrp;
2548 size_t i;
2549
2550 for (i = 0; i < 100; i++) {
2551 if ((pgrp = tcgetpgrp(fd)) == -1)
2552 return -1;
2553 if (pgrp == desired)
2554 return 0;
2555 (void)sigaction(SIGTTIN, NULL, &old);
2556 (void)signal(SIGTTIN, SIG_DFL);
2557 (void)kill(0, SIGTTIN);
2558 (void)sigaction(SIGTTIN, &old, NULL);
2559 }
2560 errno = EPERM;
2561 return -1;
2562 }