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