xref: /illumos-gate/usr/src/cmd/sh/expand.c (revision 797f979d1fe26bfb1cdeb3e7a86ed24c0b654200)
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  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1995 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 
36 #include	"defs.h"
37 #include	<sys/types.h>
38 #include	<sys/stat.h>
39 #include	<dirent.h>
40 
41 
42 
43 /*
44  * globals (file name generation)
45  *
46  * "*" in params matches r.e ".*"
47  * "?" in params matches r.e. "."
48  * "[...]" in params matches character class
49  * "[...a-z...]" in params matches a through z.
50  *
51  */
52 static void addg(unsigned char *, unsigned char *, unsigned char *,
53     unsigned char *);
54 void makearg(struct argnod *);
55 
56 int
57 expand(unsigned char	*as, int rcnt)
58 {
59 	int	count;
60 	DIR	*dirf;
61 	BOOL	dir = 0;
62 	unsigned char	*rescan = 0;
63 	unsigned char 	*slashsav = 0;
64 	unsigned char	*s, *cs;
65 	unsigned char *s2 = 0;
66 	struct argnod	*schain = gchain;
67 	BOOL	slash;
68 	int	len;
69 	wchar_t	wc;
70 
71 	if (trapnote & SIGSET)
72 		return (0);
73 	s = cs = as;
74 	/*
75 	 * check for meta chars
76 	 */
77 	{
78 		BOOL open;
79 
80 		slash = 0;
81 		open = 0;
82 		do
83 		{
84 			if ((len = mbtowc(&wc, (char *)cs, MB_LEN_MAX)) <= 0) {
85 				len = 1;
86 				wc = (unsigned char)*cs;
87 			}
88 
89 			cs += len;
90 			switch (wc) {
91 			case 0:
92 				if (rcnt && slash)
93 					break;
94 				else
95 					return (0);
96 
97 			case '/':
98 				slash++;
99 				open = 0;
100 				continue;
101 
102 			case '[':
103 				open++;
104 				continue;
105 
106 			case ']':
107 				if (open == 0)
108 					continue;
109 
110 			case '?':
111 			case '*':
112 				if (rcnt > slash)
113 					continue;
114 				else
115 					cs--;
116 				break;
117 
118 			case '\\':
119 				cs++;
120 			default:
121 				continue;
122 			}
123 			break;
124 		} while (TRUE);
125 	}
126 
127 	for (;;) {
128 		if (cs == s) {
129 			s = (unsigned char *)nullstr;
130 			break;
131 		} else if (*--cs == '/') {
132 			*cs = 0;
133 			if (s == cs)
134 				s = (unsigned char *)"/";
135 			else {
136 				/*
137 				 * push trimmed copy of directory prefix
138 				 * onto stack
139 				 */
140 				s2 = cpystak(s);
141 				trim(s2);
142 				s = s2;
143 			}
144 			break;
145 		}
146 	}
147 
148 	if ((dirf = opendir(*s ? (char *)s : (char *)".")) != 0)
149 		dir++;
150 
151 	/* Let s point to original string because it will be trimmed later */
152 	if (s2)
153 		s = as;
154 	count = 0;
155 	if (*cs == 0)
156 		slashsav = cs++; /* remember where first slash in as is */
157 
158 	/* check for rescan */
159 	if (dir) {
160 		unsigned char *rs;
161 		struct dirent *e;
162 
163 		rs = cs;
164 		do /* find next / in as */
165 		{
166 			if (*rs == '/') {
167 				rescan = rs;
168 				*rs = 0;
169 				gchain = 0;
170 			}
171 		} while (*rs++);
172 
173 		while ((e = readdir(dirf)) && (trapnote & SIGSET) == 0) {
174 			if (e->d_name[0] == '.' && *cs != '.')
175 				continue;
176 
177 			if (gmatch(e->d_name, cs)) {
178 				addg(s, (unsigned char *)e->d_name, rescan,
179 				    slashsav);
180 				count++;
181 			}
182 		}
183 		(void) closedir(dirf);
184 
185 		if (rescan) {
186 			struct argnod	*rchain;
187 
188 			rchain = gchain;
189 			gchain = schain;
190 			if (count) {
191 				count = 0;
192 				while (rchain) {
193 					count += expand(rchain->argval,
194 							slash + 1);
195 					rchain = rchain->argnxt;
196 				}
197 			}
198 			*rescan = '/';
199 		}
200 	}
201 
202 	if (slashsav)
203 		*slashsav = '/';
204 	return (count);
205 }
206 
207 static void
208 addg(unsigned char *as1, unsigned char *as2, unsigned char *as3,
209     unsigned char *as4)
210 {
211 	unsigned char	*s1, *s2;
212 	int	c;
213 	int		len;
214 	wchar_t		wc;
215 
216 	s2 = locstak() + BYTESPERWORD;
217 	s1 = as1;
218 	if (as4) {
219 		while (c = *s1++) {
220 			if (s2 >= brkend)
221 				growstak(s2);
222 			*s2++ = c;
223 		}
224 		/*
225 		 * Restore first slash before the first metacharacter
226 		 * if as1 is not "/"
227 		 */
228 		if (as4 + 1 == s1) {
229 			if (s2 >= brkend)
230 				growstak(s2);
231 			*s2++ = '/';
232 		}
233 	}
234 /* add matched entries, plus extra \\ to escape \\'s */
235 	s1 = as2;
236 	for (;;) {
237 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
238 			len = 1;
239 			wc = (unsigned char)*s1;
240 		}
241 		if (s2 >= brkend)
242 			growstak(s2);
243 
244 		if (wc == 0) {
245 			*s2 = *s1++;
246 			break;
247 		}
248 
249 		if (wc == '\\') {
250 			*s2++ = '\\';
251 			if (s2 >= brkend)
252 				growstak(s2);
253 			*s2++ = '\\';
254 			s1++;
255 			continue;
256 		}
257 		if ((s2 + len) >= brkend)
258 			growstak(s2 + len);
259 		memcpy(s2, s1, len);
260 		s2 += len;
261 		s1 += len;
262 	}
263 	if (s1 = as3) {
264 		if (s2 >= brkend)
265 			growstak(s2);
266 		*s2++ = '/';
267 		do
268 		{
269 			if (s2 >= brkend)
270 				growstak(s2);
271 		}
272 		while (*s2++ = *++s1);
273 	}
274 	makearg((struct argnod *)endstak(s2));
275 }
276 
277 void
278 makearg(struct argnod *args)
279 {
280 	args->argnxt = gchain;
281 	gchain = args;
282 }
283