This file is mapfile.def, from which is created mapfile.c. It implements the builtin "mapfile" in Bash. Copyright (C) 2005-2006 Rocky Bernstein for Free Software Foundation, Inc. Copyright (C) 2008,2009 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see . $PRODUCES mapfile.c $BUILTIN mapfile $FUNCTION mapfile_builtin $SHORT_DOC mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] Read lines from a file into an array variable. Read lines from the standard input into the array variable ARRAY, or from file descriptor FD if the -u option is supplied. The variable MAPFILE is the default ARRAY. Options: -n count Copy at most COUNT lines. If COUNT is 0, all lines are copied. -O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0. -s count Discard the first COUNT lines read. -t Remove a trailing newline from each line read. -u fd Read lines from file descriptor FD instead of the standard input. -C callback Evaluate CALLBACK each time QUANTUM lines are read. -c quantum Specify the number of lines read between each call to CALLBACK. Arguments: ARRAY Array variable name to use for file data. If -C is supplied without -c, the default quantum is 5000. If not supplied with an explicit origin, mapfile will clear ARRAY before assigning to it. Exit Status: Returns success unless an invalid option is given or ARRAY is readonly. $END #include #include "builtins.h" #include "posixstat.h" #if defined (HAVE_UNISTD_H) # include #endif #include "bashansi.h" #include #include #include "../bashintl.h" #include "../shell.h" #include "common.h" #include "bashgetopt.h" #if !defined (errno) extern int errno; #endif #if defined (ARRAY_VARS) #define DEFAULT_ARRAY_NAME "MAPFILE" /* The value specifying how frequently `mapfile' calls the callback. */ #define DEFAULT_QUANTUM 5000 /* Values for FLAGS */ #define MAPF_CLEARARRAY 0x01 #define MAPF_CHOP 0x02 static int run_callback(callback, current_index) const char *callback; unsigned int current_index; { unsigned int execlen; char *execstr; execlen = strlen (callback) + 10; /* 1 for space between %s and %d, another 1 for the last nul char for C string. */ execlen += 2; execstr = xmalloc (execlen); snprintf (execstr, execlen, "%s %d", callback, current_index); return parse_and_execute(execstr, NULL, 0); } static void do_chop(line) char * line; { int length; length = strlen (line); if (length && line[length-1] == '\n') line[length-1] = '\0'; } static int mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_name, flags) int fd; long line_count_goal, origin, nskip, callback_quantum; char *callback, *array_name; int flags; { char *line; size_t line_length; unsigned int array_index, line_count; SHELL_VAR *entry; int unbuffered_read; line = NULL; line_length = 0; unbuffered_read = 0; /* The following check should be done before reading any lines. Doing it here allows us to call bind_array_element instead of bind_array_variable and skip the variable lookup on every call. */ entry = find_or_make_array_variable (array_name, 1); if (entry == 0) return (EXECUTION_FAILURE); if (flags & MAPF_CLEARARRAY) array_flush (array_cell (entry)); #ifndef __CYGWIN__ unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); #else unbuffered_read = 1; #endif /* Skip any lines at beginning of file? */ for (line_count = 0; line_count < nskip; line_count++) zgetline(fd, &line, &line_length, unbuffered_read); line = 0; line_length = 0; /* Reset the buffer for bash own stream */ for (array_index = origin, line_count = 0; zgetline(fd, &line, &line_length, unbuffered_read) != -1; array_index++, line_count++) { /* Have we exceeded # of lines to store? */ if (line_count_goal != 0 && line_count >= line_count_goal) break; /* Remove trailing newlines? */ if (flags & MAPF_CHOP) do_chop (line); /* Has a callback been registered and if so is it time to call it? */ if (callback && line_count && (line_count % callback_quantum) == 0) { run_callback (callback, array_index); /* Reset the buffer for bash own stream. */ if (unbuffered_read == 0) zsyncfd (fd); } bind_array_element (entry, array_index, line, 0); } xfree (line); if (unbuffered_read == 0) zsyncfd (fd); return EXECUTION_SUCCESS; } int mapfile_builtin (list) WORD_LIST *list; { int opt, code, fd, clear_array, flags; intmax_t intval; long lines, origin, nskip, callback_quantum; char *array_name, *callback; clear_array = 1; fd = 0; lines = origin = nskip = 0; flags = MAPF_CLEARARRAY; callback_quantum = DEFAULT_QUANTUM; callback = 0; reset_internal_getopt (); while ((opt = internal_getopt (list, "u:n:O:tC:c:s:")) != -1) { switch (opt) { case 'u': code = legal_number (list_optarg, &intval); if (code == 0 || intval < 0 || intval != (int)intval) { builtin_error (_("%s: invalid file descriptor specification"), list_optarg); return (EXECUTION_FAILURE); } else fd = intval; if (sh_validfd (fd) == 0) { builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno)); return (EXECUTION_FAILURE); } break; case 'n': code = legal_number (list_optarg, &intval); if (code == 0 || intval < 0 || intval != (unsigned)intval) { builtin_error (_("%s: invalid line count"), list_optarg); return (EXECUTION_FAILURE); } else lines = intval; break; case 'O': code = legal_number (list_optarg, &intval); if (code == 0 || intval < 0 || intval != (unsigned)intval) { builtin_error (_("%s: invalid array origin"), list_optarg); return (EXECUTION_FAILURE); } else origin = intval; flags &= ~MAPF_CLEARARRAY; break; case 't': flags |= MAPF_CHOP; break; case 'C': callback = list_optarg; break; case 'c': code = legal_number (list_optarg, &intval); if (code == 0 || intval < 0 || intval != (unsigned)intval) { builtin_error (_("%s: invalid callback quantum"), list_optarg); return (EXECUTION_FAILURE); } else callback_quantum = intval; break; case 's': code = legal_number (list_optarg, &intval); if (code == 0 || intval < 0 || intval != (unsigned)intval) { builtin_error (_("%s: invalid line count"), list_optarg); return (EXECUTION_FAILURE); } else nskip = intval; break; default: builtin_usage (); return (EX_USAGE); } } list = loptend; if (list == 0) array_name = DEFAULT_ARRAY_NAME; else if (list->word == 0 || list->word->word == 0) { builtin_error ("internal error: getting variable name"); return (EXECUTION_FAILURE); } else if (list->word->word[0] == '\0') { builtin_error (_("empty array variable name")); return (EX_USAGE); } else array_name = list->word->word; if (legal_identifier (array_name) == 0 && valid_array_reference (array_name) == 0) { sh_invalidid (array_name); return (EXECUTION_FAILURE); } return mapfile (fd, lines, origin, nskip, callback_quantum, callback, array_name, flags); } #else int mapfile_builtin (list) WORD_LIST *list; { builtin_error (_("array variable support required")); return (EXECUTION_FAILURE); } #endif /* ARRAY_VARS */