xref: /illumos-gate/usr/src/cmd/sh/expand.c (revision 7c478bd9)
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 int	addg();
53 
54 expand(as, rcnt)
55 	unsigned char	*as;
56 {
57 	int	count;
58 	DIR	*dirf;
59 	BOOL	dir = 0;
60 	unsigned char	*rescan = 0;
61 	unsigned char 	*slashsav = 0;
62 	register unsigned char	*s, *cs;
63 	unsigned char *s2 = 0;
64 	struct argnod	*schain = gchain;
65 	BOOL	slash;
66 	int	len;
67 	wchar_t	wc;
68 
69 	if (trapnote & SIGSET)
70 		return (0);
71 	s = cs = as;
72 	/*
73 	 * check for meta chars
74 	 */
75 	{
76 		register BOOL open;
77 
78 		slash = 0;
79 		open = 0;
80 		do
81 		{
82 			if ((len = mbtowc(&wc, (char *)cs, MB_LEN_MAX)) <= 0) {
83 				len = 1;
84 				wc = (unsigned char)*cs;
85 			}
86 
87 			cs += len;
88 			switch (wc) {
89 			case 0:
90 				if (rcnt && slash)
91 					break;
92 				else
93 					return (0);
94 
95 			case '/':
96 				slash++;
97 				open = 0;
98 				continue;
99 
100 			case '[':
101 				open++;
102 				continue;
103 
104 			case ']':
105 				if (open == 0)
106 					continue;
107 
108 			case '?':
109 			case '*':
110 				if (rcnt > slash)
111 					continue;
112 				else
113 					cs--;
114 				break;
115 
116 			case '\\':
117 				cs++;
118 			default:
119 				continue;
120 			}
121 			break;
122 		} while (TRUE);
123 	}
124 
125 	for (;;)
126 	{
127 		if (cs == s)
128 		{
129 			s = (unsigned char *)nullstr;
130 			break;
131 		} else if (*--cs == '/')
132 		{
133 			*cs = 0;
134 			if (s == cs)
135 				s = (unsigned char *)"/";
136 			else {
137 				/*
138 				 * push trimmed copy of directory prefix
139 				 * onto stack
140 				 */
141 				s2 = cpystak(s);
142 				trim(s2);
143 				s = s2;
144 			}
145 			break;
146 		}
147 	}
148 
149 	if ((dirf = opendir(*s ? (char *)s : (char *)".")) != 0)
150 		dir++;
151 
152 	/* Let s point to original string because it will be trimmed later */
153 	if (s2)
154 		s = as;
155 	count = 0;
156 	if (*cs == 0)
157 		slashsav = cs++; /* remember where first slash in as is */
158 
159 	/* check for rescan */
160 	if (dir)
161 	{
162 		register unsigned char *rs;
163 		struct dirent *e;
164 
165 		rs = cs;
166 		do /* find next / in as */
167 		{
168 			if (*rs == '/')
169 			{
170 				rescan = rs;
171 				*rs = 0;
172 				gchain = 0;
173 			}
174 		} while (*rs++);
175 
176 		while ((e = readdir(dirf)) && (trapnote & SIGSET) == 0)
177 		{
178 			if (e->d_name[0] == '.' && *cs != '.')
179 				continue;
180 
181 			if (gmatch(e->d_name, cs))
182 			{
183 				addg(s, e->d_name, rescan, slashsav);
184 				count++;
185 			}
186 		}
187 		(void) closedir(dirf);
188 
189 		if (rescan)
190 		{
191 			register struct argnod	*rchain;
192 
193 			rchain = gchain;
194 			gchain = schain;
195 			if (count)
196 			{
197 				count = 0;
198 				while (rchain)
199 				{
200 					count += expand(rchain->argval,
201 							slash + 1);
202 					rchain = rchain->argnxt;
203 				}
204 			}
205 			*rescan = '/';
206 		}
207 	}
208 
209 	if (slashsav)
210 		*slashsav = '/';
211 	return (count);
212 }
213 
214 static int
215 addg(as1, as2, as3, as4)
216 unsigned char	*as1, *as2, *as3, *as4;
217 {
218 	register unsigned char	*s1, *s2;
219 	register int	c;
220 	int		len;
221 	wchar_t		wc;
222 
223 	s2 = locstak() + BYTESPERWORD;
224 	s1 = as1;
225 	if (as4) {
226 		while (c = *s1++)
227 		{
228 			if (s2 >= brkend)
229 				growstak(s2);
230 			*s2++ = c;
231 		}
232 		/*
233 		 * Restore first slash before the first metacharacter
234 		 * if as1 is not "/"
235 		 */
236 		if (as4 + 1 == s1) {
237 			if (s2 >= brkend)
238 				growstak(s2);
239 			*s2++ = '/';
240 		}
241 	}
242 /* add matched entries, plus extra \\ to escape \\'s */
243 	s1 = as2;
244 	for (;;)
245 	{
246 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
247 			len = 1;
248 			wc = (unsigned char)*s1;
249 		}
250 		if (s2 >= brkend)
251 			growstak(s2);
252 
253 		if (wc == 0) {
254 			*s2 = *s1++;
255 			break;
256 		}
257 
258 		if (wc == '\\') {
259 			*s2++ = '\\';
260 			if (s2 >= brkend)
261 				growstak(s2);
262 			*s2++ = '\\';
263 			s1++;
264 			continue;
265 		}
266 		if ((s2 + len) >= brkend)
267 			growstak(s2 + len);
268 		memcpy(s2, s1, len);
269 		s2 += len;
270 		s1 += len;
271 	}
272 	if (s1 = as3)
273 	{
274 		if (s2 >= brkend)
275 			growstak(s2);
276 		*s2++ = '/';
277 		do
278 		{
279 			if (s2 >= brkend)
280 				growstak(s2);
281 		}
282 		while (*s2++ = *++s1);
283 	}
284 	makearg(endstak(s2));
285 }
286 
287 makearg(args)
288 	register struct argnod *args;
289 {
290 	args->argnxt = gchain;
291 	gchain = args;
292 }
293