grep: option to use GNU regex matching instead of POSIX one.
[oweals/busybox.git] / libbb / get_line_from_file.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net>
6  * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2001 Matt Krai
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 /* for getline() [GNUism] */
13 #ifndef _GNU_SOURCE
14 #define _GNU_SOURCE 1
15 #endif
16 #include "libbb.h"
17
18 /* This function reads an entire line from a text file, up to a newline
19  * or NUL byte, inclusive.  It returns a malloc'ed char * which
20  * must be free'ed by the caller.  If end is NULL '\n' isn't considered
21  * end of line.  If end isn't NULL, length of the chunk read is stored in it.
22  * Return NULL if EOF/error */
23 char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end)
24 {
25         int ch;
26         int idx = 0;
27         char *linebuf = NULL;
28         int linebufsz = 0;
29
30         while ((ch = getc(file)) != EOF) {
31                 /* grow the line buffer as necessary */
32                 if (idx >= linebufsz) {
33                         linebufsz += 80;
34                         linebuf = xrealloc(linebuf, linebufsz);
35                 }
36                 linebuf[idx++] = (char) ch;
37                 if (!ch || (end && ch == '\n'))
38                         break;
39         }
40         if (end)
41                 *end = idx;
42         if (linebuf) {
43                 // huh, does fgets discard prior data on error like this?
44                 // I don't think so....
45                 //if (ferror(file)) {
46                 //      free(linebuf);
47                 //      return NULL;
48                 //}
49                 linebuf = xrealloc(linebuf, idx + 1);
50                 linebuf[idx] = '\0';
51         }
52         return linebuf;
53 }
54
55 /* Get line, including trailing \n if any */
56 char* FAST_FUNC xmalloc_fgets(FILE *file)
57 {
58         int i;
59
60         return bb_get_chunk_from_file(file, &i);
61 }
62 /* Get line.  Remove trailing \n */
63 char* FAST_FUNC xmalloc_fgetline(FILE *file)
64 {
65         int i;
66         char *c = bb_get_chunk_from_file(file, &i);
67
68         if (i && c[--i] == '\n')
69                 c[i] = '\0';
70
71         return c;
72 }
73
74 #if 0
75
76 /* GNUism getline() should be faster (not tested) than a loop with fgetc */
77
78 /* Get line, including trailing \n if any */
79 char* FAST_FUNC xmalloc_fgets(FILE *file)
80 {
81         char *res_buf = NULL;
82         size_t res_sz;
83
84         if (getline(&res_buf, &res_sz, file) == -1) {
85                 free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
86                 res_buf = NULL;
87         }
88 //TODO: trimming to res_sz?
89         return res_buf;
90 }
91 /* Get line.  Remove trailing \n */
92 char* FAST_FUNC xmalloc_fgetline(FILE *file)
93 {
94         char *res_buf = NULL;
95         size_t res_sz;
96
97         res_sz = getline(&res_buf, &res_sz, file);
98
99         if ((ssize_t)res_sz != -1) {
100                 if (res_buf[res_sz - 1] == '\n')
101                         res_buf[--res_sz] = '\0';
102 //TODO: trimming to res_sz?
103         } else {
104                 free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
105                 res_buf = NULL;
106         }
107         return res_buf;
108 }
109
110 #endif
111
112 #if 0
113 /* Faster routines (~twice as fast). +170 bytes. Unused as of 2008-07.
114  *
115  * NB: they stop at NUL byte too.
116  * Performance is important here. Think "grep 50gigabyte_file"...
117  * Ironically, grep can't use it because of NUL issue.
118  * We sorely need C lib to provide fgets which reports size!
119  *
120  * Update:
121  * Actually, uclibc and glibc have it. man getline. It's GNUism,
122  *   but very useful one (if it's as fast as this code).
123  * TODO:
124  * - currently, sed and sort use bb_get_chunk_from_file and heavily
125  *   depend on its "stop on \n or \0" behavior, and STILL they fail
126  *   to handle all cases with embedded NULs correctly. So:
127  * - audit sed and sort; convert them to getline FIRST.
128  * - THEN ditch bb_get_chunk_from_file, replace it with getline.
129  * - provide getline implementation for non-GNU systems.
130  */
131
132 static char* xmalloc_fgets_internal(FILE *file, int *sizep)
133 {
134         int len;
135         int idx = 0;
136         char *linebuf = NULL;
137
138         while (1) {
139                 char *r;
140
141                 linebuf = xrealloc(linebuf, idx + 0x100);
142                 r = fgets(&linebuf[idx], 0x100, file);
143                 if (!r) {
144                         /* need to terminate in case this is error
145                          * (EOF puts NUL itself) */
146                         linebuf[idx] = '\0';
147                         break;
148                 }
149                 /* stupid. fgets knows the len, it should report it somehow */
150                 len = strlen(&linebuf[idx]);
151                 idx += len;
152                 if (len != 0xff || linebuf[idx - 1] == '\n')
153                         break;
154         }
155         *sizep = idx;
156         if (idx) {
157                 /* xrealloc(linebuf, idx + 1) is up to caller */
158                 return linebuf;
159         }
160         free(linebuf);
161         return NULL;
162 }
163
164 /* Get line, remove trailing \n */
165 char* FAST_FUNC xmalloc_fgetline_fast(FILE *file)
166 {
167         int sz;
168         char *r = xmalloc_fgets_internal(file, &sz);
169         if (r && r[sz - 1] == '\n')
170                 r[--sz] = '\0';
171         return r; /* not xrealloc(r, sz + 1)! */
172 }
173
174 char* FAST_FUNC xmalloc_fgets(FILE *file)
175 {
176         int sz;
177         return xmalloc_fgets_internal(file, &sz);
178 }
179
180 /* Get line, remove trailing \n */
181 char* FAST_FUNC xmalloc_fgetline(FILE *file)
182 {
183         int sz;
184         char *r = xmalloc_fgets_internal(file, &sz);
185         if (!r)
186                 return r;
187         if (r[sz - 1] == '\n')
188                 r[--sz] = '\0';
189         return xrealloc(r, sz + 1);
190 }
191 #endif