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

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

Webmaster