cosmetix
[iv.d.git] / glob.d
blob4bac75ccf21af2bfc5463288122923b49ae34e98
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*/;
19 import iv.alice;
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'. */
35 enum {
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'. */
58 enum {
59 GLOB_OK = 0,
60 GLOB_NOERROR = 0,
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;
71 struct glob_t {
72 usize gl_pathc;
73 char** gl_pathv;
74 usize gl_offs;
75 int gl_flags;
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;
85 } else {
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
120 struct Glob {
121 private import std.traits;
123 @trusted:
124 private int opApplyX(string dir, DG) (scope DG dg)
125 if (isCallable!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];
142 int res = 0;
143 enum foreachBody = q{
144 static if (is(Unqual!(xarg) : Item)) {
145 auto it = Item(ge, idx);
146 } else {
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])) {
152 res = dg(idx, it);
153 } else {
154 auto i = cast(ptt[0])idx;
155 res = dg(i, it);
157 } else {
158 res = dg(it);
160 if (res) break;
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); }
166 } else {
167 static assert(false, "wtf?!");
169 return res;
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!
176 private:
177 this (globent* ange, usize idx=0) {
178 ge = ange;
179 Glob.incref(ge);
182 public:
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
195 ge.refcount = 1;
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);
232 private:
233 static struct globent {
234 usize refcount;
235 glob_t gb;
236 int res;
239 public static struct Item {
240 public import core.sys.posix.sys.types : INode = ino_t;
241 nothrow @trusted @nogc: // ah, let's dance!
242 private:
243 globent* ge;
244 usize idx;
246 this (globent* ange, usize anidx) {
247 ge = ange;
248 idx = anidx;
249 Glob.incref(ge);
252 public:
253 this (this) { assert(ge !is null); Glob.incref(ge); }
254 ~this () { assert(ge !is null); Glob.decref(ge); }
256 @property usize index () pure const { return idx; }
257 @property usize length () pure const { return ge.gb.gl_pathc; }
259 // WARNING! this can escape!
260 @property const(char)[] name () pure const return {
261 if (idx < ge.gb.gl_pathc) {
262 usize pos = 0;
263 auto ptr = ge.gb.gl_pathv[idx];
264 while (ptr[pos]) ++pos;
265 return ptr[0..pos];
266 } else {
267 return "";
271 // WARNING! this can escape!
272 @property const(char)[] basename () pure const return {
273 if (idx < ge.gb.gl_pathc) {
274 usize pos = 0;
275 auto ptr = ge.gb.gl_pathv[idx];
276 while (ptr[pos]) ++pos;
277 usize epos = pos;
278 while (pos > 0 && ptr[pos-1] != '/') --pos;
279 return ptr[pos..epos];
280 } else {
281 return "";
285 @property bool prev () { if (idx > 0) { --idx; return true; } else return false; }
286 @property bool next () { if (idx < ge.gb.gl_pathc) { ++idx; return true; } else return false; }
288 private bool checkStatFlag (uint flag) const {
289 if (idx < ge.gb.gl_pathc) {
290 import core.sys.posix.sys.stat;
291 stat_t st = void;
292 if (stat(ge.gb.gl_pathv[idx], &st) == 0) return ((st.st_mode&flag) != 0);
294 return false;
297 @property bool isFile () const { pragma(inline, true); import core.sys.posix.sys.stat; return checkStatFlag(S_IFREG); }
298 @property bool isDir () const { pragma(inline, true); import core.sys.posix.sys.stat; return checkStatFlag(S_IFDIR); }
299 @property bool isLink () const { pragma(inline, true); import core.sys.posix.sys.stat; return checkStatFlag(S_IFLNK); }
301 @property ulong size () const {
302 if (idx < ge.gb.gl_pathc) {
303 import core.sys.posix.sys.stat;
304 stat_t st = void;
305 if (stat(ge.gb.gl_pathv[idx], &st) == 0) return st.st_size;
307 return 0;
310 @property INode inode () const {
311 if (idx < ge.gb.gl_pathc) {
312 import core.sys.posix.sys.stat;
313 stat_t st = void;
314 if (stat(ge.gb.gl_pathv[idx], &st) == 0) return st.st_size;
316 return 0;
320 static void incref (globent* ge) @safe nothrow @nogc {
321 pragma(inline, true);
322 assert(ge !is null);
323 ++ge.refcount;
326 static void decref (globent* ge) @trusted nothrow @nogc {
327 pragma(inline, true);
328 assert(ge !is null);
329 if (--ge.refcount == 0) {
330 version(iv_glob_debug) { import core.stdc.stdio : stdout, fprintf; fprintf(stdout, "freeing %p\n", ge); }
331 import core.stdc.stdlib : free;
332 globfree(&ge.gb);
333 free(ge);
337 // WARNING! this can escape!
338 @property const(char)[] getName (usize idx) pure const return {
339 usize pos = 0;
340 auto ptr = ge.gb.gl_pathv[idx];
341 while (ptr[pos]) ++pos;
342 return ptr[0..pos];
345 private:
346 globent* ge;
347 usize idx; // current position in range
351 ////////////////////////////////////////////////////////////////////////////////
352 // shamelessly borrowed from `core.exception`
353 // TLS storage shared for all errors, chaining might create circular reference
354 private void[128] stestore_;
356 // only Errors for now as those are rarely chained
357 private T staticError(T, Args...) (auto ref Args args) @nogc if (is(T : Error)) {
358 // pure hack, what we actually need is @noreturn and allow to call that in pure functions
359 static T get () {
360 static assert(__traits(classInstanceSize, T) <= stestore_.length, T.stringof~" is too large for staticError()");
361 stestore_[0..__traits(classInstanceSize, T)] = typeid(T).initializer[];
362 return cast(T)stestore_.ptr;
364 auto res = (cast(T function () pure nothrow @trusted @nogc) &get)();
365 void doInit () { res.__ctor(args); }
366 void initIt (scope void delegate () dg) {
367 auto xinit = cast(void delegate () pure nothrow @trusted @nogc)dg;
368 xinit();
370 initIt(&doInit);
371 return res;