Annotation of libwww/Library/src/HTCache.c, revision 2.12
2.1 frystyk 1: /* HTCache.c
2: ** CACHE WRITER
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This modules manages the cache
8: **
9: ** History:
10: ** HFN: spawned from HTFwrite
11: ** HWL: converted the caching scheme to be hierachical by taking
12: ** AL code from Deamon
13: **
14: */
15:
16: /* Library include files */
17: #include "tcp.h"
18: #include "HTUtils.h"
19: #include "HTString.h"
20: #include "HTFormat.h"
21: #include "HTFWrite.h"
2.12 ! frystyk 22: #include "HTAccess.h"
2.1 frystyk 23: #include "HTBind.h"
24: #include "HTList.h"
2.7 frystyk 25: #include "HTReqMan.h"
2.1 frystyk 26: #include "HTParse.h"
27: #include "HTCache.h" /* Implemented here */
28:
29: /*
30: ** The cache limit is the number of files which are kept. Yes, I know,
31: ** the amount of disk space would be more relevant. So this may change.
32: ** Currently it is preset to 100 but may be changed by the application by
33: ** writing into this variable.
34: */
35: #define CACHE_LIMIT 5 /* Number of files */
36:
37: #define CACHE_INFO ".cache_info"
38: #define INDEX_FILE ".cache_dirindex"
39: #define WELCOME_FILE ".cache_welcome"
40: #define TMP_SUFFIX ".cache_tmp"
41: #define LOCK_SUFFIX ".cache_lock"
42:
2.7 frystyk 43: /* This is the default cache directory: */
44: #ifndef HT_CACHE_ROOT
45: #define HT_CACHE_ROOT "/tmp"
46: #endif
47:
48: typedef struct _HTCache {
2.1 frystyk 49: HTFormat format; /* May have many formats per anchor */
50: char * filename;
51: time_t start_time;
52: time_t load_delay;
53: int reference_count;
2.7 frystyk 54: } HTCache;
2.1 frystyk 55:
56: struct _HTStream {
57: CONST HTStreamClass * isa;
58: FILE * fp;
2.7 frystyk 59: HTCache * cache;
2.1 frystyk 60: HTRequest * request;
61: };
62:
63: PRIVATE BOOL HTCacheEnable = NO;
64: PRIVATE char * HTCacheRoot = NULL; /* Destination for cache */
2.7 frystyk 65: PRIVATE HTList * HTCacheList = NULL; /* List of cached elements */
2.1 frystyk 66: PRIVATE int HTCacheLimit = CACHE_LIMIT;
67:
2.7 frystyk 68: PRIVATE HTExpiresMode HTExpMode = HT_EXPIRES_IGNORE;
69: PRIVATE char *HTExpNotify = NULL;
70:
71: PRIVATE HTMemoryCacheHandler *HTMemoryCache = NULL; /* Memory cache handler */
2.2 frystyk 72:
2.1 frystyk 73: /* ------------------------------------------------------------------------- */
2.2 frystyk 74: /* GARBAGE COLLECTOR */
2.1 frystyk 75: /* ------------------------------------------------------------------------- */
76:
77: /*
78: ** Removes cache item from disk and corresponding object from list in memory
79: */
2.12 ! frystyk 80: PRIVATE void HTCache_remove (HTCache * item)
2.1 frystyk 81: {
2.7 frystyk 82: if (HTCacheList && item) {
2.1 frystyk 83: if (CACHE_TRACE)
2.10 frystyk 84: TTYPrint(TDEST, "Cache....... Removing %s\n", item->filename);
2.7 frystyk 85: HTList_removeObject(HTCacheList, item);
2.1 frystyk 86: REMOVE(item->filename);
87:
88: /* HWL 22/9/94: Clean up hierachical file structure */
89: {
90: char * p;
91: while ((p = strrchr(item->filename,'/')) && (p != NULL)){
92: item->filename[p-item->filename] = 0;
93: if (strcmp(item->filename, HTCacheRoot) != 0) {
94: if (CACHE_TRACE)
2.10 frystyk 95: TTYPrint(TDEST, "rmdir....... %s\n", item->filename);
2.1 frystyk 96: RMDIR(item->filename); /* fails if directory isn't empty */
97: }
98: }
99: }
100: free(item->filename);
101: free(item);
102: }
103: }
104:
105:
106: /*
107: ** Remove a file from the cache to prevent too many files from being cached
108: */
2.12 ! frystyk 109: PRIVATE void limit_cache (HTList * list)
2.1 frystyk 110: {
111: HTList * cur = list;
2.7 frystyk 112: HTCache * item;
2.1 frystyk 113: time_t best_delay = 0; /* time_t in principle can be any arith type */
2.7 frystyk 114: HTCache* best_item = NULL;
2.1 frystyk 115:
116: if (HTList_count(list) < HTCacheLimit) return; /* Limit not reached */
117:
2.7 frystyk 118: while (NULL != (item = (HTCache*)HTList_nextObject(cur))) {
2.1 frystyk 119: if (best_delay == 0 || item->load_delay < best_delay) {
120: best_delay = item->load_delay;
121: best_item = item;
122: }
123: }
124: if (best_item) HTCache_remove(best_item);
125: }
126:
127: /*
128: ** Check that the name we're about to generate doesn't
129: ** clash with anything used by the caching system.
130: */
2.12 ! frystyk 131: PRIVATE BOOL reserved_name (char * url)
2.1 frystyk 132: {
133: char * name = strrchr(url, '/');
134: char * suff = NULL;
135:
136: if (name) name++;
137: else name = url;
138:
139: if (!strcmp(name, CACHE_INFO) ||
140: !strcmp(name, INDEX_FILE) ||
141: !strcmp(name, WELCOME_FILE))
142: return YES;
143:
144: suff = strrchr(name, TMP_SUFFIX[0]);
145: if (suff && !strcmp(suff, TMP_SUFFIX))
146: return YES;
147:
148: suff = strrchr(name, LOCK_SUFFIX[0]);
149: if (suff && !strcmp(suff, LOCK_SUFFIX))
150: return YES;
151:
152: return NO;
153: }
154:
155: /*
2.2 frystyk 156: ** Removes all cache entries in memory
157: */
2.12 ! frystyk 158: PUBLIC void HTCache_clearMem (void)
2.2 frystyk 159: {
2.7 frystyk 160: HTList *cur=HTCacheList;
161: HTCache *pres;
2.2 frystyk 162: if (cur) {
2.7 frystyk 163: while ((pres = (HTCache *) HTList_nextObject(cur))) {
2.2 frystyk 164: FREE(pres->filename);
165: free(pres);
166: }
2.7 frystyk 167: HTList_delete(HTCacheList);
168: HTCacheList = NULL;
2.2 frystyk 169: }
170: }
171:
172: /*
173: ** Removes all cache entries in memory and on disk
174: */
2.12 ! frystyk 175: PUBLIC void HTCache_deleteAll (void)
2.2 frystyk 176: {
2.7 frystyk 177: HTList *cur=HTCacheList;
178: HTCache * pres;
2.2 frystyk 179: if (cur) {
2.7 frystyk 180: while ((pres = (HTCache *) HTList_lastObject(cur)))
2.2 frystyk 181: HTCache_remove(pres);
2.7 frystyk 182: HTList_delete(HTCacheList);
183: HTCacheList = NULL;
2.2 frystyk 184: }
185: }
186:
187: /* ------------------------------------------------------------------------- */
188: /* NAMING SCHEME */
189: /* ------------------------------------------------------------------------- */
190:
191: /*
2.1 frystyk 192: ** Map url to cache file name.
193: */
2.12 ! frystyk 194: PRIVATE char * cache_file_name (char * url)
2.1 frystyk 195: {
196: char * access = NULL;
197: char * host = NULL;
198: char * path = NULL;
199: char * cfn = NULL;
200: BOOL welcome = NO;
201: BOOL res = NO;
202:
203: if (!url || strchr(url, '?') || (res = reserved_name(url)) ||
204: !(access = HTParse(url, "", PARSE_ACCESS)) ||
205: (0 != strcmp(access, "http") &&
206: 0 != strcmp(access, "ftp") &&
207: 0 != strcmp(access, "gopher"))) {
208:
209: if (access) free(access);
210:
211: if (res && CACHE_TRACE)
2.10 frystyk 212: TTYPrint(TDEST,
2.1 frystyk 213: "Cache....... Clash with reserved name (\"%s\")\n",url);
214:
215: return NULL;
216: }
217:
218: host = HTParse(url, "", PARSE_HOST);
219: path = HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION);
220: if (path && path[strlen(path)-1] == '/')
221: welcome = YES;
222:
223: cfn = (char*)malloc(strlen(HTCacheRoot) +
224: strlen(access) +
225: (host ? strlen(host) : 0) +
226: (path ? strlen(path) : 0) +
227: (welcome ? strlen(WELCOME_FILE) : 0) + 3);
228: if (!cfn) outofmem(__FILE__, "cache_file_name");
229:
230: /* Removed extra slash - HF May2,95 */
231: sprintf(cfn, "%s%s/%s%s%s", HTCacheRoot, access, host, path,
232: (welcome ? WELCOME_FILE : ""));
233:
234: FREE(access); FREE(host); FREE(path);
235:
236: /*
237: ** This checks that the last component is not too long.
238: ** It could check all the components, but the last one
239: ** is most important because it could later blow up the
240: ** whole gc when reading cache info files.
241: ** Operating system handles other cases.
242: ** 64 = 42 + 22 and 22 = 42 - 20 :-)
243: ** In other words I just picked some number, it doesn't
244: ** really matter that much.
245: */
246: {
247: char * last = strrchr(cfn, '/');
248: if (!last) last = cfn;
249: if ((int)strlen(last) > 64) {
250: if (CACHE_TRACE)
2.10 frystyk 251: TTYPrint(TDEST, "Too long.... cache file name \"%s\"\n", cfn);
2.1 frystyk 252: free(cfn);
253: cfn = NULL;
254: }
255: }
256: return cfn;
257: }
258:
259:
260: /*
261: ** Create directory path for cache file
262: **
263: ** On exit:
264: ** return YES
265: ** if directories created -- after that caller
266: ** can rely on fopen(cfn,"w") succeeding.
267: **
268: */
2.12 ! frystyk 269: PRIVATE BOOL create_cache_place (char * cfn)
2.1 frystyk 270: {
271: struct stat stat_info;
272: char * cur = NULL;
273: BOOL create = NO;
274:
275: if (!cfn || (int)strlen(cfn) <= (int)strlen(HTCacheRoot) + 1)
276: return NO;
277:
278: cur = cfn + strlen(HTCacheRoot) + 1;
279:
280: while ((cur = strchr(cur, '/'))) {
281: *cur = 0;
2.6 frystyk 282: if (create || HT_STAT(cfn, &stat_info) == -1) {
2.1 frystyk 283: create = YES; /* To avoid doing stat()s in vain */
284: if (CACHE_TRACE)
2.10 frystyk 285: TTYPrint(TDEST,"Cache....... creating cache dir \"%s\"\n",cfn);
2.1 frystyk 286: if (MKDIR(cfn, 0777) < 0) {
287: if (CACHE_TRACE)
2.10 frystyk 288: TTYPrint(TDEST,"Cache....... can't create dir `%s\'\n",cfn);
2.1 frystyk 289: return NO;
290: }
291: } else {
292: if (S_ISREG(stat_info.st_mode)) {
293: int len = strlen(cfn);
294: char * tmp1 = (char*)malloc(len + strlen(TMP_SUFFIX) + 1);
295: char * tmp2 = (char*)malloc(len + strlen(INDEX_FILE) + 2);
296: /* time_t t1,t2,t3,t4,t5; */
297:
298:
299: sprintf(tmp1, "%s%s", cfn, TMP_SUFFIX);
300: sprintf(tmp2, "%s/%s", cfn, INDEX_FILE);
301:
302: if (CACHE_TRACE) {
2.10 frystyk 303: TTYPrint(TDEST,"Cache....... moving \"%s\" to \"%s\"\n",
2.1 frystyk 304: cfn,tmp1);
2.10 frystyk 305: TTYPrint(TDEST,"and......... creating dir \"%s\"\n",
2.1 frystyk 306: cfn);
2.10 frystyk 307: TTYPrint(TDEST,"and......... moving \"%s\" to \"%s\"\n",
2.1 frystyk 308: tmp1,tmp2);
309: }
310: rename(cfn,tmp1);
311: (void) MKDIR(cfn, 0777);
312: rename(tmp1,tmp2);
313: free(tmp1);
314: free(tmp2);
315: }
316: else {
317: if (CACHE_TRACE)
2.10 frystyk 318: TTYPrint(TDEST,"Cache....... dir \"%s\" already exists\n",
2.1 frystyk 319: cfn);
320: }
321: }
322: *cur = '/';
323: cur++;
324: }
325: return YES;
326: }
327:
328:
329: /* Create a cache path
330: ** -------------------
331: ** Find a full path name for the cache file and create the path if it
332: ** does not already exist. Returns name or NULL
333: ** HWL 22/9/94
334: ** HWL added support for hierachical structure
335: */
2.12 ! frystyk 336: PRIVATE char *HTCache_getName (char * url)
2.1 frystyk 337: {
338: char *filename = cache_file_name(url);
339: if (!filename)
340: return NULL;
341: if (create_cache_place(filename))
342: return(filename);
343: return NULL;
344: }
345:
346: /*
347: ** Make a WWW name from a cache name and returns it if OK, else NULL.
348: ** The string returned must be freed by the caller.
349: ** We keep this function private as we might change the naming scheme for
350: ** cache files. Right now it follows the file hierarchi.
351: */
2.12 ! frystyk 352: PRIVATE char *HTCache_wwwName (char * name)
2.1 frystyk 353: {
354: char * result = NULL;
355: if (name && *name) {
356: StrAllocCopy(result, "file:"); /* We get an absolute file name */
357: #ifdef VMS
358: /* convert directory name to Unix-style syntax */
2.4 frystyk 359: {
360: char * disk = strchr (name, ':');
361: char * dir = strchr (name, '[');
362: if (disk) {
363: *disk = '\0';
364: StrAllocCat(result, "/"); /* needs delimiter */
365: StrAllocCat(result, name);
366: }
367: if (dir) {
368: char *p;
369: *dir = '/'; /* Convert leading '[' */
370: for (p = dir ; *p != ']'; ++p)
371: if (*p == '.') *p = '/';
372: *p = '\0'; /* Cut on final ']' */
373: StrAllocCat(result, dir);
374: }
2.1 frystyk 375: }
376: #else /* not VMS */
377: #ifdef WIN32
2.4 frystyk 378: {
379: char * p = name; /* a colon */
380: while( *p != 0 ) {
381: if (*p == '\\') /* change to one true slash */
382: *p = '/' ;
383: p++;
384: }
385: StrAllocCat(result, name);
2.1 frystyk 386: }
387: #else /* not WIN32 */
388: StrAllocCat (result, name);
389: #endif /* not WIN32 */
390: #endif /* not VMS */
391: }
392: return result;
393: }
394:
2.2 frystyk 395: /* ------------------------------------------------------------------------- */
396: /* CACHE PARAMETERS */
397: /* ------------------------------------------------------------------------- */
2.1 frystyk 398:
399: /* Enable Cache
400: ** ------------
401: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
402: ** An empty string will make '/' as cache root
2.12 ! frystyk 403: ** We can only enable the cache if the HTSecure flag is not set. This
! 404: ** is for example the case if using an application as a telnet shell.
2.1 frystyk 405: */
2.12 ! frystyk 406: PUBLIC BOOL HTCache_enable (CONST char * cache_root)
2.1 frystyk 407: {
2.12 ! frystyk 408: if (!HTLib_secure()) {
2.1 frystyk 409: HTCache_setRoot(cache_root);
2.12 ! frystyk 410: HTCacheEnable = YES;
! 411: return YES;
! 412: }
! 413: return NO;
2.1 frystyk 414: }
415:
416:
417: /* Disable Cache
418: ** ------------
419: ** Turns off the cache. Note that the cache can be disabled and enabled
420: ** at any time. The cache root is kept and can be reused during the
421: ** execution.
422: */
2.12 ! frystyk 423: PUBLIC BOOL HTCache_disable (void)
2.1 frystyk 424: {
425: HTCacheEnable = NO;
426: return YES;
427: }
428:
429: /* Is Cache Enabled
430: ** ----------------
431: ** Returns YES or NO. Also makes sure that we have a root value
432: ** (even though it might be invalid)
433: */
2.12 ! frystyk 434: PUBLIC BOOL HTCache_isEnabled (void)
2.1 frystyk 435: {
2.12 ! frystyk 436: return HTCacheEnable;
2.1 frystyk 437: }
438:
439:
440: /* Set Cache Root
441: ** --------------
442: ** If `cache_root' is NULL then the current value (might be a define)
443: ** Should we check if the cache_root is actually OK? I think not!
444: */
2.12 ! frystyk 445: PUBLIC BOOL HTCache_setRoot (CONST char * cache_root)
2.1 frystyk 446: {
447: StrAllocCopy(HTCacheRoot, cache_root ? cache_root : HT_CACHE_ROOT);
448: if (*(HTCacheRoot+strlen(HTCacheRoot)-1) != '/')
449: StrAllocCat(HTCacheRoot, "/");
450: if (CACHE_TRACE)
2.10 frystyk 451: TTYPrint(TDEST, "Cache Root.. Root set to `%s\'\n", HTCacheRoot);
2.1 frystyk 452: return YES;
453: }
454:
455:
456: /* Get Cache Root
457: ** --------------
458: */
2.12 ! frystyk 459: PUBLIC CONST char * HTCache_getRoot (void)
2.1 frystyk 460: {
461: return HTCacheRoot;
462: }
463:
464: /* Free Cache Root
465: ** --------------
466: ** For clean up memory
467: */
2.12 ! frystyk 468: PUBLIC void HTCache_freeRoot (void)
2.1 frystyk 469: {
470: FREE(HTCacheRoot);
471: }
472:
473: /* ------------------------------------------------------------------------- */
2.7 frystyk 474: /* MEMORY CACHE */
475: /* ------------------------------------------------------------------------- */
476:
477: /*
478: ** Register a Memory Cache Handler. This function is introduced in order to
479: ** avoid having references to HText module outside HTML.
480: */
481: PUBLIC BOOL HTMemoryCache_register (HTMemoryCacheHandler * cbf)
482: {
483: return (HTMemoryCache = cbf) ? YES : NO;
484: }
485:
486: PUBLIC BOOL HTMemoryCache_unRegister (void)
487: {
488: HTMemoryCache = NULL;
489: return YES;
490: }
491:
492: PUBLIC int HTMemoryCache_check (HTRequest * request)
493: {
2.8 frystyk 494: return HTMemoryCache ? HTMemoryCache(request,HTExpMode,HTExpNotify) : 0;
2.7 frystyk 495: }
496:
497: /*
498: ** Set the mode for how we handle Expires header from the local history
499: ** list. The following modes are available:
500: **
501: ** HT_EXPIRES_IGNORE : No update in the history list
502: ** HT_EXPIRES_NOTIFY : The user is notified but no reload
503: ** HT_EXPIRES_AUTO : Automatic reload
504: **
505: ** The notify only makes sense when HT_EXPIRES_NOTIFY. NULL is valid.
506: */
2.12 ! frystyk 507: PUBLIC void HTCache_setExpiresMode (HTExpiresMode mode, char * notify)
2.7 frystyk 508: {
509: HTExpMode = mode;
510: HTExpNotify = notify;
511: }
512:
2.12 ! frystyk 513: PUBLIC HTExpiresMode HTCache_expiresMode (char ** notify)
2.7 frystyk 514: {
515: *notify = HTExpNotify ? HTExpNotify : "This version has expired!";
516: return HTExpMode;
517: }
518:
519: /* ------------------------------------------------------------------------- */
2.2 frystyk 520: /* CACHE MANAGER */
521: /* ------------------------------------------------------------------------- */
522:
523: /*
524: ** Verifies if a cache object exists for this URL and if so returns a URL
525: ** for the cached object. It does not verify whether the object is valid or
526: ** not, for example it might have expired.
527: **
528: ** Returns: file name If OK (must be freed by caller)
529: ** NULL If no cache object found
530: */
2.12 ! frystyk 531: PUBLIC char * HTCache_getReference (char * url)
2.2 frystyk 532: {
533: if (url && HTCache_isEnabled()) {
534: char *fnam = cache_file_name(url);
535: if (fnam) {
536: FILE *fp = fopen(fnam, "r");
537: if (fp) {
538: char *url = HTCache_wwwName(fnam);
539: fclose(fp);
540: if (CACHE_TRACE)
2.10 frystyk 541: TTYPrint(TDEST, "Cache....... Object found `%s\'\n", url);
2.2 frystyk 542: free(fnam);
543: return url;
544: } else
545: free(fnam);
546: }
547: }
548: return NULL;
549: }
550:
551: /*
552: ** This function checks whether a document has expired or not.
553: ** The check is based on the metainformation passed in the anchor object
554: ** The function returns YES or NO.
555: */
2.12 ! frystyk 556: PUBLIC BOOL HTCache_isValid (HTParentAnchor * anchor)
2.2 frystyk 557: {
558: time_t cur = time(NULL);
559: time_t expires = HTAnchor_expires(anchor);
560: return (expires>0 && cur>0 && expires<cur) ? NO : YES;
561: }
562:
563: /* ------------------------------------------------------------------------- */
2.1 frystyk 564: /* CACHE WRITER STREAM */
565: /* ------------------------------------------------------------------------- */
566:
2.12 ! frystyk 567: PRIVATE int HTCache_flush (HTStream * me)
2.1 frystyk 568: {
569: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
570: }
571:
2.12 ! frystyk 572: PRIVATE int HTCache_putBlock (HTStream * me, CONST char * s, int l)
2.1 frystyk 573: {
574: int status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK;
575: if (l > 1 && status == HT_OK)
576: (void) HTCache_flush(me);
577: return status;
578: }
579:
2.12 ! frystyk 580: PRIVATE int HTCache_putChar (HTStream * me, char c)
2.1 frystyk 581: {
582: return HTCache_putBlock(me, &c, 1);
583: }
584:
2.12 ! frystyk 585: PRIVATE int HTCache_putString (HTStream * me, CONST char * s)
2.1 frystyk 586: {
587: return HTCache_putBlock(me, s, (int) strlen(s));
588: }
589:
2.12 ! frystyk 590: PRIVATE int HTCache_free (HTStream * me)
2.1 frystyk 591: {
2.7 frystyk 592: me->cache->load_delay = time(NULL) - me->cache->start_time;
2.1 frystyk 593: fclose(me->fp);
594: free(me);
595: return HT_OK;
596: }
597:
2.12 ! frystyk 598: PRIVATE int HTCache_abort (HTStream * me, HTList * e)
2.1 frystyk 599: {
600: if (CACHE_TRACE)
2.10 frystyk 601: TTYPrint(TDEST, "Cache....... ABORTING\n");
2.1 frystyk 602: if (me->fp)
603: fclose(me->fp);
604: if (me->cache)
605: HTCache_remove(me->cache);
606: free(me);
607: return HT_ERROR;
608: }
609:
610: PRIVATE CONST HTStreamClass HTCacheClass =
611: {
612: "Cache",
613: HTCache_flush,
614: HTCache_free,
615: HTCache_abort,
616: HTCache_putChar,
617: HTCache_putString,
618: HTCache_putBlock
619: };
620:
621:
622: /* Cache Writer
623: ** ------------------
624: **
625: */
2.12 ! frystyk 626: PUBLIC HTStream* HTCacheWriter (HTRequest * request,
! 627: void * param,
! 628: HTFormat input_format,
! 629: HTFormat output_format,
! 630: HTStream * output_stream)
2.1 frystyk 631: {
632: char *fnam;
633: HTStream *me;
2.12 ! frystyk 634: if (!HTCacheEnable) {
! 635: if (CACHE_TRACE) TTYPrint(TDEST, "Cache....... Not enabled\n");
2.1 frystyk 636: return HTBlackHole();
637: }
638:
639: /* Get a file name and open file */
640: if ((fnam = HTCache_getName(HTAnchor_physical(request->anchor))) == NULL)
641: return HTBlackHole();
642:
643: /* Set up the stream */
644: if ((me = (HTStream *) calloc(sizeof(*me), 1)) == NULL)
645: outofmem(__FILE__, "Cache");
646: me->isa = &HTCacheClass;
647: me->request = request;
2.5 frystyk 648: if ((me->fp = fopen(fnam, "wb")) == NULL) {
2.1 frystyk 649: if (CACHE_TRACE)
2.10 frystyk 650: TTYPrint(TDEST, "Cache....... Can't open %s for writing\n", fnam);
2.1 frystyk 651: free(fnam);
652: return HTBlackHole();
653: } else
654: if (CACHE_TRACE)
2.10 frystyk 655: TTYPrint(TDEST, "Cache....... Creating file %s\n", fnam);
2.1 frystyk 656:
657: /* Set up a cache record */
2.7 frystyk 658: if ((me->cache = (HTCache *) calloc(sizeof(*me->cache), 1)) == NULL)
2.1 frystyk 659: outofmem(__FILE__, "Cache");
660: me->cache->filename = fnam;
661: me->cache->start_time = time(NULL);
662: me->cache->format = input_format;
663:
664: /* Keep a global list of all cache items */
2.7 frystyk 665: if (!HTCacheList) HTCacheList = HTList_new();
666: HTList_addObject(HTCacheList, me->cache);
667: limit_cache(HTCacheList); /* Limit number (not size) of files */
2.1 frystyk 668: return me;
669: }
Webmaster