X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Frepeat.c;h=f71409147cd5f167deb680a523f75b6ae7d811d7;hb=5fa244a7b0fcbd971e760cb67644514fe22aa4a3;hp=bc4b8295b74b7a5fedbd37fd5c4ef6edbb627fa2;hpb=81fff61a96bece351e381ad3fef8ab1248a952ba;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index bc4b8295b7..f71409147c 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -1,582 +1,418 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2007, 2009-2012 Free Software Foundation, Inc. - This program 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 2 of the - License, or (at your option) any later version. + This program 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. - This program 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. + This program 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 this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "repeat.h" - -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "intprops.h" +#include "data/dataset.h" +#include "data/dictionary.h" +#include "data/settings.h" +#include "language/command.h" +#include "language/lexer/lexer.h" +#include "language/lexer/segment.h" +#include "language/lexer/token.h" +#include "language/lexer/variable-parser.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/hash-functions.h" +#include "libpspp/hmap.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "libpspp/str.h" +#include "libpspp/misc.h" +#include "output/output-item.h" + +#include "gl/ftoastr.h" +#include "gl/minmax.h" +#include "gl/xalloc.h" +#include "gl/xmemdup0.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Defines a list of lines used by DO REPEAT. */ -struct line_list - { - struct line_list *next; /* Next line. */ - char *file_name; /* File name. */ - int line_number; /* Line number. */ - char *line; /* Contents. */ - }; - -/* The type of substitution made for a DO REPEAT macro. */ -enum repeat_entry_type - { - VAR_NAMES, - OTHER - }; - -/* Describes one DO REPEAT macro. */ -struct repeat_entry - { - struct repeat_entry *next; /* Next entry. */ - enum repeat_entry_type type; /* Types of replacements. */ - char id[LONG_NAME_LEN + 1]; /* Macro identifier. */ - char **replacement; /* Macro replacement. */ - }; - -/* A DO REPEAT...END REPEAT block. */ -struct repeat_block +struct dummy_var { - struct pool *pool; /* Pool used for storage. */ - struct line_list *first_line; /* First line in line buffer. */ - struct line_list *cur_line; /* Current line in line buffer. */ - int loop_cnt; /* Number of loops. */ - int loop_idx; /* Number of loops so far. */ - struct repeat_entry *macros; /* Pointer to macro table. */ - bool print; /* Print lines as executed? */ + struct hmap_node hmap_node; + struct substring name; + char **values; + size_t n_values; + int start_ofs, end_ofs; }; -static bool parse_specification (struct repeat_block *); -static bool parse_lines (struct repeat_block *); -static void create_vars (struct repeat_block *); +static bool parse_specification (struct lexer *, struct dictionary *, + struct hmap *dummies); +static bool parse_commands (struct lexer *, struct hmap *dummies); +static void destroy_dummies (struct hmap *dummies); -static int parse_ids (struct repeat_entry *); -static int parse_numbers (struct repeat_entry *); -static int parse_strings (struct repeat_entry *); - -static void do_repeat_filter (struct string *line, void *block); -static bool do_repeat_read (struct string *line, char **file_name, - int *line_number, void *block); -static void do_repeat_close (void *block); +static bool parse_ids (struct lexer *, const struct dictionary *, + struct dummy_var *); +static bool parse_numbers (struct lexer *, struct dummy_var *); +static bool parse_strings (struct lexer *, struct dummy_var *); int -cmd_do_repeat (void) +cmd_do_repeat (struct lexer *lexer, struct dataset *ds) { - struct repeat_block *block; + struct hmap dummies = HMAP_INITIALIZER (dummies); + bool ok = parse_specification (lexer, dataset_dict (ds), &dummies); + ok = parse_commands (lexer, &dummies) && ok; + destroy_dummies (&dummies); - block = pool_create_container (struct repeat_block, pool); + return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; +} - if (!parse_specification (block) || !parse_lines (block)) - goto error; - - create_vars (block); - - block->cur_line = NULL; - block->loop_idx = -1; - getl_include_filter (do_repeat_filter, do_repeat_close, block); - getl_include_function (do_repeat_read, NULL, block); +static const struct dummy_var * +find_dummy_var (struct hmap *hmap, struct substring name) +{ + const struct dummy_var *dv; - return CMD_SUCCESS; + HMAP_FOR_EACH_WITH_HASH (dv, struct dummy_var, hmap_node, + utf8_hash_case_substring (name, 0), hmap) + if (!utf8_sscasecmp (dv->name, name)) + return dv; - error: - pool_destroy (block->pool); - return CMD_CASCADING_FAILURE; + return NULL; } /* Parses the whole DO REPEAT command specification. Returns success. */ static bool -parse_specification (struct repeat_block *block) +parse_specification (struct lexer *lexer, struct dictionary *dict, + struct hmap *dummies) { - char first_name[LONG_NAME_LEN + 1]; + struct dummy_var *first_dv = NULL; - block->loop_cnt = 0; - block->macros = NULL; do { - struct repeat_entry *e; - struct repeat_entry *iter; - int count; + int start_ofs = lex_ofs (lexer); /* Get a stand-in variable name and make sure it's unique. */ - if (!lex_force_id ()) - return false; - if (dict_lookup_var (default_dict, tokid)) - msg (SW, _("Dummy variable name \"%s\" hides dictionary " - "variable \"%s\"."), - tokid, tokid); - for (iter = block->macros; iter != NULL; iter = iter->next) - if (!strcasecmp (iter->id, tokid)) - { - msg (SE, _("Dummy variable name \"%s\" is given twice."), tokid); - return false; - } - - /* Make a new stand-in variable entry and link it into the - list. */ - e = pool_alloc (block->pool, sizeof *e); - e->next = block->macros; - strcpy (e->id, tokid); - block->macros = e; + if (!lex_force_id (lexer)) + goto error; + struct substring name = lex_tokss (lexer); + if (dict_lookup_var (dict, name.string)) + lex_msg (lexer, SW, + _("Dummy variable name `%s' hides dictionary variable `%s'."), + name.string, name.string); + if (find_dummy_var (dummies, name)) + { + lex_error (lexer, _("Dummy variable name `%s' is given twice."), + name.string); + goto error; + } + + /* Make a new macro. */ + struct dummy_var *dv = xmalloc (sizeof *dv); + *dv = (struct dummy_var) { + .name = ss_clone (name), + .start_ofs = start_ofs, + }; + hmap_insert (dummies, &dv->hmap_node, utf8_hash_case_substring (name, 0)); /* Skip equals sign. */ - lex_get (); - if (!lex_force_match ('=')) - return false; + lex_get (lexer); + if (!lex_force_match (lexer, T_EQUALS)) + goto error; /* Get the details of the variable's possible values. */ - if (token == T_ID) - count = parse_ids (e); - else if (lex_is_number ()) - count = parse_numbers (e); - else if (token == T_STRING) - count = parse_strings (e); + bool ok; + if (lex_token (lexer) == T_ID || lex_token (lexer) == T_ALL) + ok = parse_ids (lexer, dict, dv); + else if (lex_is_number (lexer)) + ok = parse_numbers (lexer, dv); + else if (lex_is_string (lexer)) + ok = parse_strings (lexer, dv); else { - lex_error (NULL); - return false; + lex_error (lexer, _("Syntax error expecting substitution values.")); + goto error; } - if (count == 0) - return false; + if (!ok) + goto error; + assert (dv->n_values > 0); + if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD) + { + lex_error (lexer, _("Syntax error expecting `/' or end of command.")); + goto error; + } + dv->end_ofs = lex_ofs (lexer) - 1; - /* If this is the first variable then it defines how many - replacements there must be; otherwise enforce this number of - replacements. */ - if (block->loop_cnt == 0) + /* If this is the first variable then it defines how many replacements + there must be; otherwise enforce this number of replacements. */ + if (first_dv == NULL) + first_dv = dv; + else if (first_dv->n_values != dv->n_values) { - block->loop_cnt = count; - strcpy (first_name, e->id); - } - else if (block->loop_cnt != count) - { - msg (SE, _("Dummy variable \"%s\" had %d " - "substitutions, so \"%s\" must also, but %d " - "were specified."), - first_name, block->loop_cnt, e->id, count); - return false; + msg (SE, _("Each dummy variable must have the same number of " + "substitutions.")); + + lex_ofs_msg (lexer, SN, first_dv->start_ofs, first_dv->end_ofs, + ngettext ("Dummy variable %s had %zu substitution.", + "Dummy variable %s had %zu substitutions.", + first_dv->n_values), + first_dv->name.string, first_dv->n_values); + lex_ofs_msg (lexer, SN, dv->start_ofs, dv->end_ofs, + ngettext ("Dummy variable %s had %zu substitution.", + "Dummy variable %s had %zu substitutions.", + dv->n_values), + dv->name.string, dv->n_values); + goto error; } - lex_match ('/'); + lex_match (lexer, T_SLASH); } - while (token != '.'); + while (!lex_match (lexer, T_ENDCMD)); - return true; -} + while (lex_match (lexer, T_ENDCMD)) + continue; -/* If KEYWORD appears beginning at CP, possibly preceded by white - space, returns a pointer to the character just after the - keyword. Otherwise, returns a null pointer. */ -static const char * -recognize_keyword (const char *cp, const char *keyword) -{ - const char *end; - - while (isspace ((unsigned char) *cp)) - cp++; + return true; - end = lex_skip_identifier (cp); - if (end != cp - && lex_id_match_len (keyword, strlen (keyword), cp, end - cp)) - return end; - else - return NULL; +error: + lex_discard_rest_of_command (lexer); + while (lex_match (lexer, T_ENDCMD)) + continue; + destroy_dummies (dummies); + hmap_init (dummies); + return false; } -/* Returns CP, advanced past a '+' or '-' if present. */ -static const char * -skip_indentor (const char *cp) +static size_t +count_values (struct hmap *dummies) { - if (*cp == '+' || *cp == '-') - cp++; - return cp; + if (hmap_is_empty (dummies)) + return 0; + const struct dummy_var *dv = HMAP_FIRST (struct dummy_var, hmap_node, dummies); + return dv->n_values; } -/* Returns true if LINE contains a DO REPEAT command, false - otherwise. */ -static bool -recognize_do_repeat (const char *line) +static void +do_parse_commands (struct substring s, enum segmenter_mode mode, + struct hmap *dummies, + struct string *outputs, size_t n_outputs) { - const char *cp = recognize_keyword (skip_indentor (line), "do"); - return cp != NULL && recognize_keyword (cp, "repeat") != NULL; + struct segmenter segmenter = segmenter_init (mode, false); + while (!ss_is_empty (s)) + { + enum segment_type type; + int n = segmenter_push (&segmenter, s.string, s.length, true, &type); + assert (n >= 0); + + if (type == SEG_DO_REPEAT_COMMAND) + { + for (;;) + { + int k = segmenter_push (&segmenter, s.string + n, s.length - n, + true, &type); + if (type != SEG_NEWLINE && type != SEG_DO_REPEAT_COMMAND) + break; + + n += k; + } + + do_parse_commands (ss_head (s, n), mode, dummies, + outputs, n_outputs); + } + else if (type != SEG_END) + { + const struct dummy_var *dv + = (type == SEG_IDENTIFIER ? find_dummy_var (dummies, ss_head (s, n)) + : NULL); + for (size_t i = 0; i < n_outputs; i++) + if (dv != NULL) + ds_put_cstr (&outputs[i], dv->values[i]); + else + ds_put_substring (&outputs[i], ss_head (s, n)); + } + + ss_advance (&s, n); + } } -/* Returns true if LINE contains an END REPEAT command, false - otherwise. Sets *PRINT to true for END REPEAT PRINT, false - otherwise. */ static bool -recognize_end_repeat (const char *line, bool *print) +parse_commands (struct lexer *lexer, struct hmap *dummies) { - const char *cp = recognize_keyword (skip_indentor (line), "end"); - if (cp == NULL) - return false; + char *file_name = xstrdup_if_nonnull (lex_get_file_name (lexer)); + int line_number = lex_ofs_start_point (lexer, lex_ofs (lexer)).line; - cp = recognize_keyword (cp, "repeat"); - if (cp == NULL) - return false; + struct string input = DS_EMPTY_INITIALIZER; + while (lex_is_string (lexer)) + { + ds_put_substring (&input, lex_tokss (lexer)); + ds_put_byte (&input, '\n'); + lex_get (lexer); + } - *print = recognize_keyword (cp, "print"); - return true; -} + size_t n_values = count_values (dummies); + struct string *outputs = xmalloc (n_values * sizeof *outputs); + for (size_t i = 0; i < n_values; i++) + ds_init_empty (&outputs[i]); -/* Read all the lines we are going to substitute, inside the DO - REPEAT...END REPEAT block. */ -static bool -parse_lines (struct repeat_block *block) -{ - char *previous_file_name; - struct line_list **last_line; - int nesting_level; + do_parse_commands (ds_ss (&input), lex_get_syntax_mode (lexer), + dummies, outputs, n_values); - previous_file_name = NULL; - block->first_line = NULL; - last_line = &block->first_line; - nesting_level = 0; + ds_destroy (&input); - for (;;) - { - const char *cur_file_name; - int cur_line_number; - struct line_list *line; - bool dot; + while (lex_match (lexer, T_ENDCMD)) + continue; - if (!getl_read_line (NULL)) - return false; + bool ok = lex_match_phrase (lexer, "END REPEAT"); + if (!ok) + lex_error (lexer, _("Syntax error expecting END REPEAT.")); + bool print = ok && lex_match_id (lexer, "PRINT"); + lex_discard_rest_of_command (lexer); - /* If the current file has changed then record the fact. */ - getl_location (&cur_file_name, &cur_line_number); - if (previous_file_name == NULL - || !strcmp (cur_file_name, previous_file_name)) - previous_file_name = pool_strdup (block->pool, cur_file_name); - - ds_rtrim_spaces (&getl_buf); - dot = ds_chomp (&getl_buf, get_endcmd ()); - if (recognize_do_repeat (ds_c_str (&getl_buf))) - nesting_level++; - else if (recognize_end_repeat (ds_c_str (&getl_buf), &block->print)) + for (size_t i = 0; i < n_values; i++) + { + struct string *output = &outputs[n_values - i - 1]; + if (print) { - if (nesting_level-- == 0) - { - lex_discard_line (); - return true; - } + struct substring s = output->ss; + ss_chomp_byte (&s, '\n'); + char *label = xasprintf (_("Expansion %zu of %zu"), i + 1, n_values); + output_item_submit ( + text_item_create_nocopy (TEXT_ITEM_LOG, ss_xstrdup (s), label)); } - if (dot) - ds_putc (&getl_buf, get_endcmd ()); - - line = *last_line = pool_alloc (block->pool, sizeof *line); - line->next = NULL; - line->file_name = previous_file_name; - line->line_number = cur_line_number; - line->line = pool_strdup (block->pool, ds_c_str (&getl_buf)); - last_line = &line->next; + const char *encoding = lex_get_encoding (lexer); + struct lex_reader *reader = lex_reader_for_substring_nocopy (ds_ss (output), encoding); + lex_reader_set_file_name (reader, file_name); + reader->line_number = line_number; + lex_include (lexer, reader); } + free (file_name); + free (outputs); - lex_discard_line (); - return true; + return ok; } -/* Creates variables for the given DO REPEAT. */ static void -create_vars (struct repeat_block *block) -{ - struct repeat_entry *iter; - - for (iter = block->macros; iter; iter = iter->next) - if (iter->type == VAR_NAMES) - { - int i; - - for (i = 0; i < block->loop_cnt; i++) - { - /* Ignore return value: if the variable already - exists there is no harm done. */ - dict_create_var (default_dict, iter->replacement[i], 0); - } - } -} - -/* Parses a set of ids for DO REPEAT. */ -static int -parse_ids (struct repeat_entry *e) +destroy_dummies (struct hmap *dummies) { - size_t i; - size_t n = 0; + struct dummy_var *dv, *next; - e->type = VAR_NAMES; - e->replacement = NULL; - - do + HMAP_FOR_EACH_SAFE (dv, next, struct dummy_var, hmap_node, dummies) { - char **names; - size_t nnames; + hmap_delete (dummies, &dv->hmap_node); - if (!parse_mixed_vars (&names, &nnames, PV_NONE)) - return 0; - - e->replacement = xnrealloc (e->replacement, - nnames + n, sizeof *e->replacement); - for (i = 0; i < nnames; i++) - { - e->replacement[n + i] = xstrdup (names[i]); - free (names[i]); - } - free (names); - n += nnames; + ss_dealloc (&dv->name); + for (size_t i = 0; i < dv->n_values; i++) + free (dv->values[i]); + free (dv->values); + free (dv); } - while (token != '/' && token != '.'); - - return n; + hmap_destroy (dummies); } -/* Stores VALUE into *REPL. */ -static inline void -store_numeric (char **repl, long value) +/* Parses a set of ids for DO REPEAT. */ +static bool +parse_ids (struct lexer *lexer, const struct dictionary *dict, + struct dummy_var *dv) { - *repl = xmalloc (INT_STRLEN_BOUND (value) + 1); - sprintf (*repl, "%ld", value); + return parse_mixed_vars (lexer, dict, &dv->values, &dv->n_values, PV_NONE); } -/* Parses a list of numbers for DO REPEAT. */ -static int -parse_numbers (struct repeat_entry *e) +/* Adds REPLACEMENT to MACRO's list of replacements, which has + *USED elements and has room for *ALLOCATED. Allocates memory + from POOL. */ +static void +add_replacement (struct dummy_var *dv, char *value, size_t *allocated) { - /* First and last numbers for TO, plus the step factor. */ - long a, b; - - /* Alias to e->replacement. */ - char **array; - - /* Number of entries in array; maximum number for this allocation - size. */ - int n, m; + if (dv->n_values == *allocated) + dv->values = x2nrealloc (dv->values, allocated, sizeof *dv->values); + dv->values[dv->n_values++] = value; +} - n = m = 0; - e->type = OTHER; - e->replacement = array = NULL; +/* Parses a list or range of numbers for DO REPEAT. */ +static bool +parse_numbers (struct lexer *lexer, struct dummy_var *dv) +{ + size_t allocated = 0; do { - /* Parse A TO B into a, b. */ - if (!lex_force_int ()) - return 0; - a = lex_integer (); - - lex_get (); - if (token == T_TO) - { - lex_get (); - if (!lex_force_int ()) - return 0; - b = lex_integer (); - - lex_get (); - } - else b = a; - - if (n + (abs (b - a) + 1) > m) - { - m = n + (abs (b - a) + 1) + 16; - e->replacement = array = xnrealloc (array, - m, sizeof *e->replacement); - } + if (!lex_force_num (lexer)) + return false; - if (a == b) - store_numeric (&array[n++], a); + if (lex_next_token (lexer, 1) == T_TO) + { + if (!lex_is_integer (lexer)) + { + lex_error (lexer, _("Ranges may only have integer bounds.")); + return false; + } + + long a = lex_integer (lexer); + lex_get (lexer); + lex_get (lexer); + + if (!lex_force_int_range (lexer, NULL, a, LONG_MAX)) + return false; + + long b = lex_integer (lexer); + if (b < a) + { + lex_next_error (lexer, -2, 0, + _("%ld TO %ld is an invalid range."), a, b); + return false; + } + lex_get (lexer); + + for (long i = a; i <= b; i++) + add_replacement (dv, xasprintf ("%ld", i), &allocated); + } else - { - long iter; - - if (a < b) - for (iter = a; iter <= b; iter++) - store_numeric (&array[n++], iter); - else - for (iter = a; iter >= b; iter--) - store_numeric (&array[n++], iter); - } + { + char s[DBL_BUFSIZE_BOUND]; + + c_dtoastr (s, sizeof s, 0, 0, lex_number (lexer)); + add_replacement (dv, xstrdup (s), &allocated); + lex_get (lexer); + } - lex_match (','); + lex_match (lexer, T_COMMA); } - while (token != '/' && token != '.'); - e->replacement = xrealloc (array, n * sizeof *e->replacement); + while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD); - return n; + return true; } /* Parses a list of strings for DO REPEAT. */ -int -parse_strings (struct repeat_entry *e) +static bool +parse_strings (struct lexer *lexer, struct dummy_var *dv) { - char **string; - int n, m; - - e->type = OTHER; - string = e->replacement = NULL; - n = m = 0; + size_t allocated = 0; do { - if (token != T_STRING) - { - int i; - msg (SE, _("String expected.")); - for (i = 0; i < n; i++) - free (string[i]); - free (string); - return 0; - } + if (!lex_force_string (lexer)) + return false; - if (n + 1 > m) - { - m += 16; - e->replacement = string = xnrealloc (string, - m, sizeof *e->replacement); - } - string[n++] = lex_token_representation (); - lex_get (); + add_replacement (dv, token_to_string (lex_next (lexer, 0)), &allocated); - lex_match (','); + lex_get (lexer); + lex_match (lexer, T_COMMA); } - while (token != '/' && token != '.'); - e->replacement = xnrealloc (string, n, sizeof *e->replacement); + while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD); - return n; + return true; } int -cmd_end_repeat (void) +cmd_end_repeat (struct lexer *lexer, struct dataset *ds UNUSED) { - msg (SE, _("No matching DO REPEAT.")); + lex_ofs_error (lexer, 0, 1, _("No matching %s."), "DO REPEAT"); return CMD_CASCADING_FAILURE; } - -/* Finds a DO REPEAT macro with name MACRO_NAME and returns the - appropriate subsitution if found, or NULL if not. */ -static char * -find_substitution (struct repeat_block *block, const char *name, size_t length) -{ - struct repeat_entry *e; - - for (e = block->macros; e; e = e->next) - if (!memcasecmp (e->id, name, length) && strlen (e->id) == length) - return e->replacement[block->loop_idx]; - - return NULL; -} - -/* Makes appropriate DO REPEAT macro substitutions within getl_buf. */ -static void -do_repeat_filter (struct string *line, void *block_) -{ - struct repeat_block *block = block_; - bool in_apos, in_quote; - char *cp; - struct string output; - bool dot; - - ds_init (&output, ds_capacity (line)); - - /* Strip trailing whitespace, check for & remove terminal dot. */ - while (isspace (ds_last (line))) - ds_truncate (line, ds_length (line) - 1); - dot = ds_chomp (line, get_endcmd ()); - - in_apos = in_quote = false; - for (cp = ds_c_str (line); cp < ds_end (line); ) - { - if (*cp == '\'' && !in_quote) - in_apos = !in_apos; - else if (*cp == '"' && !in_apos) - in_quote = !in_quote; - - if (in_quote || in_apos || !lex_is_id1 (*cp)) - ds_putc (&output, *cp++); - else - { - const char *start = cp; - char *end = lex_skip_identifier (start); - const char *substitution = find_substitution (block, - start, end - start); - if (substitution != NULL) - ds_puts (&output, substitution); - else - ds_concat (&output, start, end - start); - cp = end; - } - } - if (dot) - ds_putc (&output, get_endcmd ()); - - ds_swap (line, &output); - ds_destroy (&output); -} - -/* Function called by getl to read a line. - Puts the line in OUTPUT, sets the file name in *FILE_NAME and - line number in *LINE_NUMBER. Returns true if a line was - obtained, false if the source is exhausted. */ -static bool -do_repeat_read (struct string *output, char **file_name, int *line_number, - void *block_) -{ - struct repeat_block *block = block_; - struct line_list *line; - - if (block->cur_line == NULL) - { - block->loop_idx++; - if (block->loop_idx >= block->loop_cnt) - return false; - block->cur_line = block->first_line; - } - line = block->cur_line; - - ds_assign_c_str (output, line->line); - *file_name = line->file_name; - *line_number = -line->line_number; - block->cur_line = line->next; - return true; -} - -/* Frees a DO REPEAT block. - Called by getl to close out the DO REPEAT block. */ -static void -do_repeat_close (void *block_) -{ - struct repeat_block *block = block_; - pool_destroy (block->pool); -}