1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv
.glob
/*is aliced*/;
22 ////////////////////////////////////////////////////////////////////////////////
23 // low-level interface
25 import core
.sys
.posix
.sys
.stat
: stat_t
;
26 import core
.sys
.posix
.dirent
: dirent
;
27 //import core.sys.posix.config : __USE_FILE_OFFSET64;
28 import core
.sys
.linux
.config
: __USE_GNU
, __USE_MISC
;
30 //static assert(__USE_MISC);
32 nothrow @trusted @nogc extern(C
) {
34 /* Bits set in the FLAGS argument to `glob'. */
36 GLOB_ERR
= (1 << 0), /* Return on read errors. */
37 GLOB_MARK
= (1 << 1), /* Append a slash to each name. */
38 GLOB_NOSORT
= (1 << 2), /* Don't sort the names. */
39 GLOB_DOOFFS
= (1 << 3), /* Insert PGLOB->gl_offs NULLs. */
40 GLOB_NOCHECK
= (1 << 4), /* If nothing matches, return the pattern. */
41 GLOB_APPEND
= (1 << 5), /* Append to results of a previous call. */
42 GLOB_NOESCAPE
= (1 << 6), /* Backslashes don't quote metacharacters. */
43 GLOB_PERIOD
= (1 << 7), /* Leading `.' can be matched by metachars. */
46 /* posix-2 extensions */
47 static if (__USE_MISC
) enum {
48 GLOB_MAGCHAR
= (1 << 8), /* Set in gl_flags if any metachars seen. */
49 GLOB_ALTDIRFUNC
= (1 << 9), /* Use gl_opendir et al functions. */
50 GLOB_BRACE
= (1 << 10), /* Expand "{a,b}" to "a" "b". */
51 GLOB_NOMAGIC
= (1 << 11), /* If no magic chars, return the pattern. */
52 GLOB_TILDE
= (1 << 12), /* Expand ~user and ~ to home directories. */
53 GLOB_ONLYDIR
= (1 << 13), /* Match only directories. */
54 GLOB_TILDE_CHECK
= (1 << 14), /* Like GLOB_TILDE but return an error if the user name is not available. */
57 /* Error returns from `glob'. */
61 GLOB_NOSPACE
= 1, /* Ran out of memory. */
62 GLOB_ABORTED
= 2, /* Read error. */
63 GLOB_NOMATCH
= 3, /* No matches found. */
64 GLOB_NOSYS
= 4, /* Not implemented. */
66 /* Previous versions of this file defined GLOB_ABEND instead of
67 GLOB_ABORTED. Provide a compatibility definition here. */
68 static if (__USE_GNU
) enum GLOB_ABEND
= GLOB_ABORTED
;
77 /* If the GLOB_ALTDIRFUNC flag is set, the following functions
78 are used instead of the normal file access functions. */
79 static if (__USE_GNU
) {
80 void function (void*) gl_closedir
;
81 dirent
* function (void*) gl_readdir
;
82 void* function (const char *) gl_opendir
;
83 int function (const(char)*, stat_t
*) gl_lstat
;
84 int function (const(char)*, stat_t
*) gl_stat
;
86 void function (void*) gl_closedir
;
87 void* function (void*) gl_readdir
;
88 void* function (const char *) gl_opendir
;
89 int function (const(char)*, void*) gl_lstat
;
90 int function (const(char)*, void*) gl_stat
;
94 alias GlobErrFunc
= int function (const(char)* epath
, int eerrno
) @trusted nothrow @nogc;
96 /* Do glob searching for PATTERN, placing results in PGLOB.
97 The bits defined above may be set in FLAGS.
98 If a directory cannot be opened or read and ERRFUNC is not nil,
99 it is called with the pathname that caused the error, and the
100 `errno' value from the failing call; if it returns non-zero
101 `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.
102 If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
103 Otherwise, `glob' returns zero. */
104 int glob (const(char)* pattern
, int flags
, GlobErrFunc errfunc
, glob_t
* pglob
);
106 /* Free storage allocated in PGLOB by a previous `glob' call. */
107 void globfree (glob_t
* pglob
);
109 /* Return nonzero if PATTERN contains any metacharacters.
110 Metacharacters can be quoted with backslashes if QUOTE is nonzero.
112 This function is not part of the interface specified by POSIX.2
113 but several programs want to use it. */
114 static if (__USE_GNU
) int glob_pattern_p (const(char)* __pattern
, int __quote
);
118 ////////////////////////////////////////////////////////////////////////////////
119 // high-level interface
121 private import std
.traits
;
124 private int opApplyX(string dir
, DG
) (scope DG dg
)
126 (ParameterTypeTuple
!DG
.length
== 1 &&
127 (is(Unqual
!(ParameterTypeTuple
!DG
[0]) : Item
)) ||
128 is(ParameterTypeTuple
!DG
[0] == string
) ||
129 is(ParameterTypeTuple
!DG
[0] == const(char)[]) ||
130 is(ParameterTypeTuple
!DG
[0] == const char[]) ||
131 is(ParameterTypeTuple
!DG
[0] == char[])) ||
132 (ParameterTypeTuple
!DG
.length
== 2 && isIntegral
!(ParameterTypeTuple
!DG
[0]) &&
133 (is(Unqual
!(ParameterTypeTuple
!DG
[1]) : Item
) ||
134 is(ParameterTypeTuple
!DG
[1] == string
) ||
135 is(ParameterTypeTuple
!DG
[1] == const(char)[]) ||
136 is(ParameterTypeTuple
!DG
[1] == const char[]) ||
137 is(ParameterTypeTuple
!DG
[1] == char[])))
140 alias ptt
= ParameterTypeTuple
!DG
;
141 alias xarg
= ptt
[ptt
.length
-1];
143 enum foreachBody
= q
{
144 static if (is(Unqual
!(xarg
) : Item
)) {
145 auto it
= Item(ge
, idx
);
147 // it's ok to cast here
148 auto it
= cast(xarg
)getName(idx
).dup
;
150 static if (ptt
.length
== 2) {
151 static if (is(typeof(idx
) == ptt
[0])) {
154 auto i
= cast(ptt
[0])idx
;
162 static if (dir
== "normal") {
163 foreach (usize idx
; 0..ge
.gb
.gl_pathc
) { mixin(foreachBody
); }
164 } else static if (dir
== "reverse") {
165 foreach_reverse (usize idx
; 0..ge
.gb
.gl_pathc
) { mixin(foreachBody
); }
167 static assert(false, "wtf?!");
172 auto opApply(Args
...) (Args args
) { return opApplyX
!("normal", Args
)(args
); }
173 auto opApplyReverse(Args
...) (Args args
) { return opApplyX
!("reverse", Args
)(args
); }
175 nothrow @nogc: // ah, let's dance!
177 this (globent
* ange
, usize idx
=0) {
183 this (const(char)[] pattern
, int flags
=GLOB_BRACE|GLOB_TILDE_CHECK
) {
184 // remove bad flags, add good flags
185 flags
&= ~(GLOB_DOOFFS|GLOB_APPEND
);
186 static if (__USE_MISC
) flags
&= ~(GLOB_MAGCHAR|GLOB_ALTDIRFUNC
);
187 import core
.stdc
.stdlib
: malloc
;
188 import core
.stdc
.string
: memset
;
189 import core
.exception
: onOutOfMemoryErrorNoGC
;
190 import std
.internal
.cstring
: tempCString
;
191 ge
= cast(globent
*)malloc(globent
.sizeof
);
192 if (ge
is null) onOutOfMemoryErrorNoGC();
193 version(iv_glob_debug
) { import core
.stdc
.stdio
: stdout
, fprintf
; fprintf(stdout
, "new %p\n", ge
); }
194 memset(ge
, globent
.sizeof
, 0); // just in case
196 ge
.res
= .glob(pattern
.tempCString
, flags|GLOB_ERR
, null, &ge
.gb
);
199 this (this) { Glob
.incref(ge
); }
201 ~this () { Glob
.decref(ge
); }
203 // note that "no match" is error too!
204 @property bool error () pure const { return (ge
.res
!= 0); }
205 @property bool nomatch () pure const { return (ge
.res
== GLOB_NOMATCH
); }
207 @property usize
length () pure const { return (idx
< ge
.gb
.gl_pathc ? ge
.gb
.gl_pathc
-idx
: 0); }
208 alias opDollar
= length
;
210 void rewind () { idx
= 0; }
212 @property bool empty () pure const { return (idx
>= ge
.gb
.gl_pathc
); }
213 @property auto save () { return Glob(ge
, idx
); }
214 void popFront () { if (idx
< ge
.gb
.gl_pathc
) ++idx
; }
216 @property auto front () {
217 if (idx
>= ge
.gb
.gl_pathc
) {
218 import core
.exception
: RangeError
;
219 throw staticError
!RangeError();
221 return Item(ge
, idx
);
224 auto opIndex (usize idx
) {
225 if (idx
>= ge
.gb
.gl_pathc
) {
226 import core
.exception
: RangeError
;
227 throw staticError
!RangeError();
229 return Item(ge
, idx
);
233 static struct globent
{
239 public static struct Item
{
240 public import core
.sys
.posix
.sys
.types
: INode
= ino_t
;
241 private import core
.sys
.posix
.sys
.stat
;
242 nothrow @trusted @nogc: // ah, let's dance!
249 this (globent
* ange
, usize anidx
) {
252 statvalid
= false; // just in case
258 if (!statvalid
&& idx
< ge
.gb
.gl_pathc
) {
259 if (stat(ge
.gb
.gl_pathv
[idx
], &st
) == 0) {
268 this (this) { assert(ge
!is null); Glob
.incref(ge
); }
269 ~this () { assert(ge
!is null); Glob
.decref(ge
); }
271 @property usize
index () pure const { return idx
; }
272 @property usize
length () pure const { return ge
.gb
.gl_pathc
; }
274 // WARNING! this can escape!
275 @property const(char)[] name () pure const return {
276 if (idx
< ge
.gb
.gl_pathc
) {
278 auto ptr
= ge
.gb
.gl_pathv
[idx
];
279 while (ptr
[pos
]) ++pos
;
286 // WARNING! this can escape!
287 @property const(char)[] basename () pure const return {
288 if (idx
< ge
.gb
.gl_pathc
) {
290 auto ptr
= ge
.gb
.gl_pathv
[idx
];
291 while (ptr
[pos
]) ++pos
;
293 while (pos
> 0 && ptr
[pos
-1] != '/') --pos
;
294 return ptr
[pos
..epos
];
300 @property bool prev () { if (idx
> 0) { statvalid
= false; --idx
; return true; } else return false; }
301 @property bool next () { if (idx
< ge
.gb
.gl_pathc
) { statvalid
= false; ++idx
; return true; } else return false; }
303 bool getStat (ref stat_t outst
) const {
304 if (idx
< ge
.gb
.gl_pathc
) {
307 if (stat(ge
.gb
.gl_pathv
[idx
], &st
) == 0) { outst
= st
; return true; }
316 bool getStat (ref stat_t outst
) {
317 if (updateStat()) { outst
= st
; return true; }
323 return (getStat(st
) ? st
.st_mode
: 0);
327 return (updateStat() ? st
.st_mode
: 0);
330 @property bool isFile () inout { pragma(inline
, true); return ((mode
&S_IFREG
) != 0); } // symlinks are regular files too!
331 @property bool isDir () inout { pragma(inline
, true); return ((mode
&S_IFDIR
) != 0); }
332 @property bool isLink () inout { pragma(inline
, true); return ((mode
&S_IFLNK
) == S_IFLNK
); }
334 @property ulong size () const {
336 return (getStat(st
) ? st
.st_size
: 0);
339 @property ulong size () {
340 return (updateStat() ? st
.st_size
: 0);
343 @property INode
inode () const {
345 return (getStat(st
) ? st
.st_ino
: 0);
348 @property INode
inode () {
349 return (updateStat() ? st
.st_ino
: 0);
353 static void incref (globent
* ge
) @safe nothrow @nogc {
354 pragma(inline
, true);
359 static void decref (globent
* ge
) @trusted nothrow @nogc {
360 pragma(inline
, true);
362 if (--ge
.refcount
== 0) {
363 version(iv_glob_debug
) { import core
.stdc
.stdio
: stdout
, fprintf
; fprintf(stdout
, "freeing %p\n", ge
); }
364 import core
.stdc
.stdlib
: free
;
370 // WARNING! this can escape!
371 @property const(char)[] getName (usize idx
) pure const return {
373 auto ptr
= ge
.gb
.gl_pathv
[idx
];
374 while (ptr
[pos
]) ++pos
;
380 usize idx
; // current position in range
384 ////////////////////////////////////////////////////////////////////////////////
385 // shamelessly borrowed from `core.exception`
386 // TLS storage shared for all errors, chaining might create circular reference
387 private void[128] stestore_
;
389 // only Errors for now as those are rarely chained
390 private T
staticError(T
, Args
...) (auto ref Args args
) @nogc if (is(T
: Error
)) {
391 // pure hack, what we actually need is @noreturn and allow to call that in pure functions
393 static assert(__traits(classInstanceSize
, T
) <= stestore_
.length
, T
.stringof
~" is too large for staticError()");
394 stestore_
[0..__traits(classInstanceSize
, T
)] = typeid(T
).initializer
[];
395 return cast(T
)stestore_
.ptr
;
397 auto res
= (cast(T
function () pure nothrow @trusted @nogc) &get
)();
398 void doInit () { res
.__ctor(args
); }
399 void initIt (scope void delegate () dg
) {
400 auto xinit
= cast(void delegate () pure nothrow @trusted @nogc)dg
;