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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv
.glob
/*is aliced*/;
21 ////////////////////////////////////////////////////////////////////////////////
22 // low-level interface
24 import core
.sys
.posix
.sys
.stat
: stat_t
;
25 import core
.sys
.posix
.dirent
: dirent
;
26 //import core.sys.posix.config : __USE_FILE_OFFSET64;
27 import core
.sys
.linux
.config
: __USE_GNU
, __USE_MISC
;
29 //static assert(__USE_MISC);
31 nothrow @trusted @nogc extern(C
) {
33 /* Bits set in the FLAGS argument to `glob'. */
35 GLOB_ERR
= (1 << 0), /* Return on read errors. */
36 GLOB_MARK
= (1 << 1), /* Append a slash to each name. */
37 GLOB_NOSORT
= (1 << 2), /* Don't sort the names. */
38 GLOB_DOOFFS
= (1 << 3), /* Insert PGLOB->gl_offs NULLs. */
39 GLOB_NOCHECK
= (1 << 4), /* If nothing matches, return the pattern. */
40 GLOB_APPEND
= (1 << 5), /* Append to results of a previous call. */
41 GLOB_NOESCAPE
= (1 << 6), /* Backslashes don't quote metacharacters. */
42 GLOB_PERIOD
= (1 << 7), /* Leading `.' can be matched by metachars. */
45 /* posix-2 extensions */
46 static if (__USE_MISC
) enum {
47 GLOB_MAGCHAR
= (1 << 8), /* Set in gl_flags if any metachars seen. */
48 GLOB_ALTDIRFUNC
= (1 << 9), /* Use gl_opendir et al functions. */
49 GLOB_BRACE
= (1 << 10), /* Expand "{a,b}" to "a" "b". */
50 GLOB_NOMAGIC
= (1 << 11), /* If no magic chars, return the pattern. */
51 GLOB_TILDE
= (1 << 12), /* Expand ~user and ~ to home directories. */
52 GLOB_ONLYDIR
= (1 << 13), /* Match only directories. */
53 GLOB_TILDE_CHECK
= (1 << 14), /* Like GLOB_TILDE but return an error if the user name is not available. */
56 /* Error returns from `glob'. */
60 GLOB_NOSPACE
= 1, /* Ran out of memory. */
61 GLOB_ABORTED
= 2, /* Read error. */
62 GLOB_NOMATCH
= 3, /* No matches found. */
63 GLOB_NOSYS
= 4, /* Not implemented. */
65 /* Previous versions of this file defined GLOB_ABEND instead of
66 GLOB_ABORTED. Provide a compatibility definition here. */
67 static if (__USE_GNU
) enum GLOB_ABEND
= GLOB_ABORTED
;
76 /* If the GLOB_ALTDIRFUNC flag is set, the following functions
77 are used instead of the normal file access functions. */
78 static if (__USE_GNU
) {
79 void function (void*) gl_closedir
;
80 dirent
* function (void*) gl_readdir
;
81 void* function (const char *) gl_opendir
;
82 int function (const(char)*, stat_t
*) gl_lstat
;
83 int function (const(char)*, stat_t
*) gl_stat
;
85 void function (void*) gl_closedir
;
86 void* function (void*) gl_readdir
;
87 void* function (const char *) gl_opendir
;
88 int function (const(char)*, void*) gl_lstat
;
89 int function (const(char)*, void*) gl_stat
;
93 alias GlobErrFunc
= int function (const(char)* epath
, int eerrno
) @trusted nothrow @nogc;
95 /* Do glob searching for PATTERN, placing results in PGLOB.
96 The bits defined above may be set in FLAGS.
97 If a directory cannot be opened or read and ERRFUNC is not nil,
98 it is called with the pathname that caused the error, and the
99 `errno' value from the failing call; if it returns non-zero
100 `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.
101 If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
102 Otherwise, `glob' returns zero. */
103 int glob (const(char)* pattern
, int flags
, GlobErrFunc errfunc
, glob_t
* pglob
);
105 /* Free storage allocated in PGLOB by a previous `glob' call. */
106 void globfree (glob_t
* pglob
);
108 /* Return nonzero if PATTERN contains any metacharacters.
109 Metacharacters can be quoted with backslashes if QUOTE is nonzero.
111 This function is not part of the interface specified by POSIX.2
112 but several programs want to use it. */
113 static if (__USE_GNU
) int glob_pattern_p (const(char)* __pattern
, int __quote
);
117 ////////////////////////////////////////////////////////////////////////////////
118 // high-level interface
120 private import std
.traits
;
123 private int opApplyX(string dir
, DG
) (scope DG dg
)
125 (ParameterTypeTuple
!DG
.length
== 1 &&
126 (is(Unqual
!(ParameterTypeTuple
!DG
[0]) : Item
)) ||
127 is(ParameterTypeTuple
!DG
[0] == string
) ||
128 is(ParameterTypeTuple
!DG
[0] == const(char)[]) ||
129 is(ParameterTypeTuple
!DG
[0] == const char[]) ||
130 is(ParameterTypeTuple
!DG
[0] == char[])) ||
131 (ParameterTypeTuple
!DG
.length
== 2 && isIntegral
!(ParameterTypeTuple
!DG
[0]) &&
132 (is(Unqual
!(ParameterTypeTuple
!DG
[1]) : Item
) ||
133 is(ParameterTypeTuple
!DG
[1] == string
) ||
134 is(ParameterTypeTuple
!DG
[1] == const(char)[]) ||
135 is(ParameterTypeTuple
!DG
[1] == const char[]) ||
136 is(ParameterTypeTuple
!DG
[1] == char[])))
139 alias ptt
= ParameterTypeTuple
!DG
;
140 alias xarg
= ptt
[ptt
.length
-1];
142 enum foreachBody
= q
{
143 static if (is(Unqual
!(xarg
) : Item
)) {
144 auto it
= Item(ge
, idx
);
146 // it's ok to cast here
147 auto it
= cast(xarg
)getName(idx
).dup
;
149 static if (ptt
.length
== 2) {
150 static if (is(typeof(idx
) == ptt
[0])) {
153 auto i
= cast(ptt
[0])idx
;
161 static if (dir
== "normal") {
162 foreach (usize idx
; 0..ge
.gb
.gl_pathc
) { mixin(foreachBody
); }
163 } else static if (dir
== "reverse") {
164 foreach_reverse (usize idx
; 0..ge
.gb
.gl_pathc
) { mixin(foreachBody
); }
166 static assert(false, "wtf?!");
171 auto opApply(Args
...) (Args args
) { return opApplyX
!("normal", Args
)(args
); }
172 auto opApplyReverse(Args
...) (Args args
) { return opApplyX
!("reverse", Args
)(args
); }
174 nothrow @nogc: // ah, let's dance!
176 this (globent
* ange
, usize idx
=0) {
182 this (const(char)[] pattern
, int flags
=GLOB_BRACE|GLOB_TILDE_CHECK
) {
183 // remove bad flags, add good flags
184 flags
&= ~(GLOB_DOOFFS|GLOB_APPEND
);
185 static if (__USE_MISC
) flags
&= ~(GLOB_MAGCHAR|GLOB_ALTDIRFUNC
);
186 import core
.stdc
.stdlib
: malloc
;
187 import core
.stdc
.string
: memset
;
188 import core
.exception
: onOutOfMemoryErrorNoGC
;
189 import std
.internal
.cstring
: tempCString
;
190 ge
= cast(globent
*)malloc(globent
.sizeof
);
191 if (ge
is null) onOutOfMemoryErrorNoGC();
192 version(iv_glob_debug
) { import core
.stdc
.stdio
: stdout
, fprintf
; fprintf(stdout
, "new %p\n", ge
); }
193 memset(ge
, globent
.sizeof
, 0); // just in case
195 ge
.res
= .glob(pattern
.tempCString
, flags|GLOB_ERR
, null, &ge
.gb
);
198 this (this) { Glob
.incref(ge
); }
200 ~this () { Glob
.decref(ge
); }
202 // note that "no match" is error too!
203 @property bool error () pure const { return (ge
.res
!= 0); }
204 @property bool nomatch () pure const { return (ge
.res
== GLOB_NOMATCH
); }
206 @property usize
length () pure const { return (idx
< ge
.gb
.gl_pathc ? ge
.gb
.gl_pathc
-idx
: 0); }
207 alias opDollar
= length
;
209 void rewind () { idx
= 0; }
211 @property bool empty () pure const { return (idx
>= ge
.gb
.gl_pathc
); }
212 @property auto save () { return Glob(ge
, idx
); }
213 void popFront () { if (idx
< ge
.gb
.gl_pathc
) ++idx
; }
215 @property auto front () {
216 if (idx
>= ge
.gb
.gl_pathc
) {
217 import core
.exception
: RangeError
;
218 throw staticError
!RangeError();
220 return Item(ge
, idx
);
223 auto opIndex (usize idx
) {
224 if (idx
>= ge
.gb
.gl_pathc
) {
225 import core
.exception
: RangeError
;
226 throw staticError
!RangeError();
228 return Item(ge
, idx
);
232 static struct globent
{
238 public static struct Item
{
239 public import core
.sys
.posix
.sys
.types
: INode
= ino_t
;
240 private import core
.sys
.posix
.sys
.stat
;
241 nothrow @trusted @nogc: // ah, let's dance!
248 this (globent
* ange
, usize anidx
) {
251 statvalid
= false; // just in case
257 if (!statvalid
&& idx
< ge
.gb
.gl_pathc
) {
258 if (stat(ge
.gb
.gl_pathv
[idx
], &st
) == 0) {
267 this (this) { assert(ge
!is null); Glob
.incref(ge
); }
268 ~this () { assert(ge
!is null); Glob
.decref(ge
); }
270 @property usize
index () pure const { return idx
; }
271 @property usize
length () pure const { return ge
.gb
.gl_pathc
; }
273 // WARNING! this can escape!
274 @property const(char)[] name () pure const return {
275 if (idx
< ge
.gb
.gl_pathc
) {
277 auto ptr
= ge
.gb
.gl_pathv
[idx
];
278 while (ptr
[pos
]) ++pos
;
285 // WARNING! this can escape!
286 @property const(char)[] basename () pure const return {
287 if (idx
< ge
.gb
.gl_pathc
) {
289 auto ptr
= ge
.gb
.gl_pathv
[idx
];
290 while (ptr
[pos
]) ++pos
;
292 while (pos
> 0 && ptr
[pos
-1] != '/') --pos
;
293 return ptr
[pos
..epos
];
299 @property bool prev () { if (idx
> 0) { statvalid
= false; --idx
; return true; } else return false; }
300 @property bool next () { if (idx
< ge
.gb
.gl_pathc
) { statvalid
= false; ++idx
; return true; } else return false; }
302 bool getStat (ref stat_t outst
) const {
303 if (idx
< ge
.gb
.gl_pathc
) {
306 if (stat(ge
.gb
.gl_pathv
[idx
], &st
) == 0) { outst
= st
; return true; }
315 bool getStat (ref stat_t outst
) {
316 if (updateStat()) { outst
= st
; return true; }
322 return (getStat(st
) ? st
.st_mode
: 0);
326 return (updateStat() ? st
.st_mode
: 0);
329 @property bool isFile () inout { pragma(inline
, true); return ((mode
&S_IFREG
) != 0); } // symlinks are regular files too!
330 @property bool isDir () inout { pragma(inline
, true); return ((mode
&S_IFDIR
) != 0); }
331 @property bool isLink () inout { pragma(inline
, true); return ((mode
&S_IFLNK
) == S_IFLNK
); }
333 @property ulong size () const {
335 return (getStat(st
) ? st
.st_size
: 0);
338 @property ulong size () {
339 return (updateStat() ? st
.st_size
: 0);
342 @property INode
inode () const {
344 return (getStat(st
) ? st
.st_ino
: 0);
347 @property INode
inode () {
348 return (updateStat() ? st
.st_ino
: 0);
352 static void incref (globent
* ge
) @safe nothrow @nogc {
353 pragma(inline
, true);
358 static void decref (globent
* ge
) @trusted nothrow @nogc {
359 pragma(inline
, true);
361 if (--ge
.refcount
== 0) {
362 version(iv_glob_debug
) { import core
.stdc
.stdio
: stdout
, fprintf
; fprintf(stdout
, "freeing %p\n", ge
); }
363 import core
.stdc
.stdlib
: free
;
369 // WARNING! this can escape!
370 @property const(char)[] getName (usize idx
) pure const return {
372 auto ptr
= ge
.gb
.gl_pathv
[idx
];
373 while (ptr
[pos
]) ++pos
;
379 usize idx
; // current position in range
383 ////////////////////////////////////////////////////////////////////////////////
384 // shamelessly borrowed from `core.exception`
385 // TLS storage shared for all errors, chaining might create circular reference
386 private void[128] stestore_
;
388 // only Errors for now as those are rarely chained
389 private T
staticError(T
, Args
...) (auto ref Args args
) @nogc if (is(T
: Error
)) {
390 // pure hack, what we actually need is @noreturn and allow to call that in pure functions
392 static assert(__traits(classInstanceSize
, T
) <= stestore_
.length
, T
.stringof
~" is too large for staticError()");
393 stestore_
[0..__traits(classInstanceSize
, T
)] = typeid(T
).initializer
[];
394 return cast(T
)stestore_
.ptr
;
396 auto res
= (cast(T
function () pure nothrow @trusted @nogc) &get
)();
397 void doInit () { res
.__ctor(args
); }
398 void initIt (scope void delegate () dg
) {
399 auto xinit
= cast(void delegate () pure nothrow @trusted @nogc)dg
;