#include "fileutil.h" #include #include #include #include int __fp_open(FILE *fp, int fd, bool use_buffering) { /* We initialize fdpos to 0 even in append mode (7.19.3§1) */ fp->fd = fd; fp->fdpos = 0; /* We assume all files in the filesystem are non-interactive in order to conform to (7.19.5.3§7) TODO: Vhex might want something more elaborate here */ if(use_buffering && setvbuf(fp, NULL, _IOFBF, BUFSIZ)) return -1; return 0; } void __fp_close(FILE *fp, bool free_fp) { if(!fp) return; if(fp->fd >= 0) { fflush(fp); close(fp->fd); } if(fp->bufowned) free(fp->buf); if(free_fp) free(fp); } void __fp_remove_buffer(FILE *fp) { if(fp->bufowned) free(fp->buf); fp->buf = NULL; fp->bufowned = false; fp->bufsize = 0; } bool __fp_set_buffer(FILE *fp, void *buf, size_t size) { bool owned = false; if(!buf) { owned = true; buf = malloc(size); if(!buf) return false; } fp->buf = buf; fp->bufowned = owned; fp->bufsize = size; return true; } void __fp_buffer_mode_read(FILE *fp) { if(__fp_hasbuf_write(fp)) { fflush(fp); } if(fp->buf) fp->bufdir = __FILE_BUF_READ; } void __fp_buffer_mode_write(FILE *fp) { if(__fp_hasbuf_read(fp)) fflush(fp); if(fp->buf) fp->bufdir = __FILE_BUF_WRITE; } ssize_t __fp_fread2(FILE *fp, void *data, size_t request_size, int stop_char) { if(!fp->readable) { fp->error = 1; return -1; } /* If the stream if unbuffered, we might have no buffer for the reads. If it's buffered, we always have one. It's also possible that fp is unbuffered (_IONBF) but has a buffer temporarily because ungetc() has been used, in which case we have to transition from buffered reads into direct reads midway. We use __fp_buffered_read() to handle this. */ size_t read_size = 0; __fp_buffer_mode_read(fp); while(read_size < request_size) { int remaining = request_size - read_size; int chunk = __fp_buffered_read(fp, data+read_size, remaining, stop_char); /* Stream is not/no longer buffered, finish unbuffered */ if(chunk < 0 && stop_char >= 0) { unsigned char c; /* When there is a stop char, we can only read one byte at a time, which is reaaaally slow */ for(int i = 0; i < remaining; i++) { ssize_t rc = __fp_read(fp, &c, 1); if(rc != 1) break; ((char *)data)[read_size++] = c; if(c == stop_char) break; } return read_size; } else if(chunk < 0) { ssize_t rc = __fp_read(fp, data+read_size, remaining); return read_size + (rc == EOF ? 0 : rc); } read_size += chunk; if(read_size >= request_size) break; /* If a stop char has been read, stop */ if(stop_char >= 0 && read_size > 0 && ((char *)data)[read_size-1] == stop_char) break; /* Get more data from the file descriptor into the buffer */ if(fp->buf) { ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize); if(rc <= 0) /* EOF or error */ break; fp->bufread = rc; } } return read_size; } ssize_t __fp_buffered_read(FILE *fp, void *data, size_t request_size, int stop_char) { if(!fp->buf || __fp_hasbuf_write(fp)) return -1; int read_size = request_size; if(read_size > (int)(fp->bufread - fp->bufpos)) read_size = fp->bufread - fp->bufpos; if(read_size <= 0) return 0; if(stop_char >= 0) { char *end = memchr(fp->buf+fp->bufpos, stop_char, read_size); if(end != NULL) read_size = end - (fp->buf + fp->bufpos) + 1; } memcpy(data, fp->buf + fp->bufpos, read_size); fp->bufpos += read_size; fp->bufungetc = fp->bufungetc - read_size; if(fp->bufungetc < 0) fp->bufungetc = 0; /* Rewind the buffer if we read it fully */ if(fp->bufpos >= fp->bufread) { fp->bufread = 0; fp->bufpos = 0; /* Clear temporary ungetc() buffers of _IONBF streams */ if(fp->bufmode == _IONBF) __fp_remove_buffer(fp); } return read_size; } ssize_t __fp_read(FILE *fp, void *data, size_t size) { size_t read_ = 0; while(read_ < size) { ssize_t rc = read(fp->fd, data + read_, size - read_); if(rc < 0) { fp->error = 1; return EOF; } if(rc == 0) { fp->eof = 1; break; } fp->fdpos += rc; read_ += rc; } return read_; } ssize_t __fp_write(FILE *fp, void const *data, size_t size) { size_t written = 0; while(written < size) { ssize_t rc = write(fp->fd, data + written, size - written); if(rc < 0) { fp->error = 1; return EOF; } if(rc == 0) break; fp->fdpos += rc; written += rc; } return written; } int __fp_parse_mode(char const *mode, FILE *fp) { int base = 0; bool binary = false; bool update = false; for(int i = 0; mode[i]; i++) { if(mode[i] == 'r' || mode[i] == 'w' || mode[i] == 'a') { if(base) goto err; base = mode[i]; } else if(mode[i] == 'b') binary = true; else if(mode[i] == '+') update = true; } if(!base) goto err; if(fp) { fp->readable = (base == 'r' || update); fp->writable = (base == 'w' || base == 'a' || update); fp->append = (base == 'a'); fp->text = !binary; } if(base == 'r') return (update ? O_RDWR : O_RDONLY); if(base == 'w') return (update ? O_RDWR : O_WRONLY) | O_CREAT | O_TRUNC; if(base == 'a') return (update ? O_RDWR : O_WRONLY) | O_CREAT; /* Fallthrough */ err: errno = EINVAL; return -1; }