xref: /illumos-gate/usr/src/cmd/sh/expand.c (revision 965005c81e0f731867d47892b9fb677030b102df)
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 	{
129 		if (cs == s)
130 		{
131 			s = (unsigned char *)nullstr;
132 			break;
133 		} else if (*--cs == '/')
134 		{
135 			*cs = 0;
136 			if (s == cs)
137 				s = (unsigned char *)"/";
138 			else {
139 				/*
140 				 * push trimmed copy of directory prefix
141 				 * onto stack
142 				 */
143 				s2 = cpystak(s);
144 				trim(s2);
145 				s = s2;
146 			}
147 			break;
148 		}
149 	}
150 
151 	if ((dirf = opendir(*s ? (char *)s : (char *)".")) != 0)
152 		dir++;
153 
154 	/* Let s point to original string because it will be trimmed later */
155 	if (s2)
156 		s = as;
157 	count = 0;
158 	if (*cs == 0)
159 		slashsav = cs++; /* remember where first slash in as is */
160 
161 	/* check for rescan */
162 	if (dir)
163 	{
164 		unsigned char *rs;
165 		struct dirent *e;
166 
167 		rs = cs;
168 		do /* find next / in as */
169 		{
170 			if (*rs == '/')
171 			{
172 				rescan = rs;
173 				*rs = 0;
174 				gchain = 0;
175 			}
176 		} while (*rs++);
177 
178 		while ((e = readdir(dirf)) && (trapnote & SIGSET) == 0)
179 		{
180 			if (e->d_name[0] == '.' && *cs != '.')
181 				continue;
182 
183 			if (gmatch(e->d_name, cs))
184 			{
185 				addg(s, (unsigned char *)e->d_name, rescan,
186 				    slashsav);
187 				count++;
188 			}
189 		}
190 		(void) closedir(dirf);
191 
192 		if (rescan)
193 		{
194 			struct argnod	*rchain;
195 
196 			rchain = gchain;
197 			gchain = schain;
198 			if (count)
199 			{
200 				count = 0;
201 				while (rchain)
202 				{
203 					count += expand(rchain->argval,
204 							slash + 1);
205 					rchain = rchain->argnxt;
206 				}
207 			}
208 			*rescan = '/';
209 		}
210 	}
211 
212 	if (slashsav)
213 		*slashsav = '/';
214 	return (count);
215 }
216 
217 static void
218 addg(unsigned char *as1, unsigned char *as2, unsigned char *as3,
219     unsigned char *as4)
220 {
221 	unsigned char	*s1, *s2;
222 	int	c;
223 	int		len;
224 	wchar_t		wc;
225 
226 	s2 = locstak() + BYTESPERWORD;
227 	s1 = as1;
228 	if (as4) {
229 		while (c = *s1++)
230 		{
231 			if (s2 >= brkend)
232 				growstak(s2);
233 			*s2++ = c;
234 		}
235 		/*
236 		 * Restore first slash before the first metacharacter
237 		 * if as1 is not "/"
238 		 */
239 		if (as4 + 1 == s1) {
240 			if (s2 >= brkend)
241 				growstak(s2);
242 			*s2++ = '/';
243 		}
244 	}
245 /* add matched entries, plus extra \\ to escape \\'s */
246 	s1 = as2;
247 	for (;;)
248 	{
249 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
250 			len = 1;
251 			wc = (unsigned char)*s1;
252 		}
253 		if (s2 >= brkend)
254 			growstak(s2);
255 
256 		if (wc == 0) {
257 			*s2 = *s1++;
258 			break;
259 		}
260 
261 		if (wc == '\\') {
262 			*s2++ = '\\';
263 			if (s2 >= brkend)
264 				growstak(s2);
265 			*s2++ = '\\';
266 			s1++;
267 			continue;
268 		}
269 		if ((s2 + len) >= brkend)
270 			growstak(s2 + len);
271 		memcpy(s2, s1, len);
272 		s2 += len;
273 		s1 += len;
274 	}
275 	if (s1 = as3)
276 	{
277 		if (s2 >= brkend)
278 			growstak(s2);
279 		*s2++ = '/';
280 		do
281 		{
282 			if (s2 >= brkend)
283 				growstak(s2);
284 		}
285 		while (*s2++ = *++s1);
286 	}
287 	makearg((struct argnod *)endstak(s2));
288 }
289 
290 void
291 makearg(struct argnod *args)
292 {
293 	args->argnxt = gchain;
294 	gchain = args;
295 }
296