1 /*- 2 * Copyright (c) 2008 Tim Kientzle 3 * Copyright (c) 2010 Joerg Sonnenberger 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "lafe_platform.h" 29 #include <errno.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include "err.h" 35 #include "line_reader.h" 36 37 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) 38 #define strdup _strdup 39 #endif 40 41 /* 42 * Read lines from file and do something with each one. If option_null 43 * is set, lines are terminated with zero bytes; otherwise, they're 44 * terminated with newlines. 45 * 46 * This uses a self-sizing buffer to handle arbitrarily-long lines. 47 */ 48 struct lafe_line_reader { 49 FILE *f; 50 char *buff, *buff_end, *line_start, *line_end; 51 char *pathname; 52 size_t buff_length; 53 int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */ 54 }; 55 56 struct lafe_line_reader * 57 lafe_line_reader(const char *pathname, int nullSeparator) 58 { 59 struct lafe_line_reader *lr; 60 61 lr = calloc(1, sizeof(*lr)); 62 if (lr == NULL) 63 lafe_errc(1, ENOMEM, "Can't open %s", pathname); 64 65 lr->nullSeparator = nullSeparator; 66 lr->pathname = strdup(pathname); 67 68 if (strcmp(pathname, "-") == 0) 69 lr->f = stdin; 70 else 71 lr->f = fopen(pathname, "r"); 72 if (lr->f == NULL) 73 lafe_errc(1, errno, "Couldn't open %s", pathname); 74 lr->buff_length = 8192; 75 lr->line_start = lr->line_end = lr->buff_end = lr->buff = NULL; 76 77 return (lr); 78 } 79 80 static void 81 lafe_line_reader_find_eol(struct lafe_line_reader *lr) 82 { 83 84 lr->line_end += strcspn(lr->line_end, 85 lr->nullSeparator ? "" : "\x0d\x0a"); 86 *lr->line_end = '\0'; /* Noop if line_end == buff_end */ 87 } 88 89 const char * 90 lafe_line_reader_next(struct lafe_line_reader *lr) 91 { 92 size_t bytes_wanted, bytes_read, new_buff_size; 93 char *line_start, *p; 94 95 for (;;) { 96 /* If there's a line in the buffer, return it immediately. */ 97 while (lr->line_end < lr->buff_end) { 98 line_start = lr->line_start; 99 lr->line_start = ++lr->line_end; 100 lafe_line_reader_find_eol(lr); 101 102 if (lr->nullSeparator || line_start[0] != '\0') 103 return (line_start); 104 } 105 106 /* If we're at end-of-file, process the final data. */ 107 if (lr->f == NULL) { 108 if (lr->line_start == lr->buff_end) 109 return (NULL); /* No more text */ 110 line_start = lr->line_start; 111 lr->line_start = lr->buff_end; 112 return (line_start); 113 } 114 115 /* Buffer only has part of a line. */ 116 if (lr->line_start > lr->buff) { 117 /* Move a leftover fractional line to the beginning. */ 118 memmove(lr->buff, lr->line_start, 119 lr->buff_end - lr->line_start); 120 lr->buff_end -= lr->line_start - lr->buff; 121 lr->line_end -= lr->line_start - lr->buff; 122 lr->line_start = lr->buff; 123 } else { 124 /* Line is too big; enlarge the buffer. */ 125 new_buff_size = lr->buff_length * 2; 126 if (new_buff_size <= lr->buff_length) 127 lafe_errc(1, ENOMEM, 128 "Line too long in %s", lr->pathname); 129 lr->buff_length = new_buff_size; 130 /* 131 * Allocate one extra byte to allow terminating 132 * the buffer. 133 */ 134 p = realloc(lr->buff, new_buff_size + 1); 135 if (p == NULL) 136 lafe_errc(1, ENOMEM, 137 "Line too long in %s", lr->pathname); 138 lr->buff_end = p + (lr->buff_end - lr->buff); 139 lr->line_end = p + (lr->line_end - lr->buff); 140 lr->line_start = lr->buff = p; 141 } 142 143 /* Get some more data into the buffer. */ 144 bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; 145 bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); 146 lr->buff_end += bytes_read; 147 *lr->buff_end = '\0'; /* Always terminate buffer */ 148 lafe_line_reader_find_eol(lr); 149 150 if (ferror(lr->f)) 151 lafe_errc(1, errno, "Can't read %s", lr->pathname); 152 if (feof(lr->f)) { 153 if (lr->f != stdin) 154 fclose(lr->f); 155 lr->f = NULL; 156 } 157 } 158 } 159 160 void 161 lafe_line_reader_free(struct lafe_line_reader *lr) 162 { 163 free(lr->buff); 164 free(lr->pathname); 165 free(lr); 166 } 167