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