File Coverage

builtin_findfile.inc
Criterion Covered Total %
statement 143 165 86.6
branch 97 146 66.4
condition n/a
subroutine n/a
pod n/a
total 240 311 77.1


line stmt bran cond sub pod time code
1             /*
2             * File: builtin_findfile.c
3             * Author: Igor Vlasenko
4             * Created: Tue Jul 14 22:47:11 2009
5             */
6              
7             #include /* for isalpha */
8             #include /* for getenv */
9              
10             /* TODO: support CYGWIN
11             * see
12             * http://sourceware.org/autobook/autobook/autobook_249.html
13             */
14             #if defined __CYGWIN32__ && !defined __CYGWIN__
15             /* For backwards compatibility with Cygwin b19 and
16             earlier, we define __CYGWIN__ here, so that
17             we can rely on checking just for that macro. */
18             # define __CYGWIN__ __CYGWIN32__
19             #endif
20              
21             #if defined _WIN32 && !defined __CYGWIN__
22             /* Use Windows separators on all _WIN32 defining
23             environments, except Cygwin. */
24             # define DIR_SEPARATOR_CHAR '\\'
25             #endif
26              
27             #if defined (DIR_SEPARATOR_CHAR)
28             # define IS_FILE_SEP(X) ((X=='/') || (X==DIR_SEPARATOR_CHAR))
29             #else
30             # define IS_FILE_SEP(X) (X=='/')
31             #endif
32              
33 343           static int _ff_exists(const char* path) {
34 343           FILE *file_p = fopen(path, "r");
35 343 100         if (file_p) {
36 310           fclose(file_p);
37 310 50         if (debuglevel>=TMPL_LOG_DEBUG2) tmpl_log(TMPL_LOG_DEBUG2,"_ff_exists: found [%s]\n",path);
38 310           return 1;
39             }
40 33 50         if (debuglevel>=TMPL_LOG_DEBUG2) tmpl_log(TMPL_LOG_ERROR,"_ff_exists: not found [%s]\n",path);
41 33           return 0;
42             }
43              
44             /* lame dirname implementation */
45 68           static PSTRING _ff_dirname(const char* path) {
46 68           PSTRING retval={(char*)path,(char*)path};
47 68           char c=0;
48 68 50         if (path!=NULL) retval.endnext += strlen(path);
49 0           else return retval;
50 993 100         while (retval.endnext > retval.begin && (c=*(--retval.endnext)) && ! IS_FILE_SEP(c));
    50          
    100          
51             /*fprintf(stderr,"built-in _ff_dirname: dir = %.*s\n",(int)(retval.endnext-retval.begin),retval.begin);*/
52 68           return retval;
53             }
54             /*
55             Windows Relative Paths
56              
57             For functions that manipulate files, the file names can be relative to the current directory. A file name is relative to the current directory if it does not begin with one of the following:
58              
59             * A UNC name of any format.
60             * A disk designator with a backslash, for example "C:\".
61             * A backslash, for example, "\directory").
62             */
63              
64             /* remember about \\?\ and \\?\UNC\ prefixes on WIN platform.
65             * see "File Names, Paths, and Namespaces"
66             * http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
67             */
68 310           int _ff_is_absolute(const char * filename) {
69 310           unsigned char c0 = *filename;
70             #if defined _WIN32 || defined __CYGWIN__
71             unsigned char c1;
72             unsigned char c2;
73             #endif
74 310 50         if ('\0' == c0) return 0;
75             /* \\?\ and \\?\UNC\ prefixes are included too */
76 310 50         if (IS_FILE_SEP(c0)) return 1;
77             #if defined _WIN32 || defined __CYGWIN__
78             c1 = *(++filename);
79             if ('\0' == c1) return 0;
80             c2 = *(++filename);
81             if (isalpha(c0) && ':'==c1 && IS_FILE_SEP(c2)) return 1;
82             #endif
83 310           return 0;
84             }
85              
86             #if defined _WIN32 || defined __CYGWIN__
87             int _ff_is_win_fully_qualified_path(const char * filename) {
88             unsigned char c0 = *filename;
89             unsigned char c1;
90             unsigned char c2;
91             if ('\0' == c0) return 0;
92             c1 = *(++filename);
93             if ('\0' == c1) return 0;
94             c2 = *(++filename);
95             /* \\?\ and \\?\UNC\ prefixes are included too */
96             if (isalpha(c0) && ':'==c1 && IS_FILE_SEP(c2)) return 1;
97             if ('\\'==c0 && '\\'==c1 && '?'==c2 && '\\'==*filename) return 1;
98             return 0;
99             }
100             #endif
101              
102             #define _ff_canonical_path(X) (X)
103              
104 6           static MPSTRING _shift_back_pstring_at(MPSTRING buf, char* pos, long shift) {
105 6 50         if (pos >= buf.begin && (pos+shift) <=buf.endnext) {
    50          
106 6           buf.endnext -= shift;
107 176 100         while (pos
108 170           *pos=*(pos+shift);
109 170           pos++;
110             }
111             }
112 6           *buf.endnext='\0';
113 6           return buf;
114             }
115              
116 230           static MPSTRING _filepath_remove_multiple_slashes(MPSTRING buf) {
117 230           char* pos = buf.begin;
118             #if defined _WIN32
119             /* due to \\?\ and \\?\UNC\ prefixes on WIN platform.
120             * see "File Names, Paths, and Namespaces"
121             * http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
122             * we skip first 2 bytes of path
123             */
124             if (((buf.endnext-pos)>1) && ('\\'==*pos && '\\'==*(pos+1))) pos += 2;
125             #endif
126 5170 100         while (pos
127 4940 100         if (IS_FILE_SEP(*pos) && IS_FILE_SEP(*(pos+1))) buf=_shift_back_pstring_at(buf, pos, 1);
    50          
128 4940           else pos++;
129             }
130 230           return buf;
131             }
132              
133 115           static const char* _ff_canonical_path_from_buf(MPSTRING buf) {
134             char* pos;
135             char* prev_slash_next;
136             char* slash_begin;
137             /* /./ <-- shift -2 */
138 115           pos = buf.begin;
139 2478 100         while (pos
140 2363 100         if (IS_FILE_SEP(*pos) && ('.'==*(pos+1)) && IS_FILE_SEP(*(pos+2))) buf=_shift_back_pstring_at(buf, pos, 2);
    100          
    50          
141 2363           pos++;
142             }
143              
144             /* // <-- shift -1 */
145 115           buf=_filepath_remove_multiple_slashes(buf);
146              
147             /* /.* /../ shift (scan from prevslash to slash back) */
148 115           pos = buf.begin;
149 115           slash_begin = buf.begin;
150             #if defined _WIN32
151             /* check for C: */
152             if (((buf.endnext-pos)>1) && isalpha((unsigned char) *pos) && ':'==*(pos+1)) {
153             pos += 2;
154             slash_begin += 2;
155             }
156             #endif
157 115           prev_slash_next = slash_begin;
158 2361 100         while (pos
159             /*printf("debug: %s pos=%c[%ld] fsn=%c[%ld]\n",buf.begin,*pos, pos-buf.begin, *prev_slash_next,prev_slash_next-buf.begin);*/
160 2246 100         if (IS_FILE_SEP(*pos)) {
161 110 100         if (('.'==*(pos+1)) && ('.'==*(pos+2)) && IS_FILE_SEP(*(pos+3))) {
    50          
    50          
162             /*printf("debug: do shift pos=%ld fsn=%ld shift=%ld\n", pos-buf.begin, prev_slash_next-buf.begin, pos-prev_slash_next+4);*/
163 1 50         if (pos == prev_slash_next && prev_slash_next==slash_begin) {
    0          
164             /* begining of the string ("/../") -> leave one slash */
165 0           buf=_shift_back_pstring_at(buf, prev_slash_next, pos-prev_slash_next+3);
166 0           pos=prev_slash_next-1;/* 1 to compensate pos++ */
167             } else {
168 1           buf=_shift_back_pstring_at(buf, prev_slash_next, pos-prev_slash_next+4);
169 1           pos=prev_slash_next-2;/* 2 to compensate / and pos++ */
170             /* 2 to step back slashnext char and 'slash' char, if any */
171 1 50         if (prev_slash_next>slash_begin) prev_slash_next--;
172 1 50         if (prev_slash_next>slash_begin) prev_slash_next--;
173             }
174             /* old prev_slash_next now current, so we need to recalculate it */
175             /* first find a 'slash' char */
176 10 100         while (prev_slash_next>=slash_begin && !IS_FILE_SEP(*prev_slash_next)) prev_slash_next--;
    50          
177 1 50         if (prev_slash_next>slash_begin) prev_slash_next++;/* step next to slash */
178             } else {
179 109           prev_slash_next=pos+1;
180             }
181             }
182 2246           pos++;
183             }
184              
185             /* // <-- shift -1 */
186 115           buf=_filepath_remove_multiple_slashes(buf);
187              
188             /* offset 0: if ./ shift -2 */
189 115 50         if ((buf.endnext-buf.begin)<2) return buf.begin;
190 115           pos = buf.begin;
191 115 100         if (('.'==*pos) && IS_FILE_SEP(*(pos+1))) buf=_shift_back_pstring_at(buf, pos, 2);
    50          
192 115           return buf.begin;
193             }
194              
195 68           static MPSTRING _ff_add_pstr_to_buffer(MPSTRING buf, PSTRING pstr) {
196 68           MPSTRING ret = buf;
197             const char* s;
198             //tmpl_log(TMPL_LOG_ERROR,"_ff_add_pstr_to_buffer: called as [%p,%p]+[%p,%p]\n",buf.begin,buf.endnext, pstr.begin,pstr.endnext);
199 423 100         for (s=pstr.begin;s
200             //tmpl_log(TMPL_LOG_ERROR,"_ff_add_pstr_to_buffer: ret = [%p,%p]\n",ret.begin,ret.endnext);
201 68           return ret;
202             }
203 227           static MPSTRING _ff_add_str_to_buffer(MPSTRING buf, const char* str) {
204 227           MPSTRING ret = buf;
205 227           const char* s=str;
206             //tmpl_log(TMPL_LOG_ERROR,"_ff_add_str_to_buffer: called as [%p,%p]+[%s]\n",buf.begin,buf.endnext, str);
207 3010 100         while ('\0'!=*s) {*(ret.endnext++)=*s++;}
208             //tmpl_log(TMPL_LOG_ERROR,"_ff_add_str_to_buffer: ret = [%p,%p]\n",ret.begin,ret.endnext);
209 227           return ret;
210             }
211 112           static MPSTRING _ff_add_sep_to_buffer(MPSTRING buf) {
212 112           MPSTRING ret = buf;
213 112 50         if (ret.endnext>ret.begin && IS_FILE_SEP(*(ret.endnext-1))) return ret;
    100          
214             #ifdef DIR_SEPARATOR_CHAR
215             *(ret.endnext++)=DIR_SEPARATOR_CHAR;
216             #else
217 105           *(ret.endnext++)='/';
218             #endif
219 112           return ret;
220             }
221 147           static MPSTRING _ff_add_0_to_buffer(MPSTRING buf) {
222 147           *(buf.endnext++)='\0';
223 147           return buf;
224             }
225 310           static const char* get_template_root(struct tmplpro_param* param) {
226 310           const char* retval = param->template_root;
227 310 50         if (NULL==retval) retval = getenv("HTML_TEMPLATE_ROOT");
228 310           return retval;
229             }
230              
231 310           static const char* _find_file (struct tmplpro_param* param, const char* filename, PSTRING extra_dir) {
232             // TODO: finish it
233 310           const char* HTML_TEMPLATE_ROOT = get_template_root(param);
234 310           size_t HTML_TEMPLATE_ROOT_length=0;
235 310           size_t buffsize=0;
236 310           char** pathlist=param->path;
237             MPSTRING pbuf_begin, filepath;
238              
239 310 50         if (param->debug >= TMPL_LOG_DEBUG2) {
240 0           tmpl_log(TMPL_LOG_DEBUG2,"built-in _find_file: looking for %s extra dir = %.*s\n",filename, (int)(extra_dir.endnext-extra_dir.begin),extra_dir.begin);
241 0 0         if (HTML_TEMPLATE_ROOT) tmpl_log(TMPL_LOG_DEBUG2,"built-in _find_file: HTML_TEMPLATE_ROOT = %s\n",HTML_TEMPLATE_ROOT);
242             }
243              
244             /* first check for a full path */
245 310 50         if (_ff_is_absolute(filename) && _ff_exists(filename)) return _ff_canonical_path(filename);
    0          
246              
247             #if defined _WIN32 || defined __CYGWIN__
248             /* no sense of prefixing C:\ or \\?\ */
249             if (_ff_is_win_fully_qualified_path(filename)) {
250             tmpl_log(TMPL_LOG_DEBUG2,"built-in _ff_is_win_fully_qualified_path: rejected %s: is_absolute=%d exists=%d\n",filename,_ff_is_absolute(filename),_ff_exists(filename));
251             return NULL;
252             }
253             #endif
254              
255 310 100         if (HTML_TEMPLATE_ROOT!=NULL) HTML_TEMPLATE_ROOT_length=strlen(HTML_TEMPLATE_ROOT);
256 310 100         if (pathlist!=NULL) {
257 179 100         while (NULL!=*pathlist) {
258 99           size_t pathentrylen=strlen(*pathlist);
259 99 100         if (buffsize
260 99           pathlist++;
261             }
262             }
263             /* bufsize is max possible length path of path considered
264             * min is max_len(foreach pathlist)+HTML_TEMPLATE_ROOT_length+strlen(filename)+len(extra_dir)+1)
265             * but we malloc an extra space to avoid frequent reallocing
266             */
267 310           buffsize+=HTML_TEMPLATE_ROOT_length+strlen(filename)+(extra_dir.endnext-extra_dir.begin)+4+1; /* 4 - for slashes */
268 310           pbuffer_resize(¶m->builtin_findfile_buffer, buffsize);
269 310           pbuf_begin.begin=pbuffer_string(¶m->builtin_findfile_buffer);
270 310           pbuf_begin.endnext=pbuf_begin.begin;
271              
272             /* try the extra_path if one was specified */
273 310 100         if (extra_dir.begin!=NULL) {
274 68           filepath=_ff_add_pstr_to_buffer(pbuf_begin,extra_dir);
275 68 100         if (extra_dir.endnext-extra_dir.begin >0)
276 32           filepath=_ff_add_sep_to_buffer(filepath);
277 68           filepath=_ff_add_str_to_buffer(filepath,filename);
278 68           filepath=_ff_add_0_to_buffer(filepath);
279 68 100         if (_ff_exists(filepath.begin)) return _ff_canonical_path_from_buf(filepath);
280             }
281              
282             /* try pre-prending HTML_Template_Root */
283 258 100         if (HTML_TEMPLATE_ROOT!=NULL) {
284 1           filepath=_ff_add_str_to_buffer(pbuf_begin,HTML_TEMPLATE_ROOT);
285 1 50         if (HTML_TEMPLATE_ROOT_length >0)
286 1           filepath=_ff_add_sep_to_buffer(filepath);
287 1           filepath=_ff_add_str_to_buffer(filepath,filename);
288 1           filepath=_ff_add_0_to_buffer(filepath);
289 1 50         if (_ff_exists(filepath.begin)) return _ff_canonical_path_from_buf(filepath);
290             }
291              
292             /* try "path" option list.. */
293 258           pathlist=param->path;
294 258 100         if (pathlist!=NULL) {
295 82 100         while (NULL!=*pathlist) {
296             //tmpl_log(TMPL_LOG_ERROR,"try 'path' option list..: looking in [%s]\n",*pathlist);
297 77           filepath=_ff_add_str_to_buffer(pbuf_begin,*pathlist);
298             /* add separator only if *pathlist non-empty */
299 77 50         if (0!=**pathlist) filepath=_ff_add_sep_to_buffer(filepath);
300 77           filepath=_ff_add_str_to_buffer(filepath,filename);
301 77           filepath=_ff_add_0_to_buffer(filepath);
302 77 100         if (_ff_exists(filepath.begin)) return _ff_canonical_path_from_buf(filepath);
303 15           pathlist++;
304             }
305             }
306              
307             /* try even a relative path from the current directory...*/
308 196 100         if (_ff_exists(filename)) return _ff_canonical_path(filename);
309              
310             /* try "path" option list with HTML_TEMPLATE_ROOT prepended... */
311 1 50         if (HTML_TEMPLATE_ROOT!=NULL) {
312 1           pathlist=param->path;
313 1 50         if (pathlist!=NULL) {
314 1 50         while (NULL!=*pathlist) {
315 1           filepath=_ff_add_str_to_buffer(pbuf_begin,HTML_TEMPLATE_ROOT);
316 1 50         if (HTML_TEMPLATE_ROOT_length >0)
317 1           filepath=_ff_add_sep_to_buffer(filepath);
318 1           filepath=_ff_add_str_to_buffer(filepath,*pathlist);
319             /* add separator only if *pathlist non-empty */
320 1 50         if (0!=**pathlist) filepath=_ff_add_sep_to_buffer(filepath);
321 1           filepath=_ff_add_str_to_buffer(filepath,filename);
322 1           filepath=_ff_add_0_to_buffer(filepath);
323 1 50         if (_ff_exists(filepath.begin)) return _ff_canonical_path_from_buf(filepath);
324 0           pathlist++;
325             }
326             }
327             }
328              
329 310           return NULL;
330             }
331              
332 310           static const char* BACKCALL stub_find_file_func(ABSTRACT_FINDFILE* param,const char* filename, const char* last_visited_file) {
333             const char* filepath;
334 310           PSTRING extra_path ={NULL,NULL};
335              
336 310 50         if (filename == last_visited_file) tmpl_log(TMPL_LOG_ERROR,"built-in find_file: internal error: buffer clash for %s\n",filename);
337              
338 310 50         if (((struct tmplpro_param*)param)->debug>= TMPL_LOG_DEBUG)
339 0           tmpl_log(TMPL_LOG_DEBUG,"built-in find_file: looking for %s last_visited_file = %s\n",filename, last_visited_file);
340              
341             // look for the included file...
342 310 100         if (last_visited_file!=NULL && ! ((struct tmplpro_param*) param)->search_path_on_include) {
    100          
343 68           extra_path = _ff_dirname(last_visited_file);
344             }
345 310           filepath = _find_file((struct tmplpro_param*)param,filename,extra_path);
346 310 50         if (filepath==NULL) {
347 0           char** path=((struct tmplpro_param*)param)->path;
348 0           const char* HTML_TEMPLATE_ROOT = get_template_root((struct tmplpro_param*)param);
349 0           tmpl_log(TMPL_LOG_ERROR,"built-in find_file: can't find file %s", filename);
350 0 0         if (NULL!=last_visited_file) tmpl_log(TMPL_LOG_ERROR," (included from %s)", last_visited_file);
351 0 0         if(HTML_TEMPLATE_ROOT!=NULL) {
352 0           tmpl_log(TMPL_LOG_ERROR," with HTML_TEMPLATE_ROOT = '%s'",HTML_TEMPLATE_ROOT);
353             }
354 0 0         if (NULL!=path) {
355 0           tmpl_log(TMPL_LOG_ERROR," with path = [");
356 0 0         while (NULL!=*path) {
357 0           tmpl_log(TMPL_LOG_ERROR," '%s'",*path);
358 0           path++;
359             }
360 0           tmpl_log(TMPL_LOG_ERROR," ]");
361             } else {
362 0           tmpl_log(TMPL_LOG_ERROR," with empty path list");
363             }
364 0           tmpl_log(TMPL_LOG_ERROR,"\n");
365 0           return NULL;
366             } else {
367 310           return filepath;
368             }
369             }
370              
371             /*
372             * Local Variables:
373             * mode: c
374             * End:
375             */