		/* Copyright 2000-2004 The Apache Software Foundation
		 *
		 * Licensed under the Apache License, Version 2.0 (the "License");
		 * you may not use this file except in compliance with the License.
		 * You may obtain a copy of the License at
		 *
		 *     http://www.apache.org/licenses/LICENSE-2.0
		 *
		 * Unless required by applicable law or agreed to in writing, software
		 * distributed under the License is distributed on an "AS IS" BASIS,
		 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
		 * See the License for the specific language governing permissions and
		 * limitations under the License.
		 */
		
		#include "apr_arch_file_io.h"
		#include "apr_strings.h"
		#include "apr_thread_mutex.h"
		#include "apr_support.h"
		
		/* The only case where we don't use wait_for_io_or_timeout is on
		 * pre-BONE BeOS, so this check should be sufficient and simpler */
		#if !BEOS_R5
		#define USE_WAIT_FOR_IO
		#endif
		
		/* problems: 
		 * 1) ungetchar not used for buffered files
		 */
		APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes)
       62581    {
       62581        apr_ssize_t rv;
       62581        apr_size_t bytes_read;
		
       62581        if (*nbytes <= 0) {
      ######            *nbytes = 0;
      ######            return APR_SUCCESS;
		    }
		
       62581        if (thefile->buffered) {
      ######            char *pos = (char *)buf;
      ######            apr_uint64_t blocksize;
      ######            apr_uint64_t size = *nbytes;
		
		#if APR_HAS_THREADS
      ######            if (thefile->thlock) {
      ######                apr_thread_mutex_lock(thefile->thlock);
		        }
		#endif
		
      ######            if (thefile->direction == 1) {
      ######                apr_file_flush(thefile);
      ######                thefile->bufpos = 0;
      ######                thefile->direction = 0;
      ######                thefile->dataRead = 0;
		        }
		
      ######            rv = 0;
      ######            if (thefile->ungetchar != -1) {
      ######                *pos = (char)thefile->ungetchar;
      ######                ++pos;
      ######                --size;
      ######                thefile->ungetchar = -1;
		        }
      ######            while (rv == 0 && size > 0) {
      ######                if (thefile->bufpos >= thefile->dataRead) {
      ######                    int bytesread = read(thefile->filedes, thefile->buffer, APR_FILE_BUFSIZE);
      ######                    if (bytesread == 0) {
      ######                        thefile->eof_hit = TRUE;
      ######                        rv = APR_EOF;
      ######                        break;
		                }
      ######                    else if (bytesread == -1) {
      ######                        rv = errno;
      ######                        break;
		                }
      ######                    thefile->dataRead = bytesread;
      ######                    thefile->filePtr += thefile->dataRead;
      ######                    thefile->bufpos = 0;
		            }
		
      ######                blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
      ######                memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
      ######                thefile->bufpos += blocksize;
      ######                pos += blocksize;
      ######                size -= blocksize;
		        }
		
      ######            *nbytes = pos - (char *)buf;
      ######            if (*nbytes) {
      ######                rv = 0;
		        }
		#if APR_HAS_THREADS
      ######            if (thefile->thlock) {
      ######                apr_thread_mutex_unlock(thefile->thlock);
		        }
		#endif
      ######            return rv;
		    }
		    else {
       62581            bytes_read = 0;
       62581            if (thefile->ungetchar != -1) {
           1                bytes_read = 1;
           1                *(char *)buf = (char)thefile->ungetchar;
           1                buf = (char *)buf + 1;
           1                (*nbytes)--;
           1                thefile->ungetchar = -1;
           1                if (*nbytes == 0) {
           1                    *nbytes = bytes_read;
           1                    return APR_SUCCESS;
		            }
		        }
		
       62580            do {
       62580                rv = read(thefile->filedes, buf, *nbytes);
       62580            } while (rv == -1 && errno == EINTR);
		#ifdef USE_WAIT_FOR_IO
       62580            if (rv == -1 && 
		            (errno == EAGAIN || errno == EWOULDBLOCK) && 
		            thefile->timeout != 0) {
           2                apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1);
           2                if (arv != APR_SUCCESS) {
           1                    *nbytes = bytes_read;
           1                    return arv;
		            }
		            else {
           1                    do {
           1                        rv = read(thefile->filedes, buf, *nbytes);
           1                    } while (rv == -1 && errno == EINTR);
		            }
		        }  
		#endif
       62579            *nbytes = bytes_read;
       62579            if (rv == 0) {
           9                thefile->eof_hit = TRUE;
           9                return APR_EOF;
		        }
       62570            if (rv > 0) {
       62568                *nbytes += rv;
       62568                return APR_SUCCESS;
		        }
           2            return errno;
		    }
		}
		
		APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
       20945    {
       20945        apr_size_t rv;
		
       20945        if (thefile->buffered) {
           1            char *pos = (char *)buf;
           1            int blocksize;
           1            int size = *nbytes;
		
		#if APR_HAS_THREADS
           1            if (thefile->thlock) {
      ######                apr_thread_mutex_lock(thefile->thlock);
		        }
		#endif
		
           1            if ( thefile->direction == 0 ) {
		            /* Position file pointer for writing at the offset we are 
		             * logically reading from
		             */
           1                apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
           1                if (offset != thefile->filePtr)
		#if defined(NETWARE) && APR_HAS_LARGE_FILES
		                lseek64(thefile->filedes, offset, SEEK_SET);
		#else
      ######                    lseek(thefile->filedes, offset, SEEK_SET);
		#endif
           1                thefile->bufpos = thefile->dataRead = 0;
           1                thefile->direction = 1;
		        }
		
           1            rv = 0;
           2            while (rv == 0 && size > 0) {
           1                if (thefile->bufpos == APR_FILE_BUFSIZE)   /* write buffer is full*/
      ######                    apr_file_flush(thefile);
		
           1                blocksize = size > APR_FILE_BUFSIZE - thefile->bufpos ? 
		                        APR_FILE_BUFSIZE - thefile->bufpos : size;
           1                memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);                      
           1                thefile->bufpos += blocksize;
           1                pos += blocksize;
           1                size -= blocksize;
		        }
		
		#if APR_HAS_THREADS
           1            if (thefile->thlock) {
      ######                apr_thread_mutex_unlock(thefile->thlock);
		        }
		#endif
           1            return rv;
		    }
		    else {
       20944            do {
       20944                rv = write(thefile->filedes, buf, *nbytes);
       20944            } while (rv == (apr_size_t)-1 && errno == EINTR);
		#ifdef USE_WAIT_FOR_IO
       20944            if (rv == (apr_size_t)-1 &&
		            (errno == EAGAIN || errno == EWOULDBLOCK) && 
		            thefile->timeout != 0) {
        1953                apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
        1953                if (arv != APR_SUCCESS) {
      ######                    *nbytes = 0;
      ######                    return arv;
		            }
		            else {
        1953                    do {
        1953                        do {
        1953                            rv = write(thefile->filedes, buf, *nbytes);
        1953                        } while (rv == (apr_size_t)-1 && errno == EINTR);
        1953                        if (rv == (apr_size_t)-1 &&
		                        (errno == EAGAIN || errno == EWOULDBLOCK)) {
      ######                            *nbytes /= 2; /* yes, we'll loop if kernel lied
		                                       * and we can't even write 1 byte
		                                       */
		                    }
		                    else {
       20944                            break;
		                    }
       20944                    } while (1);
		            }
		        }  
		#endif
       20944            if (rv == (apr_size_t)-1) {
      ######                (*nbytes) = 0;
      ######                return errno;
		        }
       20944            *nbytes = rv;
       20944            return APR_SUCCESS;
		    }
		}
		
		APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec,
		                                          apr_size_t nvec, apr_size_t *nbytes)
      ######    {
		#ifdef HAVE_WRITEV
      ######        int bytes;
		
      ######        if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) {
      ######            *nbytes = 0;
      ######            return errno;
		    }
		    else {
      ######            *nbytes = bytes;
      ######            return APR_SUCCESS;
		    }
		#else
		    *nbytes = vec[0].iov_len;
		    return apr_file_write(thefile, vec[0].iov_base, nbytes);
		#endif
		}
		
		APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
           2    {
           2        apr_size_t nbytes = 1;
		
           2        return apr_file_write(thefile, &ch, &nbytes);
		}
		
		APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
           1    {
           1        thefile->ungetchar = (unsigned char)ch;
           1        return APR_SUCCESS; 
		}
		
		APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
           3    {
           3        apr_size_t nbytes = 1;
		
           3        return apr_file_read(thefile, ch, &nbytes);
		}
		
		APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
           1    {
           1        apr_size_t nbytes = strlen(str);
		
           1        return apr_file_write(thefile, str, &nbytes);
		}
		
		APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
           3    {
           3        if (thefile->buffered) {
           3            apr_int64_t written = 0;
		
           3            if (thefile->direction == 1 && thefile->bufpos) {
           1                do {
           1                    written = write(thefile->filedes, thefile->buffer, thefile->bufpos);
           1                } while (written == (apr_int64_t)-1 && errno == EINTR);
           1                if (written == (apr_int64_t)-1) {
      ######                    return errno;
		            }
           1                thefile->filePtr += written;
           1                thefile->bufpos = 0;
		        }
		    }
		    /* There isn't anything to do if we aren't buffering the output
		     * so just return success.
		     */
           3        return APR_SUCCESS; 
		}
		
		APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
           4    {
           4        apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
           4        apr_size_t nbytes;
           4        const char *str_start = str;
           4        char *final = str + len - 1;
		
           4        if (len <= 1) {  
		        /* sort of like fgets(), which returns NULL and stores no bytes 
		         */
      ######            return APR_SUCCESS;
		    }
		
          49        while (str < final) { /* leave room for trailing '\0' */
          49            nbytes = 1;
          49            rv = apr_file_read(thefile, str, &nbytes);
          49            if (rv != APR_SUCCESS) {
           2                break;
		        }
          47            if (*str == '\n') {
           2                ++str;
           2                break;
		        }
          45            ++str;
		    }
		    /* We must store a terminating '\0' if we've stored any chars. We can
		     * get away with storing it if we hit an error first. 
		     */
           4        *str = '\0';
           4        if (str > str_start) {
		        /* we stored chars; don't report EOF or any other errors;
		         * the app will find out about that on the next call
		         */
           3            return APR_SUCCESS;
		    }
           1        return rv;
		}
		
		APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr, 
		                                        const char *format, ...)
           1    {
           1        apr_status_t cc;
           1        va_list ap;
           1        char *buf;
           1        int len;
		
           1        buf = malloc(HUGE_STRING_LEN);
           1        if (buf == NULL) {
      ######            return 0;
		    }
           1        va_start(ap, format);
           1        len = apr_vsnprintf(buf, HUGE_STRING_LEN, format, ap);
           1        cc = apr_file_puts(buf, fptr);
           1        va_end(ap);
           1        free(buf);
           1        return (cc == APR_SUCCESS) ? len : -1;
		}
