csh and tcsh work

Sources of csh/sh.c

csh と tcsh のソース

Bill Joy

First published: October, 1978.

Copyright © 1979 Regents of the University of California.

  1. 1
    /* Copyright (c) 1979 Regents of the University of California */
  2. 2
    #include "sh.h"
  3. 3
  4. 4
    /*
  5. 5
     * C Shell
  6. 6
     *
  7. 7
     * Bill Joy, UC Berkeley
  8. 8
     * October, 1978
  9. 9
     */
  10. 10
  11. 11
    char	*pathlist[] =	{ SRCHPATH, 0 };
  12. 12
  13. 13
    main(c, av)
  14. 14
    	int c;
  15. 15
    	char **av;
  16. 16
    {
  17. 17
    	register char **v, *cp;
  18. 18
    	int nofile = 0;
  19. 19
    	int reenter = 0;
  20. 20
    	bool nverbose = 0, nexececho = 0, quitit = 0, fast = 0, prompt = 1;
  21. 21
    	char *hp;
  22. 22
  23. 23
    	settimes();			/* Immed. estab. timing base */
  24. 24
    	hp = getenv("HOME");
  25. 25
    	v = av;
  26. 26
    	if (eq(v[0], "a.out"))		/* A.out's are quittable */
  27. 27
    		quitit = 1;
  28. 28
    	uid = getuid();
  29. 29
    #ifdef V6
  30. 30
    	loginsh = eq(*v, "-");		/* To do .login/.logout */
  31. 31
    #else
  32. 32
    	loginsh = **v == '-';
  33. 33
    #endif
  34. 34
    	if (loginsh)
  35. 35
    		time(&chktim);
  36. 36
  37. 37
    	/*
  38. 38
    	 * Move the descriptors to safe places.
  39. 39
    	 * The variable didfds is 0 while we have only FSH* to work with.
  40. 40
    	 * When didfds is true, we have 0,1,2 and prefer to use these.
  41. 41
    	 */
  42. 42
    	initdesc();
  43. 43
  44. 44
    	/*
  45. 45
    	 * Initialize the shell variables.
  46. 46
    	 * ARGV and PROMPT are initialized later.
  47. 47
    	 * STATUS is also munged in several places.
  48. 48
    	 * CHILD is munged when forking/waiting
  49. 49
    	 */
  50. 50
  51. 51
    	set("status", "0");
  52. 52
    	if (hp == 0)
  53. 53
    		fast++;			/* No home -> can't read scripts */
  54. 54
    	else
  55. 55
    		set("home", hp);
  56. 56
    	if (uid == 0)
  57. 57
    		pathlist[0] = "/etc";
  58. 58
    	set1("path", saveblk(pathlist), &shvhed);
  59. 59
    	/*
  60. 60
    	 * Re-initialize path if set in environment
  61. 61
    	 *
  62. 62
    	cp = getenv("PATH");
  63. 63
    	if (cp != 0) {
  64. 64
    		register int i = 0;
  65. 65
    		register char *dp;
  66. 66
    		register char **pv;
  67. 67
  68. 68
    		for (dp = cp; *dp; dp++)
  69. 69
    			if (*dp == ':')
  70. 70
    				i++;
  71. 71
    		pv = calloc(i+1, sizeof (char **));
  72. 72
    		dp = cp;
  73. 73
    		i = 0;
  74. 74
    		while (*dp) {
  75. 75
    			if (*dp == ':') {
  76. 76
    				*dp = 0;
  77. 77
    				pv[i++] = savestr(cp);
  78. 78
    				*dp = ':';
  79. 79
    			} else if (*dp == 0) {
  80. 80
    				pv[i++] = savestr(cp);
  81. 81
    				break;
  82. 82
    			}
  83. 83
    			dp++;
  84. 84
    		}
  85. 85
    		pv[i] = 0;
  86. 86
    		set1("path", pv, &shvhed);
  87. 87
    	}
  88. 88
    */
  89. 89
    	set("shell", SHELLPATH);
  90. 90
  91. 91
    	doldol = putn(getpid());		/* For $$ */
  92. 92
    	shtemp = strspl("/tmp/sh", doldol);	/* For << */
  93. 93
  94. 94
    	/*
  95. 95
    	 * Record the interrupt states from the parent process.
  96. 96
    	 * If the parent is non-interruptible our hand must be forced
  97. 97
    	 * or we (and our children) won't be either.
  98. 98
    	 * Our children inherit termination from our parent.
  99. 99
    	 * We catch it only if we are the login shell.
  100. 100
    	 */
  101. 101
    	parintr = signal(SIGINT, SIG_IGN);	/* parents interruptibility */
  102. 102
    	signal(SIGINT, parintr);			/* ... restore */
  103. 103
    	parterm = signal(SIGTERM, SIG_IGN);	/* parents terminability */
  104. 104
    	signal(SIGTERM, parterm);			/* ... restore */
  105. 105
  106. 106
    	/*
  107. 107
    	 * Process the arguments.
  108. 108
    	 *
  109. 109
    	 * Note that processing of -v/-x is actually delayed till after
  110. 110
    	 * script processing.
  111. 111
    	 *
  112. 112
    	 * We set the first character of our name to be '-' if we are
  113. 113
    	 * a shell running interruptible commands.  Many programs which
  114. 114
    	 * examine ps'es use this to filter such shells out.
  115. 115
    	 */
  116. 116
    	c--, v++;
  117. 117
    	while (c > 0 && (cp = v[0])[0] == '-') {
  118. 118
    		do switch (*cp++) {
  119. 119
  120. 120
    		case 0:			/* -	Interruptible, no prompt */
  121. 121
    			prompt = 0;
  122. 122
    			**av = '-';
  123. 123
    			nofile++;
  124. 124
    			break;
  125. 125
  126. 126
    		case 'c':		/* -c	Command input from arg */
  127. 127
    			if (c == 1)
  128. 128
    				exit(0);
  129. 129
    			c--, v++;
  130. 130
    			arginp = v[0];
  131. 131
    			prompt = 0;
  132. 132
    			nofile++;
  133. 133
    			break;
  134. 134
  135. 135
    		case 'e':		/* -e	Exit on any error */
  136. 136
    			exiterr++;
  137. 137
    			break;
  138. 138
  139. 139
    		case 'f':		/* -f	Fast start */
  140. 140
    			fast++;
  141. 141
    			break;
  142. 142
  143. 143
    		case 'i':		/* -i	Interactive, even if !intty */
  144. 144
    			intact++;
  145. 145
    			**av = '-';
  146. 146
    			nofile++;
  147. 147
    			break;
  148. 148
  149. 149
    		case 'n':		/* -n	Don't execute */
  150. 150
    			noexec++;
  151. 151
    			break;
  152. 152
  153. 153
    		case 'q':		/* -q	(Undoc'd) ... die on quit */
  154. 154
    			quitit = 1;
  155. 155
    			break;
  156. 156
  157. 157
    		case 's':		/* -s	Read from std input */
  158. 158
    			nofile++;
  159. 159
    			if (isatty(SHIN))
  160. 160
    				**v = '-';
  161. 161
    			break;
  162. 162
  163. 163
    		case 't':		/* -t	Read one line from input */
  164. 164
    			onelflg = 2;
  165. 165
    			if (isatty(SHIN))
  166. 166
    				**v = '-';
  167. 167
    			prompt = 0;
  168. 168
    			nofile++;
  169. 169
    			break;
  170. 170
  171. 171
    		case 'v':		/* -v	Echo hist expanded input */
  172. 172
    			nverbose = 1;			/* ... later */
  173. 173
    			break;
  174. 174
  175. 175
    		case 'x':		/* -x	Echo just before execution */
  176. 176
    			nexececho = 1;			/* ... later */
  177. 177
    			break;
  178. 178
  179. 179
    		case 'V':		/* -V	Echo hist expanded input */
  180. 180
    			setNS("verbose");		/* NOW! */
  181. 181
    			break;
  182. 182
  183. 183
    		case 'X':		/* -X	Echo just before execution */
  184. 184
    			setNS("echo");			/* NOW! */
  185. 185
    			break;
  186. 186
  187. 187
    		} while (*cp);
  188. 188
    		v++, c--;
  189. 189
    	}
  190. 190
  191. 191
    	if (quitit)			/* With all due haste, for debugging */
  192. 192
    		signal(SIGQUIT, SIG_DFL);
  193. 193
  194. 194
    	/*
  195. 195
    	 * Unless prevented by -, -c, -i, -s, or -t, if there
  196. 196
    	 * are remaining arguments the first of them is the name
  197. 197
    	 * of a shell file from which to read commands.
  198. 198
    	 */
  199. 199
    	if (nofile == 0 && c > 0) {
  200. 200
    		nofile = open(v[0], 0);
  201. 201
    		if (nofile < 0) {
  202. 202
    			child++;		/* So this ... */
  203. 203
    			Perror(v[0]);		/* ... doesn't return */
  204. 204
    		}
  205. 205
    		file = v[0];
  206. 206
    		SHIN = dmove(nofile, FSHIN);	/* Replace FSHIN */
  207. 207
    		prompt = 0;
  208. 208
    		c--, v++;
  209. 209
    	}
  210. 210
  211. 211
    	/*
  212. 212
    	 * Consider input a tty if it really is or we are interactive.
  213. 213
    	 */
  214. 214
    	intty = intact || isatty(SHIN);
  215. 215
    #ifdef TELL
  216. 216
    	settell();
  217. 217
    #endif
  218. 218
    	/*
  219. 219
    	 * Commands are interruptible if we are interactive
  220. 220
    	 * or the process which created us was.
  221. 221
    	 */
  222. 222
    	if (intact || parintr == SIG_DFL)
  223. 223
    		**av = '-';
  224. 224
  225. 225
    	/*
  226. 226
    	 * Save the remaining arguments in ARGV.
  227. 227
    	 * Normally the system-supplied argument list is ok as
  228. 228
    	 * a zero terminated value block.
  229. 229
    	 * On some version 6 systems, it is -1 terminated and setting it
  230. 230
    	 * to zero messes up "ps" so we change it to zero, copy
  231. 231
    	 * the block of pointers, and put it back the way it was.
  232. 232
    	 */
  233. 233
    /*
  234. 234
    	if (c == 0)
  235. 235
    		set("argv", 0);
  236. 236
    	else
  237. 237
     */
  238. 238
    	if ((int) v[c] == -1) {
  239. 239
    		/* ick */
  240. 240
    		v[c] = 0, setq("argv", copyblk(v), &shvhed), v[c] = (char *) -1;
  241. 241
    	} else
  242. 242
    		setq("argv", v, &shvhed);
  243. 243
  244. 244
    	/*
  245. 245
    	 * Set up the prompt.
  246. 246
    	 */
  247. 247
    	if (prompt)
  248. 248
    		set("prompt", uid == 0 ? "# " : "% ");
  249. 249
  250. 250
    	/*
  251. 251
    	 * If we are an interactive shell, then start fiddling
  252. 252
    	 * with the signals; this is a tricky game.
  253. 253
    	 */
  254. 254
    	if (**av == '-') {
  255. 255
    		setintr++;
  256. 256
    		if (!quitit)		/* Wary! */
  257. 257
    			signal(SIGQUIT, SIG_IGN);
  258. 258
    		signal(SIGINT, SIG_IGN);
  259. 259
    		signal(SIGTERM, SIG_IGN);
  260. 260
    	}
  261. 261
  262. 262
    	/*
  263. 263
    	 * Set an exit here in case of an interrupt or error reading
  264. 264
    	 * the shell start-up scripts.
  265. 265
    	 */
  266. 266
    	setexit();
  267. 267
    	haderr = 0;		/* In case second time through */
  268. 268
    	if (!fast && reenter == 0) {
  269. 269
    		reenter++;
  270. 270
    		/* Will have value("home") here because set fast if don't */
  271. 271
    		srccat(value("home"), "/.cshrc");
  272. 272
    		if (loginsh)
  273. 273
    #ifdef NOHELP
  274. 274
    			srccat("", ".login");
  275. 275
    #else
  276. 276
    			srccat(value("home"), "/.login");
  277. 277
    #endif
  278. 278
    	}
  279. 279
  280. 280
    	/*
  281. 281
    	 * Now are ready for the -v and -x flags
  282. 282
    	 */
  283. 283
    	if (nverbose)
  284. 284
    		setNS("verbose");
  285. 285
    	if (nexececho)
  286. 286
    		setNS("echo");
  287. 287
  288. 288
    	/*
  289. 289
    	 * All the rest of the world is inside this call.
  290. 290
    	 * The argument to process indicates whether it should
  291. 291
    	 * catch "error unwinds".  Thus if we are a interactive shell
  292. 292
    	 * our call here will never return by being blown past on an error.
  293. 293
    	 */
  294. 294
    	process(setintr);
  295. 295
  296. 296
    	/*
  297. 297
    	 * Mop-up.
  298. 298
    	 */
  299. 299
    	if (loginsh) {
  300. 300
    		printf("logout\n");
  301. 301
    		close(SHIN);
  302. 302
    		child++;
  303. 303
    		goodbye();
  304. 304
    	}
  305. 305
    	exitstat();
  306. 306
    }
  307. 307
  308. 308
    /*
  309. 309
     * Source to the file which is the catenation of the argument names.
  310. 310
     */
  311. 311
    srccat(cp, dp)
  312. 312
    	char *cp, *dp;
  313. 313
    {
  314. 314
    	register char *ep = strspl(cp, dp);
  315. 315
    	register int unit = dmove(open(ep, 0), -1);
  316. 316
  317. 317
    	/* ioctl(unit, FIOCLEX, NULL); */
  318. 318
    	xfree(ep);
  319. 319
    #ifdef INGRES
  320. 320
    	srcunit(unit, 0);
  321. 321
    #else
  322. 322
    	srcunit(unit, 1);
  323. 323
    #endif
  324. 324
    }
  325. 325
  326. 326
    /*
  327. 327
     * Source to a unit.  If onlyown it must be our file or
  328. 328
     * we don't chance it.	This occurs on ".cshrc"s and the like.
  329. 329
     */
  330. 330
    srcunit(unit, onlyown)
  331. 331
    	register int unit;
  332. 332
    	bool onlyown;
  333. 333
    {
  334. 334
    	/* We have to push down a lot of state here */
  335. 335
    	/* All this could go into a structure */
  336. 336
    	int oSHIN = -1, oldintty = intty;
  337. 337
    	struct whyle *oldwhyl = whyles;
  338. 338
    	char *ogointr = gointr, *oarginp = arginp;
  339. 339
    	int oonelflg = onelflg;
  340. 340
    #ifdef TELL
  341. 341
    	bool otell = cantell;
  342. 342
    #endif
  343. 343
    	struct Bin saveB;
  344. 344
  345. 345
    	/* The (few) real local variables */
  346. 346
    	jmp_buf oldexit;
  347. 347
    	int reenter;
  348. 348
    	register int (*oldint)();
  349. 349
  350. 350
    	if (unit < 0)
  351. 351
    		return;
  352. 352
    	if (onlyown) {
  353. 353
    		struct stat stb;
  354. 354
  355. 355
    #ifdef CC
  356. 356
    		if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) {
  357. 357
    #endif
  358. 358
    #ifdef CORY
  359. 359
    		if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) {
  360. 360
    #endif
  361. 361
    #ifndef CC
  362. 362
    #ifndef CORY
  363. 363
    		if (fstat(unit, &stb) < 0 || stb.st_uid != uid) {
  364. 364
    #endif
  365. 365
    #endif
  366. 366
    			close(unit);
  367. 367
    			return;
  368. 368
    		}
  369. 369
    	}
  370. 370
  371. 371
    	/*
  372. 372
    	 * There is a critical section here while we are pushing down the
  373. 373
    	 * input stream since we have stuff in different structures.
  374. 374
    	 * If we weren't careful an interrupt could corrupt SHIN's Bin
  375. 375
    	 * structure and kill the shell.
  376. 376
    	 *
  377. 377
    	 * We could avoid the critical region by grouping all the stuff
  378. 378
    	 * in a single structure and pointing at it to move it all at
  379. 379
    	 * once.  This is less efficient globally on many variable references
  380. 380
    	 * however.
  381. 381
    	 */
  382. 382
    	getexit(oldexit);
  383. 383
    	reenter = 0;
  384. 384
    	oldint = signal(SIGINT, SIG_IGN);
  385. 385
    	setexit();
  386. 386
    	reenter++;
  387. 387
    	if (reenter == 1) {
  388. 388
    		/* Setup the new values of the state stuff saved above */
  389. 389
    		copy(&saveB, &B, sizeof saveB);
  390. 390
    		fbuf = (char **) 0;
  391. 391
    		fseekp = feobp = fblocks = 0;
  392. 392
    		oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
  393. 393
    		intty = isatty(SHIN), whyles = 0, gointr = 0;
  394. 394
    		/*
  395. 395
    		 * Now if we are allowing commands to be interrupted,
  396. 396
    		 * we let ourselves be interrupted.
  397. 397
    		 */
  398. 398
    		signal(SIGINT, setintr ? pintr : oldint);
  399. 399
    #ifdef TELL
  400. 400
    		settell();
  401. 401
    #endif
  402. 402
    		process(0);		/* 0 -> blow away on errors */
  403. 403
    	}
  404. 404
    	signal(SIGINT, oldint);
  405. 405
    	if (oSHIN >= 0) {
  406. 406
    		register int i;
  407. 407
  408. 408
    		/* We made it to the new state... free up its storage */
  409. 409
    		/* This code could get run twice but xfree doesn't care */
  410. 410
    		for (i = 0; i < fblocks; i++)
  411. 411
    			xfree(fbuf[i]);
  412. 412
    		xfree(fbuf);
  413. 413
  414. 414
    		/* Reset input arena */
  415. 415
    		copy(&B, &saveB, sizeof B);
  416. 416
  417. 417
    		close(SHIN), SHIN = oSHIN;
  418. 418
    		arginp = oarginp, onelflg = oonelflg;
  419. 419
    		intty = oldintty, whyles = oldwhyl, gointr = ogointr;
  420. 420
    #ifdef TELL
  421. 421
    		cantell = otell;
  422. 422
    #endif
  423. 423
    	}
  424. 424
  425. 425
    	resexit(oldexit);
  426. 426
    	/*
  427. 427
    	 * If process reset() (effectively an unwind) then
  428. 428
    	 * we must also unwind.
  429. 429
    	 */
  430. 430
    	if (reenter >= 2)
  431. 431
    		error(0);
  432. 432
    }
  433. 433
  434. 434
    goodbye()
  435. 435
    {
  436. 436
  437. 437
    	if (loginsh) {
  438. 438
    		signal(SIGQUIT, SIG_IGN);
  439. 439
    		signal(SIGINT, SIG_IGN);
  440. 440
    		signal(SIGTERM, SIG_IGN);
  441. 441
    		setintr = 0;		/* No interrupts after "logout" */
  442. 442
    		if (adrof("home"))
  443. 443
    			srccat(value("home"), "/.logout");
  444. 444
    	}
  445. 445
    	exitstat();
  446. 446
    }
  447. 447
  448. 448
    exitstat()
  449. 449
    {
  450. 450
  451. 451
    	/*
  452. 452
    	 * Note that if STATUS is corrupted (i.e. getn bombs)
  453. 453
    	 * then error will exit directly because we poke child here.
  454. 454
    	 * Otherwise we might continue unwarrantedly (sic).
  455. 455
    	 */
  456. 456
    	child++;
  457. 457
    	exit(getn(value("status")));
  458. 458
    }
  459. 459
  460. 460
    /*
  461. 461
     * Catch an interrupt, e.g. during lexical input.
  462. 462
     * If we are an interactive shell, we reset the interrupt catch
  463. 463
     * immediately.  In any case we drain the shell output,
  464. 464
     * and finally go through the normal error mechanism, which
  465. 465
     * gets a chance to make the shell go away.
  466. 466
     */
  467. 467
    pintr()
  468. 468
    {
  469. 469
  470. 470
    	if (setintr)
  471. 471
    		signal(SIGINT, SIG_IGN);
  472. 472
    	draino();
  473. 473
  474. 474
    	/*
  475. 475
    	 * If we have an active "onintr" then we search for the label.
  476. 476
    	 * Note that if one does "onintr -" then we shan't be interruptible
  477. 477
    	 * so we needn't worry about that here.
  478. 478
    	 */
  479. 479
    	if (gointr) {
  480. 480
    		search(ZGOTO, 0, gointr);
  481. 481
    		timflg = 0;
  482. 482
    		reset();
  483. 483
    	} else if (intty)
  484. 484
    		printf("\n");		/* Some like this, others don't */
  485. 485
    	error(0);
  486. 486
    }
  487. 487
  488. 488
    /*
  489. 489
     * Process is the main driving routine for the shell.
  490. 490
     * It runs all command processing, except for those within { ... }
  491. 491
     * in expressions (which is run by a routine evalav in sh.exp.c which
  492. 492
     * is a stripped down process), and `...` evaluation which is run
  493. 493
     * also by a subset of this code in sh.glob.c in the routine backeval.
  494. 494
     *
  495. 495
     * The code here is a little strange because part of it is interruptible
  496. 496
     * and hence freeing of structures appears to occur when none is necessary
  497. 497
     * if this is ignored.
  498. 498
     *
  499. 499
     * Note that if catch is not set then we will unwind on any error.
  500. 500
     * In an end-of-file occurs, we return.
  501. 501
     */
  502. 502
    process(catch)
  503. 503
    	bool catch;
  504. 504
    {
  505. 505
    	register char *cp;
  506. 506
    	jmp_buf osetexit;
  507. 507
    	struct wordent paraml;
  508. 508
    	struct command *t;
  509. 509
  510. 510
    	getexit(osetexit);
  511. 511
    	for (;;) {
  512. 512
    		paraml.next = paraml.prev = ¶ml;
  513. 513
    		paraml.word = "";
  514. 514
    		t = 0;
  515. 515
    		setexit();
  516. 516
    		justpr = 0;			/* A chance to execute */
  517. 517
  518. 518
    		/*
  519. 519
    		 * Interruptible during interactive reads
  520. 520
    		 */
  521. 521
    		if (setintr)
  522. 522
    			signal(SIGINT, pintr);
  523. 523
  524. 524
    		/*
  525. 525
    		 * For the sake of reset()
  526. 526
    		 */
  527. 527
    		freelex(¶ml), freesyn(t), t = 0;
  528. 528
  529. 529
    		if (haderr) {
  530. 530
    			if (!catch) {
  531. 531
    				/* unwind */
  532. 532
    				doneinp = 0;
  533. 533
    				resexit(osetexit);
  534. 534
    				reset();
  535. 535
    			}
  536. 536
    			haderr = 0;
  537. 537
    			/*
  538. 538
    			 * Every error is eventually caught here or
  539. 539
    			 * the shell dies.  It is at this
  540. 540
    			 * point that we clean up any left-over open
  541. 541
    			 * files, by closing all but a fixed number
  542. 542
    			 * of pre-defined files.  Thus routines don't
  543. 543
    			 * have to worry about leaving files open due
  544. 544
    			 * to deeper errors... they will get closed here.
  545. 545
    			 */
  546. 546
    			closem();
  547. 547
    			continue;
  548. 548
    		}
  549. 549
    		if (doneinp) {
  550. 550
    			doneinp = 0;
  551. 551
    			break;
  552. 552
    		}
  553. 553
    		if (intty) {
  554. 554
    			mailchk();
  555. 555
    			/*
  556. 556
    			 * If we are at the end of the input buffer
  557. 557
    			 * then we are going to read fresh stuff.
  558. 558
    			 * Otherwise, we are rereading input and don't
  559. 559
    			 * need or want to prompt.
  560. 560
    			 */
  561. 561
    			if (fseekp == feobp)
  562. 562
    				if (!whyles)
  563. 563
    					for (cp = value("prompt"); *cp; cp++)
  564. 564
    						if (*cp == '!')
  565. 565
    							printf("%d", eventno + 1);
  566. 566
    						else {
  567. 567
    							if (*cp == '\\' && cp[1] == '!')
  568. 568
    								cp++;
  569. 569
    							putchar(*cp | QUOTE);
  570. 570
    						}
  571. 571
    				else
  572. 572
    					/*
  573. 573
    					 * Prompt for forward reading loop
  574. 574
    					 * body content.
  575. 575
    					 */
  576. 576
    					printf("? ");
  577. 577
    			flush();
  578. 578
    		}
  579. 579
    		err = 0;
  580. 580
  581. 581
    		/*
  582. 582
    		 * Echo not only on VERBOSE, but also with history expansion.
  583. 583
    		 * If there is a lexical error then we forego history echo.
  584. 584
    		 */
  585. 585
    		if (lex(¶ml) && !err && intty || adrof("verbose")) {
  586. 586
    			haderr = 1;
  587. 587
    			prlex(¶ml);
  588. 588
    			haderr = 0;
  589. 589
    		}
  590. 590
  591. 591
    		/*
  592. 592
    		 * The parser may lose space if interrupted.
  593. 593
    		 */
  594. 594
    		if (setintr)
  595. 595
    			signal(SIGINT, SIG_IGN);
  596. 596
  597. 597
    		/*
  598. 598
    		 * Save input text on the history list if it
  599. 599
    		 * is from the terminal at the top level and not
  600. 600
    		 * in a loop.
  601. 601
    		 */
  602. 602
    		if (catch && intty && !whyles)
  603. 603
    			savehist(¶ml);
  604. 604
  605. 605
    		/*
  606. 606
    		 * Print lexical error messages.
  607. 607
    		 */
  608. 608
    		if (err)
  609. 609
    			error(err);
  610. 610
  611. 611
    		/*
  612. 612
    		 * If had a history command :p modifier then
  613. 613
    		 * this is as far as we should go
  614. 614
    		 */
  615. 615
    		if (justpr)
  616. 616
    			reset();
  617. 617
  618. 618
    		alias(¶ml);
  619. 619
  620. 620
    		/*
  621. 621
    		 * Parse the words of the input into a parse tree.
  622. 622
    		 */
  623. 623
    		t = syntax(paraml.next, ¶ml);
  624. 624
    		if (err)
  625. 625
    			error(err);
  626. 626
  627. 627
    		/*
  628. 628
    		 * Execute the parse tree
  629. 629
    		 */
  630. 630
    		execute(t);
  631. 631
  632. 632
    		/*
  633. 633
    		 * Made it!
  634. 634
    		 */
  635. 635
    		freelex(¶ml), freesyn(t);
  636. 636
    	}
  637. 637
    	resexit(osetexit);
  638. 638
    }
  639. 639
  640. 640
    dosource(t)
  641. 641
    	register char **t;
  642. 642
    {
  643. 643
    	register char *f;
  644. 644
    	register int u;
  645. 645
  646. 646
    	t++;
  647. 647
    	f = globone(*t);
  648. 648
    	u = dmove(open(f, 0), -1);
  649. 649
    	xfree(f);
  650. 650
    	if (u < 0)
  651. 651
    		Perror(f);
  652. 652
    	srcunit(u, 0);
  653. 653
    }
  654. 654
  655. 655
    /*
  656. 656
     * Check for mail.
  657. 657
     * If we are a login shell, then we don't want to tell
  658. 658
     * about any mail file unless its been modified
  659. 659
     * after the time we started.
  660. 660
     * This prevents us from telling the user things he already
  661. 661
     * knows, since the login program insist on saying
  662. 662
     * "You have mail."
  663. 663
     */
  664. 664
    mailchk()
  665. 665
    {
  666. 666
    	register struct varent *v;
  667. 667
    	register char **vp;
  668. 668
    	time_t t;
  669. 669
    	int intvl, cnt;
  670. 670
  671. 671
    	v = adrof("mail");
  672. 672
    	if (v == 0)
  673. 673
    		return;
  674. 674
    	time(&t);
  675. 675
    	vp = v->vec;
  676. 676
    	cnt = blklen(vp);
  677. 677
    	intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
  678. 678
    	if (intvl < 1)
  679. 679
    		intvl = 1;
  680. 680
    	if (chktim + intvl > t)
  681. 681
    		return;
  682. 682
    	for (; *vp; vp++) {
  683. 683
    		bool new;
  684. 684
    		struct stat stb;
  685. 685
  686. 686
    		if (stat(*vp, &stb) < 0)
  687. 687
    			continue;
  688. 688
    		/*
  689. 689
    		 * We assume that a file has been read if the access time is
  690. 690
    		 * greater than the mod time.
  691. 691
    		 */
  692. 692
    #ifndef CORY
  693. 693
    		if (stb.st_size == 0)
  694. 694
    			continue;
  695. 695
    #endif
  696. 696
    		if (stb.st_atime > stb.st_mtime || stb.st_atime < chktim)
  697. 697
    			continue;
  698. 698
    		new = stb.st_mtime > time0;
  699. 699
    		if (loginsh && !new)
  700. 700
    			continue;
  701. 701
    		if (cnt == 1)
  702. 702
    			printf("You have %smail.\n", new ? "new " : "");
  703. 703
    		else
  704. 704
    			printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
  705. 705
    	}
  706. 706
    	chktim = t;
  707. 707
    }
  708. 708
  709. 709
    #include <pwd.h>
  710. 710
    /*
  711. 711
     * Extract a home directory from the password file
  712. 712
     * The argument points to a buffer where the name of the
  713. 713
     * user whose home directory is sought is currently.
  714. 714
     * We write the home directory of the user back there.
  715. 715
     */
  716. 716
    gethdir(home)
  717. 717
    	char *home;
  718. 718
    {
  719. 719
    	register struct passwd *pp = getpwnam(home);
  720. 720
  721. 721
    	if (pp == 0)
  722. 722
    		return (1);
  723. 723
    	strcpy(home, pp->pw_dir);
  724. 724
    	return (0);
  725. 725
    }
  726. 726
  727. 727
    /*
  728. 728
     * Move the initial descriptors to their eventual
  729. 729
     * resting places, closin all other units.
  730. 730
     */
  731. 731
    initdesc()
  732. 732
    {
  733. 733
  734. 734
    	didcch = 0;			/* Havent closed for child */
  735. 735
    	didfds = 0;			/* 0, 1, 2 aren't set up */
  736. 736
    	SHIN = dcopy(0, FSHIN);
  737. 737
    	SHOUT = dcopy(1, FSHOUT);
  738. 738
    	SHDIAG = dcopy(2, FSHDIAG);
  739. 739
    	OLDSTD = dcopy(SHIN, FOLDSTD);
  740. 740
    	closem();
  741. 741
    }
  742. 742
  743. 743
    #ifndef V6
  744. 744
    exit(i)
  745. 745
    	int i;
  746. 746
    {
  747. 747
  748. 748
    	_exit(i);
  749. 749
    }
  750. 750
    #endif

To the top of this page