Annotation of libwww/Library/src/HTMulti.c, revision 2.29
2.29 ! frystyk 1: /*
! 2: ** CONTENT NEGOTIATION
2.11 frystyk 3: **
2.15 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.11 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.29 ! frystyk 6: ** @(#) $Id: HTMulti.c,v 2.28 1996/06/02 00:34:55 frystyk Exp $
2.1 luotonen 7: **
8: ** History:
9: ** March 94 AL Separated from HTFile.c because
10: ** multiformat handling would be a mess in VMS.
11: */
12:
2.13 frystyk 13: /* Library include files */
2.26 frystyk 14: #include "sysdep.h"
2.28 frystyk 15: #include "WWWUtil.h"
16: #include "WWWCore.h"
2.2 duns 17: #include "HTMulti.h"
2.20 frystyk 18: #include "HTFile.h"
2.1 luotonen 19:
2.29 ! frystyk 20: #define MULTI_SUFFIX ".multi"/* Extension for scanning formats */
! 21: #define MAX_SUFF 15 /* Maximum number of suffixes for a file */
! 22: #define VARIANTS 4 /* We start with this array size */
! 23:
! 24: typedef struct _HTContentDescription {
! 25: char * filename;
! 26: HTFormat content_type;
! 27: HTLanguage content_language;
! 28: HTEncoding content_encoding;
! 29: HTEncoding content_transfer;
! 30: int content_length;
! 31: double quality;
! 32: } HTContentDescription;
! 33:
2.3 luotonen 34: PRIVATE HTList * welcome_names = NULL; /* Welcome.html, index.html etc. */
35:
2.29 ! frystyk 36: /* ------------------------------------------------------------------------- */
! 37:
! 38: /*
! 39: ** Sort the q values in descending order
! 40: */
! 41: PRIVATE int VariantSort (const void * a, const void * b)
! 42: {
! 43: HTContentDescription * aa = *(HTContentDescription **) a;
! 44: HTContentDescription * bb = *(HTContentDescription **) b;
! 45: return (aa->quality > bb->quality) ? -1 : 1;
! 46: }
! 47:
! 48: PRIVATE BOOL wild_match (HTAtom * tmplate, HTAtom * actual)
! 49: {
! 50: const char *t, *a;
! 51: char *st, *sa;
! 52: BOOL match = NO;
! 53:
! 54: if (tmplate && actual && (t = HTAtom_name(tmplate))) {
! 55: if (!strcmp(t, "*"))
! 56: return YES;
! 57:
! 58: if (strchr(t, '*') &&
! 59: (a = HTAtom_name(actual)) &&
! 60: (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {
! 61:
! 62: *sa = 0;
! 63: *st = 0;
! 64:
! 65: if ((*(st-1)=='*' &&
! 66: (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
! 67: (*(st+1)=='*' && !strcasecomp(t,a)))
! 68: match = YES;
! 69:
! 70: *sa = '/';
! 71: *st = '/';
! 72: }
! 73: }
! 74: return match;
! 75: }
! 76:
! 77: /*
! 78: * Added by takada@seraph.ntt.jp (94/04/08)
! 79: */
! 80: PRIVATE BOOL lang_match (HTAtom * tmplate, HTAtom * actual)
! 81: {
! 82: const char *t, *a;
! 83: char *st, *sa;
! 84: BOOL match = NO;
! 85:
! 86: if (tmplate && actual &&
! 87: (t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) {
! 88: st = strchr(t, '_');
! 89: sa = strchr(a, '_');
! 90: if ((st != NULL) && (sa != NULL)) {
! 91: if (!strcasecomp(t, a))
! 92: match = YES;
! 93: else
! 94: match = NO;
! 95: }
! 96: else {
! 97: if (st != NULL) *st = 0;
! 98: if (sa != NULL) *sa = 0;
! 99: if (!strcasecomp(t, a))
! 100: match = YES;
! 101: else
! 102: match = NO;
! 103: if (st != NULL) *st = '_';
! 104: if (sa != NULL) *sa = '_';
! 105: }
! 106: }
! 107: return match;
! 108: }
! 109:
! 110: PRIVATE double type_value (HTAtom * content_type, HTList * accepted)
! 111: {
! 112: if (!content_type) return (1.0);
! 113: if (accepted) {
! 114: HTList * cur = accepted;
! 115: HTPresentation * pres;
! 116: HTPresentation * wild = NULL;
! 117: while ((pres = (HTPresentation *) HTList_nextObject(cur))) {
! 118: if (pres->rep == content_type)
! 119: return pres->quality;
! 120: else if (wild_match(pres->rep, content_type))
! 121: wild = pres;
! 122: }
! 123: if (wild) return wild->quality;
! 124: else return (0.0); /* Nothing matched */
! 125: }
! 126: return (1.0); /* We accept all types */
! 127: }
! 128:
! 129: PRIVATE double lang_value (HTAtom * language, HTList * accepted)
! 130: {
! 131: if (!language) return (1.0);
! 132: if (accepted) {
! 133: HTList * cur = accepted;
! 134: HTAcceptNode * node;
! 135: HTAcceptNode * wild = NULL;
! 136: while ((node = (HTAcceptNode *) HTList_nextObject(cur))) {
! 137: if (node->atom == language)
! 138: return node->quality;
! 139: /*
! 140: * patch by takada@seraph.ntt.jp (94/04/08)
! 141: * the original line was
! 142: * else if (wild_match(node->atom, language)) {
! 143: * and the new line is
! 144: */
! 145: else if (lang_match(node->atom, language))
! 146: wild = node;
! 147: }
! 148: if (wild) return wild->quality;
! 149: else return (0.0); /* Nothing matched */
! 150: }
! 151: return (1.0); /* We accept all languages */
! 152: }
! 153:
! 154: PRIVATE double encoding_value (HTAtom * encoding, HTList * accepted)
! 155: {
! 156: if (!encoding) return (1.0);
! 157: if (accepted) {
! 158: HTList * cur = accepted;
! 159: HTAcceptNode * node;
! 160: HTAcceptNode * wild = NULL;
! 161: const char * e = HTAtom_name(encoding);
! 162: if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary"))
! 163: return (1.0);
! 164: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
! 165: if (node->atom == encoding)
! 166: return node->quality;
! 167: else if (wild_match(node->atom, encoding))
! 168: wild = node;
! 169: }
! 170: if (wild) return wild->quality;
! 171: else return (0.0); /* Nothing matched */
! 172: }
! 173: return (1.0); /* We accept all encodings */
! 174: }
! 175:
! 176: PRIVATE BOOL HTRank (HTRequest * request, HTArray * variants)
! 177: {
! 178: HTContentDescription * cd;
! 179: void ** data;
! 180: if (!variants) {
! 181: if (PROT_TRACE) HTTrace("Ranking..... No variants\n");
! 182: return NO;
! 183: }
! 184: /*
! 185: ** Walk through the list of local and global preferences and find the
! 186: ** overall q factor for each variant
! 187: */
! 188: cd = (HTContentDescription *) HTArray_firstObject(variants, data);
! 189: while (cd) {
! 190: double ctq_local = type_value(cd->content_type, HTRequest_conversion(request));
! 191: double ctq_global = type_value(cd->content_type, HTFormat_conversion());
! 192: double clq_local = lang_value(cd->content_language, HTRequest_language(request));
! 193: double clq_global = lang_value(cd->content_language, HTFormat_language());
! 194: double ceq_local = encoding_value(cd->content_encoding, HTRequest_encoding(request));
! 195: double ceq_global = encoding_value(cd->content_encoding, HTFormat_contentCoding());
! 196: if (PROT_TRACE)
! 197: HTTrace("Qualities... Content type: %.3f, Content language: %.3f, Content encoding: %.3f\n",
! 198: HTMAX(ctq_local, ctq_global),
! 199: HTMAX(clq_local, clq_global),
! 200: HTMAX(ceq_local, ceq_global));
! 201: cd->quality *= (HTMAX(ctq_local, ctq_global) *
! 202: HTMAX(clq_local, clq_global) *
! 203: HTMAX(ceq_local, ceq_global));
! 204: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
! 205: }
! 206:
! 207: /* Sort the array of all our accepted preferences */
! 208: HTArray_sort(variants, VariantSort);
! 209:
! 210: /* Write out the result */
! 211: if (PROT_TRACE) {
! 212: int cnt = 1;
! 213: cd = (HTContentDescription *) HTArray_firstObject(variants, data);
! 214: HTTrace("Ranking.....\n");
! 215: HTTrace("RANK QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
! 216: while (cd) {
! 217: HTTrace("%d. %.4f %-20.20s %-8.8s %-10.10s %s\n",
! 218: cnt++,
! 219: cd->quality,
! 220: cd->content_type ? HTAtom_name(cd->content_type) : "-",
! 221: cd->content_language?HTAtom_name(cd->content_language):"-",
! 222: cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
! 223: cd->filename ? cd->filename :"-");
! 224: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
! 225: }
! 226: }
! 227: return YES;
! 228: }
2.3 luotonen 229:
2.14 frystyk 230: /* PUBLIC HTSplitFilename()
231: **
232: ** Split the filename to an array of suffixes.
233: ** Return the number of parts placed to the array.
234: ** Array should have MAX_SUFF+1 items.
235: */
2.23 frystyk 236: PRIVATE int HTSplitFilename (char * s_str, char ** s_arr)
2.14 frystyk 237: {
2.26 frystyk 238: const char *delimiters = HTBind_delimiters();
2.14 frystyk 239: char * start = s_str;
240: char * end;
241: char save;
242: int i;
243:
244: if (!s_str || !s_arr) return 0;
245:
246: for (i=0; i < MAX_SUFF && *start; i++) {
247: for(end=start+1; *end && !strchr(delimiters, *end); end++);
248: save = *end;
249: *end = 0;
250: StrAllocCopy(s_arr[i], start); /* Frees the previous value */
251: *end = save;
252: start = end;
253: }
2.24 frystyk 254: HT_FREE(s_arr[i]); /* Terminating NULL */
2.14 frystyk 255: return i;
256: }
257:
258:
2.3 luotonen 259: /*
260: ** Set default file name for welcome page on each directory.
261: */
2.23 frystyk 262: PUBLIC void HTAddWelcome (char * name)
2.3 luotonen 263: {
264: if (name) {
265: char * mycopy = NULL;
266: StrAllocCopy(mycopy,name);
267:
268: if (!welcome_names)
269: welcome_names = HTList_new();
270: HTList_addObject(welcome_names, (void*)mycopy);
271: }
272: }
273:
274:
2.26 frystyk 275: #ifdef HAVE_READDIR
2.1 luotonen 276:
277: /* PRIVATE multi_match()
278: **
279: ** Check if actual filename (split in parts) fulfills
280: ** the requirements.
281: */
2.23 frystyk 282: PRIVATE BOOL multi_match (char ** required, int m, char ** actual, int n)
2.1 luotonen 283: {
284: int c;
285: int i,j;
286:
2.2 duns 287: #ifdef VMS
288: for(c=0; c<m && c<n && !strcasecomp(required[c], actual[c]); c++);
289: #else /* not VMS */
2.1 luotonen 290: for(c=0; c<m && c<n && !strcmp(required[c], actual[c]); c++);
2.2 duns 291: #endif /* not VMS */
2.1 luotonen 292:
293: if (!c) return NO; /* Names differ rigth from start */
294:
295: for(i=c; i<m; i++) {
296: BOOL found = NO;
297: for(j=c; j<n; j++) {
2.2 duns 298: #ifdef VMS
299: if (!strcasecomp(required[i], actual[j])) {
300: #else /* not VMS */
2.1 luotonen 301: if (!strcmp(required[i], actual[j])) {
2.2 duns 302: #endif /* not VMS */
2.1 luotonen 303: found = YES;
304: break;
305: }
306: }
307: if (!found) return NO;
308: }
309: return YES;
310: }
311:
312:
313: /*
314: ** Get multi-match possibilities for a given file
315: ** ----------------------------------------------
316: ** On entry:
317: ** path absolute path to one file in a directory,
318: ** may end in .multi.
319: ** On exit:
320: ** returns a list of ContentDesription structures
321: ** describing the mathing files.
322: **
323: */
2.29 ! frystyk 324: PRIVATE HTArray * dir_matches (char * path)
2.1 luotonen 325: {
326: static char * required[MAX_SUFF+1];
327: static char * actual[MAX_SUFF+1];
328: int m,n;
329: char * dirname = NULL;
330: char * basename = NULL;
331: int baselen;
332: char * multi = NULL;
333: DIR * dp;
2.26 frystyk 334: struct dirent * dirbuf;
2.29 ! frystyk 335: HTArray * matches = NULL;
2.16 frystyk 336: #ifdef HT_REENTRANT
2.26 frystyk 337: DIR result; /* For readdir_r */
2.16 frystyk 338: #endif
2.1 luotonen 339:
340: if (!path) return NULL;
341:
342: StrAllocCopy(dirname, path);
343: basename = (strrchr(dirname, '/'));
344: if (!basename)
345: goto dir_match_failed;
346: *basename++ = 0;
347:
348: multi = strrchr(basename, MULTI_SUFFIX[0]);
2.2 duns 349: if (multi && !strcasecomp(multi, MULTI_SUFFIX))
2.1 luotonen 350: *multi = 0;
351: baselen = strlen(basename);
352:
353: m = HTSplitFilename(basename, required);
354:
355: dp = opendir(dirname);
356: if (!dp) {
2.13 frystyk 357: if (PROT_TRACE)
2.25 eric 358: HTTrace("Warning..... Can't open directory %s\n", dirname);
2.1 luotonen 359: goto dir_match_failed;
360: }
361:
2.29 ! frystyk 362: matches = HTArray_new(VARIANTS);
2.16 frystyk 363: #ifdef HT_REENTRANT
2.26 frystyk 364: while ((dirbuf = (DIR *) readdir_r(dp, &result))) {
2.16 frystyk 365: #else
366: while ((dirbuf = readdir(dp))) {
367: #endif /* HT_REENTRANT */
2.1 luotonen 368: if (!dirbuf->d_ino) continue; /* Not in use */
2.3 luotonen 369: if (!strcmp(dirbuf->d_name,".") ||
370: !strcmp(dirbuf->d_name,"..") ||
2.20 frystyk 371: !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
2.3 luotonen 372: continue;
2.7 frystyk 373:
2.13 frystyk 374: /* Use of direct->namlen is only valid in BSD'ish system */
375: /* Thanks to chip@chinacat.unicom.com (Chip Rosenthal) */
376: /* if ((int)(dirbuf->d_namlen) >= baselen) { */
377: if ((int) strlen(dirbuf->d_name) >= baselen) {
2.1 luotonen 378: n = HTSplitFilename(dirbuf->d_name, actual);
379: if (multi_match(required, m, actual, n)) {
380: HTContentDescription * cd;
2.29 ! frystyk 381: if ((cd = (HTContentDescription *)
! 382: HT_CALLOC(1, sizeof(HTContentDescription))) == NULL)
! 383: HT_OUTOFMEM("dir_matches");
! 384: if (HTBind_getFormat(dirbuf->d_name,
! 385: &cd->content_type,
! 386: &cd->content_encoding,
! 387: &cd->content_transfer,
! 388: &cd->content_language,
! 389: &cd->quality)) {
2.1 luotonen 390: if (cd->content_type) {
2.24 frystyk 391: if ((cd->filename = (char *) HT_MALLOC(strlen(dirname) + 2 + strlen(dirbuf->d_name))) == NULL)
392: HT_OUTOFMEM("dir_matches");
2.29 ! frystyk 393: sprintf(cd->filename, "%s/%s", dirname, dirbuf->d_name);
! 394: HTArray_addObject(matches, (void *) cd);
! 395: } else {
! 396: HT_FREE(cd);
2.1 luotonen 397: }
2.29 ! frystyk 398: } else {
! 399: HT_FREE(cd);
2.1 luotonen 400: }
401: }
402: }
403: }
404: closedir(dp);
405:
406: dir_match_failed:
2.24 frystyk 407: HT_FREE(dirname);
2.1 luotonen 408: return matches;
409: }
410:
411:
412: /*
413: ** Get the best match for a given file
414: ** -----------------------------------
415: ** On entry:
416: ** req->conversions accepted content-types
417: ** req->encodings accepted content-transfer-encodings
418: ** req->languages accepted content-languages
419: ** path absolute pathname of the filename for
420: ** which the match is desired.
421: ** On exit:
422: ** returns a newly allocated absolute filepath.
423: */
2.23 frystyk 424: PRIVATE char * HTGetBest (HTRequest * req, char * path)
2.1 luotonen 425: {
2.29 ! frystyk 426: HTArray * variants = NULL;
! 427: char * representation = NULL;
2.1 luotonen 428:
2.13 frystyk 429: if (!path || !*path) return NULL;
2.1 luotonen 430:
2.29 ! frystyk 431: if ((variants = dir_matches(path)) == NULL) {
! 432: if (PROT_TRACE) HTTrace("No matches.. for \"%s\"\n", path);
2.13 frystyk 433: return NULL;
2.1 luotonen 434: }
435:
2.29 ! frystyk 436: if (PROT_TRACE) {
! 437: void ** data;
! 438: HTContentDescription * cd = HTArray_firstObject(variants, data);
2.25 eric 439: HTTrace("Multi....... Possibilities for \"%s\"\n", path);
2.29 ! frystyk 440: HTTrace(" QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
! 441: while (cd) {
! 442: HTTrace(" %.4f %-20.20s %-8.8s %-10.10s %s\n",
! 443: cd->quality,
! 444: cd->content_type ?HTAtom_name(cd->content_type) :"-\t",
! 445: cd->content_language?HTAtom_name(cd->content_language):"-",
! 446: cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
! 447: cd->filename ?cd->filename :"-");
! 448: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
! 449: }
2.1 luotonen 450: }
451:
452: /*
2.29 ! frystyk 453: ** Finally get the best variant which is readable
2.1 luotonen 454: */
2.29 ! frystyk 455: if (HTRank(req, variants)) {
! 456: void ** data;
! 457: HTContentDescription * cd = HTArray_firstObject(variants, data);
! 458: while (cd) {
! 459: if (cd->filename) {
! 460: if (access(cd->filename, R_OK) != -1)
! 461: StrAllocCopy(representation, cd->filename);
! 462: else if (PROT_TRACE)
! 463: HTTrace("Multi....... `%s\' is not readable\n",
! 464: cd->filename);
2.1 luotonen 465: }
2.29 ! frystyk 466: HT_FREE(cd->filename);
! 467: HT_FREE(cd);
! 468: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
2.1 luotonen 469: }
470: }
2.29 ! frystyk 471: HTArray_delete(variants);
! 472: return representation;
2.1 luotonen 473: }
474:
2.3 luotonen 475:
476:
2.23 frystyk 477: PRIVATE int welcome_value (char * name)
2.3 luotonen 478: {
479: HTList * cur = welcome_names;
480: char * welcome;
481: int v = 0;
482:
483: while ((welcome = (char*)HTList_nextObject(cur))) {
484: v++;
485: if (!strcmp(welcome,name)) return v;
486: }
487: return 0;
488: }
489:
490:
491:
2.23 frystyk 492: PRIVATE char * get_best_welcome (char * path)
2.3 luotonen 493: {
494: char * best_welcome = NULL;
495: int best_value = 0;
496: DIR * dp;
2.26 frystyk 497: struct dirent * dirbuf;
2.3 luotonen 498: char * last = strrchr(path, '/');
499:
500: if (!welcome_names) {
501: HTAddWelcome("Welcome.html");
502: HTAddWelcome("welcome.html");
2.5 luotonen 503: #if 0
2.3 luotonen 504: HTAddWelcome("Index.html");
2.5 luotonen 505: #endif
2.3 luotonen 506: HTAddWelcome("index.html");
507: }
508:
2.5 luotonen 509: if (last && last!=path) *last = 0;
2.3 luotonen 510: dp = opendir(path);
2.5 luotonen 511: if (last && last!=path) *last='/';
2.3 luotonen 512: if (!dp) {
2.13 frystyk 513: if (PROT_TRACE)
2.25 eric 514: HTTrace("Warning..... Can't open directory %s\n",path);
2.3 luotonen 515: return NULL;
516: }
517: while ((dirbuf = readdir(dp))) {
2.13 frystyk 518: if (!dirbuf->d_ino ||
2.3 luotonen 519: !strcmp(dirbuf->d_name,".") ||
520: !strcmp(dirbuf->d_name,"..") ||
2.20 frystyk 521: !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
2.3 luotonen 522: continue;
523: else {
524: int v = welcome_value(dirbuf->d_name);
525: if (v > best_value) {
526: best_value = v;
527: StrAllocCopy(best_welcome, dirbuf->d_name);
528: }
529: }
530: }
531: closedir(dp);
532:
533: if (best_welcome) {
2.24 frystyk 534: char * welcome;
535: if ((welcome = (char *) HT_MALLOC(strlen(path) + strlen(best_welcome)+2)) == NULL)
536: HT_OUTOFMEM("get_best_welcome");
2.4 luotonen 537: sprintf(welcome, "%s%s%s", path, last ? "" : "/", best_welcome);
2.24 frystyk 538: HT_FREE(best_welcome);
2.13 frystyk 539: if (PROT_TRACE)
2.25 eric 540: HTTrace("Welcome..... \"%s\"\n",welcome);
2.3 luotonen 541: return welcome;
542: }
543: return NULL;
544: }
545:
2.26 frystyk 546: #endif /* HAVE_READDIR */
2.1 luotonen 547:
548:
549: /*
550: ** Do multiformat handling
551: ** -----------------------
552: ** On entry:
553: ** req->conversions accepted content-types
554: ** req->encodings accepted content-transfer-encodings
555: ** req->languages accepted content-languages
556: ** path absolute pathname of the filename for
557: ** which the match is desired.
558: ** stat_info pointer to result space.
559: **
560: ** On exit:
561: ** returns a newly allocated absolute filepath of the best
562: ** match, or NULL if no match.
563: ** stat_info will contain inode information as
564: ** returned by stat().
565: */
2.23 frystyk 566: PUBLIC char * HTMulti (HTRequest * req,
567: char * path,
568: struct stat * stat_info)
2.1 luotonen 569: {
570: char * new_path = NULL;
571: int stat_status = -1;
572:
2.3 luotonen 573: if (!req || !path || !*path || !stat_info)
2.1 luotonen 574: return NULL;
575:
2.26 frystyk 576: #ifdef HAVE_READDIR
2.19 frystyk 577: if (*(path+strlen(path)-1) == '/') { /* Find welcome page */
2.3 luotonen 578: new_path = get_best_welcome(path);
579: if (new_path) path = new_path;
2.29 ! frystyk 580: } else{
2.3 luotonen 581: char * multi = strrchr(path, MULTI_SUFFIX[0]);
582: if (multi && !strcasecomp(multi, MULTI_SUFFIX)) {
2.13 frystyk 583: if (PROT_TRACE)
2.25 eric 584: HTTrace("Multi....... by %s suffix\n", MULTI_SUFFIX);
2.1 luotonen 585: if (!(new_path = HTGetBest(req, path))) {
2.13 frystyk 586: if (PROT_TRACE)
2.25 eric 587: HTTrace("Multi....... failed -- giving up\n");
2.1 luotonen 588: return NULL;
589: }
590: path = new_path;
2.29 ! frystyk 591: } else {
2.18 frystyk 592: stat_status = HT_STAT(path, stat_info);
2.3 luotonen 593: if (stat_status == -1) {
2.13 frystyk 594: if (PROT_TRACE)
2.29 ! frystyk 595: HTTrace("AutoMulti... can't stat \"%s\"(errno %d)\n",
2.13 frystyk 596: path, errno);
2.3 luotonen 597: if (!(new_path = HTGetBest(req, path))) {
2.13 frystyk 598: if (PROT_TRACE)
2.25 eric 599: HTTrace("AutoMulti... failed -- giving up\n");
2.3 luotonen 600: return NULL;
601: }
602: path = new_path;
603: }
2.1 luotonen 604: }
605: }
2.26 frystyk 606: #endif /* HAVE_READDIR */
2.1 luotonen 607:
608: if (stat_status == -1)
2.18 frystyk 609: stat_status = HT_STAT(path, stat_info);
2.1 luotonen 610: if (stat_status == -1) {
2.13 frystyk 611: if (PROT_TRACE)
2.25 eric 612: HTTrace("Stat fails.. on \"%s\" -- giving up (errno %d)\n",
2.13 frystyk 613: path, errno);
2.1 luotonen 614: return NULL;
2.29 ! frystyk 615: } else {
2.1 luotonen 616: if (!new_path) {
617: StrAllocCopy(new_path, path);
618: return new_path;
619: }
620: else return path;
621: }
622: }
623:
624:
Webmaster