1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23
24 /*
25 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31
32 #if defined(sun)
33 #pragma ident "@(#)bltin.c 1.16 06/06/16 SMI"
34 #endif
35
36 #include "defs.h"
37
38 /*
39 * Copyright 2008-2021 J. Schilling
40 *
41 * @(#)bltin.c 1.150 21/07/12 2008-2021 J. Schilling
42 */
43 #ifndef lint
44 static UConst char sccsid[] =
45 "@(#)bltin.c 1.150 21/07/12 2008-2021 J. Schilling";
46 #endif
47
48 /*
49 *
50 * UNIX shell
51 *
52 */
53
54 #if defined(INTERACTIVE) || defined(DO_SYSFC)
55 #include <schily/shedit.h>
56 #endif
57
58 #include <errno.h>
59 #include "sym.h"
60 #include "hash.h"
61 #ifdef DO_SYSALIAS
62 #include "abbrev.h"
63 #endif
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/times.h>
67
68 #include <schily/resource.h>
69 #include <schily/wait.h> /* Needed for CLD_EXITED */
70
71 void builtin __PR((int type, int argc, unsigned char **argv,
72 struct trenod *t, int xflags));
73 #ifdef DO_POSIX_CD
74 static int opt_LP __PR((int argc, unsigned char **argv,
75 int *opts, const char *use));
76 #endif
77 static int whatis __PR((unsigned char *arg, int verbose));
78 #ifdef DO_SYSCOMMAND
79 static void syscommand __PR((int argc, unsigned char **argv,
80 struct trenod *t, int xflags));
81 #endif
82 #if defined(INTERACTIVE) || defined(DO_SYSFC)
83 static void syshist __PR((int argc, unsigned char **argv,
84 struct trenod *t, int xflags));
85 #endif
86 #ifdef DO_TILDE
87 static unsigned char *etilde __PR((unsigned char *arg, unsigned char *s));
88 #endif
89
90 #define no_pipe (int *)0
91
92 void
builtin(type,argc,argv,t,xflags)93 builtin(type, argc, argv, t, xflags)
94 int type;
95 int argc;
96 unsigned char **argv;
97 struct trenod *t;
98 int xflags;
99 {
100 short fdindex;
101 unsigned char *a0 = NULL;
102 unsigned char *a1 = argv[1];
103 struct argnod *np = NULL;
104 int cdopt = 0;
105 #if defined(DO_POSIX_CD) || defined(DO_SYSPUSHD) || defined(DO_SYSDOSH) || \
106 defined(DO_GETOPT_UTILS) || defined(DO_SYSERRSTR)
107 int ind = 1;
108 #endif
109 #ifdef DO_POSIX_FAILURE
110 unsigned long oflags = flags;
111 #endif
112
113 exitval = 0;
114 exval_clear();
115 #ifdef DO_POSIX_FAILURE
116 if ((type & SPC_BUILTIN) == 0)
117 flags |= noexit;
118 #endif
119 type = hashdata(type);
120 fdindex = initio(t->treio, (type != SYSEXEC));
121 #ifdef DO_POSIX_FAILURE
122 flags = oflags;
123 if (exitval)
124 goto out;
125 #endif
126
127 switch (type) {
128
129 case SYSSUSP:
130 syssusp(argc, (char **)argv);
131 break;
132
133 case SYSSTOP:
134 sysstop(argc, (char **)argv);
135 break;
136
137 case SYSKILL:
138 syskill(argc, (char **)argv);
139 break;
140
141 case SYSFGBG:
142 sysfgbg(argc, (char **)argv);
143 break;
144
145 case SYSJOBS:
146 sysjobs(argc, argv);
147 break;
148
149 case SYSDOT: /* POSIX special builtin */
150 if (a1) {
151 int f;
152
153 if ((f = pathopen(getpath(a1), a1)) < 0)
154 failed(a1, notfound);
155 else {
156 #ifdef DO_POSIX_RETURN
157 jmps_t dotjmp;
158 jmps_t *odotjmp = dotshell;
159 struct fileblk *ostandin = standin;
160
161 if (setjmp(dotjmp.jb)) {
162 /*
163 * pushed in execexp()
164 */
165 while (ostandin != standin) {
166 if (!pop())
167 break;
168 }
169 dotshell = odotjmp;
170 dotcnt--;
171 break;
172 }
173 dotshell = &dotjmp;
174 dotcnt++;
175 #endif
176
177 execexp(0, (Intptr_t)f, xflags);
178
179 #ifdef DO_POSIX_RETURN
180 dotcnt--;
181 dotshell = odotjmp;
182 #endif
183 }
184 }
185 break;
186
187 case SYSTIMES: /* POSIX special builtin */
188 {
189 struct rusage ru;
190
191 getrusage(RUSAGE_SELF, &ru);
192 prtv(&ru.ru_utime, 3, 'l');
193 prc_buff(SPACE);
194 prtv(&ru.ru_stime, 3, 'l');
195 prc_buff(NL);
196 getrusage(RUSAGE_CHILDREN, &ru);
197 prtv(&ru.ru_utime, 3, 'l');
198 prc_buff(SPACE);
199 prtv(&ru.ru_stime, 3, 'l');
200 prc_buff(NL);
201 }
202 break;
203
204 case SYSEXIT: /* POSIX special builtin */
205 if (tried_to_exit++ || endjobs(JOB_STOPPED)) {
206 #ifdef INTERACTIVE
207 if (!(xflags & XEC_EXECED))
208 shedit_treset(); /* Writes ~/.history */
209 #endif
210 flags |= forcexit; /* force exit */
211 #ifdef DO_SIGNED_EXIT
212 exitsh(a1 ? stosi(a1) : retval);
213 #else
214 exitsh(a1 ? stoi(a1) : retval);
215 #endif
216 }
217 break;
218
219 case SYSNULL: /* POSIX special builtin */
220 break;
221
222 case SYSCONT: /* POSIX special builtin */
223 if (loopcnt) {
224 execbrk = breakcnt = 1;
225 if (a1) {
226 breakcnt = stoi(a1);
227 #ifdef DO_CONT_BRK_POSIX
228 if (breakcnt == 0) {
229 exitval = ERROR;
230 break;
231 }
232 #endif
233 }
234 if (breakcnt > loopcnt)
235 breakcnt = loopcnt;
236 #ifndef DO_CONT_BRK_FIX
237 else
238 #endif
239 breakcnt = -breakcnt;
240 }
241 break;
242
243 case SYSBREAK: /* POSIX special builtin */
244 if (loopcnt) {
245 execbrk = breakcnt = 1;
246 if (a1) {
247 breakcnt = stoi(a1);
248 #ifdef DO_CONT_BRK_POSIX
249 if (breakcnt == 0) {
250 exitval = ERROR;
251 break;
252 }
253 #endif
254 }
255 if (breakcnt > loopcnt)
256 breakcnt = loopcnt;
257 }
258 break;
259
260 case SYSTRAP: /* POSIX special builtin */
261 systrap(argc, (char **)argv);
262 break;
263
264 case SYSEXEC: /* POSIX special builtin */
265 argv++;
266 ioset = 0;
267 if (a1 == 0) {
268 setmode(0);
269 break;
270 }
271 #ifdef DO_EXEC_AC
272 if (eq(a1, "-a")) {
273 argv++;
274 if (*argv) {
275 a0 = *argv++;
276 }
277 }
278 #endif
279 /* FALLTHROUGH */
280
281 #ifdef RES /* Research includes login as part of the shell */
282
283 case SYSLOGIN:
284 if (!endjobs(JOB_STOPPED|JOB_RUNNING))
285 break;
286 oldsigs(TRUE);
287 execa(argv, -1, FALSE, a0);
288 done(0);
289 #else
290
291 case SYSNEWGRP:
292 if (flags & rshflg)
293 failed(argv[0], restricted);
294 else if (!endjobs(JOB_STOPPED|JOB_RUNNING))
295 break;
296 else {
297 flags |= forcexit; /* bad exec will terminate shell */
298 oldsigs(TRUE);
299 rmtemp(0);
300 rmfunctmp(0);
301 #ifdef ACCT
302 doacct();
303 #endif
304 execa(argv, -1, FALSE, a0);
305 done(0);
306 /* NOTREACHED */
307 }
308
309 #endif
310
311 #ifdef DO_SYSPUSHD
312 case SYSPOPD:
313 /* FALLTHROUGH */
314 case SYSPUSHD:
315 #ifdef DO_POSIX_CD
316 cdopt = -1;
317 ind = opt_LP(argc, argv, &cdopt,
318 type == SYSPOPD ?
319 "popd [ -L | -P ] [-offset]":
320 "pushd [ -L | -P ] [-offset | directory]");
321 if (ind < 0)
322 break;
323 a1 = argv[ind];
324 #endif
325 init_dirs();
326 if (a1 && a1[0] == '-') {
327 if (type == SYSPUSHD && a1[1] == '\0') {
328 extern struct namnod opwdnod;
329
330 a1 = opwdnod.namval;
331 if (a1 == NULL || *a1 == '\0') {
332 free(np);
333 failure(a1, baddir);
334 break;
335 }
336 } else {
337 int off = stoi(&a1[1]);
338
339 if (!(np = pop_dir(off))) {
340 failure(a1, badoff);
341 break;
342 }
343 a1 = np->argval;
344 }
345 } else if (type == SYSPOPD) {
346 struct argnod *dnp = pop_dir(0);
347
348 /*
349 * init_dirs() grants pop_dir(0) != NULL
350 */
351 if (dnp->argnxt == NULL) {
352 /*
353 * "dnp" is the static "dirs",
354 * no need to free()
355 */
356 gfailure(UC emptystack, NULL);
357 break;
358 }
359 a1 = dnp->argnxt->argval;
360 free(dnp);
361 }
362 #endif /* DO_SYSPUSHD */
363
364 /* FALLTHROUGH */
365 case SYSCD:
366 #ifdef DO_POSIX_CD
367 if (type == SYSCD) {
368 ind = opt_LP(argc, argv, &cdopt,
369 "cd [ -L | -P ] directory");
370 if (ind < 0)
371 break;
372 a1 = argv[ind];
373 }
374
375 /*
376 * Enable cd - & cd -- ... only with DO_POSIX_CD
377 */
378 if (type == SYSCD && a1 && a1[0] == '-') {
379 if (a1[1] == '\0') {
380 extern struct namnod opwdnod;
381
382 a1 = opwdnod.namval;
383 if (a1 == NULL || *a1 == '\0') {
384 free(np);
385 failure(a1, baddir);
386 break;
387 }
388 }
389 }
390 #endif
391 /*
392 * A restricted Shell does not allow "cd" at all.
393 */
394 if (flags & rshflg) {
395 free(np);
396 failed(argv[0], restricted);
397 } else if ((a1 && *a1) || (a1 == 0 && (a1 = homenod.namval))) {
398 unsigned char *cdpath;
399 unsigned char *dir;
400 int f;
401
402 /*
403 * Make sure that cwdname[] is set to be able to track
404 * the previous working directory in OLDPWD.
405 */
406 cwdset();
407
408 if ((cdpath = cdpnod.namval) == 0 ||
409 *a1 == '/' ||
410 cf(a1, UC ".") == 0 ||
411 cf(a1, UC "..") == 0 ||
412 (*a1 == '.' && (*(a1+1) == '/' ||
413 (*(a1+1) == '.' && *(a1+2) == '/'))))
414 cdpath = UC nullstr;
415
416 do {
417 dir = cdpath;
418 cdpath = catpath(cdpath, a1);
419 #ifdef DO_POSIX_CD
420 if ((cdopt & CHDIR_L) && *curstak() != '/') {
421 /*
422 * Concatenate $PWD and curstak() and
423 * normalize the resulting path.
424 */
425 if (!cwdrel2abs()) {
426 Failure(a1, baddir);
427 goto out;
428 }
429 }
430 #endif
431 } while ((f = lchdir((char *) curstak())) < 0 &&
432 cdpath);
433
434 free(np);
435 if (f < 0) {
436 switch (errno) {
437 #ifdef EMULTIHOP
438 case EMULTIHOP:
439 Failure(a1, emultihop);
440 break;
441 #endif
442 case ENOTDIR:
443 Failure(a1, enotdir);
444 break;
445 case ENOENT:
446 Failure(a1, enoent);
447 break;
448 case EACCES:
449 Failure(a1, eacces);
450 break;
451 #ifdef ENOLINK
452 case ENOLINK:
453 Failure(a1, enolink);
454 break;
455 #endif
456 default:
457 Failure(a1, baddir);
458 break;
459 }
460 break; /* No zapcd(), chdir() did not work */
461 } else {
462 unsigned char *wd;
463
464 ocwdnod(); /* Update OLDPWD= */
465 cwd(curstak(), NULL); /* Canonic from stak */
466 wd = cwdget(cdopt); /* Get reliable cwd */
467 #ifdef DO_SYSPUSHD
468 if (type != SYSPUSHD)
469 free(pop_dir(0));
470 push_dir(wd); /* Update dir stack */
471 if (pr_dirs(type == /* Print if len > 0 */
472 SYSPOPD ? /* or cmd was "popd" */
473 0:1, cdopt)) /* If already printed */
474 wd = NULL; /* don't do it again */
475 #endif
476 #ifdef DO_POSIX_CD
477 /*
478 * "cd -" should print the new directory.
479 */
480 if ((cf(UC nullstr, dir) || a1 != argv[ind]) &&
481 #else
482 if (cf(UC nullstr, dir) &&
483 #endif
484 *dir != ':' &&
485 any('/', curstak()) &&
486 flags & prompt) {
487 if (wd) { /* Not yet printed */
488 prs_buff(wd);
489 prc_buff(NL);
490 }
491 }
492 #ifdef DO_SYSALIAS
493 if (flags2 & localaliasflg) {
494 ab_use(LOCAL_AB, (char *)localname);
495 }
496 #endif
497 }
498 zapcd();
499 } else {
500 free(np);
501 /*
502 * cd "" is not permitted,
503 * cd without parameter is cd $HOME
504 * but $HOME was not set.
505 */
506 if (a1)
507 Error(nulldir);
508 else
509 Error(nohome);
510 }
511 break;
512
513 case SYSSHFT: /* POSIX special builtin */
514 {
515 int places;
516
517 places = a1 ? stoi(a1) : 1;
518
519 if ((dolc -= places) < 0) {
520 dolc = 0;
521 error(badshift);
522 } else {
523 dolv += places;
524 }
525 }
526 break;
527
528 case SYSWAIT:
529 syswait(argc, (char **)argv);
530 break;
531
532 case SYSREAD:
533 #ifndef DO_READ_R
534 if (argc < 2) {
535 Failure(argv[0], mssgargn);
536 break;
537 }
538 #endif
539 rwait = 1;
540 exitval = readvar(argc, argv);
541 rwait = 0;
542 break;
543
544 case SYSSET: /* POSIX special builtin */
545 if (a1) {
546 int cnt;
547
548 cnt = options(argc, argv);
549 #ifdef DO_POSIX_SET
550 if (cnt > 1 || dashdash)
551 #else
552 if (cnt > 1)
553 #endif
554 setargs(argv + argc - cnt);
555 } else if (comptr(t)->comset == 0) {
556 /*
557 * scan name chain and print
558 */
559 namscan(printnam);
560 }
561 break;
562
563 #ifdef DO_SYSLOCAL
564 case SYSLOCAL:
565 {
566 ind = optskip(argc, argv, "local [name[=value] ...]");
567 if (ind-- < 0)
568 break;
569 argv += ind;
570
571 if (localp == NULL)
572 error("local can only be used in a function");
573
574 if (argv[1]) {
575 while (*++argv) {
576 unsigned char *p;
577
578 p = UC strchr((char *)*argv, '=');
579 if (p)
580 *p = '\0';
581 pushval(lookup(*argv), localp);
582 if (p) {
583 *p = '=';
584 #ifdef DO_TILDE
585 if (strchr(C p, '~') != NULL)
586 p = etilde(*argv, p);
587 else
588 #endif
589 p = *argv;
590 setname(p, 0);
591 }
592 localcnt++;
593 }
594 } else {
595 namscan(printlocal);
596 }
597 }
598 break;
599 #endif /* DO_SYSLOCAL */
600
601 case SYSRDONLY: /* POSIX special builtin */
602 {
603 #ifdef DO_POSIX_EXPORT
604 struct optv optv;
605 int c;
606 int isp = 0;
607
608 optinit(&optv);
609 optv.optflag |= OPT_SPC;
610
611 while ((c = optnext(argc, argv, &optv, "p",
612 "readonly [-p] [name[=value] ...]")) !=
613 -1) {
614 if (c == 0) /* Was -help */
615 goto out;
616 else if (c == 'p')
617 isp++;
618 }
619 argv += --optv.optind;
620 #endif
621 if (argv[1]) {
622 while (*++argv) {
623 #ifdef DO_POSIX_EXPORT
624 unsigned char *p;
625
626 p = UC strchr((char *)*argv, '=');
627 if (p) {
628 #ifdef DO_TILDE
629 if (strchr(C p, '~') != NULL)
630 p = etilde(*argv, p);
631 else
632 #endif
633 p = *argv;
634 setname(p, N_RDONLY);
635 continue;
636 }
637 #endif
638 attrib(lookup(*argv), N_RDONLY);
639 }
640 } else {
641 #ifdef DO_POSIX_EXPORT
642 namscan(isp?printpro:printro);
643 #else
644 namscan(printro);
645 #endif
646 }
647 }
648 break;
649
650 case SYSXPORT: /* POSIX special builtin */
651 {
652 struct namnod *n;
653 #ifdef DO_POSIX_EXPORT
654 struct optv optv;
655 int c;
656 int isp = 0;
657
658 optinit(&optv);
659 optv.optflag |= OPT_SPC;
660
661 while ((c = optnext(argc, argv, &optv, "p",
662 "export [-p] [name[=value] ...]")) != -1) {
663 if (c == 0) /* Was -help */
664 goto out;
665 else if (c == 'p')
666 isp++;
667 }
668 argv += --optv.optind;
669 #endif
670 if (argv[1]) {
671 while (*++argv) {
672 #ifdef DO_POSIX_EXPORT
673 unsigned char *p;
674
675 p = UC strchr((char *)*argv, '=');
676 if (p) {
677 #ifdef DO_TILDE
678 if (strchr(C p, '~') != NULL)
679 p = etilde(*argv, p);
680 else
681 #endif
682 p = *argv;
683 setname(p, N_EXPORT);
684 continue;
685 }
686 #endif
687 n = lookup(*argv);
688 #ifndef DO_POSIX_UNSET
689 if (n->namflg & N_FUNCTN)
690 error(badexport);
691 else
692 #endif
693 attrib(n, N_EXPORT);
694 }
695 } else {
696 #ifdef DO_POSIX_EXPORT
697 namscan(isp?printpexp:printexp);
698 #else
699 namscan(printexp);
700 #endif
701 }
702 }
703 break;
704
705 case SYSEVAL: /* POSIX special builtin */
706 if (a1) {
707 flags &= ~noexit;
708 execexp(a1, (Intptr_t)&argv[2], xflags);
709 }
710 break;
711
712 #ifdef DO_SYSDOSH
713 case SYSDOSH:
714 if (a1 == NULL) {
715 break;
716 } else {
717 struct dolnod *olddolh;
718 unsigned char **olddolv = dolv;
719 int olddolc = dolc;
720 struct ionod *io = t->treio;
721 short idx;
722
723 ind = optskip(argc, argv,
724 "dosh command [commandname args]");
725 if (ind < 0)
726 break;
727 if (ind >= argc)
728 break;
729
730 /*
731 * save current positional parameters
732 */
733 olddolh = (struct dolnod *)savargs(funcnt);
734 funcnt++;
735 setargs(&argv[ind+1]);
736 idx = initio(io, 1);
737 execexp(argv[ind], (Intptr_t)0, xflags);
738 restore(idx);
739 (void) restorargs(olddolh, funcnt);
740 dolv = olddolv;
741 dolc = olddolc;
742 funcnt--;
743 }
744 break;
745 #endif /* DO_SYSDOSH */
746
747 #ifdef DO_SYSREPEAT
748 case SYSREPEAT:
749 if (a1) {
750 struct optv optv;
751 int c;
752 int delay = 0;
753 int count = -1;
754 int err = 0;
755
756 optinit(&optv);
757
758 while ((c = optget(argc, argv, &optv,
759 "c:(count)d:(delay)")) != -1) {
760 switch (c) {
761 case 'c':
762 count = stoi(UC optv.optarg);
763 break;
764 case 'd':
765 delay = stoi(UC optv.optarg);
766 break;
767 case '?':
768 gfailure(UC usage, repuse);
769 err = 1;
770 goto reperr;
771 }
772 }
773 reperr:
774 if (err)
775 break;
776
777 while (count != 0) {
778 unsigned char *sav = savstak();
779 struct ionod *iosav = iotemp;
780
781 execexp(argv[optv.optind],
782 (Intptr_t)&argv[optv.optind+1], xflags);
783 tdystak(sav, iosav);
784
785 if (delay > 0)
786 sh_sleep(delay);
787 if (count > 0)
788 count--;
789 /*
790 * Exit if this shell received a signal
791 */
792 sigchk();
793 /*
794 * Exit if child received a signal
795 */
796 if ((ex.ex_code != 0 &&
797 ex.ex_code != CLD_EXITED) ||
798 ex.ex_status != 0) {
799 exitsh(exitval ? exitval : SIGFAIL);
800 }
801 }
802 } else {
803 gfailure(UC usage, repuse);
804 }
805 break;
806 #endif /* DO_SYSREPEAT */
807
808 #ifndef RES
809 case SYSULIMIT:
810 sysulimit(argc, argv);
811 break;
812
813 case SYSUMASK:
814 sysumask(argc, (char **)argv);
815 break;
816 #endif
817
818 case SYSTST:
819 exitval = test(argc, argv);
820 break;
821
822 #ifdef DO_SYSATEXPR
823 case SYSEXPR:
824 expr(argc, argv);
825 break;
826 #endif
827
828 case SYSECHO:
829 exitval = echo(argc, argv);
830 break;
831
832 case SYSHASH:
833 {
834 #ifdef DO_GETOPT_UTILS /* For all builtins that support -- */
835 struct optv optv;
836 int c;
837
838 optinit(&optv);
839
840 while ((c = optnext(argc, argv, &optv, "r",
841 "hash [-r] [name ...]")) != -1) {
842 if (c == 0) /* Was -help */
843 goto out;
844 else if (c == 'r') {
845 zaphash();
846 goto out;
847 }
848 }
849 argv += --optv.optind;
850 #else
851 if (a1 && a1[0] == '-') {
852 if (a1[1] == 'r')
853 zaphash();
854 else
855 Error(badopt);
856 break;
857 }
858 #endif
859 if (argv[1]) {
860 while (*++argv) {
861 if (hashtype(hash_cmd(*argv)) ==
862 NOTFOUND) {
863 Failure(*argv, notfound);
864 }
865 }
866 } else {
867 hashpr();
868 }
869 }
870 break;
871
872 #ifdef DO_SYSPUSHD
873 case SYSDIRS:
874 ind = opt_LP(argc, argv, &cdopt, "dirs [ -L | -P ]");
875 if (ind < 0)
876 break;
877 pr_dirs(0, cdopt);
878 break;
879 #endif
880
881 case SYSPWD:
882 {
883 #ifdef DO_POSIX_CD
884 ind = opt_LP(argc, argv, &cdopt, "pwd [ -L | -P ]");
885 if (ind < 0)
886 break;
887 #endif
888 cwdprint(cdopt);
889 }
890 break;
891
892 case SYSRETURN: /* POSIX special builtin */
893 if (funcnt == 0 && dotcnt == 0)
894 error(badreturn);
895
896 if (dotcnt > 0)
897 dotbrk = 1;
898 else
899 execbrk = 1;
900 exitval = (a1 ? stoi(a1) : retval);
901 break;
902
903 case SYSTYPE:
904 if (a1) {
905 #ifdef DO_GETOPT_UTILS /* For all builtins that support -- */
906 struct optv optv;
907 int c;
908
909 optinit(&optv);
910 while ((c = optnext(argc, argv, &optv, "F",
911 "type [-F] [name ...]")) != -1) {
912 if (c == 0) /* Was -help */
913 goto out;
914 else if (c == 'F') {
915 if (argv[optv.optind] == NULL)
916 namscan(printfunc);
917 else
918 failure(argv[0], toomanyargs);
919 goto out;
920 }
921 }
922 argv += --optv.optind;
923 #endif
924 /* return success only if all names are found */
925 while (*++argv) {
926 exitval |= whatis(*argv, 2);
927 }
928 }
929 break;
930
931 case SYSUNS: /* POSIX special builtin */
932 if (a1) {
933 int uflg = 0;
934 #ifdef DO_POSIX_UNSET
935 struct optv optv;
936 int c;
937
938 optinit(&optv);
939 optv.optflag |= OPT_SPC;
940
941 while ((c = optnext(argc, argv, &optv, "fv",
942 "unset [-f | -v] [name ...]")) != -1) {
943 if (c == 0) /* Was -help */
944 goto out;
945 else if (c == 'f')
946 uflg = UNSET_FUNC;
947 else if (c == 'v')
948 uflg = UNSET_VAR;
949 }
950 argv += --optv.optind;
951 #endif
952 while (*++argv)
953 unset_name(*argv, uflg);
954 }
955 break;
956
957 case SYSGETOPT: {
958 int getoptval;
959 struct namnod *n;
960 extern unsigned char numbuf[];
961 unsigned char *varnam;
962 unsigned char c[3]; /* '+', 'c' and '\0' */
963 unsigned char *cptr; /* points to + or to c */
964 unsigned char *cmdp = *argv;
965 unsigned char *optstring;
966
967 #ifdef DO_GETOPT_UTILS /* For all builtins that support -- */
968 ind = optskip(argc, argv,
969 "getopts optstring name [arg ...]");
970 if (ind-- < 0)
971 break;
972 argc -= ind;
973 argv += ind;
974 #endif
975 if (argc < 3) {
976 failure(cmdp, mssgargn);
977 break;
978 }
979 exitval = 0;
980 n = lookup(UC "OPTIND");
981 optind = stoi(n->namval);
982 if (optind <= 0) /* Paranoia */
983 optind = 1;
984 varnam = argv[2];
985 optstring = argv[1];
986 #ifndef DO_GETOPT_PLUS
987 if (*optstring == '+')
988 optstring++;
989 #endif
990 if (argc > 3) {
991 argv[2] = dolv[0];
992 getoptval = getopt(argc-2,
993 (char **)&argv[2], (char *)optstring);
994 argv[2] = varnam;
995 } else {
996 getoptval = getopt(dolc+1,
997 (char **)dolv, (char *)optstring);
998 }
999 if (getoptval == -1) {
1000 itos(optind);
1001 assign(n, numbuf);
1002 n = lookup(varnam);
1003 #ifdef DO_GETOPT_POSIX
1004 assign(n, UC "?");
1005 #else
1006 assign(n, UC nullstr);
1007 #endif
1008 exitval = 1;
1009 break;
1010 }
1011 itos(optind);
1012 assign(n, numbuf);
1013 c[0] = '+';
1014 c[1] = (char)getoptval;
1015 c[2] = '\0';
1016 cptr = &c[1];
1017 #ifdef DO_GETOPT_PLUS
1018 if (optflg & GETOPT_PLUS_FL)
1019 cptr = c;
1020 #endif
1021 n = lookup(varnam);
1022 if (getoptval > 256) {
1023 /*
1024 * We should come here only with the Schily enhanced
1025 * getopt() from libgetopt.
1026 */
1027 #ifdef DO_GETOPT_LONGONLY
1028 itos(getoptval);
1029 #ifdef DO_GETOPT_PLUS
1030 if (optflg & GETOPT_PLUS_FL) {
1031 unsigned char pnumbuf[64];
1032 strcpy(C pnumbuf, "+");
1033 strcat(C pnumbuf, C numbuf);
1034 assign(n, pnumbuf);
1035 } else
1036 #endif /* DO_GETOPT_PLUS */
1037 assign(n, numbuf);
1038 #else
1039 assign(n, UC "?"); /* Pretend illegal option */
1040 #endif
1041 } else
1042 assign(n, cptr);
1043 n = lookup(UC "OPTARG");
1044 assign(n, UC optarg);
1045 #ifdef DO_GETOPT_POSIX
1046 /*
1047 * In case that the option string starts with ':',
1048 * POSIX requires to set OPTARG to the option
1049 * character that causes getopt() to return '?'/':'.
1050 */
1051 if (optarg == NULL && argv[1][0] == ':' &&
1052 (getoptval == '?' || getoptval == ':')) {
1053 c[1] = optopt;
1054 assign(n, &c[1]);
1055 }
1056 #endif
1057 }
1058 break;
1059
1060 #ifdef INTERACTIVE
1061 case SYSHISTORY:
1062 syshist(argc, argv, t, xflags);
1063 if (intrptr) { /* Was set by syshist()? */
1064 *intrptr = 0; /* Clear interrupt counter */
1065 intrptr = 0; /* Disable intrptr for now */
1066 }
1067 break;
1068
1069 case SYSSAVEHIST:
1070 shedit_bshist(&intrptr);
1071
1072 if (intrptr) { /* Was set by shedit_bshist()? */
1073 *intrptr = 0; /* Clear interrupt counter */
1074 intrptr = 0; /* Disable intrptr for now */
1075 }
1076 break;
1077
1078 case SYSMAP:
1079 {
1080 int f = STDOUT_FILENO;
1081 struct optv optv;
1082 int c;
1083 int rfl = 0;
1084 int ufl = 0;
1085 unsigned char *cmdp = *argv;
1086
1087 optinit(&optv);
1088
1089 while ((c = optnext(argc, argv, &optv, "ru",
1090 "map [-r | -u] [fromstr [tostr [comment]]]")) !=
1091 -1) {
1092 if (c == 0) /* Was -help */
1093 goto out;
1094 else if (c == 'r')
1095 rfl++;
1096 else if (c == 'u')
1097 ufl++;
1098 }
1099 argv += --optv.optind;
1100 argc -= optv.optind;
1101
1102 if (argc == 1 && (rfl+ufl) == 0) {
1103 shedit_list_map(&f);
1104 } else if (argc == 1 && rfl) {
1105 shedit_remap();
1106 } else if (argc == 2 && ufl) {
1107 shedit_del_map((char *)argv[1]);
1108 } else if (argc == 3 || argc == 4) {
1109 /*
1110 * argv[1] is map from
1111 * argv[2] is map to
1112 * argv[3] is the optional comment
1113 */
1114 if (!shedit_add_map((char *)argv[1],
1115 (char *)argv[2],
1116 (char *)argv[3])) {
1117 prs(cmdp);
1118 prs(UC ": ");
1119 prs(UC "already defined\n");
1120 gfailure(UC "bad map", NULL);
1121 }
1122 } else if (argc > 4) {
1123 gfailure(UC arglist, NULL);
1124 } else {
1125 gfailure(UC mssgargn, NULL);
1126 }
1127 }
1128 break;
1129 #endif /* INTERACTIVE */
1130
1131 #ifdef DO_SYSALLOC
1132 case SYSALLOC:
1133 chkmem();
1134 break;
1135 #endif
1136
1137 #ifdef DO_SYSALIAS
1138 case SYSALIAS:
1139 sysalias(argc, argv);
1140 break;
1141 case SYSUNALIAS:
1142 sysunalias(argc, argv);
1143 break;
1144 #endif
1145
1146 #ifdef DO_SYSTRUE
1147 case SYSTRUE:
1148 break;
1149 case SYSFALSE:
1150 exitval = 1;
1151 break;
1152 #endif
1153
1154 #ifdef DO_SYSBUILTIN
1155 case SYSBUILTIN:
1156 sysbuiltin(argc, argv);
1157 break;
1158 #endif
1159
1160 #ifdef HAVE_LOADABLE_LIBS
1161 case SYSLOADABLE: {
1162 int exval;
1163 struct sysnod2 *s2;
1164
1165 s2 = sh_findbuiltin(*argv);
1166 if (s2 == NULL) {
1167 failure(*argv, notfound);
1168 break;
1169 }
1170 exval = (*s2->sysptr)(argc, argv, &bosh);
1171 if (exval)
1172 exitval = exval;
1173 }
1174 break;
1175 #endif
1176
1177 #ifdef DO_SYSFIND
1178 case SYSFIND: {
1179 int exval = sysfind(argc, argv, &bosh);
1180
1181 if (exval)
1182 exitval = exval;
1183 }
1184 break;
1185 #endif
1186
1187 #ifdef DO_SYSSYNC
1188 case SYSSYNC:
1189 #ifdef HAVE_SYNC
1190 sync();
1191 #else
1192 failure(*argv, unimplemented);
1193 #endif
1194 break;
1195 #endif
1196
1197 #ifdef DO_SYSPGRP
1198 case SYSPGRP:
1199 syspgrp(argc, (char **)argv);
1200 break;
1201 #endif
1202
1203 #ifdef DO_SYSERRSTR
1204 case SYSERRSTR: {
1205 int err;
1206 char *msg;
1207
1208 ind = optskip(argc, argv, "errstr errno");
1209 #ifndef HAVE_STRERROR
1210 #define strerror(a) errmsgstr(a)
1211 #endif
1212 if (ind < 0)
1213 break;
1214
1215 a1 = argv[ind];
1216
1217 if (a1 && (err = stoi(a1)) > 0) {
1218 errno = 0;
1219 msg = strerror(err);
1220 if (errno == 0 && msg) {
1221 prs_buff(UC msg);
1222 prc_buff(NL);
1223 }
1224 }
1225 }
1226 break;
1227 #endif
1228
1229 #ifdef DO_SYSPRINTF
1230 case SYSPRINTF:
1231 sysprintf(argc, argv);
1232 break;
1233 #endif
1234
1235 #ifdef DO_SYSCOMMAND
1236 case SYSCOMMAND:
1237 syscommand(argc, argv, t, xflags);
1238 break;
1239 #endif
1240
1241 #ifdef DO_SYSFC
1242 case SYSFC:
1243 syshist(argc, argv, t, xflags);
1244 if (intrptr) { /* Was set by syshist()? */
1245 *intrptr = 0; /* Clear interrupt counter */
1246 intrptr = 0; /* Disable intrptr for now */
1247 }
1248 break;
1249 #endif
1250
1251 default:
1252 prs_buff(_gettext("unknown builtin\n"));
1253 }
1254 #if defined(DO_POSIX_EXPORT) || defined(DO_POSIX_UNSET) || \
1255 defined(DO_GETOPT_UTILS) || defined(INTERACTIVE) || \
1256 defined(DO_POSIX_CD) || defined(DO_POSIX_FAILURE)
1257 out:
1258 #endif
1259 flushb(); /* Flush print buffer */
1260 restore(fdindex); /* Restore file descriptors */
1261 if (type != SYSWAIT) /* Do not clobber $/ from wait */
1262 exval_set(exitval); /* Prepare ${.sh.*} parameters */
1263 #ifdef DO_ERR_TRAP
1264 if ((trapnote & TRAPSET) ||
1265 (exitval &&
1266 (xflags & XEC_NOSTOP) == 0))
1267 #endif
1268 chktrap(); /* Run installed traps */
1269 }
1270
1271 #ifdef DO_POSIX_CD
1272 static int
opt_LP(argc,argv,opts,use)1273 opt_LP(argc, argv, opts, use)
1274 int argc;
1275 unsigned char **argv;
1276 int *opts;
1277 const char *use;
1278 {
1279 struct optv optv;
1280 int c;
1281 int opt = *opts;
1282
1283 optinit(&optv);
1284 if (opt < 0) {
1285 optv.optflag |= OPT_NOFAIL;
1286 *opts = 0;
1287 }
1288 while ((c = optnext(argc, argv, &optv, "LP", use)) != -1) {
1289 if (c == 0) { /* Was -help or bad opt */
1290 return (-1);
1291 } else if (c == '?') {
1292 if (opt < 0) {
1293 /*
1294 * If *opts has been preinitialzed with -1,
1295 * accept e.g. -2 as a a pushd offset arg.
1296 */
1297 c = argv[optv.ooptind][1];
1298 if (c >= '0' && c <= '9')
1299 return (optv.ooptind);
1300 optbad(argc, argv, &optv);
1301 gfailure((unsigned char *)usage, use);
1302 return (-1);
1303 }
1304 } else if (c == 'L') {
1305 *opts = CHDIR_L;
1306 } else if (c == 'P') {
1307 *opts = CHDIR_P;
1308 }
1309 }
1310
1311 /*
1312 * POSIX decided in 1992 to introduce an incompatible default for "cd".
1313 * Even though this is incompatible with the Bourne Shell behavior, it
1314 * may be too late to go back.
1315 */
1316 if ((flags2 & posixflg) && *opts == 0)
1317 *opts = CHDIR_L;
1318
1319 return (optv.optind);
1320 }
1321 #endif /* DO_POSIX_CD */
1322
1323 static int
whatis(arg,verbose)1324 whatis(arg, verbose)
1325 unsigned char *arg;
1326 int verbose;
1327 {
1328 #ifdef DO_SYSALIAS
1329 char *val;
1330
1331 if ((val = ab_value(LOCAL_AB, (char *)arg, NULL,
1332 AB_BEGIN)) != NULL) {
1333 if (verbose) {
1334 prs_buff(arg);
1335 prs_buff(_gettext(" is a local alias for '"));
1336 }
1337 prs_buff(UC val);
1338 if (verbose)
1339 prs_buff(UC "'\n");
1340 else
1341 prs_buff(UC "\n");
1342 return (0);
1343 } else if ((val = ab_value(GLOBAL_AB, (char *)arg, NULL,
1344 AB_BEGIN)) != NULL) {
1345 if (verbose) {
1346 prs_buff(arg);
1347 prs_buff(_gettext(" is a global alias for '"));
1348 }
1349 prs_buff(UC val);
1350 if (verbose)
1351 prs_buff(UC "'\n");
1352 else
1353 prs_buff(UC "\n");
1354 return (0);
1355 }
1356 #endif /* DO_SYSALIAS */
1357 return (what_is_path(arg, verbose));
1358 }
1359
1360 #ifdef DO_SYSCOMMAND
1361 static void
syscommand(argc,argv,t,xflags)1362 syscommand(argc, argv, t, xflags)
1363 int argc;
1364 unsigned char **argv;
1365 struct trenod *t;
1366 int xflags;
1367 {
1368 struct optv optv;
1369 int c;
1370 int pflg = 0;
1371 int vflg = 0;
1372 int Vflg = 0;
1373 char *cusage = "command [-p][-v | -V] [name [arg ...]]";
1374
1375 optinit(&optv);
1376
1377 while ((c = optnext(argc, argv, &optv, "pvV", cusage)) != -1) {
1378 if (c == 0) { /* Was -help */
1379 return;
1380 } else if (c == 'p') {
1381 pflg++;
1382 } else if (c == 'v') {
1383 if (Vflg) {
1384 optbad(argc, UCP argv, &optv);
1385 gfailure((unsigned char *)usage, cusage);
1386 return;
1387 }
1388 vflg++;
1389 } else if (c == 'V') {
1390 if (vflg) {
1391 optbad(argc, UCP argv, &optv);
1392 gfailure((unsigned char *)usage, cusage);
1393 return;
1394 }
1395 Vflg++;
1396 }
1397 }
1398 argc -= optv.optind;
1399 argv += optv.optind;
1400 if ((vflg + Vflg) && *argv == NULL) {
1401 gfailure((unsigned char *)usage, cusage);
1402 return;
1403 }
1404 if (pflg) {
1405 flags |= ppath;
1406 }
1407 if (vflg) {
1408 exitval = whatis(*argv, 0);
1409 } else if (Vflg) {
1410 exitval = whatis(*argv, 1);
1411 } else if (*argv) {
1412 short cmdhash;
1413 unsigned long oflags = flags;
1414
1415 /*
1416 * A function may overlay a builtin command.
1417 * We therefore do not search for functions.
1418 */
1419 flags |= nofuncs;
1420 cmdhash = pathlook(argv[0], 0, (struct argnod *)0);
1421 flags = oflags;
1422
1423 if (hashtype(cmdhash) == BUILTIN) {
1424 struct ionod *iop = t->treio;
1425
1426 if (cmdhash & SPC_BUILTIN)
1427 flags |= noexit;
1428 t->treio = NULL;
1429 builtin(cmdhash, argc, argv, t, xflags);
1430 t->treio = iop;
1431 flags &= ~(ppath | noexit);
1432 return;
1433 } else {
1434 unsigned char *sav = savstak();
1435 struct ionod *iosav = iotemp;
1436 struct comnod cnod;
1437
1438 cnod.comtyp = TCOM;
1439 cnod.comio = NULL;
1440 cnod.comarg = (struct argnod *)argv;
1441 cnod.comset = comptr(t)->comset;
1442
1443 flags |= nofuncs;
1444 execute((struct trenod *)&cnod, xflags | XEC_HASARGV,
1445 0, no_pipe, no_pipe);
1446 flags &= ~nofuncs;
1447 tdystak(sav, iosav);
1448 }
1449 }
1450 flags &= ~ppath;
1451 }
1452 #endif /* DO_SYSCOMMAND */
1453
1454 #if defined(INTERACTIVE) || defined(DO_SYSFC)
1455 #define LFLAG 1 /* Do listing */
1456 #define SFLAG 2 /* Re-execute */
1457 #define EFLAG 4 /* Edit and Re-execute */
1458 #define ALLFLAG 1024 /* Show full history */
1459 /*
1460 * The implementation for the "history" and the "fc" builtin command.
1461 *
1462 * The "fc" builtin is an historic artefact from ksh.
1463 * It is based on the original ksh history concept that has never been used in
1464 * our history editor that is rather based on the history editor in "bsh", the
1465 * shell from H.Berthold AG that had a fully integrated history editor in 1984
1466 * already; based on a concept from 1982. Ksh originally called an external
1467 * editor command to modify the history.
1468 *
1469 * As "fc" is part of the POSIX standard, we have to implement it even though
1470 * the features required by POSIX do not match the features from a modern
1471 * history editor, so we try to do the best we can...
1472 *
1473 * The "history" implementation currently just disables the re-run and edit
1474 * features.
1475 */
1476 static void
syshist(argc,argv,t,xflags)1477 syshist(argc, argv, t, xflags)
1478 int argc;
1479 unsigned char **argv;
1480 struct trenod *t;
1481 int xflags;
1482 {
1483 struct optv optv;
1484 int c;
1485 int fd;
1486 int first = 0;
1487 int last = 0;
1488 int flg = 0;
1489 int neg = 0;
1490 int hflg = HI_INTR|HI_TAB;
1491 unsigned char *av0 = argv[0];
1492 unsigned char *cp;
1493 unsigned char *editor = NULL;
1494 unsigned char *substp = NULL;
1495 struct tempblk tb;
1496 char *fusage =
1497 "fc [-r][-l][-n][-s][-e editor] [first [last]]";
1498 char *husage =
1499 "history [-r][-n] [first [last]]";
1500 char *xusage = fusage;
1501
1502 if (*av0 == 'h') {
1503 xusage = husage;
1504 flg |= LFLAG;
1505 }
1506
1507 optinit(&optv);
1508
1509 while ((c = optnext(argc, argv, &optv,
1510 "rlnse:1234567890", xusage)) != -1) {
1511 if (c == 0) { /* Was -help */
1512 return;
1513 } else if (c == 'r') {
1514 hflg |= HI_REVERSE;
1515 } else if (c == 'l') {
1516 flg |= LFLAG;
1517 } else if (c == 'n') {
1518 hflg |= HI_NONUM;
1519 } else if (c == 's') {
1520 flg |= SFLAG;
1521 } else if (c == 'e') {
1522 editor = UC optv.optarg;
1523 } else if (digit(c)) {
1524 first = stosi(argv[optv.ooptind]);
1525 argc--;
1526 argv++;
1527 neg++;
1528 break;
1529 }
1530 }
1531 argc -= optv.optind;
1532 argv += optv.optind;
1533
1534 if ((flg & (LFLAG | SFLAG)) == 0)
1535 flg |= EFLAG;
1536
1537 if ((flg & SFLAG) && argc > 0) {
1538 /*
1539 * POSIX requires new=old only with "fc -s".
1540 */
1541 if (anys(UC "=", argv[0])) {
1542 substp = argv[0];
1543 argc--;
1544 argv++;
1545 }
1546 }
1547
1548 if (!first && argc > 0) {
1549 cp = argv[0];
1550 if (*cp == '+')
1551 cp++;
1552 if (digit(*cp)) {
1553 first = stoi(UC cp);
1554 if (first == 0)
1555 flg |= ALLFLAG;
1556 } else {
1557 first = shedit_search_history(&intrptr,
1558 hflg, 0, C argv[0]);
1559 }
1560 argc--;
1561 argv++;
1562 }
1563 if (first == 0 && !neg && (flg & ALLFLAG) == 0) {
1564 if (flg & LFLAG)
1565 first = -15;
1566 else
1567 first = -1;
1568 }
1569 if (flg & SFLAG) {
1570 last = first;
1571 } else if (argc > 0) {
1572 cp = argv[0];
1573 if (*cp == '+')
1574 cp++;
1575 if (digit(*cp)) {
1576 last = stosi(UC cp);
1577 } else {
1578 last = shedit_search_history(&intrptr,
1579 hflg, 0, C argv[0]);
1580 }
1581 argc--;
1582 argv++;
1583 }
1584 if (argc > 0) {
1585 gfailure(av0, toomanyargs);
1586 return;
1587 }
1588 if (last == 0) {
1589 if (flg & LFLAG) {
1590 unsigned lastn;
1591
1592 shedit_histrange(NULL, &lastn, NULL);
1593 last = lastn;
1594 if (first == 0 && neg)
1595 first = last;
1596 } else {
1597 last = first;
1598 }
1599 }
1600
1601 if (flg & LFLAG) {
1602 if (isatty(STDOUT_FILENO))
1603 hflg |= HI_PRETTYP;
1604 /*
1605 * We do not remove the new "fc -l" command but should include
1606 * the last command in our listing.
1607 */
1608 if (first < 0)
1609 first--;
1610 } else {
1611 unsigned lastn;
1612
1613 shedit_histrange(NULL, &lastn, NULL);
1614 /*
1615 * Remove this "fc" command from the history.
1616 */
1617 (void) shedit_remove_history(&intrptr,
1618 hflg, lastn, NULL);
1619 if (last == lastn) {
1620 shedit_histrange(NULL, &lastn, NULL);
1621 last = lastn;
1622 }
1623 fd = tmpfil(&tb);
1624 }
1625 /*
1626 * Even in case we write the history into a file, we do not
1627 * convert ASCII newlines into ANSI newlines, so we may see
1628 * more lines in the file than history entries selected.
1629 */
1630 if (shedit_history((flg & LFLAG) ? NULL : &fd,
1631 &intrptr, hflg, first, last, C substp) != 0) {
1632 gfailure(av0, "history specification out of range");
1633 return;
1634 }
1635 if (flg & LFLAG)
1636 return;
1637
1638 if (flg & EFLAG) {
1639 char *av[2];
1640
1641 av[0] = C make(tmpout);
1642 av[1] = NULL;
1643
1644 if (editor == NULL)
1645 editor = fcenod.namval;
1646 if (editor == NULL)
1647 editor = UC fcename;
1648 execexp(editor, (Intptr_t)av, xflags);
1649 unlink(av[0]);
1650 free(av[0]);
1651 if (exitval) {
1652 close(fd);
1653 return;
1654 }
1655 } else {
1656 unlink(C tmpout);
1657 }
1658
1659 lseek(fd, (off_t)0, SEEK_SET);
1660 execexp(0, (Intptr_t)fd, xflags);
1661 /*
1662 * Add re-executed commands to the history.
1663 */
1664 lseek(fd, (off_t)0, SEEK_SET);
1665 shedit_read_history(&fd, &intrptr, hflg);
1666
1667 close(fd);
1668 }
1669 #endif /* defined(INTERACTIVE) || defined(DO_SYSFC) */
1670
1671 #ifdef DO_TILDE
1672 static unsigned char *
etilde(arg,s)1673 etilde(arg, s)
1674 unsigned char *arg; /* complete encironment entry */
1675 unsigned char *s; /* points to '=' */
1676 {
1677 UIntptr_t b = relstak();
1678 unsigned char *p;
1679
1680 ++s; /* Point to 1st char of value */
1681 while (arg < s) { /* Copy parts up to '=' */
1682 GROWSTAKTOP();
1683 pushstak(*arg++);
1684 }
1685 while (*arg) {
1686 p = UC strchr(C arg, '~');
1687 if (p == NULL)
1688 break;
1689 while (arg < p) { /* Copy up to '~' */
1690 GROWSTAKTOP();
1691 pushstak(*arg++);
1692 }
1693 if (p > s && p[-1] != ':') {
1694 /* EMPTY */
1695 ;
1696 } else if ((p = do_tilde(arg)) != NULL) {
1697 unsigned char c;
1698
1699 while (*p) { /* Copy expanded directory */
1700 GROWSTAKTOP();
1701 pushstak(*p++);
1702 }
1703 do { /* Skip unexpanded input */
1704 c = *++arg;
1705 } while (c && c != '/' && c != ':');
1706 continue;
1707 }
1708 GROWSTAKTOP();
1709 pushstak(*arg++); /* Copy '~' */
1710 }
1711 while (*arg) { /* Copy rest */
1712 GROWSTAKTOP();
1713 pushstak(*arg++);
1714 }
1715 zerostak();
1716 return (absstak(b));
1717 }
1718 #endif
1719