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

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

Webmaster