Home | History | Annotate | Line # | Download | only in ksh
c_sh.c revision 1.1
      1 /*
      2  * built-in Bourne commands
      3  */
      4 
      5 #include "sh.h"
      6 #include "ksh_stat.h" 	/* umask() */
      7 #include "ksh_time.h"
      8 #include "ksh_times.h"
      9 
     10 static	char *clocktos ARGS((clock_t t));
     11 
     12 /* :, false and true */
     13 int
     14 c_label(wp)
     15 	char **wp;
     16 {
     17 	return wp[0][0] == 'f' ? 1 : 0;
     18 }
     19 
     20 int
     21 c_shift(wp)
     22 	char **wp;
     23 {
     24 	register struct block *l = e->loc;
     25 	register int n;
     26 	long val;
     27 	char *arg;
     28 
     29 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
     30 		return 1;
     31 	arg = wp[builtin_opt.optind];
     32 
     33 	if (arg) {
     34 		evaluate(arg, &val, FALSE);
     35 		n = val;
     36 	} else
     37 		n = 1;
     38 	if (n < 0) {
     39 		bi_errorf("%s: bad number", arg);
     40 		return (1);
     41 	}
     42 	if (l->argc < n) {
     43 		bi_errorf("nothing to shift");
     44 		return (1);
     45 	}
     46 	l->argv[n] = l->argv[0];
     47 	l->argv += n;
     48 	l->argc -= n;
     49 	return 0;
     50 }
     51 
     52 int
     53 c_umask(wp)
     54 	char **wp;
     55 {
     56 	register int i;
     57 	register char *cp;
     58 	int symbolic = 0;
     59 	int old_umask;
     60 	int optc;
     61 
     62 	while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF)
     63 		switch (optc) {
     64 		  case 'S':
     65 			symbolic = 1;
     66 			break;
     67 		  case '?':
     68 			return 1;
     69 		}
     70 	cp = wp[builtin_opt.optind];
     71 	if (cp == NULL) {
     72 		old_umask = umask(0);
     73 		umask(old_umask);
     74 		if (symbolic) {
     75 			char buf[18];
     76 			int j;
     77 
     78 			old_umask = ~old_umask;
     79 			cp = buf;
     80 			for (i = 0; i < 3; i++) {
     81 				*cp++ = "ugo"[i];
     82 				*cp++ = '=';
     83 				for (j = 0; j < 3; j++)
     84 					if (old_umask & (1 << (8 - (3*i + j))))
     85 						*cp++ = "rwx"[j];
     86 				*cp++ = ',';
     87 			}
     88 			cp[-1] = '\0';
     89 			shprintf("%s\n", buf);
     90 		} else
     91 			shprintf("%#3.3o\n", old_umask);
     92 	} else {
     93 		int new_umask;
     94 
     95 		if (digit(*cp)) {
     96 			for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
     97 				new_umask = new_umask * 8 + (*cp - '0');
     98 			if (*cp) {
     99 				bi_errorf("bad number");
    100 				return 1;
    101 			}
    102 		} else {
    103 			/* symbolic format */
    104 			int positions, new_val;
    105 			char op;
    106 
    107 			old_umask = umask(0);
    108 			umask(old_umask); /* in case of error */
    109 			old_umask = ~old_umask;
    110 			new_umask = old_umask;
    111 			positions = 0;
    112 			while (*cp) {
    113 				while (*cp && strchr("augo", *cp))
    114 					switch (*cp++) {
    115 					case 'a': positions |= 0111; break;
    116 					case 'u': positions |= 0100; break;
    117 					case 'g': positions |= 0010; break;
    118 					case 'o': positions |= 0001; break;
    119 					}
    120 				if (!positions)
    121 					positions = 0111; /* default is a */
    122 				if (!strchr("=+-", op = *cp))
    123 					break;
    124 				cp++;
    125 				new_val = 0;
    126 				while (*cp && strchr("rwxugoXs", *cp))
    127 					switch (*cp++) {
    128 					case 'r': new_val |= 04; break;
    129 					case 'w': new_val |= 02; break;
    130 					case 'x': new_val |= 01; break;
    131 					case 'u': new_val |= old_umask >> 6;
    132 						  break;
    133 					case 'g': new_val |= old_umask >> 3;
    134 						  break;
    135 					case 'o': new_val |= old_umask >> 0;
    136 						  break;
    137 					case 'X': if (old_umask & 0111)
    138 							new_val |= 01;
    139 						  break;
    140 					case 's': /* ignored */
    141 						  break;
    142 					}
    143 				new_val = (new_val & 07) * positions;
    144 				switch (op) {
    145 				case '-':
    146 					new_umask &= ~new_val;
    147 					break;
    148 				case '=':
    149 					new_umask = new_val
    150 					    | (new_umask & ~(positions * 07));
    151 					break;
    152 				case '+':
    153 					new_umask |= new_val;
    154 				}
    155 				if (*cp == ',') {
    156 					positions = 0;
    157 					cp++;
    158 				} else if (!strchr("=+-", *cp))
    159 					break;
    160 			}
    161 			if (*cp) {
    162 				bi_errorf("bad mask");
    163 				return 1;
    164 			}
    165 			new_umask = ~new_umask;
    166 		}
    167 		umask(new_umask);
    168 	}
    169 	return 0;
    170 }
    171 
    172 int
    173 c_dot(wp)
    174 	char **wp;
    175 {
    176 	char *file, *cp;
    177 	char **argv;
    178 	int argc;
    179 	int i;
    180 
    181 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    182 		return 1;
    183 
    184 	if ((cp = wp[builtin_opt.optind]) == NULL)
    185 		return 0;
    186 	file = search(cp, path, R_OK, (int *) 0);
    187 	if (file == NULL) {
    188 		bi_errorf("%s: not found", cp);
    189 		return 1;
    190 	}
    191 
    192 	/* Set positional parameters? */
    193 	if (wp[builtin_opt.optind + 1]) {
    194 		argv = wp + builtin_opt.optind;
    195 		argv[0] = e->loc->argv[0]; /* preserve $0 */
    196 		for (argc = 0; argv[argc + 1]; argc++)
    197 			;
    198 	} else {
    199 		argc = 0;
    200 		argv = (char **) 0;
    201 	}
    202 	i = include(file, argc, argv, 0);
    203 	if (i < 0) { /* should not happen */
    204 		bi_errorf("%s: %s", cp, strerror(errno));
    205 		return 1;
    206 	}
    207 	return i;
    208 }
    209 
    210 int
    211 c_wait(wp)
    212 	char **wp;
    213 {
    214 	int UNINITIALIZED(rv);
    215 	int sig;
    216 
    217 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    218 		return 1;
    219 	wp += builtin_opt.optind;
    220 	if (*wp == (char *) 0) {
    221 		while (waitfor((char *) 0, &sig) >= 0)
    222 			;
    223 		rv = sig;
    224 	} else {
    225 		for (; *wp; wp++)
    226 			rv = waitfor(*wp, &sig);
    227 		if (rv < 0)
    228 			rv = sig ? sig : 127; /* magic exit code: bad job-id */
    229 	}
    230 	return rv;
    231 }
    232 
    233 int
    234 c_read(wp)
    235 	char **wp;
    236 {
    237 	register int c = 0;
    238 	int expand = 1, history = 0;
    239 	int expanding;
    240 	int ecode = 0;
    241 	register char *cp;
    242 	int fd = 0;
    243 	struct shf *shf;
    244 	int optc;
    245 	const char *emsg;
    246 	XString cs, xs;
    247 	struct tbl *vp;
    248 	char UNINITIALIZED(*xp);
    249 
    250 	while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != EOF)
    251 		switch (optc) {
    252 #ifdef KSH
    253 		  case 'p':
    254 			if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
    255 				bi_errorf("-p: %s", emsg);
    256 				return 1;
    257 			}
    258 			break;
    259 #endif /* KSH */
    260 		  case 'r':
    261 			expand = 0;
    262 			break;
    263 		  case 's':
    264 			history = 1;
    265 			break;
    266 		  case 'u':
    267 			if (!*(cp = builtin_opt.optarg))
    268 				fd = 0;
    269 			else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
    270 				bi_errorf("-u: %s: %s", cp, emsg);
    271 				return 1;
    272 			}
    273 			break;
    274 		  case '?':
    275 			return 1;
    276 		}
    277 	wp += builtin_opt.optind;
    278 
    279 	if (*wp == NULL)
    280 		*--wp = "REPLY";
    281 
    282 	/* Since we can't necessarily seek backwards on non-regular files,
    283 	 * don't buffer them so we can't read too much.
    284 	 */
    285 	shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
    286 
    287 	if ((cp = strchr(*wp, '?')) != NULL) {
    288 		*cp = 0;
    289 		if (Flag(FTALKING)) {
    290 			/* at&t says it prints prompt on fd if its open
    291 			 * for writing and is a tty, but it doesn't do it
    292 			 */
    293 			shellf("%s", cp+1);
    294 		}
    295 	}
    296 
    297 #ifdef KSH
    298 	/* If we are reading from the co-process for the first time,
    299 	 * make sure the other side of the pipe is closed first.  This allows
    300 	 * the detection of eof.
    301 	 *
    302 	 * This is not compatiable with at&t ksh... the fd is kept so another
    303 	 * coproc can be started with same ouput, however, this means eof
    304 	 * can't be detected...  This is why it is closed here.
    305 	 * If this call is removed, remove the eof check below, too.
    306 	* coproc_readw_close(fd);
    307 	 */
    308 #endif /* KSH */
    309 
    310 	if (history)
    311 		Xinit(xs, xp, 128, ATEMP);
    312 	expanding = 0;
    313 	Xinit(cs, cp, 128, ATEMP);
    314 	for (; *wp != NULL; wp++) {
    315 		for (cp = Xstring(cs, cp); ; ) {
    316 			if (c == '\n' || c == EOF)
    317 				break;
    318 			while (1) {
    319 				c = shf_getc(shf);
    320 				if (c == '\0'
    321 #ifdef OS2
    322 				    || c == '\r'
    323 #endif /* OS2 */
    324 				    )
    325 					continue;
    326 				if (c == EOF && shf_error(shf)
    327 				    && shf_errno(shf) == EINTR)
    328 				{
    329 					/* Was the offending signal one that
    330 					 * would normally kill a process?
    331 					 * If so, pretend the read was killed.
    332 					 */
    333 					ecode = fatal_trap_check();
    334 
    335 					/* non fatal (eg, CHLD), carry on */
    336 					if (!ecode) {
    337 						shf_clearerr(shf);
    338 						continue;
    339 					}
    340 				}
    341 				break;
    342 			}
    343 			if (history) {
    344 				Xcheck(xs, xp);
    345 				Xput(xs, xp, c);
    346 			}
    347 			Xcheck(cs, cp);
    348 			if (expanding) {
    349 				expanding = 0;
    350 				if (c == '\n') {
    351 					c = 0;
    352 					if (Flag(FTALKING) && isatty(fd)) {
    353 						/* set prompt in case this is
    354 						 * called from .profile or $ENV
    355 						 */
    356 						set_prompt(PS2, (Source *) 0);
    357 						pprompt(prompt, 0);
    358 					}
    359 				} else if (c != EOF)
    360 					Xput(cs, cp, c);
    361 				continue;
    362 			}
    363 			if (expand && c == '\\') {
    364 				expanding = 1;
    365 				continue;
    366 			}
    367 			if (c == '\n' || c == EOF)
    368 				break;
    369 			if (ctype(c, C_IFS)) {
    370 				if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS))
    371 					continue;
    372 				if (wp[1])
    373 					break;
    374 			}
    375 			Xput(cs, cp, c);
    376 		}
    377 		/* strip trailing IFS white space from last variable */
    378 		if (!wp[1])
    379 			while (Xlength(cs, cp) && ctype(cp[-1], C_IFS)
    380 			       && ctype(cp[-1], C_IFSWS))
    381 				cp--;
    382 		Xput(cs, cp, '\0');
    383 		vp = global(*wp);
    384 		if (vp->flag & RDONLY) {
    385 			shf_flush(shf);
    386 			bi_errorf("%s is read only", *wp);
    387 			return 1;
    388 		}
    389 		if (Flag(FEXPORT))
    390 			typeset(*wp, EXPORT, 0, 0, 0);
    391 		setstr(vp, Xstring(cs, cp));
    392 	}
    393 
    394 	shf_flush(shf);
    395 	if (history) {
    396 		Xput(xs, xp, '\0');
    397 		source->line++;
    398 		histsave(source->line, Xstring(xs, xp), 1);
    399 		Xfree(xs, xp);
    400 	}
    401 #ifdef KSH
    402 	/* if this is the co-process fd, close the file descriptor
    403 	 * (can get eof if and only if all processes are have died, ie,
    404 	 * coproc.njobs is 0 and the pipe is closed).
    405 	 */
    406 	if (c == EOF && !ecode)
    407 		coproc_read_close(fd);
    408 #endif /* KSH */
    409 
    410 	return ecode ? ecode : c == EOF;
    411 }
    412 
    413 int
    414 c_eval(wp)
    415 	char **wp;
    416 {
    417 	register struct source *s;
    418 
    419 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    420 		return 1;
    421 	s = pushs(SWORDS, ATEMP);
    422 	s->u.strv = wp + builtin_opt.optind;
    423 	return shell(s, FALSE);
    424 }
    425 
    426 int
    427 c_trap(wp)
    428 	char **wp;
    429 {
    430 	int i;
    431 	char *s;
    432 	register Trap *p;
    433 
    434 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    435 		return 1;
    436 	wp += builtin_opt.optind;
    437 
    438 	if (*wp == NULL) {
    439 		int anydfl = 0;
    440 
    441 		for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) {
    442 			if (p->trap == NULL)
    443 				anydfl = 1;
    444 			else {
    445 				shprintf("trap -- ");
    446 				print_value_quoted(p->trap);
    447 				shprintf(" %s\n", p->name);
    448 			}
    449 		}
    450 #if 0 /* this is ugly and not clear POSIX needs it */
    451 		/* POSIX may need this so output of trap can be saved and
    452 		 * used to restore trap conditions
    453 		 */
    454 		if (anydfl) {
    455 			shprintf("trap -- -");
    456 			for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
    457 				if (p->trap == NULL && p->name)
    458 					shprintf(" %s", p->name);
    459 			shprintf(newline);
    460 		}
    461 #endif
    462 		return 0;
    463 	}
    464 
    465 	s = (gettrap(*wp) == NULL) ? *wp++ : NULL; /* get command */
    466 	if (s != NULL && s[0] == '-' && s[1] == '\0')
    467 		s = NULL;
    468 
    469 	/* set/clear traps */
    470 	while (*wp != NULL) {
    471 		p = gettrap(*wp++);
    472 		if (p == NULL) {
    473 			bi_errorf("bad signal %s", wp[-1]);
    474 			return 1;
    475 		}
    476 		settrap(p, s);
    477 	}
    478 	return 0;
    479 }
    480 
    481 int
    482 c_exitreturn(wp)
    483 	char **wp;
    484 {
    485 	int how = LEXIT;
    486 	char *arg;
    487 
    488 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    489 		return 1;
    490 	arg = wp[builtin_opt.optind];
    491 
    492 	if (arg != NULL && !getn(arg, &exstat)) {
    493 		exstat = 1;
    494 		warningf(TRUE, "%s: bad number", arg);
    495 	}
    496 	if (wp[0][0] == 'r') { /* return */
    497 		struct env *ep;
    498 
    499 		/* need to tell if this is exit or return so trap exit will
    500 		 * work right (POSIX)
    501 		 */
    502 		for (ep = e; ep; ep = ep->oenv)
    503 			if (STOP_RETURN(ep->type)) {
    504 				how = LRETURN;
    505 				break;
    506 			}
    507 	}
    508 
    509 	if (how == LEXIT && !really_exit && j_stopped_running()) {
    510 		really_exit = 1;
    511 		how = LSHELL;
    512 	}
    513 
    514 	quitenv();	/* get rid of any i/o redirections */
    515 	unwind(how);
    516 	/*NOTREACHED*/
    517 	return 0;
    518 }
    519 
    520 int
    521 c_brkcont(wp)
    522 	char **wp;
    523 {
    524 	int n, quit;
    525 	struct env *ep, *last_ep = (struct env *) 0;
    526 	char *arg;
    527 
    528 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    529 		return 1;
    530 	arg = wp[builtin_opt.optind];
    531 
    532 	if (!arg)
    533 		n = 1;
    534 	else if (!bi_getn(arg, &n))
    535 		return 1;
    536 	quit = n;
    537 	if (quit <= 0) {
    538 		/* at&t ksh does this for non-interactive shells only - weird */
    539 		bi_errorf("%s: bad value", arg);
    540 		return 1;
    541 	}
    542 
    543 	/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
    544 	for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
    545 		if (ep->type == E_LOOP) {
    546 			if (--quit == 0)
    547 				break;
    548 			ep->flags |= EF_BRKCONT_PASS;
    549 			last_ep = ep;
    550 		}
    551 
    552 	if (quit) {
    553 		/* at&t ksh doesn't print a message - just does what it
    554 		 * can.  We print a message 'cause it helps in debugging
    555 		 * scripts, but don't generate an error (ie, keep going).
    556 		 */
    557 		if (n == quit) {
    558 			warningf(TRUE, "%s: cannot %s", wp[0], wp[0]);
    559 			return 0;
    560 		}
    561 		/* POSIX says if n is too big, the last enclosing loop
    562 		 * shall be used.  Doesn't say to print an error but we
    563 		 * do anyway 'cause the user messed up.
    564 		 */
    565 		last_ep->flags &= ~EF_BRKCONT_PASS;
    566 		warningf(TRUE, "%s: can only %s %d level(s)",
    567 			wp[0], wp[0], n - quit);
    568 	}
    569 
    570 	unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
    571 	/*NOTREACHED*/
    572 }
    573 
    574 int
    575 c_set(wp)
    576 	char **wp;
    577 {
    578 	int argi, setargs;
    579 	struct block *l = e->loc;
    580 	register char **owp = wp;
    581 
    582 	if (wp[1] == NULL) {
    583 		static const char *const args [] = { "set", "-", NULL };
    584 		return c_typeset((char **) args);
    585 	}
    586 
    587 	argi = parse_args(wp, OF_SET, &setargs);
    588 	if (argi < 0)
    589 		return 1;
    590 	/* set $# and $* */
    591 	if (setargs) {
    592 		owp = wp += argi - 1;
    593 		wp[0] = l->argv[0]; /* save $0 */
    594 		while (*++wp != NULL)
    595 			*wp = str_save(*wp, &l->area);
    596 		l->argc = wp - owp - 1;
    597 		l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
    598 		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
    599 			;
    600 	}
    601 	/* POSIX says set exit status is 0, but old scripts that use
    602 	 * getopt(1), use the construct: set -- `getopt ab:c "$@"`
    603 	 * which assumes the exit value set will be that of the ``
    604 	 * (subst_exstat is cleared in execute() so that it will be 0
    605 	 * if there are no command substitutions).
    606 	 */
    607 	return Flag(FPOSIX) ? 0 : subst_exstat;
    608 }
    609 
    610 int
    611 c_unset(wp)
    612 	char **wp;
    613 {
    614 	register char *id;
    615 	int optc, unset_var = 1;
    616 	int ret = 0;
    617 
    618 	while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF)
    619 		switch (optc) {
    620 		  case 'f':
    621 			unset_var = 0;
    622 			break;
    623 		  case 'v':
    624 			unset_var = 1;
    625 			break;
    626 		  case '?':
    627 			return 1;
    628 		}
    629 	wp += builtin_opt.optind;
    630 	for (; (id = *wp) != NULL; wp++)
    631 		if (unset_var) {	/* unset variable */
    632 			struct tbl *vp = global(id);
    633 
    634 			if (!(vp->flag & ISSET))
    635 			    ret = 1;
    636 			if ((vp->flag&RDONLY)) {
    637 				bi_errorf("%s is read only", vp->name);
    638 				return 1;
    639 			}
    640 			unset(vp, strchr(id, '[') ? 1 : 0);
    641 		} else {		/* unset function */
    642 			if (define(id, (struct op *) NULL))
    643 				ret = 1;
    644 		}
    645 	return ret;
    646 }
    647 
    648 int
    649 c_times(wp)
    650 	char **wp;
    651 {
    652 	struct tms all;
    653 
    654 	(void) ksh_times(&all);
    655 	shprintf("Shell: %8s user ", clocktos(all.tms_utime));
    656 	shprintf("%8s system\n", clocktos(all.tms_stime));
    657 	shprintf("Kids:  %8s user ", clocktos(all.tms_cutime));
    658 	shprintf("%8s system\n", clocktos(all.tms_cstime));
    659 
    660 	return 0;
    661 }
    662 
    663 /*
    664  * time pipeline (really a statement, not a built-in command)
    665  */
    666 int
    667 timex(t, f)
    668 	struct op *t;
    669 	int f;
    670 {
    671 	int rv;
    672 	struct tms t0, t1;
    673 	clock_t t0t, t1t;
    674 	extern clock_t j_usrtime, j_systime; /* computed by j_wait */
    675 
    676 	j_usrtime = j_systime = 0;
    677 	t0t = ksh_times(&t0);
    678 	rv = execute(t->left, f);
    679 	t1t = ksh_times(&t1);
    680 
    681 	shf_fprintf(shl_out, "%8s real ", clocktos(t1t - t0t));
    682 	shf_fprintf(shl_out, "%8s user ",
    683 	       clocktos(t1.tms_utime - t0.tms_utime + j_usrtime));
    684 	shf_fprintf(shl_out, "%8s system ",
    685 	       clocktos(t1.tms_stime - t0.tms_stime + j_systime));
    686 	shf_fprintf(shl_out, newline);
    687 
    688 	return rv;
    689 }
    690 
    691 static char *
    692 clocktos(t)
    693 	clock_t t;
    694 {
    695 	static char temp[20];
    696 	register int i;
    697 	register char *cp = temp + sizeof(temp);
    698 
    699 	if (CLK_TCK != 100)	/* convert to 1/100'ths */
    700 	    t = (t < 1000000000/CLK_TCK) ?
    701 		    (t * 100) / CLK_TCK : (t / CLK_TCK) * 100;
    702 
    703 	*--cp = '\0';
    704 	*--cp = 's';
    705 	for (i = -2; i <= 0 || t > 0; i++) {
    706 		if (i == 0)
    707 			*--cp = '.';
    708 		*--cp = '0' + (char)(t%10);
    709 		t /= 10;
    710 	}
    711 	return cp;
    712 }
    713 
    714 /* exec with no args - args case is taken care of in comexec() */
    715 int
    716 c_exec(wp)
    717 	char ** wp;
    718 {
    719 	int i;
    720 
    721 	/* make sure redirects stay in place */
    722 	if (e->savefd != NULL) {
    723 		for (i = 0; i < NUFILE; i++) {
    724 			if (e->savefd[i] > 0)
    725 				close(e->savefd[i]);
    726 			/* keep anything > 2 private */
    727 			if (i > 2 && e->savefd[i])
    728 				fd_clexec(i);
    729 		}
    730 		e->savefd = NULL;
    731 	}
    732 	return 0;
    733 }
    734 
    735 /* dummy function, special case in comexec() */
    736 int
    737 c_builtin(wp)
    738 	char ** wp;
    739 {
    740 	return 0;
    741 }
    742 
    743 extern	int c_test ARGS((char **wp));		/* in c_test.c */
    744 extern	int c_ulimit ARGS((char **wp));		/* in c_ulimit.c */
    745 
    746 /* A leading = means assignments before command are kept;
    747  * a leading * means a POSIX special builtin;
    748  * a leading + means a POSIX regular builtin
    749  * (* and + should not be combined).
    750  */
    751 const struct builtin shbuiltins [] = {
    752 	{"*=.", c_dot},
    753 	{"*=:", c_label},
    754 	{"[", c_test},
    755 	{"*=break", c_brkcont},
    756 	{"=builtin", c_builtin},
    757 	{"*=continue", c_brkcont},
    758 	{"*=eval", c_eval},
    759 	{"*=exec", c_exec},
    760 	{"*=exit", c_exitreturn},
    761 	{"+false", c_label},
    762 	{"*=return", c_exitreturn},
    763 	{"*=set", c_set},
    764 	{"*=shift", c_shift},
    765 	{"=times", c_times},
    766 	{"*=trap", c_trap},
    767 	{"+=wait", c_wait},
    768 	{"+read", c_read},
    769 	{"test", c_test},
    770 	{"+true", c_label},
    771 	{"ulimit", c_ulimit},
    772 	{"+umask", c_umask},
    773 	{"*=unset", c_unset},
    774 #ifdef OS2
    775 	/* In OS2, the first line of a file can be "extproc name", which
    776 	 * tells the command interpreter (cmd.exe) to use name to execute
    777 	 * the file.  For this to be useful, ksh must ignore commands
    778 	 * starting with extproc and this does the trick...
    779 	 */
    780 	{"extproc", c_label},
    781 #endif /* OS2 */
    782 	{NULL, NULL}
    783 };
    784