19c82a63eSPeter Avalos /*-
29c82a63eSPeter Avalos  * Copyright (c) 2008 Tim Kientzle
3c09f92d2SPeter Avalos  * Copyright (c) 2010 Joerg Sonnenberger
49c82a63eSPeter Avalos  * All rights reserved.
59c82a63eSPeter Avalos  *
69c82a63eSPeter Avalos  * Redistribution and use in source and binary forms, with or without
79c82a63eSPeter Avalos  * modification, are permitted provided that the following conditions
89c82a63eSPeter Avalos  * are met:
99c82a63eSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
109c82a63eSPeter Avalos  *    notice, this list of conditions and the following disclaimer
119c82a63eSPeter Avalos  *    in this position and unchanged.
129c82a63eSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
139c82a63eSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
149c82a63eSPeter Avalos  *    documentation and/or other materials provided with the distribution.
159c82a63eSPeter Avalos  *
169c82a63eSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
179c82a63eSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
189c82a63eSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
199c82a63eSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
209c82a63eSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
219c82a63eSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
229c82a63eSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
239c82a63eSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
249c82a63eSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
259c82a63eSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
269c82a63eSPeter Avalos  */
279c82a63eSPeter Avalos 
289c82a63eSPeter Avalos #include "lafe_platform.h"
299c82a63eSPeter Avalos __FBSDID("$FreeBSD$");
309c82a63eSPeter Avalos 
319c82a63eSPeter Avalos #include <errno.h>
329c82a63eSPeter Avalos #include <stdio.h>
339c82a63eSPeter Avalos #include <stdlib.h>
349c82a63eSPeter Avalos #include <string.h>
359c82a63eSPeter Avalos 
369c82a63eSPeter Avalos #include "err.h"
379c82a63eSPeter Avalos #include "line_reader.h"
389c82a63eSPeter Avalos 
399c82a63eSPeter Avalos #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__)
409c82a63eSPeter Avalos #define strdup _strdup
419c82a63eSPeter Avalos #endif
429c82a63eSPeter Avalos 
439c82a63eSPeter Avalos /*
449c82a63eSPeter Avalos  * Read lines from file and do something with each one.  If option_null
459c82a63eSPeter Avalos  * is set, lines are terminated with zero bytes; otherwise, they're
469c82a63eSPeter Avalos  * terminated with newlines.
479c82a63eSPeter Avalos  *
489c82a63eSPeter Avalos  * This uses a self-sizing buffer to handle arbitrarily-long lines.
499c82a63eSPeter Avalos  */
509c82a63eSPeter Avalos struct lafe_line_reader {
519c82a63eSPeter Avalos 	FILE *f;
52085658deSDaniel Fojt 	char *buff, *buff_end, *line_start, *line_end;
539c82a63eSPeter Avalos 	char *pathname;
549c82a63eSPeter Avalos 	size_t buff_length;
559c82a63eSPeter Avalos 	int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */
569c82a63eSPeter Avalos };
579c82a63eSPeter Avalos 
589c82a63eSPeter Avalos struct lafe_line_reader *
lafe_line_reader(const char * pathname,int nullSeparator)599c82a63eSPeter Avalos lafe_line_reader(const char *pathname, int nullSeparator)
609c82a63eSPeter Avalos {
619c82a63eSPeter Avalos 	struct lafe_line_reader *lr;
629c82a63eSPeter Avalos 
639c82a63eSPeter Avalos 	lr = calloc(1, sizeof(*lr));
649c82a63eSPeter Avalos 	if (lr == NULL)
659c82a63eSPeter Avalos 		lafe_errc(1, ENOMEM, "Can't open %s", pathname);
669c82a63eSPeter Avalos 
679c82a63eSPeter Avalos 	lr->nullSeparator = nullSeparator;
689c82a63eSPeter Avalos 	lr->pathname = strdup(pathname);
699c82a63eSPeter Avalos 
709c82a63eSPeter Avalos 	if (strcmp(pathname, "-") == 0)
719c82a63eSPeter Avalos 		lr->f = stdin;
729c82a63eSPeter Avalos 	else
739c82a63eSPeter Avalos 		lr->f = fopen(pathname, "r");
749c82a63eSPeter Avalos 	if (lr->f == NULL)
759c82a63eSPeter Avalos 		lafe_errc(1, errno, "Couldn't open %s", pathname);
769c82a63eSPeter Avalos 	lr->buff_length = 8192;
77c09f92d2SPeter Avalos 	lr->line_start = lr->line_end = lr->buff_end = lr->buff = NULL;
789c82a63eSPeter Avalos 
799c82a63eSPeter Avalos 	return (lr);
809c82a63eSPeter Avalos }
819c82a63eSPeter Avalos 
82c09f92d2SPeter Avalos static void
lafe_line_reader_find_eol(struct lafe_line_reader * lr)83c09f92d2SPeter Avalos lafe_line_reader_find_eol(struct lafe_line_reader *lr)
84c09f92d2SPeter Avalos {
85c09f92d2SPeter Avalos 
86c09f92d2SPeter Avalos 	lr->line_end += strcspn(lr->line_end,
87c09f92d2SPeter Avalos 	    lr->nullSeparator ? "" : "\x0d\x0a");
88c09f92d2SPeter Avalos 	*lr->line_end = '\0'; /* Noop if line_end == buff_end */
89c09f92d2SPeter Avalos }
90c09f92d2SPeter Avalos 
919c82a63eSPeter Avalos const char *
lafe_line_reader_next(struct lafe_line_reader * lr)929c82a63eSPeter Avalos lafe_line_reader_next(struct lafe_line_reader *lr)
939c82a63eSPeter Avalos {
949c82a63eSPeter Avalos 	size_t bytes_wanted, bytes_read, new_buff_size;
959c82a63eSPeter Avalos 	char *line_start, *p;
969c82a63eSPeter Avalos 
979c82a63eSPeter Avalos 	for (;;) {
989c82a63eSPeter Avalos 		/* If there's a line in the buffer, return it immediately. */
999c82a63eSPeter Avalos 		while (lr->line_end < lr->buff_end) {
1009c82a63eSPeter Avalos 			line_start = lr->line_start;
101c09f92d2SPeter Avalos 			lr->line_start = ++lr->line_end;
102c09f92d2SPeter Avalos 			lafe_line_reader_find_eol(lr);
103c09f92d2SPeter Avalos 
104c09f92d2SPeter Avalos 			if (lr->nullSeparator || line_start[0] != '\0')
1059c82a63eSPeter Avalos 				return (line_start);
1069c82a63eSPeter Avalos 		}
1079c82a63eSPeter Avalos 
1089c82a63eSPeter Avalos 		/* If we're at end-of-file, process the final data. */
1099c82a63eSPeter Avalos 		if (lr->f == NULL) {
110c09f92d2SPeter Avalos 			if (lr->line_start == lr->buff_end)
111c09f92d2SPeter Avalos 				return (NULL); /* No more text */
1129c82a63eSPeter Avalos 			line_start = lr->line_start;
113c09f92d2SPeter Avalos 			lr->line_start = lr->buff_end;
1149c82a63eSPeter Avalos 			return (line_start);
1159c82a63eSPeter Avalos 		}
1169c82a63eSPeter Avalos 
1179c82a63eSPeter Avalos 		/* Buffer only has part of a line. */
1189c82a63eSPeter Avalos 		if (lr->line_start > lr->buff) {
1199c82a63eSPeter Avalos 			/* Move a leftover fractional line to the beginning. */
1209c82a63eSPeter Avalos 			memmove(lr->buff, lr->line_start,
1219c82a63eSPeter Avalos 			    lr->buff_end - lr->line_start);
1229c82a63eSPeter Avalos 			lr->buff_end -= lr->line_start - lr->buff;
1239c82a63eSPeter Avalos 			lr->line_end -= lr->line_start - lr->buff;
1249c82a63eSPeter Avalos 			lr->line_start = lr->buff;
1259c82a63eSPeter Avalos 		} else {
1269c82a63eSPeter Avalos 			/* Line is too big; enlarge the buffer. */
1279c82a63eSPeter Avalos 			new_buff_size = lr->buff_length * 2;
1289c82a63eSPeter Avalos 			if (new_buff_size <= lr->buff_length)
1299c82a63eSPeter Avalos 				lafe_errc(1, ENOMEM,
1309c82a63eSPeter Avalos 				    "Line too long in %s", lr->pathname);
1319c82a63eSPeter Avalos 			lr->buff_length = new_buff_size;
132c09f92d2SPeter Avalos 			/*
133c09f92d2SPeter Avalos 			 * Allocate one extra byte to allow terminating
134c09f92d2SPeter Avalos 			 * the buffer.
135c09f92d2SPeter Avalos 			 */
136c09f92d2SPeter Avalos 			p = realloc(lr->buff, new_buff_size + 1);
1379c82a63eSPeter Avalos 			if (p == NULL)
1389c82a63eSPeter Avalos 				lafe_errc(1, ENOMEM,
1399c82a63eSPeter Avalos 				    "Line too long in %s", lr->pathname);
1409c82a63eSPeter Avalos 			lr->buff_end = p + (lr->buff_end - lr->buff);
1419c82a63eSPeter Avalos 			lr->line_end = p + (lr->line_end - lr->buff);
1429c82a63eSPeter Avalos 			lr->line_start = lr->buff = p;
1439c82a63eSPeter Avalos 		}
1449c82a63eSPeter Avalos 
1459c82a63eSPeter Avalos 		/* Get some more data into the buffer. */
1469c82a63eSPeter Avalos 		bytes_wanted = lr->buff + lr->buff_length - lr->buff_end;
1479c82a63eSPeter Avalos 		bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f);
1489c82a63eSPeter Avalos 		lr->buff_end += bytes_read;
149c09f92d2SPeter Avalos 		*lr->buff_end = '\0'; /* Always terminate buffer */
150c09f92d2SPeter Avalos 		lafe_line_reader_find_eol(lr);
1519c82a63eSPeter Avalos 
1529c82a63eSPeter Avalos 		if (ferror(lr->f))
1539c82a63eSPeter Avalos 			lafe_errc(1, errno, "Can't read %s", lr->pathname);
1549c82a63eSPeter Avalos 		if (feof(lr->f)) {
1559c82a63eSPeter Avalos 			if (lr->f != stdin)
1569c82a63eSPeter Avalos 				fclose(lr->f);
1579c82a63eSPeter Avalos 			lr->f = NULL;
1589c82a63eSPeter Avalos 		}
1599c82a63eSPeter Avalos 	}
1609c82a63eSPeter Avalos }
1619c82a63eSPeter Avalos 
1629c82a63eSPeter Avalos void
lafe_line_reader_free(struct lafe_line_reader * lr)1639c82a63eSPeter Avalos lafe_line_reader_free(struct lafe_line_reader *lr)
1649c82a63eSPeter Avalos {
1659c82a63eSPeter Avalos 	free(lr->buff);
1669c82a63eSPeter Avalos 	free(lr->pathname);
1679c82a63eSPeter Avalos 	free(lr);
1689c82a63eSPeter Avalos }
169