Annotation of libwww/Library/src/HTFWrite.c, revision 2.2.2.4

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

Webmaster