Annotation of libwww/Library/src/HTFWrite.c, revision 2.2.2.4
2.1 frystyk 1: /* HTFWrite.c
2: ** FILE WRITER
3: **
4: ** (c) COPYRIGHT CERN 1994.
5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This version of the stream object just writes to a C file.
8: ** The file is assumed open and left open.
9: **
10: ** Bugs:
11: ** strings written must be less than buffer size.
12: **
13: ** History:
14: ** HFN: wrote it
15: ** HWL: converted the caching scheme to be hierachical by taking
16: ** AL code from Deamon
2.2.2.4 ! frystyk 17: **
2.1 frystyk 18: */
19:
20: /* Library include files */
21: #include "tcp.h"
22: #include "HTUtils.h"
23: #include "HTString.h"
24: #include "HTFormat.h"
25: #include "HTAlert.h"
26: #include "HTFile.h"
27: #include "HTList.h"
2.2.2.4 ! frystyk 28: #include "HTParse.h"
! 29: #include "HTFWrite.h" /* Implemented here */
2.1 frystyk 30:
31: #define CACHE_LIMIT 100 /* files */
32:
33: #define CACHE_INFO ".cache_info"
34: #define INDEX_FILE ".cache_dirindex"
35: #define WELCOME_FILE ".cache_welcome"
36: #define TMP_SUFFIX ".cache_tmp"
37: #define LOCK_SUFFIX ".cache_lock"
38:
2.2.2.4 ! frystyk 39: PUBLIC int HTCacheLimit = CACHE_LIMIT;
! 40:
! 41: struct _HTStream {
! 42: CONST HTStreamClass * isa;
! 43:
! 44: FILE * fp;
! 45: BOOL leave_open; /* Close file? HFN 08/02-94 */
! 46: char * end_command;
! 47: BOOL remove_on_close;
! 48: BOOL announce;
! 49: char * filename;
! 50: HTRequest * request; /* saved for callback */
! 51: BOOL (*callback) PARAMS((struct _HTRequest * req, void * filename));
! 52: HTCacheItem * cache;
! 53: };
! 54:
! 55: PRIVATE HTStream HTBlackHoleInstance; /* Made static */
2.1 frystyk 56:
2.2.2.4 ! frystyk 57: PRIVATE BOOL HTCacheEnable = NO;
! 58: PRIVATE char * HTCacheRoot = NULL; /* Destination for cache */
! 59: PRIVATE HTList * HTCache = NULL; /* List of cached elements */
! 60:
! 61: PRIVATE char * HTTmpRoot = NULL; /* Dest for tmp files */
! 62:
! 63: /* ------------------------------------------------------------------------- */
! 64: /* BASIC STREAM CLASSES */
! 65: /* ------------------------------------------------------------------------- */
! 66:
! 67: /*
2.1 frystyk 68: **
2.2.2.4 ! frystyk 69: ** B L A C K H O L E C L A S S
2.1 frystyk 70: **
2.2.2.4 ! frystyk 71: ** There is only one black hole instance shared by anyone
! 72: ** who wants a black hole. These black holes don't radiate,
! 73: ** they just absorb data.
! 74: */
! 75: PRIVATE int HTBlackHole_put_character ARGS2(HTStream *, me, char, c)
! 76: {
! 77: return HT_OK;
! 78: }
! 79:
! 80: PRIVATE int HTBlackHole_put_string ARGS2(HTStream *, me, CONST char*, s)
! 81: {
! 82: return HT_OK;
! 83: }
! 84:
! 85: PRIVATE int HTBlackHole_write ARGS3(HTStream *, me, CONST char*, s, int, l)
! 86: {
! 87: return HT_OK;
! 88: }
! 89:
! 90: PRIVATE int HTBlackHole_flush ARGS1(HTStream *, me)
! 91: {
! 92: return HT_OK;
! 93: }
! 94:
! 95: PRIVATE int HTBlackHole_free ARGS1(HTStream *, me)
! 96: {
! 97: return HT_OK;
! 98: }
! 99:
! 100: PRIVATE int HTBlackHole_abort ARGS2(HTStream *, me, HTError, e)
! 101: {
! 102: return HT_ERROR;
! 103: }
! 104:
! 105:
! 106: /* Black Hole stream
! 107: ** -----------------
! 108: */
! 109: PRIVATE CONST HTStreamClass HTBlackHoleClass =
! 110: {
! 111: "BlackHole",
! 112: HTBlackHole_flush,
! 113: HTBlackHole_free,
! 114: HTBlackHole_abort,
! 115: HTBlackHole_put_character,
! 116: HTBlackHole_put_string,
! 117: HTBlackHole_write
! 118: };
! 119:
! 120: PUBLIC HTStream * HTBlackHole NOARGS
! 121: {
! 122: if (TRACE)
! 123: fprintf(TDEST, "BlackHole... Created\n");
! 124: HTBlackHoleInstance.isa = &HTBlackHoleClass; /* The rest is random */
! 125: return &HTBlackHoleInstance;
! 126: }
! 127:
! 128:
! 129: /* HTThroughLine
! 130: ** -------------
2.1 frystyk 131: **
2.2.2.4 ! frystyk 132: ** This function is a dummy function that returns the same output stream
! 133: ** as given as a parameter. Henrik 01/03-94
2.1 frystyk 134: */
2.2.2.4 ! frystyk 135: PUBLIC HTStream* HTThroughLine ARGS5(
! 136: HTRequest *, request,
! 137: void *, param,
! 138: HTFormat, input_format,
! 139: HTFormat, output_format,
! 140: HTStream *, output_stream) /* Only one used */
! 141: {
! 142: return output_stream;
! 143: }
! 144:
! 145: /* ------------------------------------------------------------------------- */
! 146: /* SOCKET WRITER STREAM */
! 147: /* ------------------------------------------------------------------------- */
! 148:
! 149: PRIVATE int HTFWriter_put_character ARGS2(HTStream *, me, char, c)
! 150: {
! 151: return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK;
! 152: }
! 153:
! 154: PRIVATE int HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
! 155: {
! 156: if (*s) /* For vms :-( 10/04-94 */
! 157: return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK;
! 158: return HT_OK;
! 159: }
! 160:
! 161: PRIVATE int HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
! 162: {
! 163: return (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK;
! 164: }
! 165:
! 166: PRIVATE int HTFWriter_flush ARGS1(HTStream *, me)
! 167: {
! 168: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
! 169: }
! 170:
! 171: PRIVATE int HTFWriter_free ARGS1(HTStream *, me)
! 172: {
! 173: if (me->cache) {
! 174: time_t finish_time;
! 175: time(&finish_time);
! 176: me->cache->load_delay = finish_time - me->cache->load_time;
! 177: /* Actually, ought to use draft ANSI-C difftime() */
! 178: /* But that I bet is more portable in real life (@@?) */
! 179: }
! 180:
! 181: if (me->leave_open != YES) fclose(me->fp);
! 182:
! 183: if (me->end_command) { /* Temp file */
! 184: HTProgress(me->end_command); /* Tell user what's happening */
! 185: system(me->end_command); /* @@ Beware of security hole */
! 186: free (me->end_command);
! 187: if (me->remove_on_close) {
! 188: unlink(me->filename);
! 189: }
! 190: }
! 191: if (me->callback) {
! 192: (*me->callback)(me->request, me->filename);
! 193: }
! 194: if (me->filename) free(me->filename);
! 195: free(me);
! 196: return HT_OK;
! 197: }
2.1 frystyk 198:
2.2.2.4 ! frystyk 199: PRIVATE int HTFWriter_abort ARGS2(HTStream *, me, HTError, e)
! 200: {
! 201: if (me->leave_open != YES) fclose(me->fp);
! 202: if (me->end_command) { /* Temp file */
! 203: if (TRACE)
! 204: fprintf(TDEST,"FileWriter.. Aborting: file %s not executed.\n",
! 205: me->filename ? me->filename : "???" );
! 206: free (me->end_command);
! 207: if (me->remove_on_close) {
! 208: unlink(me->filename);
! 209: }
! 210: }
! 211:
! 212: if (me->filename) free(me->filename);
! 213: free(me);
! 214: return HT_ERROR;
! 215: }
! 216:
! 217: PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
! 218: {
! 219: "FileWriter",
! 220: HTFWriter_flush,
! 221: HTFWriter_free,
! 222: HTFWriter_abort,
! 223: HTFWriter_put_character,
! 224: HTFWriter_put_string,
! 225: HTFWriter_write
! 226: };
! 227:
! 228: PUBLIC HTStream* HTFWriter_new ARGS2(FILE *, fp, BOOL, leave_open)
! 229: {
! 230: HTStream* me;
! 231:
! 232: if (!fp) {
! 233: if (TRACE)
! 234: fprintf(TDEST, "FileWriter.. Bad file descriptor\n");
! 235: return NULL;
! 236: }
! 237: me = (HTStream*)calloc(sizeof(*me),1);
! 238: if (me == NULL) outofmem(__FILE__, "HTFWriter_new");
! 239: me->isa = &HTFWriter;
! 240:
! 241: me->fp = fp;
! 242: me->leave_open = leave_open; /* HENRIK 08/02-94 */
! 243: me->end_command = NULL;
! 244: me->remove_on_close = NO;
! 245: me->announce = NO;
! 246: me->callback = NULL;
! 247: return me;
! 248: }
! 249:
! 250: /* ------------------------------------------------------------------------- */
! 251: /* CACHE MANAGER */
! 252: /* ------------------------------------------------------------------------- */
2.1 frystyk 253:
254: /*
255: ** Check that the name we're about to generate doesn't
256: ** clash with anything used by the caching system.
257: */
258: PRIVATE BOOL reserved_name ARGS1(char *, url)
259: {
260: char * name = strrchr(url, '/');
261: char * suff = NULL;
262:
263: if (name) name++;
264: else name = url;
265:
266: if (!strcmp(name, CACHE_INFO) ||
267: !strcmp(name, INDEX_FILE) ||
268: !strcmp(name, WELCOME_FILE))
269: return YES;
270:
271: suff = strrchr(name, TMP_SUFFIX[0]);
272: if (suff && !strcmp(suff, TMP_SUFFIX))
273: return YES;
274:
275: suff = strrchr(name, LOCK_SUFFIX[0]);
276: if (suff && !strcmp(suff, LOCK_SUFFIX))
277: return YES;
278:
279: return NO;
280: }
281:
282:
283: /*
284: ** Map url to cache file name.
285: */
2.2.2.4 ! frystyk 286: PRIVATE char * cache_file_name ARGS2(char *, base, char *, url)
2.1 frystyk 287: {
288: char * access = NULL;
289: char * host = NULL;
290: char * path = NULL;
291: char * cfn = NULL;
292: BOOL welcome = NO;
293: BOOL res = NO;
294:
295: if (!url || strchr(url, '?') || (res = reserved_name(url)) ||
296: !(access = HTParse(url, "", PARSE_ACCESS)) ||
297: (0 != strcmp(access, "http") &&
298: 0 != strcmp(access, "ftp") &&
299: 0 != strcmp(access, "gopher"))) {
300:
301: if (access) free(access);
302:
2.2.2.4 ! frystyk 303: if (res && CACHE_TRACE)
2.1 frystyk 304: fprintf(TDEST,
305: "Cache....... Clash with reserved name (\"%s\")\n",url);
306:
307: return NULL;
308: }
309:
310: host = HTParse(url, "", PARSE_HOST);
311: path = HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION);
312: if (path && path[strlen(path)-1] == '/')
313: welcome = YES;
314:
315: cfn = (char*)malloc(strlen(base) +
316: strlen(access) +
317: (host ? strlen(host) : 0) +
318: (path ? strlen(path) : 0) +
319: (welcome ? strlen(WELCOME_FILE) : 0) + 3);
320: if (!cfn) outofmem(__FILE__, "cache_file_name");
2.2.2.4 ! frystyk 321:
! 322: /* Removed extra slash - HF May2,95 */
! 323: sprintf(cfn, "%s%s/%s%s%s", base, access, host, path,
! 324: (welcome ? WELCOME_FILE : ""));
2.1 frystyk 325:
326: FREE(access); FREE(host); FREE(path);
327:
328: /*
329: ** This checks that the last component is not too long.
330: ** It could check all the components, but the last one
331: ** is most important because it could later blow up the
332: ** whole gc when reading cache info files.
333: ** Operating system handles other cases.
334: ** 64 = 42 + 22 and 22 = 42 - 20 :-)
335: ** In other words I just picked some number, it doesn't
336: ** really matter that much.
337: */
338: {
339: char * last = strrchr(cfn, '/');
340: if (!last) last = cfn;
341: if ((int)strlen(last) > 64) {
2.2.2.4 ! frystyk 342: if (CACHE_TRACE)
2.1 frystyk 343: fprintf(TDEST, "Too long.... cache file name \"%s\"\n", cfn);
344: free(cfn);
345: cfn = NULL;
346: }
347: }
348: return cfn;
349: }
350:
351:
352: /*
353: ** Create directory path for cache file
354: **
355: ** On exit:
356: ** return YES
357: ** if directories created -- after that caller
358: ** can rely on fopen(cfn,"w") succeeding.
359: **
360: */
361: PRIVATE BOOL create_cache_place ARGS2(char *, base, char *, cfn)
362: {
363: struct stat stat_info;
364: char * cur = NULL;
365: BOOL create = NO;
366:
367: if (!cfn || (int)strlen(cfn) <= (int)strlen(base) + 1)
368: return NO;
369:
370: cur = cfn + strlen(base) + 1;
371:
372: while ((cur = strchr(cur, '/'))) {
373: *cur = 0;
374: if (create || HTStat(cfn, &stat_info) == -1) {
375: create = YES; /* To avoid doing stat()s in vain */
2.2.2.4 ! frystyk 376: if (CACHE_TRACE)
2.1 frystyk 377: fprintf(TDEST,"Cache....... creating cache dir \"%s\"\n",cfn);
2.2.2.4 ! frystyk 378: if (MKDIR(cfn, 0777) < 0) {
! 379: if (CACHE_TRACE)
! 380: fprintf(TDEST,"Cache....... can't create dir `%s\'\n",cfn);
2.1 frystyk 381: return NO;
382: }
2.2.2.4 ! frystyk 383: } else {
2.1 frystyk 384: if (S_ISREG(stat_info.st_mode)) {
385: int len = strlen(cfn);
386: char * tmp1 = (char*)malloc(len + strlen(TMP_SUFFIX) + 1);
387: char * tmp2 = (char*)malloc(len + strlen(INDEX_FILE) + 2);
388: /* time_t t1,t2,t3,t4,t5; */
389:
390:
391: sprintf(tmp1, "%s%s", cfn, TMP_SUFFIX);
392: sprintf(tmp2, "%s/%s", cfn, INDEX_FILE);
393:
2.2.2.4 ! frystyk 394: if (CACHE_TRACE) {
2.1 frystyk 395: fprintf(TDEST,"Cache....... moving \"%s\" to \"%s\"\n",
396: cfn,tmp1);
397: fprintf(TDEST,"and......... creating dir \"%s\"\n",
398: cfn);
399: fprintf(TDEST,"and......... moving \"%s\" to \"%s\"\n",
400: tmp1,tmp2);
401: }
402: rename(cfn,tmp1);
2.2.2.4 ! frystyk 403: (void) MKDIR(cfn, 0777);
2.1 frystyk 404: rename(tmp1,tmp2);
405:
406: /* if (HTCacheInfo_for(cfn,&t1,&t2,&t3,&t4,&t5)) {
2.2.2.4 ! frystyk 407: if (CACHE_TRACE)
2.1 frystyk 408: fprintf(TDEST,"Adding...... info entry for %s\n",tmp2);
409: if (!HTCacheInfo_writeEntryFor(tmp2,t1,t2,t3,t4,t5))
410: HTLog_error2("Can't write cache info entry for",tmp2);
411: }
412: */
413: free(tmp1);
414: free(tmp2);
415: }
416: else {
2.2.2.4 ! frystyk 417: if (CACHE_TRACE)
2.1 frystyk 418: fprintf(TDEST,"Cache....... dir \"%s\" already exists\n",
419: cfn);
420: }
421: }
422: *cur = '/';
423: cur++;
424: }
425: return YES;
426: }
427:
428:
429: /*
2.2.2.4 ! frystyk 430: ** This function tries really hard to find a non-existent filename relative
! 431: ** to the path given.
! 432: ** path: path of directory in which to put temp file
! 433: ** url: used for a similar name or generating a hash within 'limit'
! 434: ** suffix: if != 0, this suffix is used, else no suffix
! 435: ** limit: if 0 => use last part of url
! 436: ** if >0 generate hash. Max value
! 437: ** of limit is max(unsigned int).
! 438: ** Returns NULL if no filename could be found.
! 439: */
! 440: PRIVATE char *HTFWriter_filename ARGS5(char *, path, char *, url,
! 441: CONST char *, suffix,
! 442: unsigned int, limit,
! 443: BOOL, flat)
2.1 frystyk 444: {
2.2.2.4 ! frystyk 445: #define HASHTRY 5 /* Keep this less than 10, please */
! 446: static int primes[HASHTRY] = {31, 37, 41, 43, 47}; /* Some primes */
! 447: char *urlfile = strrchr(url, '/');
! 448: char *filename = NULL;
2.1 frystyk 449:
2.2.2.4 ! frystyk 450: /* HWL 22/9/94 */
! 451: /* HWL added support for hierachical structure */
! 452: if (!flat) {
! 453: filename = cache_file_name(path, url);
! 454: if (!filename)
! 455: return NULL;
! 456: if (create_cache_place(path, filename))
! 457: return(filename);
! 458: else
! 459: return NULL;
! 460: }
2.1 frystyk 461:
462: StrAllocCopy(filename, path);
463: if (filename && urlfile++) { /* We don't want two '/'s */
464: int digits = 1;
465: unsigned hash;
466: char *hashstr = NULL;
467: char *replace = 0;
468: char *ptr; /* Dummy used several places */
469: char format[10];
470: if (*(filename+strlen(filename)-1) != '/') /* But one is OK, though */
471: StrAllocCat(filename, "/");
472: if (limit) { /* Use hash method */
473: unsigned int residue = limit;
474: while ((residue /= 10)) /* Find number of digits */
475: digits++;
476: if ((hashstr = (char *) malloc(digits+1)) == NULL)
477: outofmem(__FILE__, "HTFWriter_filename");
478: for(ptr=urlfile, hash=0; *ptr; ptr++) /* Calculate hash */
479: hash = *ptr + *primes * hash;
480: hash %= limit;
481: sprintf(format, "%%0%du", digits); /* Convert hash to string */
482: sprintf(hashstr, format, hash);
483: *(hashstr+digits) = '\0';
484: StrAllocCat(filename, hashstr);
485: #ifndef NO_GETPID
486: /* RISK: Race conditions may occur if this is not added to the filename */
487: {
488: char pidstr[10];
489: sprintf(pidstr, "-%d", getpid());
490: StrAllocCat(filename, pidstr);
491: }
492: #endif
493: if (suffix) { /* Add suffix */
494: if (*suffix != '.')
495: StrAllocCat(filename, ".");
496: StrAllocCat(filename, suffix);
497: }
498: replace = strrchr(filename, '/')+1; /* Remember place */
499: } else { /* Use url name */
500: char *urlptr = 0; /* Strip off any suffix */
501: StrAllocCopy(urlptr, urlfile);
502: ptr = strrchr(urlptr, '.');
503: if (ptr)
504: *ptr = '\0';
505: StrAllocCat(filename, urlptr);
506: free(urlptr);
507: if (suffix) { /* Add new suffix if any */
508: if (*suffix != '.')
509: StrAllocCat(filename, ".");
510: StrAllocCat(filename, suffix);
511: }
512: }
513: { /* Try if file exists */
514: int cnt;
515: FILE *fp;
516: for (cnt=1; cnt<HASHTRY; cnt++) {
517: if ((fp = fopen(filename, "r")) != NULL) {
518: fclose(fp);
519: if (limit) { /* recalculate hash with new prime */
520: for(ptr=urlfile, hash=0; *ptr; ptr++)
521: hash = *ptr + *(primes+cnt) * hash;
522: hash %= limit;
523: sprintf(hashstr, format, hash);
524: *(hashstr+digits) = '\0';
525: memcpy(replace, hashstr, digits);
526: } else { /* Add .n to the urlfile. n is a int */
527: if (cnt == 1) {
528: StrAllocCat(filename, ".1");
529: replace = filename+strlen(filename)-1;
530: } else
531: *replace = (char) cnt+0x30; /* Works if cnt<10 */
532: }
533: } else
534: break; /* File does not exist, so OK */
535: }
536: if (cnt >= HASHTRY) { /* If no file name could be found */
537: free(filename);
538: filename = NULL;
539: }
540: }
541: FREE(hashstr);
542: }
543: return filename;
544: }
545:
546:
2.2.2.4 ! frystyk 547: PRIVATE void HTCache_remove ARGS2(HTList *, list, HTCacheItem *, item)
2.1 frystyk 548: {
2.2.2.4 ! frystyk 549: if (CACHE_TRACE)
! 550: fprintf(TDEST, "Cache: Removing %s\n", item->filename);
! 551: HTList_removeObject(list, item);
! 552: HTList_removeObject(item->anchor->cacheItems, item);
! 553: unlink(item->filename);
2.1 frystyk 554:
2.2.2.4 ! frystyk 555: /* HWL 22/9/94: since we added a hierachical file structure, we should also clean up */
! 556: {
! 557: char * p;
! 558:
! 559: while ((p = strrchr(item->filename,'/')) && (p != NULL)){
! 560: item->filename[p - item->filename] = 0; /* this will be freed in a sec */
! 561: if (strcmp(item->filename, HTCacheRoot) != 0) {
! 562: if (CACHE_TRACE)
! 563: fprintf(TDEST, "rmdir %s\n", item->filename);
! 564: rmdir(item->filename); /* this will luckily fail if directory is not empty */
! 565: }
! 566: }
! 567: }
2.1 frystyk 568:
2.2.2.4 ! frystyk 569: free(item->filename);
! 570: free(item);
! 571: }
2.1 frystyk 572:
2.2.2.4 ! frystyk 573: /*
! 574: ** This can be called for the main list or an anchor's list
2.1 frystyk 575: */
2.2.2.4 ! frystyk 576: PUBLIC void HTCacheClear ARGS1(HTList *, list)
2.1 frystyk 577: {
2.2.2.4 ! frystyk 578: HTCacheItem * item;
! 579: while ((item = (HTCacheItem *) HTList_objectAt(list, 0)) != NULL) {
! 580: HTCache_remove(list, item);
! 581: }
2.1 frystyk 582: }
583:
584:
2.2.2.4 ! frystyk 585: /*
! 586: ** This function removes ALL cache files known to this session. The anchors
! 587: ** ARE updated, but normally this function is for exiting purposes.
2.1 frystyk 588: */
2.2.2.4 ! frystyk 589: PUBLIC void HTCacheDeleteAll NOARGS
2.1 frystyk 590: {
2.2.2.4 ! frystyk 591: HTCacheItem * item;
! 592: while ((item = (HTCacheItem *) HTList_objectAt(HTCache, 0)) != NULL) {
! 593: HTCache_remove(HTCache, item);
! 594: }
2.1 frystyk 595: }
596:
2.2.2.4 ! frystyk 597: /* Remove a file from the cache to prevent too many files from being cached
2.1 frystyk 598: */
2.2.2.4 ! frystyk 599: PRIVATE void limit_cache ARGS1(HTList * , list)
2.1 frystyk 600: {
2.2.2.4 ! frystyk 601: HTList * cur = list;
! 602: HTCacheItem * item;
! 603: time_t best_delay = 0; /* time_t in principle can be any arith type */
! 604: HTCacheItem* best_item = NULL;
2.1 frystyk 605:
2.2.2.4 ! frystyk 606: if (HTList_count(list) < HTCacheLimit) return; /* Limit not reached */
2.1 frystyk 607:
2.2.2.4 ! frystyk 608: while (NULL != (item = (HTCacheItem*)HTList_nextObject(cur))) {
! 609: if (best_delay == 0 || item->load_delay < best_delay) {
! 610: best_delay = item->load_delay;
! 611: best_item = item;
! 612: }
2.1 frystyk 613: }
2.2.2.4 ! frystyk 614:
! 615: if (best_item) HTCache_remove(list, best_item);
2.1 frystyk 616: }
617:
2.2.2.4 ! frystyk 618: /* Enable Cache
! 619: ** ------------
! 620: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
! 621: ** An empty string will make '/' as cache root
2.1 frystyk 622: */
2.2.2.4 ! frystyk 623: PUBLIC BOOL HTCache_enable ARGS1(CONST char *, cache_root)
! 624: {
! 625: if (cache_root)
! 626: HTCache_setRoot(cache_root);
! 627: HTCacheEnable = YES;
! 628: return YES;
! 629: }
2.1 frystyk 630:
2.2.2.4 ! frystyk 631:
! 632: /* Disable Cache
! 633: ** ------------
! 634: ** Turns off the cache. Note that the cache can be disabled and enabled
! 635: ** at any time. The cache root is kept and can be reused during the
! 636: ** execution.
! 637: */
! 638: PUBLIC BOOL HTCache_disable NOARGS
2.1 frystyk 639: {
2.2.2.4 ! frystyk 640: HTCacheEnable = NO;
! 641: return YES;
! 642: }
! 643:
! 644: /* Is Cache Enabled
! 645: ** ----------------
! 646: ** Returns YES or NO. Also makes sure that we have a root value
! 647: ** (even though it might be invalid)
! 648: */
! 649: PUBLIC BOOL HTCache_isEnabled NOARGS
! 650: {
! 651: if (HTCacheEnable) {
! 652: if (!HTCacheRoot)
! 653: HTCache_setRoot(NULL);
! 654: return YES;
2.1 frystyk 655: }
2.2.2.4 ! frystyk 656: return NO;
! 657: }
2.1 frystyk 658:
2.2.2.4 ! frystyk 659:
! 660: /* Set Cache Root
! 661: ** --------------
! 662: ** If `cache_root' is NULL then the current value (might be a define)
! 663: ** Should we check if the cache_root is actually OK? I think not!
! 664: */
! 665: PUBLIC BOOL HTCache_setRoot ARGS1(CONST char *, cache_root)
! 666: {
! 667: StrAllocCopy(HTCacheRoot, cache_root ? cache_root : HT_CACHE_ROOT);
! 668: if (*(HTCacheRoot+strlen(HTCacheRoot)-1) != '/')
! 669: StrAllocCat(HTCacheRoot, "/");
! 670: if (CACHE_TRACE)
! 671: fprintf(TDEST, "Cache Root.. Root set to `%s\'\n", HTCacheRoot);
! 672: return YES;
2.1 frystyk 673: }
674:
675:
2.2.2.4 ! frystyk 676: /* Get Cache Root
! 677: ** --------------
! 678: */
! 679: PUBLIC CONST char * HTCache_getRoot NOARGS
! 680: {
! 681: return HTCacheRoot;
! 682: }
! 683:
2.1 frystyk 684:
2.2.2.4 ! frystyk 685: /* Free Cache Root
! 686: ** --------------
! 687: ** For clean up memory
2.1 frystyk 688: */
2.2.2.4 ! frystyk 689: PUBLIC void HTCache_freeRoot NOARGS
! 690: {
! 691: FREE(HTCacheRoot);
! 692: }
2.1 frystyk 693:
694:
2.2.2.4 ! frystyk 695: /* Cache Writer
! 696: ** ------------------
! 697: **
2.1 frystyk 698: */
2.2.2.4 ! frystyk 699: PUBLIC HTStream* HTCacheWriter ARGS5(
! 700: HTRequest *, request,
! 701: void *, param,
! 702: HTFormat, input_format,
! 703: HTFormat, output_format,
! 704: HTStream *, output_stream)
! 705:
2.1 frystyk 706: {
2.2.2.4 ! frystyk 707: char *fnam;
2.1 frystyk 708: HTStream* me;
709:
2.2.2.4 ! frystyk 710: if (HTClientHost) {
! 711: if (CACHE_TRACE)
! 712: fprintf(TDEST, "Only caching if WWW is run locally.\n");
! 713: return HTBlackHole();
! 714: }
2.1 frystyk 715: me = (HTStream*)calloc(sizeof(*me),1);
2.2.2.4 ! frystyk 716: if (me == NULL) outofmem(__FILE__, "CacheWriter");
! 717: me->isa = &HTFWriter;
2.1 frystyk 718: me->end_command = NULL;
2.2.2.4 ! frystyk 719: me->remove_on_close = NO; /* If needed, put into end_command */
2.1 frystyk 720: me->announce = NO;
2.2.2.4 ! frystyk 721:
! 722: /* Save the file under a suitably suffixed name */
! 723: fnam = HTFWriter_filename(HTCacheRoot,
! 724: HTAnchor_physical(request->anchor),
! 725: HTFileSuffix(input_format),
! 726: 0, NO);
! 727: if (!fnam) { /* HWL 22/9/94 */
! 728: free(me);
! 729: return HTBlackHole();
! 730: }
! 731:
! 732: me->filename = NULL;
! 733: limit_cache(HTCache); /* Limit number (not size) of files */
! 734: me->fp = fopen (fnam, "w");
! 735: if (!me->fp) {
! 736: HTAlert("Can't open local file for writing.");
! 737: if (CACHE_TRACE)
! 738: fprintf(TDEST, "HTStream: can't open %s for writing\n",fnam);
! 739: free(fnam);
! 740: free(me);
! 741: return HTBlackHole();
! 742: }
! 743:
! 744: /* Set up a cache record */
! 745:
! 746: if (CACHE_TRACE)
! 747: fprintf(TDEST, "Cache....... Creating file %s\n", fnam);
! 748: me->cache = (HTCacheItem*)calloc(sizeof(*me->cache),1);
! 749: if (!me->cache)outofmem(__FILE__, "cache");
! 750: time(&me->cache->load_time);
! 751: StrAllocCopy(me->cache->filename, fnam);
! 752: me->cache->anchor = request->anchor;
! 753: if (!request->anchor->cacheItems)
! 754: request->anchor->cacheItems = HTList_new();
! 755: HTList_addObject(request->anchor->cacheItems, me->cache);
! 756: me->cache->format = input_format;
! 757:
! 758: if (!HTCache) HTCache = HTList_new();
! 759: HTList_addObject(HTCache, me->cache);
! 760:
! 761: me->callback = request->callback;
! 762: me->request = request; /* won't be freed */
! 763: me->filename = fnam; /* will be freed */
2.1 frystyk 764: return me;
765: }
766:
2.2.2.4 ! frystyk 767: /* ------------------------------------------------------------------------- */
! 768: /* FILE WRITER ROUTINES */
! 769: /* ------------------------------------------------------------------------- */
! 770:
! 771: /* Set TMP Root
! 772: ** --------------
! 773: ** If `tmp_root' is NULL use the current value (might be a define)
2.1 frystyk 774: */
2.2.2.4 ! frystyk 775: PUBLIC BOOL HTTmp_setRoot ARGS1(CONST char *, tmp_root)
! 776: {
! 777: StrAllocCopy(HTTmpRoot, tmp_root ? tmp_root : HT_TMP_ROOT);
! 778: if (*(HTTmpRoot+strlen(HTTmpRoot)-1) != '/')
! 779: StrAllocCat(HTTmpRoot, "/");
! 780: if (TRACE)
! 781: fprintf(TDEST, "Tmp Root.... Root set to `%s\'\n", HTTmpRoot);
! 782: return YES;
! 783: }
2.1 frystyk 784:
785:
2.2.2.4 ! frystyk 786: /* Get Tmp Root
! 787: ** --------------
! 788: */
! 789: PUBLIC CONST char * HTTmp_getRoot NOARGS
! 790: {
! 791: return HTTmpRoot;
! 792: }
! 793:
! 794:
! 795: /* Free Tmp Root
! 796: ** --------------
! 797: ** For clean up memory
! 798: */
! 799: PUBLIC void HTTmp_freeRoot NOARGS
! 800: {
! 801: FREE(HTTmpRoot);
! 802: }
2.1 frystyk 803:
804: /* Take action using a system command
805: ** ----------------------------------
806: **
807: ** Creates temporary file, writes to it, executes system command
808: ** on end-document. The suffix of the temp file can be given
809: ** in case the application is fussy, or so that a generic opener can
810: ** be used.
811: */
812: PUBLIC HTStream* HTSaveAndExecute ARGS5(
813: HTRequest *, request,
814: void *, param,
815: HTFormat, input_format,
816: HTFormat, output_format,
817: HTStream *, output_stream)
2.2.2.3 cbrooks 818:
2.1 frystyk 819: #ifdef REMOVE_FILE
820: {
821: char *fnam;
822:
823: HTStream* me;
824:
825: if (HTSecure) {
826: HTAlert("Can't save data to file -- please run WWW locally");
827: return HTBlackHole();
828: }
829:
2.2.2.4 ! frystyk 830: if (!HTTmpRoot) {
2.1 frystyk 831: if (TRACE) fprintf(TDEST, "Save and execute turned off");
832: return HTBlackHole();
833: }
834:
835: me = (HTStream*)calloc(sizeof(*me),1);
836: me = (HTStream*)calloc(sizeof(*me), 1);
837: if (me == NULL) outofmem(__FILE__, "Save and execute");
838: me->isa = &HTFWriter;
839:
840: /* Let's find a hash name for this file */
2.2.2.4 ! frystyk 841: if ((fnam = HTFWriter_filename(HTTmpRoot,
2.1 frystyk 842: HTAnchor_physical(request->anchor),
843: HTFileSuffix(input_format),
844: 1000, YES)) == NULL) {
845: HTAlert("Can't find a suitable file name");
846: return NULL;
847: }
848:
849: me->request = request; /* won't be freed */
850: me->fp = fopen (fnam, "w");
851: if (!me->fp) {
852: HTAlert("Can't open temporary file!");
853: free(fnam);
854: free(me);
855: return NULL;
856: }
857: StrAllocCopy(me->filename, fnam);
858:
859: /* Make command to process file
860: */
861: me->end_command = (char *) malloc ((strlen((char *) param) + 10 +
862: 3*strlen(fnam)) * sizeof (char));
863: if (me == NULL) outofmem(__FILE__, "SaveAndExecute");
864:
865: sprintf (me->end_command, (char *) param, fnam, fnam, fnam);
866:
867: me->remove_on_close = NO;
868:
869: me->announce = NO;
870: free (fnam);
871: return me;
872: }
873:
874: #else /* can't do remove */
875: { return NULL; }
876: #endif
877:
878:
879: /* Save Locally
880: ** ------------
881: **
882: ** Bugs:
883: ** GUI Apps should open local Save panel here really.
884: **
885: */
886: PUBLIC HTStream* HTSaveLocally ARGS5(
887: HTRequest *, request,
888: void *, param,
889: HTFormat, input_format,
890: HTFormat, output_format,
891: HTStream *, output_stream) /* Not used */
892:
893: {
894: char *fnam = NULL;
895: char *answer = NULL;
896: HTStream* me;
897:
898: if (HTClientHost) {
899: HTAlert("Can't save data to file -- please run WWW locally");
900: return HTBlackHole();
901: }
902:
2.2.2.4 ! frystyk 903: if (!HTTmpRoot) {
2.1 frystyk 904: if (TRACE) fprintf(TDEST, "Save locally turned off");
905: return HTBlackHole();
906: }
907:
908: me = (HTStream*)calloc(sizeof(*me),1);
909: if (me == NULL) outofmem(__FILE__, "SaveLocally");
910: me->isa = &HTFWriter;
911: me->announce = YES;
912:
913: /* Let's find a 'human' file name for this file */
2.2.2.4 ! frystyk 914: fnam = HTFWriter_filename(HTTmpRoot,
2.1 frystyk 915: HTAnchor_physical(request->anchor),
916: HTFileSuffix(input_format),
917: 0, YES);
918:
919: /* Save Panel */
920: answer = HTPrompt("Give name of file to save in", fnam ? fnam : "");
921: FREE(fnam);
922:
923: me->fp = fopen (answer, "w");
924: if (!me->fp) {
925: HTAlert("Can't open local file to write into.");
926: FREE(answer);
927: free(me);
928: return NULL;
929: }
930: me->callback = NULL;
931: me->request = request; /* won't be freed */
932: me->filename = answer; /* Will be freed */
933: return me;
934: }
935:
936:
937: /* Save and Call Back
938: ** ------------------
939: **
940: **
941: ** The special case is a kludge. Better is everything uses streams
942: ** and nothing uses files. Then this routine will go too. :-))
943: */
944: PUBLIC HTStream* HTSaveAndCallBack ARGS5(
945: HTRequest *, request,
946: void *, param,
947: HTFormat, input_format,
948: HTFormat, output_format,
949: HTStream *, output_stream)
950: {
951: HTStream * me;
952:
953: if (request->using_cache) { /* Special case! file wanted && cache hit */
954: (*request->callback)(request,
955: ((HTCacheItem*)request->using_cache)->filename);
2.2.2.4 ! frystyk 956: return HTBlackHole();
2.1 frystyk 957: } else {
958: me = HTCacheWriter(request, param,
959: input_format, output_format, output_stream);
960: if (me) {
961: me->callback = request->callback;
962: }
963: }
964: return me;
965: }
966:
Webmaster