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