Annotation of libwww/Library/src/HTFile.c, revision 1.81

1.72      frystyk     1: /*                                                                    HTFile.c
                      2: **     FILE ACCESS
                      3: **
1.79      frystyk     4: **     (c) COPYRIGHT MIT 1995.
1.72      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
1.1       timbl       6: **
                      7: **     This is unix-specific code in general, with some VMS bits.
1.8       timbl       8: **     These are routines for file access used by browsers.
1.1       timbl       9: **
                     10: ** History:
                     11: **        Feb 91       Written Tim Berners-Lee CERN/CN
                     12: **        Apr 91       vms-vms access included using DECnet syntax
                     13: **     26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
                     14: **                     Fixed access bug for relative names on VMS.
1.28      duns       15: **        Sep 93 (MD)  Access to VMS files allows sharing.
                     16: **     15 Nov 93 (MD)  Moved HTVMSname to HTVMSUTILS.C
1.52      duns       17: **     22 Feb 94 (MD)  Excluded two routines if we are not READING directories
1.61      frystyk    18: **     18 May 94 (HF)  Directory stuff removed and stream handling updated,
                     19: **                     error messages introduced etc.
1.1       timbl      20: **
                     21: ** Bugs:
1.2       timbl      22: **     FTP: Cannot access VMS files from a unix machine.
                     23: **      How can we know that the
1.1       timbl      24: **     target machine runs VMS?
                     25: */
                     26: 
1.67      frystyk    27: /* Library Includes */
1.75      frystyk    28: #include "tcp.h"
1.67      frystyk    29: #include "HTUtils.h"
1.75      frystyk    30: #include "HTString.h"
1.1       timbl      31: #include "HTParse.h"
                     32: #include "HTTCP.h"
                     33: #include "HTAnchor.h"
1.2       timbl      34: #include "HTAtom.h"
                     35: #include "HTWriter.h"
1.75      frystyk    36: #include "HTFWrite.h"
1.52      duns       37: #include "HTFormat.h"
1.54      luotonen   38: #include "HTMulti.h"
1.78      frystyk    39: #include "HTDirBrw.h"
                     40: #include "HTBind.h"
1.80      frystyk    41: #include "HTSocket.h"
                     42: #include "HTThread.h"
1.61      frystyk    43: #include "HTError.h"
1.67      frystyk    44: #include "HTFile.h"            /* Implemented here */
                     45: 
1.80      frystyk    46: /* This is the local definition of HTRequest->net_info */
                     47: typedef enum _FileState {
                     48:     FILE_RETRY         = -4,
                     49:     FILE_ERROR         = -3,
                     50:     FILE_NO_DATA       = -2,
                     51:     FILE_GOT_DATA      = -1,
                     52:     FILE_BEGIN         = 0,
                     53:     FILE_NEED_OPEN_FILE,
                     54:     FILE_NEED_BODY,
                     55:     FILE_PARSE_DIR,
                     56:     FILE_TRY_FTP
                     57: } FileState;
                     58: 
1.77      frystyk    59: typedef struct _file_info {
                     60:     SOCKFD             sockfd;                         /* Socket descripter */
                     61:     SockA              sock_addr;              /* SockA is defined in tcp.h */
                     62:     HTInputSocket *    isoc;                                /* Input buffer */
                     63:     SocAction          action;                 /* Result of the select call */
                     64:     HTStream *         target;                             /* Target stream */
                     65:     int                addressCount;        /* Attempts if multi-homed host */
                     66:     time_t             connecttime;             /* Used on multihomed hosts */
                     67:     struct _HTRequest *        request;           /* Link back to request structure */
1.80      frystyk    68: 
                     69:     FileState          state;            /* Current state of the connection */
                     70:     char *             localname;      /* Local representation of file name */
                     71:     FILE *             fp;        /* If we can't use sockets on local files */
1.77      frystyk    72: } file_info;
1.2       timbl      73: 
1.80      frystyk    74: /* Local definition */
                     75: struct _HTStream {
                     76:     CONST HTStreamClass *      isa;
                     77:     HTStream *                 target;
                     78: };
                     79: 
1.77      frystyk    80: /* Controlling globals */
1.33      timbl      81: PUBLIC BOOL HTTakeBackup = YES;
1.1       timbl      82: 
                     83: PRIVATE char *HTMountRoot = "/Net/";           /* Where to find mounts */
1.2       timbl      84: 
1.61      frystyk    85: /* ------------------------------------------------------------------------- */
1.11      timbl      86: 
1.80      frystyk    87: /*     Convert file URLs into a local representation
                     88: **     ---------------------------------------------
                     89: **     The URL has already been translated through the rules in get_physical
                     90: **     in HTAccess.c and all we need to do now is to map the path to a local
                     91: **     representation, for example if must translate '/' to the ones that
                     92: **     turn the wrong way ;-)
                     93: **
                     94: ** On Entry
                     95: **     The full URL that's is going to be translated
                     96: ** On Exit,
                     97: **     -1      Error
                     98: **      0      URL is not pointing to the local file system
                     99: **      1      A local name is returned in `local' which must be freed
                    100: **             by caller
1.1       timbl     101: */
1.80      frystyk   102: PRIVATE int HTLocalName ARGS2(CONST char *, url, char **, local)
1.1       timbl     103: {
1.80      frystyk   104:     if (url) {
                    105:        char * access = HTParse(url, "", PARSE_ACCESS);
                    106:        char * host = HTParse(url, "", PARSE_HOST);
                    107:        char * path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
                    108:        CONST char *myhost;
                    109:        
                    110:        /* Find out if this is a reference to the local file system */
                    111:        if ((*access && strcmp(access, "file")) ||
                    112:            (*host && strcasecomp(host, "localhost") &&
                    113:             (myhost=HTGetHostName()) && strcmp(host, myhost))) {
                    114:            if (PROT_TRACE)
                    115:                fprintf(TDEST, "LocalName... Not on local file system\n");
                    116:            free(access);
                    117:            free(host);
                    118:            free(path);
                    119:            return 0;
                    120:        } else {
                    121:            /*
                    122:             ** Do whatever translation is required here in order to fit your
                    123:             ** platform _before_ the path is unescaped.
                    124:             */
1.65      duns      125: #if VMS
1.80      frystyk   126:            HTVMS_checkDecnet(path);
                    127: #endif
1.77      frystyk   128: #ifdef _WINDOWS
                    129:            /* an absolute pathname with logical drive */
                    130:            if (*path == '/' && path[2] == ':')    
                    131:                /* NB. need memmove because overlaps */
1.80      frystyk   132:                memmove( path, path+1, strlen(path) + 1);
1.77      frystyk   133: #endif
1.80      frystyk   134:            
                    135:            HTUnEscape(path);             /* Take out the escaped characters */
                    136:            if (PROT_TRACE)
                    137:                fprintf(TDEST, "Node........ `%s' means path `%s'\n",url,path);
                    138:            free(access);
1.1       timbl     139:            free(host);
1.80      frystyk   140:            *local = path;
                    141:            return 1;
1.1       timbl     142:        }
                    143:     }
1.80      frystyk   144:     return -1;
1.1       timbl     145: }
                    146: 
                    147: 
                    148: /*     Make a WWW name from a full local path name
                    149: **
                    150: ** Bugs:
                    151: **     At present, only the names of two network root nodes are hand-coded
                    152: **     in and valid for the NeXT only. This should be configurable in
                    153: **     the general case.
                    154: */
                    155: 
                    156: PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
                    157: {
                    158:     char * result;
                    159: #ifdef NeXT
                    160:     if (0==strncmp("/private/Net/", name, 13)) {
                    161:        result = (char *)malloc(7+strlen(name+13)+1);
                    162:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
                    163:        sprintf(result, "file://%s", name+13);
                    164:     } else
                    165: #endif
                    166:     if (0==strncmp(HTMountRoot, name, 5)) {
                    167:        result = (char *)malloc(7+strlen(name+5)+1);
                    168:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
                    169:        sprintf(result, "file://%s", name+5);
                    170:     } else {
1.63      frystyk   171:         result = (char *)malloc(7+strlen(HTGetHostName())+strlen(name)+1);
1.1       timbl     172:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
1.63      frystyk   173:        sprintf(result, "file://%s%s", HTGetHostName(), name);
1.1       timbl     174:     }
1.75      frystyk   175:     if (TRACE) fprintf(TDEST, "File `%s'\n\tmeans node `%s'\n", name, result);
1.1       timbl     176:     return result;
                    177: }
                    178: 
                    179: 
                    180: /*     Determine write access to a file
1.2       timbl     181: **     --------------------------------
1.80      frystyk   182: **     If stat_info is NULL then the function calls stat() on it's own,
                    183: **     otherwise it uses the information found in stat_info
1.2       timbl     184: ** On exit,
                    185: **     return value    YES if file can be accessed and can be written to.
                    186: **
                    187: ** Bugs:
                    188: **     1.      No code for non-unix systems.
                    189: **     2.      Isn't there a quicker way?
1.1       timbl     190: */
1.80      frystyk   191: PUBLIC BOOL HTEditable ARGS2(CONST char *, filename, struct stat *, stat_info)
                    192: {
1.75      frystyk   193: #ifndef NO_UNIX_IO
                    194: #ifdef NO_GROUPS
1.80      frystyk   195:     return NO;            /* Safe answer till we find the correct algorithm */
1.1       timbl     196: #else
1.75      frystyk   197: #ifdef NeXT
1.80      frystyk   198:     int groups[NGROUPS];
1.75      frystyk   199: #else
1.80      frystyk   200:     gid_t groups[NGROUPS];
1.75      frystyk   201: #endif 
1.80      frystyk   202:     int i;
                    203:     uid_t myUid;
                    204:     int        ngroups;                        /* The number of groups  */
1.1       timbl     205:     struct stat        fileStatus;
1.80      frystyk   206:     struct stat *fileptr = stat_info ? stat_info : &fileStatus;
1.1       timbl     207:         
1.80      frystyk   208:     if (!stat_info) {
                    209:        if (HTStat(filename, &fileStatus))
                    210:            return NO;                            /* Can't even access file! */
                    211:     }
1.1       timbl     212:     ngroups = getgroups(NGROUPS, groups);      /* Groups to which I belong  */
                    213:     myUid = geteuid();                         /* Get my user identifier */
                    214: 
                    215:     if (TRACE) {
                    216:         int i;
1.75      frystyk   217:        fprintf(TDEST, 
1.19      timbl     218:            "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
1.80      frystyk   219:            (unsigned int) fileptr->st_mode, (int) fileptr->st_uid,
                    220:            (int) fileptr->st_gid, (int) myUid, ngroups);
1.75      frystyk   221:        for (i=0; i<ngroups; i++) fprintf(TDEST, " %d", (int) groups[i]);
                    222:        fprintf(TDEST, ")\n");
1.1       timbl     223:     }
                    224:     
1.80      frystyk   225:     if (fileptr->st_mode & 0002)               /* I can write anyway? */
1.1       timbl     226:        return YES;
                    227:        
1.80      frystyk   228:     if ((fileptr->st_mode & 0200)              /* I can write my own file? */
                    229:      && (fileptr->st_uid == myUid))
1.1       timbl     230:        return YES;
                    231: 
1.80      frystyk   232:     if (fileptr->st_mode & 0020)               /* Group I am in can write? */
1.1       timbl     233:     {
                    234:        for (i=0; i<ngroups; i++) {
1.80      frystyk   235:             if (groups[i] == fileptr->st_gid)
1.1       timbl     236:                return YES;
                    237:        }
                    238:     }
1.75      frystyk   239:     if (TRACE) fprintf(TDEST, "\tFile is not editable.\n");
1.1       timbl     240:     return NO;                                 /* If no excuse, can't do */
                    241: #endif
1.80      frystyk   242: #else
                    243:     /*
                    244:     ** We don't know and can't find out. Can we be sure that when opening
                    245:     ** a file in mode "a" that the file is not modified?
                    246:     */
                    247:     return NO;
                    248: #endif /* !NO_UNIX_IO */
1.1       timbl     249: }
1.80      frystyk   250: 
1.1       timbl     251: 
1.2       timbl     252: /*     Make a save stream
                    253: **     ------------------
                    254: **
                    255: **     The stream must be used for writing back the file.
                    256: **     @@@ no backup done
                    257: */
1.29      timbl     258: PUBLIC HTStream * HTFileSaveStream ARGS1(HTRequest *, request)
1.2       timbl     259: {
                    260: 
1.29      timbl     261:     CONST char * addr = HTAnchor_address((HTAnchor*)request->anchor);
1.80      frystyk   262:     char *  filename;
1.33      timbl     263:     FILE* fp;
1.80      frystyk   264:     HTLocalName(addr, &filename);
1.33      timbl     265: 
                    266: /*     @ Introduce CVS handling here one day
                    267: */
                    268: /*     Take a backup before we do anything silly   931205
                    269: */        
                    270:     if (HTTakeBackup) {
1.69      frystyk   271:        char * p;
                    272:        char * backup_filename = (char *) malloc(strlen(filename)+2);
1.33      timbl     273:        if (!backup_filename) outofmem(__FILE__, "taking backup");
                    274:        strcpy(backup_filename, filename);
1.34      timbl     275:        for(p=backup_filename+strlen(backup_filename);; p--) {
1.33      timbl     276:            if ((*p=='/') || (p<backup_filename)) {
                    277:                p[1]=',';               /* Insert comma after slash */
                    278:                break;
                    279:            }
                    280:            p[1] = p[0];        /* Move up everything to the right of it */
                    281:        }
                    282:        
1.65      duns      283: 
                    284: #ifdef VMS
                    285:        if ((fp=fopen(filename, "r", "shr=put", "shr=upd"))) {  /* File exists */
                    286: #else /* not VMS */
1.35      luotonen  287:        if ((fp=fopen(filename, "r"))) {                /* File exists */
1.65      duns      288: #endif /* not VMS */
1.33      timbl     289:            fclose(fp);
1.75      frystyk   290:            if (TRACE) fprintf(TDEST, "File `%s' exists\n", filename);
1.77      frystyk   291:            if (REMOVE_FILE(backup_filename)) {
1.75      frystyk   292:                if (TRACE) fprintf(TDEST, "Backup file `%s' removed\n",
1.56      frystyk   293:                                   backup_filename);
1.33      timbl     294:            }
                    295:            if (rename(filename, backup_filename)) {    /* != 0 => Failure */
1.75      frystyk   296:                if (TRACE) fprintf(TDEST, "Rename `%s' to `%s' FAILED!\n",
1.56      frystyk   297:                                   filename, backup_filename);
1.33      timbl     298:            } else {                                    /* Success */
1.75      frystyk   299:                if (TRACE) fprintf(TDEST, "Renamed `%s' to `%s'\n", filename,
1.56      frystyk   300:                                   backup_filename);
1.33      timbl     301:            }
                    302:        }
                    303:        free(backup_filename);
                    304:     } /* if take backup */    
1.2       timbl     305:     
1.61      frystyk   306:     if ((fp = fopen(filename, "w")) == NULL) {
1.75      frystyk   307:        HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
1.61      frystyk   308:        return NULL;
                    309:     } else
1.40      frystyk   310:        return HTFWriter_new(fp, NO);
1.2       timbl     311: }
                    312: 
1.10      secret    313: 
1.80      frystyk   314: /*                                                                  FileCleanup
                    315: **
                    316: **      This function closes the connection and frees memory.
                    317: **
                    318: **      Returns 0 on OK, else -1
                    319: */
                    320: PRIVATE int FileCleanup ARGS2(HTRequest *, req, BOOL, abort)
                    321: {
                    322:     file_info *file;
                    323:     int status = 0;
                    324:     if (!req || !req->net_info) {
                    325:        if (PROT_TRACE) fprintf(TDEST, "FileCleanup. Bad argument!\n");
                    326:        status = -1;
                    327:     } else {
                    328:        file = (file_info *) req->net_info;
                    329:        if (file->sockfd != INVSOC) {
                    330: 
                    331:            /* Free stream with data FROM local file system */
                    332:            if (file->target) {
                    333:                if (abort)
                    334:                    (*file->target->isa->abort)(file->target, NULL);
                    335:                else
                    336:                    (*file->target->isa->_free)(file->target);
                    337:            }
                    338: 
                    339:            if (PROT_TRACE)
                    340:                fprintf(TDEST,"FILE........ Closing socket %d\n",file->sockfd);
                    341:            if ((status = NETCLOSE(file->sockfd)) < 0)
                    342:                HTErrorSysAdd(file->request, ERR_FATAL, socerrno, NO,
                    343:                              "NETCLOSE");
                    344:            HTThreadState(file->sockfd, THD_CLOSE);
                    345:            file->sockfd = INVSOC;
                    346:            HTThread_clear((HTNetInfo *) file);
                    347:        }
                    348:        if (file->isoc)
                    349:            HTInputSocket_free(file->isoc);
                    350:        if (file->fp)
                    351:            fclose(file->fp);
                    352:        FREE(file->localname);
                    353:        free(file);
                    354:        req->net_info = NULL;
                    355:     }
                    356:     return status;
                    357: }
                    358: 
                    359: 
1.1       timbl     360: /*     Load a document
                    361: **     ---------------
                    362: **
                    363: ** On entry,
1.80      frystyk   364: **     request         This is the request structure
1.1       timbl     365: ** On exit,
1.77      frystyk   366: **     returns         HT_ERROR        Error has occured or interrupted
                    367: **                     HT_WOULD_BLOCK  We are using blocking I/O so this
                    368: **                                     indicates that we have a read a chunk
                    369: **                                     of data and now want to see what's up
                    370: **                                     in the eventloop.
1.80      frystyk   371: **                     HT_LOADED       if file has been loaded successfully
                    372: **                     HT_NO_DATA      if file is empty
                    373: **                     HT_RETRY        if file service is unavailable
1.1       timbl     374: */
1.36      luotonen  375: PUBLIC int HTLoadFile ARGS1 (HTRequest *, request)
1.1       timbl     376: {
1.80      frystyk   377:     int status = HT_ERROR;
                    378:     file_info *file;                         /* Specific access information */
1.45      luotonen  379: 
1.61      frystyk   380:     if (!request || !request->anchor) {
1.75      frystyk   381:        if (TRACE) fprintf(TDEST, "HTLoadFile.. Called with bad argument\n");
1.77      frystyk   382:        return HT_ERROR;
1.50      frystyk   383:     }
1.1       timbl     384: 
1.80      frystyk   385:     /* Only do the setup first time through. This is actually state FILE_BEGIN
                    386:        but it can't be in the state machine as we need the structure first */
                    387:     if (!request->net_info) {    
                    388:        if (PROT_TRACE) {
                    389:            char *url = HTAnchor_physical(request->anchor);
                    390:            fprintf(TDEST, "LoadFile.... Looking for `%s\'\n", url);
                    391:        }
                    392:        if ((file = (file_info *) calloc(1, sizeof(file_info))) == NULL)
                    393:            outofmem(__FILE__, "HTLoadFILE");
                    394:        file->sockfd = INVSOC;                      /* Invalid socket number */
                    395:        file->request = request;
                    396:        request->net_info = (HTNetInfo *) file;
                    397:        file->state = FILE_BEGIN;
                    398:        HTThread_new((HTNetInfo *) file);
                    399:     } else {
                    400:        file = (file_info *) request->net_info;         /* Get existing copy */
1.78      frystyk   401: 
1.80      frystyk   402:        /* @@@ NEED ALSO TO CHECK FOR ANSI FILE DESCRIPTORS @@@ */
                    403:        if (file->sockfd != INVSOC && HTThreadIntr(file->sockfd))
                    404:            file->state = FILE_ERROR;
                    405:     }
1.36      luotonen  406: 
1.80      frystyk   407:     /* Now jump into the machine. We know the state from the previous run */
                    408:     while (1) {
                    409:        switch (file->state) {
                    410:          case FILE_BEGIN:
                    411:            if (HTSecure) {
                    412:                if (PROT_TRACE)
                    413:                    fprintf(TDEST, "LoadFile.... No access to local file system\n");
                    414:                file->state = FILE_TRY_FTP;
                    415:                break;
                    416:            }
                    417:            if ((status = HTLocalName(HTAnchor_physical(request->anchor),
                    418:                                      &file->localname)) == -1) {
                    419:                file->state = FILE_ERROR;
                    420:                break;
                    421:            } else if (status == 0) {
                    422:                file->state = FILE_TRY_FTP;
                    423:                break;
1.2       timbl     424:            }
1.80      frystyk   425:            /*
                    426:            ** If we have to do content negotiation then find the object that
                    427:            ** fits best into either what the client has indicated in the
                    428:            ** accept headers or what the client has registered on it own.
                    429:            ** The object chosen can in fact be a directory! However, content
                    430:            ** negotiation only makes sense it we can read the directory!
                    431:            ** We stat the file in order to find the size and to see it if
                    432:            ** exists.
                    433:            */
                    434:            {
                    435:                struct stat stat_info;        /* Contains actual file chosen */
                    436:                if (request->ContentNegotiation) {
                    437:                    char *new_path=HTMulti(request,file->localname,&stat_info);
                    438:                    if (new_path) {
                    439:                        FREE(file->localname);
                    440:                        file->localname = new_path;
                    441:                        HTAnchor_setPhysical(request->anchor, new_path);
                    442:                    } else {
                    443:                        file->state = FILE_ERROR;
                    444:                        break;
                    445:                    }
                    446:                } else {
                    447:                    if (HTStat(file->localname, &stat_info) == -1) {
                    448:                        if (PROT_TRACE)
                    449:                            fprintf(TDEST, "HTLoadFile.. Can't stat %s\n",
                    450:                                    file->localname);
1.81    ! frystyk   451:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
        !           452:                                   NULL, 0, "HTLoadFile");
1.80      frystyk   453:                        file->state = FILE_ERROR;
                    454:                        break;
                    455:                    }
                    456:                }
                    457:                /* Check to see if the 'localname' is in fact a directory */
                    458:                if (((stat_info.st_mode) & S_IFMT) == S_IFDIR)
                    459:                    file->state = FILE_PARSE_DIR;
                    460:                else {
                    461:                    /*
                    462:                    ** If empty file then only serve it if it is editable
                    463:                    */
                    464:                    BOOL editable = HTEditable(file->localname, &stat_info);
                    465:                    HTBind_getBindings(request->anchor);
                    466:                    if (editable)
                    467:                        HTAnchor_appendMethods(request->anchor, METHOD_PUT);
                    468:                    if (stat_info.st_size)
                    469:                        HTAnchor_setLength(request->anchor, stat_info.st_size);
                    470: 
                    471:                    /* Put all relevant metainformation into the anchor */
                    472:                    request->anchor->header_parsed = YES;
                    473: 
                    474:                    if (!editable && !stat_info.st_size) {
1.81    ! frystyk   475:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_NO_CONTENT,
1.80      frystyk   476:                                   NULL, 0, "HTLoadFile");
                    477:                        file->state = FILE_NO_DATA;
                    478:                    } else
                    479:                        file->state = FILE_NEED_OPEN_FILE;
                    480:                }
1.2       timbl     481:            }
1.80      frystyk   482:            break;
1.61      frystyk   483: 
1.80      frystyk   484:          case FILE_NEED_OPEN_FILE:
                    485:            /*
                    486:            ** If we have unix file descriptors then use this otherwise use
                    487:            ** the ANSI C file descriptors
                    488:            */
                    489: #ifndef NO_UNIX_IO
                    490:            if ((file->sockfd = open(file->localname, O_RDONLY)) == -1) {
                    491:                HTErrorSysAdd(request, ERR_FATAL, errno, NO, "open");
                    492:                file->state = FILE_ERROR;
                    493:            } else {
                    494:                if (PROT_TRACE)
                    495:                    fprintf(TDEST,"HTLoadFile.. `%s' opened using fd %d \n",
                    496:                            file->localname, file->sockfd);
                    497: 
                    498:                /* If non-blocking protocol then change socket status
                    499:                ** I use FCNTL so that I can ask the status before I set it.
                    500:                ** See W. Richard Stevens (Advan. Prog. in UNIX env, p.364)
                    501:                ** Be CAREFULL with the old `O_NDELAY' - it wont work as read()
                    502:                ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD
                    503:                ** and does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
                    504:                */
                    505:                if (!HTProtocolBlocking(request)) {
                    506:                    if ((status = FCNTL(file->sockfd, F_GETFL, 0)) != -1) {
                    507:                        status |= O_NONBLOCK;                       /* POSIX */
                    508:                        status = FCNTL(file->sockfd, F_SETFL, status);
                    509:                    }
                    510:                    if (PROT_TRACE) {
                    511:                        if (status == -1)
                    512:                            fprintf(TDEST, "HTLoadFile.. Can't make socket non-blocking\n");
                    513:                        else
                    514:                            fprintf(TDEST, "HTLoadFile.. Using NON_BLOCKING I/O\n");
                    515:                    }
                    516:                }
                    517:            
                    518:                /* Set up read buffer and streams */
                    519:                file->isoc = HTInputSocket_new(file->sockfd);
                    520:                file->target = HTStreamStack(HTAnchor_format(request->anchor),
                    521:                                             request->output_format,
                    522:                                             request->output_stream,
                    523:                                             request, YES);
                    524:                HTThreadState(file->sockfd, THD_SET_READ);
                    525:                file->state = file->target ? FILE_NEED_BODY : FILE_ERROR;
1.61      frystyk   526:            }
1.80      frystyk   527: #else
1.65      duns      528: #ifdef VMS
1.80      frystyk   529:            if ((file->fp = fopen(file->localname,"r","shr=put","shr=upd")) == NULL) {
                    530: #else
                    531:            if ((file->fp = fopen(file->localname,"r")) == NULL){
1.65      duns      532: #endif /* not VMS */
1.80      frystyk   533:                HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
                    534:                file->state = FILE_ERROR;
                    535:            } else {
                    536:                if (PROT_TRACE)
                    537:                    fprintf(TDEST,"HTLoadFile.. Open `%s'\n",file->localname);
                    538:                file->state = FILE_NEED_BODY;
                    539:            }
                    540: #endif /* !NO_UNIX_IO */
                    541:            
                    542:            break;
1.65      duns      543: 
1.80      frystyk   544:          case FILE_NEED_BODY:
                    545: #ifndef NO_UNIX_IO
                    546:            status = HTSocketRead(request, file->target);
                    547:            if (status == HT_WOULD_BLOCK)
                    548:                return HT_WOULD_BLOCK;
                    549:            else if (status == HT_INTERRUPTED)
                    550:                file->state = FILE_ERROR;
                    551:            else if (status == HT_LOADED) {
                    552:                file->state = FILE_GOT_DATA;
1.61      frystyk   553:            } else
1.80      frystyk   554:                file->state = FILE_ERROR;
                    555: #else
                    556:            if (HTParseFile(HTAnchor_format(request->anchor), file->fp,
                    557:                            request) < 0)
                    558:                file->state = FILE_ERROR;
                    559:            else
                    560:                file->state = FILE_GOT_DATA;
                    561: #endif
                    562:            break;
1.1       timbl     563: 
1.80      frystyk   564:          case FILE_PARSE_DIR:
                    565: #ifdef GOT_READ_DIR
                    566:            file->state = HTBrowseDirectory(request, file->localname) < 0 ?
                    567:                FILE_ERROR : FILE_GOT_DATA;
                    568: #else
                    569:            file->state = FILE_ERROR;
1.1       timbl     570: #endif
1.80      frystyk   571:            break;
                    572: 
                    573:          case FILE_TRY_FTP:
                    574:            {
                    575:                char *url = HTAnchor_physical(request->anchor);
                    576:                HTAnchor *anchor;
                    577:                char *newname = NULL;
                    578:                StrAllocCopy(newname, "ftp:");
                    579:                if (!strncmp(url, "file:", 5))
                    580:                    StrAllocCat(newname, url+5);
                    581:                else
                    582:                    StrAllocCat(newname, url);
                    583:                anchor = HTAnchor_findAddress(newname);
                    584:                free(newname);
                    585:                FileCleanup(request, NO);
                    586:                return HTLoadAnchorRecursive(anchor, request);
                    587:            }
                    588:            break;
1.1       timbl     589: 
1.80      frystyk   590:          case FILE_GOT_DATA:
                    591:            FileCleanup(request, NO);
                    592:            return HT_LOADED;
                    593:            break;
                    594: 
                    595:          case FILE_NO_DATA:
                    596:            FileCleanup(request, NO);
                    597:            return HT_NO_DATA;
                    598:            break;
                    599: 
                    600:          case FILE_RETRY:
                    601:            FileCleanup(request, YES);
                    602:            return HT_RETRY;
                    603:            break;
                    604: 
                    605:          case FILE_ERROR:
                    606:            FileCleanup(request, YES);
                    607:            return HT_ERROR;
                    608:            break;
                    609:        }
                    610:     } /* End of while(1) */
1.1       timbl     611: }
                    612: 
1.80      frystyk   613: 
                    614: /*
                    615: **  Protocol descriptor
                    616: **  Make this non-blocking is possible
                    617: */
                    618: #ifndef NO_UNIX_IO
                    619: GLOBALDEF PUBLIC HTProtocol HTFile = {
                    620:     "file", SOC_NON_BLOCK, HTLoadFile, HTFileSaveStream, NULL
                    621: };
                    622: #else
1.71      frystyk   623: GLOBALDEF PUBLIC HTProtocol HTFile = {
                    624:     "file", SOC_BLOCK, HTLoadFile, HTFileSaveStream, NULL
                    625: };
1.80      frystyk   626: #endif

Webmaster