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 __FBSDID("$FreeBSD$"); 30 31 #include <errno.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 #include "err.h" 37 #include "line_reader.h" 38 39 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) 40 #define strdup _strdup 41 #endif 42 43 /* 44 * Read lines from file and do something with each one. If option_null 45 * is set, lines are terminated with zero bytes; otherwise, they're 46 * terminated with newlines. 47 * 48 * This uses a self-sizing buffer to handle arbitrarily-long lines. 49 */ 50 struct lafe_line_reader { 51 FILE *f; 52 char *buff, *buff_end, *line_start, *line_end; 53 char *pathname; 54 size_t buff_length; 55 int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */ 56 }; 57 58 struct lafe_line_reader * 59 lafe_line_reader(const char *pathname, int nullSeparator) 60 { 61 struct lafe_line_reader *lr; 62 63 lr = calloc(1, sizeof(*lr)); 64 if (lr == NULL) 65 lafe_errc(1, ENOMEM, "Can't open %s", pathname); 66 67 lr->nullSeparator = nullSeparator; 68 lr->pathname = strdup(pathname); 69 70 if (strcmp(pathname, "-") == 0) 71 lr->f = stdin; 72 else 73 lr->f = fopen(pathname, "r"); 74 if (lr->f == NULL) 75 lafe_errc(1, errno, "Couldn't open %s", pathname); 76 lr->buff_length = 8192; 77 lr->line_start = lr->line_end = lr->buff_end = lr->buff = NULL; 78 79 return (lr); 80 } 81 82 static void 83 lafe_line_reader_find_eol(struct lafe_line_reader *lr) 84 { 85 86 lr->line_end += strcspn(lr->line_end, 87 lr->nullSeparator ? "" : "\x0d\x0a"); 88 *lr->line_end = '\0'; /* Noop if line_end == buff_end */ 89 } 90 91 const char * 92 lafe_line_reader_next(struct lafe_line_reader *lr) 93 { 94 size_t bytes_wanted, bytes_read, new_buff_size; 95 char *line_start, *p; 96 97 for (;;) { 98 /* If there's a line in the buffer, return it immediately. */ 99 while (lr->line_end < lr->buff_end) { 100 line_start = lr->line_start; 101 lr->line_start = ++lr->line_end; 102 lafe_line_reader_find_eol(lr); 103 104 if (lr->nullSeparator || line_start[0] != '\0') 105 return (line_start); 106 } 107 108 /* If we're at end-of-file, process the final data. */ 109 if (lr->f == NULL) { 110 if (lr->line_start == lr->buff_end) 111 return (NULL); /* No more text */ 112 line_start = lr->line_start; 113 lr->line_start = lr->buff_end; 114 return (line_start); 115 } 116 117 /* Buffer only has part of a line. */ 118 if (lr->line_start > lr->buff) { 119 /* Move a leftover fractional line to the beginning. */ 120 memmove(lr->buff, lr->line_start, 121 lr->buff_end - lr->line_start); 122 lr->buff_end -= lr->line_start - lr->buff; 123 lr->line_end -= lr->line_start - lr->buff; 124 lr->line_start = lr->buff; 125 } else { 126 /* Line is too big; enlarge the buffer. */ 127 new_buff_size = lr->buff_length * 2; 128 if (new_buff_size <= lr->buff_length) 129 lafe_errc(1, ENOMEM, 130 "Line too long in %s", lr->pathname); 131 lr->buff_length = new_buff_size; 132 /* 133 * Allocate one extra byte to allow terminating 134 * the buffer. 135 */ 136 p = realloc(lr->buff, new_buff_size + 1); 137 if (p == NULL) 138 lafe_errc(1, ENOMEM, 139 "Line too long in %s", lr->pathname); 140 lr->buff_end = p + (lr->buff_end - lr->buff); 141 lr->line_end = p + (lr->line_end - lr->buff); 142 lr->line_start = lr->buff = p; 143 } 144 145 /* Get some more data into the buffer. */ 146 bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; 147 bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); 148 lr->buff_end += bytes_read; 149 *lr->buff_end = '\0'; /* Always terminate buffer */ 150 lafe_line_reader_find_eol(lr); 151 152 if (ferror(lr->f)) 153 lafe_errc(1, errno, "Can't read %s", lr->pathname); 154 if (feof(lr->f)) { 155 if (lr->f != stdin) 156 fclose(lr->f); 157 lr->f = NULL; 158 } 159 } 160 } 161 162 void 163 lafe_line_reader_free(struct lafe_line_reader *lr) 164 { 165 free(lr->buff); 166 free(lr->pathname); 167 free(lr); 168 } 169