1 #include <stdexcept>
2 #include <algorithm>
3 #include "varexp.h"
4 #include "auto_dir.h"
5 #if defined(_WIN32)
6 #    include <io.h>
7 #    define F_OK 0
8 #    define X_OK 0
9 #else
10 #    include <unistd.h>
11 #endif
12 #include "fileexp.h"
13 
14 /*
15 
16   copyright (c) 2004, 2005, 2015 squell <squell@alumina.nl>
17 
18   use, modification, copying and distribution of this software is permitted
19   under the conditions described in the file 'COPYING'.
20 
21 */
22 
23 #if defined(__DJGPP__) || defined(_WIN32)
24 #    define readlink(path, buf, n) (-1)         // readlink dummy
25 #endif
26 
27 using namespace std;
28 
29 namespace fileexp {
30 
31  /* filefind::nested variables split in two categories: stuff that drives the
32     the recursion (parameters); and supporting information (class members) */
33 
34 struct filefind : record {
35     char  mask[PATH_MAX];
36     find* invoker;
37     char* rec_base;                             // start of recursive search
38 
39     class direxp;
40     bool  nested (auto_dir, char* pathpos, char* filespec);
41     char* pathcpy(char*, const char*);          // special strcpy variant
42 };
43 
glob(const char * filemask,bool wildslash)44 bool find::glob(const char* filemask, bool wildslash)
45 {
46     filefind t;
47     strncpy(t.mask, filemask, sizeof t.mask);
48     t.path[0]  = t.mask[sizeof t.mask-1] = '\0'; // duct tape
49     t.invoker  = this;
50     t.rec_base = wildslash? t.path : 0;
51 #if defined(_WIN32)
52     if(strncmp(t.mask, "//", 2) == 0) {         // unc path
53         char* shmask = strchr(t.mask+2, '/');
54         if(shmask && (shmask=strchr(++shmask, '/'))) {
55             *shmask++ = '\0';                   // separate sharename
56             char* shpath = t.pathcpy(t.pathcpy(t.path, t.mask), "/");
57             if(t.rec_base) t.rec_base = shpath;
58             return t.nested(auto_dir(t.mask), shpath, shmask);
59         }
60         return false;
61     } else
62 #endif
63     return t.nested(auto_dir("./"), t.path, t.mask);
64 }
65 
66 struct filefind::direxp : varexp {              // special dotfile handling
is_specialfileexp::filefind::direxp67     inline static bool is_special(const char* fn)
68     {
69          return (fn[0] == '.') && (!fn[1] || fn[1] == '.' && !fn[2]);
70     }
71 
direxpfileexp::filefind::direxp72     direxp(const char* mask, const char* test, bool force = false)
73     {
74 #if defined(__DJGPP__) || defined(_WIN32)
75         if(force || !is_special(test))
76             result = match(mask, test);
77         else
78             result = 0;
79 #else                                           // conform to Bourne shell
80         if(force || test[0] != '.' || mask[0] == '.')
81             result = match(mask, test);
82         else
83             result = 0;
84 #endif
85     }
86 };
87 
pathcpy(char * dest,const char * src)88 char* filefind::pathcpy(char* dest, const char* src)
89 {
90     do {                                        // added safety
91         if(dest == &path[sizeof path])
92             throw length_error("filefindexp: pathname error");
93     } while(*dest++ = *src++);
94     return dest-1;                              // return *resume*-ptr
95 }
96 
97 struct slash_split {
slash_splitfileexp::slash_split98     slash_split(char* p) : ptr(p) { *ptr = '\0'; }
~slash_splitfileexp::slash_split99    ~slash_split()                 { *ptr = '/'; }
100 private:
101     char* ptr;
102 };
103 
104   // recursive file search routine, leaves this->mask nonsensical on success
105   // pathpos  - write position (inside path)
106   // filespec - read position  (inside mask)
107 
nested(auto_dir dir,char * pathpos,char * filespec)108 bool filefind::nested(auto_dir dir, char* pathpos, char* filespec)
109 {
110     typedef vector<string> strvec;
111     strvec::size_type prevlen = var.size();     // backup value
112     char* wpos;                                 // next write position
113 
114     bool w = false;                             // idle check
115 
116     if(!rec_base || rec_base == pathpos)
117     while( char* fndirsep = strchr(filespec, '/') ) {
118         slash_split lock(fndirsep++);
119         wpos = pathcpy(pathcpy(pathpos, filespec), "/");
120         if(access(path, X_OK) == 0) {
121             dir      = auto_dir(path);          // if allready a valid
122             pathpos  = wpos;                    //   directory, use as is
123             filespec = fndirsep;                // (tail recursion)
124             if(rec_base) rec_base = pathpos;
125         } else if(!strpbrk(filespec, "*?[") || !dir) {
126             return false;                       // shortcut mismatchers
127         } else if(rec_base && strchr(filespec,'*')) {
128             break;
129         } else {
130             while( dirent* fn = dir.read() ) {  // search cur open dir
131                 direxp match(filespec, fn->d_name);
132                 if(match) {
133                     wpos = pathcpy(pathcpy(pathpos, fn->d_name), "/");
134                     if(rec_base) rec_base = wpos;
135                     if(auto_dir newdir = auto_dir(path)) {
136                         for(varexp::iterator i = match.begin(); i != match.end(); ++i)
137                             var.push_back(*i);
138                         w |= nested(newdir, wpos, fndirsep);
139                         var.resize(prevlen);
140                     }
141                 }
142             }
143             return w;
144         }
145     }
146 
147     if(! invoker->dir(*this) )
148         return false;
149 
150     if(*filespec == '\0')
151         return invoker->file(pathpos, *this);
152 
153     pathcpy(pathpos, filespec);                 // check if file is 'simple'
154     if(!strpbrk(filespec, "*?["))
155         return access(path, F_OK) == 0 && invoker->file(pathpos, *this);
156 
157     if(! dir )                                  // we might not have read access
158         return false;
159 
160     strvec files;
161     while( dirent* fn = dir.read() ) {          // read all files in dir
162         if(!direxp::is_special(fn->d_name))
163             files.push_back(fn->d_name);
164     }
165 
166     sort(files.begin(), files.end());            // speeds things up!? wow
167 
168     for(strvec::iterator fn = files.begin(); fn != files.end(); ++fn) {
169         wpos = pathcpy(pathpos, fn->c_str());
170         direxp match(filespec, rec_base? rec_base : pathpos, !!rec_base);
171         if(match) {
172             for(varexp::iterator i = match.begin(); i != match.end(); ++i)
173                 var.push_back(*i);
174             w |= invoker->file(pathpos, *this);
175             var.resize(prevlen);
176         }
177     }
178 
179     if(rec_base)
180     for(strvec::iterator fn = files.begin(); fn != files.end(); ++fn) {
181         wpos = pathcpy(pathpos, fn->c_str());
182         auto_dir newdir(path);
183         char sympath[1];                         // dont recurse into symlinks
184         if(newdir && readlink(path, sympath, sizeof sympath) < 0)
185             w |= nested(newdir, pathcpy(wpos, "/"), filespec);
186     }
187 
188     return w;
189 }
190 
191 } // namespace
192