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