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

2.1     ! frystyk     1: /*                                                                  HTSocket.c
        !             2: **     MANAGES READ AND WRITE TO AND FROM THE NETWORK
        !             3: **
        !             4: **     (c) COPYRIGHT MIT 1995.
        !             5: **     Please first read the full copyright statement in the file COPYRIGH.
        !             6: **
        !             7: **
        !             8: ** HISTORY:
        !             9: **     6 June 95  HFN  Spawned off from HTFormat
        !            10: */
        !            11: 
        !            12: /* Library Include files */
        !            13: #include "tcp.h"
        !            14: #include "HTUtils.h"
        !            15: #include "HTString.h"
        !            16: #include "HTTCP.h"
        !            17: #include "HTStream.h"
        !            18: #include "HTFormat.h"
        !            19: #include "HTThread.h"
        !            20: #include "HTError.h"
        !            21: #include "HTSocket.h"                                   /* Implemented here */
        !            22: 
        !            23: struct _HTStream {
        !            24:     CONST HTStreamClass *      isa;
        !            25: };
        !            26: 
        !            27: /* ------------------------------------------------------------------------- */
        !            28: /*                             SOCKET INPUT BUFFERING                       */
        !            29: /* ------------------------------------------------------------------------- */
        !            30: /*                     
        !            31: **     This code is used because one cannot in general open a
        !            32: **     file descriptor for a socket.
        !            33: **
        !            34: **     The input file is read using the macro which can read from
        !            35: **     a socket or a file, but this should not be used for files
        !            36: **     as fopen() etc is more portable of course.
        !            37: **
        !            38: **     The input buffer size, if large will give greater efficiency and
        !            39: **     release the server faster, and if small will save space on PCs etc.
        !            40: */
        !            41: 
        !            42: 
        !            43: /*     Set up the buffering
        !            44: **
        !            45: **     These routines are public because they are in fact needed by
        !            46: **     many parsers, and on PCs and Macs we should not duplicate
        !            47: **     the static buffer area.
        !            48: */
        !            49: PUBLIC HTInputSocket * HTInputSocket_new ARGS1 (SOCKFD, file_number)
        !            50: {
        !            51:     HTInputSocket *isoc = (HTInputSocket *)calloc(1, sizeof(*isoc));
        !            52:     if (!isoc) outofmem(__FILE__, "HTInputSocket_new");
        !            53:     isoc->input_file_number = file_number;
        !            54:     isoc->input_pointer = isoc->input_limit = isoc->input_buffer;
        !            55:     return isoc;
        !            56: }
        !            57: 
        !            58: /* This should return HT_INTERRUPTED if interrupted BUT the connection
        !            59:    MUST not be closed */ 
        !            60: PUBLIC int HTInputSocket_getCharacter ARGS1(HTInputSocket*, isoc)
        !            61: {
        !            62:     int ch;
        !            63:     do {
        !            64:        if (isoc->input_pointer >= isoc->input_limit) {
        !            65:            int status = NETREAD(isoc->input_file_number,
        !            66:                                 isoc->input_buffer, INPUT_BUFFER_SIZE);
        !            67:            if (status <= 0) {
        !            68:                if (status == 0)
        !            69:                    return EOF;
        !            70:                if (status == HT_INTERRUPTED) {
        !            71:                    if (TRACE)
        !            72:                        fprintf(TDEST, "Get Char.... Interrupted in HTInputSocket_getCharacter\n");
        !            73:                    return HT_INTERRUPTED;
        !            74:                }
        !            75:                if (PROT_TRACE)
        !            76:                    fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
        !            77:                return EOF;     /* -1 is returned by UCX at end of HTTP link */
        !            78:            }
        !            79:            isoc->input_pointer = isoc->input_buffer;
        !            80:            isoc->input_limit = isoc->input_buffer + status;
        !            81:        }
        !            82:        ch = (unsigned char) *isoc->input_pointer++;
        !            83:     } while (ch == 13);                             /* Ignore ASCII carriage return */
        !            84:     
        !            85:     return FROMASCII(ch);
        !            86: }
        !            87: 
        !            88: PUBLIC void HTInputSocket_free ARGS1(HTInputSocket *, me)
        !            89: {
        !            90:     if (me) free(me);
        !            91: }
        !            92: 
        !            93: 
        !            94: PUBLIC char * HTInputSocket_getBlock ARGS2(HTInputSocket*,     isoc,
        !            95:                                           int *,               len)
        !            96: {
        !            97:     if (isoc->input_pointer >= isoc->input_limit) {
        !            98:        int status = NETREAD(isoc->input_file_number,
        !            99:                             isoc->input_buffer,
        !           100:                             ((*len < INPUT_BUFFER_SIZE) ?
        !           101:                              *len : INPUT_BUFFER_SIZE));
        !           102:        if (status <= 0) {
        !           103:            isoc->input_limit = isoc->input_buffer;
        !           104:            if (status < 0) {
        !           105:                if (PROT_TRACE)
        !           106:                    fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
        !           107:            }
        !           108:            *len = 0;
        !           109:            return NULL;
        !           110:        }
        !           111:        else {
        !           112:            *len = status;
        !           113:            return isoc->input_buffer;
        !           114:        }
        !           115:     }
        !           116:     else {
        !           117:        char * ret = isoc->input_pointer;
        !           118:        *len = isoc->input_limit - isoc->input_pointer;
        !           119:        isoc->input_pointer = isoc->input_limit;
        !           120:        return ret;
        !           121:     }
        !           122: }
        !           123: 
        !           124: 
        !           125: PRIVATE int fill_in_buffer ARGS1(HTInputSocket *, isoc)
        !           126: {
        !           127:     if (isoc) {
        !           128:        int status;
        !           129: 
        !           130:        isoc->input_pointer = isoc->input_buffer;
        !           131:        status = NETREAD(isoc->input_file_number,
        !           132:                         isoc->input_buffer,
        !           133:                         INPUT_BUFFER_SIZE);
        !           134:        if (status <= 0) {
        !           135:            isoc->input_limit = isoc->input_buffer;
        !           136:            if (status < 0) {
        !           137:                if (PROT_TRACE)
        !           138:                    fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
        !           139:            }
        !           140:        }
        !           141:        else 
        !           142:            isoc->input_limit = isoc->input_buffer + status;
        !           143:        return status;
        !           144:     }
        !           145:     return -1;
        !           146: }
        !           147: 
        !           148: 
        !           149: PRIVATE void ascii_cat ARGS3(char **,  linep,
        !           150:                             char *,    start,
        !           151:                             char *,    end)
        !           152: {
        !           153:     if (linep && start && end && start <= end) {
        !           154:        char *ptr;
        !           155: 
        !           156:        if (*linep) {
        !           157:            int len = strlen(*linep);
        !           158:            *linep = (char*)realloc(*linep, len + end-start + 1);
        !           159:            ptr = *linep + len;
        !           160:        }
        !           161:        else {
        !           162:            ptr = *linep = (char*)malloc(end-start + 1);
        !           163:        }
        !           164: 
        !           165:        while (start < end) {
        !           166:            *ptr = FROMASCII(*start);
        !           167:            ptr++;
        !           168:            start++;
        !           169:        }
        !           170:        *ptr = 0;
        !           171:     }
        !           172: }
        !           173: 
        !           174: 
        !           175: PRIVATE char * get_some_line ARGS2(HTInputSocket *,    isoc,
        !           176:                                   BOOL,                unfold)
        !           177: {
        !           178:     if (!isoc)
        !           179:        return NULL;
        !           180:     else {
        !           181:        BOOL check_unfold = NO;
        !           182:        int prev_cr = 0;
        !           183:        char *start = isoc->input_pointer;
        !           184:        char *cur = isoc->input_pointer;
        !           185:        char * line = NULL;
        !           186: 
        !           187:        for(;;) {
        !           188:            /*
        !           189:            ** Get more if needed to complete line
        !           190:            */
        !           191:            if (cur >= isoc->input_limit) { /* Need more data */
        !           192:                ascii_cat(&line, start, cur);
        !           193:                if (fill_in_buffer(isoc) <= 0)
        !           194:                    return line;
        !           195:                start = cur = isoc->input_pointer;
        !           196:            } /* if need more data */
        !           197: 
        !           198:            /*
        !           199:            ** Find a line feed if there is one
        !           200:            */
        !           201:            for(; cur < isoc->input_limit; cur++) {
        !           202:                char c = FROMASCII(*cur);
        !           203:                if (!c) {
        !           204:                    if (line) free(line);       /* Leak fixed AL 6 Feb 94 */
        !           205:                    return NULL;        /* Panic! read a 0! */
        !           206:                }
        !           207:                if (check_unfold  &&  c != ' '  &&  c != '\t') {
        !           208:                    return line;  /* Note: didn't update isoc->input_pointer */
        !           209:                }
        !           210:                else {
        !           211:                    check_unfold = NO;
        !           212:                }
        !           213: 
        !           214:                if (c=='\r') {
        !           215:                    prev_cr = 1;
        !           216:                }
        !           217:                else {
        !           218:                    if (c=='\n') {              /* Found a line feed */
        !           219:                        ascii_cat(&line, start, cur-prev_cr);
        !           220:                        start = isoc->input_pointer = cur+1;
        !           221: 
        !           222:                        if (line && (int) strlen(line) > 0 && unfold) {
        !           223:                            check_unfold = YES;
        !           224:                        }
        !           225:                        else {
        !           226:                            return line;
        !           227:                        }
        !           228:                    } /* if NL */
        !           229:                    /* else just a regular character */
        !           230:                    prev_cr = 0;
        !           231:                } /* if not CR */
        !           232:            } /* while characters in buffer remain */
        !           233:        } /* until line read or end-of-file */
        !           234:     } /* valid parameters to function */
        !           235: }
        !           236: 
        !           237: /* The returned string must be freed by the caller */
        !           238: PUBLIC char * HTInputSocket_getLine ARGS1(HTInputSocket *, isoc)
        !           239: {
        !           240:     return get_some_line(isoc, NO);
        !           241: }
        !           242: 
        !           243: /* The returned string must be freed by the caller */
        !           244: PUBLIC char * HTInputSocket_getUnfoldedLine ARGS1(HTInputSocket *, isoc)
        !           245: {
        !           246:     return get_some_line(isoc, YES);
        !           247: }
        !           248: 
        !           249: 
        !           250: /*     Push data from a socket down a stream
        !           251: **     -------------------------------------
        !           252: **
        !           253: **   This routine is responsible for creating and PRESENTING any
        !           254: **   graphic (or other) objects described by the file.
        !           255: **
        !           256: **   The file number given is assumed to be a TELNET stream ie containing
        !           257: **   CRLF at the end of lines which need to be stripped to LF for unix
        !           258: **   when the format is textual.
        !           259: **
        !           260: **   RETURNS the number of bytes transferred.
        !           261: **
        !           262: */
        !           263: PUBLIC int HTCopy ARGS2(
        !           264:        SOCKFD,                 file_number,
        !           265:        HTStream*,              sink)
        !           266: {
        !           267:     HTStreamClass targetClass;    
        !           268:     HTInputSocket * isoc;
        !           269:     int cnt = 0;
        !           270: 
        !           271: /*     Push the data down the stream
        !           272: **
        !           273: */
        !           274:     targetClass = *(sink->isa);        /* Copy pointers to procedures */
        !           275:     isoc = HTInputSocket_new(file_number);
        !           276:     
        !           277:     /* Push binary from socket down sink
        !           278:     **
        !           279:     **         This operation could be put into a main event loop
        !           280:     */
        !           281:     for(;;) {
        !           282:        int status = NETREAD(
        !           283:                file_number, isoc->input_buffer, INPUT_BUFFER_SIZE);
        !           284:        if (status <= 0) {
        !           285:            if (status == 0) break;
        !           286:            if (TRACE) fprintf(TDEST,
        !           287:                "Socket Copy. Read error, read returns %d with errno=%d\n",
        !           288:                status, socerrno);
        !           289:            break;
        !           290:        }
        !           291: 
        !           292: #ifdef NOT_ASCII
        !           293:        {
        !           294:            char * p;
        !           295:            for(p = isoc->input_buffer; p < isoc->input_buffer+status; p++) {
        !           296:                *p = FROMASCII(*p);
        !           297:            }
        !           298:        }
        !           299: #endif
        !           300: 
        !           301:        (*targetClass.put_block)(sink, isoc->input_buffer, status);
        !           302:        cnt += status;
        !           303:     } /* next bufferload */
        !           304: 
        !           305:     HTInputSocket_free(isoc);
        !           306: 
        !           307:     return cnt;
        !           308: }
        !           309: 
        !           310: 
        !           311: 
        !           312: /*     Push data from a file pointer down a stream
        !           313: **     -------------------------------------
        !           314: **
        !           315: **   This routine is responsible for creating and PRESENTING any
        !           316: **   graphic (or other) objects described by the file.
        !           317: **
        !           318: **
        !           319: */
        !           320: PUBLIC void HTFileCopy ARGS2(
        !           321:        FILE *,                 fp,
        !           322:        HTStream*,              sink)
        !           323: {
        !           324:     HTStreamClass targetClass;    
        !           325:     char input_buffer[INPUT_BUFFER_SIZE];
        !           326:     
        !           327: /*     Push the data down the stream
        !           328: **
        !           329: */
        !           330:     targetClass = *(sink->isa);        /* Copy pointers to procedures */
        !           331:     
        !           332:     /* Push binary from socket down sink
        !           333:     */
        !           334:     for(;;) {
        !           335:        int status = fread(
        !           336:               input_buffer, 1, INPUT_BUFFER_SIZE, fp);
        !           337:        if (status == 0) { /* EOF or error */
        !           338:            if (ferror(fp) == 0) break;
        !           339:            if (TRACE) fprintf(TDEST,
        !           340:                "File Copy... Read error, read returns %d\n", ferror(fp));
        !           341:            break;
        !           342:        }
        !           343:        (*targetClass.put_block)(sink, input_buffer, status);
        !           344:     } /* next bufferload */    
        !           345: }
        !           346: 
        !           347: 
        !           348: 
        !           349: 
        !           350: /*     Push data from a socket down a stream STRIPPING CR
        !           351: **     --------------------------------------------------
        !           352: **
        !           353: **   This routine is responsible for creating and PRESENTING any
        !           354: **   graphic (or other) objects described by the socket.
        !           355: **
        !           356: **   The file number given is assumed to be a TELNET stream ie containing
        !           357: **   CRLF at the end of lines which need to be stripped to LF for unix
        !           358: **   when the format is textual.
        !           359: **     
        !           360: **     Character handling is now of type int, Henrik, May 09-94
        !           361: */
        !           362: PUBLIC void HTCopyNoCR ARGS2(
        !           363:        SOCKFD,                 file_number,
        !           364:        HTStream*,              sink)
        !           365: {
        !           366:     HTStreamClass targetClass;
        !           367:     HTInputSocket * isoc;   
        !           368:     int ch;
        !           369:     
        !           370: /*     Push the data, ignoring CRLF, down the stream
        !           371: **
        !           372: */
        !           373:     targetClass = *(sink->isa);        /* Copy pointers to procedures */
        !           374: 
        !           375: /*     Push text from telnet socket down sink
        !           376: **
        !           377: **     @@@@@ To push strings could be faster? (especially is we
        !           378: **     cheat and don't ignore CR! :-}
        !           379: */  
        !           380:     isoc = HTInputSocket_new(file_number);
        !           381:     while ((ch = HTInputSocket_getCharacter(isoc)) >= 0)
        !           382:        (*targetClass.put_character)(sink, ch);
        !           383:     HTInputSocket_free(isoc);
        !           384: }
        !           385: 
        !           386: 
        !           387: /*     Parse a socket given format and file number
        !           388: **
        !           389: **   This routine is responsible for creating and PRESENTING any
        !           390: **   graphic (or other) objects described by the file.
        !           391: **
        !           392: **   The file number given is assumed to be a TELNET stream ie containing
        !           393: **   CRLF at the end of lines which need to be stripped to LF for unix
        !           394: **   when the format is textual.
        !           395: **
        !           396: **     Returns <0 on error, HT_LOADED on success.
        !           397: */
        !           398: 
        !           399: /* The parameter to this function and HTParsefile should be HTRequest */
        !           400: 
        !           401: PUBLIC int HTParseSocket ARGS3(
        !           402:        HTFormat,               rep_in,
        !           403:        SOCKFD,                 file_number,
        !           404:        HTRequest *,            request)
        !           405: {
        !           406:     HTStream * stream;
        !           407:     HTStreamClass targetClass;    
        !           408: 
        !           409:     if (request->error_stack) {
        !           410:        if (TRACE) fprintf(TDEST, "ParseSocket. Called whith non-empty error stack, so I return right away!\n");
        !           411:        return -1;
        !           412:     }
        !           413: 
        !           414:     /* Set up stream stack */
        !           415:     if ((stream = HTStreamStack(rep_in, request->output_format,
        !           416:                                request->output_stream,
        !           417:                                request, YES)) == NULL)
        !           418:        return -1;
        !           419:     
        !           420: /*     Push the data, ignoring CRLF if necessary, down the stream
        !           421: **
        !           422: **
        !           423: **   @@  Bug:  This decision ought to be made based on "encoding"
        !           424: **   rather than on format.  @@@  When we handle encoding.
        !           425: **   The current method smells anyway.
        !           426: */
        !           427:     targetClass = *(stream->isa);      /* Copy pointers to procedures */
        !           428:     if (rep_in == WWW_BINARY || rep_in == WWW_UNKNOWN
        !           429:        || (HTAnchor_encoding(request->anchor) != HTAtom_for("8bit") &&
        !           430:            HTAnchor_encoding(request->anchor) != HTAtom_for("7bit"))
        !           431:         || strstr(HTAtom_name(rep_in), "image/")
        !           432:        || strstr(HTAtom_name(rep_in), "video/")) { /* @@@@@@ */
        !           433:        HTCopy(file_number, stream);
        !           434:     } else
        !           435:         HTCopyNoCR(file_number, stream);
        !           436:     (*targetClass._free)(stream);
        !           437:     
        !           438:     return HT_LOADED;
        !           439: }
        !           440: 
        !           441: 
        !           442: 
        !           443: /*     Parse a file given format and file pointer
        !           444: **
        !           445: **   This routine is responsible for creating and PRESENTING any
        !           446: **   graphic (or other) objects described by the file.
        !           447: **
        !           448: **   The file number given is assumed to be a TELNET stream ie containing
        !           449: **   CRLF at the end of lines which need to be stripped to \n for unix
        !           450: **   when the format is textual.
        !           451: **
        !           452: */
        !           453: PUBLIC int HTParseFile ARGS3(
        !           454:        HTFormat,               rep_in,
        !           455:        FILE *,                 fp,
        !           456:        HTRequest *,            request)
        !           457: {
        !           458:     HTStream * stream;
        !           459:     HTStreamClass targetClass;    
        !           460: 
        !           461:     if (request->error_stack) {
        !           462:        if (TRACE) fprintf(TDEST, "ParseFile... Called whith non-empty error stack, so I return right away!\n");
        !           463:        return -1;
        !           464:     }
        !           465: 
        !           466:     /* Set up stream stack */
        !           467:     if ((stream = HTStreamStack(rep_in, request->output_format,
        !           468:                                request->output_stream, request, YES)) == NULL)
        !           469:        return -1;
        !           470:     
        !           471: /*     Push the data down the stream
        !           472: **
        !           473: **
        !           474: **   @@  Bug:  This decision ought to be made based on "encoding"
        !           475: **   rather than on content-type.  @@@  When we handle encoding.
        !           476: **   The current method smells anyway.
        !           477: */
        !           478:     targetClass = *(stream->isa);      /* Copy pointers to procedures */
        !           479:     HTFileCopy(fp, stream);
        !           480:     (*targetClass._free)(stream);
        !           481:     
        !           482:     return HT_LOADED;
        !           483: }
        !           484: 
        !           485: 
        !           486: /* ------------------------------------------------------------------------- */
        !           487: /*                     MULTI THREADED IMPLEMENTATIONS                       */
        !           488: /* ------------------------------------------------------------------------- */
        !           489: 
        !           490: /*     Push data from a socket down a stream
        !           491: **     -------------------------------------
        !           492: **
        !           493: **   This routine is responsible for creating and PRESENTING any
        !           494: **   graphic (or other) objects described by the file. As this function
        !           495: **   max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
        !           496: **   with both blocking or non-blocking sockets. It will always return to
        !           497: **   the event loop, however if we are using blocking I/O then we get a full
        !           498: **   buffer read, otherwise we get what's available.
        !           499: **
        !           500: ** Returns      HT_LOADED      if finished reading
        !           501: **             HT_ERROR        if error,
        !           502: **             HT_INTERRUPTED  if interrupted
        !           503: **                     HT_WOULD_BLOCK  if read would block
        !           504: */
        !           505: PUBLIC int HTSocketRead ARGS2(HTRequest *, request, HTStream *, target)
        !           506: {
        !           507:     HTInputSocket *isoc = request->net_info->isoc;
        !           508:     int b_read = isoc->input_limit-isoc->input_buffer;
        !           509:     int status;
        !           510:     if (!isoc || isoc->input_file_number==INVSOC) {
        !           511:        if (PROT_TRACE) fprintf(TDEST, "Read Socket. Bad argument\n");
        !           512:        return HT_ERROR;
        !           513:     }
        !           514: 
        !           515:     if (HTThreadIntr(isoc->input_file_number))               /* Interrupted */
        !           516:        return HT_INTERRUPTED;
        !           517: #if 0
        !           518:     while(1) {
        !           519: #endif
        !           520:        /* Read from socket if we got rid of all the data previously read */
        !           521:        if (isoc->input_pointer >= isoc->input_limit) {
        !           522:            if ((b_read = NETREAD(isoc->input_file_number, isoc->input_buffer,
        !           523:                                  INPUT_BUFFER_SIZE)) < 0) {
        !           524: #ifdef EAGAIN
        !           525:                if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX, SVR4*/
        !           526: #else
        !           527:                if (socerrno==EWOULDBLOCK)                            /* BSD */
        !           528: #endif
        !           529:                {
        !           530:                    if (PROT_TRACE)
        !           531:                        fprintf(TDEST, "Read Socket. WOULD BLOCK soc %d\n",
        !           532:                                isoc->input_file_number);
        !           533:                    HTThreadState(isoc->input_file_number, THD_SET_READ);
        !           534:                    return HT_WOULD_BLOCK;
        !           535:                } else {        /* We have a real error */
        !           536:                    if (PROT_TRACE)
        !           537:                        fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
        !           538:                    return HT_ERROR;
        !           539:                }
        !           540:            } else if (!b_read) {
        !           541:                HTThreadState(isoc->input_file_number, THD_CLR_READ);
        !           542:                return HT_LOADED;
        !           543:            }
        !           544: 
        !           545:            /* Remember how much we have read from the input socket */
        !           546:            isoc->input_pointer = isoc->input_buffer;
        !           547:            isoc->input_limit = isoc->input_buffer + b_read;
        !           548: 
        !           549: #ifdef NOT_ASCII
        !           550:            {
        !           551:                char *p = isoc->input_buffer;
        !           552:                while (p < isoc->input_limit) {
        !           553:                    *p = FROMASCII(*p);
        !           554:                    p++;
        !           555:                }
        !           556:            }
        !           557: #endif
        !           558:            if (PROT_TRACE)
        !           559:                fprintf(TDEST, "Read Socket. %d bytes read from socket %d\n",
        !           560:                        b_read, isoc->input_file_number);
        !           561:        }
        !           562:        
        !           563:        /* Now push the data down the stream */
        !           564:        if ((status = (*target->isa->put_block)(target, isoc->input_buffer,
        !           565:                                                b_read)) != HT_OK) {
        !           566:            if (status==HT_WOULD_BLOCK) {
        !           567:                if (PROT_TRACE)
        !           568:                    fprintf(TDEST, "Read Socket. Stream WOULD BLOCK\n");
        !           569:                HTThreadState(isoc->input_file_number, THD_CLR_READ);
        !           570:                return HT_WOULD_BLOCK;
        !           571:            } else {                                 /* We have a real error */
        !           572:                if (PROT_TRACE)
        !           573:                    fprintf(TDEST, "Read Socket. Stream ERROR\n");
        !           574:                return status;
        !           575:            }
        !           576:        }
        !           577:        isoc->input_pointer = isoc->input_buffer + b_read;
        !           578: #if 0
        !           579:     }
        !           580: #else
        !           581: /*     HTThreadState(isoc->input_file_number, THD_SET_READ); */
        !           582:     return HT_WOULD_BLOCK;
        !           583: #endif
        !           584: }

Webmaster