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