The R Project SVN R

Rev

Rev 87319 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
44013 ripley 1
/* modified from dos_glob.c to work with wchar_t */
2
 
3
/*
4
 * Copyright (c) 1989, 1993
5
 *	The Regents of the University of California.  All rights reserved.
6
 *
7
 * This code is derived from software contributed to Berkeley by
8
 * Guido van Rossum.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 * 3. Neither the name of the University nor the names of its contributors
19
 *    may be used to endorse or promote products derived from this software
20
 *    without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
 * SUCH DAMAGE.
33
 */
34
 
35
#if defined(LIBC_SCCS) && !defined(lint)
36
static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
37
/* most changes between the version above and the one below have been ported:
38
static char sscsid[]=  "$OpenBSD: glob.c,v 1.8.10.1 2001/04/10 jason Exp $";
39
 */
40
#endif /* LIBC_SCCS and not lint */
41
 
42
/*
43
 * glob(3) -- a superset of the one defined in POSIX 1003.2.
44
 *
45
 * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
46
 *
47
 * Optional extra services, controlled by flags not defined by POSIX:
48
 *
49
 * GLOB_QUOTE:
50
 *	Escaping convention: \ inhibits any special meaning the following
51
 *	character might have (except \ at end of string is retained).
52
 * GLOB_MAGCHAR:
53
 *	Set in gl_flags if pattern contained a globbing character.
54
 * GLOB_NOMAGIC:
55
 *	Same as GLOB_NOCHECK, but it will only append pattern if it did
56
 *	not contain any magic characters.  [Used in csh style globbing]
57
 * GLOB_ALTDIRFUNC:
58
 *	Use alternately specified directory access functions.
59
 * GLOB_TILDE:
60
 *	expand ~user/foo to the /home/dir/of/user/foo
61
 * GLOB_BRACE:
62
 *	expand {1,2}{a,b} to 1a 1b 2a 2b
63
 * gl_matchc:
64
 *	Number of matches in the current invocation of glob.
65
 * GLOB_ALPHASORT:
66
 *	sort alphabetically like csh (case doesn't matter) instead of in ASCII
67
 *	order
68
 */
69
 
70
#include <wchar.h>
71
#include "dos_wglob.h"
72
 
46024 ripley 73
//#define GLOB_DEBUG
44013 ripley 74
 
46024 ripley 75
#ifdef GLOB_DEBUG
76
void Rprintf(const char *, ...);
77
#endif
78
 
83897 kalibera 79
/* declarations from Defn.h, definitions in platform.c */
80
struct R_wdirent {
81
    wchar_t *d_name; /* null-terminated filename */
82
};
83
 
84
typedef struct R_WDIR_INTERNAL R_WDIR;
85
 
86
R_WDIR *R_wopendir(const wchar_t *name);
87
struct R_wdirent *R_wreaddir(R_WDIR *rdir);
88
int R_wclosedir(R_WDIR *rdir);
89
 
83820 kalibera 90
#define	MAXPATHLEN	65536	
44013 ripley 91
#define DOSISH
92
#define ARG_MAX		14500
93
 
94
 
46029 ripley 95
#define	BG_DOLLAR	L'$'
96
#define	BG_DOT		L'.'
97
#define	BG_EOS		L'\0'
98
#define	BG_LBRACKET	L'['
99
#define	BG_NOT		L'!'
100
#define	BG_QUESTION	L'?'
101
#define	BG_QUOTE	L'\\'
102
#define	BG_RANGE	L'-'
103
#define	BG_RBRACKET	L']'
104
#define	BG_SEP		L'/'
77337 kalibera 105
#ifdef DOSISH /* true, cannot be set to false anymore */
46029 ripley 106
#define BG_SEP2		L'\\'
44013 ripley 107
#endif
46029 ripley 108
#define	BG_STAR		L'*'
109
#define	BG_TILDE	L'~'
110
#define	BG_UNDERSCORE	L'_'
111
#define	BG_LBRACE	L'{'
112
#define	BG_RBRACE	L'}'
113
#define	BG_SLASH	L'/'
114
#define	BG_COMMA	L','
44013 ripley 115
 
116
 
117
#include <stdlib.h>
118
#include <dirent.h>
119
#include <sys/types.h>
120
#include <sys/stat.h>
121
#include <string.h>
122
#include <wctype.h>
123
 
124
 
125
typedef size_t STRLEN;
126
typedef struct _stat Stat_t;
83897 kalibera 127
typedef struct R_wdirent Direntry_t;
44013 ripley 128
 
129
 
130
static int	 compare(const void *, const void *);
131
static int	 ci_compare(const void *, const void *);
132
static int	 g_Ctoc(const wchar_t *, wchar_t *, STRLEN);
133
static int	 g_lstat(wchar_t *, Stat_t *, wglob_t *);
83897 kalibera 134
static R_WDIR	*g_opendir(wchar_t *, wglob_t *);
44013 ripley 135
static const wchar_t *
136
		 g_strchr(const wchar_t *, int);
137
static int	 glob0(const wchar_t *, wglob_t *);
138
static int	 glob1(wchar_t *, wchar_t *, wglob_t *, size_t *);
139
static int	 glob2(wchar_t *, wchar_t *, wchar_t *, wchar_t *, wchar_t *, wchar_t *,
140
		       wglob_t *, size_t *);
141
static int	 glob3(wchar_t *, wchar_t *, wchar_t *, wchar_t *, wchar_t *, wchar_t *,
142
		       wchar_t *, wchar_t *, wglob_t *, size_t *);
143
static int	 globextend(const wchar_t *, wglob_t *, size_t *);
144
static const wchar_t *
145
		 globtilde(const wchar_t *, wchar_t *, size_t, wglob_t *);
146
static int	 globexp1(const wchar_t *, wglob_t *);
147
static int	 globexp2(const wchar_t *, const wchar_t *, wglob_t *, int *);
148
static int	 match(wchar_t *, wchar_t *, wchar_t *, int);
149
#ifdef GLOB_DEBUG
150
static void	 qprintf(const char *, wchar_t *);
151
#endif /* GLOB_DEBUG */
152
 
77337 kalibera 153
/* 
154
   Protected and meta characters are from Unicode Private Use Area.
155
   The DOS version set the upper bit of bytes for meta characters.
156
   An earlier wchar_t version set the top bits of wchar_t.
157
*/
158
 
159
#define	M_MASK		0xffff /* pointless in current setup */
160
 
161
#define P_LBRACKET  (wchar_t)	0xfdd0
162
#define P_RBRACKET  (wchar_t)	0xfdd1
163
#define P_RANGE	    (wchar_t)	0xfdd2
164
#define P_LBRACE    (wchar_t)	0xfdd3
165
#define P_RBRACE    (wchar_t)	0xfdd4
166
#define P_TILDE	    (wchar_t)	0xfdd5
167
#define P_QUOTE	    (wchar_t)	0xfdd6
168
#define	M_ALL	    (wchar_t)	0xfdd7
169
#define	M_END	    (wchar_t)	0xfdd8
170
#define	M_NOT	    (wchar_t)	0xfdd9
171
#define	M_ONE	    (wchar_t)	0xfdda
172
#define	M_RNG	    (wchar_t)	0xfddb
173
#define	M_SET	    (wchar_t)	0xfddc
174
 
175
static wchar_t WC_PROTECT(wchar_t c)
176
{
177
    /* []-{}~\ */
178
 
179
    switch(c) {
180
	case BG_LBRACKET: return P_LBRACKET;
181
	case BG_RBRACKET: return P_RBRACKET;
182
	case BG_RANGE: return P_RANGE;
183
	case BG_LBRACE: return P_LBRACE;
184
	case BG_RBRACE: return P_RBRACE;
185
	case BG_TILDE: return P_TILDE;
186
	case BG_QUOTE: return P_QUOTE;
187
	default:
188
	    /* not reachable */
189
	    return c;
190
    }
191
}
192
 
193
static int ismeta(wchar_t c)
194
{
195
    switch(c) {
196
	case M_ALL:
197
	case M_END:
198
	case M_NOT:
199
	case M_ONE:
200
	case M_RNG:
201
	case M_SET:
202
	    return 1;
203
	default:
204
	    return 0;
205
    }
206
}
207
 
208
static wchar_t CHAR(wchar_t c)
209
{
210
    switch(c) {
211
	case P_LBRACKET: return BG_LBRACKET;
212
	case P_RBRACKET: return BG_RBRACKET;
213
	case P_RANGE: return BG_RANGE;
214
	case P_LBRACE: return BG_LBRACE;
215
	case P_RBRACE: return BG_RBRACE;
216
	case P_TILDE: return BG_TILDE;
217
	case P_QUOTE: return BG_QUOTE;
218
	case M_ALL: return BG_STAR;
219
	case M_END: return BG_RBRACKET;
220
	case M_NOT: return BG_NOT;
221
	case M_ONE: return BG_QUESTION;
222
	case M_RNG: return BG_RANGE;
223
	case M_SET: return BG_LBRACKET;
224
	default:
225
	    return c;
226
    }
227
}
228
 
44013 ripley 229
int
230
dos_wglob(const wchar_t *pattern, int flags,
231
	  int (*errfunc)(const wchar_t *, int), wglob_t *pglob)
232
{
233
    const wchar_t *patnext;
234
    int c;
235
    wchar_t *bufnext, *bufend, patbuf[MAXPATHLEN];
236
 
237
    patnext = pattern;
238
#if 1
239
    if (!(flags & GLOB_APPEND)) {
240
	pglob->gl_pathc = 0;
241
	pglob->gl_pathv = NULL;
242
	if (!(flags & GLOB_DOOFFS))
243
	    pglob->gl_offs = 0;
244
    }
245
#else
246
    pglob->gl_pathc = 0;
247
    pglob->gl_pathv = NULL;
248
    pglob->gl_offs = 0;
249
#endif
250
    pglob->gl_flags = flags & ~GLOB_MAGCHAR;
251
    pglob->gl_errfunc = errfunc;
252
    pglob->gl_matchc = 0;
45070 ripley 253
 
44013 ripley 254
    bufnext = patbuf;
255
    bufend = bufnext + MAXPATHLEN - 1;
46175 ripley 256
#ifdef DOSISH /* true */
44013 ripley 257
    /* Nasty hack to treat patterns like "C:*" correctly. In this
258
     * case, the * should match any file in the current directory
259
     * on the C: drive. However, the glob code does not treat the
260
     * colon specially, so it looks for files beginning "C:" in
261
     * the current directory. To fix this, change the pattern to
262
     * add an explicit "./" at the start (just after the drive
263
     * letter and colon - ie change to "C:./").
264
     */
46029 ripley 265
    if (iswalpha(pattern[0]) && pattern[1] == L':' &&
44013 ripley 266
	pattern[2] != BG_SEP && pattern[2] != BG_SEP2 &&
267
	bufend - bufnext > 4) {
268
	*bufnext++ = pattern[0];
46029 ripley 269
	*bufnext++ = L':';
270
	*bufnext++ = L'.';
44013 ripley 271
	*bufnext++ = BG_SEP;
272
	patnext += 2;
273
    }
49215 ripley 274
 
275
    /* Hack from Tony Plate to allow UNC network drive specification:
276
     * Without this code, '\\' (i.e., literally two backslashes inpattern)
277
     * at the beginning of a path is not recognized as a network drive,
278
     * because the GLOB_QUOTE loop below changes the two backslashes to one.
279
     * So, in the case where there are two but not three backslashes at
280
     * the beginning of the path, transfer these to the output.
281
     */
282
    if (patnext == pattern && bufend - bufnext > 2 &&
283
	pattern[0] == BG_SEP2 && pattern[1] == BG_SEP2 &&
284
	pattern[2] != BG_SEP2) {
59110 murdoch 285
	*bufnext++ = pattern[0];
286
	*bufnext++ = pattern[1];
49215 ripley 287
	patnext += 2;
288
    }
44013 ripley 289
#endif
290
 
291
    if (flags & GLOB_QUOTE) {
292
	/* Protect the quoted characters. */
293
	while (bufnext < bufend && (c = *patnext++) != BG_EOS)
294
	    if (c == BG_QUOTE) {
46175 ripley 295
#ifdef DOSISH /* true */
44013 ripley 296
		/* To avoid backslashitis on Win32,
297
		 * we only treat \ as a quoting character
298
		 * if it precedes one of the
299
		 * metacharacters []-{}~\
300
		 */
46029 ripley 301
		if ((c = *patnext++) != L'[' && c != L']' &&
302
		    c != L'-' && c != L'{' && c != L'}' &&
303
		    c != L'~' && c != L'\\') {
77337 kalibera 304
		    /* WC_PROTECT has to support all characters above */
44013 ripley 305
#else
77337 kalibera 306
# error DOSISH must be true
45070 ripley 307
		if ((c = *patnext++) == BG_EOS) {
44013 ripley 308
#endif
309
		    c = BG_QUOTE;
310
		    --patnext;
311
		}
77337 kalibera 312
		*bufnext++ = WC_PROTECT(c);
44013 ripley 313
	    } else
314
		*bufnext++ = c;
45070 ripley 315
	} else
44013 ripley 316
	    while (bufnext < bufend && (c = *patnext++) != BG_EOS)
317
		*bufnext++ = c;
318
    *bufnext = BG_EOS;
319
 
320
    if (flags & GLOB_BRACE)
321
	return globexp1(patbuf, pglob);
322
    else
323
	return glob0(patbuf, pglob);
324
}
325
 
326
/*
327
 * Expand recursively a glob {} pattern. When there is no more expansion
328
 * invoke the standard globbing routine to glob the rest of the magic
329
 * characters
330
 */
331
static int
332
globexp1(const wchar_t *pattern, wglob_t *pglob)
333
{
334
    const wchar_t* ptr = pattern;
335
    int rv;
336
 
337
    /* Protect a single {}, for find(1), like csh */
338
    if (pattern[0] == BG_LBRACE && pattern[1] == BG_RBRACE && pattern[2] == BG_EOS)
339
	return glob0(pattern, pglob);
340
 
341
    while ((ptr = (const wchar_t *) g_strchr(ptr, BG_LBRACE)) != NULL)
342
	if (!globexp2(ptr, pattern, pglob, &rv))
343
	    return rv;
344
 
345
    return glob0(pattern, pglob);
346
}
347
 
348
 
349
/*
350
 * Recursive brace globbing helper. Tries to expand a single brace.
351
 * If it succeeds then it invokes globexp1 with the new pattern.
352
 * If it fails then it tries to glob the rest of the pattern and returns.
353
 */
354
static int
355
globexp2(const wchar_t *ptr, const wchar_t *pattern,
356
	 wglob_t *pglob, int *rv)
357
{
358
    int     i;
359
    wchar_t   *lm, *ls;
360
    const wchar_t *pe, *pm, *pl;
361
    wchar_t    patbuf[MAXPATHLEN];
362
 
363
    /* copy part up to the brace */
364
    for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
365
	;
366
    *lm = BG_EOS;
367
    ls = lm;
368
 
369
    /* Find the balanced brace */
370
    for (i = 0, pe = ++ptr; *pe; pe++)
371
	if (*pe == BG_LBRACKET) {
372
	    /* Ignore everything between [] */
373
	    for (pm = pe++; *pe != BG_RBRACKET && *pe != BG_EOS; pe++)
374
		;
375
	    if (*pe == BG_EOS) {
376
		/*
377
		 * We could not find a matching BG_RBRACKET.
378
		 * Ignore and just look for BG_RBRACE
379
		 */
380
		pe = pm;
381
	    }
382
	} else if (*pe == BG_LBRACE)
383
	    i++;
384
	else if (*pe == BG_RBRACE) {
385
	    if (i == 0)
386
		break;
387
	    i--;
388
	}
389
 
390
    /* Non matching braces; just glob the pattern */
391
    if (i != 0 || *pe == BG_EOS) {
392
	*rv = glob0(patbuf, pglob);
393
	return 0;
394
    }
395
 
396
    for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
397
	switch (*pm) {
398
	case BG_LBRACKET:
399
	    /* Ignore everything between [] */
400
	    for (pl = pm++; *pm != BG_RBRACKET && *pm != BG_EOS; pm++)
401
		;
402
	    if (*pm == BG_EOS) {
403
		/*
404
		 * We could not find a matching BG_RBRACKET.
405
		 * Ignore and just look for BG_RBRACE
406
		 */
407
		pm = pl;
408
	    }
409
	    break;
410
 
411
	case BG_LBRACE:
412
	    i++;
413
	    break;
414
 
415
	case BG_RBRACE:
416
	    if (i) {
417
		i--;
418
		break;
419
	    }
420
	    /* FALLTHROUGH */
421
	case BG_COMMA:
422
	    if (i && *pm == BG_COMMA)
423
		break;
424
	    else {
425
		/* Append the current string */
426
		for (lm = ls; (pl < pm); *lm++ = *pl++)
427
		    ;
428
 
429
		/*
430
		 * Append the rest of the pattern after the
431
		 * closing brace
432
		 */
433
		for (pl = pe + 1; (*lm++ = *pl++) != BG_EOS; )
434
		    ;
435
 
436
		/* Expand the current pattern */
437
#ifdef GLOB_DEBUG
438
		qprintf("globexp2:", patbuf);
439
#endif /* GLOB_DEBUG */
440
		*rv = globexp1(patbuf, pglob);
441
 
442
		/* move after the comma, to the next string */
443
		pl = pm + 1;
444
	    }
445
	    break;
446
 
447
	default:
448
	    break;
449
	}
450
    }
451
    *rv = 0;
452
    return 0;
453
}
454
 
455
 
456
 
457
/*
458
 * expand tilde from the passwd file: not supported.
459
 */
460
static const wchar_t *
461
globtilde(const wchar_t *pattern, wchar_t *patbuf, size_t patbuf_len, wglob_t *pglob)
462
{
463
    wchar_t *h;
464
    const wchar_t *p;
465
    wchar_t *b, *eb;
466
 
467
    if (*pattern != BG_TILDE || !(pglob->gl_flags & GLOB_TILDE))
468
	return pattern;
469
 
470
    /* Copy up to the end of the string or / */
471
    eb = &patbuf[patbuf_len - 1];
472
    for (p = pattern + 1, h = (wchar_t *) patbuf;
473
	 h < eb && *p && *p != BG_SLASH; *h++ = *p++)
474
	;
475
 
476
    *h = BG_EOS;
477
 
478
    if (((wchar_t *) patbuf)[0] == BG_EOS) {
479
	/*
480
	 * handle a plain ~ or ~/ by expanding $HOME
481
	 * first and then trying the password file
482
	 */
483
	if ((h = _wgetenv(L"R_USER")) == NULL) {
484
	    return pattern;
485
	}
486
    } else {
487
	/*
488
	 * Expand a ~user
489
	 */
490
	return pattern;
491
    }
492
 
493
    /* Copy the home directory */
494
    for (b = patbuf; b < eb && *h; *b++ = *h++)
495
	;
496
 
497
    /* Append the rest of the pattern */
498
    while (b < eb && (*b++ = *p++) != BG_EOS)
499
	;
500
    *b = BG_EOS;
501
 
502
    return patbuf;
503
}
504
 
505
 
506
/*
507
 * The main glob() routine: compiles the pattern (optionally processing
508
 * quotes), calls glob1() to do the real pattern matching, and finally
509
 * sorts the list (unless unsorted operation is requested).  Returns 0
510
 * if things went well, nonzero if errors occurred.  It is not an error
511
 * to find no matches.
512
 */
513
static int
514
glob0(const wchar_t *pattern, wglob_t *pglob)
515
{
516
    const wchar_t *qpat, *qpatnext;
517
    int c, err, oldflags, oldpathc;
518
    wchar_t *bufnext, patbuf[MAXPATHLEN];
519
    size_t limit = 0;
520
 
521
    qpat = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
522
    qpatnext = qpat;
523
    oldflags = pglob->gl_flags;
524
    oldpathc = pglob->gl_pathc;
525
    bufnext = patbuf;
526
 
527
    /* We don't need to check for buffer overflow any more. */
528
    while ((c = *qpatnext++) != BG_EOS) {
529
	switch (c) {
530
	case BG_LBRACKET:
531
	    c = *qpatnext;
532
	    if (c == BG_NOT)
533
		++qpatnext;
534
	    if (*qpatnext == BG_EOS ||
535
		g_strchr((wchar_t *) qpatnext+1, BG_RBRACKET) == NULL) {
536
		*bufnext++ = BG_LBRACKET;
537
		if (c == BG_NOT)
538
		    --qpatnext;
539
		break;
540
	    }
541
	    *bufnext++ = M_SET;
542
	    if (c == BG_NOT)
543
		*bufnext++ = M_NOT;
544
	    c = *qpatnext++;
545
	    do {
546
		*bufnext++ = CHAR(c);
547
		if (*qpatnext == BG_RANGE &&
548
		    (c = qpatnext[1]) != BG_RBRACKET) {
549
		    *bufnext++ = M_RNG;
550
		    *bufnext++ = CHAR(c);
551
		    qpatnext += 2;
552
		}
553
	    } while ((c = *qpatnext++) != BG_RBRACKET);
554
	    pglob->gl_flags |= GLOB_MAGCHAR;
555
	    *bufnext++ = M_END;
556
	    break;
557
	case BG_QUESTION:
558
	    pglob->gl_flags |= GLOB_MAGCHAR;
559
	    *bufnext++ = M_ONE;
560
	    break;
561
	case BG_STAR:
562
	    pglob->gl_flags |= GLOB_MAGCHAR;
563
	    /* collapse adjacent stars to one,
564
	     * to avoid exponential behavior
565
	     */
566
	    if (bufnext == patbuf || bufnext[-1] != M_ALL)
567
		*bufnext++ = M_ALL;
568
	    break;
569
	default:
570
	    *bufnext++ = CHAR(c);
571
	    break;
572
	}
573
    }
574
    *bufnext = BG_EOS;
575
#ifdef GLOB_DEBUG
576
    qprintf("glob0:", patbuf);
577
#endif /* GLOB_DEBUG */
578
 
579
    if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0) {
580
	pglob->gl_flags = oldflags;
581
	return(err);
582
    }
583
 
584
    /*
585
     * If there was no match we are going to append the pattern
586
     * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
587
     * and the pattern did not contain any magic characters
588
     * GLOB_NOMAGIC is there just for compatibility with csh.
589
     */
590
    if (pglob->gl_pathc == oldpathc &&
591
	((pglob->gl_flags & GLOB_NOCHECK) ||
592
	 ((pglob->gl_flags & GLOB_NOMAGIC) &&
593
	  !(pglob->gl_flags & GLOB_MAGCHAR))))
594
    {
595
#ifdef GLOB_DEBUG
46024 ripley 596
	Rprintf("calling globextend from glob0\n");
44013 ripley 597
#endif /* GLOB_DEBUG */
598
	pglob->gl_flags = oldflags;
599
	return(globextend(qpat, pglob, &limit));
600
    }
87508 kalibera 601
    else if (!(pglob->gl_flags & GLOB_NOSORT) &&
602
	      (pglob->gl_pathc - oldpathc) > 0)
44013 ripley 603
	qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
604
	      pglob->gl_pathc - oldpathc, sizeof(wchar_t *),
605
	      (pglob->gl_flags & (GLOB_ALPHASORT|GLOB_NOCASE))
606
	      ? ci_compare : compare);
607
    pglob->gl_flags = oldflags;
608
    return(0);
609
}
610
 
611
static int
612
ci_compare(const void *p, const void *q)
613
{
614
    const wchar_t *pp = *(const wchar_t **)p;
615
    const wchar_t *qq = *(const wchar_t **)q;
616
    int ci;
617
    while (*pp && *qq) {
618
	if (towlower(*pp) != towlower(*qq))
619
	    break;
620
	++pp;
621
	++qq;
622
    }
623
    ci = towlower(*pp) - towlower(*qq);
624
    if (ci == 0)
625
	return compare(p, q);
626
    return ci;
627
}
628
 
629
static int
630
compare(const void *p, const void *q)
631
{
632
    return(wcscmp(*(wchar_t **)p, *(wchar_t **)q));
633
}
634
 
635
static int
636
glob1(wchar_t *pattern, wchar_t *pattern_last, wglob_t *pglob, size_t *limitp)
637
{
638
    wchar_t pathbuf[MAXPATHLEN];
639
 
640
    /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
641
    if (*pattern == BG_EOS) return(0);
642
    return(glob2(pathbuf, pathbuf+MAXPATHLEN-1,
643
		 pathbuf, pathbuf+MAXPATHLEN-1,
644
		 pattern, pattern_last, pglob, limitp));
645
}
646
 
647
/*
648
 * The functions glob2 and glob3 are mutually recursive; there is one level
649
 * of recursion for each segment in the pattern that contains one or more
650
 * meta characters.
651
 */
652
static int
653
glob2(wchar_t *pathbuf, wchar_t *pathbuf_last, wchar_t *pathend, wchar_t *pathend_last,
654
      wchar_t *pattern, wchar_t *pattern_last, wglob_t *pglob, size_t *limitp)
655
{
656
    Stat_t sb;
657
    wchar_t *p, *q;
658
    int anymeta;
659
 
660
    /*
661
     * Loop over pattern segments until end of pattern or until
662
     * segment with meta character found.
663
     */
664
    for (anymeta = 0;;) {
665
	if (*pattern == BG_EOS) {		/* End of pattern? */
666
	    *pathend = BG_EOS;
667
	    if (g_lstat(pathbuf, &sb, pglob)) return(0);
668
 
669
	    if (((pglob->gl_flags & GLOB_MARK) &&
670
		 pathend[-1] != BG_SEP
46175 ripley 671
#ifdef DOSISH /* true */
44013 ripley 672
		 && pathend[-1] != BG_SEP2
673
#endif
674
		    ) && S_ISDIR(sb.st_mode) ) {
675
		if (pathend+1 > pathend_last)
676
		    return (1);
677
		*pathend++ = BG_SEP;
678
		*pathend = BG_EOS;
679
	    }
680
	    ++pglob->gl_matchc;
681
#ifdef GLOB_DEBUG
46024 ripley 682
	    Rprintf("calling globextend from glob2\n");
44013 ripley 683
#endif /* GLOB_DEBUG */
684
	    return(globextend(pathbuf, pglob, limitp));
685
	}
686
 
687
	/* Find end of next segment, copy tentatively to pathend. */
688
	q = pathend;
689
	p = pattern;
690
	while (*p != BG_EOS && *p != BG_SEP
46175 ripley 691
#ifdef DOSISH /* true */
44013 ripley 692
	       && *p != BG_SEP2
693
#endif
694
	    ) {
695
	    if (ismeta(*p)) anymeta = 1;
696
	    if (q+1 > pathend_last) return (1);
697
	    *q++ = *p++;
698
	}
699
 
700
	if (!anymeta) {		/* No expansion, do next segment. */
701
	    pathend = q;
702
	    pattern = p;
703
	    while (*pattern == BG_SEP
46175 ripley 704
#ifdef DOSISH /* true */
44013 ripley 705
		   || *pattern == BG_SEP2
706
#endif
707
		) {
708
		if (pathend+1 > pathend_last) return (1);
709
		*pathend++ = *pattern++;
710
	    }
711
	} else
712
	    /* Need expansion, recurse. */
713
	    return(glob3(pathbuf, pathbuf_last, pathend,
714
			 pathend_last, pattern, pattern_last,
715
			 p, pattern_last, pglob, limitp));
716
    }
717
    /* NOTREACHED */
718
}
719
 
720
static int
721
glob3(wchar_t *pathbuf, wchar_t *pathbuf_last, wchar_t *pathend, wchar_t *pathend_last,
722
      wchar_t *pattern, wchar_t *pattern_last,
723
      wchar_t *restpattern, wchar_t *restpattern_last, wglob_t *pglob, size_t *limitp)
724
{
46029 ripley 725
    Direntry_t *dp;
83897 kalibera 726
    R_WDIR *dirp;
44013 ripley 727
    int err;
728
    int nocase;
729
    wchar_t buf[MAXPATHLEN];
730
 
731
    if (pathend > pathend_last)
732
	return (1);
733
    *pathend = BG_EOS;
734
    errno = 0;
735
 
736
    if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
737
	/* TODO: don't call for ENOENT or ENOTDIR? */
738
	if (pglob->gl_errfunc) {
739
	    if (g_Ctoc(pathbuf, buf, sizeof(buf)))
740
		return (GLOB_ABEND);
741
	    if (pglob->gl_errfunc(buf, errno) ||
742
		(pglob->gl_flags & GLOB_ERR))
743
		return (GLOB_ABEND);
744
	}
745
	return(0);
746
    }
747
 
748
    err = 0;
749
    nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);
750
 
751
    /* Search directory for matching names. */
83897 kalibera 752
    while ((dp = R_wreaddir(dirp))) {
44013 ripley 753
	wchar_t *sc, *dc;
754
 
755
	/* Initial BG_DOT must be matched literally. */
756
	if (dp->d_name[0] == BG_DOT && *pattern != BG_DOT)
757
	    continue;
758
	dc = pathend;
759
	sc = dp->d_name;
760
	while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS)
761
	    ;
762
	if (dc >= pathend_last) {
763
	    *dc = BG_EOS;
764
	    err = 1;
765
	    break;
766
	}
767
 
768
	if (!match(pathend, pattern, restpattern, nocase)) {
769
	    *pathend = BG_EOS;
770
	    continue;
771
	}
772
	err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
773
		    restpattern, restpattern_last, pglob, limitp);
774
	if (err)
775
	    break;
776
    }
777
 
83897 kalibera 778
    R_wclosedir(dirp);
44013 ripley 779
    return(err);
780
}
781
 
782
 
81491 ripley 783
#include <R_ext/RS.h> /* for R_Calloc, R_Realloc, R_Free */
44013 ripley 784
 
785
/*
84215 maechler 786
 * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
44013 ripley 787
 * add the new item, and update gl_pathc.
788
 *
789
 * This assumes the BSD realloc, which only copies the block when its size
790
 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
791
 * behavior.
792
 *
793
 * Return 0 if new item added, error code if memory couldn't be allocated.
794
 *
795
 * Invariant of the glob_t structure:
796
 *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
797
 *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
798
 */
799
static int
800
globextend(const wchar_t *path, wglob_t *pglob, size_t *limitp)
801
{
46029 ripley 802
    wchar_t **pathv;
803
    int i;
44013 ripley 804
    STRLEN newsize, len;
805
    wchar_t *copy;
806
    const wchar_t *p;
807
 
808
#ifdef GLOB_DEBUG
46024 ripley 809
    Rprintf("Adding ");
44013 ripley 810
    for (p = path; *p; p++)
46024 ripley 811
	(void)Rprintf("%c", CHAR(*p));
812
    Rprintf("\n");
44013 ripley 813
#endif /* GLOB_DEBUG */
814
 
815
    newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
816
    if (pglob->gl_pathv)
81491 ripley 817
	pathv = R_Realloc(pglob->gl_pathv, newsize, wchar_t *);
44013 ripley 818
    else
81491 ripley 819
	pathv = R_Calloc(newsize, wchar_t *);
44013 ripley 820
    if (pathv == NULL) {
821
	if (pglob->gl_pathv) {
87319 kalibera 822
	    R_Free(pglob->gl_pathv);
44013 ripley 823
	    pglob->gl_pathv = NULL;
824
	}
825
	return(GLOB_NOSPACE);
826
    }
827
 
828
    if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
829
	/* first time around -- clear initial gl_offs items */
830
	pathv += pglob->gl_offs;
831
	for (i = pglob->gl_offs; --i >= 0; )
832
	    *--pathv = NULL;
833
    }
834
    pglob->gl_pathv = pathv;
835
 
836
    for (p = path; *p++;)
837
	;
838
    len = (STRLEN)(p - path);
839
    *limitp += len;
81491 ripley 840
    copy = R_Calloc(p-path, wchar_t);
44013 ripley 841
    if (copy != NULL) {
842
	if (g_Ctoc(path, copy, len)) {
81491 ripley 843
	    R_Free(copy);
44013 ripley 844
	    return(GLOB_NOSPACE);
845
	}
846
	pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
847
    }
848
    pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
849
 
850
    if ((pglob->gl_flags & GLOB_LIMIT) &&
851
	newsize + *limitp >= ARG_MAX) {
852
	errno = 0;
853
	return(GLOB_NOSPACE);
854
    }
855
 
856
    return(copy == NULL ? GLOB_NOSPACE : 0);
857
}
858
 
859
 
860
/*
861
 * pattern matching function for filenames.  Each occurrence of the *
862
 * pattern causes a recursion level.
863
 */
864
static int
46029 ripley 865
match(wchar_t *name, wchar_t *pat, wchar_t *patend, int nocase)
44013 ripley 866
{
867
    int ok, negate_range;
868
    wchar_t c, k;
869
 
870
    while (pat < patend) {
871
	c = *pat++;
872
	switch (c & M_MASK) {
873
	case M_ALL:
874
	    if (pat == patend)
875
		return(1);
876
	    do
877
		if (match(name, pat, patend, nocase))
878
		    return(1);
879
	    while (*name++ != BG_EOS)
880
		;
881
	    return(0);
882
	case M_ONE:
883
	    if (*name++ == BG_EOS)
884
		return(0);
885
	    break;
886
	case M_SET:
887
	    ok = 0;
888
	    if ((k = *name++) == BG_EOS)
889
		return(0);
890
	    if ((negate_range = ((*pat & M_MASK) == M_NOT)) != BG_EOS)
891
		++pat;
892
	    while (((c = *pat++) & M_MASK) != M_END)
893
		if ((*pat & M_MASK) == M_RNG) {
894
		    if (nocase) {
895
			if (towlower(c) <= towlower(k) && towlower(k) <= towlower(pat[1]))
896
			    ok = 1;
897
		    } else {
898
			if (c <= k && k <= pat[1])
899
			    ok = 1;
900
		    }
901
		    pat += 2;
902
		} else if (nocase ? (towlower(c) == towlower(k)) : (c == k))
903
		    ok = 1;
904
	    if (ok == negate_range)
905
		return(0);
906
	    break;
907
	default:
908
	    k = *name++;
909
	    if (nocase ? (towlower(k) != towlower(c)) : (k != c))
910
		return(0);
45070 ripley 911
	    break;
44013 ripley 912
	}
913
    }
914
    return(*name == BG_EOS);
915
}
916
 
917
/* Free allocated data belonging to a wglob_t structure. */
918
void
919
dos_wglobfree(wglob_t *pglob)
920
{
46029 ripley 921
    int i;
922
    wchar_t **pp;
44013 ripley 923
 
924
    if (pglob->gl_pathv != NULL) {
925
	pp = pglob->gl_pathv + pglob->gl_offs;
926
	for (i = pglob->gl_pathc; i--; ++pp)
927
	    if (*pp)
87319 kalibera 928
		R_Free(*pp);
929
	R_Free(pglob->gl_pathv);
44013 ripley 930
	pglob->gl_pathv = NULL;
931
    }
932
}
933
 
83897 kalibera 934
static R_WDIR *
46029 ripley 935
g_opendir(wchar_t *str, wglob_t *pglob)
44013 ripley 936
{
937
    wchar_t buf[MAXPATHLEN];
938
 
939
    if (!*str) wcscpy(buf, L".");
940
    else
941
	if (g_Ctoc(str, buf, sizeof(buf))) return(NULL);
83897 kalibera 942
    return R_wopendir(buf);
44013 ripley 943
}
944
 
945
static int
46029 ripley 946
g_lstat(wchar_t *fn, Stat_t *sb, wglob_t *pglob)
44013 ripley 947
{
948
    wchar_t buf[MAXPATHLEN];
949
 
950
    if (g_Ctoc(fn, buf, sizeof(buf)))
951
	return(-1);
952
    return(_wstat(buf, sb));
953
}
954
 
955
static const wchar_t *
956
g_strchr(const wchar_t *str, int ch)
957
{
958
    do {
959
	if (*str == ch)
960
	    return (str);
961
    } while (*str++);
962
    return (NULL);
963
}
964
 
965
static int
46029 ripley 966
g_Ctoc(const wchar_t *str, wchar_t *buf, STRLEN len)
44013 ripley 967
{
46029 ripley 968
    while (len--)
969
	if ((*buf++ = *str++) == BG_EOS) return 0;
970
    return 1;
44013 ripley 971
}
972
 
973
#ifdef GLOB_DEBUG
974
static void
46029 ripley 975
qprintf(const char *str, wchar_t *s)
44013 ripley 976
{
46029 ripley 977
    wchar_t *p;
44013 ripley 978
 
46024 ripley 979
    (void)Rprintf("%s:\n", str);
44013 ripley 980
    for (p = s; *p; p++)
46024 ripley 981
	(void)Rprintf("%lc", CHAR(*p));
982
    (void)Rprintf("\n");
46029 ripley 983
#if 0
44013 ripley 984
    for (p = s; *p; p++)
46029 ripley 985
	(void)Rprintf("%lc", *p & M_PROTECT ? L'"' : L' ');
46024 ripley 986
    (void)Rprintf("\n");
46029 ripley 987
#endif
44013 ripley 988
    for (p = s; *p; p++)
46029 ripley 989
	(void)Rprintf("%lc", ismeta(*p) ? L'_' : L' ');
46024 ripley 990
    (void)Rprintf("\n");
44013 ripley 991
}
992
#endif /* GLOB_DEBUG */