X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Frepeat.c;h=cd88a1e141884121f691b812a496a19fb77f698f;hb=173d1687aea88e0e5e1b1d8615ed68ebefb15d08;hp=bba14c6c7bdb3c1bd6febb6a50a99498a0f9d152;hpb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index bba14c6c7b..cd88a1e141 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -1,111 +1,148 @@ -/* 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 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 "message.h" + #include #include #include -#include "alloc.h" -#include "command.h" -#include "dictionary.h" -#include "message.h" -#include "line-buffer.h" -#include "lexer.h" -#include "misc.h" -#include "pool.h" -#include "settings.h" -#include "str.h" -#include "variable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "intprops.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -#include "debug-print.h" - -/* Defines a list of lines used by DO REPEAT. */ -struct line_list +/* A line repeated by DO REPEAT. */ +struct repeat_line { - struct line_list *next; /* Next line. */ - char *file_name; /* File name. */ + struct ll ll; /* In struct repeat_block line_list. */ + const char *file_name; /* File name. */ int line_number; /* Line number. */ - char *line; /* Contents. */ + struct substring text; /* Contents. */ }; /* The type of substitution made for a DO REPEAT macro. */ -enum repeat_entry_type +enum repeat_macro_type { VAR_NAMES, OTHER }; /* Describes one DO REPEAT macro. */ -struct repeat_entry +struct repeat_macro { - 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. */ + struct ll ll; /* In struct repeat_block macros. */ + enum repeat_macro_type type; /* Types of replacements. */ + struct substring name; /* Macro name. */ + struct substring *replacements; /* Macro replacement. */ }; /* A DO REPEAT...END REPEAT block. */ -struct repeat_block +struct repeat_block { + struct getl_interface parent; + 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. */ + struct dataset *ds; /* The dataset for this block */ + + struct ll_list lines; /* Lines in buffer. */ + struct ll *cur_line; /* Last line output. */ int loop_cnt; /* Number of loops. */ int loop_idx; /* Number of loops so far. */ - struct repeat_entry *macros; /* Pointer to macro table. */ + + struct ll_list macros; /* Table of macros. */ + bool print; /* Print lines as executed? */ }; -static bool parse_specification (struct repeat_block *); -static bool parse_lines (struct repeat_block *); +static bool parse_specification (struct lexer *, struct repeat_block *); +static bool parse_lines (struct lexer *, struct repeat_block *); static void create_vars (struct repeat_block *); -static int parse_ids (struct repeat_entry *); -static int parse_numbers (struct repeat_entry *); -static int parse_strings (struct repeat_entry *); +static struct repeat_macro *find_macro (struct repeat_block *, + struct substring name); + +static int parse_ids (struct lexer *, const struct dictionary *dict, + struct repeat_macro *, struct pool *); -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 int parse_numbers (struct lexer *, struct repeat_macro *, + struct pool *); + +static int parse_strings (struct lexer *, struct repeat_macro *, + struct pool *); + +static void do_repeat_filter (struct getl_interface *, + struct string *); +static bool do_repeat_read (struct getl_interface *, + struct string *); +static void do_repeat_close (struct getl_interface *); +static bool always_false (const struct getl_interface *); +static const char *do_repeat_name (const struct getl_interface *); +static int do_repeat_location (const struct getl_interface *); int -cmd_do_repeat (void) +cmd_do_repeat (struct lexer *lexer, struct dataset *ds) { struct repeat_block *block; block = pool_create_container (struct repeat_block, pool); + block->ds = ds; + ll_init (&block->lines); + block->cur_line = ll_null (&block->lines); + block->loop_idx = 0; + ll_init (&block->macros); - if (!parse_specification (block) || !parse_lines (block)) + if (!parse_specification (lexer, block) || !parse_lines (lexer, 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); + + block->parent.read = do_repeat_read; + block->parent.close = do_repeat_close; + block->parent.filter = do_repeat_filter; + block->parent.interactive = always_false; + block->parent.name = do_repeat_name; + block->parent.location = do_repeat_location; + + if (!ll_is_empty (&block->lines)) + getl_include_source (lex_get_source_stream (lexer), + &block->parent, + lex_current_syntax_mode (lexer), + lex_current_error_mode (lexer) + ); + else + pool_destroy (block->pool); return CMD_SUCCESS; @@ -117,58 +154,61 @@ cmd_do_repeat (void) /* Parses the whole DO REPEAT command specification. Returns success. */ static bool -parse_specification (struct repeat_block *block) +parse_specification (struct lexer *lexer, struct repeat_block *block) { - char first_name[LONG_NAME_LEN + 1]; + struct substring first_name; block->loop_cnt = 0; - block->macros = NULL; do { - struct repeat_entry *e; - struct repeat_entry *iter; + struct repeat_macro *macro; + struct dictionary *dict = dataset_dict (block->ds); int count; /* Get a stand-in variable name and make sure it's unique. */ - if (!lex_force_id ()) + if (!lex_force_id (lexer)) 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)) + if (dict_lookup_var (dict, lex_tokid (lexer))) + msg (SW, _("Dummy variable name `%s' hides dictionary " + "variable `%s'."), + lex_tokid (lexer), lex_tokid (lexer)); + if (find_macro (block, ss_cstr (lex_tokid (lexer)))) { - msg (SE, _("Dummy variable name \"%s\" is given twice."), tokid); + msg (SE, _("Dummy variable name `%s' is given twice."), + lex_tokid (lexer)); 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; + /* Make a new macro. */ + macro = pool_alloc (block->pool, sizeof *macro); + ss_alloc_substring_pool (¯o->name, ss_cstr (lex_tokid (lexer)), + block->pool); + ll_push_tail (&block->macros, ¯o->ll); /* Skip equals sign. */ - lex_get (); - if (!lex_force_match ('=')) + lex_get (lexer); + if (!lex_force_match (lexer, '=')) return false; /* 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); + if (lex_token (lexer) == T_ID) + count = parse_ids (lexer, dict, macro, block->pool); + else if (lex_is_number (lexer)) + count = parse_numbers (lexer, macro, block->pool); + else if (lex_token (lexer) == T_STRING) + count = parse_strings (lexer, macro, block->pool); else { - lex_error (NULL); + lex_error (lexer, NULL); return false; } if (count == 0) return false; + if (lex_token (lexer) != '/' && lex_token (lexer) != '.') + { + lex_error (lexer, NULL); + return false; + } /* If this is the first variable then it defines how many replacements there must be; otherwise enforce this number of @@ -176,144 +216,148 @@ parse_specification (struct repeat_block *block) if (block->loop_cnt == 0) { block->loop_cnt = count; - strcpy (first_name, e->id); + first_name = macro->name; } else if (block->loop_cnt != count) { - msg (SE, _("Dummy variable \"%s\" had %d " - "substitutions, so \"%s\" must also, but %d " + msg (SE, _("Dummy variable `%.*s' had %d " + "substitutions, so `%.*s' must also, but %d " "were specified."), - first_name, block->loop_cnt, e->id, count); + (int) ss_length (first_name), ss_data (first_name), + block->loop_cnt, + (int) ss_length (macro->name), ss_data (macro->name), + count); return false; } - lex_match ('/'); + lex_match (lexer, '/'); } - while (token != '.'); + while (lex_token (lexer) != '.'); return true; } -/* 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) +/* Finds and returns a DO REPEAT macro with the given NAME, or + NULL if there is none */ +static struct repeat_macro * +find_macro (struct repeat_block *block, struct substring name) { - const char *end; + struct repeat_macro *macro; - while (isspace ((unsigned char) *cp)) - cp++; + ll_for_each (macro, struct repeat_macro, ll, &block->macros) + if (ss_equals (macro->name, name)) + return macro; - end = lex_skip_identifier (cp); - if (end != cp - && lex_id_match_len (keyword, strlen (keyword), cp, end - cp)) - return end; - else - return NULL; + return NULL; } -/* Returns CP, advanced past a '+' or '-' if present. */ -static const char * -skip_indentor (const char *cp) +/* Advances LINE past white space and an identifier, if present. + Returns true if KEYWORD matches the identifer, false + otherwise. */ +static bool +recognize_keyword (struct substring *line, const char *keyword) { - if (*cp == '+' || *cp == '-') - cp++; - return cp; + struct substring id; + ss_ltrim (line, ss_cstr (CC_SPACES)); + ss_get_chars (line, lex_id_get_length (*line), &id); + return lex_id_match (ss_cstr (keyword), id); } /* Returns true if LINE contains a DO REPEAT command, false otherwise. */ static bool -recognize_do_repeat (const char *line) +recognize_do_repeat (struct substring line) { - const char *cp = recognize_keyword (skip_indentor (line), "do"); - return cp != NULL && recognize_keyword (cp, "repeat") != NULL; + return (recognize_keyword (&line, "do") + && recognize_keyword (&line, "repeat")); } /* 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) +recognize_end_repeat (struct substring line, bool *print) { - const char *cp = recognize_keyword (skip_indentor (line), "end"); - if (cp == NULL) + if (!recognize_keyword (&line, "end") + || !recognize_keyword (&line, "repeat")) return false; - cp = recognize_keyword (cp, "repeat"); - if (cp == NULL) - return false; - - *print = recognize_keyword (cp, "print"); - return true; + *print = recognize_keyword (&line, "print"); + return true; } /* Read all the lines we are going to substitute, inside the DO REPEAT...END REPEAT block. */ static bool -parse_lines (struct repeat_block *block) +parse_lines (struct lexer *lexer, struct repeat_block *block) { char *previous_file_name; - struct line_list **last_line; int nesting_level; previous_file_name = NULL; - block->first_line = NULL; - last_line = &block->first_line; nesting_level = 0; for (;;) { const char *cur_file_name; - int cur_line_number; - struct line_list *line; - bool dot; + struct repeat_line *line; + struct string text; + bool command_ends_before_line, command_ends_after_line; - if (!getl_read_line (NULL)) + /* Retrieve an input line and make a copy of it. */ + if (!lex_get_line_raw (lexer)) return false; + ds_init_string (&text, lex_entire_line_ds (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)) + /* Record file name. */ + cur_file_name = getl_source_name (lex_get_source_stream (lexer)); + if (cur_file_name != NULL && + (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)) + /* Create a line structure. */ + line = pool_alloc (block->pool, sizeof *line); + line->file_name = previous_file_name; + line->line_number = getl_source_location (lex_get_source_stream (lexer)); + ss_alloc_substring_pool (&line->text, ds_ss (&text), block->pool); + + + /* Check whether the line contains a DO REPEAT or END + REPEAT command. */ + lex_preprocess_line (&text, + lex_current_syntax_mode (lexer), + &command_ends_before_line, + &command_ends_after_line); + if (recognize_do_repeat (ds_ss (&text))) { - if (nesting_level-- == 0) - { - lex_discard_line (); - return true; - } + if (settings_get_syntax () == COMPATIBLE) + msg (SE, _("DO REPEAT may not nest in compatibility mode.")); + else + nesting_level++; } - 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; - } + else if (recognize_end_repeat (ds_ss (&text), &block->print) + && nesting_level-- == 0) + { + lex_discard_line (lexer); + ds_destroy (&text); + return true; + } + ds_destroy (&text); - lex_discard_line (); - return true; + /* Add the line to the list. */ + ll_push_tail (&block->lines, &line->ll); + } } /* 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) + struct repeat_macro *macro; + + ll_for_each (macro, struct repeat_macro, ll, &block->macros) + if (macro->type == VAR_NAMES) { int i; @@ -321,258 +365,263 @@ create_vars (struct repeat_block *block) { /* Ignore return value: if the variable already exists there is no harm done. */ - dict_create_var (default_dict, iter->replacement[i], 0); + char *var_name = ss_xstrdup (macro->replacements[i]); + dict_create_var (dataset_dict (block->ds), var_name, 0); + free (var_name); } } } /* Parses a set of ids for DO REPEAT. */ static int -parse_ids (struct repeat_entry *e) +parse_ids (struct lexer *lexer, const struct dictionary *dict, + struct repeat_macro *macro, struct pool *pool) { - size_t i; - size_t n = 0; - - e->type = VAR_NAMES; - e->replacement = NULL; - - do - { - char **names; - size_t nnames; + char **replacements; + size_t n, i; - 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; - } - while (token != '/' && token != '.'); + macro->type = VAR_NAMES; + if (!parse_mixed_vars_pool (lexer, dict, pool, &replacements, &n, PV_NONE)) + return 0; + macro->replacements = pool_nalloc (pool, n, sizeof *macro->replacements); + for (i = 0; i < n; i++) + macro->replacements[i] = ss_cstr (replacements[i]); return n; } -/* Stores VALUE into *REPL. */ -static inline void -store_numeric (char **repl, long value) +/* 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 substring replacement, + struct repeat_macro *macro, struct pool *pool, + size_t *used, size_t *allocated) { - *repl = xmalloc (INT_DIGITS + 1); - sprintf (*repl, "%ld", value); + if (*used == *allocated) + macro->replacements = pool_2nrealloc (pool, macro->replacements, allocated, + sizeof *macro->replacements); + macro->replacements[(*used)++] = replacement; } -/* Parses a list of numbers for DO REPEAT. */ +/* Parses a list or range of numbers for DO REPEAT. */ static int -parse_numbers (struct repeat_entry *e) +parse_numbers (struct lexer *lexer, struct repeat_macro *macro, + struct pool *pool) { - /* 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; + size_t used = 0; + size_t allocated = 0; - n = m = 0; - e->type = OTHER; - e->replacement = array = NULL; + macro->type = OTHER; + macro->replacements = NULL; do { + bool integer_value_seen; + double a, b, i; + /* Parse A TO B into a, b. */ - if (!lex_force_int ()) + if (!lex_force_num (lexer)) 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 ( (integer_value_seen = lex_is_integer (lexer) ) ) + a = lex_integer (lexer); + else + a = lex_number (lexer); - if (n + (abs (b - a) + 1) > m) + lex_get (lexer); + if (lex_token (lexer) == T_TO) { - m = n + (abs (b - a) + 1) + 16; - e->replacement = array = xnrealloc (array, - m, sizeof *e->replacement); + if ( !integer_value_seen ) + { + msg (SE, _("Ranges may only have integer bounds")); + return 0; + } + lex_get (lexer); + if (!lex_force_int (lexer)) + return 0; + b = lex_integer (lexer); + if (b < a) + { + msg (SE, _("%g TO %g is an invalid range."), a, b); + return 0; + } + lex_get (lexer); } - - if (a == b) - store_numeric (&array[n++], a); 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); - } + b = a; + + for (i = a; i <= b; i++) + add_replacement (ss_cstr (pool_asprintf (pool, "%g", i)), + macro, pool, &used, &allocated); - lex_match (','); + lex_match (lexer, ','); } - while (token != '/' && token != '.'); - e->replacement = xrealloc (array, n * sizeof *e->replacement); + while (lex_token (lexer) != '/' && lex_token (lexer) != '.'); - return n; + return used; } /* Parses a list of strings for DO REPEAT. */ int -parse_strings (struct repeat_entry *e) +parse_strings (struct lexer *lexer, struct repeat_macro *macro, struct pool *pool) { - char **string; - int n, m; + size_t used = 0; + size_t allocated = 0; - e->type = OTHER; - string = e->replacement = NULL; - n = m = 0; + macro->type = OTHER; + macro->replacements = NULL; do { - if (token != T_STRING) + char *string; + + if (lex_token (lexer) != T_STRING) { - int i; msg (SE, _("String expected.")); - for (i = 0; i < n; i++) - free (string[i]); - free (string); return 0; } - if (n + 1 > m) - { - m += 16; - e->replacement = string = xnrealloc (string, - m, sizeof *e->replacement); - } - string[n++] = lex_token_representation (); - lex_get (); + string = lex_token_representation (lexer); + pool_register (pool, free, string); + add_replacement (ss_cstr (string), macro, pool, &used, &allocated); - lex_match (','); + lex_get (lexer); + lex_match (lexer, ','); } - while (token != '/' && token != '.'); - e->replacement = xnrealloc (string, n, sizeof *e->replacement); + while (lex_token (lexer) != '/' && lex_token (lexer) != '.'); - return n; + return used; } int -cmd_end_repeat (void) +cmd_end_repeat (struct lexer *lexer UNUSED, struct dataset *ds UNUSED) { msg (SE, _("No matching 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) +/* Finds a DO REPEAT macro with the given NAME and returns the + appropriate substitution if found, or NAME otherwise. */ +static struct substring +find_substitution (struct repeat_block *block, struct substring name) { - 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; + struct repeat_macro *macro = find_macro (block, name); + return macro ? macro->replacements[block->loop_idx] : name; } -/* Makes appropriate DO REPEAT macro substitutions within getl_buf. */ +/* Makes appropriate DO REPEAT macro substitutions within the + repeated lines. */ static void -do_repeat_filter (struct string *line, void *block_) +do_repeat_filter (struct getl_interface *interface, struct string *line) { - struct repeat_block *block = block_; - bool in_apos, in_quote; - char *cp; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); + bool in_apos, in_quote, dot; + struct substring input; struct string output; - bool dot; + int c; - ds_init (&output, ds_capacity (line)); + ds_init_empty (&output); /* 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 ()); - + ds_rtrim (line, ss_cstr (CC_SPACES)); + dot = ds_chomp (line, settings_get_endcmd ()); + input = ds_ss (line); in_apos = in_quote = false; - for (cp = ds_c_str (line); cp < ds_end (line); ) + while ((c = ss_first (input)) != EOF) { - if (*cp == '\'' && !in_quote) + if (c == '\'' && !in_quote) in_apos = !in_apos; - else if (*cp == '"' && !in_apos) + else if (c == '"' && !in_apos) in_quote = !in_quote; - - if (in_quote || in_apos || !lex_is_id1 (*cp)) - ds_putc (&output, *cp++); - else + + if (in_quote || in_apos || !lex_is_id1 (c)) { - 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; + ds_put_char (&output, c); + ss_advance (&input, 1); + } + else + { + struct substring id; + ss_get_chars (&input, lex_id_get_length (input), &id); + ds_put_substring (&output, find_substitution (block, id)); } } if (dot) - ds_putc (&output, get_endcmd ()); + ds_put_char (&output, settings_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 struct repeat_line * +current_line (const struct getl_interface *interface) +{ + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); + return (block->cur_line != ll_null (&block->lines) + ? ll_data (block->cur_line, struct repeat_line, ll) + : NULL); +} + +/* Function called by getl to read a line. Puts the line in + OUTPUT and its syntax mode in *SYNTAX. 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_) +do_repeat_read (struct getl_interface *interface, + struct string *output) { - struct repeat_block *block = block_; - struct line_list *line; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); + struct repeat_line *line; - if (block->cur_line == NULL) + block->cur_line = ll_next (block->cur_line); + if (block->cur_line == ll_null (&block->lines)) { block->loop_idx++; if (block->loop_idx >= block->loop_cnt) return false; - block->cur_line = block->first_line; + + block->cur_line = ll_head (&block->lines); } - line = block->cur_line; - ds_replace (output, line->line); - *file_name = line->file_name; - *line_number = -line->line_number; - block->cur_line = line->next; + line = current_line (interface); + ds_assign_substring (output, line->text); return true; } /* Frees a DO REPEAT block. Called by getl to close out the DO REPEAT block. */ static void -do_repeat_close (void *block_) +do_repeat_close (struct getl_interface *interface) { - struct repeat_block *block = block_; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); pool_destroy (block->pool); } + + +static bool +always_false (const struct getl_interface *i UNUSED) +{ + return false; +} + +/* Returns the name of the source file from which the previous + line was originally obtained, or a null pointer if none. */ +static const char * +do_repeat_name (const struct getl_interface *interface) +{ + struct repeat_line *line = current_line (interface); + return line ? line->file_name : NULL; +} + +/* Returns the line number in the source file from which the + previous line was originally obtained, or -1 if none. */ +static int +do_repeat_location (const struct getl_interface *interface) +{ + struct repeat_line *line = current_line (interface); + return line ? line->line_number : -1; +}