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