]>
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. | |
8868edaf | 5 | Copyright (C) 2008-2020 Free Software Foundation, Inc. |
3185942a JA |
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 | |
a0c0a00f | 26 | $SHORT_DOC mapfile [-d delim] [-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: | |
a0c0a00f CR |
34 | -d delim Use DELIM to terminate lines, instead of newline |
35 | -n count Copy at most COUNT lines. If COUNT is 0, all lines are copied | |
36 | -O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0 | |
37 | -s count Discard the first COUNT lines read | |
38 | -t Remove a trailing DELIM from each line read (default newline) | |
39 | -u fd Read lines from file descriptor FD instead of the standard input | |
40 | -C callback Evaluate CALLBACK each time QUANTUM lines are read | |
41 | -c quantum Specify the number of lines read between each call to | |
42 | CALLBACK | |
3185942a JA |
43 | |
44 | Arguments: | |
a0c0a00f | 45 | ARRAY Array variable name to use for file data |
3185942a | 46 | |
17345e5a JA |
47 | If -C is supplied without -c, the default quantum is 5000. When |
48 | CALLBACK is evaluated, it is supplied the index of the next array | |
495aee44 CR |
49 | element to be assigned and the line to be assigned to that element |
50 | as additional arguments. | |
3185942a JA |
51 | |
52 | If not supplied with an explicit origin, mapfile will clear ARRAY before | |
53 | assigning to it. | |
54 | ||
55 | Exit Status: | |
0001803f CR |
56 | Returns success unless an invalid option is given or ARRAY is readonly or |
57 | not an indexed array. | |
3185942a JA |
58 | $END |
59 | ||
17345e5a JA |
60 | $BUILTIN readarray |
61 | $FUNCTION mapfile_builtin | |
d233b485 | 62 | $SHORT_DOC readarray [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] |
17345e5a JA |
63 | Read lines from a file into an array variable. |
64 | ||
65 | A synonym for `mapfile'. | |
66 | $END | |
67 | ||
3185942a JA |
68 | #include <config.h> |
69 | ||
70 | #include "builtins.h" | |
71 | #include "posixstat.h" | |
72 | ||
73 | #if defined (HAVE_UNISTD_H) | |
74 | # include <unistd.h> | |
75 | #endif | |
76 | ||
77 | #include "bashansi.h" | |
0001803f | 78 | #include "bashintl.h" |
3185942a JA |
79 | |
80 | #include <stdio.h> | |
81 | #include <errno.h> | |
82 | ||
83 | #include "../bashintl.h" | |
84 | #include "../shell.h" | |
85 | #include "common.h" | |
86 | #include "bashgetopt.h" | |
87 | ||
3185942a JA |
88 | #if !defined (errno) |
89 | extern int errno; | |
90 | #endif | |
91 | ||
92 | #if defined (ARRAY_VARS) | |
93 | ||
8868edaf | 94 | static int run_callback PARAMS((const char *, unsigned int, const char *)); |
495aee44 | 95 | |
3185942a | 96 | #define DEFAULT_ARRAY_NAME "MAPFILE" |
495aee44 | 97 | #define DEFAULT_VARIABLE_NAME "MAPLINE" /* not used right now */ |
3185942a JA |
98 | |
99 | /* The value specifying how frequently `mapfile' calls the callback. */ | |
100 | #define DEFAULT_QUANTUM 5000 | |
101 | ||
102 | /* Values for FLAGS */ | |
103 | #define MAPF_CLEARARRAY 0x01 | |
104 | #define MAPF_CHOP 0x02 | |
105 | ||
a0c0a00f CR |
106 | static int delim; |
107 | ||
3185942a | 108 | static int |
495aee44 | 109 | run_callback (callback, curindex, curline) |
3185942a | 110 | const char *callback; |
495aee44 CR |
111 | unsigned int curindex; |
112 | const char *curline; | |
3185942a JA |
113 | { |
114 | unsigned int execlen; | |
495aee44 | 115 | char *execstr, *qline; |
17345e5a | 116 | int flags; |
3185942a | 117 | |
495aee44 CR |
118 | qline = sh_single_quote (curline); |
119 | execlen = strlen (callback) + strlen (qline) + 10; | |
120 | /* 1 for each space between %s and %d, | |
3185942a | 121 | another 1 for the last nul char for C string. */ |
495aee44 | 122 | execlen += 3; |
3185942a JA |
123 | execstr = xmalloc (execlen); |
124 | ||
0001803f | 125 | flags = SEVAL_NOHIST; |
17345e5a JA |
126 | #if 0 |
127 | if (interactive) | |
0001803f | 128 | flags |= SEVAL_INTERACT; |
17345e5a | 129 | #endif |
495aee44 CR |
130 | snprintf (execstr, execlen, "%s %d %s", callback, curindex, qline); |
131 | free (qline); | |
ac50fbac | 132 | return evalstring (execstr, NULL, flags); |
3185942a JA |
133 | } |
134 | ||
135 | static void | |
a0c0a00f CR |
136 | do_chop(line, delim) |
137 | char *line; | |
138 | unsigned char delim; | |
3185942a JA |
139 | { |
140 | int length; | |
141 | ||
142 | length = strlen (line); | |
a0c0a00f | 143 | if (length && line[length-1] == delim) |
3185942a JA |
144 | line[length-1] = '\0'; |
145 | } | |
146 | ||
147 | static int | |
a0c0a00f | 148 | mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_name, delim, flags) |
3185942a JA |
149 | int fd; |
150 | long line_count_goal, origin, nskip, callback_quantum; | |
151 | char *callback, *array_name; | |
a0c0a00f | 152 | int delim; |
3185942a JA |
153 | int flags; |
154 | { | |
155 | char *line; | |
156 | size_t line_length; | |
157 | unsigned int array_index, line_count; | |
158 | SHELL_VAR *entry; | |
159 | int unbuffered_read; | |
160 | ||
161 | line = NULL; | |
162 | line_length = 0; | |
163 | unbuffered_read = 0; | |
164 | ||
165 | /* The following check should be done before reading any lines. Doing it | |
166 | here allows us to call bind_array_element instead of bind_array_variable | |
167 | and skip the variable lookup on every call. */ | |
168 | entry = find_or_make_array_variable (array_name, 1); | |
17345e5a JA |
169 | if (entry == 0 || readonly_p (entry) || noassign_p (entry)) |
170 | { | |
0001803f | 171 | if (entry && readonly_p (entry)) |
17345e5a JA |
172 | err_readonly (array_name); |
173 | ||
174 | return (EXECUTION_FAILURE); | |
175 | } | |
0001803f CR |
176 | else if (array_p (entry) == 0) |
177 | { | |
178 | builtin_error (_("%s: not an indexed array"), array_name); | |
179 | return (EXECUTION_FAILURE); | |
180 | } | |
ac50fbac CR |
181 | else if (invisible_p (entry)) |
182 | VUNSETATTR (entry, att_invisible); /* no longer invisible */ | |
0001803f | 183 | |
3185942a JA |
184 | if (flags & MAPF_CLEARARRAY) |
185 | array_flush (array_cell (entry)); | |
186 | ||
187 | #ifndef __CYGWIN__ | |
188 | unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); | |
189 | #else | |
190 | unbuffered_read = 1; | |
191 | #endif | |
192 | ||
a0c0a00f CR |
193 | if (delim != '\n') |
194 | unbuffered_read = 1; | |
195 | ||
17345e5a JA |
196 | zreset (); |
197 | ||
3185942a JA |
198 | /* Skip any lines at beginning of file? */ |
199 | for (line_count = 0; line_count < nskip; line_count++) | |
a0c0a00f | 200 | if (zgetline (fd, &line, &line_length, delim, unbuffered_read) < 0) |
17345e5a JA |
201 | break; |
202 | ||
3185942a JA |
203 | line = 0; |
204 | line_length = 0; | |
205 | ||
206 | /* Reset the buffer for bash own stream */ | |
17345e5a | 207 | for (array_index = origin, line_count = 1; |
a0c0a00f | 208 | zgetline (fd, &line, &line_length, delim, unbuffered_read) != -1; |
8e9dc917 | 209 | array_index++) |
3185942a | 210 | { |
3185942a JA |
211 | /* Remove trailing newlines? */ |
212 | if (flags & MAPF_CHOP) | |
a0c0a00f | 213 | do_chop (line, delim); |
3185942a JA |
214 | |
215 | /* Has a callback been registered and if so is it time to call it? */ | |
216 | if (callback && line_count && (line_count % callback_quantum) == 0) | |
217 | { | |
495aee44 | 218 | run_callback (callback, array_index, line); |
3185942a JA |
219 | |
220 | /* Reset the buffer for bash own stream. */ | |
221 | if (unbuffered_read == 0) | |
222 | zsyncfd (fd); | |
223 | } | |
224 | ||
ac50fbac CR |
225 | /* XXX - bad things can happen if the callback modifies ENTRY, e.g., |
226 | unsetting it or changing it to a non-indexed-array type. */ | |
3185942a | 227 | bind_array_element (entry, array_index, line, 0); |
8e9dc917 CR |
228 | |
229 | /* Have we exceeded # of lines to store? */ | |
230 | line_count++; | |
231 | if (line_count_goal != 0 && line_count > line_count_goal) | |
232 | break; | |
3185942a JA |
233 | } |
234 | ||
d233b485 | 235 | free (line); |
3185942a JA |
236 | |
237 | if (unbuffered_read == 0) | |
238 | zsyncfd (fd); | |
239 | ||
240 | return EXECUTION_SUCCESS; | |
241 | } | |
242 | ||
243 | int | |
244 | mapfile_builtin (list) | |
245 | WORD_LIST *list; | |
246 | { | |
d233b485 | 247 | int opt, code, fd, flags; |
3185942a JA |
248 | intmax_t intval; |
249 | long lines, origin, nskip, callback_quantum; | |
250 | char *array_name, *callback; | |
251 | ||
3185942a JA |
252 | fd = 0; |
253 | lines = origin = nskip = 0; | |
254 | flags = MAPF_CLEARARRAY; | |
255 | callback_quantum = DEFAULT_QUANTUM; | |
256 | callback = 0; | |
a0c0a00f | 257 | delim = '\n'; |
3185942a JA |
258 | |
259 | reset_internal_getopt (); | |
a0c0a00f | 260 | while ((opt = internal_getopt (list, "d:u:n:O:tC:c:s:")) != -1) |
3185942a JA |
261 | { |
262 | switch (opt) | |
263 | { | |
a0c0a00f CR |
264 | case 'd': |
265 | delim = *list_optarg; | |
266 | break; | |
3185942a JA |
267 | case 'u': |
268 | code = legal_number (list_optarg, &intval); | |
269 | if (code == 0 || intval < 0 || intval != (int)intval) | |
270 | { | |
271 | builtin_error (_("%s: invalid file descriptor specification"), list_optarg); | |
272 | return (EXECUTION_FAILURE); | |
273 | } | |
274 | else | |
275 | fd = intval; | |
276 | ||
277 | if (sh_validfd (fd) == 0) | |
278 | { | |
279 | builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno)); | |
280 | return (EXECUTION_FAILURE); | |
281 | } | |
282 | break; | |
283 | ||
284 | case 'n': | |
285 | code = legal_number (list_optarg, &intval); | |
286 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
287 | { | |
288 | builtin_error (_("%s: invalid line count"), list_optarg); | |
289 | return (EXECUTION_FAILURE); | |
290 | } | |
291 | else | |
292 | lines = intval; | |
293 | break; | |
294 | ||
295 | case 'O': | |
296 | code = legal_number (list_optarg, &intval); | |
297 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
298 | { | |
299 | builtin_error (_("%s: invalid array origin"), list_optarg); | |
300 | return (EXECUTION_FAILURE); | |
301 | } | |
302 | else | |
303 | origin = intval; | |
304 | flags &= ~MAPF_CLEARARRAY; | |
305 | break; | |
306 | case 't': | |
307 | flags |= MAPF_CHOP; | |
308 | break; | |
309 | case 'C': | |
310 | callback = list_optarg; | |
311 | break; | |
312 | case 'c': | |
313 | code = legal_number (list_optarg, &intval); | |
0001803f | 314 | if (code == 0 || intval <= 0 || intval != (unsigned)intval) |
3185942a JA |
315 | { |
316 | builtin_error (_("%s: invalid callback quantum"), list_optarg); | |
317 | return (EXECUTION_FAILURE); | |
318 | } | |
319 | else | |
320 | callback_quantum = intval; | |
321 | break; | |
322 | case 's': | |
323 | code = legal_number (list_optarg, &intval); | |
324 | if (code == 0 || intval < 0 || intval != (unsigned)intval) | |
325 | { | |
326 | builtin_error (_("%s: invalid line count"), list_optarg); | |
327 | return (EXECUTION_FAILURE); | |
328 | } | |
329 | else | |
330 | nskip = intval; | |
331 | break; | |
a0c0a00f | 332 | CASE_HELPOPT; |
3185942a JA |
333 | default: |
334 | builtin_usage (); | |
335 | return (EX_USAGE); | |
336 | } | |
337 | } | |
338 | list = loptend; | |
339 | ||
340 | if (list == 0) | |
341 | array_name = DEFAULT_ARRAY_NAME; | |
342 | else if (list->word == 0 || list->word->word == 0) | |
343 | { | |
344 | builtin_error ("internal error: getting variable name"); | |
345 | return (EXECUTION_FAILURE); | |
346 | } | |
347 | else if (list->word->word[0] == '\0') | |
348 | { | |
349 | builtin_error (_("empty array variable name")); | |
350 | return (EX_USAGE); | |
351 | } | |
352 | else | |
353 | array_name = list->word->word; | |
354 | ||
a0c0a00f | 355 | if (legal_identifier (array_name) == 0) |
3185942a JA |
356 | { |
357 | sh_invalidid (array_name); | |
358 | return (EXECUTION_FAILURE); | |
359 | } | |
360 | ||
a0c0a00f | 361 | return mapfile (fd, lines, origin, nskip, callback_quantum, callback, array_name, delim, flags); |
3185942a JA |
362 | } |
363 | ||
364 | #else | |
365 | ||
366 | int | |
367 | mapfile_builtin (list) | |
368 | WORD_LIST *list; | |
369 | { | |
370 | builtin_error (_("array variable support required")); | |
371 | return (EXECUTION_FAILURE); | |
372 | } | |
373 | ||
374 | #endif /* ARRAY_VARS */ |