xref: /illumos-gate/usr/src/cmd/sh/name.c (revision b6c3f786)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 /*
32  * UNIX shell
33  */
34 
35 #include	"defs.h"
36 #include	<stropts.h>
37 
38 extern BOOL	chkid();
39 extern unsigned char	*simple();
40 extern int	mailchk;
41 
42 static void	setname(unsigned char *, int);
43 static void	set_builtins_path();
44 static int	patheq();
45 static void	namwalk(struct namnod *);
46 static void	dolocale();
47 
48 struct namnod ps2nod =
49 {
50 	(struct namnod *)NIL,
51 	&acctnod,
52 	(unsigned char *)ps2name
53 };
54 struct namnod cdpnod =
55 {
56 	(struct namnod *)NIL,
57 	(struct namnod *)NIL,
58 	(unsigned char *)cdpname
59 };
60 struct namnod pathnod =
61 {
62 	&mailpnod,
63 	(struct namnod *)NIL,
64 	(unsigned char *)pathname
65 };
66 struct namnod ifsnod =
67 {
68 	&homenod,
69 	&mailnod,
70 	(unsigned char *)ifsname
71 };
72 struct namnod ps1nod =
73 {
74 	&pathnod,
75 	&ps2nod,
76 	(unsigned char *)ps1name
77 };
78 struct namnod homenod =
79 {
80 	&cdpnod,
81 	(struct namnod *)NIL,
82 	(unsigned char *)homename
83 };
84 struct namnod mailnod =
85 {
86 	(struct namnod *)NIL,
87 	(struct namnod *)NIL,
88 	(unsigned char *)mailname
89 };
90 struct namnod mchknod =
91 {
92 	&ifsnod,
93 	&ps1nod,
94 	(unsigned char *)mchkname
95 };
96 struct namnod acctnod =
97 {
98 	(struct namnod *)NIL,
99 	(struct namnod *)NIL,
100 	(unsigned char *)acctname
101 };
102 struct namnod mailpnod =
103 {
104 	(struct namnod *)NIL,
105 	(struct namnod *)NIL,
106 	(unsigned char *)mailpname
107 };
108 
109 
110 struct namnod *namep = &mchknod;
111 
112 /* ========	variable and string handling	======== */
113 
114 int
115 syslook(unsigned char *w, struct sysnod syswds[], int n)
116 {
117 	int	low;
118 	int	high;
119 	int	mid;
120 	int	cond;
121 
122 	if (w == 0 || *w == 0)
123 		return(0);
124 
125 	low = 0;
126 	high = n - 1;
127 
128 	while (low <= high)
129 	{
130 		mid = (low + high) / 2;
131 
132 		if ((cond = cf(w, syswds[mid].sysnam)) < 0)
133 			high = mid - 1;
134 		else if (cond > 0)
135 			low = mid + 1;
136 		else
137 			return(syswds[mid].sysval);
138 	}
139 	return(0);
140 }
141 
142 void
143 setlist(struct argnod *arg, int xp)
144 {
145 	if (flags & exportflg)
146 		xp |= N_EXPORT;
147 
148 	while (arg)
149 	{
150 		unsigned char *s = mactrim(arg->argval);
151 		setname(s, xp);
152 		arg = arg->argnxt;
153 		if (flags & execpr)
154 		{
155 			prs(s);
156 			if (arg)
157 				blank();
158 			else
159 				newline();
160 		}
161 	}
162 }
163 
164 static void
165 setname(unsigned char *argi, int xp)	/* does parameter assignments */
166 {
167 	unsigned char *argscan = argi;
168 	struct namnod *n;
169 
170 	if (letter(*argscan))
171 	{
172 		while (alphanum(*argscan))
173 			argscan++;
174 
175 		if (*argscan == '=')
176 		{
177 			*argscan = 0;	/* make name a cohesive string */
178 
179 			n = lookup(argi);
180 			*argscan++ = '=';
181 			attrib(n, xp);
182 			if (xp & N_ENVNAM)
183 			{
184 				n->namenv = n->namval = argscan;
185 				if (n == &pathnod)
186 					set_builtins_path();
187 			}
188 			else
189 				assign(n, argscan);
190 
191 			dolocale(n->namid);
192 			return;
193 		}
194 	}
195 }
196 
197 void
198 replace(unsigned char **a, unsigned char *v)
199 {
200 	free(*a);
201 	*a = make(v);
202 }
203 
204 void
205 dfault(struct namnod *n, unsigned char	*v)
206 {
207 	if (n->namval == 0)
208 		assign(n, v);
209 }
210 
211 void
212 assign(struct namnod *n, unsigned char *v)
213 {
214 	if (n->namflg & N_RDONLY)
215 		failed(n->namid, wtfailed);
216 
217 #ifndef RES
218 
219 	else if (flags & rshflg)
220 	{
221 		if (n == &pathnod || eq(n->namid,"SHELL"))
222 			failed(n->namid, restricted);
223 	}
224 #endif
225 
226 	else if (n->namflg & N_FUNCTN)
227 	{
228 		func_unhash(n->namid);
229 		freefunc(n);
230 
231 		n->namenv = 0;
232 		n->namflg = N_DEFAULT;
233 	}
234 
235 	if (n == &mchknod)
236 	{
237 		mailchk = stoi(v);
238 	}
239 
240 	replace(&n->namval, v);
241 	attrib(n, N_ENVCHG);
242 
243 	if (n == &pathnod)
244 	{
245 		zaphash();
246 		set_dotpath();
247 		set_builtins_path();
248 		return;
249 	}
250 
251 	if (flags & prompt)
252 	{
253 		if ((n == &mailpnod) || (n == &mailnod && mailpnod.namflg == N_DEFAULT))
254 			setmail(n->namval);
255 	}
256 }
257 
258 static void
259 set_builtins_path()
260 {
261 	unsigned char *path;
262 
263         ucb_builtins = 0;
264         path = getpath("");
265         while (path && *path)
266         {
267                 if (patheq(path, "/usr/ucb"))
268                 {
269                         ucb_builtins++;
270                         break;
271                 }
272                 else if (patheq(path, "/usr/bin"))
273                         break;
274                 else if (patheq(path, "/bin"))
275                         break;
276                 else if (patheq(path, "/usr/5bin"))
277                         break;
278                 path = nextpath(path);
279         }
280 }
281 
282 static int
283 patheq(unsigned char *component, char *dir)
284 {
285 	unsigned char   c;
286 
287         for (;;)
288         {
289                 c = *component++;
290                 if (c == COLON)
291                         c = '\0';       /* end of component of path */
292 		if (c != *dir++)
293 			return (0);
294                 if (c == '\0')
295                         return(1);
296         }
297 }
298 
299 int
300 readvar(unsigned char **names)
301 {
302 	struct fileblk	fb;
303 	struct fileblk *f = &fb;
304 	unsigned char	c[MULTI_BYTE_MAX+1];
305 	int	rc = 0;
306 	struct namnod *n = lookup(*names++);	/* done now to avoid storage mess */
307 	unsigned char	*rel = (unsigned char *)relstak();
308 	unsigned char *oldstak;
309 	unsigned char *pc, *rest;
310 	int		d;
311 
312 	push(f);
313 	initf(dup(0));
314 
315 	/*
316 	 * If stdin is a pipe then this lseek(2) will fail with ESPIPE, so
317 	 * the read buffer size is set to 1 because we will not be able
318 	 * lseek(2) back towards the beginning of the file, so we have
319 	 * to read a byte at a time instead
320 	 *
321 	 */
322 	if (lseek(0, (off_t)0, SEEK_CUR) == -1)
323 		f->fsiz = 1;
324 
325 	/*
326 	 * If stdin is a socket then this isastream(3C) will return 1, so
327 	 * the read buffer size is set to 1 because we will not be able
328 	 * lseek(2) back towards the beginning of the file, so we have
329 	 * to read a byte at a time instead
330 	 *
331 	 */
332 	if (isastream(0) == 1)
333 		f->fsiz = 1;
334 
335 	/*
336 	 * strip leading IFS characters
337 	 */
338 	for (;;)
339 	{
340 		d = nextwc();
341 		if(eolchar(d))
342 			break;
343 		rest = readw(d);
344 		pc = c;
345 		while(*pc++ = *rest++);
346 		if(!anys(c, ifsnod.namval))
347 			break;
348 	}
349 
350 	oldstak = curstak();
351 	for (;;)
352 	{
353 		if ((*names && anys(c, ifsnod.namval)) || eolchar(d))
354 		{
355 			if (staktop >= brkend)
356 				growstak(staktop);
357 			zerostak();
358 			assign(n, absstak(rel));
359 			setstak(rel);
360 			if (*names)
361 				n = lookup(*names++);
362 			else
363 				n = 0;
364 			if (eolchar(d))
365 			{
366 				break;
367 			}
368 			else		/* strip imbedded IFS characters */
369 				while(1) {
370 					d = nextwc();
371 					if(eolchar(d))
372 						break;
373 					rest = readw(d);
374 					pc = c;
375 					while(*pc++ = *rest++);
376 					if(!anys(c, ifsnod.namval))
377 						break;
378 				}
379 		}
380 		else
381 		{
382 			if(d == '\\') {
383 				d = readwc();
384 				rest = readw(d);
385 				while(d = *rest++) {
386 					if (staktop >= brkend)
387 						growstak(staktop);
388 					pushstak(d);
389 				}
390 				oldstak = staktop;
391 			}
392 			else
393 			{
394 				pc = c;
395 				while(d = *pc++) {
396 					if (staktop >= brkend)
397 						growstak(staktop);
398 					pushstak(d);
399 				}
400 				if(!anys(c, ifsnod.namval))
401 					oldstak = staktop;
402 			}
403 			d = nextwc();
404 
405 			if (eolchar(d))
406 				staktop = oldstak;
407 			else
408 			{
409 				rest = readw(d);
410 				pc = c;
411 				while(*pc++ = *rest++);
412 			}
413 		}
414 	}
415 	while (n)
416 	{
417 		assign(n, (unsigned char *)nullstr);
418 		if (*names)
419 			n = lookup(*names++);
420 		else
421 			n = 0;
422 	}
423 
424 	if (eof)
425 		rc = 1;
426 
427 	if (isastream(0) != 1)
428 		/*
429 		 * If we are reading on a stream do not attempt to
430 		 * lseek(2) back towards the start because this is
431 		 * logically meaningless, but there is nothing in
432 		 * the standards to pervent the stream implementation
433 		 * from attempting it and breaking our code here
434 		 *
435 		 */
436 		lseek(0, (off_t)(f->nxtoff - f->endoff), SEEK_CUR);
437 
438 	pop();
439 	return(rc);
440 }
441 
442 void
443 assnum(unsigned char **p, long i)
444 {
445 	int j = ltos(i);
446 	replace(p, &numbuf[j]);
447 }
448 
449 unsigned char *
450 make(v)
451 unsigned char	*v;
452 {
453 	unsigned char	*p;
454 
455 	if (v)
456 	{
457 		movstr(v, p = (unsigned char *)alloc(length(v)));
458 		return(p);
459 	}
460 	else
461 		return(0);
462 }
463 
464 
465 struct namnod *
466 lookup(unsigned char *nam)
467 {
468 	struct namnod *nscan = namep;
469 	struct namnod **prev;
470 	int		LR;
471 
472 	if (!chkid(nam))
473 		failed(nam, notid);
474 
475 	while (nscan)
476 	{
477 		if ((LR = cf(nam, nscan->namid)) == 0)
478 			return(nscan);
479 
480 		else if (LR < 0)
481 			prev = &(nscan->namlft);
482 		else
483 			prev = &(nscan->namrgt);
484 		nscan = *prev;
485 	}
486 	/*
487 	 * add name node
488 	 */
489 	nscan = (struct namnod *)alloc(sizeof *nscan);
490 	nscan->namlft = nscan->namrgt = (struct namnod *)NIL;
491 	nscan->namid = make(nam);
492 	nscan->namval = 0;
493 	nscan->namflg = N_DEFAULT;
494 	nscan->namenv = 0;
495 
496 	return(*prev = nscan);
497 }
498 
499 BOOL
500 chkid(nam)
501 unsigned char	*nam;
502 {
503 	unsigned char *cp = nam;
504 
505 	if (!letter(*cp))
506 		return(FALSE);
507 	else
508 	{
509 		while (*++cp)
510 		{
511 			if (!alphanum(*cp))
512 				return(FALSE);
513 		}
514 	}
515 	return(TRUE);
516 }
517 
518 static void (*namfn)();
519 
520 void
521 namscan(void (*fn)())
522 {
523 	namfn = fn;
524 	namwalk(namep);
525 }
526 
527 static void
528 namwalk(struct namnod *np)
529 {
530 	if (np)
531 	{
532 		namwalk(np->namlft);
533 		(*namfn)(np);
534 		namwalk(np->namrgt);
535 	}
536 }
537 
538 void
539 printnam(struct namnod *n)
540 {
541 	unsigned char	*s;
542 
543 	sigchk();
544 
545 	if (n->namflg & N_FUNCTN)
546 	{
547 		prs_buff(n->namid);
548 		prs_buff("(){\n");
549 		prf(n->namenv);
550 		prs_buff("\n}\n");
551 	}
552 	else if (s = n->namval)
553 	{
554 		prs_buff(n->namid);
555 		prc_buff('=');
556 		prs_buff(s);
557 		prc_buff(NL);
558 	}
559 }
560 
561 static int namec;
562 
563 void
564 printro(struct namnod *n)
565 {
566 	if (n->namflg & N_RDONLY)
567 	{
568 		prs_buff(_gettext(readonly));
569 		prc_buff(SPACE);
570 		prs_buff(n->namid);
571 		prc_buff(NL);
572 	}
573 }
574 
575 void
576 printexp(struct namnod *n)
577 {
578 	if (n->namflg & N_EXPORT)
579 	{
580 		prs_buff(_gettext(export));
581 		prc_buff(SPACE);
582 		prs_buff(n->namid);
583 		prc_buff(NL);
584 	}
585 }
586 
587 void
588 setup_env(void)
589 {
590 	unsigned char **e = environ;
591 
592 	while (*e)
593 		setname(*e++, N_ENVNAM);
594 }
595 
596 
597 static unsigned char **argnam;
598 
599 static void
600 countnam(struct namnod *n)
601 {
602 	if (n->namval)
603 		namec++;
604 }
605 
606 static void
607 pushnam(struct namnod *n)
608 {
609 	int 	flg = n->namflg;
610 	unsigned char	*p;
611 	unsigned char	*namval;
612 
613 	if (((flg & N_ENVCHG) && (flg & N_EXPORT)) || (flg & N_FUNCTN))
614 		namval = n->namval;
615 	else {
616 		/* Discard Local variable in child process */
617 		if (!(flg & ~N_ENVCHG)) {
618 			n->namflg = 0;
619 			n->namenv = 0;
620 			if (n->namval) {
621 				/* Release for re-use */
622 				free(n->namval);
623 				n->namval = (unsigned char *)NIL;
624 			}
625 		}
626 		namval = n->namenv;
627 	}
628 
629 	if (namval)
630 	{
631 		p = movstrstak(n->namid, staktop);
632 		p = movstrstak("=", p);
633 		p = movstrstak(namval, p);
634 		*argnam++ = getstak(p + 1 - (unsigned char *)(stakbot));
635 	}
636 }
637 
638 unsigned char **
639 local_setenv()
640 {
641 	unsigned char	**er;
642 
643 	namec = 0;
644 	namscan(countnam);
645 
646 	argnam = er = (unsigned char **)getstak(namec * BYTESPERWORD + BYTESPERWORD);
647 	namscan(pushnam);
648 	*argnam++ = 0;
649 	return(er);
650 }
651 
652 struct namnod *
653 findnam(nam)
654 	unsigned char	*nam;
655 {
656 	struct namnod	*nscan = namep;
657 	int		LR;
658 
659 	if (!chkid(nam))
660 		return(0);
661 	while (nscan)
662 	{
663 		if ((LR = cf(nam, nscan->namid)) == 0)
664 			return(nscan);
665 		else if (LR < 0)
666 			nscan = nscan->namlft;
667 		else
668 			nscan = nscan->namrgt;
669 	}
670 	return(0);
671 }
672 
673 void
674 unset_name(unsigned char 	*name)
675 {
676 	struct namnod	*n;
677 	unsigned char 	call_dolocale = 0;
678 
679 	if (n = findnam(name))
680 	{
681 		if (n->namflg & N_RDONLY)
682 			failed(name, wtfailed);
683 
684 		if (n == &pathnod ||
685 		    n == &ifsnod ||
686 		    n == &ps1nod ||
687 		    n == &ps2nod ||
688 		    n == &mchknod)
689 		{
690 			failed(name, badunset);
691 		}
692 
693 #ifndef RES
694 
695 		if ((flags & rshflg) && eq(name, "SHELL"))
696 			failed(name, restricted);
697 
698 #endif
699 
700 		if (n->namflg & N_FUNCTN)
701 		{
702 			func_unhash(name);
703 			freefunc(n);
704 		}
705 		else
706 		{
707 			call_dolocale++;
708 			free(n->namval);
709 			free(n->namenv);
710 		}
711 
712 		n->namval = n->namenv = 0;
713 		n->namflg = N_DEFAULT;
714 
715 		if (call_dolocale)
716 			dolocale(name);
717 
718 		if (flags & prompt)
719 		{
720 			if (n == &mailpnod)
721 				setmail(mailnod.namval);
722 			else if (n == &mailnod && mailpnod.namflg == N_DEFAULT)
723 				setmail(0);
724 		}
725 	}
726 }
727 
728 /*
729  * The environment variables which affect locale.
730  * Note: if all names in this list do not begin with 'L',
731  * you MUST modify dolocale().  Also, be sure that the
732  * fake_env has the same number of elements as localevar.
733  */
734 static char *localevar[] = {
735 	"LC_ALL",
736 	"LC_CTYPE",
737 	"LC_MESSAGES",
738 	"LANG",
739 	0
740 };
741 
742 static char *fake_env[] = {
743 	0,
744 	0,
745 	0,
746 	0,
747 	0
748 };
749 
750 /*
751  * If name is one of several special variables which affect the locale,
752  * do a setlocale().
753  */
754 static void
755 dolocale(nm)
756 	char *nm;
757 {
758 	char **real_env;
759 	struct namnod *n;
760 	int lv, fe;
761 	int i;
762 
763 	/*
764 	 * Take advantage of fact that names of these vars all start
765 	 * with 'L' to avoid unnecessary work.
766 	 * Do locale processing only if /usr is mounted.
767 	 */
768 	if ((*nm != 'L') || !localedir_exists ||
769 	    (!(eq(nm, "LC_ALL") || eq(nm, "LC_CTYPE") ||
770 	    eq(nm, "LANG") || eq(nm, "LC_MESSAGES"))))
771 		return;
772 
773 	/*
774 	 * setlocale() has all the smarts built into it, but
775 	 * it works by examining the environment.  Unfortunately,
776 	 * when you set an environment variable, the shell does
777 	 * not modify its own environment; it just remembers that the
778 	 * variable needs to be exported to any children.  We hack around
779 	 * this by consing up a fake environment for the use of setlocale()
780 	 * and substituting it for the real env before calling setlocale().
781 	 */
782 
783 	/*
784 	 * Build the fake environment.
785 	 * Look up the value of each of the special environment
786 	 * variables, and put their value into the fake environment,
787 	 * if they are exported.
788 	 */
789 	for (lv = 0, fe = 0; localevar[lv]; lv++) {
790 		if ((n = findnam(localevar[lv]))) {
791 			char *p, *q;
792 
793 			if (!n->namval)
794 				continue;
795 
796 			fake_env[fe++] = p = alloc(length(localevar[lv])
797 					       + length(n->namval) + 2);
798 			/* copy name */
799 			q = localevar[lv];
800 			while (*q)
801 				*p++ = *q++;
802 
803 			*p++ = '=';
804 
805 			/* copy value */
806 			q = (char*)(n->namval);
807 			while (*q)
808 				*p++ = *q++;
809 			*p++ = '\0';
810 		}
811 	}
812 	fake_env[fe] = (char *)0;
813 
814 	/*
815 	 * Switch fake env for real and call setlocale().
816 	 */
817 	real_env = (char **)environ;
818 	environ = (unsigned char **)fake_env;
819 
820 	if (setlocale(LC_ALL, "") == NULL)
821 		prs(_gettext(badlocale));
822 
823 	/*
824 	 * Switch back and tear down the fake env.
825 	 */
826 	environ = (unsigned char **)real_env;
827 	for (i = 0; i < fe; i++) {
828 		free(fake_env[i]);
829 		fake_env[i] = (char *)0;
830 	}
831 }
832