|
1 //glob.c - filename generation |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1992-1997 Paul Falstad |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 |
|
32 #include "zsh.mdh" |
|
33 #include "glob.pro" |
|
34 |
|
35 #ifdef __SYMBIAN32__ |
|
36 #ifdef __WINSCW__ |
|
37 #pragma warn_unusedarg off |
|
38 #pragma warn_possunwant off |
|
39 #endif//__WINSCW__ |
|
40 #endif//__SYMBIAN32__ |
|
41 |
|
42 |
|
43 #if defined(OFF_T_IS_64_BIT) && defined(__GNUC__) |
|
44 # define ALIGN64 __attribute__((aligned(8))) |
|
45 #else |
|
46 # define ALIGN64 |
|
47 #endif |
|
48 |
|
49 /* flag for CSHNULLGLOB */ |
|
50 |
|
51 typedef struct gmatch *Gmatch; |
|
52 |
|
53 struct gmatch { |
|
54 char *name; |
|
55 off_t size ALIGN64; |
|
56 long atime; |
|
57 long mtime; |
|
58 long ctime; |
|
59 long links; |
|
60 off_t _size ALIGN64; |
|
61 long _atime; |
|
62 long _mtime; |
|
63 long _ctime; |
|
64 long _links; |
|
65 }; |
|
66 |
|
67 #define GS_NAME 1 |
|
68 #define GS_DEPTH 2 |
|
69 #define GS_SIZE 4 |
|
70 #define GS_ATIME 8 |
|
71 #define GS_MTIME 16 |
|
72 #define GS_CTIME 32 |
|
73 #define GS_LINKS 64 |
|
74 |
|
75 #define GS_SHIFT 5 |
|
76 #define GS__SIZE (GS_SIZE << GS_SHIFT) |
|
77 #define GS__ATIME (GS_ATIME << GS_SHIFT) |
|
78 #define GS__MTIME (GS_MTIME << GS_SHIFT) |
|
79 #define GS__CTIME (GS_CTIME << GS_SHIFT) |
|
80 #define GS__LINKS (GS_LINKS << GS_SHIFT) |
|
81 |
|
82 #define GS_DESC 4096 |
|
83 |
|
84 #define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS) |
|
85 #define GS_LINKED (GS_NORMAL << GS_SHIFT) |
|
86 |
|
87 /**/ |
|
88 int badcshglob; |
|
89 |
|
90 /**/ |
|
91 int pathpos; /* position in pathbuf (needed by pattern code) */ |
|
92 |
|
93 /**/ |
|
94 char *pathbuf; /* pathname buffer (needed by pattern code) */ |
|
95 |
|
96 typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */ |
|
97 |
|
98 /* modifier for unit conversions */ |
|
99 |
|
100 #define TT_DAYS 0 |
|
101 #define TT_HOURS 1 |
|
102 #define TT_MINS 2 |
|
103 #define TT_WEEKS 3 |
|
104 #define TT_MONTHS 4 |
|
105 #define TT_SECONDS 5 |
|
106 |
|
107 #define TT_BYTES 0 |
|
108 #define TT_POSIX_BLOCKS 1 |
|
109 #define TT_KILOBYTES 2 |
|
110 #define TT_MEGABYTES 3 |
|
111 |
|
112 |
|
113 typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *)); |
|
114 |
|
115 struct qual { |
|
116 struct qual *next; /* Next qualifier, must match */ |
|
117 struct qual *or; /* Alternative set of qualifiers to match */ |
|
118 TestMatchFunc func; /* Function to call to test match */ |
|
119 off_t data ALIGN64; /* Argument passed to function */ |
|
120 int sense; /* Whether asserting or negating */ |
|
121 int amc; /* Flag for which time to test (a, m, c) */ |
|
122 int range; /* Whether to test <, > or = (as per signum) */ |
|
123 int units; /* Multiplier for time or size, respectively */ |
|
124 char *sdata; /* currently only: expression to eval */ |
|
125 }; |
|
126 |
|
127 /* Prefix, suffix for doing zle trickery */ |
|
128 |
|
129 /**/ |
|
130 mod_export char *glob_pre, *glob_suf; |
|
131 |
|
132 /* struct to easily save/restore current state */ |
|
133 |
|
134 struct globdata { |
|
135 int gd_pathpos; |
|
136 char *gd_pathbuf; |
|
137 |
|
138 int gd_matchsz; /* size of matchbuf */ |
|
139 int gd_matchct; /* number of matches found */ |
|
140 int gd_pathbufsz; /* size of pathbuf */ |
|
141 int gd_pathbufcwd; /* where did we chdir()'ed */ |
|
142 Gmatch gd_matchbuf; /* array of matches */ |
|
143 Gmatch gd_matchptr; /* &matchbuf[matchct] */ |
|
144 char *gd_colonmod; /* colon modifiers in qualifier list */ |
|
145 |
|
146 /* Qualifiers pertaining to current pattern */ |
|
147 struct qual *gd_quals; |
|
148 |
|
149 /* Other state values for current pattern */ |
|
150 int gd_qualct, gd_qualorct; |
|
151 int gd_range, gd_amc, gd_units; |
|
152 int gd_gf_nullglob, gd_gf_markdirs, gd_gf_noglobdots, gd_gf_listtypes; |
|
153 int gd_gf_numsort; |
|
154 int gd_gf_follow, gd_gf_sorts, gd_gf_nsorts, gd_gf_sortlist[11]; |
|
155 |
|
156 char *gd_glob_pre, *gd_glob_suf; |
|
157 }; |
|
158 |
|
159 /* The variable with the current globbing state and convenience macros */ |
|
160 |
|
161 static struct globdata curglobdata; |
|
162 |
|
163 #define matchsz (curglobdata.gd_matchsz) |
|
164 #define matchct (curglobdata.gd_matchct) |
|
165 #define pathbufsz (curglobdata.gd_pathbufsz) |
|
166 #define pathbufcwd (curglobdata.gd_pathbufcwd) |
|
167 #define matchbuf (curglobdata.gd_matchbuf) |
|
168 #define matchptr (curglobdata.gd_matchptr) |
|
169 #define colonmod (curglobdata.gd_colonmod) |
|
170 #define quals (curglobdata.gd_quals) |
|
171 #define qualct (curglobdata.gd_qualct) |
|
172 #define qualorct (curglobdata.gd_qualorct) |
|
173 #define g_range (curglobdata.gd_range) |
|
174 #define g_amc (curglobdata.gd_amc) |
|
175 #define g_units (curglobdata.gd_units) |
|
176 #define gf_nullglob (curglobdata.gd_gf_nullglob) |
|
177 #define gf_markdirs (curglobdata.gd_gf_markdirs) |
|
178 #define gf_noglobdots (curglobdata.gd_gf_noglobdots) |
|
179 #define gf_listtypes (curglobdata.gd_gf_listtypes) |
|
180 #define gf_numsort (curglobdata.gd_gf_numsort) |
|
181 #define gf_follow (curglobdata.gd_gf_follow) |
|
182 #define gf_sorts (curglobdata.gd_gf_sorts) |
|
183 #define gf_nsorts (curglobdata.gd_gf_nsorts) |
|
184 #define gf_sortlist (curglobdata.gd_gf_sortlist) |
|
185 |
|
186 /* and macros for save/restore */ |
|
187 |
|
188 #define save_globstate(N) \ |
|
189 do { \ |
|
190 memcpy(&(N), &curglobdata, sizeof(struct globdata)); \ |
|
191 (N).gd_pathpos = pathpos; \ |
|
192 (N).gd_pathbuf = pathbuf; \ |
|
193 (N).gd_pathbufsz = 0; \ |
|
194 (N).gd_glob_pre = glob_pre; \ |
|
195 (N).gd_glob_suf = glob_suf; \ |
|
196 pathbuf = NULL; \ |
|
197 } while (0) |
|
198 |
|
199 #define restore_globstate(N) \ |
|
200 do { \ |
|
201 zfree(pathbuf, pathbufsz); \ |
|
202 memcpy(&curglobdata, &(N), sizeof(struct globdata)); \ |
|
203 pathpos = (N).gd_pathpos; \ |
|
204 pathbuf = (N).gd_pathbuf; \ |
|
205 glob_pre = (N).gd_glob_pre; \ |
|
206 glob_suf = (N).gd_glob_suf; \ |
|
207 } while (0) |
|
208 |
|
209 /* pathname component in filename patterns */ |
|
210 |
|
211 struct complist { |
|
212 Complist next; |
|
213 Patprog pat; |
|
214 int closure; /* 1 if this is a (foo/)# */ |
|
215 int follow; /* 1 to go thru symlinks */ |
|
216 }; |
|
217 |
|
218 /* Next character after one which may be a Meta (x is any char *) */ |
|
219 #define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) |
|
220 /* |
|
221 * Increment pointer which may be on a Meta (x is a pointer variable), |
|
222 * returning the incremented value (i.e. like pre-increment). |
|
223 */ |
|
224 #define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1) |
|
225 /* |
|
226 * Return unmetafied char from string (x is any char *) |
|
227 */ |
|
228 #define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) |
|
229 |
|
230 /* Add a component to pathbuf: This keeps track of how * |
|
231 * far we are into a file name, since each path component * |
|
232 * must be matched separately. */ |
|
233 |
|
234 /**/ |
|
235 static void |
|
236 addpath(char *s, int l) |
|
237 { |
|
238 DPUTS(!pathbuf, "BUG: pathbuf not initialised"); |
|
239 while (pathpos + l + 1 >= pathbufsz) |
|
240 pathbuf = realloc(pathbuf, pathbufsz *= 2); |
|
241 while (l--) |
|
242 pathbuf[pathpos++] = *s++; |
|
243 pathbuf[pathpos++] = '/'; |
|
244 pathbuf[pathpos] = '\0'; |
|
245 } |
|
246 |
|
247 /* stat the filename s appended to pathbuf. l should be true for lstat, * |
|
248 * false for stat. If st is NULL, the file is only checked for existance. * |
|
249 * s == "" is treated as s == ".". This is necessary since on most systems * |
|
250 * foo/ can be used to reference a non-directory foo. Returns nonzero if * |
|
251 * the file does not exists. */ |
|
252 |
|
253 /**/ |
|
254 static int |
|
255 statfullpath(const char *s, struct stat *st, int l) |
|
256 { |
|
257 char buf[PATH_MAX]; |
|
258 |
|
259 DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX, |
|
260 "BUG: statfullpath(): pathname too long"); |
|
261 strcpy(buf, pathbuf + pathbufcwd); |
|
262 strcpy(buf + pathpos - pathbufcwd, s); |
|
263 if (!*s && *buf) { |
|
264 /* |
|
265 * Don't add the '.' if the path so far is empty, since |
|
266 * then we get bogus empty strings inserted as files. |
|
267 */ |
|
268 buf[pathpos - pathbufcwd] = '.'; |
|
269 buf[pathpos - pathbufcwd + 1] = '\0'; |
|
270 l = 0; |
|
271 } |
|
272 unmetafy(buf, NULL); |
|
273 if (!st) |
|
274 return access(buf, F_OK) && (!l || readlink(buf, NULL, 0)); |
|
275 return l ? lstat(buf, st) : stat(buf, st); |
|
276 } |
|
277 |
|
278 /* This may be set by qualifier functions to an array of strings to insert |
|
279 * into the list instead of the original string. */ |
|
280 |
|
281 char **inserts; |
|
282 |
|
283 /* add a match to the list */ |
|
284 |
|
285 /**/ |
|
286 static void |
|
287 insert(char *s, int checked) |
|
288 { |
|
289 struct stat buf, buf2, *bp; |
|
290 char *news = s; |
|
291 int statted = 0; |
|
292 |
|
293 queue_signals(); |
|
294 inserts = NULL; |
|
295 |
|
296 if (gf_listtypes || gf_markdirs) { |
|
297 /* Add the type marker to the end of the filename */ |
|
298 mode_t mode; |
|
299 checked = statted = 1; |
|
300 if (statfullpath(s, &buf, 1)) { |
|
301 unqueue_signals(); |
|
302 return; |
|
303 } |
|
304 mode = buf.st_mode; |
|
305 if (gf_follow) { |
|
306 if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0)) |
|
307 memcpy(&buf2, &buf, sizeof(buf)); |
|
308 statted |= 2; |
|
309 mode = buf2.st_mode; |
|
310 } |
|
311 if (gf_listtypes || S_ISDIR(mode)) { |
|
312 int ll = strlen(s); |
|
313 |
|
314 news = (char *) hcalloc(ll + 2); |
|
315 strcpy(news, s); |
|
316 news[ll] = file_type(mode); |
|
317 news[ll + 1] = '\0'; |
|
318 } |
|
319 } |
|
320 if (qualct || qualorct) { |
|
321 /* Go through the qualifiers, rejecting the file if appropriate */ |
|
322 struct qual *qo, *qn; |
|
323 |
|
324 if (!statted && statfullpath(s, &buf, 1)) { |
|
325 unqueue_signals(); |
|
326 return; |
|
327 } |
|
328 news = dyncat(pathbuf, news); |
|
329 |
|
330 statted = 1; |
|
331 qo = quals; |
|
332 for (qn = qo; qn && qn->func;) { |
|
333 g_range = qn->range; |
|
334 g_amc = qn->amc; |
|
335 g_units = qn->units; |
|
336 if ((qn->sense & 2) && !(statted & 2)) { |
|
337 /* If (sense & 2), we're following links */ |
|
338 if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0)) |
|
339 memcpy(&buf2, &buf, sizeof(buf)); |
|
340 statted |= 2; |
|
341 } |
|
342 bp = (qn->sense & 2) ? &buf2 : &buf; |
|
343 /* Reject the file if the function returned zero * |
|
344 * and the sense was positive (sense&1 == 0), or * |
|
345 * vice versa. */ |
|
346 if ((!((qn->func) (news, bp, qn->data, qn->sdata)) ^ qn->sense) & 1) { |
|
347 /* Try next alternative, or return if there are no more */ |
|
348 if (!(qo = qo->or)) { |
|
349 unqueue_signals(); |
|
350 return; |
|
351 } |
|
352 qn = qo; |
|
353 continue; |
|
354 } |
|
355 qn = qn->next; |
|
356 } |
|
357 } else if (!checked) { |
|
358 if (statfullpath(s, NULL, 1)) { |
|
359 unqueue_signals(); |
|
360 return; |
|
361 } |
|
362 statted = 1; |
|
363 news = dyncat(pathbuf, news); |
|
364 } else |
|
365 news = dyncat(pathbuf, news); |
|
366 |
|
367 while (!inserts || (news = dupstring(*inserts++))) { |
|
368 if (colonmod) { |
|
369 /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */ |
|
370 s = colonmod; |
|
371 modify(&news, &s); |
|
372 } |
|
373 if (!statted && (gf_sorts & GS_NORMAL)) { |
|
374 statfullpath(s, &buf, 1); |
|
375 statted = 1; |
|
376 } |
|
377 if (!(statted & 2) && (gf_sorts & GS_LINKED)) { |
|
378 if (statted) { |
|
379 if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0)) |
|
380 memcpy(&buf2, &buf, sizeof(buf)); |
|
381 } else if (statfullpath(s, &buf2, 0)) |
|
382 statfullpath(s, &buf2, 1); |
|
383 statted |= 2; |
|
384 } |
|
385 matchptr->name = news; |
|
386 if (statted & 1) { |
|
387 matchptr->size = buf.st_size; |
|
388 matchptr->atime = buf.st_atime; |
|
389 matchptr->mtime = buf.st_mtime; |
|
390 matchptr->ctime = buf.st_ctime; |
|
391 matchptr->links = buf.st_nlink; |
|
392 } |
|
393 if (statted & 2) { |
|
394 matchptr->_size = buf2.st_size; |
|
395 matchptr->_atime = buf2.st_atime; |
|
396 matchptr->_mtime = buf2.st_mtime; |
|
397 matchptr->_ctime = buf2.st_ctime; |
|
398 matchptr->_links = buf2.st_nlink; |
|
399 } |
|
400 matchptr++; |
|
401 |
|
402 if (++matchct == matchsz) { |
|
403 matchbuf = (Gmatch )realloc((char *)matchbuf, |
|
404 sizeof(struct gmatch) * (matchsz *= 2)); |
|
405 |
|
406 matchptr = matchbuf + matchct; |
|
407 } |
|
408 if (!inserts) |
|
409 break; |
|
410 } |
|
411 unqueue_signals(); |
|
412 } |
|
413 |
|
414 /* Check to see if str is eligible for filename generation. */ |
|
415 |
|
416 /**/ |
|
417 mod_export int |
|
418 haswilds(char *str) |
|
419 { |
|
420 /* `[' and `]' are legal even if bad patterns are usually not. */ |
|
421 if ((*str == Inbrack || *str == Outbrack) && !str[1]) |
|
422 return 0; |
|
423 |
|
424 /* If % is immediately followed by ?, then that ? is * |
|
425 * not treated as a wildcard. This is so you don't have * |
|
426 * to escape job references such as %?foo. */ |
|
427 if (str[0] == '%' && str[1] == Quest) |
|
428 str[1] = '?'; |
|
429 |
|
430 for (; *str; str++) { |
|
431 switch (*str) { |
|
432 case Inpar: |
|
433 case Bar: |
|
434 case Star: |
|
435 case Inbrack: |
|
436 case Inang: |
|
437 case Quest: |
|
438 return 1; |
|
439 case Pound: |
|
440 case Hat: |
|
441 if (isset(EXTENDEDGLOB)) |
|
442 return 1; |
|
443 break; |
|
444 } |
|
445 } |
|
446 return 0; |
|
447 } |
|
448 |
|
449 /* Do the globbing: scanner is called recursively * |
|
450 * with successive bits of the path until we've * |
|
451 * tried all of it. */ |
|
452 |
|
453 /**/ |
|
454 static void |
|
455 scanner(Complist q) |
|
456 { |
|
457 Patprog p; |
|
458 int closure; |
|
459 int pbcwdsav = pathbufcwd; |
|
460 int errssofar = errsfound; |
|
461 struct dirsav ds; |
|
462 |
|
463 ds.ino = ds.dev = 0; |
|
464 ds.dirname = NULL; |
|
465 ds.dirfd = ds.level = -1; |
|
466 if (!q) |
|
467 return; |
|
468 |
|
469 if ((closure = q->closure)) { |
|
470 /* (foo/)# - match zero or more dirs */ |
|
471 if (q->closure == 2) /* (foo/)## - match one or more dirs */ |
|
472 q->closure = 1; |
|
473 else |
|
474 scanner(q->next); |
|
475 } |
|
476 p = q->pat; |
|
477 /* Now the actual matching for the current path section. */ |
|
478 if (p->flags & PAT_PURES) { |
|
479 /* |
|
480 * It's a straight string to the end of the path section. |
|
481 */ |
|
482 char *str = (char *)p + p->startoff; |
|
483 int l = p->patmlen; |
|
484 |
|
485 if (l + !l + pathpos - pathbufcwd >= PATH_MAX) { |
|
486 int err; |
|
487 |
|
488 if (l >= PATH_MAX) |
|
489 return; |
|
490 err = lchdir(pathbuf + pathbufcwd, &ds, 0); |
|
491 if (err == -1) |
|
492 return; |
|
493 if (err) { |
|
494 zerr("current directory lost during glob", NULL, 0); |
|
495 return; |
|
496 } |
|
497 pathbufcwd = pathpos; |
|
498 } |
|
499 if (q->next) { |
|
500 /* Not the last path section. Just add it to the path. */ |
|
501 int oppos = pathpos; |
|
502 |
|
503 if (!errflag) { |
|
504 int add = 1; |
|
505 |
|
506 if (q->closure && *pathbuf) { |
|
507 if (!strcmp(str, ".")) |
|
508 add = 0; |
|
509 else if (!strcmp(str, "..")) { |
|
510 struct stat sc, sr; |
|
511 |
|
512 add = (stat("/", &sr) || stat(pathbuf, &sc) || |
|
513 sr.st_ino != sc.st_ino || |
|
514 sr.st_dev != sc.st_dev); |
|
515 } |
|
516 } |
|
517 if (add) { |
|
518 addpath(str, l); |
|
519 if (!closure || !statfullpath("", NULL, 1)) |
|
520 scanner((q->closure) ? q : q->next); |
|
521 pathbuf[pathpos = oppos] = '\0'; |
|
522 } |
|
523 } |
|
524 } else { |
|
525 if (str[l]) |
|
526 str = dupstrpfx(str, l); |
|
527 insert(str, 0); |
|
528 } |
|
529 } else { |
|
530 /* Do pattern matching on current path section. */ |
|
531 char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : "."; |
|
532 int dirs = !!q->next; |
|
533 DIR *lock = opendir(fn); |
|
534 char *subdirs = NULL; |
|
535 int subdirlen = 0; |
|
536 |
|
537 if (lock == NULL) |
|
538 return; |
|
539 while ((fn = zreaddir(lock, 1)) && !errflag) { |
|
540 /* prefix and suffix are zle trickery */ |
|
541 if (!dirs && !colonmod && |
|
542 ((glob_pre && !strpfx(glob_pre, fn)) |
|
543 || (glob_suf && !strsfx(glob_suf, fn)))) |
|
544 continue; |
|
545 errsfound = errssofar; |
|
546 if (pattry(p, fn)) { |
|
547 /* if this name matchs the pattern... */ |
|
548 if (pbcwdsav == pathbufcwd && |
|
549 strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) { |
|
550 int err; |
|
551 |
|
552 DPUTS(pathpos == pathbufcwd, |
|
553 "BUG: filename longer than PATH_MAX"); |
|
554 err = lchdir(pathbuf + pathbufcwd, &ds, 0); |
|
555 if (err == -1) |
|
556 break; |
|
557 if (err) { |
|
558 zerr("current directory lost during glob", NULL, 0); |
|
559 break; |
|
560 } |
|
561 pathbufcwd = pathpos; |
|
562 } |
|
563 if (dirs) { |
|
564 int l; |
|
565 |
|
566 /* |
|
567 * If not the last component in the path: |
|
568 * |
|
569 * If we made an approximation in the new path segment, |
|
570 * then it is possible we made too many errors. For |
|
571 * example, (ab)#(cb)# will match the directory abcb |
|
572 * with one error if allowed to, even though it can |
|
573 * match with none. This will stop later parts of the |
|
574 * path matching, so we need to check by reducing the |
|
575 * maximum number of errors and seeing if the directory |
|
576 * still matches. Luckily, this is not a terribly |
|
577 * common case, since complex patterns typically occur |
|
578 * in the last part of the path which is not affected |
|
579 * by this problem. |
|
580 */ |
|
581 if (errsfound > errssofar) { |
|
582 forceerrs = errsfound - 1; |
|
583 while (forceerrs >= errssofar) { |
|
584 errsfound = errssofar; |
|
585 if (!pattry(p, fn)) |
|
586 break; |
|
587 forceerrs = errsfound - 1; |
|
588 } |
|
589 errsfound = forceerrs + 1; |
|
590 forceerrs = -1; |
|
591 } |
|
592 if (closure) { |
|
593 /* if matching multiple directories */ |
|
594 struct stat buf; |
|
595 |
|
596 if (statfullpath(fn, &buf, !q->follow)) { |
|
597 if (errno != ENOENT && errno != EINTR && |
|
598 errno != ENOTDIR && !errflag) { |
|
599 zwarn("%e: %s", fn, errno); |
|
600 } |
|
601 continue; |
|
602 } |
|
603 if (!S_ISDIR(buf.st_mode)) |
|
604 continue; |
|
605 } |
|
606 l = strlen(fn) + 1; |
|
607 subdirs = hrealloc(subdirs, subdirlen, subdirlen + l |
|
608 + sizeof(int)); |
|
609 strcpy(subdirs + subdirlen, fn); |
|
610 subdirlen += l; |
|
611 /* store the count of errors made so far, too */ |
|
612 memcpy(subdirs + subdirlen, (char *)&errsfound, |
|
613 sizeof(int)); |
|
614 subdirlen += sizeof(int); |
|
615 } else |
|
616 /* if the last filename component, just add it */ |
|
617 insert(fn, 1); |
|
618 } |
|
619 } |
|
620 closedir(lock); |
|
621 if (subdirs) { |
|
622 int oppos = pathpos; |
|
623 |
|
624 for (fn = subdirs; fn < subdirs+subdirlen; ) { |
|
625 int l = strlen(fn); |
|
626 addpath(fn, l); |
|
627 fn += l + 1; |
|
628 memcpy((char *)&errsfound, fn, sizeof(int)); |
|
629 fn += sizeof(int); |
|
630 scanner((q->closure) ? q : q->next); /* scan next level */ |
|
631 pathbuf[pathpos = oppos] = '\0'; |
|
632 } |
|
633 hrealloc(subdirs, subdirlen, 0); |
|
634 } |
|
635 } |
|
636 if (pbcwdsav < pathbufcwd) { |
|
637 if (restoredir(&ds)) |
|
638 zerr("current directory lost during glob", NULL, 0); |
|
639 zsfree(ds.dirname); |
|
640 if (ds.dirfd >= 0) |
|
641 close(ds.dirfd); |
|
642 pathbufcwd = pbcwdsav; |
|
643 } |
|
644 } |
|
645 |
|
646 /* This function tokenizes a zsh glob pattern */ |
|
647 |
|
648 /**/ |
|
649 static Complist |
|
650 parsecomplist(char *instr) |
|
651 { |
|
652 Patprog p1; |
|
653 Complist l1; |
|
654 char *str; |
|
655 int compflags = gf_noglobdots ? (PAT_FILE|PAT_NOGLD) : PAT_FILE; |
|
656 |
|
657 if (instr[0] == Star && instr[1] == Star && |
|
658 (instr[2] == '/' || (instr[2] == Star && instr[3] == '/'))) { |
|
659 /* Match any number of directories. */ |
|
660 int follow; |
|
661 |
|
662 /* with three stars, follow symbolic links */ |
|
663 follow = (instr[2] == Star); |
|
664 instr += (3 + follow); |
|
665 |
|
666 /* Now get the next path component if there is one. */ |
|
667 l1 = (Complist) zhalloc(sizeof *l1); |
|
668 if ((l1->next = parsecomplist(instr)) == NULL) { |
|
669 errflag = 1; |
|
670 return NULL; |
|
671 } |
|
672 l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); |
|
673 l1->closure = 1; /* ...zero or more times. */ |
|
674 l1->follow = follow; |
|
675 return l1; |
|
676 } |
|
677 |
|
678 /* Parse repeated directories such as (dir/)# and (dir/)## */ |
|
679 if (*(str = instr) == Inpar && !skipparens(Inpar, Outpar, (char **)&str) && |
|
680 *str == Pound && isset(EXTENDEDGLOB) && str[-2] == '/') { |
|
681 instr++; |
|
682 if (!(p1 = patcompile(instr, compflags, &instr))) |
|
683 return NULL; |
|
684 if (instr[0] == '/' && instr[1] == Outpar && instr[2] == Pound) { |
|
685 int pdflag = 0; |
|
686 |
|
687 instr += 3; |
|
688 if (*instr == Pound) { |
|
689 pdflag = 1; |
|
690 instr++; |
|
691 } |
|
692 l1 = (Complist) zhalloc(sizeof *l1); |
|
693 l1->pat = p1; |
|
694 l1->closure = 1 + pdflag; |
|
695 l1->follow = 0; |
|
696 l1->next = parsecomplist(instr); |
|
697 return (l1->pat) ? l1 : NULL; |
|
698 } |
|
699 } else { |
|
700 /* parse single path component */ |
|
701 if (!(p1 = patcompile(instr, compflags|PAT_FILET, &instr))) |
|
702 return NULL; |
|
703 /* then do the remaining path components */ |
|
704 if (*instr == '/' || !*instr) { |
|
705 int ef = *instr == '/'; |
|
706 |
|
707 l1 = (Complist) zhalloc(sizeof *l1); |
|
708 l1->pat = p1; |
|
709 l1->closure = 0; |
|
710 l1->next = ef ? parsecomplist(instr+1) : NULL; |
|
711 return (ef && !l1->next) ? NULL : l1; |
|
712 } |
|
713 } |
|
714 errflag = 1; |
|
715 return NULL; |
|
716 } |
|
717 |
|
718 /* turn a string into a Complist struct: this has path components */ |
|
719 |
|
720 /**/ |
|
721 static Complist |
|
722 parsepat(char *str) |
|
723 { |
|
724 long assert; |
|
725 int ignore; |
|
726 |
|
727 patcompstart(); |
|
728 /* |
|
729 * Check for initial globbing flags, so that they don't form |
|
730 * a bogus path component. |
|
731 */ |
|
732 if ((*str == Inpar && str[1] == Pound && isset(EXTENDEDGLOB)) || |
|
733 (isset(KSHGLOB) && *str == '@' && str[1] == Inpar && |
|
734 str[2] == Pound)) { |
|
735 str += (*str == Inpar) ? 2 : 3; |
|
736 if (!patgetglobflags(&str, &assert, &ignore)) |
|
737 return NULL; |
|
738 } |
|
739 |
|
740 /* Now there is no (#X) in front, we can check the path. */ |
|
741 if (!pathbuf) |
|
742 pathbuf = zalloc(pathbufsz = PATH_MAX); |
|
743 DPUTS(pathbufcwd, "BUG: glob changed directory"); |
|
744 if (*str == '/') { /* pattern has absolute path */ |
|
745 str++; |
|
746 pathbuf[0] = '/'; |
|
747 pathbuf[pathpos = 1] = '\0'; |
|
748 } else /* pattern is relative to pwd */ |
|
749 pathbuf[pathpos = 0] = '\0'; |
|
750 |
|
751 return parsecomplist(str); |
|
752 } |
|
753 |
|
754 /* get number after qualifier */ |
|
755 |
|
756 /**/ |
|
757 static off_t |
|
758 qgetnum(char **s) |
|
759 { |
|
760 off_t v = 0; |
|
761 |
|
762 if (!idigit(**s)) { |
|
763 zerr("number expected", NULL, 0); |
|
764 return 0; |
|
765 } |
|
766 while (idigit(**s)) |
|
767 v = v * 10 + *(*s)++ - '0'; |
|
768 return v; |
|
769 } |
|
770 |
|
771 /* get mode spec after qualifier */ |
|
772 |
|
773 /**/ |
|
774 static zlong |
|
775 qgetmodespec(char **s) |
|
776 { |
|
777 zlong yes = 0, no = 0, val, mask, t; |
|
778 char *p = *s, c, how, end; |
|
779 |
|
780 if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' || |
|
781 c == '?' || c == Quest || (c >= '0' && c <= '7')) { |
|
782 end = 0; |
|
783 c = 0; |
|
784 } else { |
|
785 end = (c == '<' ? '>' : |
|
786 (c == '[' ? ']' : |
|
787 (c == '{' ? '}' : |
|
788 (c == Inang ? Outang : |
|
789 (c == Inbrack ? Outbrack : |
|
790 (c == Inbrace ? Outbrace : c)))))); |
|
791 p++; |
|
792 } |
|
793 do { |
|
794 mask = 0; |
|
795 while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) { |
|
796 switch (c) { |
|
797 case 'o': mask |= 01007; break; |
|
798 case 'g': mask |= 02070; break; |
|
799 case 'u': mask |= 04700; break; |
|
800 case 'a': mask |= 07777; break; |
|
801 } |
|
802 p++; |
|
803 } |
|
804 how = ((c == '+' || c == '-') ? c : '='); |
|
805 if (c == '+' || c == '-' || c == '=' || c == Equals) |
|
806 p++; |
|
807 val = 0; |
|
808 if (mask) { |
|
809 while ((c = *p++) != ',' && c != end) { |
|
810 switch (c) { |
|
811 case 'x': val |= 00111; break; |
|
812 case 'w': val |= 00222; break; |
|
813 case 'r': val |= 00444; break; |
|
814 case 's': val |= 06000; break; |
|
815 case 't': val |= 01000; break; |
|
816 case '0': case '1': case '2': case '3': |
|
817 case '4': case '5': case '6': case '7': |
|
818 t = ((zlong) c - '0'); |
|
819 val |= t | (t << 3) | (t << 6); |
|
820 break; |
|
821 default: |
|
822 zerr("invalid mode specification", NULL, 0); |
|
823 return 0; |
|
824 } |
|
825 } |
|
826 if (how == '=' || how == '+') { |
|
827 yes |= val & mask; |
|
828 val = ~val; |
|
829 } |
|
830 if (how == '=' || how == '-') |
|
831 no |= val & mask; |
|
832 } else if (!(end && c == end) && c != ',' && c) { |
|
833 t = 07777; |
|
834 while ((c = *p) == '?' || c == Quest || |
|
835 (c >= '0' && c <= '7')) { |
|
836 if (c == '?' || c == Quest) { |
|
837 t = (t << 3) | 7; |
|
838 val <<= 3; |
|
839 } else { |
|
840 t <<= 3; |
|
841 val = (val << 3) | ((zlong) c - '0'); |
|
842 } |
|
843 p++; |
|
844 } |
|
845 if (end && c != end && c != ',') { |
|
846 zerr("invalid mode specification", NULL, 0); |
|
847 return 0; |
|
848 } |
|
849 if (how == '=') { |
|
850 yes = (yes & ~t) | val; |
|
851 no = (no & ~t) | (~val & ~t); |
|
852 } else if (how == '+') |
|
853 yes |= val; |
|
854 else |
|
855 no |= val; |
|
856 } else { |
|
857 zerr("invalid mode specification", NULL, 0); |
|
858 return 0; |
|
859 } |
|
860 } while (end && c != end); |
|
861 |
|
862 *s = p; |
|
863 return ((yes & 07777) | ((no & 07777) << 12)); |
|
864 } |
|
865 |
|
866 static int |
|
867 gmatchcmp(Gmatch a, Gmatch b) |
|
868 { |
|
869 int i, *s; |
|
870 off_t r = 0L; |
|
871 |
|
872 for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { |
|
873 switch (*s & ~GS_DESC) { |
|
874 case GS_NAME: |
|
875 if (gf_numsort) |
|
876 r = nstrpcmp(&b->name, &a->name); |
|
877 else |
|
878 r = strpcmp(&b->name, &a->name); |
|
879 break; |
|
880 case GS_DEPTH: |
|
881 { |
|
882 char *aptr = a->name, *bptr = b->name; |
|
883 int slasha = 0, slashb = 0; |
|
884 /* Count slashes. Trailing slashes don't count. */ |
|
885 while (*aptr && *aptr == *bptr) |
|
886 aptr++, bptr++; |
|
887 if (*aptr) |
|
888 for (; aptr[1]; aptr++) |
|
889 if (*aptr == '/') { |
|
890 slasha = 1; |
|
891 break; |
|
892 } |
|
893 if (*bptr) |
|
894 for (; bptr[1]; bptr++) |
|
895 if (*bptr == '/') { |
|
896 slashb = 1; |
|
897 break; |
|
898 } |
|
899 r = slasha - slashb; |
|
900 } |
|
901 break; |
|
902 case GS_SIZE: |
|
903 r = b->size - a->size; |
|
904 break; |
|
905 case GS_ATIME: |
|
906 r = a->atime - b->atime; |
|
907 break; |
|
908 case GS_MTIME: |
|
909 r = a->mtime - b->mtime; |
|
910 break; |
|
911 case GS_CTIME: |
|
912 r = a->ctime - b->ctime; |
|
913 break; |
|
914 case GS_LINKS: |
|
915 r = b->links - a->links; |
|
916 break; |
|
917 case GS__SIZE: |
|
918 r = b->_size - a->_size; |
|
919 break; |
|
920 case GS__ATIME: |
|
921 r = a->_atime - b->_atime; |
|
922 break; |
|
923 case GS__MTIME: |
|
924 r = a->_mtime - b->_mtime; |
|
925 break; |
|
926 case GS__CTIME: |
|
927 r = a->_ctime - b->_ctime; |
|
928 break; |
|
929 case GS__LINKS: |
|
930 r = b->_links - a->_links; |
|
931 break; |
|
932 } |
|
933 if (r) |
|
934 return (int) ((*s & GS_DESC) ? -r : r); |
|
935 } |
|
936 return 0; |
|
937 } |
|
938 |
|
939 /* |
|
940 * Duplicate a list of qualifiers using the `next' linkage (not the |
|
941 * `or' linkage). Return the head element and set *last (if last non-NULL) |
|
942 * to point to the last element of the new list. All allocation is on the |
|
943 * heap (or off the heap?) |
|
944 */ |
|
945 static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp) |
|
946 { |
|
947 struct qual *qfirst = NULL, *qlast = NULL; |
|
948 |
|
949 while (orig) { |
|
950 struct qual *qnew = (struct qual *)zhalloc(sizeof(struct qual)); |
|
951 *qnew = *orig; |
|
952 qnew->next = qnew->or = NULL; |
|
953 |
|
954 if (!qfirst) |
|
955 qfirst = qnew; |
|
956 if (qlast) |
|
957 qlast->next = qnew; |
|
958 qlast = qnew; |
|
959 |
|
960 orig = orig->next; |
|
961 } |
|
962 |
|
963 if (lastp) |
|
964 *lastp = qlast; |
|
965 return qfirst; |
|
966 } |
|
967 |
|
968 /* Main entry point to the globbing code for filename globbing. * |
|
969 * np points to a node in the list list which will be expanded * |
|
970 * into a series of nodes. */ |
|
971 |
|
972 /**/ |
|
973 void |
|
974 zglob(LinkList list, LinkNode np, int nountok) |
|
975 { |
|
976 struct qual *qo, *qn, *ql; |
|
977 LinkNode node = prevnode(np); |
|
978 char *str; /* the pattern */ |
|
979 int sl; /* length of the pattern */ |
|
980 Complist q; /* pattern after parsing */ |
|
981 char *ostr = (char *)getdata(np); /* the pattern before the parser */ |
|
982 /* chops it up */ |
|
983 int first = 0, end = -1; /* index of first match to return */ |
|
984 /* and index+1 of the last match */ |
|
985 struct globdata saved; /* saved glob state */ |
|
986 int nobareglob = !isset(BAREGLOBQUAL); |
|
987 |
|
988 if (unset(GLOBOPT) || !haswilds(ostr)) { |
|
989 if (!nountok) |
|
990 untokenize(ostr); |
|
991 return; |
|
992 } |
|
993 save_globstate(saved); |
|
994 |
|
995 str = dupstring(ostr); |
|
996 uremnode(list, np); |
|
997 |
|
998 /* quals will hold the complete list of qualifiers (file static). */ |
|
999 quals = NULL; |
|
1000 /* |
|
1001 * qualct and qualorct indicate we have qualifiers in the last |
|
1002 * alternative, or a set of alternatives, respectively. They |
|
1003 * are not necessarily an accurate count, however. |
|
1004 */ |
|
1005 qualct = qualorct = 0; |
|
1006 /* |
|
1007 * colonmod is a concatenated list of all colon modifiers found in |
|
1008 * all sets of qualifiers. |
|
1009 */ |
|
1010 colonmod = NULL; |
|
1011 /* The gf_* flags are qualifiers which are applied globally. */ |
|
1012 gf_nullglob = isset(NULLGLOB); |
|
1013 gf_markdirs = isset(MARKDIRS); |
|
1014 gf_listtypes = gf_follow = 0; |
|
1015 gf_noglobdots = unset(GLOBDOTS); |
|
1016 gf_numsort = isset(NUMERICGLOBSORT); |
|
1017 gf_sorts = gf_nsorts = 0; |
|
1018 |
|
1019 /* Check for qualifiers */ |
|
1020 while (!nobareglob || isset(EXTENDEDGLOB)) { |
|
1021 struct qual *newquals; |
|
1022 char *s; |
|
1023 int sense, paren; |
|
1024 off_t data; |
|
1025 char *sdata, *newcolonmod; |
|
1026 int (*func) _((char *, Statptr, off_t, char *)); |
|
1027 |
|
1028 /* |
|
1029 * Initialise state variables for current file pattern. |
|
1030 * newquals is the root for the linked list of all qualifiers. |
|
1031 * qo is the root of the current list of alternatives. |
|
1032 * ql is the end of the current alternative where the `next' will go. |
|
1033 * qn is the current qualifier node to be added. |
|
1034 * |
|
1035 * Here is an attempt at a diagram. An `or' is added horizontally |
|
1036 * to the top line, a `next' at the bottom of the right hand line. |
|
1037 * `qn' is usually NULL unless a new `or' has just been added. |
|
1038 * |
|
1039 * quals -> x -> x -> qo |
|
1040 * | | | |
|
1041 * x x x |
|
1042 * | | |
|
1043 * x ql |
|
1044 * |
|
1045 * In fact, after each loop the complete set is in the file static |
|
1046 * `quals'. Then, if we have a second set of qualifiers, we merge |
|
1047 * the lists together. This is only tricky if one or both have an |
|
1048 * `or' in them; then we need to distribute over all alternatives. |
|
1049 */ |
|
1050 newquals = qo = qn = ql = NULL; |
|
1051 |
|
1052 sl = strlen(str); |
|
1053 if (str[sl - 1] != Outpar) |
|
1054 break; |
|
1055 |
|
1056 /* Check these are really qualifiers, not a set of * |
|
1057 * alternatives or exclusions. We can be more * |
|
1058 * lenient with an explicit (#q) than with a bare * |
|
1059 * set of qualifiers. */ |
|
1060 paren = 0; |
|
1061 for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) { |
|
1062 switch (*s) { |
|
1063 case Outpar: |
|
1064 paren++; /*FALLTHROUGH*/ |
|
1065 case Bar: |
|
1066 nobareglob = 1; |
|
1067 break; |
|
1068 case Tilde: |
|
1069 if (isset(EXTENDEDGLOB)) |
|
1070 nobareglob = 1; |
|
1071 break; |
|
1072 case Inpar: |
|
1073 paren--; |
|
1074 break; |
|
1075 } |
|
1076 } |
|
1077 if (*s != Inpar) |
|
1078 break; |
|
1079 if (isset(EXTENDEDGLOB) && s[1] == Pound) { |
|
1080 if (s[2] == 'q') { |
|
1081 *s = 0; |
|
1082 s += 2; |
|
1083 } else |
|
1084 break; |
|
1085 } else if (nobareglob) |
|
1086 break; |
|
1087 |
|
1088 /* Real qualifiers found. */ |
|
1089 nobareglob = 1; |
|
1090 sense = 0; /* bit 0 for match (0)/don't match (1) */ |
|
1091 /* bit 1 for follow links (2), don't (0) */ |
|
1092 data = 0; /* Any numerical argument required */ |
|
1093 sdata = NULL; /* Any list argument required */ |
|
1094 newcolonmod = NULL; /* Contains trailing colon modifiers */ |
|
1095 |
|
1096 str[sl-1] = 0; |
|
1097 *s++ = 0; |
|
1098 while (*s && !newcolonmod) { |
|
1099 func = (int (*) _((char *, Statptr, off_t, char *)))0; |
|
1100 if (idigit(*s)) { |
|
1101 /* Store numeric argument for qualifier */ |
|
1102 func = qualflags; |
|
1103 data = 0; |
|
1104 sdata = NULL; |
|
1105 while (idigit(*s)) |
|
1106 data = data * 010 + (*s++ - '0'); |
|
1107 } else if (*s == ',') { |
|
1108 /* A comma separates alternative sets of qualifiers */ |
|
1109 s++; |
|
1110 sense = 0; |
|
1111 if (qualct) { |
|
1112 qn = (struct qual *)hcalloc(sizeof *qn); |
|
1113 qo->or = qn; |
|
1114 qo = qn; |
|
1115 qualorct++; |
|
1116 qualct = 0; |
|
1117 ql = NULL; |
|
1118 } |
|
1119 } else { |
|
1120 switch (*s++) { |
|
1121 case ':': |
|
1122 /* Remaining arguments are history-type * |
|
1123 * colon substitutions, handled separately. */ |
|
1124 newcolonmod = s - 1; |
|
1125 untokenize(newcolonmod); |
|
1126 if (colonmod) { |
|
1127 /* remember we're searching backwards */ |
|
1128 colonmod = dyncat(newcolonmod, colonmod); |
|
1129 } else |
|
1130 colonmod = newcolonmod; |
|
1131 break; |
|
1132 case Hat: |
|
1133 case '^': |
|
1134 /* Toggle sense: go from positive to * |
|
1135 * negative match and vice versa. */ |
|
1136 sense ^= 1; |
|
1137 break; |
|
1138 case '-': |
|
1139 /* Toggle matching of symbolic links */ |
|
1140 sense ^= 2; |
|
1141 break; |
|
1142 case '@': |
|
1143 /* Match symbolic links */ |
|
1144 func = qualislnk; |
|
1145 break; |
|
1146 case Equals: |
|
1147 case '=': |
|
1148 /* Match sockets */ |
|
1149 func = qualissock; |
|
1150 break; |
|
1151 case 'p': |
|
1152 /* Match named pipes */ |
|
1153 func = qualisfifo; |
|
1154 break; |
|
1155 case '/': |
|
1156 /* Match directories */ |
|
1157 func = qualisdir; |
|
1158 break; |
|
1159 case '.': |
|
1160 /* Match regular files */ |
|
1161 func = qualisreg; |
|
1162 break; |
|
1163 case '%': |
|
1164 /* Match special files: block, * |
|
1165 * character or any device */ |
|
1166 if (*s == 'b') |
|
1167 s++, func = qualisblk; |
|
1168 else if (*s == 'c') |
|
1169 s++, func = qualischr; |
|
1170 else |
|
1171 func = qualisdev; |
|
1172 break; |
|
1173 case Star: |
|
1174 /* Match executable plain files */ |
|
1175 func = qualiscom; |
|
1176 break; |
|
1177 case 'R': |
|
1178 /* Match world-readable files */ |
|
1179 func = qualflags; |
|
1180 data = 0004; |
|
1181 break; |
|
1182 case 'W': |
|
1183 /* Match world-writeable files */ |
|
1184 func = qualflags; |
|
1185 data = 0002; |
|
1186 break; |
|
1187 case 'X': |
|
1188 /* Match world-executable files */ |
|
1189 func = qualflags; |
|
1190 data = 0001; |
|
1191 break; |
|
1192 case 'A': |
|
1193 func = qualflags; |
|
1194 data = 0040; |
|
1195 break; |
|
1196 case 'I': |
|
1197 func = qualflags; |
|
1198 data = 0020; |
|
1199 break; |
|
1200 case 'E': |
|
1201 func = qualflags; |
|
1202 data = 0010; |
|
1203 break; |
|
1204 case 'r': |
|
1205 /* Match files readable by current process */ |
|
1206 func = qualflags; |
|
1207 data = 0400; |
|
1208 break; |
|
1209 case 'w': |
|
1210 /* Match files writeable by current process */ |
|
1211 func = qualflags; |
|
1212 data = 0200; |
|
1213 break; |
|
1214 case 'x': |
|
1215 /* Match files executable by current process */ |
|
1216 func = qualflags; |
|
1217 data = 0100; |
|
1218 break; |
|
1219 case 's': |
|
1220 /* Match setuid files */ |
|
1221 func = qualflags; |
|
1222 data = 04000; |
|
1223 break; |
|
1224 case 'S': |
|
1225 /* Match setgid files */ |
|
1226 func = qualflags; |
|
1227 data = 02000; |
|
1228 break; |
|
1229 case 't': |
|
1230 func = qualflags; |
|
1231 data = 01000; |
|
1232 break; |
|
1233 case 'd': |
|
1234 /* Match device files by device number * |
|
1235 * (as given by stat's st_dev element). */ |
|
1236 func = qualdev; |
|
1237 data = qgetnum(&s); |
|
1238 break; |
|
1239 case 'l': |
|
1240 /* Match files with the given no. of hard links */ |
|
1241 func = qualnlink; |
|
1242 g_amc = -1; |
|
1243 goto getrange; |
|
1244 case 'U': |
|
1245 /* Match files owned by effective user ID */ |
|
1246 func = qualuid; |
|
1247 data = geteuid(); |
|
1248 break; |
|
1249 case 'G': |
|
1250 /* Match files owned by effective group ID */ |
|
1251 func = qualgid; |
|
1252 data = getegid(); |
|
1253 break; |
|
1254 case 'u': |
|
1255 /* Match files owned by given user id */ |
|
1256 func = qualuid; |
|
1257 /* either the actual uid... */ |
|
1258 if (idigit(*s)) |
|
1259 data = qgetnum(&s); |
|
1260 else { |
|
1261 /* ... or a user name */ |
|
1262 char sav, *tt; |
|
1263 |
|
1264 /* Find matching delimiters */ |
|
1265 tt = get_strarg(s); |
|
1266 if (!*tt) { |
|
1267 zerr("missing end of name", |
|
1268 NULL, 0); |
|
1269 data = 0; |
|
1270 } else { |
|
1271 #ifdef HAVE_GETPWNAM |
|
1272 struct passwd *pw; |
|
1273 sav = *tt; |
|
1274 *tt = '\0'; |
|
1275 |
|
1276 if ((pw = getpwnam(s + 1))) |
|
1277 data = pw->pw_uid; |
|
1278 else { |
|
1279 zerr("unknown user", NULL, 0); |
|
1280 data = 0; |
|
1281 } |
|
1282 *tt = sav; |
|
1283 #else /* !HAVE_GETPWNAM */ |
|
1284 sav = *tt; |
|
1285 zerr("unknown user", NULL, 0); |
|
1286 data = 0; |
|
1287 #endif /* !HAVE_GETPWNAM */ |
|
1288 if (sav) |
|
1289 s = tt + 1; |
|
1290 else |
|
1291 s = tt; |
|
1292 } |
|
1293 } |
|
1294 break; |
|
1295 case 'g': |
|
1296 /* Given gid or group id... works like `u' */ |
|
1297 func = qualgid; |
|
1298 /* either the actual gid... */ |
|
1299 if (idigit(*s)) |
|
1300 data = qgetnum(&s); |
|
1301 else { |
|
1302 /* ...or a delimited group name. */ |
|
1303 char sav, *tt; |
|
1304 |
|
1305 tt = get_strarg(s); |
|
1306 if (!*tt) { |
|
1307 zerr("missing end of name", |
|
1308 NULL, 0); |
|
1309 data = 0; |
|
1310 } else { |
|
1311 #ifdef HAVE_GETGRNAM |
|
1312 struct group *gr; |
|
1313 sav = *tt; |
|
1314 *tt = '\0'; |
|
1315 |
|
1316 if ((gr = getgrnam(s + 1))) |
|
1317 data = gr->gr_gid; |
|
1318 else { |
|
1319 zerr("unknown group", NULL, 0); |
|
1320 data = 0; |
|
1321 } |
|
1322 *tt = sav; |
|
1323 #else /* !HAVE_GETGRNAM */ |
|
1324 sav = *tt; |
|
1325 zerr("unknown group", NULL, 0); |
|
1326 data = 0; |
|
1327 #endif /* !HAVE_GETGRNAM */ |
|
1328 if (sav) |
|
1329 s = tt + 1; |
|
1330 else |
|
1331 s = tt; |
|
1332 } |
|
1333 } |
|
1334 break; |
|
1335 case 'f': |
|
1336 /* Match modes with chmod-spec. */ |
|
1337 func = qualmodeflags; |
|
1338 data = qgetmodespec(&s); |
|
1339 break; |
|
1340 case 'F': |
|
1341 func = qualnonemptydir; |
|
1342 break; |
|
1343 case 'M': |
|
1344 /* Mark directories with a / */ |
|
1345 if ((gf_markdirs = !(sense & 1))) |
|
1346 gf_follow = sense & 2; |
|
1347 break; |
|
1348 case 'T': |
|
1349 /* Mark types in a `ls -F' type fashion */ |
|
1350 if ((gf_listtypes = !(sense & 1))) |
|
1351 gf_follow = sense & 2; |
|
1352 break; |
|
1353 case 'N': |
|
1354 /* Nullglob: remove unmatched patterns. */ |
|
1355 gf_nullglob = !(sense & 1); |
|
1356 break; |
|
1357 case 'D': |
|
1358 /* Glob dots: match leading dots implicitly */ |
|
1359 gf_noglobdots = sense & 1; |
|
1360 break; |
|
1361 case 'n': |
|
1362 /* Numeric glob sort */ |
|
1363 gf_numsort = !(sense & 1); |
|
1364 break; |
|
1365 case 'a': |
|
1366 /* Access time in given range */ |
|
1367 g_amc = 0; |
|
1368 func = qualtime; |
|
1369 goto getrange; |
|
1370 case 'm': |
|
1371 /* Modification time in given range */ |
|
1372 g_amc = 1; |
|
1373 func = qualtime; |
|
1374 goto getrange; |
|
1375 case 'c': |
|
1376 /* Inode creation time in given range */ |
|
1377 g_amc = 2; |
|
1378 func = qualtime; |
|
1379 goto getrange; |
|
1380 case 'L': |
|
1381 /* File size (Length) in given range */ |
|
1382 func = qualsize; |
|
1383 g_amc = -1; |
|
1384 /* Get size multiplier */ |
|
1385 g_units = TT_BYTES; |
|
1386 if (*s == 'p' || *s == 'P') |
|
1387 g_units = TT_POSIX_BLOCKS, ++s; |
|
1388 else if (*s == 'k' || *s == 'K') |
|
1389 g_units = TT_KILOBYTES, ++s; |
|
1390 else if (*s == 'm' || *s == 'M') |
|
1391 g_units = TT_MEGABYTES, ++s; |
|
1392 getrange: |
|
1393 /* Get time multiplier */ |
|
1394 if (g_amc >= 0) { |
|
1395 g_units = TT_DAYS; |
|
1396 if (*s == 'h') |
|
1397 g_units = TT_HOURS, ++s; |
|
1398 else if (*s == 'm') |
|
1399 g_units = TT_MINS, ++s; |
|
1400 else if (*s == 'w') |
|
1401 g_units = TT_WEEKS, ++s; |
|
1402 else if (*s == 'M') |
|
1403 g_units = TT_MONTHS, ++s; |
|
1404 else if (*s == 's') |
|
1405 g_units = TT_SECONDS, ++s; |
|
1406 } |
|
1407 /* See if it's greater than, equal to, or less than */ |
|
1408 if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0)) |
|
1409 ++s; |
|
1410 data = qgetnum(&s); |
|
1411 break; |
|
1412 |
|
1413 case 'o': |
|
1414 case 'O': |
|
1415 { |
|
1416 int t; |
|
1417 |
|
1418 switch (*s) { |
|
1419 case 'n': t = GS_NAME; break; |
|
1420 case 'L': t = GS_SIZE; break; |
|
1421 case 'l': t = GS_LINKS; break; |
|
1422 case 'a': t = GS_ATIME; break; |
|
1423 case 'm': t = GS_MTIME; break; |
|
1424 case 'c': t = GS_CTIME; break; |
|
1425 case 'd': t = GS_DEPTH; break; |
|
1426 default: |
|
1427 zerr("unknown sort specifier", NULL, 0); |
|
1428 restore_globstate(saved); |
|
1429 return; |
|
1430 } |
|
1431 if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH))) |
|
1432 t <<= GS_SHIFT; |
|
1433 if (gf_sorts & t) { |
|
1434 zerr("doubled sort specifier", NULL, 0); |
|
1435 restore_globstate(saved); |
|
1436 return; |
|
1437 } |
|
1438 gf_sorts |= t; |
|
1439 gf_sortlist[gf_nsorts++] = t | |
|
1440 (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0); |
|
1441 s++; |
|
1442 break; |
|
1443 } |
|
1444 case '+': |
|
1445 case 'e': |
|
1446 { |
|
1447 char sav, *tt; |
|
1448 int plus; |
|
1449 |
|
1450 if (s[-1] == '+') { |
|
1451 plus = 0; |
|
1452 tt = s; |
|
1453 while (iident(*tt)) |
|
1454 tt++; |
|
1455 if (tt == s) |
|
1456 { |
|
1457 zerr("missing identifier after `+'", NULL, 0); |
|
1458 tt = NULL; |
|
1459 } |
|
1460 } else { |
|
1461 plus = 1; |
|
1462 tt = get_strarg(s); |
|
1463 if (!*tt) |
|
1464 { |
|
1465 zerr("missing end of string", NULL, 0); |
|
1466 tt = NULL; |
|
1467 } |
|
1468 } |
|
1469 |
|
1470 if (tt == NULL) { |
|
1471 data = 0; |
|
1472 } else { |
|
1473 sav = *tt; |
|
1474 *tt = '\0'; |
|
1475 func = qualsheval; |
|
1476 sdata = dupstring(s + plus); |
|
1477 untokenize(sdata); |
|
1478 *tt = sav; |
|
1479 if (sav) |
|
1480 s = tt + plus; |
|
1481 else |
|
1482 s = tt; |
|
1483 } |
|
1484 break; |
|
1485 } |
|
1486 case '[': |
|
1487 case Inbrack: |
|
1488 { |
|
1489 char *os = --s; |
|
1490 struct value v; |
|
1491 |
|
1492 v.isarr = SCANPM_WANTVALS; |
|
1493 v.pm = NULL; |
|
1494 v.end = -1; |
|
1495 v.inv = 0; |
|
1496 if (getindex(&s, &v, 0) || s == os) { |
|
1497 zerr("invalid subscript", NULL, 0); |
|
1498 restore_globstate(saved); |
|
1499 return; |
|
1500 } |
|
1501 first = v.start; |
|
1502 end = v.end; |
|
1503 break; |
|
1504 } |
|
1505 default: |
|
1506 zerr("unknown file attribute", NULL, 0); |
|
1507 restore_globstate(saved); |
|
1508 return; |
|
1509 } |
|
1510 } |
|
1511 if (func) { |
|
1512 /* Requested test is performed by function func */ |
|
1513 if (!qn) |
|
1514 qn = (struct qual *)hcalloc(sizeof *qn); |
|
1515 if (ql) |
|
1516 ql->next = qn; |
|
1517 ql = qn; |
|
1518 if (!newquals) |
|
1519 newquals = qo = qn; |
|
1520 qn->func = func; |
|
1521 qn->sense = sense; |
|
1522 qn->data = data; |
|
1523 qn->sdata = sdata; |
|
1524 qn->range = g_range; |
|
1525 qn->units = g_units; |
|
1526 qn->amc = g_amc; |
|
1527 |
|
1528 qn = NULL; |
|
1529 qualct++; |
|
1530 } |
|
1531 if (errflag) { |
|
1532 restore_globstate(saved); |
|
1533 return; |
|
1534 } |
|
1535 } |
|
1536 |
|
1537 if (quals && newquals) { |
|
1538 /* Merge previous group of qualifiers with new set. */ |
|
1539 if (quals->or || newquals->or) { |
|
1540 /* The hard case. */ |
|
1541 struct qual *qorhead = NULL, *qortail = NULL; |
|
1542 /* |
|
1543 * Distribute in the most trivial way, by creating |
|
1544 * all possible combinations of the two sets and chaining |
|
1545 * these into one long set of alternatives given |
|
1546 * by qorhead and qortail. |
|
1547 */ |
|
1548 for (qn = newquals; qn; qn = qn->or) { |
|
1549 for (qo = quals; qo; qo = qo->or) { |
|
1550 struct qual *qfirst, *qlast; |
|
1551 int islast = !qn->or && !qo->or; |
|
1552 /* Generate first set of qualifiers... */ |
|
1553 if (islast) { |
|
1554 /* Last time round: don't bother copying. */ |
|
1555 qfirst = qn; |
|
1556 for (qlast = qfirst; qlast->next; |
|
1557 qlast = qlast->next) |
|
1558 ; |
|
1559 } else |
|
1560 qfirst = dup_qual_list(qn, &qlast); |
|
1561 /* ... link into new `or' chain ... */ |
|
1562 if (!qorhead) |
|
1563 qorhead = qfirst; |
|
1564 if (qortail) |
|
1565 qortail->or = qfirst; |
|
1566 qortail = qfirst; |
|
1567 /* ... and concatenate second set. */ |
|
1568 qlast->next = islast ? qo : dup_qual_list(qo, NULL); |
|
1569 } |
|
1570 } |
|
1571 quals = qorhead; |
|
1572 } else { |
|
1573 /* |
|
1574 * Easy: we can just chain the qualifiers together. |
|
1575 * This is an optimisation; the code above will work, too. |
|
1576 * We retain the original left to right ordering --- remember |
|
1577 * we are searching for sets of qualifiers from the right. |
|
1578 */ |
|
1579 qn = newquals; |
|
1580 for ( ; newquals->next; newquals = newquals->next) |
|
1581 ; |
|
1582 newquals->next = quals; |
|
1583 quals = qn; |
|
1584 } |
|
1585 } else if (newquals) |
|
1586 quals = newquals; |
|
1587 } |
|
1588 q = parsepat(str); |
|
1589 if (!q || errflag) { /* if parsing failed */ |
|
1590 restore_globstate(saved); |
|
1591 if (unset(BADPATTERN)) { |
|
1592 if (!nountok) |
|
1593 untokenize(ostr); |
|
1594 insertlinknode(list, node, ostr); |
|
1595 return; |
|
1596 } |
|
1597 errflag = 0; |
|
1598 zerr("bad pattern: %s", ostr, 0); |
|
1599 return; |
|
1600 } |
|
1601 if (!gf_nsorts) { |
|
1602 gf_sortlist[0] = gf_sorts = GS_NAME; |
|
1603 gf_nsorts = 1; |
|
1604 } |
|
1605 /* Initialise receptacle for matched files, * |
|
1606 * expanded by insert() where necessary. */ |
|
1607 matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * |
|
1608 sizeof(struct gmatch)); |
|
1609 matchct = 0; |
|
1610 pattrystart(); |
|
1611 |
|
1612 /* The actual processing takes place here: matches go into * |
|
1613 * matchbuf. This is the only top-level call to scanner(). */ |
|
1614 scanner(q); |
|
1615 |
|
1616 /* Deal with failures to match depending on options */ |
|
1617 if (matchct) |
|
1618 badcshglob |= 2; /* at least one cmd. line expansion O.K. */ |
|
1619 else if (!gf_nullglob) { |
|
1620 if (isset(CSHNULLGLOB)) { |
|
1621 badcshglob |= 1; /* at least one cmd. line expansion failed */ |
|
1622 } else if (isset(NOMATCH)) { |
|
1623 zerr("no matches found: %s", ostr, 0); |
|
1624 free(matchbuf); |
|
1625 restore_globstate(saved); |
|
1626 return; |
|
1627 } else { |
|
1628 /* treat as an ordinary string */ |
|
1629 untokenize(matchptr->name = dupstring(ostr)); |
|
1630 matchptr++; |
|
1631 matchct = 1; |
|
1632 } |
|
1633 } |
|
1634 /* Sort arguments in to lexical (and possibly numeric) order. * |
|
1635 * This is reversed to facilitate insertion into the list. */ |
|
1636 qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), |
|
1637 (int (*) _((const void *, const void *)))gmatchcmp); |
|
1638 |
|
1639 if (first < 0) { |
|
1640 first += matchct; |
|
1641 if (first < 0) |
|
1642 first = 0; |
|
1643 } |
|
1644 if (end < 0) |
|
1645 end += matchct + 1; |
|
1646 else if (end > matchct) |
|
1647 end = matchct; |
|
1648 if ((end -= first) > 0) { |
|
1649 matchptr = matchbuf + matchct - first - end; |
|
1650 while (end-- > 0) { /* insert matches in the arg list */ |
|
1651 insertlinknode(list, node, matchptr->name); |
|
1652 matchptr++; |
|
1653 } |
|
1654 } |
|
1655 free(matchbuf); |
|
1656 |
|
1657 restore_globstate(saved); |
|
1658 } |
|
1659 |
|
1660 /* Return the trailing character for marking file types */ |
|
1661 |
|
1662 /**/ |
|
1663 mod_export char |
|
1664 file_type(mode_t filemode) |
|
1665 { |
|
1666 if(S_ISBLK(filemode)) |
|
1667 return '#'; |
|
1668 else if(S_ISCHR(filemode)) |
|
1669 return '%'; |
|
1670 else if(S_ISDIR(filemode)) |
|
1671 return '/'; |
|
1672 else if(S_ISFIFO(filemode)) |
|
1673 return '|'; |
|
1674 else if(S_ISLNK(filemode)) |
|
1675 return '@'; |
|
1676 else if(S_ISREG(filemode)) |
|
1677 return (filemode & S_IXUGO) ? '*' : ' '; |
|
1678 else if(S_ISSOCK(filemode)) |
|
1679 return '='; |
|
1680 else |
|
1681 return '?'; |
|
1682 } |
|
1683 |
|
1684 /* check to see if str is eligible for brace expansion */ |
|
1685 |
|
1686 /**/ |
|
1687 mod_export int |
|
1688 hasbraces(char *str) |
|
1689 { |
|
1690 char *lbr, *mbr, *comma; |
|
1691 |
|
1692 if (isset(BRACECCL)) { |
|
1693 /* In this case, any properly formed brace expression * |
|
1694 * will match and expand to the characters in between. */ |
|
1695 int bc, c; |
|
1696 |
|
1697 for (bc = 0; (c = *str); ++str) |
|
1698 if (c == Inbrace) { |
|
1699 if (!bc && str[1] == Outbrace) |
|
1700 *str++ = '{', *str = '}'; |
|
1701 else |
|
1702 bc++; |
|
1703 } else if (c == Outbrace) { |
|
1704 if (!bc) |
|
1705 *str = '}'; |
|
1706 else if (!--bc) |
|
1707 return 1; |
|
1708 } |
|
1709 return 0; |
|
1710 } |
|
1711 /* Otherwise we need to look for... */ |
|
1712 lbr = mbr = comma = NULL; |
|
1713 for (;;) { |
|
1714 switch (*str++) { |
|
1715 case Inbrace: |
|
1716 if (!lbr) { |
|
1717 lbr = str - 1; |
|
1718 while (idigit(*str)) |
|
1719 str++; |
|
1720 if (*str == '.' && str[1] == '.') { |
|
1721 str++; |
|
1722 while (idigit(*++str)); |
|
1723 if (*str == Outbrace && |
|
1724 (idigit(lbr[1]) || idigit(str[-1]))) |
|
1725 return 1; |
|
1726 } |
|
1727 } else { |
|
1728 char *s = --str; |
|
1729 |
|
1730 if (skipparens(Inbrace, Outbrace, &str)) { |
|
1731 *lbr = *s = '{'; |
|
1732 if (comma) |
|
1733 str = comma; |
|
1734 if (mbr && mbr < str) |
|
1735 str = mbr; |
|
1736 lbr = mbr = comma = NULL; |
|
1737 } else if (!mbr) |
|
1738 mbr = s; |
|
1739 } |
|
1740 break; |
|
1741 case Outbrace: |
|
1742 if (!lbr) |
|
1743 str[-1] = '}'; |
|
1744 else if (comma) |
|
1745 return 1; |
|
1746 else { |
|
1747 *lbr = '{'; |
|
1748 str[-1] = '}'; |
|
1749 if (mbr) |
|
1750 str = mbr; |
|
1751 mbr = lbr = NULL; |
|
1752 } |
|
1753 break; |
|
1754 case Comma: |
|
1755 if (!lbr) |
|
1756 str[-1] = ','; |
|
1757 else if (!comma) |
|
1758 comma = str - 1; |
|
1759 break; |
|
1760 case '\0': |
|
1761 if (lbr) |
|
1762 *lbr = '{'; |
|
1763 if (!mbr && !comma) |
|
1764 return 0; |
|
1765 if (comma) |
|
1766 str = comma; |
|
1767 if (mbr && mbr < str) |
|
1768 str = mbr; |
|
1769 lbr = mbr = comma = NULL; |
|
1770 break; |
|
1771 } |
|
1772 } |
|
1773 } |
|
1774 |
|
1775 /* expand stuff like >>*.c */ |
|
1776 |
|
1777 /**/ |
|
1778 int |
|
1779 xpandredir(struct redir *fn, LinkList tab) |
|
1780 { |
|
1781 char *nam; |
|
1782 struct redir *ff; |
|
1783 int ret = 0; |
|
1784 local_list1(fake); |
|
1785 |
|
1786 /* Stick the name in a list... */ |
|
1787 init_list1(fake, fn->name); |
|
1788 /* ...which undergoes all the usual shell expansions */ |
|
1789 prefork(&fake, isset(MULTIOS) ? 0 : PF_SINGLE); |
|
1790 /* Globbing is only done for multios. */ |
|
1791 if (!errflag && isset(MULTIOS)) |
|
1792 globlist(&fake, 0); |
|
1793 if (errflag) |
|
1794 return 0; |
|
1795 if (nonempty(&fake) && !nextnode(firstnode(&fake))) { |
|
1796 /* Just one match, the usual case. */ |
|
1797 char *s = peekfirst(&fake); |
|
1798 fn->name = s; |
|
1799 untokenize(s); |
|
1800 if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) { |
|
1801 if (s[0] == '-' && !s[1]) |
|
1802 fn->type = REDIR_CLOSE; |
|
1803 else if (s[0] == 'p' && !s[1]) |
|
1804 fn->fd2 = -2; |
|
1805 else { |
|
1806 while (idigit(*s)) |
|
1807 s++; |
|
1808 if (!*s && s > fn->name) |
|
1809 fn->fd2 = zstrtol(fn->name, NULL, 10); |
|
1810 else if (fn->type == REDIR_MERGEIN) |
|
1811 zerr("file number expected", NULL, 0); |
|
1812 else |
|
1813 fn->type = REDIR_ERRWRITE; |
|
1814 } |
|
1815 } |
|
1816 } else if (fn->type == REDIR_MERGEIN) |
|
1817 zerr("file number expected", NULL, 0); |
|
1818 else { |
|
1819 if (fn->type == REDIR_MERGEOUT) |
|
1820 fn->type = REDIR_ERRWRITE; |
|
1821 while ((nam = (char *)ugetnode(&fake))) { |
|
1822 /* Loop over matches, duplicating the * |
|
1823 * redirection for each file found. */ |
|
1824 ff = (struct redir *) zhalloc(sizeof *ff); |
|
1825 *ff = *fn; |
|
1826 ff->name = nam; |
|
1827 addlinknode(tab, ff); |
|
1828 ret = 1; |
|
1829 } |
|
1830 } |
|
1831 return ret; |
|
1832 } |
|
1833 |
|
1834 /* brace expansion */ |
|
1835 |
|
1836 /**/ |
|
1837 mod_export void |
|
1838 xpandbraces(LinkList list, LinkNode *np) |
|
1839 { |
|
1840 LinkNode node = (*np), last = prevnode(node); |
|
1841 char *str = (char *)getdata(node), *str3 = str, *str2; |
|
1842 int prev, bc, comma, dotdot; |
|
1843 |
|
1844 for (; *str != Inbrace; str++); |
|
1845 /* First, match up braces and see what we have. */ |
|
1846 for (str2 = str, bc = comma = dotdot = 0; *str2; ++str2) |
|
1847 if (*str2 == Inbrace) |
|
1848 ++bc; |
|
1849 else if (*str2 == Outbrace) { |
|
1850 if (--bc == 0) |
|
1851 break; |
|
1852 } else if (bc == 1) { |
|
1853 if (*str2 == Comma) |
|
1854 ++comma; /* we have {foo,bar} */ |
|
1855 else if (*str2 == '.' && str2[1] == '.') |
|
1856 dotdot++; /* we have {num1..num2} */ |
|
1857 } |
|
1858 DPUTS(bc, "BUG: unmatched brace in xpandbraces()"); |
|
1859 if (!comma && dotdot) { |
|
1860 /* Expand range like 0..10 numerically: comma or recursive |
|
1861 brace expansion take precedence. */ |
|
1862 char *dots, *p; |
|
1863 LinkNode olast = last; |
|
1864 /* Get the first number of the range */ |
|
1865 int rstart = zstrtol(str+1,&dots,10), rend = 0, err = 0, rev = 0; |
|
1866 int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2; |
|
1867 int strp = str - str3; |
|
1868 |
|
1869 if (dots == str + 1 || *dots != '.' || dots[1] != '.') |
|
1870 err++; |
|
1871 else { |
|
1872 /* Get the last number of the range */ |
|
1873 rend = zstrtol(dots+2,&p,10); |
|
1874 if (p == dots+2 || p != str2) |
|
1875 err++; |
|
1876 } |
|
1877 if (!err) { |
|
1878 /* If either no. begins with a zero, pad the output with * |
|
1879 * zeroes. Otherwise, choose a min width to suppress them. */ |
|
1880 int minw = (str[1] == '0') ? wid1 : (dots[2] == '0' ) ? wid2 : |
|
1881 (wid2 > wid1) ? wid1 : wid2; |
|
1882 if (rstart > rend) { |
|
1883 /* Handle decreasing ranges correctly. */ |
|
1884 int rt = rend; |
|
1885 rend = rstart; |
|
1886 rstart = rt; |
|
1887 rev = 1; |
|
1888 } |
|
1889 uremnode(list, node); |
|
1890 for (; rend >= rstart; rend--) { |
|
1891 /* Node added in at end, so do highest first */ |
|
1892 p = dupstring(str3); |
|
1893 sprintf(p + strp, "%0*d", minw, rend); |
|
1894 strcat(p + strp, str2 + 1); |
|
1895 insertlinknode(list, last, p); |
|
1896 if (rev) /* decreasing: add in reverse order. */ |
|
1897 last = nextnode(last); |
|
1898 } |
|
1899 *np = nextnode(olast); |
|
1900 return; |
|
1901 } |
|
1902 } |
|
1903 if (!comma && isset(BRACECCL)) { /* {a-mnop} */ |
|
1904 /* Here we expand each character to a separate node, * |
|
1905 * but also ranges of characters like a-m. ccl is a * |
|
1906 * set of flags saying whether each character is present; * |
|
1907 * the final list is in lexical order. */ |
|
1908 char ccl[256], *p; |
|
1909 unsigned char c1, c2; |
|
1910 unsigned int len, pl; |
|
1911 int lastch = -1; |
|
1912 |
|
1913 uremnode(list, node); |
|
1914 memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0])); |
|
1915 for (p = str + 1; p < str2;) { |
|
1916 if (itok(c1 = *p++)) |
|
1917 c1 = ztokens[c1 - STOUC(Pound)]; |
|
1918 if ((char) c1 == Meta) |
|
1919 c1 = 32 ^ *p++; |
|
1920 if (itok(c2 = *p)) |
|
1921 c2 = ztokens[c2 - STOUC(Pound)]; |
|
1922 if ((char) c2 == Meta) |
|
1923 c2 = 32 ^ p[1]; |
|
1924 if (c1 == '-' && lastch >= 0 && p < str2 && lastch <= (int)c2) { |
|
1925 while (lastch < (int)c2) |
|
1926 ccl[lastch++] = 1; |
|
1927 lastch = -1; |
|
1928 } else |
|
1929 ccl[lastch = c1] = 1; |
|
1930 } |
|
1931 pl = str - str3; |
|
1932 len = pl + strlen(++str2) + 2; |
|
1933 for (p = ccl + 255; p-- > ccl;) |
|
1934 if (*p) { |
|
1935 c1 = p - ccl; |
|
1936 if (imeta(c1)) { |
|
1937 str = hcalloc(len + 1); |
|
1938 str[pl] = Meta; |
|
1939 str[pl+1] = c1 ^ 32; |
|
1940 strcpy(str + pl + 2, str2); |
|
1941 } else { |
|
1942 str = hcalloc(len); |
|
1943 str[pl] = c1; |
|
1944 strcpy(str + pl + 1, str2); |
|
1945 } |
|
1946 memcpy(str, str3, pl); |
|
1947 insertlinknode(list, last, str); |
|
1948 } |
|
1949 *np = nextnode(last); |
|
1950 return; |
|
1951 } |
|
1952 prev = str++ - str3; |
|
1953 str2++; |
|
1954 uremnode(list, node); |
|
1955 node = last; |
|
1956 /* Finally, normal comma expansion * |
|
1957 * str1{foo,bar}str2 -> str1foostr2 str1barstr2. * |
|
1958 * Any number of intervening commas is allowed. */ |
|
1959 for (;;) { |
|
1960 char *zz, *str4; |
|
1961 int cnt; |
|
1962 |
|
1963 for (str4 = str, cnt = 0; cnt || (*str != Comma && *str != |
|
1964 Outbrace); str++) { |
|
1965 if (*str == Inbrace) |
|
1966 cnt++; |
|
1967 else if (*str == Outbrace) |
|
1968 cnt--; |
|
1969 DPUTS(!*str, "BUG: illegal brace expansion"); |
|
1970 } |
|
1971 /* Concatenate the string before the braces (str3), the section * |
|
1972 * just found (str4) and the text after the braces (str2) */ |
|
1973 zz = (char *) hcalloc(prev + (str - str4) + strlen(str2) + 1); |
|
1974 ztrncpy(zz, str3, prev); |
|
1975 strncat(zz, str4, str - str4); |
|
1976 strcat(zz, str2); |
|
1977 /* and add this text to the argument list. */ |
|
1978 insertlinknode(list, node, zz); |
|
1979 incnode(node); |
|
1980 if (*str != Outbrace) |
|
1981 str++; |
|
1982 else |
|
1983 break; |
|
1984 } |
|
1985 *np = nextnode(last); |
|
1986 } |
|
1987 |
|
1988 /* check to see if a matches b (b is not a filename pattern) */ |
|
1989 |
|
1990 /**/ |
|
1991 int |
|
1992 matchpat(char *a, char *b) |
|
1993 { |
|
1994 Patprog p = patcompile(b, PAT_STATIC, NULL); |
|
1995 |
|
1996 if (!p) { |
|
1997 zerr("bad pattern: %s", b, 0); |
|
1998 return 0; |
|
1999 } |
|
2000 return pattry(p, a); |
|
2001 } |
|
2002 |
|
2003 /* do the ${foo%%bar}, ${foo#bar} stuff */ |
|
2004 /* please do not laugh at this code. */ |
|
2005 |
|
2006 struct repldata { |
|
2007 int b, e; /* beginning and end of chunk to replace */ |
|
2008 char *replstr; /* replacement string to use */ |
|
2009 }; |
|
2010 typedef struct repldata *Repldata; |
|
2011 |
|
2012 /* |
|
2013 * List of bits of matches to concatenate with replacement string. |
|
2014 * The data is a struct repldata. It is not used in cases like |
|
2015 * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match |
|
2016 * is anchored. It goes on the heap. |
|
2017 */ |
|
2018 |
|
2019 static LinkList repllist; |
|
2020 |
|
2021 /* Having found a match in getmatch, decide what part of string |
|
2022 * to return. The matched part starts b characters into string s |
|
2023 * and finishes e characters in: 0 <= b <= e <= strlen(s) |
|
2024 * (yes, empty matches should work). |
|
2025 * fl is a set of the SUB_* matches defined in zsh.h from SUB_MATCH onwards; |
|
2026 * the lower parts are ignored. |
|
2027 * replstr is the replacement string for a substitution |
|
2028 */ |
|
2029 |
|
2030 /**/ |
|
2031 static char * |
|
2032 get_match_ret(char *s, int b, int e, int fl, char *replstr) |
|
2033 { |
|
2034 char buf[80], *r, *p, *rr; |
|
2035 int ll = 0, l = strlen(s), bl = 0, t = 0, i; |
|
2036 |
|
2037 if (replstr) { |
|
2038 if (fl & SUB_DOSUBST) { |
|
2039 replstr = dupstring(replstr); |
|
2040 singsub(&replstr); |
|
2041 untokenize(replstr); |
|
2042 } |
|
2043 if ((fl & SUB_GLOBAL) && repllist) { |
|
2044 /* We are replacing the chunk, just add this to the list */ |
|
2045 Repldata rd = (Repldata) zhalloc(sizeof(*rd)); |
|
2046 rd->b = b; |
|
2047 rd->e = e; |
|
2048 rd->replstr = replstr; |
|
2049 addlinknode(repllist, rd); |
|
2050 return s; |
|
2051 } |
|
2052 ll += strlen(replstr); |
|
2053 } |
|
2054 if (fl & SUB_MATCH) /* matched portion */ |
|
2055 ll += 1 + (e - b); |
|
2056 if (fl & SUB_REST) /* unmatched portion */ |
|
2057 ll += 1 + (l - (e - b)); |
|
2058 if (fl & SUB_BIND) { |
|
2059 /* position of start of matched portion */ |
|
2060 sprintf(buf, "%d ", b + 1); |
|
2061 ll += (bl = strlen(buf)); |
|
2062 } |
|
2063 if (fl & SUB_EIND) { |
|
2064 /* position of end of matched portion */ |
|
2065 sprintf(buf + bl, "%d ", e + 1); |
|
2066 ll += (bl = strlen(buf)); |
|
2067 } |
|
2068 if (fl & SUB_LEN) { |
|
2069 /* length of matched portion */ |
|
2070 sprintf(buf + bl, "%d ", e - b); |
|
2071 ll += (bl = strlen(buf)); |
|
2072 } |
|
2073 if (bl) |
|
2074 buf[bl - 1] = '\0'; |
|
2075 |
|
2076 rr = r = (char *) hcalloc(ll); |
|
2077 |
|
2078 if (fl & SUB_MATCH) { |
|
2079 /* copy matched portion to new buffer */ |
|
2080 for (i = b, p = s + b; i < e; i++) |
|
2081 *rr++ = *p++; |
|
2082 t = 1; |
|
2083 } |
|
2084 if (fl & SUB_REST) { |
|
2085 /* Copy unmatched portion to buffer. If both portions * |
|
2086 * requested, put a space in between (why?) */ |
|
2087 if (t) |
|
2088 *rr++ = ' '; |
|
2089 /* there may be unmatched bits at both beginning and end of string */ |
|
2090 for (i = 0, p = s; i < b; i++) |
|
2091 *rr++ = *p++; |
|
2092 if (replstr) |
|
2093 for (p = replstr; *p; ) |
|
2094 *rr++ = *p++; |
|
2095 for (i = e, p = s + e; i < l; i++) |
|
2096 *rr++ = *p++; |
|
2097 t = 1; |
|
2098 } |
|
2099 *rr = '\0'; |
|
2100 if (bl) { |
|
2101 /* if there was a buffer (with a numeric result), add it; * |
|
2102 * if there was other stuff too, stick in a space first. */ |
|
2103 if (t) |
|
2104 *rr++ = ' '; |
|
2105 strcpy(rr, buf); |
|
2106 } |
|
2107 return r; |
|
2108 } |
|
2109 |
|
2110 static Patprog |
|
2111 compgetmatch(char *pat, int *flp, char **replstrp) |
|
2112 { |
|
2113 Patprog p; |
|
2114 /* |
|
2115 * Flags to pattern compiler: use static buffer since we only |
|
2116 * have one pattern at a time; we will try the must-match test ourselves, |
|
2117 * so tell the pattern compiler we are scanning. |
|
2118 */ |
|
2119 |
|
2120 /* int patflags = PAT_STATIC|PAT_SCAN|PAT_NOANCH;*/ |
|
2121 |
|
2122 /* Unfortunately, PAT_STATIC doesn't work if we have a replstr with |
|
2123 * something like ${x#...} in it which will be singsub()ed below because |
|
2124 * that would overwrite the pattern buffer. */ |
|
2125 |
|
2126 int patflags = PAT_SCAN|PAT_NOANCH | (*replstrp ? 0 : PAT_STATIC); |
|
2127 |
|
2128 /* |
|
2129 * Search is anchored to the end of the string if we want to match |
|
2130 * it all, or if we are matching at the end of the string and not |
|
2131 * using substrings. |
|
2132 */ |
|
2133 if ((*flp & SUB_ALL) || ((*flp & SUB_END) && !(*flp & SUB_SUBSTR))) |
|
2134 patflags &= ~PAT_NOANCH; |
|
2135 p = patcompile(pat, patflags, NULL); |
|
2136 if (!p) { |
|
2137 zerr("bad pattern: %s", pat, 0); |
|
2138 return NULL; |
|
2139 } |
|
2140 if (*replstrp) { |
|
2141 if (p->patnpar || (p->globend & GF_MATCHREF)) { |
|
2142 /* |
|
2143 * Either backreferences or match references, so we |
|
2144 * need to re-substitute replstr each time round. |
|
2145 */ |
|
2146 *flp |= SUB_DOSUBST; |
|
2147 } else { |
|
2148 singsub(replstrp); |
|
2149 untokenize(*replstrp); |
|
2150 } |
|
2151 } |
|
2152 |
|
2153 return p; |
|
2154 } |
|
2155 |
|
2156 /* |
|
2157 * This is called from paramsubst to get the match for ${foo#bar} etc. |
|
2158 * fl is a set of the SUB_* flags defined in zsh.h |
|
2159 * *sp points to the string we have to modify. The n'th match will be |
|
2160 * returned in *sp. The heap is used to get memory for the result string. |
|
2161 * replstr is the replacement string from a ${.../orig/repl}, in |
|
2162 * which case pat is the original. |
|
2163 * |
|
2164 * n is now ignored unless we are looking for a substring, in |
|
2165 * which case the n'th match from the start is counted such that |
|
2166 * there is no more than one match from each position. |
|
2167 */ |
|
2168 |
|
2169 /**/ |
|
2170 int |
|
2171 getmatch(char **sp, char *pat, int fl, int n, char *replstr) |
|
2172 { |
|
2173 Patprog p; |
|
2174 |
|
2175 if (!(p = compgetmatch(pat, &fl, &replstr))) |
|
2176 return 1; |
|
2177 |
|
2178 return igetmatch(sp, p, fl, n, replstr); |
|
2179 } |
|
2180 |
|
2181 /**/ |
|
2182 void |
|
2183 getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr) |
|
2184 { |
|
2185 char **arr = *ap, **pp; |
|
2186 Patprog p; |
|
2187 |
|
2188 if (!(p = compgetmatch(pat, &fl, &replstr))) |
|
2189 return; |
|
2190 |
|
2191 *ap = pp = hcalloc(sizeof(char *) * (arrlen(arr) + 1)); |
|
2192 while ((*pp = *arr++)) |
|
2193 if (igetmatch(pp, p, fl, n, replstr)) |
|
2194 pp++; |
|
2195 } |
|
2196 |
|
2197 /**/ |
|
2198 static void |
|
2199 set_pat_start(Patprog p, int offs) |
|
2200 { |
|
2201 /* |
|
2202 * If we are messing around with the test string by advancing up |
|
2203 * it from the start, we need to tell the pattern matcher that |
|
2204 * a start-of-string assertion, i.e. (#s), should fail. Hence |
|
2205 * we test whether the offset of the real start of string from |
|
2206 * the actual start, passed as offs, is zero. |
|
2207 */ |
|
2208 if (offs) |
|
2209 p->flags |= PAT_NOTSTART; |
|
2210 else |
|
2211 p->flags &= ~PAT_NOTSTART; |
|
2212 } |
|
2213 |
|
2214 /**/ |
|
2215 static void |
|
2216 set_pat_end(Patprog p, char null_me) |
|
2217 { |
|
2218 /* |
|
2219 * If we are messing around with the string by shortening it at the |
|
2220 * tail, we need to tell the pattern matcher that an end-of-string |
|
2221 * assertion, i.e. (#e), should fail. Hence we test whether |
|
2222 * the character null_me about to be zapped is or is not already a null. |
|
2223 */ |
|
2224 if (null_me) |
|
2225 p->flags |= PAT_NOTEND; |
|
2226 else |
|
2227 p->flags &= ~PAT_NOTEND; |
|
2228 } |
|
2229 |
|
2230 /**/ |
|
2231 static int |
|
2232 igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) |
|
2233 { |
|
2234 char *s = *sp, *t; |
|
2235 /* |
|
2236 * Note that ioff and uml count characters in the character |
|
2237 * set (Meta's are not included), while l counts characters in the |
|
2238 * metafied string. umlen is a counter for (unmetafied) character |
|
2239 * lengths. |
|
2240 */ |
|
2241 int ioff, l = strlen(*sp), uml = ztrlen(*sp), matched = 1, umlen; |
|
2242 |
|
2243 repllist = NULL; |
|
2244 |
|
2245 /* perform must-match test for complex closures */ |
|
2246 if (p->mustoff) |
|
2247 { |
|
2248 /* |
|
2249 * Yuk. Probably we should rewrite this whole function to |
|
2250 * use an unmetafied test string. |
|
2251 * |
|
2252 * Use META_HEAPDUP because we need a terminating NULL. |
|
2253 */ |
|
2254 char *muststr = metafy((char *)p + p->mustoff, |
|
2255 p->patmlen, META_HEAPDUP); |
|
2256 |
|
2257 if (!strstr(s, muststr)) |
|
2258 matched = 0; |
|
2259 } |
|
2260 |
|
2261 /* in case we used the prog before... */ |
|
2262 p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); |
|
2263 |
|
2264 if (fl & SUB_ALL) { |
|
2265 int i = matched && pattry(p, s); |
|
2266 *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0); |
|
2267 if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) |
|
2268 return 0; |
|
2269 return 1; |
|
2270 } |
|
2271 if (matched) { |
|
2272 switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { |
|
2273 case 0: |
|
2274 case SUB_LONG: |
|
2275 /* |
|
2276 * Largest/smallest possible match at head of string. |
|
2277 * First get the longest match... |
|
2278 */ |
|
2279 if (pattry(p, s)) { |
|
2280 /* patmatchlen returns metafied length, as we need */ |
|
2281 int mlen = patmatchlen(); |
|
2282 if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { |
|
2283 /* |
|
2284 * ... now we know whether it's worth looking for the |
|
2285 * shortest, which we do by brute force. |
|
2286 */ |
|
2287 for (t = s, umlen = 0; t < s + mlen; METAINC(t), umlen++) { |
|
2288 set_pat_end(p, *t); |
|
2289 if (pattrylen(p, s, t - s, umlen, 0)) { |
|
2290 mlen = patmatchlen(); |
|
2291 break; |
|
2292 } |
|
2293 } |
|
2294 } |
|
2295 *sp = get_match_ret(*sp, 0, mlen, fl, replstr); |
|
2296 return 1; |
|
2297 } |
|
2298 break; |
|
2299 |
|
2300 case SUB_END: |
|
2301 /* Smallest possible match at tail of string: * |
|
2302 * move back down string until we get a match. * |
|
2303 * There's no optimization here. */ |
|
2304 for (ioff = uml, t = s + l, umlen = 0; t >= s; |
|
2305 t--, ioff--, umlen++) { |
|
2306 if (t > s && t[-1] == Meta) |
|
2307 t--; |
|
2308 set_pat_start(p, t-s); |
|
2309 if (pattrylen(p, t, s + l - t, umlen, ioff)) { |
|
2310 *sp = get_match_ret(*sp, t - s, l, fl, replstr); |
|
2311 return 1; |
|
2312 } |
|
2313 if (t > s+1 && t[-2] == Meta) |
|
2314 t--; |
|
2315 } |
|
2316 break; |
|
2317 |
|
2318 case (SUB_END|SUB_LONG): |
|
2319 /* Largest possible match at tail of string: * |
|
2320 * move forward along string until we get a match. * |
|
2321 * Again there's no optimisation. */ |
|
2322 for (ioff = 0, t = s, umlen = uml; t < s + l; |
|
2323 ioff++, METAINC(t), umlen--) { |
|
2324 set_pat_start(p, t-s); |
|
2325 if (pattrylen(p, t, s + l - t, umlen, ioff)) { |
|
2326 *sp = get_match_ret(*sp, t-s, l, fl, replstr); |
|
2327 return 1; |
|
2328 } |
|
2329 if (*t == Meta) |
|
2330 t++; |
|
2331 } |
|
2332 break; |
|
2333 |
|
2334 case SUB_SUBSTR: |
|
2335 /* Smallest at start, but matching substrings. */ |
|
2336 set_pat_start(p, l); |
|
2337 if (!(fl & SUB_GLOBAL) && pattry(p, s + l) && !--n) { |
|
2338 *sp = get_match_ret(*sp, 0, 0, fl, replstr); |
|
2339 return 1; |
|
2340 } /* fall through */ |
|
2341 case (SUB_SUBSTR|SUB_LONG): |
|
2342 /* longest or smallest at start with substrings */ |
|
2343 t = s; |
|
2344 if (fl & SUB_GLOBAL) |
|
2345 repllist = newlinklist(); |
|
2346 ioff = 0; /* offset into string */ |
|
2347 umlen = uml; |
|
2348 do { |
|
2349 /* loop over all matches for global substitution */ |
|
2350 matched = 0; |
|
2351 for (; t < s + l; METAINC(t), ioff++, umlen--) { |
|
2352 /* Find the longest match from this position. */ |
|
2353 set_pat_start(p, t-s); |
|
2354 if (pattrylen(p, t, s + l - t, umlen, ioff)) { |
|
2355 char *mpos = t + patmatchlen(); |
|
2356 if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { |
|
2357 char *ptr; |
|
2358 int umlen2; |
|
2359 for (ptr = t, umlen2 = 0; ptr < mpos; |
|
2360 METAINC(ptr), umlen2++) { |
|
2361 set_pat_end(p, *ptr); |
|
2362 if (pattrylen(p, t, ptr - t, umlen2, ioff)) { |
|
2363 mpos = t + patmatchlen(); |
|
2364 break; |
|
2365 } |
|
2366 } |
|
2367 } |
|
2368 if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { |
|
2369 *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); |
|
2370 if (mpos == t) |
|
2371 METAINC(mpos); |
|
2372 } |
|
2373 if (!(fl & SUB_GLOBAL)) { |
|
2374 if (n) { |
|
2375 /* |
|
2376 * Looking for a later match: in this case, |
|
2377 * we can continue looking for matches from |
|
2378 * the next character, even if it overlaps |
|
2379 * with what we just found. |
|
2380 */ |
|
2381 continue; |
|
2382 } else { |
|
2383 return 1; |
|
2384 } |
|
2385 } |
|
2386 /* |
|
2387 * For a global match, we need to skip the stuff |
|
2388 * which is already marked for replacement. |
|
2389 */ |
|
2390 matched = 1; |
|
2391 for ( ; t < mpos; t++, ioff++, umlen--) |
|
2392 if (*t == Meta) |
|
2393 t++; |
|
2394 break; |
|
2395 } |
|
2396 if (*t == Meta) |
|
2397 t++; |
|
2398 } |
|
2399 } while (matched); |
|
2400 /* |
|
2401 * check if we can match a blank string, if so do it |
|
2402 * at the start. Goodness knows if this is a good idea |
|
2403 * with global substitution, so it doesn't happen. |
|
2404 */ |
|
2405 set_pat_start(p, l); |
|
2406 if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && |
|
2407 pattry(p, s + l) && !--n) { |
|
2408 *sp = get_match_ret(*sp, 0, 0, fl, replstr); |
|
2409 return 1; |
|
2410 } |
|
2411 break; |
|
2412 |
|
2413 case (SUB_END|SUB_SUBSTR): |
|
2414 case (SUB_END|SUB_LONG|SUB_SUBSTR): |
|
2415 /* Longest/shortest at end, matching substrings. */ |
|
2416 if (!(fl & SUB_LONG)) { |
|
2417 set_pat_start(p, l); |
|
2418 if (pattrylen(p, s + l, 0, 0, uml) && !--n) { |
|
2419 *sp = get_match_ret(*sp, l, l, fl, replstr); |
|
2420 return 1; |
|
2421 } |
|
2422 } |
|
2423 for (ioff = uml - 1, t = s + l - 1, umlen = 1; t >= s; |
|
2424 t--, ioff--, umlen++) { |
|
2425 if (t > s && t[-1] == Meta) |
|
2426 t--; |
|
2427 set_pat_start(p, t-s); |
|
2428 if (pattrylen(p, t, s + l - t, umlen, ioff) && !--n) { |
|
2429 /* Found the longest match */ |
|
2430 char *mpos = t + patmatchlen(); |
|
2431 if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { |
|
2432 char *ptr; |
|
2433 int umlen2; |
|
2434 for (ptr = t, umlen2 = 0; ptr < mpos; |
|
2435 METAINC(ptr), umlen2++) { |
|
2436 set_pat_end(p, *ptr); |
|
2437 if (pattrylen(p, t, ptr - t, umlen2, ioff)) { |
|
2438 mpos = t + patmatchlen(); |
|
2439 break; |
|
2440 } |
|
2441 } |
|
2442 } |
|
2443 *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); |
|
2444 return 1; |
|
2445 } |
|
2446 } |
|
2447 set_pat_start(p, l); |
|
2448 if ((fl & SUB_LONG) && pattrylen(p, s + l, 0, 0, uml) && !--n) { |
|
2449 *sp = get_match_ret(*sp, l, l, fl, replstr); |
|
2450 return 1; |
|
2451 } |
|
2452 break; |
|
2453 } |
|
2454 } |
|
2455 |
|
2456 if (repllist && nonempty(repllist)) { |
|
2457 /* Put all the bits of a global search and replace together. */ |
|
2458 LinkNode nd; |
|
2459 Repldata rd; |
|
2460 int lleft = 0; /* size of returned string */ |
|
2461 char *ptr, *start; |
|
2462 int i; |
|
2463 |
|
2464 i = 0; /* start of last chunk we got from *sp */ |
|
2465 for (nd = firstnode(repllist); nd; incnode(nd)) { |
|
2466 rd = (Repldata) getdata(nd); |
|
2467 lleft += rd->b - i; /* previous chunk of *sp */ |
|
2468 lleft += strlen(rd->replstr); /* the replaced bit */ |
|
2469 i = rd->e; /* start of next chunk of *sp */ |
|
2470 } |
|
2471 lleft += l - i; /* final chunk from *sp */ |
|
2472 start = t = zhalloc(lleft+1); |
|
2473 i = 0; |
|
2474 for (nd = firstnode(repllist); nd; incnode(nd)) { |
|
2475 rd = (Repldata) getdata(nd); |
|
2476 memcpy(t, s + i, rd->b - i); |
|
2477 t += rd->b - i; |
|
2478 ptr = rd->replstr; |
|
2479 while (*ptr) |
|
2480 *t++ = *ptr++; |
|
2481 i = rd->e; |
|
2482 } |
|
2483 memcpy(t, s + i, l - i); |
|
2484 start[lleft] = '\0'; |
|
2485 *sp = (char *)start; |
|
2486 return 1; |
|
2487 } |
|
2488 |
|
2489 /* munge the whole string: no match, so no replstr */ |
|
2490 *sp = get_match_ret(*sp, 0, 0, fl, 0); |
|
2491 return 1; |
|
2492 } |
|
2493 |
|
2494 /* blindly turn a string into a tokenised expression without lexing */ |
|
2495 |
|
2496 /**/ |
|
2497 mod_export void |
|
2498 tokenize(char *s) |
|
2499 { |
|
2500 zshtokenize(s, 0); |
|
2501 } |
|
2502 |
|
2503 /**/ |
|
2504 mod_export void |
|
2505 shtokenize(char *s) |
|
2506 { |
|
2507 zshtokenize(s, isset(SHGLOB)); |
|
2508 } |
|
2509 |
|
2510 /**/ |
|
2511 static void |
|
2512 zshtokenize(char *s, int shglob) |
|
2513 { |
|
2514 char *t; |
|
2515 int bslash = 0; |
|
2516 |
|
2517 for (; *s; s++) { |
|
2518 cont: |
|
2519 switch (*s) { |
|
2520 case Bnull: |
|
2521 case '\\': |
|
2522 if (bslash) { |
|
2523 s[-1] = Bnull; |
|
2524 break; |
|
2525 } |
|
2526 bslash = 1; |
|
2527 continue; |
|
2528 case '<': |
|
2529 if (shglob) |
|
2530 break; |
|
2531 if (bslash) { |
|
2532 s[-1] = Bnull; |
|
2533 break; |
|
2534 } |
|
2535 t = s; |
|
2536 while (idigit(*++s)); |
|
2537 if (*s != '-') |
|
2538 goto cont; |
|
2539 while (idigit(*++s)); |
|
2540 if (*s != '>') |
|
2541 goto cont; |
|
2542 *t = Inang; |
|
2543 *s = Outang; |
|
2544 break; |
|
2545 case '(': |
|
2546 case '|': |
|
2547 case ')': |
|
2548 if (shglob) |
|
2549 break; |
|
2550 case '>': |
|
2551 case '^': |
|
2552 case '#': |
|
2553 case '~': |
|
2554 case '[': |
|
2555 case ']': |
|
2556 case '*': |
|
2557 case '?': |
|
2558 case '=': |
|
2559 for (t = ztokens; *t; t++) |
|
2560 if (*t == *s) { |
|
2561 if (bslash) |
|
2562 s[-1] = Bnull; |
|
2563 else |
|
2564 *s = (t - ztokens) + Pound; |
|
2565 break; |
|
2566 } |
|
2567 } |
|
2568 bslash = 0; |
|
2569 } |
|
2570 } |
|
2571 |
|
2572 /* remove unnecessary Nulargs */ |
|
2573 |
|
2574 /**/ |
|
2575 mod_export void |
|
2576 remnulargs(char *s) |
|
2577 { |
|
2578 if (*s) { |
|
2579 char *o = s, c; |
|
2580 |
|
2581 while ((c = *s++)) |
|
2582 if (INULL(c)) { |
|
2583 char *t = s - 1; |
|
2584 |
|
2585 while ((c = *s++)) |
|
2586 if (!INULL(c)) |
|
2587 *t++ = c; |
|
2588 *t = '\0'; |
|
2589 if (!*o) { |
|
2590 o[0] = Nularg; |
|
2591 o[1] = '\0'; |
|
2592 } |
|
2593 break; |
|
2594 } |
|
2595 } |
|
2596 } |
|
2597 |
|
2598 /* qualifier functions: mostly self-explanatory, see glob(). */ |
|
2599 |
|
2600 /* device number */ |
|
2601 |
|
2602 /**/ |
|
2603 static int |
|
2604 qualdev(UNUSED(char *name), struct stat *buf, off_t dv, UNUSED(char *dummy)) |
|
2605 { |
|
2606 return (off_t)buf->st_dev == dv; |
|
2607 } |
|
2608 |
|
2609 /* number of hard links to file */ |
|
2610 |
|
2611 /**/ |
|
2612 static int |
|
2613 qualnlink(UNUSED(char *name), struct stat *buf, off_t ct, UNUSED(char *dummy)) |
|
2614 { |
|
2615 return (g_range < 0 ? buf->st_nlink < ct : |
|
2616 g_range > 0 ? buf->st_nlink > ct : |
|
2617 buf->st_nlink == ct); |
|
2618 } |
|
2619 |
|
2620 /* user ID */ |
|
2621 |
|
2622 /**/ |
|
2623 static int |
|
2624 qualuid(UNUSED(char *name), struct stat *buf, off_t uid, UNUSED(char *dummy)) |
|
2625 { |
|
2626 return buf->st_uid == uid; |
|
2627 } |
|
2628 |
|
2629 /* group ID */ |
|
2630 |
|
2631 /**/ |
|
2632 static int |
|
2633 qualgid(UNUSED(char *name), struct stat *buf, off_t gid, UNUSED(char *dummy)) |
|
2634 { |
|
2635 return buf->st_gid == gid; |
|
2636 } |
|
2637 |
|
2638 /* device special file? */ |
|
2639 |
|
2640 /**/ |
|
2641 static int |
|
2642 qualisdev(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2643 { |
|
2644 return S_ISBLK(buf->st_mode) || S_ISCHR(buf->st_mode); |
|
2645 } |
|
2646 |
|
2647 /* block special file? */ |
|
2648 |
|
2649 /**/ |
|
2650 static int |
|
2651 qualisblk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2652 { |
|
2653 return S_ISBLK(buf->st_mode); |
|
2654 } |
|
2655 |
|
2656 /* character special file? */ |
|
2657 |
|
2658 /**/ |
|
2659 static int |
|
2660 qualischr(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2661 { |
|
2662 return S_ISCHR(buf->st_mode); |
|
2663 } |
|
2664 |
|
2665 /* directory? */ |
|
2666 |
|
2667 /**/ |
|
2668 static int |
|
2669 qualisdir(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2670 { |
|
2671 return S_ISDIR(buf->st_mode); |
|
2672 } |
|
2673 |
|
2674 /* FIFO? */ |
|
2675 |
|
2676 /**/ |
|
2677 static int |
|
2678 qualisfifo(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2679 { |
|
2680 return S_ISFIFO(buf->st_mode); |
|
2681 } |
|
2682 |
|
2683 /* symbolic link? */ |
|
2684 |
|
2685 /**/ |
|
2686 static int |
|
2687 qualislnk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2688 { |
|
2689 return S_ISLNK(buf->st_mode); |
|
2690 } |
|
2691 |
|
2692 /* regular file? */ |
|
2693 |
|
2694 /**/ |
|
2695 static int |
|
2696 qualisreg(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2697 { |
|
2698 return S_ISREG(buf->st_mode); |
|
2699 } |
|
2700 |
|
2701 /* socket? */ |
|
2702 |
|
2703 /**/ |
|
2704 static int |
|
2705 qualissock(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) |
|
2706 { |
|
2707 return S_ISSOCK(buf->st_mode); |
|
2708 } |
|
2709 |
|
2710 /* given flag is set in mode */ |
|
2711 |
|
2712 /**/ |
|
2713 static int |
|
2714 qualflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) |
|
2715 { |
|
2716 return mode_to_octal(buf->st_mode) & mod; |
|
2717 } |
|
2718 |
|
2719 /* mode matches specification */ |
|
2720 |
|
2721 /**/ |
|
2722 static int |
|
2723 qualmodeflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) |
|
2724 { |
|
2725 long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12; |
|
2726 |
|
2727 return ((v & y) == y && !(v & n)); |
|
2728 } |
|
2729 |
|
2730 /* regular executable file? */ |
|
2731 |
|
2732 /**/ |
|
2733 static int |
|
2734 qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char *dummy)) |
|
2735 { |
|
2736 return S_ISREG(buf->st_mode) && (buf->st_mode & S_IXUGO); |
|
2737 } |
|
2738 |
|
2739 /* size in required range? */ |
|
2740 |
|
2741 /**/ |
|
2742 static int |
|
2743 qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy)) |
|
2744 { |
|
2745 #if defined(LONG_IS_64_BIT) || defined(OFF_T_IS_64_BIT) |
|
2746 # define QS_CAST_SIZE() |
|
2747 off_t scaled = buf->st_size; |
|
2748 #else |
|
2749 # define QS_CAST_SIZE() (unsigned long) |
|
2750 unsigned long scaled = (unsigned long)buf->st_size; |
|
2751 #endif |
|
2752 |
|
2753 switch (g_units) { |
|
2754 case TT_POSIX_BLOCKS: |
|
2755 scaled += 511l; |
|
2756 scaled /= 512l; |
|
2757 break; |
|
2758 case TT_KILOBYTES: |
|
2759 scaled += 1023l; |
|
2760 scaled /= 1024l; |
|
2761 break; |
|
2762 case TT_MEGABYTES: |
|
2763 scaled += 1048575l; |
|
2764 scaled /= 1048576l; |
|
2765 break; |
|
2766 } |
|
2767 |
|
2768 return (g_range < 0 ? scaled < QS_CAST_SIZE() size : |
|
2769 g_range > 0 ? scaled > QS_CAST_SIZE() size : |
|
2770 scaled == QS_CAST_SIZE() size); |
|
2771 #undef QS_CAST_SIZE |
|
2772 } |
|
2773 |
|
2774 /* time in required range? */ |
|
2775 |
|
2776 /**/ |
|
2777 static int |
|
2778 qualtime(UNUSED(char *name), struct stat *buf, off_t days, UNUSED(char *dummy)) |
|
2779 { |
|
2780 time_t now, diff; |
|
2781 |
|
2782 time(&now); |
|
2783 diff = now - (g_amc == 0 ? buf->st_atime : g_amc == 1 ? buf->st_mtime : |
|
2784 buf->st_ctime); |
|
2785 /* handle multipliers indicating units */ |
|
2786 switch (g_units) { |
|
2787 case TT_DAYS: |
|
2788 diff /= 86400l; |
|
2789 break; |
|
2790 case TT_HOURS: |
|
2791 diff /= 3600l; |
|
2792 break; |
|
2793 case TT_MINS: |
|
2794 diff /= 60l; |
|
2795 break; |
|
2796 case TT_WEEKS: |
|
2797 diff /= 604800l; |
|
2798 break; |
|
2799 case TT_MONTHS: |
|
2800 diff /= 2592000l; |
|
2801 break; |
|
2802 } |
|
2803 |
|
2804 return (g_range < 0 ? diff < days : |
|
2805 g_range > 0 ? diff > days : |
|
2806 diff == days); |
|
2807 } |
|
2808 |
|
2809 /* evaluate a string */ |
|
2810 |
|
2811 /**/ |
|
2812 static int |
|
2813 qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str) |
|
2814 { |
|
2815 Eprog prog; |
|
2816 |
|
2817 if ((prog = parse_string(str))) { |
|
2818 int ef = errflag, lv = lastval, ret; |
|
2819 |
|
2820 unsetparam("reply"); |
|
2821 setsparam("REPLY", ztrdup(name)); |
|
2822 |
|
2823 execode(prog, 1, 0); |
|
2824 |
|
2825 ret = lastval; |
|
2826 errflag = ef; |
|
2827 lastval = lv; |
|
2828 |
|
2829 if (!(inserts = getaparam("reply")) && |
|
2830 !(inserts = gethparam("reply"))) { |
|
2831 char *tmp; |
|
2832 |
|
2833 if ((tmp = getsparam("reply")) || (tmp = getsparam("REPLY"))) { |
|
2834 static char *tmparr[2]; |
|
2835 |
|
2836 tmparr[0] = tmp; |
|
2837 tmparr[1] = NULL; |
|
2838 |
|
2839 inserts = tmparr; |
|
2840 } |
|
2841 } |
|
2842 return !ret; |
|
2843 } |
|
2844 return 0; |
|
2845 } |
|
2846 |
|
2847 /**/ |
|
2848 static int |
|
2849 qualnonemptydir(char *name, struct stat *buf, UNUSED(off_t days), UNUSED(char *str)) |
|
2850 { |
|
2851 DIR *dirh; |
|
2852 struct dirent *de; |
|
2853 |
|
2854 if (!S_ISDIR(buf->st_mode)) |
|
2855 return 0; |
|
2856 |
|
2857 if (buf->st_nlink > 2) |
|
2858 return 1; |
|
2859 |
|
2860 if (!(dirh = opendir(name))) |
|
2861 return 0; |
|
2862 |
|
2863 while ((de = readdir(dirh))) { |
|
2864 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { |
|
2865 closedir(dirh); |
|
2866 return 1; |
|
2867 } |
|
2868 } |
|
2869 |
|
2870 closedir(dirh); |
|
2871 return 0; |
|
2872 } |