| 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 |  |  |  |  |  |  | */ |