2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley Software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
18 #include <locale.h> /* For LC_ALL */
19 #include "sh.tconst.h"
20 #include <sys/types.h>
24 * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0. In
25 * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new. Beware of consusing
26 * the keywords that the command prints for these two. The old one was
27 * "memoryuse" and the new one is "memorysize". Note also that a given limit
28 * doesn't necessarily appear in the same position in the two releases.
36 RLIMIT_CPU
, S_cputime
, /* "cputime" */
37 1, S_seconds
, /* "seconds" */
38 RLIMIT_FSIZE
, S_filesize
, /* "filesize" */
39 1024, S_kbytes
, /* "kbytes" */
40 RLIMIT_DATA
, S_datasize
, /* "datasize" */
41 1024, S_kbytes
, /* "kbytes" */
42 RLIMIT_STACK
, S_stacksize
, /* "stacksize" */
43 1024, S_kbytes
, /* "kbytes" */
44 RLIMIT_CORE
, S_coredumpsize
, /* "coredumpsize" */
45 1024, S_kbytes
, /* "kbytes" */
46 RLIMIT_NOFILE
, S_descriptors
, /* "descriptors" */
48 RLIMIT_VMEM
, S_memorysize
, /* "memorysize" */
49 1024, S_kbytes
, /* "kbytes" */
54 static int getval(struct limits
*lp
, tchar
**v
, rlim_t
*);
57 void reexecute(struct command
*kp
);
62 void echo(tchar sep
, tchar
**v
);
63 void local_setenv(tchar
*name
, tchar
*val
);
64 void local_unsetenv(tchar
*name
);
65 void limtail(tchar
*cp
, tchar
*str0
);
66 void plim(struct limits
*lp
, tchar hard
);
76 isbfunc(struct command
*t
)
78 tchar
*cp
= t
->t_dcom
[0];
79 struct biltins
*bp
, *bp1
, *bp2
;
82 static struct biltins label
= { S_
, dolabel
, 0, 0 };
83 static struct biltins foregnd
= { S_Pjob
, dofg1
, 0, 0 };
84 static struct biltins backgnd
= { S_PjobAND
, dobg1
, 0, 0 };
86 tprintf("TRACE- isbfunc()\n");
88 if (lastchr(cp
) == ':') {
93 if (t
->t_dflg
& FAND
) {
103 * Bp1 is the beginning of the current search range.
104 * Bp2 is one past the end.
106 for (bp1
= bfunc
, bp2
= bfunc
+ nbfunc
; bp1
< bp2
; ) {
109 bp
= bp1
+ (bp2
- bp1
>> 1);
110 if ((i
= *cp
- *bp
->bname
) == 0 &&
111 (i
= strcmp_(cp
, bp
->bname
)) == 0) {
124 func(struct command
*t
, struct biltins
*bp
)
129 tprintf("TRACE- func()\n");
133 i
= blklen(t
->t_dcom
) - 1;
134 if (i
< bp
->minargs
) {
135 bferr("Too few arguments");
137 if (i
> bp
->maxargs
) {
138 bferr("Too many arguments");
140 (*bp
->bfunct
)(t
->t_dcom
, t
);
147 tprintf("TRACE- dolabel()\n");
159 tprintf("TRACE- doonintr()\n");
161 if (parintr
== SIG_IGN
) {
164 if (setintr
&& intty
) {
165 bferr("Can't from terminal");
167 cp
= gointr
, gointr
= 0, xfree(cp
);
170 (void) sigblock(sigmask(SIGINT
));
172 (void) signal(SIGINT
, SIG_DFL
);
175 } else if (eq((vv
= strip(vv
)), S_MINUS
)) {
176 (void) signal(SIGINT
, SIG_IGN
);
179 gointr
= savestr(vv
);
180 (void) signal(SIGINT
, pintr
);
189 tprintf("TRACE- donohup()\n");
192 bferr("Can't from terminal");
195 (void) signal(SIGHUP
, SIG_IGN
);
212 tprintf("TRACE- prvars()\n");
225 tprintf("TRACE- doalias()\n");
231 } else if (*v
== 0) {
232 vp
= adrof1(strip(p
), &aliases
);
234 blkpr(vp
->vec
), printf("\n");
237 if (eq(p
, S_alias
) ||
240 bferr("Too dangerous to alias that");
242 set1(strip(p
), saveblk(v
), &aliases
);
251 tprintf("TRACE- unalias()\n");
261 tprintf("TRACE- dologout()\n");
273 tprintf("TRACE- dologin()\n");
277 (void) signal(SIGTERM
, parterm
);
279 v_
= tstostr(NULL
, v
[1]); /* No need to free */
283 execl("/bin/login", "login", v_
, 0);
295 tprintf("TRACE- donewgrp()\n");
297 if (chkstop
== 0 && setintr
) {
300 (void) signal(SIGTERM
, parterm
);
303 v_
= tstostr(NOSTR
, v
[1]); /* No need to free */
307 execl("/bin/newgrp", "newgrp", v_
, 0);
308 execl("/usr/bin/newgrp", "newgrp", v_
, 0);
319 tprintf("TRACE- islogin()\n");
321 if (chkstop
== 0 && setintr
) {
327 error("Not login shell");
331 doif(tchar
**v
, struct command
*kp
)
337 tprintf("TRACE- doif()\n");
345 if (eq(*vv
, S_then
)) {
347 bferr("Improper then");
351 * If expression was zero, then scan to else,
352 * otherwise just fall into following code.
360 * Simple command attached to this if.
361 * Left shift the node in this tree, munging it
362 * so we can reexecute it.
365 lshift(kp
->t_dcom
, vv
- kp
->t_dcom
);
372 * Reexecute a command, being careful not
373 * to redo i/o redirection, which is already set up.
376 reexecute(struct command
*kp
)
380 tprintf("TRACE- reexecute()\n");
385 * If tty is still ours to arbitrate, arbitrate it;
386 * otherwise dont even set pgrp's as the jobs would
387 * then have no way to get the tty (we can't give it
388 * to them, and our parent wouldn't know their pgrp, etc.
390 execute(kp
, tpgrp
> 0 ? tpgrp
: -1);
398 tprintf("TRACE- doelse()\n");
409 tprintf("TRACE- dogoto()\n");
413 * While we still can, locate any unknown ends of existing loops.
414 * This obscure code is the WORST result of the fact that we
415 * don't really parse.
417 for (wp
= whyles
; wp
; wp
= wp
->w_next
) {
418 if (wp
->w_end
== 0) {
425 search(ZGOTO
, 0, lp
= globone(v
[1]));
428 * Eliminate loops which were exited.
439 tprintf("TRACE- doswitch()\n");
442 if (!*v
|| *(*v
++) != '(') {
445 cp
= **v
== ')' ? S_
: *v
++;
446 if (*(*v
++) != ')') {
451 error("Syntax error");
453 search(ZSWITCH
, 0, lp
= globone(cp
));
462 tprintf("TRACE- dobreak()\n");
467 bferr("Not in while/foreach");
476 tprintf("TRACE- doexit()\n");
482 * Don't DEMAND parentheses here either.
486 set(S_status
, putn(exp(&v
)));
488 bferr("Expression syntax");
505 tprintf("TRACE- doforeach()\n");
509 while (*cp
&& alnum(*cp
)) {
512 if (*cp
|| strlen_(*v
) >= MAX_VAR_LEN
|| !letter(**v
)) {
513 bferr("Invalid variable");
516 if (v
[0][0] != '(' || v
[blklen(v
) - 1][0] != ')') {
517 bferr("Words not ()'ed");
525 nwp
= (struct whyle
*)xcalloc(1, sizeof (*nwp
));
526 nwp
->w_fe
= nwp
->w_fe0
= v
; gargv
= 0;
527 nwp
->w_start
= btell();
528 nwp
->w_fename
= savestr(cp
);
529 nwp
->w_next
= whyles
;
532 * Pre-read the loop so as to be more
533 * comprehensible to a terminal user.
545 bool again
= whyles
!= 0 && whyles
->w_start
== lineloc
&&
546 whyles
->w_fename
== 0;
549 tprintf("TRACE- dowhile()\n");
553 * Implement prereading here also, taking care not to
554 * evaluate the expression before the loop has been read up
557 if (intty
&& !again
) {
558 status
= !exp0(&v
, 1);
563 bferr("Expression syntax");
566 struct whyle
*nwp
= (struct whyle
*)xcalloc(1, sizeof (*nwp
));
568 nwp
->w_start
= lineloc
;
570 nwp
->w_next
= whyles
;
582 /* We ain't gonna loop no more, no more! */
591 tprintf("TRACE- preread()\n");
596 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT
));
600 (void) sigblock(sigmask(SIGINT
));
602 whyles
->w_end
= btell();
610 tprintf("TRACE- doend()\n");
613 bferr("Not in while/foreach");
615 whyles
->w_end
= btell();
623 tprintf("TRACE- docontin()\n");
627 bferr("Not in while/foreach");
637 tprintf("TRACE- doagain()\n");
639 /* Repeating a while is simple */
640 if (whyles
->w_fename
== 0) {
641 bseek(whyles
->w_start
);
645 * The foreach variable list actually has a spurious word
646 * ")" at the end of the w_fe list. Thus we are at the
647 * of the list if one word beyond this is 0.
649 if (!whyles
->w_fe
[1]) {
653 set(whyles
->w_fename
, savestr(*whyles
->w_fe
++));
654 bseek(whyles
->w_start
);
658 dorepeat(tchar
**v
, struct command
*kp
)
663 tprintf("TRACE- dorepeat()\n");
667 omask
= sigblock(sigmask(SIGINT
)) & ~sigmask(SIGINT
);
672 (void) sigsetmask(omask
);
679 (void) sigsetmask(omask
);
688 tprintf("TRACE- doswbrk()\n");
696 struct srch
*sp
, *sp1
, *sp2
;
700 tprintf("TRACE- srchx()\n");
704 * Sp1 is the beginning of the current search range.
705 * Sp2 is one past the end.
707 for (sp1
= srchn
, sp2
= srchn
+ nsrchn
; sp1
< sp2
; ) {
708 sp
= sp1
+ (sp2
- sp1
>> 1);
709 if ((i
= *cp
- *sp
->s_name
) == 0 &&
710 (i
= strcmp_(cp
, sp
->s_name
)) == 0) {
711 return (sp
->s_value
);
727 search(type
, level
, goal
)
728 int type
; int level
; tchar
*goal
;
730 tchar wordbuf
[BUFSIZ
];
731 tchar
*aword
= wordbuf
;
735 tprintf("TRACE- search()\n");
737 Stype
= type
; Sgoal
= goal
;
742 if (intty
&& fseekp
== feobp
) {
743 printf("? "), flush();
746 (void) getword(aword
);
748 switch (srchx(aword
)) {
751 if (level
== 0 && type
== ZIF
) {
757 while (getword(aword
)) {
760 if ((type
== ZIF
|| type
== ZELSE
) &&
767 if (type
== ZIF
|| type
== ZELSE
) {
774 if (type
== ZBREAK
) {
780 if (type
== ZBREAK
) {
786 if (type
== ZSWITCH
|| type
== ZBRKSW
) {
792 if (type
== ZSWITCH
|| type
== ZBRKSW
) {
798 if (type
== ZGOTO
&& getword(aword
) &&
805 if (type
!= ZGOTO
&& (type
!= ZSWITCH
|| level
!= 0)) {
808 if (lastchr(aword
) != ':') {
811 aword
[strlen_(aword
) - 1] = 0;
812 if (type
== ZGOTO
&& eq(aword
, goal
) ||
813 type
== ZSWITCH
&& eq(aword
, S_default
)) {
819 if (type
!= ZSWITCH
|| level
!= 0) {
822 (void) getword(aword
);
823 if (lastchr(aword
) == ':') {
824 aword
[strlen_(aword
) - 1] = 0;
826 cp
= strip(Dfix1(aword
));
827 if (Gmatch(goal
, cp
)) {
834 if (type
== ZSWITCH
&& level
== 0) {
839 (void) getword(NOSTR
);
840 } while (level
>= 0);
849 tprintf("TRACE- getword()\n");
861 } while (c
>= 0 && c
!= '\n');
873 /* ( and ) form separate words */
874 if (c
== '(' || c
== ')') {
882 if (c
== '\\' && (c
= readc(1)) == '\n') {
885 if (c
== '\'' || c
== '"') {
898 } while ((d
|| !issp(c
) && c
!= '(' && c
!= ')') && c
!= '\n');
910 bferr("then/endif not found");
913 bferr("endif not found");
917 bferr("endsw not found");
920 bferr("end not found");
924 bferr("label not found");
936 tprintf("TRACE- toend()\n");
938 if (whyles
->w_end
== 0) {
940 whyles
->w_end
= btell() - 1;
942 bseek(whyles
->w_end
);
953 tprintf("TRACE- wfree()\n");
956 struct whyle
*wp
= whyles
;
957 struct whyle
*nwp
= wp
->w_next
;
959 if (o
>= wp
->w_start
&& (wp
->w_end
== 0 || o
< wp
->w_end
)) {
978 tprintf("TRACE- doecho()\n");
988 tprintf("TRACE- doglob()\n");
995 echo(tchar sep
, tchar
**v
)
1001 tprintf("TRACE- echo()\n");
1004 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT
));
1009 * echo command needs to have newline when there are no
1010 * flags or arguments. glob should have no newline. If
1011 * the separator is a blank, we are doing an echo. If the
1012 * separator is zero, we are globbing.
1014 if (sep
== (tchar
)' ')
1018 gflag
= 0, tglob(v
);
1025 /* check for -n arg, NOTE: it might be quoted */
1026 if (sep
== ' ' && *v
&& strlen_(*v
) == 2 &&
1027 ((**v
&TRIM
) == '-' && (*(*v
+ 1) & TRIM
) == 'n' &&
1028 (*(*v
+2)&TRIM
) == 0)) {
1038 Putchar(sep
| QUOTE
);
1041 if (sep
&& nonl
== 0) {
1047 (void) sigblock(sigmask(SIGINT
));
1050 blkfree(gargv
), gargv
= 0;
1054 extern char **environ
;
1057 * Check if the environment variable vp affects this csh's behavior
1058 * and therefore we should call setlocale() or not.
1059 * This function has two side effects when it returns 1:
1060 * variable islocalevar_catnum is set to the LC_xxx value.
1061 * variable islocalevar_catname is set to the string "LC_xxx"
1063 static int islocalevar_catnum
;
1064 static char *islocalevar_catname
;
1068 islocalevar(tchar
*vp
)
1070 static struct lcinfo
{
1071 tchar
* evname
; /* The name of the env. var. */
1072 } categories_we_care
[] = {
1073 S_LANG
, S_LC_ALL
, S_LC_CTYPE
, S_LC_MESSAGES
,
1074 NOSTR
/* assumption: LC_xxx >= 0 */
1076 struct lcinfo
*p
= categories_we_care
;
1079 if (strcmp_(vp
, p
->evname
) == 0) {
1082 } while (((++p
)->evname
) != NOSTR
);
1092 tprintf("TRACE- dosetenv()\n");
1095 if ((vp
= *v
++) == 0) {
1099 (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT
));
1101 for (ep
= environ
; *ep
; ep
++) {
1102 printf("%s\n", *ep
);
1107 if ((lp
= *v
++) == 0) {
1110 local_setenv(vp
, lp
= globone(lp
));
1111 if (eq(vp
, S_PATH
)) {
1114 } else if (islocalevar(vp
)) {
1115 if (!setlocale(LC_ALL
, "")) {
1116 error("Locale could not be set properly");
1124 dounsetenv(tchar
**v
)
1127 tprintf("TRACE- dounsetenv()\n");
1132 if (islocalevar(*v
++)) {
1133 setlocale(LC_ALL
, ""); /* Hope no error! */
1139 local_setenv(tchar
*name
, tchar
*val
)
1141 char **ep
= environ
;
1144 tchar
*ep_
; /* temporary */
1145 char *blk
[2], **oep
= ep
;
1148 /* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */
1149 /* printf("IN local_setenv args = (%t)\n", val); */
1153 for (cp
= name
, dp
= *ep
; *cp
&& *dp
; cp
++) {
1155 * This loop compares two chars in different
1156 * representations, EUC (as char *) and wchar_t
1157 * (in tchar), and ends when they are different.
1162 n
= mbtowc(&dwc
, dp
, MB_CUR_MAX
);
1164 break; /* Illegal multibyte. */
1166 dp
+= n
; /* Advance to next multibyte char. */
1167 if (dwc
== (wchar_t)(*cp
& TRIM
)) {
1174 for (cp
= name
, dp
= *ep
; *cp
&& (char)*cp
== *dp
; cp
++, dp
++) {
1177 #endif /* !MBCHAR */
1178 if (*cp
!= 0 || *dp
!= '=') {
1181 cp
= strspl(S_EQ
, val
);
1183 ep_
= strspl(name
, cp
); /* ep_ is xalloc'ed */
1186 * Trimming is not needed here.
1189 *ep
= tstostr(NULL
, ep_
);
1190 xfree(ep_
); /* because temp. use */
1193 ep_
= strspl(name
, S_EQ
); /* ep_ is xalloc'ed */
1194 blk
[0] = tstostr(NULL
, ep_
);
1197 environ
= (char **)blkspl_((char **)environ
, blk
);
1199 local_setenv(name
, val
);
1203 local_unsetenv(tchar
*name
)
1205 char **ep
= environ
;
1209 char *cp_
; /* tmp use */
1210 static int cnt
= 0; /* delete counter */
1213 tprintf("TRACE- local_unsetenv()\n");
1217 for (cp
= name
, dp
= *ep
; *cp
&& *dp
; cp
++) {
1219 * This loop compares two chars in different
1220 * representations, EUC (as char *) and wchar_t
1221 * (in tchar), and ends when they are different.
1226 n
= mbtowc(&dwc
, dp
, MB_CUR_MAX
);
1228 break; /* Illegal multibyte. */
1230 dp
+= n
; /* Advance to next multibyte char. */
1231 if (dwc
== (wchar_t)(*cp
& TRIM
)) {
1238 for (cp
= name
, dp
= *ep
; *cp
&& (char)*cp
== *dp
; cp
++, dp
++) {
1241 #endif /* !MBCHAR */
1242 if (*cp
!= 0 || *dp
!= '=') {
1247 environ
= (char **)blkspl_((char **)environ
, ep
+1);
1262 tprintf("TRACE- dounmask()\n");
1271 while (digit(*cp
) && *cp
!= '8' && *cp
!= '9') {
1272 i
= i
* 8 + *cp
++ - '0';
1274 if (*cp
|| i
< 0 || i
> 0777) {
1275 bferr("Improper mask");
1284 struct limits
*lp
, *res
;
1287 tprintf("TRACE- findlim()\n");
1290 for (lp
= limits
; lp
->limconst
>= 0; lp
++) {
1291 if (prefix(cp
, lp
->limname
)) {
1301 bferr("No such limit");
1313 tprintf("TRACE- dolimit()\n");
1316 if (*v
&& eq(*v
, S_h
)) {
1321 for (lp
= limits
; lp
->limconst
>= 0; lp
++) {
1331 switch (getval(lp
, v
+1, &limit
)) {
1333 error("Value specified for limit is too large");
1336 error("Numeric conversion failed");
1339 if (setlim(lp
, hard
, limit
) < 0) {
1346 getval(struct limits
*lp
, tchar
**v
, rlim_t
*retval
)
1348 rlim_t value
, tmp
, tmp2
;
1350 char chbuf
[BUFSIZ
* MB_LEN_MAX
];
1353 tprintf("TRACE- getval()\n");
1358 value
= strtoull(chbuf
, NULL
, 0);
1360 * we must accept zero, but the conversion can fail and give us
1361 * zero as well...try to deal with it as gracefully as possible
1362 * by checking for EINVAL
1364 if (value
== 0 && errno
== EINVAL
)
1367 while (digit(*cp
) || *cp
== '.' || *cp
== 'e' || *cp
== 'E') {
1372 tmp
= value
* (rlim_t
)lp
->limdiv
;
1373 /* Check for overflow */
1386 if (lp
->limconst
!= RLIMIT_CPU
) {
1389 tstostr(chbuf
, cp
+ 1);
1390 tmp
= strtoull(chbuf
, NULL
, 0);
1391 tmp2
= value
* 60 + tmp
;
1392 if (tmp2
>= value
) {
1400 if (lp
->limconst
!= RLIMIT_CPU
) {
1403 limtail(cp
, S_hours
);
1412 if (lp
->limconst
== RLIMIT_CPU
) {
1413 limtail(cp
, S_minutes
);
1422 if (lp
->limconst
== RLIMIT_CPU
) {
1426 limtail(cp
, S_megabytes
);
1427 tmp
= value
* 1024 * 1024;
1435 if (lp
->limconst
!= RLIMIT_CPU
) {
1438 limtail(cp
, S_seconds
);
1442 if (lp
->limconst
== RLIMIT_CPU
) {
1445 limtail(cp
, S_kbytes
);
1454 limtail(cp
, S_unlimited
);
1455 *retval
= RLIM_INFINITY
;
1460 bferr("Improper or unknown scale factor");
1467 limtail(tchar
*cp
, tchar
*str0
)
1471 tprintf("TRACE- limtail()\n");
1474 while (*cp
&& *cp
== *str
) {
1478 error("Bad scaling; did you mean ``%t''?", str0
);
1483 plim(struct limits
*lp
, tchar hard
)
1491 tprintf("TRACE- plim()\n");
1493 printf("%t \t", lp
->limname
);
1494 (void) getrlimit(lp
->limconst
, &rlim
);
1495 limit
= hard
? rlim
.rlim_max
: rlim
.rlim_cur
;
1496 if (limit
== RLIM_INFINITY
) {
1497 printf("unlimited");
1498 } else if (lp
->limconst
== RLIMIT_CPU
) {
1501 buf
[BUFSZ
- 1] = '\0';
1502 pbuf
= ulltostr((limit
/ lp
->limdiv
), &buf
[BUFSZ
- 1]);
1503 printf("%s %t", pbuf
, lp
->limscale
);
1509 dounlimit(tchar
**v
)
1515 tprintf("TRACE- dounlimit()\n");
1519 if (*v
&& eq(*v
, S_h
)) {
1524 for (lp
= limits
; lp
->limconst
>= 0; lp
++) {
1525 if (setlim(lp
, hard
, RLIM_INFINITY
) < 0) {
1536 if (setlim(lp
, hard
, RLIM_INFINITY
) < 0) {
1543 setlim(struct limits
*lp
, tchar hard
, rlim_t limit
)
1548 tprintf("TRACE- setlim()\n");
1550 (void) getrlimit(lp
->limconst
, &rlim
);
1552 rlim
.rlim_max
= limit
;
1553 } else if (limit
== RLIM_INFINITY
&& geteuid() != 0) {
1554 rlim
.rlim_cur
= rlim
.rlim_max
;
1556 rlim
.rlim_cur
= limit
;
1558 if (setrlimit(lp
->limconst
, &rlim
) < 0) {
1559 printf("%t: %t: Can't %s%s limit\n", bname
, lp
->limname
,
1560 limit
== RLIM_INFINITY
? "remove" : "set",
1561 hard
? " hard" : "");
1574 tprintf("TRACE- dosuspend()\n");
1577 error("Can't suspend a login shell (yet)");
1579 if (getpid() == getsid(0)) {
1580 error("Can't suspend this shell");
1583 old
= (void (*)())signal(SIGTSTP
, SIG_DFL
);
1584 (void) kill(0, SIGTSTP
);
1585 /* the shell stops here */
1586 (void) signal(SIGTSTP
, old
);
1589 (void) ioctl(FSHTTY
, TIOCGPGRP
, (char *)&ctpgrp
);
1590 if (ctpgrp
!= opgrp
) {
1591 old
= (void (*)())signal(SIGTTIN
, SIG_DFL
);
1592 (void) kill(0, SIGTTIN
);
1593 (void) signal(SIGTTIN
, old
);
1596 (void) setpgid(0, shpgrp
);
1597 (void) ioctl(FSHTTY
, TIOCSPGRP
, (char *)&shpgrp
);
1604 tchar
**oevalvec
= evalvec
;
1605 tchar
*oevalp
= evalp
;
1611 tprintf("TRACE- doeval()\n");
1617 gflag
= 0, tglob(v
);