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