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