Annotation of libwww/Library/src/HTFWrite.c, revision 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);
! 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: #ifdef GOT_READ_DIR
! 861:
! 862: /* Cache Writer
! 863: ** ------------------
! 864: **
! 865: */
! 866: PUBLIC HTStream* HTCacheWriter ARGS5(
! 867: HTRequest *, request,
! 868: void *, param,
! 869: HTFormat, input_format,
! 870: HTFormat, output_format,
! 871: HTStream *, output_stream)
! 872:
! 873: {
! 874: char *fnam;
! 875: HTStream* me;
! 876:
! 877: if (HTClientHost) {
! 878: if (TRACE) fprintf(TDEST, "Only caching if WWW is run locally.\n");
! 879: return HTBlackHole();
! 880: }
! 881: me = (HTStream*)calloc(sizeof(*me),1);
! 882: if (me == NULL) outofmem(__FILE__, "CacheWriter");
! 883: me->isa = &HTFWriter;
! 884: me->end_command = NULL;
! 885: me->remove_on_close = NO; /* If needed, put into end_command */
! 886: me->announce = NO;
! 887:
! 888: /* Save the file under a suitably suffixed name */
! 889: fnam = HTFWriter_filename(HTCacheDir,
! 890: HTAnchor_physical(request->anchor),
! 891: HTFileSuffix(input_format),
! 892: 0, NO);
! 893: if (!fnam) { /* HWL 22/9/94 */
! 894: free(me);
! 895: return NULL;
! 896: }
! 897:
! 898: me->filename = NULL;
! 899: limit_cache(HTCache); /* Limit number (not size) of files */
! 900: me->fp = fopen (fnam, "w");
! 901: if (!me->fp) {
! 902: HTAlert("Can't open local file for writing.");
! 903: if (TRACE) fprintf(TDEST, "HTStream: can't open %s for writing\n",fnam);
! 904: free(fnam);
! 905: free(me);
! 906: return NULL;
! 907: }
! 908:
! 909: /* Set up a cache record */
! 910:
! 911: if (TRACE) fprintf(TDEST, "Cache....... Creating file %s\n", fnam);
! 912: me->cache = (HTCacheItem*)calloc(sizeof(*me->cache),1);
! 913: if (!me->cache)outofmem(__FILE__, "cache");
! 914: time(&me->cache->load_time);
! 915: StrAllocCopy(me->cache->filename, fnam);
! 916: me->cache->anchor = request->anchor;
! 917: if (!request->anchor->cacheItems)
! 918: request->anchor->cacheItems = HTList_new();
! 919: HTList_addObject(request->anchor->cacheItems, me->cache);
! 920: me->cache->format = input_format;
! 921:
! 922: if (!HTCache) HTCache = HTList_new();
! 923: HTList_addObject(HTCache, me->cache);
! 924:
! 925: me->callback = request->callback;
! 926: me->request = request; /* won't be freed */
! 927: me->filename = fnam; /* will be freed */
! 928: return me;
! 929: }
! 930:
! 931: #endif /* GOT_READ_DIR */
! 932:
! 933:
! 934: /* Save and Call Back
! 935: ** ------------------
! 936: **
! 937: **
! 938: ** The special case is a kludge. Better is everything uses streams
! 939: ** and nothing uses files. Then this routine will go too. :-))
! 940: */
! 941:
! 942:
! 943: PUBLIC HTStream* HTSaveAndCallBack ARGS5(
! 944: HTRequest *, request,
! 945: void *, param,
! 946: HTFormat, input_format,
! 947: HTFormat, output_format,
! 948: HTStream *, output_stream)
! 949: {
! 950: HTStream * me;
! 951:
! 952: if (request->using_cache) { /* Special case! file wanted && cache hit */
! 953: (*request->callback)(request,
! 954: ((HTCacheItem*)request->using_cache)->filename);
! 955: return &HTBlackHoleInstance; /* @@@@@@@@@@@@@@ */
! 956: } else {
! 957: me = HTCacheWriter(request, param,
! 958: input_format, output_format, output_stream);
! 959: if (me) {
! 960: me->callback = request->callback;
! 961: }
! 962: }
! 963: return me;
! 964: }
! 965:
! 966:
Webmaster