]>
Commit | Line | Data |
---|---|---|
3185942a JA |
1 | This file is mapfile.def, from which is created mapfile.c. |
2 | It implements the builtin "mapfile" in Bash. | |
3 | ||
4 | Copyright (C) 2005-2006 Rocky Bernstein for Free Software Foundation, Inc. | |
5 | Copyright (C) 2008,2009 Free Software Foundation, Inc. | |
6 | ||
7 | This file is part of GNU Bash, the Bourne Again SHell. | |
8 | ||
9 | Bash is free software: you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation, either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | Bash is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
21 | ||
22 | $PRODUCES mapfile.c | |
23 | ||
24 | $BUILTIN mapfile | |
25 | $FUNCTION mapfile_builtin | |
26 | $SHORT_DOC mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] | |
0001803f | 27 | Read lines from the standard input into an indexed array variable. |
3185942a | 28 | |
0001803f CR |
29 | Read lines from the standard input into the indexed array variable ARRAY, or |
30 | from file descriptor FD if the -u option is supplied. The variable MAPFILE | |
31 | is the default ARRAY. | |
3185942a JA |
32 | |
33 | Options: | |
34 | -n count Copy at most COUNT lines. If COUNT is 0, all lines are copied. | |
35 | -O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0. | |
36 | -s count Discard the first COUNT lines read. | |
37 | -t Remove a trailing newline from each line read. | |
38 | -u fd Read lines from file descriptor FD instead of the standard input. | |
39 | -C callback Evaluate CALLBACK each time QUANTUM lines are read. | |
40 | -c quantum Specify the number of lines read between each call to CALLBACK. | |
41 | ||
42 | Arguments: | |
43 | ARRAY Array variable name to use for file data. | |
44 | ||
17345e5a JA |
45 | If -C is supplied without -c, the default quantum is 5000. When |
46 | CALLBACK is evaluated, it is supplied the index of the next array | |
47 | element to be assigned as an additional argument. | |
3185942a JA |
48 | |
49 | If not supplied with an explicit origin, mapfile will clear ARRAY before | |
50 | assigning to it. | |
51 | ||
52 | Exit Status: | |
0001803f CR |
53 | Returns success unless an invalid option is given or ARRAY is readonly or |
54 | not an indexed array. | |
3185942a JA |
55 | $END |
56 | ||
17345e5a JA |
57 | $BUILTIN readarray |
58 | $FUNCTION mapfile_builtin | |
59 | $SHORT_DOC readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] | |
60 | Read lines from a file into an array variable. | |
61 | ||
62 | A synonym for `mapfile'. | |
63 | $END | |
64 | ||
3185942a JA |
65 | #include <config.h> |
66 | ||
67 | #include "builtins.h" | |
68 | #include "posixstat.h" | |
69 | ||
70 | #if defined (HAVE_UNISTD_H) | |
71 | # include <unistd.h> | |
72 | #endif | |
73 | ||
74 | #include "bashansi.h" | |
0001803f | 75 | #include "bashintl.h" |
3185942a JA |
76 | |
77 | #include <stdio.h> | |
78 | #include <errno.h> | |
79 | ||
80 | #include "../bashintl.h" | |
81 | #include "../shell.h" | |
82 | #include "common.h" | |
83 | #include "bashgetopt.h" | |
84 | ||
3185942a JA |
85 | #if !defined (errno) |
86 | extern int errno; | |
87 | #endif | |
88 | ||
89 | #if defined (ARRAY_VARS) | |
90 | ||
91 | #define DEFAULT_ARRAY_NAME "MAPFILE" | |
92 | ||
93 | /* The value specifying how frequently `mapfile' calls the callback. */ | |
94 | #define DEFAULT_QUANTUM 5000 | |
95 | ||
96 | /* Values for FLAGS */ | |
97 | #define MAPF_CLEARARRAY 0x01 | |
98 | #define MAPF_CHOP 0x02 | |
99 | ||
100 | static int | |
101 | run_callback(callback, current_index) | |
102 | const char *callback; | |
103 | unsigned int current_index; | |
104 | { | |
105 | unsigned int execlen; | |
106 | char *execstr; | |
17345e5a | 107 | int flags; |
3185942a JA |
108 | |
109 | execlen = strlen (callback) + 10; | |
110 | /* 1 for space between %s and %d, | |
111 | another 1 for the last nul char for C string. */ | |
112 | execlen += 2; | |
113 | execstr = xmalloc (execlen); | |
114 | ||
0001803f | 115 | flags = SEVAL_NOHIST; |
17345e5a JA |
116 | #if 0 |
117 | if (interactive) | |
0001803f | 118 | flags |= SEVAL_INTERACT; |
17345e5a | 119 | #endif |
3185942a | 120 | snprintf (execstr, execlen, "%s %d", callback, current_index); |
17345e5a | 121 | return parse_and_execute(execstr, NULL, flags); |
3185942a JA |
122 | } |
123 | ||
124 | static void | |
125 | do_chop(line) | |
126 | char * line; | |
127 | { | |
128 | int length; | |
129 | ||
130 | length = strlen (line); | |
131 | if (length && line[length-1] == '\n') | |
132 | line[length-1] = '\0'; | |
133 | } | |
134 | ||
135 | static int | |
136 | mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_name, flags) | |
137 | int fd; | |
138 | long line_count_goal, origin, nskip, callback_quantum; | |
139 | char *callback, *array_name; | |
140 | int flags; | |
141 | { | |
142 | char *line; | |
143 | size_t line_length; | |
144 | unsigned int array_index, line_count; | |
145 | SHELL_VAR *entry; | |
146 | int unbuffered_read; | |
147 | ||
148 | line = NULL; | |
149 | line_length = 0; | |
150 | unbuffered_read = 0; | |
151 | ||
152 | /* The following check should be done before reading any lines. Doing it | |
153 | here allows us to call bind_array_element instead of bind_array_variable | |
154 | and skip the variable lookup on every call. */ | |
155 | entry = find_or_make_array_variable (array_name, 1); | |
17345e5a JA |
156 | if (entry == 0 || readonly_p (entry) || noassign_p (entry)) |
157 | { | |
0001803f | 158 | if (entry && readonly_p (entry)) |
17345e5a JA |
159 | err_readonly (array_name); |
160 | ||
161 | return (EXECUTION_FAILURE); | |
162 | } | |
0001803f CR |
163 | else if (array_p (entry) == 0) |
164 | { | |
165 | builtin_error (_("%s: not an indexed array"), array_name); | |
166 | return (EXECUTION_FAILURE); | |
167 | } | |
168 | ||
3185942a JA |
169 | if (flags & MAPF_CLEARARRAY) |
170 | array_flush (array_cell (entry)); | |
171 | ||
172 | #ifndef __CYGWIN__ | |
173 | unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); | |
174 | #else | |
175 | unbuffered_read = 1; | |
176 | #endif | |
177 | ||
17345e5a JA |
178 | zreset (); |
179 | ||
3185942a JA |
180 | /* Skip any lines at beginning of file? */ |
181 | for (line_count = 0; line_count < nskip; line_count++) | |
17345e5a JA |
182 | if (zgetline (fd, &line, &line_length, unbuffered_read) < 0) |
183 | break; | |
184 | ||
3185942a JA |
185 | line = 0; |
186 | line_length = 0; | |
187 | ||
188 | /* Reset the buffer for bash own stream */ | |
17345e5a JA |
189 | interrupt_immediately++; |
190 | for (array_index = origin, line_count = 1; | |
191 | zgetline (fd, &line, &line_length, unbuffered_read) != -1; | |
3185942a JA |
192 | array_index++, line_count++) |
193 | { | |
194 | /* Have we exceeded # of lines to store? */ | |
17345e5a | 195 | if (line_count_goal != 0 && line_count > line_count_goal) |
3185942a JA |
196 | break; |
197 | ||
198 | /* Remove trailing newlines? */ | |
199 | if (flags & MAPF_CHOP) | |
200 | do_chop (line); | |
201 | ||
202 | /* Has a callback been registered and if so is it time to call it? */ | |
203 | if (callback && line_count && (line_count % callback_quantum) == 0) | |
204 | { | |
205 | run_callback (callback, array_index); | |
206 | ||
207 | /* Reset the buffer for bash own stream. */ | |
208 | if (unbuffered_read == 0) | |
209 | zsyncfd (fd); | |
210 | } | |
211 | ||
212 | bind_array_element (entry, array_index, line, 0); | |
213 | } | |
214 | ||
215 | xfree (line); | |
216 | ||
217 | if (unbuffered_read == 0) | |
218 | zsyncfd (fd); | |
219 | ||
17345e5a | 220 | interrupt_immediately--; |
3185942a JA |
221 | return EXECUTION_SUCCESS; |
222 | } | |
223 | ||
224 | int | |
225 | mapfile_builtin (list) | |
226 | WORD_LIST *list; | |
227 | { | |
228 | int opt, code, fd, clear_array, flags; | |
229 | intmax_t intval; | |
230 | long lines, origin, nskip, callback_quantum; | |
231 | char *array_name, *callback; | |
232 | ||
233 | clear_array = 1; | |
234 | fd = 0; | |
235 | lines = origin = nskip = 0; | |
236 | flags = MAPF_CLEARARRAY; | |
237 | callback_quantum = DEFAULT_QUANTUM; | |
238 | callback = 0; | |
239 | ||
240 | reset_internal_getopt (); | |
241 | while ((opt = internal_getopt (list, "u:n:O:tC:c:s:")) != -1) | |
242 | { | |
243 | switch (opt) | |
244 | { | |
245 | case 'u': | |
246 | code = legal_number (list_optarg, &intval); | |
247 | if (code == 0 || intval < 0 || intval != (int)intval) | |
248 | { | |
249 | builtin_error (_("%s: invalid file descriptor specification"), list_optarg); | |
250 | return (EXECUTION_FAILURE); | |
251 | } | |
252 | else | |
253 | fd = intval; | |
254 | ||
255 | if (sh_validfd (fd) == 0) | |
256 | { | |
257 | builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno)); | |
258 | return (EXECUTION_FAILURE); | |
259 | } | |
260 | break; | |
261 | ||
262 | case 'n': | |
263 | code = legal_number (list_optarg, &intval); | |
264 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
265 | { | |
266 | builtin_error (_("%s: invalid line count"), list_optarg); | |
267 | return (EXECUTION_FAILURE); | |
268 | } | |
269 | else | |
270 | lines = intval; | |
271 | break; | |
272 | ||
273 | case 'O': | |
274 | code = legal_number (list_optarg, &intval); | |
275 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
276 | { | |
277 | builtin_error (_("%s: invalid array origin"), list_optarg); | |
278 | return (EXECUTION_FAILURE); | |
279 | } | |
280 | else | |
281 | origin = intval; | |
282 | flags &= ~MAPF_CLEARARRAY; | |
283 | break; | |
284 | case 't': | |
285 | flags |= MAPF_CHOP; | |
286 | break; | |
287 | case 'C': | |
288 | callback = list_optarg; | |
289 | break; | |
290 | case 'c': | |
291 | code = legal_number (list_optarg, &intval); | |
0001803f | 292 | if (code == 0 || intval <= 0 || intval != (unsigned)intval) |
3185942a JA |
293 | { |
294 | builtin_error (_("%s: invalid callback quantum"), list_optarg); | |
295 | return (EXECUTION_FAILURE); | |
296 | } | |
297 | else | |
298 | callback_quantum = intval; | |
299 | break; | |
300 | case 's': | |
301 | code = legal_number (list_optarg, &intval); | |
302 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
303 | { | |
304 | builtin_error (_("%s: invalid line count"), list_optarg); | |
305 | return (EXECUTION_FAILURE); | |
306 | } | |
307 | else | |
308 | nskip = intval; | |
309 | break; | |
310 | default: | |
311 | builtin_usage (); | |
312 | return (EX_USAGE); | |
313 | } | |
314 | } | |
315 | list = loptend; | |
316 | ||
317 | if (list == 0) | |
318 | array_name = DEFAULT_ARRAY_NAME; | |
319 | else if (list->word == 0 || list->word->word == 0) | |
320 | { | |
321 | builtin_error ("internal error: getting variable name"); | |
322 | return (EXECUTION_FAILURE); | |
323 | } | |
324 | else if (list->word->word[0] == '\0') | |
325 | { | |
326 | builtin_error (_("empty array variable name")); | |
327 | return (EX_USAGE); | |
328 | } | |
329 | else | |
330 | array_name = list->word->word; | |
331 | ||
332 | if (legal_identifier (array_name) == 0 && valid_array_reference (array_name) == 0) | |
333 | { | |
334 | sh_invalidid (array_name); | |
335 | return (EXECUTION_FAILURE); | |
336 | } | |
337 | ||
338 | return mapfile (fd, lines, origin, nskip, callback_quantum, callback, array_name, flags); | |
339 | } | |
340 | ||
341 | #else | |
342 | ||
343 | int | |
344 | mapfile_builtin (list) | |
345 | WORD_LIST *list; | |
346 | { | |
347 | builtin_error (_("array variable support required")); | |
348 | return (EXECUTION_FAILURE); | |
349 | } | |
350 | ||
351 | #endif /* ARRAY_VARS */ |