Home | History | Annotate | Line # | Download | only in ksh
c_sh.c revision 1.1.1.2
      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 (isatty(fd)) {
    290 			/* at&t ksh says it prints prompt on fd if it's open
    291 			 * for writing and is a tty, but it doesn't do it
    292 			 * (it also doesn't check the interactive flag,
    293 			 * as is indicated in the Kornshell book).
    294 			 */
    295 			shellf("%s", cp+1);
    296 		}
    297 	}
    298 
    299 #ifdef KSH
    300 	/* If we are reading from the co-process for the first time,
    301 	 * make sure the other side of the pipe is closed first.  This allows
    302 	 * the detection of eof.
    303 	 *
    304 	 * This is not compatiable with at&t ksh... the fd is kept so another
    305 	 * coproc can be started with same ouput, however, this means eof
    306 	 * can't be detected...  This is why it is closed here.
    307 	 * If this call is removed, remove the eof check below, too.
    308 	* coproc_readw_close(fd);
    309 	 */
    310 #endif /* KSH */
    311 
    312 	if (history)
    313 		Xinit(xs, xp, 128, ATEMP);
    314 	expanding = 0;
    315 	Xinit(cs, cp, 128, ATEMP);
    316 	for (; *wp != NULL; wp++) {
    317 		for (cp = Xstring(cs, cp); ; ) {
    318 			if (c == '\n' || c == EOF)
    319 				break;
    320 			while (1) {
    321 				c = shf_getc(shf);
    322 				if (c == '\0'
    323 #ifdef OS2
    324 				    || c == '\r'
    325 #endif /* OS2 */
    326 				    )
    327 					continue;
    328 				if (c == EOF && shf_error(shf)
    329 				    && shf_errno(shf) == EINTR)
    330 				{
    331 					/* Was the offending signal one that
    332 					 * would normally kill a process?
    333 					 * If so, pretend the read was killed.
    334 					 */
    335 					ecode = fatal_trap_check();
    336 
    337 					/* non fatal (eg, CHLD), carry on */
    338 					if (!ecode) {
    339 						shf_clearerr(shf);
    340 						continue;
    341 					}
    342 				}
    343 				break;
    344 			}
    345 			if (history) {
    346 				Xcheck(xs, xp);
    347 				Xput(xs, xp, c);
    348 			}
    349 			Xcheck(cs, cp);
    350 			if (expanding) {
    351 				expanding = 0;
    352 				if (c == '\n') {
    353 					c = 0;
    354 					if (Flag(FTALKING) && isatty(fd)) {
    355 						/* set prompt in case this is
    356 						 * called from .profile or $ENV
    357 						 */
    358 						set_prompt(PS2, (Source *) 0);
    359 						pprompt(prompt, 0);
    360 					}
    361 				} else if (c != EOF)
    362 					Xput(cs, cp, c);
    363 				continue;
    364 			}
    365 			if (expand && c == '\\') {
    366 				expanding = 1;
    367 				continue;
    368 			}
    369 			if (c == '\n' || c == EOF)
    370 				break;
    371 			if (ctype(c, C_IFS)) {
    372 				if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS))
    373 					continue;
    374 				if (wp[1])
    375 					break;
    376 			}
    377 			Xput(cs, cp, c);
    378 		}
    379 		/* strip trailing IFS white space from last variable */
    380 		if (!wp[1])
    381 			while (Xlength(cs, cp) && ctype(cp[-1], C_IFS)
    382 			       && ctype(cp[-1], C_IFSWS))
    383 				cp--;
    384 		Xput(cs, cp, '\0');
    385 		vp = global(*wp);
    386 		if (vp->flag & RDONLY) {
    387 			shf_flush(shf);
    388 			bi_errorf("%s is read only", *wp);
    389 			return 1;
    390 		}
    391 		if (Flag(FEXPORT))
    392 			typeset(*wp, EXPORT, 0, 0, 0);
    393 		setstr(vp, Xstring(cs, cp));
    394 	}
    395 
    396 	shf_flush(shf);
    397 	if (history) {
    398 		Xput(xs, xp, '\0');
    399 		source->line++;
    400 		histsave(source->line, Xstring(xs, xp), 1);
    401 		Xfree(xs, xp);
    402 	}
    403 #ifdef KSH
    404 	/* if this is the co-process fd, close the file descriptor
    405 	 * (can get eof if and only if all processes are have died, ie,
    406 	 * coproc.njobs is 0 and the pipe is closed).
    407 	 */
    408 	if (c == EOF && !ecode)
    409 		coproc_read_close(fd);
    410 #endif /* KSH */
    411 
    412 	return ecode ? ecode : c == EOF;
    413 }
    414 
    415 int
    416 c_eval(wp)
    417 	char **wp;
    418 {
    419 	register struct source *s;
    420 
    421 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    422 		return 1;
    423 	s = pushs(SWORDS, ATEMP);
    424 	s->u.strv = wp + builtin_opt.optind;
    425 	return shell(s, FALSE);
    426 }
    427 
    428 int
    429 c_trap(wp)
    430 	char **wp;
    431 {
    432 	int i;
    433 	char *s;
    434 	register Trap *p;
    435 
    436 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    437 		return 1;
    438 	wp += builtin_opt.optind;
    439 
    440 	if (*wp == NULL) {
    441 		int anydfl = 0;
    442 
    443 		for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) {
    444 			if (p->trap == NULL)
    445 				anydfl = 1;
    446 			else {
    447 				shprintf("trap -- ");
    448 				print_value_quoted(p->trap);
    449 				shprintf(" %s\n", p->name);
    450 			}
    451 		}
    452 #if 0 /* this is ugly and not clear POSIX needs it */
    453 		/* POSIX may need this so output of trap can be saved and
    454 		 * used to restore trap conditions
    455 		 */
    456 		if (anydfl) {
    457 			shprintf("trap -- -");
    458 			for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
    459 				if (p->trap == NULL && p->name)
    460 					shprintf(" %s", p->name);
    461 			shprintf(newline);
    462 		}
    463 #endif
    464 		return 0;
    465 	}
    466 
    467 	s = (gettrap(*wp) == NULL) ? *wp++ : NULL; /* get command */
    468 	if (s != NULL && s[0] == '-' && s[1] == '\0')
    469 		s = NULL;
    470 
    471 	/* set/clear traps */
    472 	while (*wp != NULL) {
    473 		p = gettrap(*wp++);
    474 		if (p == NULL) {
    475 			bi_errorf("bad signal %s", wp[-1]);
    476 			return 1;
    477 		}
    478 		settrap(p, s);
    479 	}
    480 	return 0;
    481 }
    482 
    483 int
    484 c_exitreturn(wp)
    485 	char **wp;
    486 {
    487 	int how = LEXIT;
    488 	char *arg;
    489 
    490 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    491 		return 1;
    492 	arg = wp[builtin_opt.optind];
    493 
    494 	if (arg != NULL && !getn(arg, &exstat)) {
    495 		exstat = 1;
    496 		warningf(TRUE, "%s: bad number", arg);
    497 	}
    498 	if (wp[0][0] == 'r') { /* return */
    499 		struct env *ep;
    500 
    501 		/* need to tell if this is exit or return so trap exit will
    502 		 * work right (POSIX)
    503 		 */
    504 		for (ep = e; ep; ep = ep->oenv)
    505 			if (STOP_RETURN(ep->type)) {
    506 				how = LRETURN;
    507 				break;
    508 			}
    509 	}
    510 
    511 	if (how == LEXIT && !really_exit && j_stopped_running()) {
    512 		really_exit = 1;
    513 		how = LSHELL;
    514 	}
    515 
    516 	quitenv();	/* get rid of any i/o redirections */
    517 	unwind(how);
    518 	/*NOTREACHED*/
    519 	return 0;
    520 }
    521 
    522 int
    523 c_brkcont(wp)
    524 	char **wp;
    525 {
    526 	int n, quit;
    527 	struct env *ep, *last_ep = (struct env *) 0;
    528 	char *arg;
    529 
    530 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
    531 		return 1;
    532 	arg = wp[builtin_opt.optind];
    533 
    534 	if (!arg)
    535 		n = 1;
    536 	else if (!bi_getn(arg, &n))
    537 		return 1;
    538 	quit = n;
    539 	if (quit <= 0) {
    540 		/* at&t ksh does this for non-interactive shells only - weird */
    541 		bi_errorf("%s: bad value", arg);
    542 		return 1;
    543 	}
    544 
    545 	/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
    546 	for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
    547 		if (ep->type == E_LOOP) {
    548 			if (--quit == 0)
    549 				break;
    550 			ep->flags |= EF_BRKCONT_PASS;
    551 			last_ep = ep;
    552 		}
    553 
    554 	if (quit) {
    555 		/* at&t ksh doesn't print a message - just does what it
    556 		 * can.  We print a message 'cause it helps in debugging
    557 		 * scripts, but don't generate an error (ie, keep going).
    558 		 */
    559 		if (n == quit) {
    560 			warningf(TRUE, "%s: cannot %s", wp[0], wp[0]);
    561 			return 0;
    562 		}
    563 		/* POSIX says if n is too big, the last enclosing loop
    564 		 * shall be used.  Doesn't say to print an error but we
    565 		 * do anyway 'cause the user messed up.
    566 		 */
    567 		last_ep->flags &= ~EF_BRKCONT_PASS;
    568 		warningf(TRUE, "%s: can only %s %d level(s)",
    569 			wp[0], wp[0], n - quit);
    570 	}
    571 
    572 	unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
    573 	/*NOTREACHED*/
    574 }
    575 
    576 int
    577 c_set(wp)
    578 	char **wp;
    579 {
    580 	int argi, setargs;
    581 	struct block *l = e->loc;
    582 	register char **owp = wp;
    583 
    584 	if (wp[1] == NULL) {
    585 		static const char *const args [] = { "set", "-", NULL };
    586 		return c_typeset((char **) args);
    587 	}
    588 
    589 	argi = parse_args(wp, OF_SET, &setargs);
    590 	if (argi < 0)
    591 		return 1;
    592 	/* set $# and $* */
    593 	if (setargs) {
    594 		owp = wp += argi - 1;
    595 		wp[0] = l->argv[0]; /* save $0 */
    596 		while (*++wp != NULL)
    597 			*wp = str_save(*wp, &l->area);
    598 		l->argc = wp - owp - 1;
    599 		l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
    600 		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
    601 			;
    602 	}
    603 	/* POSIX says set exit status is 0, but old scripts that use
    604 	 * getopt(1), use the construct: set -- `getopt ab:c "$@"`
    605 	 * which assumes the exit value set will be that of the ``
    606 	 * (subst_exstat is cleared in execute() so that it will be 0
    607 	 * if there are no command substitutions).
    608 	 */
    609 	return Flag(FPOSIX) ? 0 : subst_exstat;
    610 }
    611 
    612 int
    613 c_unset(wp)
    614 	char **wp;
    615 {
    616 	register char *id;
    617 	int optc, unset_var = 1;
    618 	int ret = 0;
    619 
    620 	while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF)
    621 		switch (optc) {
    622 		  case 'f':
    623 			unset_var = 0;
    624 			break;
    625 		  case 'v':
    626 			unset_var = 1;
    627 			break;
    628 		  case '?':
    629 			return 1;
    630 		}
    631 	wp += builtin_opt.optind;
    632 	for (; (id = *wp) != NULL; wp++)
    633 		if (unset_var) {	/* unset variable */
    634 			struct tbl *vp = global(id);
    635 
    636 			if (!(vp->flag & ISSET))
    637 			    ret = 1;
    638 			if ((vp->flag&RDONLY)) {
    639 				bi_errorf("%s is read only", vp->name);
    640 				return 1;
    641 			}
    642 			unset(vp, strchr(id, '[') ? 1 : 0);
    643 		} else {		/* unset function */
    644 			if (define(id, (struct op *) NULL))
    645 				ret = 1;
    646 		}
    647 	return ret;
    648 }
    649 
    650 int
    651 c_times(wp)
    652 	char **wp;
    653 {
    654 	struct tms all;
    655 
    656 	(void) ksh_times(&all);
    657 	shprintf("Shell: %8s user ", clocktos(all.tms_utime));
    658 	shprintf("%8s system\n", clocktos(all.tms_stime));
    659 	shprintf("Kids:  %8s user ", clocktos(all.tms_cutime));
    660 	shprintf("%8s system\n", clocktos(all.tms_cstime));
    661 
    662 	return 0;
    663 }
    664 
    665 /*
    666  * time pipeline (really a statement, not a built-in command)
    667  */
    668 int
    669 timex(t, f)
    670 	struct op *t;
    671 	int f;
    672 {
    673 	int rv;
    674 	struct tms t0, t1;
    675 	clock_t t0t, t1t;
    676 	extern clock_t j_usrtime, j_systime; /* computed by j_wait */
    677 
    678 	j_usrtime = j_systime = 0;
    679 	t0t = ksh_times(&t0);
    680 	rv = execute(t->left, f);
    681 	t1t = ksh_times(&t1);
    682 
    683 	shf_fprintf(shl_out, "%8s real ", clocktos(t1t - t0t));
    684 	shf_fprintf(shl_out, "%8s user ",
    685 	       clocktos(t1.tms_utime - t0.tms_utime + j_usrtime));
    686 	shf_fprintf(shl_out, "%8s system ",
    687 	       clocktos(t1.tms_stime - t0.tms_stime + j_systime));
    688 	shf_fprintf(shl_out, newline);
    689 
    690 	return rv;
    691 }
    692 
    693 static char *
    694 clocktos(t)
    695 	clock_t t;
    696 {
    697 	static char temp[20];
    698 	register int i;
    699 	register char *cp = temp + sizeof(temp);
    700 
    701 	if (CLK_TCK != 100)	/* convert to 1/100'ths */
    702 	    t = (t < 1000000000/CLK_TCK) ?
    703 		    (t * 100) / CLK_TCK : (t / CLK_TCK) * 100;
    704 
    705 	*--cp = '\0';
    706 	*--cp = 's';
    707 	for (i = -2; i <= 0 || t > 0; i++) {
    708 		if (i == 0)
    709 			*--cp = '.';
    710 		*--cp = '0' + (char)(t%10);
    711 		t /= 10;
    712 	}
    713 	return cp;
    714 }
    715 
    716 /* exec with no args - args case is taken care of in comexec() */
    717 int
    718 c_exec(wp)
    719 	char ** wp;
    720 {
    721 	int i;
    722 
    723 	/* make sure redirects stay in place */
    724 	if (e->savefd != NULL) {
    725 		for (i = 0; i < NUFILE; i++) {
    726 			if (e->savefd[i] > 0)
    727 				close(e->savefd[i]);
    728 			/* keep anything > 2 private */
    729 			if (i > 2 && e->savefd[i])
    730 				fd_clexec(i);
    731 		}
    732 		e->savefd = NULL;
    733 	}
    734 	return 0;
    735 }
    736 
    737 /* dummy function, special case in comexec() */
    738 int
    739 c_builtin(wp)
    740 	char ** wp;
    741 {
    742 	return 0;
    743 }
    744 
    745 extern	int c_test ARGS((char **wp));		/* in c_test.c */
    746 extern	int c_ulimit ARGS((char **wp));		/* in c_ulimit.c */
    747 
    748 /* A leading = means assignments before command are kept;
    749  * a leading * means a POSIX special builtin;
    750  * a leading + means a POSIX regular builtin
    751  * (* and + should not be combined).
    752  */
    753 const struct builtin shbuiltins [] = {
    754 	{"*=.", c_dot},
    755 	{"*=:", c_label},
    756 	{"[", c_test},
    757 	{"*=break", c_brkcont},
    758 	{"=builtin", c_builtin},
    759 	{"*=continue", c_brkcont},
    760 	{"*=eval", c_eval},
    761 	{"*=exec", c_exec},
    762 	{"*=exit", c_exitreturn},
    763 	{"+false", c_label},
    764 	{"*=return", c_exitreturn},
    765 	{"*=set", c_set},
    766 	{"*=shift", c_shift},
    767 	{"=times", c_times},
    768 	{"*=trap", c_trap},
    769 	{"+=wait", c_wait},
    770 	{"+read", c_read},
    771 	{"test", c_test},
    772 	{"+true", c_label},
    773 	{"ulimit", c_ulimit},
    774 	{"+umask", c_umask},
    775 	{"*=unset", c_unset},
    776 #ifdef OS2
    777 	/* In OS2, the first line of a file can be "extproc name", which
    778 	 * tells the command interpreter (cmd.exe) to use name to execute
    779 	 * the file.  For this to be useful, ksh must ignore commands
    780 	 * starting with extproc and this does the trick...
    781 	 */
    782 	{"extproc", c_label},
    783 #endif /* OS2 */
    784 	{NULL, NULL}
    785 };
    786