From 74a57f26f1458b28a0fddbb9f46004ac8f4d9c30 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 21 Mar 2004 03:06:11 +0000 Subject: [PATCH] Changed DFM from open-at-first-access to explicit-open. Before, calling dfm_get_record() or dfm_put_record() would automatically open the file. Now, you have to call dfm_open_for_reading() or dfm_open_for_writing() explicitly. This makes it possible to check permissions, file existence, etc. earlier. Also made struct file_handle more opaque, and clean up in general. Fixed cmd_parse() so that it always skips past a full command name. A few special commands for which this would be bad get special treatment. This lets us drop code for skipping past the end of a command name in most cmd_*() functions. It's not worth listing all the commands affected. Start work on better test framework. Fix memory leaks. Misc other changes. --- src/ChangeLog | 196 ++++++++++++++++ src/Makefile.am | 2 +- src/aggregate.c | 2 - src/apply-dict.c | 3 - src/autorecode.c | 4 +- src/command.c | 507 +++++++++++++++++++++++++--------------- src/command.def | 17 +- src/command.h | 4 +- src/compute.c | 25 -- src/correlations.q | 3 - src/count.c | 2 - src/crosstabs.q | 1 - src/data-list.c | 19 +- src/debug.c | 45 ++++ src/descript.q | 2 - src/dfm.c | 377 +++++++++++++++--------------- src/dfm.h | 2 + src/do-if.c | 6 - src/error.c | 2 + src/expr-evl.c | 7 + src/expr-prs.c | 5 +- src/expr.h | 2 - src/file-handle.h | 53 ++--- src/file-handle.q | 375 +++++++++++++---------------- src/file-type.c | 8 +- src/filename.c | 90 +++++++ src/filename.h | 5 + src/flip.c | 1 - src/formats.c | 3 - src/frequencies.q | 1 - src/get.c | 14 +- src/include.c | 43 +--- src/inpt-pgm.c | 24 +- src/levene.c | 1 + src/lexer.c | 89 ++++--- src/lexer.h | 2 +- src/list.q | 1 - src/loop.c | 4 - src/main.c | 1 - src/matrix-data.c | 6 +- src/means.q | 1 - src/mis-val.c | 2 - src/modify-vars.c | 3 - src/numeric.c | 3 - src/pfm-read.c | 20 +- src/pfm-write.c | 15 +- src/print.c | 93 ++------ src/recode.c | 2 - src/rename-vars.c | 3 - src/repeat.c | 3 - src/sample.c | 2 - src/sel-if.c | 8 - src/set.q | 8 +- src/sfm-read.c | 200 +++++++++------- src/sfm-write.c | 18 +- src/sort.c | 2 - src/split-file.c | 3 - src/sysfile-info.c | 7 +- src/t-test.q | 6 - src/temporary.c | 2 - src/title.c | 3 - src/val-labs.c | 6 +- src/var-labs.c | 2 - src/vector.c | 2 - src/vfm.c | 8 +- src/weight.c | 2 - tests/ChangeLog | 8 + tests/command/list.sh | 2 +- tests/command/print.sh | 8 +- tests/command/weight.sh | 2 +- 70 files changed, 1324 insertions(+), 1074 deletions(-) create mode 100644 src/debug.c diff --git a/src/ChangeLog b/src/ChangeLog index 30730f0a2e..90df01c471 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,199 @@ +Sat Mar 20 17:57:23 2004 Ben Pfaff + + * levene.c: Add #include. + + * set.q: (set_viewport) Add `int' argument to make its prototype + correct for signal(). + +Sat Mar 20 15:35:17 2004 Ben Pfaff + + * expr-evl.c: (expr_evaluate) Assert that `c' is nonzero before + using it. + +Sat Mar 20 15:18:16 2004 Ben Pfaff + + Changed DFM from open-at-first-access to explicit-open. Before, + calling dfm_get_record() or dfm_put_record() would automatically + open the file. Now, you have to call dfm_open_for_reading() or + dfm_open_for_writing() explicitly. This makes it possible to + check permissions, file existence, etc. earlier. + + Also made struct file_handle more opaque, and clean up in general. + + * data-list.c: (cmd_data_list) Open handle for reading. + + * dfm.c: (struct dfm_fhuser_ext) Add `where', `saw_begin_data' + members. + (open_file_r) Renamed dfm_open_for_reading(), rewrote. + (open_file_w) Renamed dfm_open_for_writing(), rewrote. + (open_inline_file) Removed. + (read_record) For inline_file, if we haven't seen BEGIN DATA, read + it. Deal with line_number in extension record instead of file + handle. + (dfm_get_record) Rewrote. + (dfm_put_record) Rewrote. + (dfm_push) Assert file is open and one of ours. Deal with + line_number in extension record instead of file handle. + (dfm_pop) Assert file is open and one of ours. Deal with + line_number in extension record instead of file handle. + (cmd_begin_data) Use dfm_open_for_reading(). Mark that we saw + BEGIN DATA. + + * file-handle.h: (enum constants RH_RF_*) Removed. + (enum constants FH_MD_*) Removed. + (struct file_handle) Removed `name', `norm_fn', `fn', `where', + `recform', `lrecl', `mode' members. Public references to + `recform' changed to use handle_get_mode(), references to `lrecl' + changed to use handle_get_record_width(). Added `private' member. + (enum file_handle_mode) New. + + * file-handle.q: (struct private_file_handle) New structure. + (struct file_handle_list) New structure. + (static var files) New. + (static var file_handles) Removed. + (init_file_handle) Removed. + (create_file_handle) Removed. + (get_handle_with_name) New function. + (get_handle_for_filename) New function. + (cmd_file_handle) Rewritten. + (hash_file_handle) Removed. + (cmp_file_handle) Removed. + (fh_init_files) Rewritten. + (fh_parse_file_handle) Rewritten. Allows identifiers as + filenames. + (fh_get_handle_by_name) Renamed handle_get_name(), all references + updated. Rewritten. + (fh_get_handle_by_filename) Renamed handle_get_filename(), all + references updated. Rewritten. + (fh_record_width) Renamed handle_get_record_width(), all + references updated. Rewritten. + (handle_get_mode) New function. + + * file-type.c: (cmd_file_type) Open handle for reading. + + * filename.c: [unix] (struct file_identity) New structure. + [unix] (fn_get_identity) New function. + [unix] (fn_free_identity) New function. + [unix] (fn_compare_file_identities) New function. + [!unix] (struct file_identity) New structure. + [!unix] (fn_get_identity) New function. + [!unix] (fn_free_identity) New function. + [!unix] (fn_compare_file_identities) New function. + + * lexer.c: (static var put) Renamed put_token, all references + updated. + (static var put_tokstr) New. + (static var put_tokval) New. + (lex_init) Initialize put_tokstr(). + (restore_token) New function. + (save_token) New function. + (lex_get) Use restore_token(). + (lex_put_back) Use save_token(). + (lex_put_back_id) New function. + (lex_put_forward) Removed. + (lex_preprocess_line) Set put_token instead of using + lex_put_forward(). + (lex_negative_to_dash) Use save_token(), set put_token directly. + (dump_token) Use stderr instead of stdout. + + * main.c: (main) Remove call to cmd_init(). + + * matrix-data.c: (cmd_matrix_data) Open file for reading. + + * pfm-read.c: Use handle_get_filename() instead of trying to use + h->fn directly, all over. + + * pfm-write.c: Ditto. + + * print.c: (internal_cmd_print) Open handle for writing. + (dump_table) Use handle_get_filename(). + (print_trns_proc) Use handle_get_mode(). + (cmd_print_space) Use fh_parse_file_handle(). + Open handle for writing. + [0] (debug_print) Removed. + + * sfm-read.c: Use handle_get_filename() instead of trying to use + h->fn directly, all over. + + * sfm-write.c: Ditto. + +Sat Mar 20 14:35:48 2004 Ben Pfaff + + Fix memory leaks. + + * autorecode.c: (arc_free) Free arc->src_values. + + * error.c: (msg) Free buf. + + * val-labs.c: (do_value_labels) Always free vars. + + * vfm.c: (close_active_file) If sink has no make_source then call + its destroy function. + +Sat Mar 20 14:00:24 2004 Ben Pfaff + + Fixed cmd_parse() so that it always skips past a full command + name. A few special commands for which this would be bad get + special treatment. This lets us drop code for skipping past the + end of a command name in most cmd_*() functions. It's not worth + listing all the commands affected. + + * command.c: (struct command) Remove `cmd' member, replace by + `name' member, all references updated. Remove `word', `next', + `skip_entire_name' members. + (macro DEFCMD) Deal with revised `struct command'. + (macro UNIMPL) Ditto. + (macro SPCCMD) New macro for commands whose last word shouldn't be + skipped. + (static array cmd_table[]) Make const, rename `commands', remove + sentinel element. + (macro COMMAND_CNT) New macro. + (split_words) Removed. + (cmd_init) Removed. + (FILE_TYPE_okay) Make parameter const. + (cmd_parse) Improve error messages. + (match_strings) New function. + (next_word) New function. + (enum command_match) New enum. + (conflicting_3char_prefixes) New function. + (conflicting_3char_prefix_command) New function. + (cmd_match_words) New function. + (count_matching_commands) New function. + (get_command_name) New function. + (free_words) New function. + (unknown_command_error) New function. + (figure_out_command) Renamed parse_command_name(), rewritten. + + * command.def: Removed @ command. Marked BEGIN DATA, DOCUMENT, + FILE LABEL, REMARK, SUBTITLE, TITLE as special. Renamed EVALUATE + to DEBUG EVALUATE. Added N alias for N OF CASES, SORT alias for + SORT CASES. + + * command.h: (macro SPCCMD) New. + + * include.c: (cmd_include_at) Removed. + (cmd_include) Allow identifier to be used as filename. + + * inpt-pgm.c: (cmd_reread) Use fh_parse_file_handle(). + + * t-test.q: (cmd_t_test) Command name is now parsed for us. + + +Sat Mar 20 13:56:00 2004 Ben Pfaff + + Start work on better test framework. + + * Makefile.am: (pspp_sources) Add debug.c. + + * debug.c: New file. + + * compute.c: (cmd_evaluate) Moved to debug.c, renamed + cmd_debug_evaluate(). + + * expr-prs.c: (expr_parse) Remove PXP_DUMP support. + + * expr.h: (enum constant PXP_DUMP) Removed. + Sat Mar 20 00:05:42 WST 2004 John Darrington * set.q: Implemented the SHOW command, and synced it to the existing diff --git a/src/Makefile.am b/src/Makefile.am index 487b4e3623..3c77737857 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,7 +39,7 @@ apply-dict.c ascii.c autorecode.c bitvector.h \ cmdline.c cmdline.h command.c command.def command.h compute.c \ copyleft.c copyleft.h \ count.c data-in.c data-in.h data-list.c data-list.h \ -data-out.c debug-print.h devind.c devind.h dfm.c dfm.h \ +data-out.c debug.c debug-print.h devind.c devind.h dfm.c dfm.h \ dictionary.c do-if.c do-ifP.h error.c error.h expr-evl.c expr-opt.c \ expr-prs.c expr.h exprP.h file-handle.h file-type.c \ filename.c filename.h flip.c font.h format.c format.def format.h \ diff --git a/src/aggregate.c b/src/aggregate.c index 72b4605978..193125087c 100644 --- a/src/aggregate.c +++ b/src/aggregate.c @@ -169,8 +169,6 @@ cmd_aggregate (void) dict_set_label (agr.dict, dict_get_label (default_dict)); dict_set_documents (agr.dict, dict_get_documents (default_dict)); - lex_match_id ("AGGREGATE"); - /* Read most of the subcommands. */ for (;;) { diff --git a/src/apply-dict.c b/src/apply-dict.c index 541d93382d..9f6867cc4e 100644 --- a/src/apply-dict.c +++ b/src/apply-dict.c @@ -42,9 +42,6 @@ cmd_apply_dictionary (void) int i; - lex_match_id ("APPLY"); - lex_match_id ("DICTIONARY"); - lex_match_id ("FROM"); lex_match ('='); handle = fh_parse_file_handle (); diff --git a/src/autorecode.c b/src/autorecode.c index f6b8e15ece..52104e7aca 100644 --- a/src/autorecode.c +++ b/src/autorecode.c @@ -103,7 +103,6 @@ cmd_autorecode (void) arc.print = 0; dst_cnt = 0; - lex_match_id ("AUTORECODE"); lex_match_id ("VARIABLES"); lex_match ('='); if (!parse_variables (default_dict, &arc.src_vars, &arc.var_cnt, @@ -205,7 +204,8 @@ arc_free (struct autorecode_pgm *arc) int i; for (i = 0; i < arc->var_cnt; i++) - hsh_destroy (arc->src_values[i]); + hsh_destroy (arc->src_values[i]); + free (arc->src_values); } pool_destroy (arc->src_values_pool); } diff --git a/src/command.c b/src/command.c index 163f984574..b6a0babeb5 100644 --- a/src/command.c +++ b/src/command.c @@ -43,8 +43,6 @@ #if HAVE_SYS_WAIT_H #include #endif - -#include "debug-print.h" /* Global variables. */ @@ -59,71 +57,37 @@ const char *cur_proc; /* A single command. */ struct command { - /* Initialized statically. */ - char cmd[22]; /* Command name. */ + const char *name; /* Command name. */ int transition[4]; /* Transitions to make from each state. */ int (*func) (void); /* Function to call. */ - - /* Calculated at startup time. */ - char *word[3]; /* cmd[], divided into individual words. */ - struct command *next; /* Next command with same word[0]. */ + int skip_entire_name; /* If zero, we don't skip the + final token in the command name. */ }; /* Define the command array. */ #define DEFCMD(NAME, T1, T2, T3, T4, FUNC) \ - {NAME, {T1, T2, T3, T4}, FUNC, {NULL, NULL, NULL}, NULL}, + {NAME, {T1, T2, T3, T4}, FUNC, 1}, +#define SPCCMD(NAME, T1, T2, T3, T4, FUNC) \ + {NAME, {T1, T2, T3, T4}, FUNC, 0}, #define UNIMPL(NAME, T1, T2, T3, T4) \ - {NAME, {T1, T2, T3, T4}, NULL, {NULL, NULL, NULL}, NULL}, -static struct command cmd_table[] = + {NAME, {T1, T2, T3, T4}, NULL, 1}, +static const struct command commands[] = { #include "command.def" - {"", {ERRO, ERRO, ERRO, ERRO}, NULL, {NULL, NULL, NULL}, NULL}, }; #undef DEFCMD #undef UNIMPL + +#define COMMAND_CNT (sizeof commands / sizeof *commands) /* Command parser. */ -static struct command *figure_out_command (void); - -/* Breaks the `cmd' member of C into individual words and sets C's - word[] member appropriately. */ -static void -split_words (struct command *c) -{ - char *cmd, *save; - int i; - - cmd = xstrdup (c->cmd); - for (i = 0; i < 3; i++) - cmd = c->word[i] = strtok_r (i == 0 ? cmd : NULL, " -", &save); -} - -/* Initializes the command parser. */ -void -cmd_init (void) -{ - struct command *c; - - /* Break up command names into words. */ - for (c = cmd_table; c->cmd[0]; c++) - split_words (c); - - /* Make chains of commands having the same first word. */ - for (c = cmd_table; c->cmd[0]; c++) - { - struct command *first; - for (first = c; c[1].word[0] && !strcmp (c[0].word[0], c[1].word[0]); c++) - c->next = c + 1; - - c->next = NULL; - } -} +static const struct command *parse_command_name (void); /* Determines whether command C is appropriate to call in this part of a FILE TYPE structure. */ static int -FILE_TYPE_okay (struct command *c) +FILE_TYPE_okay (const struct command *c) { int okay = 0; @@ -131,12 +95,12 @@ FILE_TYPE_okay (struct command *c) && c->func != cmd_data_list && c->func != cmd_repeating_data && c->func != cmd_end_file_type) - msg (SE, _("%s not allowed inside FILE TYPE/END FILE TYPE."), c->cmd); + msg (SE, _("%s not allowed inside FILE TYPE/END FILE TYPE."), c->name); #if 0 /* FIXME */ else if (c->func == cmd_repeating_data && fty.type == FTY_GROUPED) msg (SE, _("%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."), - c->cmd); + c->name); else if (!fty.had_rec_type && c->func != cmd_record_type) msg (SE, _("RECORD TYPE must be the first command inside a " "FILE TYPE structure.")); @@ -159,7 +123,7 @@ FILE_TYPE_okay (struct command *c) int cmd_parse (void) { - struct command *cp; /* Iterator used to find the proper command. */ + const struct command *cp; /* Iterator used to find the proper command. */ #if C_ALLOCA /* The generic alloca package performs garbage collection when it is @@ -183,17 +147,17 @@ cmd_parse (void) always an ID token. */ if (token != T_ID) { - msg (SE, _("This line does not begin with a valid command name.")); + lex_error (_("expecting command name")); return CMD_FAILURE; } /* Parse the command name. */ - cp = figure_out_command (); + cp = parse_command_name (); if (cp == NULL) return CMD_FAILURE; if (cp->func == NULL) { - msg (SE, _("%s is not yet implemented."), cp->cmd); + msg (SE, _("%s is not yet implemented."), cp->name); while (token && token != '.') lex_get (); return CMD_SUCCESS; @@ -221,15 +185,10 @@ cmd_parse (void) N_("%s is only allowed within an input program."), }; - msg (SE, gettext (state_name[pgm_state]), cp->cmd); + msg (SE, gettext (state_name[pgm_state]), cp->name); return CMD_FAILURE; } -#if DEBUGGING - if (cp->func != cmd_remark) - printf (_("%s command beginning\n"), cp->cmd); -#endif - /* The structured output manager numbers all its tables. Increment the major table number for each separate procedure. */ som_new_series (); @@ -243,7 +202,7 @@ cmd_parse (void) const char *prev_proc; prev_proc = cur_proc; - cur_proc = cp->cmd; + cur_proc = cp->name; result = cp->func (); cur_proc = prev_proc; } @@ -261,140 +220,331 @@ cmd_parse (void) } } -#if DEBUGGING - if (cp->func != cmd_remark) - printf (_("%s command completed\n\n"), cp->cmd); -#endif - /* Pass the command's success value up to the caller. */ return result; } } -/* Parse the command name and return a pointer to the corresponding - struct command if successful. - If not successful, return a null pointer. */ -static struct command * -figure_out_command (void) +static size_t +match_strings (const char *a, size_t a_len, + const char *b, size_t b_len) { - static const char *unk = - N_("The identifier(s) specified do not form a valid command name:"); - - static const char *inc = - N_("The identifier(s) specified do not form a complete command name:"); - - struct command *cp; + size_t match_len = 0; + + while (a_len > 0 && b_len > 0) + { + /* Mismatch always returns zero. */ + if (*a++ != *b++) + return 0; + + /* Advance. */ + a_len--; + b_len--; + match_len++; + } - /* Parse the INCLUDE short form. - Note that `@' is a valid character in identifiers. */ - if (tokid[0] == '@') - return &cmd_table[0]; + return match_len; +} - /* Find a command whose first word matches this identifier. - If it is the only command that begins with this word, return - it. */ - for (cp = cmd_table; cp->cmd[0]; cp++) - if (lex_id_match (cp->word[0], tokid)) - break; +/* Returns the first character in the first word in STRING, + storing the word's length in *WORD_LEN. If no words remain, + returns a null pointer and stores 0 in *WORD_LEN. Words are + sequences of alphanumeric characters or single + non-alphanumeric characters. Words are delimited by + spaces. */ +static const char * +find_word (const char *string, size_t *word_len) +{ + /* Skip whitespace and asterisks. */ + while (isspace (*string)) + string++; - if (cp->cmd[0] == '\0') + /* End of string? */ + if (*string == '\0') { - msg (SE, "%s %s.", gettext (unk), ds_value (&tokstr)); + *word_len = 0; return NULL; } - if (cp->next == NULL) - return cp; - - /* We know that there is more than one command starting with this - word. Read the next word in the command name. */ + /* Special one-character word? */ + if (!isalnum ((unsigned char) *string)) + { + *word_len = 1; + return string; + } + + /* Alphanumeric word. */ + *word_len = 1; + while (isalnum ((unsigned char) string[*word_len])) + (*word_len)++; + + return string; +} + +/* Returns nonzero if strings A and B can be confused based on + their first three letters. */ +static int +conflicting_3char_prefixes (const char *a, const char *b) +{ + size_t aw_len, bw_len; + const char *aw, *bw; + + aw = find_word (a, &aw_len); + bw = find_word (b, &bw_len); + assert (aw != NULL && bw != NULL); + + return ((aw_len > 3 && bw_len > 3) + || (aw_len == 3 && bw_len > 3) + || (bw_len == 3 && aw_len > 3)) && !memcmp (aw, bw, 3); +} + +/* Returns nonzero if CMD can be confused with another command + based on the first three letters of its first word. */ +static int +conflicting_3char_prefix_command (const struct command *cmd) +{ + assert (cmd >= commands && cmd < commands + COMMAND_CNT); + + return ((cmd > commands + && conflicting_3char_prefixes (cmd[-1].name, cmd[0].name)) + || (cmd < commands + COMMAND_CNT + && conflicting_3char_prefixes (cmd[0].name, cmd[1].name))); +} + +/* Ways that a set of words can match a command name. */ +enum command_match { - struct command *ocp = cp; - - /* Verify that the next token is an identifier, because we - must disambiguate this command name. */ - lex_get (); - if (token != T_ID) + MISMATCH, /* Not a match. */ + PARTIAL_MATCH, /* The words begin the command name. */ + COMPLETE_MATCH /* The words are the command name. */ + }; + +/* Figures out how well the WORD_CNT words in WORDS match CMD, + and returns the appropriate enum value. If WORDS are a + partial match for CMD and the next word in CMD is a dash, then + *DASH_POSSIBLE is set to 1 if DASH_POSSIBLE is non-null; + otherwise, *DASH_POSSIBLE is unchanged. */ +static enum command_match +cmd_match_words (const struct command *cmd, + char *const words[], size_t word_cnt, + int *dash_possible) +{ + const char *word; + size_t word_len; + size_t word_idx; + + for (word = find_word (cmd->name, &word_len), word_idx = 0; + word != NULL && word_idx < word_cnt; + word = find_word (word + word_len, &word_len), word_idx++) + if (word_len != strlen (words[word_idx]) + || memcmp (word, words[word_idx], word_len)) { - /* If there's a command whose name is the first word only, - return it. This happens with, i.e., PRINT vs. PRINT - SPACE. */ - if (ocp->word[1] == NULL) - return ocp; - - msg (SE, "%s %s.", gettext (inc), ds_value (&tokstr)); - return NULL; + size_t match_chars = match_strings (word, word_len, + words[word_idx], + strlen (words[word_idx])); + if (match_chars == 0) + { + /* Mismatch. */ + return MISMATCH; + } + else if (match_chars == 1 || match_chars == 2) + { + /* One- and two-character abbreviations are not + acceptable. */ + return MISMATCH; + } + else if (match_chars == 3) + { + /* Three-character abbreviations are acceptable + in the first word of a command if there are + no name conflicts. They are always + acceptable after the first word. */ + if (word_idx == 0 && conflicting_3char_prefix_command (cmd)) + return MISMATCH; + } + else /* match_chars > 3 */ + { + /* Four-character and longer abbreviations are + always acceptable. */ + } } - for (; cp; cp = cp->next) - if (cp->word[1] && lex_id_match (cp->word[1], tokid)) - break; + if (word == NULL && word_idx == word_cnt) + { + /* cmd->name = "FOO BAR", words[] = {"FOO", "BAR"}. */ + return COMPLETE_MATCH; + } + else if (word == NULL) + { + /* cmd->name = "FOO BAR", words[] = {"FOO", "BAR", "BAZ"}. */ + return MISMATCH; + } + else + { + /* cmd->name = "FOO BAR BAZ", words[] = {"FOO", "BAR"}. */ + if (word[0] == '-' && dash_possible != NULL) + *dash_possible = 1; + return PARTIAL_MATCH; + } +} - if (cp == NULL) - { - /* No match. If there's a command whose name is the first - word only, return it. This happens with, i.e., PRINT - vs. PRINT SPACE. */ - if (ocp->word[1] == NULL) - return ocp; - - msg (SE, "%s %s %s.", gettext (unk), ocp->word[0], tokid); - return NULL; - } +/* Returns the number of commands for which the WORD_CNT words in + WORDS are a partial or complete match. If some partial match + has a dash as the next word, then *DASH_POSSIBLE is set to 1, + otherwise it is set to 0. */ +static int +count_matching_commands (char *const words[], size_t word_cnt, + int *dash_possible) +{ + const struct command *cmd; + int cmd_match_count; + + cmd_match_count = 0; + *dash_possible = 0; + for (cmd = commands; cmd < commands + COMMAND_CNT; cmd++) + if (cmd_match_words (cmd, words, word_cnt, dash_possible) != MISMATCH) + cmd_match_count++; + + return cmd_match_count; +} + +/* Returns the command for which the WORD_CNT words in WORDS are + a complete match. Returns a null pointer if no such command + exists. */ +static const struct command * +get_complete_match (char *const words[], size_t word_cnt) +{ + const struct command *cmd; - /* Check whether the next token is an identifier. - If not, bail. */ - if (!isalpha ((unsigned char) (lex_look_ahead ()))) - { - /* Check whether there is an unambiguous interpretation. - If not, give an error. */ - if (cp->word[2] - && cp->next - && !strcmp (cp->word[1], cp->next->word[1])) - { - msg (SE, "%s %s %s.", gettext (inc), ocp->word[0], ocp->word[1]); - return NULL; - } - else - return cp; - } - } + for (cmd = commands; cmd < commands + COMMAND_CNT; cmd++) + if (cmd_match_words (cmd, words, word_cnt, NULL) == COMPLETE_MATCH) + return cmd; - /* If this command can have a third word, disambiguate based on it. */ - if (cp->word[2] - || (cp->next - && cp->next->word[2] - && !strcmp (cp->word[1], cp->next->word[1]))) + return NULL; +} + +/* Frees the WORD_CNT words in WORDS. */ +static void +free_words (char *words[], size_t word_cnt) +{ + size_t idx; + + for (idx = 0; idx < word_cnt; idx++) + free (words[idx]); +} + +/* Flags an error that the command whose name is given by the + WORD_CNT words in WORDS is unknown. */ +static void +unknown_command_error (char *const words[], size_t word_cnt) +{ + size_t idx; + size_t words_len; + char *name, *cp; + + words_len = 0; + for (idx = 0; idx < word_cnt; idx++) + words_len += strlen (words[idx]); + + cp = name = xmalloc (words_len + word_cnt + 16); + for (idx = 0; idx < word_cnt; idx++) { - struct command *ocp = cp; + if (idx != 0) + *cp++ = ' '; + cp = stpcpy (cp, words[idx]); + } + *cp = '\0'; + + msg (SE, _("Unknown command %s."), name); + + free (name); +} + + +/* Parse the command name and return a pointer to the corresponding + struct command if successful. + If not successful, return a null pointer. */ +static const struct command * +parse_command_name (void) +{ + char *words[16]; + int word_cnt; + int complete_word_cnt; + int dash_possible; + + dash_possible = 0; + word_cnt = complete_word_cnt = 0; + while (token == T_ID || (dash_possible && token == '-')) + { + int cmd_match_cnt; + assert (word_cnt < sizeof words / sizeof *words); + if (token == T_ID) + words[word_cnt++] = xstrdup (ds_value (&tokstr)); + else + words[word_cnt++] = xstrdup ("-"); + + cmd_match_cnt = count_matching_commands (words, word_cnt, + &dash_possible); + if (cmd_match_cnt == 0) + break; + else if (cmd_match_cnt == 1) + { + const struct command *command = get_complete_match (words, word_cnt); + if (command != NULL) + { + if (command->skip_entire_name) + lex_get (); + free_words (words, word_cnt); + return command; + } + } + else /* cmd_match_cnt > 1 */ + { + /* Do we have a complete command name so far? */ + if (get_complete_match (words, word_cnt) != NULL) + complete_word_cnt = word_cnt; + } lex_get (); - assert (token == T_ID); - - /* Try to find a command with this third word. - If found, bail. */ - for (; cp; cp = cp->next) - if (cp->word[2] - && !strcmp (cp->word[1], ocp->word[1]) - && lex_id_match (cp->word[2], tokid)) - break; - - if (cp != NULL) - return cp; - - /* If no command with this third word found, make sure that - there's a command with those first two words but without a - third word. */ - cp = ocp; - if (cp->word[2]) - { - msg (SE, "%s %s %s %s.", - gettext (unk), ocp->word[0], ocp->word[1], ds_value (&tokstr)); - return 0; - } } - return cp; + /* If we saw a complete command name earlier, drop back to + it. */ + if (complete_word_cnt) + { + int pushback_word_cnt; + const struct command *command; + + /* Get the command. */ + command = get_complete_match (words, complete_word_cnt); + assert (command != NULL); + + /* Figure out how many words we want to keep. + We normally want to swallow the entire command. */ + pushback_word_cnt = complete_word_cnt + 1; + if (!command->skip_entire_name) + pushback_word_cnt--; + + /* FIXME: We only support one-token pushback. */ + assert (pushback_word_cnt + 1 >= word_cnt); + + while (word_cnt > pushback_word_cnt) + { + word_cnt--; + if (strcmp (words[word_cnt], "-")) + lex_put_back_id (words[word_cnt]); + else + lex_put_back ('-'); + free (words[word_cnt]); + } + + free_words (words, word_cnt); + return command; + } + + unknown_command_error (words, word_cnt); + free_words (words, word_cnt); + return NULL; } /* Simple commands. */ @@ -544,9 +694,6 @@ cmd_n_of_cases (void) /* Value for N. */ int x; - lex_match_id ("N"); - lex_match_id ("OF"); - lex_match_id ("CASES"); if (!lex_force_int ()) return CMD_FAILURE; x = lex_integer (); @@ -561,7 +708,6 @@ cmd_n_of_cases (void) int cmd_execute (void) { - lex_match_id ("EXECUTE"); procedure (NULL, NULL); return lex_end_of_command (); } @@ -570,15 +716,12 @@ cmd_execute (void) int cmd_erase (void) { - if ( safer_mode() ) { msg (SE, _("This command not allowed when the SAFER option is set.")); return CMD_FAILURE; } - - lex_match_id ("ERASE"); if (!lex_force_match_id ("FILE")) return CMD_FAILURE; lex_match ('='); @@ -709,8 +852,6 @@ cmd_host (void) return CMD_FAILURE; } - lex_match_id ("HOST"); - #ifdef unix /* Figure out whether to invoke an interactive shell or to execute a single shell command. */ @@ -740,9 +881,6 @@ cmd_host (void) int cmd_new_file (void) { - lex_match_id ("NEW"); - lex_match_id ("FILE"); - discard_variables (); return lex_end_of_command (); @@ -752,9 +890,6 @@ cmd_new_file (void) int cmd_clear_transformations (void) { - lex_match_id ("CLEAR"); - lex_match_id ("TRANSFORMATIONS"); - if (getl_reading_script) { msg (SW, _("This command is not valid in a syntax file.")); @@ -762,6 +897,8 @@ cmd_clear_transformations (void) } cancel_transformations (); + /* FIXME: what about variables created by transformations? + They need to be properly initialized. */ return CMD_SUCCESS; } diff --git a/src/command.def b/src/command.def index c3ee874b61..a6f4610b19 100644 --- a/src/command.def +++ b/src/command.def @@ -24,13 +24,12 @@ #define PROC STATE_PROC #define ERRO STATE_ERROR -DEFCMD ("@", INIT, INPU, TRAN, PROC, cmd_include_at) UNIMPL ("ADD FILES", TRAN, ERRO, TRAN, TRAN) DEFCMD ("ADD VALUE LABELS", ERRO, INPU, TRAN, TRAN, cmd_add_value_labels) DEFCMD ("AGGREGATE", ERRO, ERRO, PROC, TRAN, cmd_aggregate) DEFCMD ("APPLY DICTIONARY", ERRO, ERRO, TRAN, TRAN, cmd_apply_dictionary) DEFCMD ("AUTORECODE", ERRO, ERRO, PROC, PROC, cmd_autorecode) -DEFCMD ("BEGIN DATA", ERRO, ERRO, PROC, PROC, cmd_begin_data) +SPCCMD ("BEGIN DATA", ERRO, ERRO, PROC, PROC, cmd_begin_data) DEFCMD ("BREAK", ERRO, INPU, TRAN, TRAN, cmd_break) UNIMPL ("CASESTOVARS", ERRO, ERRO, PROC, PROC) DEFCMD ("CLEAR TRANSFORMATIONS", ERRO, INPU, TRAN, TRAN, cmd_clear_transformations) @@ -40,11 +39,12 @@ DEFCMD ("CONDESCRIPTIVES", ERRO, ERRO, PROC, PROC, cmd_descriptives) DEFCMD ("COUNT", ERRO, INPU, TRAN, TRAN, cmd_count) DEFCMD ("CROSSTABS", ERRO, ERRO, PROC, PROC, cmd_crosstabs) DEFCMD ("DATA LIST", TRAN, INPU, TRAN, TRAN, cmd_data_list) +DEFCMD ("DEBUG EVALUATE", INIT, INPU, TRAN, PROC, cmd_debug_evaluate) DEFCMD ("DESCRIPTIVES", ERRO, ERRO, PROC, PROC, cmd_descriptives) DEFCMD ("DISPLAY", ERRO, INPU, TRAN, PROC, cmd_display) DEFCMD ("DO IF", ERRO, INPU, TRAN, TRAN, cmd_do_if) DEFCMD ("DO REPEAT", ERRO, INPU, TRAN, TRAN, cmd_do_repeat) -DEFCMD ("DOCUMENT", ERRO, INPU, TRAN, TRAN, cmd_document) +SPCCMD ("DOCUMENT", ERRO, INPU, TRAN, TRAN, cmd_document) DEFCMD ("DROP DOCUMENTS", INIT, INPU, TRAN, PROC, cmd_drop_documents) UNIMPL ("EDIT", INIT, INPU, TRAN, PROC) DEFCMD ("ELSE", ERRO, INPU, TRAN, TRAN, cmd_else) @@ -57,12 +57,11 @@ DEFCMD ("END INPUT PROGRAM", ERRO, TRAN, ERRO, ERRO, cmd_end_input_program) DEFCMD ("END LOOP", ERRO, INPU, TRAN, TRAN, cmd_end_loop) DEFCMD ("END REPEAT", ERRO, INPU, TRAN, TRAN, cmd_end_repeat) DEFCMD ("ERASE", INIT, INPU, TRAN, PROC, cmd_erase) -DEFCMD ("EVALUATE", INIT, INPU, TRAN, PROC, cmd_evaluate) DEFCMD ("EXECUTE", ERRO, ERRO, PROC, PROC, cmd_execute) DEFCMD ("EXIT", INIT, INPU, TRAN, PROC, cmd_exit) DEFCMD ("EXPORT", ERRO, ERRO, PROC, PROC, cmd_export) DEFCMD ("FILE HANDLE", INIT, INPU, TRAN, PROC, cmd_file_handle) -DEFCMD ("FILE LABEL", INIT, INPU, TRAN, PROC, cmd_file_label) +SPCCMD ("FILE LABEL", INIT, INPU, TRAN, PROC, cmd_file_label) DEFCMD ("FILE TYPE", INPU, ERRO, INPU, INPU, cmd_file_type) DEFCMD ("FILTER", ERRO, ERRO, TRAN, TRAN, cmd_filter) DEFCMD ("FINISH", INIT, INPU, TRAN, PROC, cmd_finish) @@ -87,6 +86,7 @@ DEFCMD ("MEANS", ERRO, ERRO, PROC, PROC, cmd_means) DEFCMD ("MISSING VALUES", ERRO, INPU, TRAN, TRAN, cmd_missing_values) DEFCMD ("MODIFY VARS", ERRO, ERRO, TRAN, PROC, cmd_modify_vars) DEFCMD ("NEW FILE", INIT, ERRO, INIT, INIT, cmd_new_file) +DEFCMD ("N", INIT, INPU, TRAN, TRAN, cmd_n_of_cases) DEFCMD ("N OF CASES", INIT, INPU, TRAN, TRAN, cmd_n_of_cases) UNIMPL ("NUMBERED", INIT, INPU, TRAN, PROC) DEFCMD ("NUMERIC", ERRO, INPU, TRAN, TRAN, cmd_numeric) @@ -105,7 +105,7 @@ DEFCMD ("QUIT", INIT, INPU, TRAN, PROC, cmd_exit) DEFCMD ("RECODE", ERRO, INPU, TRAN, TRAN, cmd_recode) DEFCMD ("RECORD TYPE", ERRO, INPU, ERRO, ERRO, cmd_record_type) UNIMPL ("REFORMAT", ERRO, ERRO, TRAN, TRAN) -DEFCMD ("REMARK", INIT, INPU, TRAN, PROC, cmd_remark) +SPCCMD ("REMARK", INIT, INPU, TRAN, PROC, cmd_remark) DEFCMD ("RENAME VARIABLES", ERRO, INPU, TRAN, PROC, cmd_rename_variables) DEFCMD ("REPEATING DATA", ERRO, INPU, ERRO, ERRO, cmd_repeating_data) DEFCMD ("REREAD", ERRO, INPU, ERRO, ERRO, cmd_reread) @@ -115,13 +115,14 @@ DEFCMD ("SAVE", ERRO, ERRO, PROC, PROC, cmd_save) DEFCMD ("SELECT IF", ERRO, ERRO, TRAN, TRAN, cmd_select_if) DEFCMD ("SET", INIT, INPU, TRAN, PROC, cmd_set) DEFCMD ("SHOW", INIT, INPU, TRAN, PROC, cmd_show) +DEFCMD ("SORT", ERRO, ERRO, PROC, PROC, cmd_sort_cases) DEFCMD ("SORT CASES", ERRO, ERRO, PROC, PROC, cmd_sort_cases) DEFCMD ("SPLIT FILE", ERRO, INPU, TRAN, TRAN, cmd_split_file) DEFCMD ("STRING", ERRO, INPU, TRAN, TRAN, cmd_string) -DEFCMD ("SUBTITLE", INIT, INPU, TRAN, PROC, cmd_subtitle) +SPCCMD ("SUBTITLE", INIT, INPU, TRAN, PROC, cmd_subtitle) DEFCMD ("SYSFILE INFO", INIT, INPU, TRAN, PROC, cmd_sysfile_info) DEFCMD ("TEMPORARY", ERRO, ERRO, TRAN, TRAN, cmd_temporary) -DEFCMD ("TITLE", INIT, INPU, TRAN, PROC, cmd_title) +SPCCMD ("TITLE", INIT, INPU, TRAN, PROC, cmd_title) DEFCMD ("T-TEST", ERRO, ERRO, PROC, PROC, cmd_t_test) UNIMPL ("UPDATE", TRAN, ERRO, TRAN, TRAN) DEFCMD ("VALUE LABELS", ERRO, INPU, TRAN, TRAN, cmd_value_labels) diff --git a/src/command.h b/src/command.h index b051a4be1b..423a8a3019 100644 --- a/src/command.h +++ b/src/command.h @@ -43,15 +43,17 @@ enum extern int pgm_state; extern const char *cur_proc; -void cmd_init (void); int cmd_parse (void); /* Prototype all the command functions. */ #define DEFCMD(NAME, T1, T2, T3, T4, FUNC) \ int FUNC (void); +#define SPCCMD(NAME, T1, T2, T3, T4, FUNC) \ + int FUNC (void); #define UNIMPL(NAME, T1, T2, T3, T4) #include "command.def" #undef DEFCMD +#undef SPCCMD #undef UNIMPL #endif /* !command_h */ diff --git a/src/compute.c b/src/compute.c index 9c20e0ee78..b08e76fedd 100644 --- a/src/compute.c +++ b/src/compute.c @@ -75,8 +75,6 @@ cmd_compute (void) struct lvalue *lvalue = NULL; struct compute_trns *compute = NULL; - lex_match_id ("COMPUTE"); - lvalue = lvalue_parse (); if (lvalue == NULL) goto fail; @@ -232,7 +230,6 @@ cmd_if (void) struct compute_trns *compute = NULL; struct lvalue *lvalue = NULL; - lex_match_id ("IF"); compute = compute_trns_create (); /* Test expression. */ @@ -442,25 +439,3 @@ lvalue_destroy (struct lvalue *lvalue) expr_free (lvalue->element); free (lvalue); } - -/* EVALUATE. */ - -int -cmd_evaluate (void) -{ - struct expression *expr; - - lex_match_id ("EVALUATE"); - expr = expr_parse (PXP_DUMP); - if (!expr) - return CMD_FAILURE; - - expr_free (expr); - if (token != '.') - { - msg (SE, _("Extra characters after expression.")); - return CMD_FAILURE; - } - - return CMD_SUCCESS; -} diff --git a/src/correlations.q b/src/correlations.q index 5b4fc2b38b..4c9f74b7f3 100644 --- a/src/correlations.q +++ b/src/correlations.q @@ -72,9 +72,6 @@ internal_cmd_correlations (void) cor_list = cor_last = NULL; matrix_file = NULL; - lex_match_id ("PEARSON"); - lex_match_id ("CORRELATIONS"); - if (!parse_correlations (&cmd)) return CMD_FAILURE; free_correlations (&cmd); diff --git a/src/count.c b/src/count.c index 2ab52e250e..0fc7370bb8 100644 --- a/src/count.c +++ b/src/count.c @@ -134,8 +134,6 @@ cmd_count (void) struct count_trns *trns; /* Transformation. */ struct cnt_var_info *head; /* First counting in chain. */ - lex_match_id ("COUNT"); - /* Parses each slash-delimited specification. */ head = cnt = xmalloc (sizeof *cnt); for (;;) diff --git a/src/crosstabs.q b/src/crosstabs.q index 97db9763d7..b16cb02c75 100644 --- a/src/crosstabs.q +++ b/src/crosstabs.q @@ -185,7 +185,6 @@ internal_cmd_crosstabs (void) pl_tc = pool_create (); pl_col = pool_create (); - lex_match_id ("CROSSTABS"); if (!parse_crosstabs (&cmd)) return CMD_FAILURE; diff --git a/src/data-list.c b/src/data-list.c index a5ec8da24c..da75d5d318 100644 --- a/src/data-list.c +++ b/src/data-list.c @@ -111,9 +111,6 @@ cmd_data_list (void) /* 0=print no table, 1=print table. (TABLE subcommand.) */ int table = -1; - lex_match_id ("DATA"); - lex_match_id ("LIST"); - if (!case_source_is_complex (vfm_source)) discard_variables (); @@ -237,6 +234,9 @@ cmd_data_list (void) dump_free_table (dls); } + if (!dfm_open_for_reading (dls->handle)) + goto error; + if (vfm_source != NULL) { struct data_list_pgm *new_pgm; @@ -783,7 +783,7 @@ dump_fixed_table (const struct dls_var_spec *specs, fmt_to_string (&spec->input)); } - filename = fh_handle_name (handle); + filename = handle_get_filename (handle); if (filename == NULL) filename = ""; buf = local_alloc (strlen (filename) + INT_DIGITS + 80); @@ -797,7 +797,6 @@ dump_fixed_table (const struct dls_var_spec *specs, tab_title (t, 0, buf); tab_submit (t); - fh_handle_name (NULL); local_free (buf); } @@ -915,7 +914,7 @@ dump_free_table (const struct data_list_pgm *dls) { const char *filename; - filename = fh_handle_name (dls->handle); + filename = handle_get_filename (dls->handle); if (filename == NULL) filename = ""; tab_title (t, 1, @@ -926,7 +925,6 @@ dump_free_table (const struct data_list_pgm *dls) } tab_submit (t); - fh_handle_name (NULL); } /* Input procedure. */ @@ -1322,9 +1320,6 @@ cmd_repeating_data (void) /* Bits are set when a particular subcommand has been seen. */ unsigned seen = 0; - lex_match_id ("REPEATING"); - lex_match_id ("DATA"); - assert (case_source_is_complex (vfm_source)); rpd = xmalloc (sizeof *rpd); @@ -1539,9 +1534,9 @@ cmd_repeating_data (void) /* Calculate starts_end, cont_end if necessary. */ if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL) - rpd->starts_end.num = fh_record_width (rpd->handle); + rpd->starts_end.num = handle_get_record_width (rpd->handle); if (rpd->cont_end.num == 0 && rpd->starts_end.var == NULL) - rpd->cont_end.num = fh_record_width (rpd->handle); + rpd->cont_end.num = handle_get_record_width (rpd->handle); /* Calculate length if possible. */ if ((seen & 4) == 0) diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000000..441504f2fb --- /dev/null +++ b/src/debug.c @@ -0,0 +1,45 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2004 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 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., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include "command.h" +#include "error.h" +#include "expr.h" +#include "lexer.h" +#include "var.h" + +int +cmd_debug_evaluate (void) +{ + struct expression *expr; + + discard_variables (); + expr = expr_parse (PXP_NONE); + if (!expr) + return CMD_FAILURE; + + expr_free (expr); + if (token != '.') + { + msg (SE, _("Extra characters after expression.")); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} diff --git a/src/descript.q b/src/descript.q index 3e5c6a4e42..9ac10325ff 100644 --- a/src/descript.q +++ b/src/descript.q @@ -193,8 +193,6 @@ cmd_descriptives (void) v_variables = NULL; n_variables = 0; - lex_match_id ("DESCRIPTIVES"); - lex_match_id ("CONDESCRIPTIVES"); if (!parse_descriptives (&cmd)) goto lossage; diff --git a/src/dfm.c b/src/dfm.c index cec6234f98..f9aa574111 100644 --- a/src/dfm.c +++ b/src/dfm.c @@ -41,14 +41,17 @@ struct dfm_fhuser_ext { struct file_ext file; /* Associated file. */ + struct file_locator where; /* Current location in data file. */ char *line; /* Current line, not null-terminated. */ + size_t size; /* Number of bytes allocated for line. */ size_t len; /* Length of line. */ char *ptr; /* Pointer into line that is returned by dfm_get_record(). */ - size_t size; /* Number of bytes allocated for line. */ int advance; /* Nonzero=dfm_get_record() reads a new record; otherwise returns current record. */ + int saw_begin_data; /* For inline_file only, whether we've + already read a BEGIN DATA line. */ }; /* These are defined at the end of this file. */ @@ -72,7 +75,7 @@ dfm_close (struct file_handle *h) read_record (h); msg (VM (2), _("%s: Closing data-file handle %s."), - fh_handle_filename (h), fh_handle_name (h)); + handle_get_filename (h), handle_get_name (h)); assert (h->class == &dfm_r_class || h->class == &dfm_w_class); if (ext->file.file) { @@ -84,152 +87,131 @@ dfm_close (struct file_handle *h) free (ext); } -/* Initializes EXT properly as an inline data file. */ -static void -open_inline_file (struct dfm_fhuser_ext *ext) +/* Opens a file handle for reading as a data file. Returns + nonzero only if successful. */ +int +dfm_open_for_reading (struct file_handle *h) { - /* We want to indicate that the file is open, that we are not at - eof, and that another line needs to be read in. */ + struct dfm_fhuser_ext *ext; + + if (h->class != NULL) + { + if (h->class == &dfm_r_class) + return 1; + else + { + msg (ME, _("Cannot read from file %s already opened for %s."), + handle_get_name (h), gettext (h->class->name)); + return 0; + } + } + + ext = xmalloc (sizeof *ext); + ext->where.filename = handle_get_filename (h); + ext->where.line_number = 0; ext->file.file = NULL; ext->line = xmalloc (128); -#if !PRODUCTION - strcpy (ext->line, _("<>")); -#endif - ext->len = strlen (ext->line); - ext->ptr = ext->line; + ext->len = 0; + ext->ptr = NULL; ext->size = 128; ext->advance = 1; -} - -/* Opens a file handle for reading as a data file. */ -static int -open_file_r (struct file_handle *h) -{ - struct dfm_fhuser_ext ext; - - h->where.line_number = 0; - ext.file.file = NULL; - ext.line = NULL; - ext.len = 0; - ext.ptr = NULL; - ext.size = 0; - ext.advance = 0; + ext->saw_begin_data = 0; msg (VM (1), _("%s: Opening data-file handle %s for reading."), - fh_handle_filename (h), fh_handle_name (h)); + handle_get_filename (h), handle_get_name (h)); assert (h != NULL); - if (h == inline_file) - { - char *s; - - /* WTF can't this just be done with tokens? - Is this really a special case? - FIXME! */ - do - { - char *cp; - - if (!getl_read_line ()) - { - msg (SE, _("BEGIN DATA expected.")); - err_failure (); - } - - /* Skip leading whitespace, separate out first word, so that - S points to a single word reduced to lowercase. */ - s = ds_value (&getl_buf); - while (isspace ((unsigned char) *s)) - s++; - for (cp = s; isalpha ((unsigned char) *cp); cp++) - *cp = tolower ((unsigned char) (*cp)); - ds_truncate (&getl_buf, cp - s); - } - while (*s == '\0'); - - if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n"))) - { - msg (SE, _("BEGIN DATA expected.")); - err_cond_fail (); - lex_preprocess_line (); - return 0; - } - getl_prompt = GETL_PRPT_DATA; - - open_inline_file (&ext); - } - else + if (h != inline_file) { - ext.file.filename = xstrdup (h->norm_fn); - ext.file.mode = "rb"; - ext.file.file = NULL; - ext.file.sequence_no = NULL; - ext.file.param = NULL; - ext.file.postopen = NULL; - ext.file.preclose = NULL; - if (!fn_open_ext (&ext.file)) + ext->file.filename = xstrdup (handle_get_filename (h)); + ext->file.mode = "rb"; + ext->file.file = NULL; + ext->file.sequence_no = NULL; + ext->file.param = NULL; + ext->file.postopen = NULL; + ext->file.preclose = NULL; + if (!fn_open_ext (&ext->file)) { - msg (ME, _("An error occurred while opening \"%s\" for reading " - "as a data file: %s."), h->fn, strerror (errno)); - err_cond_fail (); - return 0; + msg (ME, _("Could not open \"%s\" for reading " + "as a data file: %s."), + handle_get_filename (h), strerror (errno)); + goto error; } } h->class = &dfm_r_class; - h->ext = xmalloc (sizeof (struct dfm_fhuser_ext)); - memcpy (h->ext, &ext, sizeof (struct dfm_fhuser_ext)); - + h->ext = ext; return 1; + + error: + err_cond_fail (); + free (ext); + return 0; } /* Opens a file handle for writing as a data file. */ -static int -open_file_w (struct file_handle *h) +int +dfm_open_for_writing (struct file_handle *h) { - struct dfm_fhuser_ext ext; + struct dfm_fhuser_ext *ext; - ext.file.file = NULL; - ext.line = NULL; - ext.len = 0; - ext.ptr = NULL; - ext.size = 0; - ext.advance = 0; + if (h->class != NULL) + { + if (h->class == &dfm_w_class) + return 1; + else + { + msg (ME, _("Cannot write to file %s already opened for %s."), + handle_get_name (h), gettext (h->class->name)); + err_cond_fail (); + return 0; + } + } - h->where.line_number = 0; + ext = xmalloc (sizeof *ext); + ext->where.filename = handle_get_filename (h); + ext->where.line_number = 0; + ext->file.file = NULL; + ext->line = NULL; + ext->len = 0; + ext->ptr = NULL; + ext->size = 0; + ext->advance = 0; msg (VM (1), _("%s: Opening data-file handle %s for writing."), - fh_handle_filename (h), fh_handle_name (h)); + handle_get_filename (h), handle_get_name (h)); assert (h != NULL); if (h == inline_file) { msg (ME, _("Cannot open the inline file for writing.")); - err_cond_fail (); - return 0; + goto error; } - ext.file.filename = xstrdup (h->norm_fn); - ext.file.mode = "wb"; - ext.file.file = NULL; - ext.file.sequence_no = NULL; - ext.file.param = NULL; - ext.file.postopen = NULL; - ext.file.preclose = NULL; + ext->file.filename = xstrdup (handle_get_filename (h)); + ext->file.mode = "wb"; + ext->file.file = NULL; + ext->file.sequence_no = NULL; + ext->file.param = NULL; + ext->file.postopen = NULL; + ext->file.preclose = NULL; - if (!fn_open_ext (&ext.file)) + if (!fn_open_ext (&ext->file)) { msg (ME, _("An error occurred while opening \"%s\" for writing " - "as a data file: %s."), h->fn, strerror (errno)); - err_cond_fail (); - return 0; + "as a data file: %s."), + handle_get_filename (h), strerror (errno)); + goto error; } h->class = &dfm_w_class; - h->ext = xmalloc (sizeof (struct dfm_fhuser_ext)); - memcpy (h->ext, &ext, sizeof (struct dfm_fhuser_ext)); - + h->ext = ext; return 1; + + error: + free (ext); + err_cond_fail (); + return 0; } /* Ensures that the line buffer in file handle with extension EXT is @@ -362,6 +344,44 @@ read_record (struct file_handle *h) if (h == inline_file) { + if (!ext->saw_begin_data) + { + char *s; + + ext->saw_begin_data = 1; + + /* FIXME: WTF can't this just be done with tokens? + Is this really a special case? */ + do + { + char *cp; + + if (!getl_read_line ()) + { + msg (SE, _("BEGIN DATA expected.")); + err_failure (); + } + + /* Skip leading whitespace, separate out first word, so that + S points to a single word reduced to lowercase. */ + s = ds_value (&getl_buf); + while (isspace ((unsigned char) *s)) + s++; + for (cp = s; isalpha ((unsigned char) *cp); cp++) + *cp = tolower ((unsigned char) (*cp)); + ds_truncate (&getl_buf, cp - s); + } + while (*s == '\0'); + + if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n"))) + { + msg (SE, _("BEGIN DATA expected.")); + lex_preprocess_line (); + goto eof; + } + getl_prompt = GETL_PRPT_DATA; + } + if (!getl_read_line ()) { msg (SE, _("Unexpected end-of-file while reading data in BEGIN " @@ -372,7 +392,7 @@ read_record (struct file_handle *h) err_failure (); } - h->where.line_number++; + ext->where.line_number++; if (ds_length (&getl_buf) >= 8 && !strncasecmp (ds_value (&getl_buf), "end data", 8)) @@ -387,7 +407,7 @@ read_record (struct file_handle *h) } else { - if (h->recform == FH_RF_VARIABLE) + if (handle_get_mode (h) == MODE_TEXT) { /* PORTME: here you should adapt the routine to your system's concept of a "line" of text. */ @@ -398,31 +418,32 @@ read_record (struct file_handle *h) if (ferror (ext->file.file)) { msg (ME, _("Error reading file %s: %s."), - fh_handle_name (h), strerror (errno)); + handle_get_name (h), strerror (errno)); err_cond_fail (); } goto eof; } ext->len = (size_t) read_len; } - else if (h->recform == FH_RF_FIXED) + else if (handle_get_mode (h) == MODE_BINARY) { + size_t record_width = handle_get_record_width (h); size_t amt; - if (ext->size < h->lrecl) + if (ext->size < record_width) { - ext->size = h->lrecl; + ext->size = record_width; ext->line = xmalloc (ext->size); } - amt = fread (ext->line, 1, h->lrecl, ext->file.file); - if (h->lrecl != amt) + amt = fread (ext->line, 1, record_width, ext->file.file); + if (record_width != amt) { if (ferror (ext->file.file)) msg (ME, _("Error reading file %s: %s."), - fh_handle_name (h), strerror (errno)); + handle_get_name (h), strerror (errno)); else if (amt != 0) msg (ME, _("%s: Partial record at end of file."), - fh_handle_name (h)); + handle_get_name (h)); else goto eof; @@ -433,12 +454,12 @@ read_record (struct file_handle *h) else assert (0); - h->where.line_number++; + ext->where.line_number++; } /* Strip trailing whitespace, I forget why. But there's a good reason, I'm sure. I'm too scared to eliminate this code. */ - if (h->recform == FH_RF_VARIABLE) + if (handle_get_mode (h) == MODE_TEXT) { while (ext->len && isspace ((unsigned char) ext->line[ext->len - 1])) ext->len--; @@ -451,7 +472,7 @@ read_record (struct file_handle *h) return; eof: - /*hit eof or an error, clean up everything. */ + /* Hit eof or an error, clean up everything. */ if (ext->line) free (ext->line); ext->size = 0; @@ -469,50 +490,29 @@ eof: char * dfm_get_record (struct file_handle *h, int *len) { + struct dfm_fhuser_ext *ext; + assert (h != NULL); + assert (h->class == &dfm_r_class); + assert (h->ext != NULL); - if (h->class == NULL) - { - if (!open_file_r (h)) - return NULL; - read_record (h); - } - else if (h->class != &dfm_r_class) + ext = h->ext; + if (ext->advance) { - msg (ME, _("Cannot read from file %s already opened for %s."), - fh_handle_name (h), gettext (h->class->name)); - goto lossage; - } - else - { - struct dfm_fhuser_ext *ext = h->ext; - - if (ext->advance) - { - if (ext->line) - read_record (h); - else - { - msg (SE, _("Attempt to read beyond end-of-file on file %s."), - fh_handle_name (h)); - goto lossage; - } - } + if (ext->line) + read_record (h); + else + { + msg (SE, _("Attempt to read beyond end-of-file on file %s."), + handle_get_name (h)); + goto lossage; + } } - { - struct dfm_fhuser_ext *ext = h->ext; - - if (ext) - { - ext->advance = 0; - if (len) - *len = ext->len - (ext->ptr - ext->line); - return ext->ptr; - } - } - - return NULL; + ext->advance = 0; + if (len) + *len = ext->len - (ext->ptr - ext->line); + return ext->ptr; lossage: /* Come here on reading beyond eof or reading from a file already @@ -577,31 +577,21 @@ dfm_get_cur_col (struct file_handle *h) int dfm_put_record (struct file_handle *h, const char *rec, size_t len) { + struct dfm_fhuser_ext *ext; char *ptr; size_t amt; - if (h->class == NULL) - { - if (!open_file_w (h)) - return 0; - } - else if (h->class != &dfm_w_class) - { - msg (ME, _("Cannot write to file %s already opened for %s."), - fh_handle_name (h), gettext (h->class->name)); - err_cond_fail (); - return 0; - } + assert (h != NULL); + assert (h->class == &dfm_w_class); + assert (h->ext != NULL); - if (h->recform == FH_RF_FIXED && len < h->lrecl) + ext = h->ext; + if (handle_get_mode (h) == MODE_BINARY && len < handle_get_record_width (h)) { - int ch; - - amt = h->lrecl; + amt = handle_get_record_width (h); ptr = local_alloc (amt); memcpy (ptr, rec, len); - ch = h->mode == FH_MD_CHARACTER ? ' ' : 0; - memset (&ptr[len], ch, amt - len); + memset (&ptr[len], 0, amt - len); } else { @@ -609,10 +599,10 @@ dfm_put_record (struct file_handle *h, const char *rec, size_t len) amt = len; } - if (1 != fwrite (ptr, amt, 1, ((struct dfm_fhuser_ext *) h->ext)->file.file)) + if (1 != fwrite (ptr, amt, 1, ext->file.file)) { - msg (ME, _("Error writing file %s: %s."), fh_handle_name (h), - strerror (errno)); + msg (ME, _("Error writing file %s: %s."), + handle_get_name (h), strerror (errno)); err_cond_fail (); return 0; } @@ -627,16 +617,24 @@ dfm_put_record (struct file_handle *h, const char *rec, size_t len) void dfm_push (struct file_handle *h) { + struct dfm_fhuser_ext *ext = h->ext; + + assert (h->class == &dfm_r_class || h->class == &dfm_w_class); + assert (ext != NULL); if (h != inline_file) - err_push_file_locator (&h->where); + err_push_file_locator (&ext->where); } /* Pops the filename and line number from the fn/ln stack. */ void dfm_pop (struct file_handle *h) { + struct dfm_fhuser_ext *ext = h->ext; + + assert (h->class == &dfm_r_class || h->class == &dfm_w_class); + assert (ext != NULL); if (h != inline_file) - err_pop_file_locator (&h->where); + err_pop_file_locator (&ext->where); } /* BEGIN DATA...END DATA procedure. */ @@ -661,17 +659,16 @@ cmd_begin_data (void) /* Initialize inline_file. */ msg (VM (1), _("inline file: Opening for reading.")); - inline_file->class = &dfm_r_class; - inline_file->ext = xmalloc (sizeof (struct dfm_fhuser_ext)); - open_inline_file (inline_file->ext); + dfm_open_for_reading (inline_file); + ext = inline_file->ext; + ext->saw_begin_data = 1; /* We don't actually read from the inline file. The input procedure is what reads from it. */ getl_prompt = GETL_PRPT_DATA; procedure (NULL, NULL); - + ext = inline_file->ext; - if (ext && ext->line) { msg (MW, _("Skipping remaining inline data.")); diff --git a/src/dfm.h b/src/dfm.h index f561a45eee..30f12846a8 100644 --- a/src/dfm.h +++ b/src/dfm.h @@ -30,6 +30,8 @@ /* I/O utilities. */ struct file_handle; +int dfm_open_for_reading (struct file_handle *handle); +int dfm_open_for_writing (struct file_handle *handle); char *dfm_get_record (struct file_handle *handle, int *len); int dfm_put_record (struct file_handle *handle, const char *rec, size_t len); diff --git a/src/do-if.c b/src/do-if.c index 3397106963..0dd6466a52 100644 --- a/src/do-if.c +++ b/src/do-if.c @@ -155,8 +155,6 @@ cmd_else (void) { struct do_if_trns *t; - lex_match_id ("ELSE"); - /* Check that we're in a pleasing situation. */ if (!ctl_stack || ctl_stack->type != CST_DO_IF) { @@ -198,8 +196,6 @@ cmd_end_if (void) /* List iterator. */ struct do_if_trns *iter; - lex_match_id ("IF"); - /* Check that we're in a pleasing situation. */ if (!ctl_stack || ctl_stack->type != CST_DO_IF) { @@ -252,8 +248,6 @@ parse_do_if (void) struct do_if_trns *t; struct expression *e; - lex_match_id ("IF"); - e = expr_parse (PXP_BOOLEAN); if (!e) return NULL; diff --git a/src/error.c b/src/error.c index 3dadab4246..65a7b0f10a 100644 --- a/src/error.c +++ b/src/error.c @@ -102,6 +102,8 @@ msg (int class, const char *format, ...) e.text = buf.string; err_vmsg (&e); } + + ds_destroy (&buf); } /* Terminate due to fatal error in input. */ diff --git a/src/expr-evl.c b/src/expr-evl.c index 62a9f6b2ea..d031eff6a0 100644 --- a/src/expr-evl.c +++ b/src/expr-evl.c @@ -1167,6 +1167,7 @@ expr_evaluate (struct expression *e, const struct ccase *c, int case_num, sp->f = SYSMIS; break; } + assert (c != NULL); sp->f = c->data[v->var[rindx - 1]->fv].f; } break; @@ -1195,6 +1196,7 @@ expr_evaluate (struct expression *e, const struct ccase *c, int case_num, v = vect->var[rindx - 1]; sp->c = pool_alloc (e->pool, v->width + 1); sp->c[0] = v->width; + assert (c != NULL); memcpy (&sp->c[1], c->data[v->fv].s, v->width); } break; @@ -1212,6 +1214,7 @@ expr_evaluate (struct expression *e, const struct ccase *c, int case_num, break; case OP_NUM_VAR: sp++; + assert (c != NULL); sp->f = c->data[(*vars)->fv].f; if (is_num_user_missing (sp->f, *vars)) sp->f = SYSMIS; @@ -1224,6 +1227,7 @@ expr_evaluate (struct expression *e, const struct ccase *c, int case_num, sp++; sp->c = pool_alloc (e->pool, width + 1); sp->c[0] = width; + assert (c != NULL); memcpy (&sp->c[1], &c->data[(*vars)->fv], width); vars++; } @@ -1263,15 +1267,18 @@ expr_evaluate (struct expression *e, const struct ccase *c, int case_num, break; case OP_NUM_SYS: sp++; + assert (c != NULL); sp->f = c->data[*op++].f == SYSMIS; break; case OP_STR_MIS: sp++; + assert (c != NULL); sp->f = is_str_user_missing (c->data[(*vars)->fv].s, *vars); vars++; break; case OP_NUM_VAL: sp++; + assert (c != NULL); sp->f = c->data[*op++].f; break; case OP_CASENUM: diff --git a/src/expr-prs.c b/src/expr-prs.c index fee00a3e93..701347894d 100644 --- a/src/expr-prs.c +++ b/src/expr-prs.c @@ -120,10 +120,7 @@ expr_parse (int flags) /* If we're debugging or the user requested it, print the postfix representation. */ -#if GLOBAL_DEBUGGING -#if !DEBUGGING - if (flags & PXP_DUMP) -#endif +#if DEBUGGING debug_print_postfix (e); #endif diff --git a/src/expr.h b/src/expr.h index 5a1da2aef9..e2cc9e3657 100644 --- a/src/expr.h +++ b/src/expr.h @@ -24,8 +24,6 @@ enum { PXP_NONE = 000, /* No flags. */ - PXP_DUMP = 001, /* Dump postfix representation to screen; - only for use by EVALUATE. */ /* Specify expression type. */ PXP_BOOLEAN = 002, /* Coerce return value to Boolean. */ diff --git a/src/file-handle.h b/src/file-handle.h index c7b4550535..145d520228 100644 --- a/src/file-handle.h +++ b/src/file-handle.h @@ -30,24 +30,6 @@ #include #include "error.h" -/* Record formats. */ -enum - { - FH_RF_FIXED, /* Fixed length records. */ - FH_RF_VARIABLE, /* Variable length records. */ - FH_RF_SPANNED /* ? */ - }; - -/* File modes. */ -enum - { - FH_MD_CHARACTER, /* Character data. */ - FH_MD_IMAGE, /* ? */ - FH_MD_BINARY, /* Character and/or binary data. */ - FH_MD_MULTIPUNCH, /* Column binary data (not supported). */ - FH_MD_360 /* ? */ - }; - struct file_handle; /* Services that fhusers provide to fhp. */ @@ -56,28 +38,24 @@ struct fh_ext_class int magic; /* Magic identifier for fhuser. */ const char *name; /* String identifier for fhuser. */ - void (*close) (struct file_handle *); - /* Closes any associated file, etc. */ + void (*close) (struct file_handle *); /* Closes the file. */ }; -/* Opaque structure. The `ext' member is an exception for use by - subclasses. `where.ln' is also acceptable. */ +/* Mostly-opaque structure. */ struct file_handle { - /* name must be the first member. */ - const char *name; /* File handle identifier. */ - char *norm_fn; /* Normalized filename. */ - char *fn; /* Filename as provided by user. */ - struct file_locator where; /* Used for reporting error messages. */ - - int recform; /* One of FH_RF_*. */ - size_t lrecl; /* Length of records for FH_RF_FIXED. */ - int mode; /* One of FH_MD_*. */ - - struct fh_ext_class *class; /* Polymorphism support. */ + struct private_file_handle *private; + const struct fh_ext_class *class; /* Polymorphism support. */ void *ext; /* Extension struct for fhuser use. */ }; +/* File modes. */ +enum file_handle_mode + { + MODE_TEXT, /* New-line delimited lines. */ + MODE_BINARY /* Fixed-length records. */ + }; + /* Pointer to the file handle that corresponds to data in the command file entered via BEGIN DATA/END DATA. */ extern struct file_handle *inline_file; @@ -86,14 +64,13 @@ extern struct file_handle *inline_file; void fh_init_files (void); /* Opening and closing handles. */ -struct file_handle *fh_get_handle_by_name (const char name[9]); -struct file_handle *fh_get_handle_by_filename (const char *filename); struct file_handle *fh_parse_file_handle (void); void fh_close_handle (struct file_handle *handle); /* Handle info. */ -const char *fh_handle_name (const struct file_handle *handle); -char *fh_handle_filename (struct file_handle *handle); -size_t fh_record_width (struct file_handle *handle); +const char *handle_get_name (const struct file_handle *handle); +const char *handle_get_filename (const struct file_handle *handle); +enum file_handle_mode handle_get_mode (const struct file_handle *); +size_t handle_get_record_width (const struct file_handle *); #endif /* !file_handle.h */ diff --git a/src/file-handle.q b/src/file-handle.q index 77291a45e4..dc1e9383c6 100644 --- a/src/file-handle.q +++ b/src/file-handle.q @@ -25,7 +25,6 @@ #include "alloc.h" #include "filename.h" #include "command.h" -#include "hash.h" #include "lexer.h" #include "getline.h" #include "error.h" @@ -33,12 +32,29 @@ #include "var.h" /* (headers) */ -#include "debug-print.h" - -static struct hsh_table *files; +/* File handle private data. */ +struct private_file_handle + { + char *name; /* File handle identifier. */ + char *filename; /* Filename as provided by user. */ + struct file_identity *identity; /* For checking file identity. */ + struct file_locator where; /* Used for reporting error messages. */ + enum file_handle_mode mode; /* File mode. */ + size_t length; /* Length of fixed-format records. */ + }; + +/* Linked list of file handles. */ +struct file_handle_list + { + struct file_handle *handle; + struct file_handle_list *next; + }; + +static struct file_handle_list *file_handles; struct file_handle *inline_file; -static void init_file_handle (struct file_handle * handle); +static struct file_handle *create_file_handle (const char *handle_name, + const char *filename); /* (specification) "FILE HANDLE" (fh_): @@ -50,29 +66,65 @@ static void init_file_handle (struct file_handle * handle); /* (declarations) */ /* (functions) */ +static struct file_handle * +get_handle_with_name (const char *handle_name) +{ + struct file_handle_list *iter; + + for (iter = file_handles; iter != NULL; iter = iter->next) + if (!strcmp (handle_name, iter->handle->private->name)) + return iter->handle; + return NULL; +} + +static struct file_handle * +get_handle_for_filename (const char *filename) +{ + struct file_identity *identity; + struct file_handle_list *iter; + + /* First check for a file with the same identity. */ + identity = fn_get_identity (filename); + if (identity != NULL) + { + for (iter = file_handles; iter != NULL; iter = iter->next) + if (iter->handle->private->identity != NULL + && !fn_compare_file_identities (identity, + iter->handle->private->identity)) + { + fn_free_identity (identity); + return iter->handle; + } + fn_free_identity (identity); + } + + /* Then check for a file with the same name. */ + for (iter = file_handles; iter != NULL; iter = iter->next) + if (!strcmp (filename, iter->handle->private->filename)) + return iter->handle; + + return NULL; +} + int cmd_file_handle (void) { char handle_name[9]; - char *handle_name_p = handle_name; struct cmd_file_handle cmd; - struct file_handle *fp; + struct file_handle *handle; - lex_get (); if (!lex_force_id ()) return CMD_FAILURE; strcpy (handle_name, tokid); - fp = NULL; - if (files) - fp = hsh_find (files, &handle_name_p); - if (fp) + handle = get_handle_with_name (handle_name); + if (handle != NULL) { - msg (SE, _("File handle %s had already been defined to refer to " - "file %s. It is not possible to redefine a file " - "handle within a session."), - tokid, fp->fn); + msg (SE, _("File handle %s already refers to " + "file %s. File handle cannot be redefined within a " + "session."), + tokid, handle->private->filename); return CMD_FAILURE; } @@ -96,68 +148,34 @@ cmd_file_handle (void) goto lossage; } - fp = xmalloc (sizeof *fp); - init_file_handle (fp); - - switch (cmd.recform) + handle = create_file_handle (handle_name, cmd.s_name); + switch (cmd.mode) { - case FH_FIXED: + case FH_CHARACTER: + handle->private->mode = MODE_TEXT; + break; + case FH_IMAGE: + handle->private->mode = MODE_BINARY; if (cmd.n_lrecl == NOT_LONG) { - msg (SE, _("Fixed length records were specified on /RECFORM, but " - "record length was not specified on /LRECL. 80-character " - "records will be assumed.")); - cmd.n_lrecl = 80; + msg (SE, _("Fixed-length records were specified on /RECFORM, but " + "record length was not specified on /LRECL. " + "Assuming 1024-character records.")); + handle->private->length = 1024; } else if (cmd.n_lrecl < 1) { msg (SE, _("Record length (%ld) must be at least one byte. " - "80-character records will be assumed."), cmd.n_lrecl); - cmd.n_lrecl = 80; + "1-character records will be assumed."), cmd.n_lrecl); + handle->private->length = 1; } - fp->recform = FH_RF_FIXED; - fp->lrecl = cmd.n_lrecl; - break; - case FH_VARIABLE: - fp->recform = FH_RF_VARIABLE; - break; - case FH_SPANNED: - msg (SE, - _("%s is not implemented, as the author doesn't know what it is supposed to do. Send a note to %s.") , - "/RECFORM SPANNED",PACKAGE_BUGREPORT); - break; - default: - assert (0); - } - - switch (cmd.mode) - { - case FH_CHARACTER: - fp->mode = FH_MD_CHARACTER; - break; - case FH_IMAGE: - msg (SE, - _("%s is not implemented, as the author doesn't know what it is supposed to do. Send a note to %s.") , - "/MODE IMAGE",PACKAGE_BUGREPORT); - break; - case FH_BINARY: - fp->mode = FH_MD_BINARY; - break; - case FH_MULTIPUNCH: - msg (SE, _("%s is not implemented. If you care, complain."),"/MODE MULTIPUNCH"); - break; - case FH__360: - msg (SE, _("%s is not implemented. If you care, complain."),"/MODE 360"); + else + handle->private->length = cmd.n_lrecl; break; default: assert (0); } - fp->name = xstrdup (handle_name); - fp->norm_fn = fn_normalize (cmd.s_name); - fp->where.filename = fp->fn = cmd.s_name; - hsh_force_insert (files, fp); - return CMD_SUCCESS; lossage: @@ -167,103 +185,35 @@ cmd_file_handle (void) /* File handle functions. */ -/* Sets up some fields in H; caller should fill in - H->{NAME,NORM_FN,FN}. */ -static void -init_file_handle (struct file_handle *h) +/* Creates and returns a new file handle with the given values + and defaults for other values. Adds the created file handle + to the global list. */ +static struct file_handle * +create_file_handle (const char *handle_name, const char *filename) { - h->recform = FH_RF_VARIABLE; - h->mode = FH_MD_CHARACTER; - h->ext = NULL; - h->class = NULL; -} - -/* Returns the handle corresponding to FILENAME. Creates the handle - if no handle exists for that file. All filenames are normalized - first, so different filenames referring to the same file will - return the same file handle. */ -struct file_handle * -fh_get_handle_by_filename (const char *filename) -{ - struct file_handle f, *fp; - char *fn; - char *name; - int len; - - /* Get filename. */ - fn = fn_normalize (filename); - len = strlen (fn); - - /* Create handle name with invalid identifier character to prevent - conflicts with handles created with FILE HANDLE. */ - name = xmalloc (len + 2); - name[0] = '*'; - strcpy (&name[1], fn); - - f.name = name; - fp = hsh_find (files, &f); - if (!fp) - { - fp = xmalloc (sizeof *fp); - init_file_handle (fp); - fp->name = name; - fp->norm_fn = fn; - fp->where.filename = fp->fn = xstrdup (filename); - hsh_force_insert (files, fp); - } - else - { - free (fn); - free (name); - } - return fp; -} - -/* Returns the handle with identifier NAME, if it exists; otherwise - reports error to user and returns NULL. */ -struct file_handle * -fh_get_handle_by_name (const char name[9]) -{ - struct file_handle f, *fp; - f.name = (char *) name; - fp = hsh_find (files, &f); - - if (!fp) - msg (SE, _("File handle `%s' has not been previously declared on " - "FILE HANDLE."), name); - return fp; -} - -/* Returns the identifier of file HANDLE. If HANDLE was created by - referring to a filename (i.e., DATA LIST FILE='yyy' instead of FILE - HANDLE XXX='yyy'), returns the filename, enclosed in double quotes. - Return value is in a static buffer. - - Useful for printing error messages about use of file handles. */ -const char * -fh_handle_name (const struct file_handle *h) -{ - static char *buf = NULL; - - if (buf) - { - free (buf); - buf = NULL; - } - if (!h) - return NULL; - - if (h->name[0] == '*') - { - int len = strlen (h->fn); + struct file_handle *handle; + struct file_handle_list *list; + + /* Create and initialize file handle. */ + handle = xmalloc (sizeof *handle); + handle->private = xmalloc (sizeof *handle->private); + handle->private->name = xstrdup (handle_name); + handle->private->filename = xstrdup (filename); + handle->private->identity = fn_get_identity (filename); + handle->private->where.filename = handle->private->filename; + handle->private->where.line_number = 0; + handle->private->mode = MODE_TEXT; + handle->private->length = 1024; + handle->ext = NULL; + handle->class = NULL; + + /* Add file handle to global list. */ + list = xmalloc (sizeof *list); + list->handle = handle; + list->next = file_handles; + file_handles = list; - buf = xmalloc (len + 3); - strcpy (&buf[1], h->fn); - buf[0] = buf[len + 1] = '"'; - buf[len + 2] = 0; - return buf; - } - return h->name; + return handle; } /* Closes the stdio FILE associated with handle H. Frees internal @@ -275,50 +225,22 @@ fh_close_handle (struct file_handle *h) if (h == NULL) return; - debug_printf (("Closing %s%s.\n", fh_handle_name (h), - h->class == NULL ? " (already closed)" : "")); - - if (h->class) + if (h->class != NULL) h->class->close (h); h->class = NULL; h->ext = NULL; } -/* Hashes the name of file handle H. */ -static unsigned -hash_file_handle (const void *handle_, void *param UNUSED) -{ - const struct file_handle *handle = handle_; - - return hsh_hash_string (handle->name); -} - -/* Compares names of file handles A and B. */ -static int -cmp_file_handle (const void *a_, const void *b_, void *foo UNUSED) -{ - const struct file_handle *a = a_; - const struct file_handle *b = b_; - - return strcmp (a->name, b->name); -} - /* Initialize the hash of file handles; inserts the "inline file" inline_file. */ void fh_init_files (void) { - /* Create hash. */ - files = hsh_create (4, cmp_file_handle, hash_file_handle, NULL, NULL); - - /* Insert inline file. */ - inline_file = xmalloc (sizeof *inline_file); - init_file_handle (inline_file); - inline_file->name = "INLINE"; - inline_file->where.filename - = inline_file->fn = inline_file->norm_fn = (char *) _(""); - inline_file->where.line_number = 0; - hsh_force_insert (files, inline_file); + if (inline_file == NULL) + { + inline_file = create_file_handle ("INLINE", _("")); + inline_file->private->length = 80; + } } /* Parses a file handle name, which may be a filename as a string or @@ -329,40 +251,65 @@ fh_parse_file_handle (void) { struct file_handle *handle; - if (token == T_ID) - handle = fh_get_handle_by_name (tokid); - else if (token == T_STRING) - handle = fh_get_handle_by_filename (ds_value (&tokstr)); - else + if (token != T_ID && token != T_STRING) { - lex_error (_("expecting a file name or handle")); + lex_error (_("expecting a file name or handle name")); return NULL; } - if (!handle) - return NULL; + /* Check for named handles first, then go by filename. */ + handle = NULL; + if (token == T_ID) + handle = get_handle_with_name (tokid); + if (handle == NULL) + handle = get_handle_for_filename (ds_value (&tokstr)); + if (handle == NULL) + { + char *filename = ds_value (&tokstr); + char *handle_name = xmalloc (strlen (filename) + 3); + sprintf (handle_name, "\"%s\"", filename); + handle = create_file_handle (handle_name, filename); + free (handle_name); + } + lex_get (); return handle; } -/* Returns the (normalized) filename associated with file handle H. */ -char * -fh_handle_filename (struct file_handle * h) +/* Returns the identifier of file HANDLE. If HANDLE was created + by referring to a filename instead of a handle name, returns + the filename, enclosed in double quotes. Return value is + owned by the file handle. + + Useful for printing error messages about use of file handles. */ +const char * +handle_get_name (const struct file_handle *handle) +{ + return handle->private->name; +} + +/* Returns the name of the file associated with HANDLE. */ +const char * +handle_get_filename (const struct file_handle *handle) +{ + return handle->private->filename; +} + +/* Returns the mode of HANDLE. */ +enum file_handle_mode +handle_get_mode (const struct file_handle *handle) { - return h->norm_fn; + assert (handle != NULL); + return handle->private->mode; } -/* Returns the width of a logical record on file handle H. */ +/* Returns the width of a logical record on HANDLE. */ size_t -fh_record_width (struct file_handle *h) +handle_get_record_width (const struct file_handle *handle) { - if (h == inline_file) - return 80; - else if (h->recform == FH_RF_FIXED) - return h->lrecl; - else - return 1024; + assert (handle != NULL); + return handle->private->length; } /* diff --git a/src/file-type.c b/src/file-type.c index e5e3d81e9f..89a9793866 100644 --- a/src/file-type.c +++ b/src/file-type.c @@ -109,7 +109,6 @@ cmd_file_type (void) fty->had_rec_type = 0; fty->recs_head = fty->recs_tail = NULL; - lex_match_id ("TYPE"); if (lex_match_id ("MIXED")) fty->type = FTY_MIXED; else if (lex_match_id ("GROUPED")) @@ -270,6 +269,8 @@ cmd_file_type (void) } } + if (!dfm_open_for_reading (fty->handle)) + goto error; default_handle = fty->handle; create_col_var (&fty->record); @@ -423,9 +424,6 @@ cmd_record_type (void) } } - lex_match_id ("RECORD"); - lex_match_id ("TYPE"); - /* Parse record type values. */ if (lex_match_id ("OTHER")) rct->flags |= RCT_OTHER; @@ -582,8 +580,6 @@ cmd_end_file_type (void) fty = vfm_source->aux; fty->case_size = dict_get_case_size (default_dict); - lex_match_id ("TYPE"); - if (fty->recs_tail) { fty->recs_tail->lt = n_trns - 1; diff --git a/src/filename.c b/src/filename.c index a56918f334..f666674b31 100644 --- a/src/filename.c +++ b/src/filename.c @@ -854,3 +854,93 @@ fn_close_ext (struct file_ext *f) } return 1; } + +#ifdef unix +/* A file's identity. */ +struct file_identity + { + dev_t device; /* Device number. */ + ino_t inode; /* Inode number. */ + }; + +/* Returns a pointer to a dynamically allocated structure whose + value can be used to tell whether two files are actually the + same file. Returns a null pointer if no information about the + file is available, perhaps because it does not exist. The + caller is responsible for freeing the structure with + fn_free_identity() when finished. */ +struct file_identity * +fn_get_identity (const char *filename) +{ + struct stat s; + + if (stat (filename, &s) == 0) + { + struct file_identity *identity = xmalloc (sizeof *identity); + identity->device = s.st_dev; + identity->inode = s.st_ino; + return identity; + } + else + return NULL; +} + +/* Frees IDENTITY obtained from fn_get_identity(). */ +void +fn_free_identity (struct file_identity *identity) +{ + free (identity); +} + +/* Compares A and B, returning a strcmp()-type result. */ +int +fn_compare_file_identities (const struct file_identity *a, + const struct file_identity *b) +{ + assert (a != NULL); + assert (b != NULL); + if (a->device != b->device) + return a->device < b->device ? -1 : 1; + else + return a->inode < b->inode ? -1 : a->inode > b->inode; +} +#else /* not unix */ +/* A file's identity. */ +struct file_identity + { + char *normalized_filename; /* File's normalized name. */ + }; + +/* Returns a pointer to a dynamically allocated structure whose + value can be used to tell whether two files are actually the + same file. Returns a null pointer if no information about the + file is available, perhaps because it does not exist. The + caller is responsible for freeing the structure with + fn_free_identity() when finished. */ +struct file_identity * +fn_get_identity (const char *filename) +{ + struct file_identity *identity = xmalloc (sizeof *identity); + identity->normalized_filename = fn_normalize (filename); + return identity; +} + +/* Frees IDENTITY obtained from fn_get_identity(). */ +void +fn_free_identity (struct file_identity *identity) +{ + if (identity != NULL) + { + free (identity->normalized_filename); + free (identity); + } +} + +/* Compares A and B, returning a strcmp()-type result. */ +int +fn_compare_file_identities (const struct file_identity *a, + const struct file_identity *b) +{ + return strcmp (a->normalized_filename, b->normalized_filename); +} +#endif /* not unix */ diff --git a/src/filename.h b/src/filename.h index 499e693bc4..9b3f5ab80f 100644 --- a/src/filename.h +++ b/src/filename.h @@ -48,6 +48,11 @@ const char *fn_getenv_default (const char *variable, const char *def); FILE *fn_open (const char *fn, const char *mode); int fn_close (const char *fn, FILE *file); + +struct file_identity *fn_get_identity (const char *filename); +void fn_free_identity (struct file_identity *); +int fn_compare_file_identities (const struct file_identity *, + const struct file_identity *); /* Extended file routines. */ struct file_ext; diff --git a/src/flip.c b/src/flip.c index 5fd1dea30f..6bd6a24390 100644 --- a/src/flip.c +++ b/src/flip.c @@ -87,7 +87,6 @@ cmd_flip (void) flip->new_names_tail = NULL; flip->file = NULL; - lex_match_id ("FLIP"); lex_match ('/'); if (lex_match_id ("VARIABLES")) { diff --git a/src/formats.c b/src/formats.c index 9198b0ad35..36c019c01d 100644 --- a/src/formats.c +++ b/src/formats.c @@ -45,21 +45,18 @@ static int internal_cmd_formats (int); int cmd_print_formats (void) { - lex_match_id ("FORMATS"); return internal_cmd_formats (FORMATS_PRINT); } int cmd_write_formats (void) { - lex_match_id ("FORMATS"); return internal_cmd_formats (FORMATS_WRITE); } int cmd_formats (void) { - lex_match_id ("FORMATS"); return internal_cmd_formats (FORMATS_PRINT | FORMATS_WRITE); } diff --git a/src/frequencies.q b/src/frequencies.q index e46bb1dc38..37862b5ac5 100644 --- a/src/frequencies.q +++ b/src/frequencies.q @@ -211,7 +211,6 @@ internal_cmd_frequencies (void) for (i = 0; i < dict_get_var_cnt (default_dict); i++) dict_get_var(default_dict, i)->p.frq.used = 0; - lex_match_id ("FREQUENCIES"); if (!parse_frequencies (&cmd)) return CMD_FAILURE; diff --git a/src/get.c b/src/get.c index c33680fd5a..17722ce664 100644 --- a/src/get.c +++ b/src/get.c @@ -79,7 +79,6 @@ cmd_get (void) struct get_pgm *pgm; int options = GTSV_OPT_NONE; - lex_match_id ("GET"); discard_variables (); lex_match ('/'); @@ -150,8 +149,6 @@ cmd_save_internal (enum save_cmd save_cmd) int i; - lex_match_id ("SAVE"); - lex_match ('/'); if (lex_match_id ("OUTFILE")) lex_match ('='); @@ -586,9 +583,6 @@ cmd_match_files (void) int seen = 0; - lex_match_id ("MATCH"); - lex_match_id ("FILES"); - mtf.head = mtf.tail = NULL; mtf.by = NULL; mtf.by_cnt = 0; @@ -826,7 +820,7 @@ cmd_match_files (void) if (iter->by[i] == NULL) { msg (SE, _("File %s lacks BY variable %s."), - iter->handle ? fh_handle_name (iter->handle) : "*", + iter->handle ? handle_get_name (iter->handle) : "*", mtf.by[i]->name); goto lossage; } @@ -1341,7 +1335,7 @@ mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f) msg (SE, _("Variable %s in file %s (%s) has different " "type or width from the same variable in " "earlier file (%s)."), - dv->name, fh_handle_name (f->handle), + dv->name, handle_get_name (f->handle), var_type_description (dv), var_type_description (mv)); return 0; } @@ -1364,8 +1358,6 @@ cmd_import (void) int options = GTSV_OPT_NONE; int type; - lex_match_id ("IMPORT"); - for (;;) { lex_match ('/'); @@ -1479,8 +1471,6 @@ cmd_export (void) int i; - lex_match_id ("EXPORT"); - lex_match ('/'); if (lex_match_id ("OUTFILE")) lex_match ('='); diff --git a/src/include.c b/src/include.c index 78557bdd37..19f5ca1b8f 100644 --- a/src/include.c +++ b/src/include.c @@ -28,47 +28,18 @@ #include "str.h" int -cmd_include_at (void) +cmd_include (void) { - char *incfn, *s, *bp, *ep; - - s = bp = lex_entire_line (); - while (isspace ((unsigned char) *bp)) - bp++; - bp++; /* skip `@' */ - while (isspace ((unsigned char) *bp)) - bp++; - if (*bp == '\'') - bp++; - - ep = bp + strlen (bp); - while (isspace ((unsigned char) *--ep)); - if (*ep != '\'') - ep++; + /* Skip optional FILE=. */ + if (lex_match_id ("FILE")) + lex_match ('='); - if (ep <= bp) + /* Filename can be identifier or string. */ + if (token != T_ID && token != T_STRING) { - msg (SE, _("Unrecognized filename format.")); + lex_error (_("expecting filename")); return CMD_FAILURE; } - - /* Now the filename is trapped between bp and ep. */ - incfn = xmalloc (ep - bp + 1); - strncpy (incfn, bp, ep - bp); - incfn[ep - bp] = 0; - getl_include (incfn); - free (incfn); - - return CMD_SUCCESS; -} - -int -cmd_include (void) -{ - lex_get (); - - if (!lex_force_string ()) - return CMD_SUCCESS; getl_include (ds_value (&tokstr)); lex_get (); diff --git a/src/inpt-pgm.c b/src/inpt-pgm.c index dc08f346a9..ee2fd7cb13 100644 --- a/src/inpt-pgm.c +++ b/src/inpt-pgm.c @@ -59,8 +59,6 @@ static trns_free_func reread_trns_free; int cmd_input_program (void) { - lex_match_id ("INPUT"); - lex_match_id ("PROGRAM"); discard_variables (); /* FIXME: we shouldn't do this here, but I'm afraid that other @@ -77,10 +75,6 @@ cmd_end_input_program (void) struct input_program_pgm *inp; size_t i; - lex_match_id ("END"); - lex_match_id ("INPUT"); - lex_match_id ("PROGRAM"); - if (!case_source_is_class (vfm_source, &input_program_source_class)) { msg (SE, _("No matching INPUT PROGRAM command.")); @@ -287,9 +281,6 @@ cmd_end_case (void) { struct trns_header *t; - lex_match_id ("END"); - lex_match_id ("CASE"); - if (!case_source_is_class (vfm_source, &input_program_source_class)) { msg (SE, _("This command may only be executed between INPUT PROGRAM " @@ -336,8 +327,6 @@ cmd_reread (void) /* Created transformation. */ struct reread_trns *t; - lex_match_id ("REREAD"); - h = default_handle; e = NULL; while (token != '.') @@ -360,14 +349,8 @@ cmd_reread (void) else if (lex_match_id ("FILE")) { lex_match ('='); - if (token != T_ID) - { - lex_error (_("expecting file handle name")); - expr_free (e); - return CMD_FAILURE; - } - h = fh_get_handle_by_name (tokid); - if (!h) + h = fh_parse_file_handle (); + if (h == NULL) { expr_free (e); return CMD_FAILURE; @@ -430,9 +413,6 @@ cmd_end_file (void) { struct trns_header *t; - lex_match_id ("END"); - lex_match_id ("FILE"); - if (!case_source_is_class (vfm_source, &input_program_source_class)) { msg (SE, _("This command may only be executed between INPUT PROGRAM " diff --git a/src/levene.c b/src/levene.c index 3d171716b9..34abe3981e 100644 --- a/src/levene.c +++ b/src/levene.c @@ -23,6 +23,7 @@ #include #include "levene.h" #include "hash.h" +#include "str.h" #include "var.h" #include "vfm.h" #include "alloc.h" diff --git a/src/lexer.c b/src/lexer.c index 5b1b394153..91712bf265 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -76,7 +76,9 @@ static int eof; /* If nonzero, next token returned by lex_get(). Used only in exceptional circumstances. */ -static int put; +static int put_token; +static struct string put_tokstr; +static double put_tokval; static void unexpected_eof (void); static inline int check_id (const char *id, size_t len); @@ -93,22 +95,46 @@ static void dump_token (void); void lex_init (void) { + ds_init (NULL, &put_tokstr, 64); if (!lex_get_line ()) unexpected_eof (); } /* Common functions. */ +/* Copies put_token, put_tokstr, put_tokval into token, tokstr, + tokval, respectively, and sets tokid appropriately. */ +static void +restore_token (void) +{ + assert (put_token != 0); + token = put_token; + ds_replace (&tokstr, ds_value (&put_tokstr)); + strncpy (tokid, ds_value (&put_tokstr), 8); + tokid[8] = 0; + tokval = put_tokval; + put_token = 0; +} + +/* Copies token, tokstr, tokval into put_token, put_tokstr, + put_tokval respectively. */ +static void +save_token (void) +{ + put_token = token; + ds_replace (&put_tokstr, ds_value (&tokstr)); + put_tokval = tokval; +} + /* Parses a single token, setting appropriate global variables to indicate the token's attributes. */ void lex_get (void) { /* If a token was pushed ahead, return it. */ - if (put) + if (put_token) { - token = put; - put = 0; + restore_token (); #if DUMP_TOKENS dump_token (); #endif @@ -150,10 +176,9 @@ lex_get (void) return; } - if (put) + if (put_token) { - token = put; - put = 0; + restore_token (); #if DUMP_TOKENS dump_token (); #endif @@ -615,8 +640,8 @@ lex_id_match (const char *kw, const char *tok) int lex_look_ahead (void) { - if (put) - return put; + if (put_token) + return put_token; for (;;) { @@ -635,8 +660,8 @@ lex_look_ahead (void) else if (!lex_get_line ()) unexpected_eof (); - if (put) - return put; + if (put_token) + return put_token; } if ((toupper ((unsigned char) *prog) == 'X' @@ -653,15 +678,20 @@ lex_look_ahead (void) void lex_put_back (int t) { - put = token; + save_token (); token = t; } -/* Makes T the next token read. */ +/* Makes the current token become the next token to be read; the + current token is set to the identifier ID. */ void -lex_put_forward (int t) +lex_put_back_id (const char *id) { - put = t; + save_token (); + token = T_ID; + ds_replace (&tokstr, id); + strncpy (tokid, ds_value (&tokstr), 8); + tokid[8] = 0; } /* Weird line processing functions. */ @@ -703,7 +733,7 @@ lex_discard_line (void) ds_clear (&getl_buf); prog = ds_value (&getl_buf); - dot = put = 0; + dot = put_token = 0; } /* Sets the current position in the current line to P, which must be @@ -817,7 +847,7 @@ lex_preprocess_line (void) if (s[0] == '+' || s[0] == '-' || s[0] == '.') s[0] = ' '; else if (s[0] && !isspace ((unsigned char) s[0])) - lex_put_forward ('.'); + put_token = '.'; } prog = ds_value (&getl_buf); @@ -929,10 +959,11 @@ lex_negative_to_dash (void) { if (token == T_NUM && tokval < 0.0) { - token = '-'; + token = T_NUM; tokval = -tokval; ds_replace (&tokstr, ds_value (&tokstr) + 1); - lex_put_forward (T_NUM); + save_token (); + token = '-'; } } @@ -950,7 +981,7 @@ lex_skip_comment (void) for (;;) { lex_get_line (); - if (put == '.') + if (put_token == '.') break; prog = ds_end (&getl_buf); @@ -1175,40 +1206,40 @@ dump_token (void) getl_location (&curfn, &curln); if (curfn) - printf ("%s:%d\t", curfn, curln); + fprintf (stderr, "%s:%d\t", curfn, curln); } switch (token) { case T_ID: - printf ("ID\t%s\n", tokid); + fprintf (stderr, "ID\t%s\n", tokid); break; case T_NUM: - printf ("NUM\t%f\n", tokval); + fprintf (stderr, "NUM\t%f\n", tokval); break; case T_STRING: - printf ("STRING\t\"%s\"\n", ds_value (&tokstr)); + fprintf (stderr, "STRING\t\"%s\"\n", ds_value (&tokstr)); break; case T_STOP: - printf ("STOP\n"); + fprintf (stderr, "STOP\n"); break; case T_EXP: - puts ("MISC\tEXP"); + fprintf (stderr, "MISC\tEXP\""); break; case 0: - puts ("MISC\tEOF"); + fprintf (stderr, "MISC\tEOF\n"); break; default: if (token >= T_FIRST_KEYWORD && token <= T_LAST_KEYWORD) - printf ("KEYWORD\t%s\n", lex_token_name (token)); + fprintf (stderr, "KEYWORD\t%s\n", lex_token_name (token)); else - printf ("PUNCT\t%c\n", token); + fprintf (stderr, "PUNCT\t%c\n", token); break; } } diff --git a/src/lexer.h b/src/lexer.h index cf74515821..2dd7da0627 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -111,7 +111,7 @@ int lex_id_match (const char *keyword_string, const char *token_string); /* Weird token functions. */ int lex_look_ahead (void); void lex_put_back (int); -void lex_put_forward (int); +void lex_put_back_id (const char *tokid); /* Weird line processing functions. */ char *lex_entire_line (void); diff --git a/src/list.q b/src/list.q index c875e6f53a..10d2baad38 100644 --- a/src/list.q +++ b/src/list.q @@ -125,7 +125,6 @@ cmd_list (void) { struct variable casenum_var; - lex_match_id ("LIST"); if (!parse_list (&cmd)) return CMD_FAILURE; diff --git a/src/loop.c b/src/loop.c index 7a31234c4f..7f8aa87559 100644 --- a/src/loop.c +++ b/src/loop.c @@ -166,8 +166,6 @@ internal_cmd_loop (void) /* Name of indexing variable if applicable. */ char name[9]; - lex_match_id ("LOOP"); - /* Create and initialize transformations to facilitate error-handling. */ two = xmalloc (sizeof *two); @@ -502,8 +500,6 @@ cmd_break (void) /* New transformation. */ struct break_trns *t; - lex_match_id ("BREAK"); - for (loop = ctl_stack; loop; loop = loop->down) if (loop->type == CST_LOOP) break; diff --git a/src/main.c b/src/main.c index d69e661d74..b20f8a2441 100644 --- a/src/main.c +++ b/src/main.c @@ -71,7 +71,6 @@ main (int argc, char **argv) msg (FE, _("Error initializing output drivers.")); lex_init (); - cmd_init (); /* Execution. */ parse_script (); diff --git a/src/matrix-data.c b/src/matrix-data.c index 277cde3823..5835e82292 100644 --- a/src/matrix-data.c +++ b/src/matrix-data.c @@ -153,9 +153,6 @@ cmd_matrix_data (void) unsigned seen = 0; - lex_match_id ("MATRIX"); - lex_match_id ("DATA"); - discard_variables (); pool = pool_create (); @@ -600,6 +597,9 @@ cmd_matrix_data (void) debug_print (); #endif + if (!dfm_open_for_reading (mx->data_file)) + goto lossage; + if (mx->explicit_rowtype) read_matrices_with_rowtype (mx); else diff --git a/src/means.q b/src/means.q index a8921b38bf..bf7b19765e 100644 --- a/src/means.q +++ b/src/means.q @@ -73,7 +73,6 @@ cmd_means (void) v_dim = NULL; v_var = NULL; - lex_match_id ("MEANS"); if (!parse_means (&cmd)) goto free; diff --git a/src/mis-val.c b/src/mis-val.c index 1e07fcfd54..210d2389f8 100644 --- a/src/mis-val.c +++ b/src/mis-val.c @@ -56,8 +56,6 @@ cmd_missing_values (void) { int i; - lex_match_id ("MISSING"); - lex_match_id ("VALUES"); while (token != '.') { if (!parse_varnames ()) diff --git a/src/modify-vars.c b/src/modify-vars.c index 46fe644d10..f12cb7ed63 100644 --- a/src/modify-vars.c +++ b/src/modify-vars.c @@ -90,9 +90,6 @@ cmd_modify_vars (void) cancel_temporary (); } - lex_match_id ("MODIFY"); - lex_match_id ("VARS"); - vm.reorder_vars = NULL; vm.reorder_cnt = 0; vm.rename_vars = NULL; diff --git a/src/numeric.c b/src/numeric.c index b97410a51b..8031d65d20 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -42,7 +42,6 @@ cmd_numeric (void) be used. */ struct fmt_spec f; - lex_match_id ("NUMERIC"); do { if (!parse_DATA_LIST_vars (&v, &nv, PV_NONE)) @@ -116,7 +115,6 @@ cmd_string (void) /* Width of variables to create. */ int width; - lex_match_id ("STRING"); do { if (!parse_DATA_LIST_vars (&v, &nv, PV_NONE)) @@ -188,7 +186,6 @@ cmd_leave (void) int i; - lex_match_id ("LEAVE"); if (!parse_variables (default_dict, &v, &nv, PV_NONE)) return CMD_FAILURE; for (i = 0; i < nv; i++) diff --git a/src/pfm-read.c b/src/pfm-read.c index 8f19f1ba87..8f9e9e4ec4 100644 --- a/src/pfm-read.c +++ b/src/pfm-read.c @@ -82,12 +82,14 @@ corrupt_msg (struct file_handle *h, const char *format, ...) { char *title; struct error e; + const char *filename; e.class = ME; getl_location (&e.where.filename, &e.where.line_number); - e.title = title = local_alloc (strlen (h->fn) + 80); + filename = handle_get_filename (h); + e.title = title = local_alloc (strlen (filename) + 80); sprintf (title, _("portable file %s corrupt at offset %ld: "), - h->fn, ftell (ext->file) - (82 - (long) (ext->bp - ext->buf))); + filename, ftell (ext->file) - (82 - (long) (ext->bp - ext->buf))); e.text = buf; err_vmsg (&e); @@ -100,12 +102,13 @@ corrupt_msg (struct file_handle *h, const char *format, ...) /* Closes a portable file after we're done with it. */ static void -pfm_close (struct file_handle * h) +pfm_close (struct file_handle *h) { struct pfm_fhuser_ext *ext = h->ext; if (EOF == fclose (ext->file)) - msg (ME, _("%s: Closing portable file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Closing portable file: %s."), + handle_get_filename (h), strerror (errno)); free (ext->vars); free (ext->trans); free (h->ext); @@ -220,20 +223,21 @@ pfm_read_dictionary (struct file_handle *h, struct pfm_read_info *inf) { msg (ME, _("Cannot read file %s as portable file: already opened " "for %s."), - fh_handle_name (h), h->class->name); + handle_get_name (h), h->class->name); return NULL; } msg (VM (1), _("%s: Opening portable-file handle %s for reading."), - fh_handle_filename (h), fh_handle_name (h)); + handle_get_filename (h), handle_get_name (h)); /* Open the physical disk file. */ ext = xmalloc (sizeof (struct pfm_fhuser_ext)); - ext->file = fopen (h->norm_fn, "rb"); + ext->file = fopen (handle_get_filename (h), "rb"); if (ext->file == NULL) { msg (ME, _("An error occurred while opening \"%s\" for reading " - "as a portable file: %s."), h->fn, strerror (errno)); + "as a portable file: %s."), + handle_get_filename (h), strerror (errno)); err_cond_fail (); free (ext); return NULL; diff --git a/src/pfm-write.c b/src/pfm-write.c index 054621fea3..e229072ea5 100644 --- a/src/pfm-write.c +++ b/src/pfm-write.c @@ -70,22 +70,23 @@ pfm_write_dictionary (struct file_handle *handle, struct dictionary *dict) { msg (ME, _("Cannot write file %s as portable file: already opened " "for %s."), - fh_handle_name (handle), handle->class->name); + handle_get_name (handle), handle->class->name); return 0; } msg (VM (1), _("%s: Opening portable-file handle %s for writing."), - fh_handle_filename (handle), fh_handle_name (handle)); + handle_get_filename (handle), handle_get_name (handle)); /* Open the physical disk file. */ handle->class = &pfm_w_class; handle->ext = ext = xmalloc (sizeof (struct pfm_fhuser_ext)); - ext->file = fopen (handle->norm_fn, "wb"); + ext->file = fopen (handle_get_filename (handle), "wb"); ext->lc = 0; if (ext->file == NULL) { msg (ME, _("An error occurred while opening \"%s\" for writing " - "as a portable file: %s."), handle->fn, strerror (errno)); + "as a portable file: %s."), + handle_get_filename (handle), strerror (errno)); err_cond_fail (); free (ext); return 0; @@ -166,7 +167,8 @@ bufwrite (struct file_handle *h, const void *buf_, size_t nbytes) lossage: abort (); - msg (ME, _("%s: Writing portable file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Writing portable file: %s."), + handle_get_filename (h), strerror (errno)); return 0; } @@ -499,7 +501,8 @@ pfm_close (struct file_handle *h) } if (EOF == fclose (ext->file)) - msg (ME, _("%s: Closing portable file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Closing portable file: %s."), + handle_get_filename (h), strerror (errno)); free (ext->vars); free (ext); diff --git a/src/print.c b/src/print.c index 17c0eab237..7f93f13f71 100644 --- a/src/print.c +++ b/src/print.c @@ -34,8 +34,6 @@ #include "tab.h" #include "var.h" -#include "debug-print.h" - /* Describes what to do when an output field is encountered. */ enum { @@ -104,10 +102,6 @@ static int parse_specs (void); static void dump_table (void); static void append_var_spec (struct prt_out_spec *spec); static void alloc_line (void); - -#if DEBUGGING -void debug_print (void); -#endif /* Basic parsing. */ @@ -115,7 +109,6 @@ void debug_print (void); int cmd_print (void) { - lex_match_id ("PRINT"); return internal_cmd_print (PRT_PRINT); } @@ -123,7 +116,6 @@ cmd_print (void) int cmd_print_eject (void) { - lex_match_id ("EJECT"); return internal_cmd_print (PRT_PRINT | PRT_EJECT); } @@ -131,7 +123,6 @@ cmd_print_eject (void) int cmd_write (void) { - lex_match_id ("WRITE"); return internal_cmd_print (PRT_WRITE); } @@ -194,6 +185,9 @@ internal_cmd_print (int f) if (!parse_specs ()) goto lossage; + if (prt.handle != NULL && !dfm_open_for_writing (prt.handle)) + goto lossage; + /* Output the variable table if requested. */ if (table) dump_table (); @@ -207,10 +201,6 @@ internal_cmd_print (int f) memcpy (trns, &prt, sizeof *trns); add_transformation ((struct trns_header *) trns); -#if 0 && DEBUGGING - debug_print (); -#endif - return CMD_SUCCESS; lossage: @@ -787,7 +777,6 @@ static void dump_table (void) { struct prt_out_spec *spec; - const char *filename; struct tab_table *t; int recno; int nspec; @@ -841,13 +830,12 @@ dump_table (void) assert (0); } - filename = fh_handle_name (prt.handle); - tab_title (t, 1, (prt.handle != NULL - ? _("Writing %3d records to file %s.") - : _("Writing %3d records to the listing file.")), - recno, filename); + if (prt.handle != NULL) + tab_title (t, 1, _("Writing %d record(s) to file %s."), + recno, handle_get_filename (prt.handle)); + else + tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno); tab_submit (t); - fh_handle_name (NULL); } /* PORTME: The number of characters in a line terminator. */ @@ -937,7 +925,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c, else { if ((t->options & PRT_CMD_MASK) == PRT_PRINT - || t->handle->mode != FH_MD_BINARY) + || handle_get_mode (t->handle) != MODE_BINARY) { /* PORTME: Line ends. */ #ifdef __MSDOS__ @@ -1028,23 +1016,12 @@ cmd_print_space (void) struct file_handle *handle; struct expression *e; - lex_match_id ("SPACE"); if (lex_match_id ("OUTFILE")) { lex_match ('='); - if (token == T_ID) - handle = fh_get_handle_by_name (tokid); - else if (token == T_STRING) - handle = fh_get_handle_by_filename (tokid); - else - { - msg (SE, _("A file name or handle was expected in the " - "OUTFILE subcommand.")); - return CMD_FAILURE; - } - - if (!handle) + handle = fh_parse_file_handle (); + if (handle == NULL) return CMD_FAILURE; lex_get (); } @@ -1064,6 +1041,12 @@ cmd_print_space (void) else e = NULL; + if (handle != NULL && !dfm_open_for_writing (handle)) + { + expr_free (e); + return CMD_FAILURE; + } + t = xmalloc (sizeof *t); t->h.proc = print_space_trns_proc; if (e) @@ -1128,45 +1111,3 @@ print_space_trns_free (struct trns_header * trns) { expr_free (((struct print_space_trns *) trns)->e); } - -/* Debugging code. */ - -#if 0 && DEBUGGING -void -debug_print (void) -{ - struct prt_out_spec *p; - - if (prt.handle == NULL) - { - printf ("PRINT"); - if (prt.eject) - printf (" EJECT"); - } - else - printf ("WRITE OUTFILE=%s", handle_name (prt.handle)); - printf (" MAX_WIDTH=%d", prt.max_width); - printf (" /"); - for (p = prt.spec; p; p = p->next) - switch (p->type) - { - case PRT_ERROR: - printf (_("")); - break; - case PRT_NEWLINE: - printf ("\n /"); - break; - case PRT_CONST: - printf (" \"%s\" %d-%d", p->u.c, p->fc + 1, p->fc + strlen (p->u.c)); - break; - case PRT_VAR: - printf (" %s %d %d-%d (%s)", p->u.v.v->name, p->u.v.v->fv, p->fc + 1, - p->fc + p->u.v.v->print.w, fmt_to_string (&p->u.v.v->print)); - break; - case PRT_SPACE: - printf (" \" \" %d", p->fc + 1); - break; - } - printf (".\n"); -} -#endif /* DEBUGGING */ diff --git a/src/recode.c b/src/recode.c index 34342a1e18..e2df650f26 100644 --- a/src/recode.c +++ b/src/recode.c @@ -142,8 +142,6 @@ cmd_recode (void) struct variable **v; int nv; - lex_match_id ("RECODE"); - /* Parses each specification between slashes. */ head = rcd = xmalloc (sizeof *rcd); v = NULL; diff --git a/src/rename-vars.c b/src/rename-vars.c index 21b80432cd..4b81f90484 100644 --- a/src/rename-vars.c +++ b/src/rename-vars.c @@ -49,9 +49,6 @@ cmd_rename_variables (void) cancel_temporary (); } - lex_match_id ("RENAME"); - lex_match_id ("VARIABLES"); - do { int prev_nv_1 = rename_cnt; diff --git a/src/repeat.c b/src/repeat.c index 9141684c82..c3236744a2 100644 --- a/src/repeat.c +++ b/src/repeat.c @@ -138,9 +138,6 @@ internal_cmd_do_repeat (void) int print; /* The first step is parsing the DO REPEAT command itself. */ - lex_match_id ("DO"); - lex_match_id ("REPEAT"); - count = 0; line_buf_head = NULL; do diff --git a/src/sample.c b/src/sample.c index 3f88793559..67523b3d80 100644 --- a/src/sample.c +++ b/src/sample.c @@ -59,8 +59,6 @@ cmd_sample (void) int a, b; unsigned frac; - lex_match_id ("SAMPLE"); - if (!lex_force_num ()) return CMD_FAILURE; if (!lex_integer_p ()) diff --git a/src/sel-if.c b/src/sel-if.c index f686a9ddf5..330ae4371a 100644 --- a/src/sel-if.c +++ b/src/sel-if.c @@ -43,9 +43,6 @@ cmd_select_if (void) struct expression *e; struct select_if_trns *t; - lex_match_id ("SELECT"); - lex_match_id ("IF"); - e = expr_parse (PXP_BOOLEAN); if (!e) return CMD_FAILURE; @@ -86,8 +83,6 @@ select_if_free (struct trns_header * t) int cmd_filter (void) { - lex_match_id ("FILTER"); - if (lex_match_id ("OFF")) dict_set_filter (default_dict, NULL); else @@ -125,9 +120,6 @@ cmd_process_if (void) { struct expression *e; - lex_match_id ("PROCESS"); - lex_match_id ("IF"); - e = expr_parse (PXP_BOOLEAN); if (!e) return CMD_FAILURE; diff --git a/src/set.q b/src/set.q index f297bc847d..e9f3ddedee 100644 --- a/src/set.q +++ b/src/set.q @@ -358,8 +358,6 @@ int cmd_set (void) { - lex_match_id ("SET"); - if (!parse_set (&cmd)) return CMD_FAILURE; @@ -981,7 +979,7 @@ stc_custom_workdev (struct cmd_set *cmd UNUSED) static void -set_viewport(void) +set_viewport(int sig_num UNUSED) { #if HAVE_LIBTERMCAP static char term_buffer[16384]; @@ -1112,8 +1110,8 @@ init_settings(void) if ( ! long_view ) { - set_viewport(); - signal (SIGWINCH,set_viewport); + set_viewport (0); + signal (SIGWINCH, set_viewport); } } diff --git a/src/sfm-read.c b/src/sfm-read.c index 03b6d5ab55..01f6330f19 100644 --- a/src/sfm-read.c +++ b/src/sfm-read.c @@ -156,14 +156,15 @@ corrupt_msg (int class, const char *format,...) /* Closes a system file after we're done with it. */ static void -sfm_close (struct file_handle * h) +sfm_close (struct file_handle *h) { struct sfm_fhuser_ext *ext = h->ext; ext->opened--; assert (ext->opened == 0); - if (EOF == fn_close (h->fn, ext->file)) - msg (ME, _("%s: Closing system file: %s."), h->fn, strerror (errno)); + if (EOF == fn_close (handle_get_filename (h), ext->file)) + msg (ME, _("%s: Closing system file: %s."), + handle_get_filename (h), strerror (errno)); free (ext->buf); free (h->ext); } @@ -237,20 +238,21 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) else if (h->class != NULL) { msg (ME, _("Cannot read file %s as system file: already opened for %s."), - fh_handle_name (h), h->class->name); + handle_get_name (h), h->class->name); return NULL; } msg (VM (1), _("%s: Opening system-file handle %s for reading."), - fh_handle_filename (h), fh_handle_name (h)); + handle_get_filename (h), handle_get_name (h)); /* Open the physical disk file. */ ext = xmalloc (sizeof (struct sfm_fhuser_ext)); - ext->file = fn_open (h->norm_fn, "rb"); + ext->file = fn_open (handle_get_filename (h), "rb"); if (ext->file == NULL) { msg (ME, _("An error occurred while opening \"%s\" for reading " - "as a system file: %s."), h->fn, strerror (errno)); + "as a system file: %s."), + handle_get_filename (h), strerror (errno)); err_cond_fail (); free (ext); return NULL; @@ -284,10 +286,10 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) if (wv == NULL) lose ((ME, _("%s: Weighting variable may not be a continuation of " - "a long string variable."), h->fn)); + "a long string variable."), handle_get_filename (h))); else if (wv->type == ALPHA) lose ((ME, _("%s: Weighting variable may not be a string variable."), - h->fn)); + handle_get_filename (h))); dict_set_weight (ext->dict, wv); } @@ -312,8 +314,9 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) case 4: lose ((ME, _("%s: Orphaned variable index record (type 4). Type 4 " - "records must always immediately follow type 3 records."), - h->fn)); + "records must always immediately follow type 3 " + "records."), + handle_get_filename (h))); case 6: if (!read_documents (h)) @@ -340,13 +343,6 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) bswap_int32 (&data.count); } - /*if(data.size != sizeof(int32) && data.size != sizeof(flt64)) - lose((ME, "%s: Element size in record type 7, subtype %d, is " - "not either the size of IN (%d) or OBS (%d); actual value " - "is %d.", - h->fn, data.subtype, sizeof(int32), sizeof(flt64), - data.size)); */ - switch (data.subtype) { case 3: @@ -367,7 +363,8 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) default: msg (MW, _("%s: Unrecognized record type 7, subtype %d " - "encountered in system file."), h->fn, data.subtype); + "encountered in system file."), + handle_get_filename (h), data.subtype); skip = 1; } @@ -390,7 +387,8 @@ sfm_read_dictionary (struct file_handle * h, struct sfm_read_info * inf) } default: - lose ((ME, _("%s: Unrecognized record type %d."), h->fn, rec_type)); + lose ((ME, _("%s: Unrecognized record type %d."), + handle_get_filename (h), rec_type)); } } @@ -409,7 +407,7 @@ lossage: msg (VM (1), _("Error reading system-file header.")); free (var_by_index); - fn_close (h->fn, ext->file); + fn_close (handle_get_filename (h), ext->file); if (ext && ext->dict) dict_destroy (ext->dict); free (ext); @@ -431,8 +429,8 @@ read_machine_int32_info (struct file_handle * h, int size, int count) if (size != sizeof (int32) || count != 8) lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, " - "subtype 3. Expected size %d, count 8."), - h->fn, size, count, sizeof (int32))); + "subtype 3. Expected size %d, count 8."), + handle_get_filename (h), size, count, sizeof (int32))); assertive_bufread (h, data, sizeof data, 0); if (ext->reverse_endian) @@ -444,7 +442,8 @@ read_machine_int32_info (struct file_handle * h, int size, int count) if (data[4] != 1) lose ((ME, _("%s: Floating-point representation in system file is not " "IEEE-754. PSPP cannot convert between floating-point " - "formats."), h->fn)); + "formats."), + handle_get_filename (h))); #endif /* PORTME: Check recorded file endianness against intuited file @@ -458,16 +457,19 @@ read_machine_int32_info (struct file_handle * h, int size, int count) file_bigendian ^= 1; if (file_bigendian ^ (data[6] == 1)) lose ((ME, _("%s: File-indicated endianness (%s) does not match endianness " - "intuited from file header (%s)."), - h->fn, file_bigendian ? _("big-endian") : _("little-endian"), + "intuited from file header (%s)."), + handle_get_filename (h), + file_bigendian ? _("big-endian") : _("little-endian"), data[6] == 1 ? _("big-endian") : (data[6] == 2 ? _("little-endian") : _("unknown")))); /* PORTME: Character representation code. */ if (data[7] != 2 && data[7] != 3) lose ((ME, _("%s: File-indicated character representation code (%s) is not " - "ASCII."), h->fn, - data[7] == 1 ? "EBCDIC" : (data[7] == 4 ? _("DEC Kanji") : _("Unknown")))); + "ASCII."), + handle_get_filename (h), + (data[7] == 1 ? "EBCDIC" + : (data[7] == 4 ? _("DEC Kanji") : _("Unknown"))))); return 1; @@ -487,8 +489,8 @@ read_machine_flt64_info (struct file_handle * h, int size, int count) if (size != sizeof (flt64) || count != 3) lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, " - "subtype 4. Expected size %d, count 8."), - h->fn, size, count, sizeof (flt64))); + "subtype 4. Expected size %d, count 8."), + handle_get_filename (h), size, count, sizeof (flt64))); assertive_bufread (h, data, sizeof data, 0); if (ext->reverse_endian) @@ -505,7 +507,7 @@ read_machine_flt64_info (struct file_handle * h, int size, int count) "for at least one of the three system values. SYSMIS: " "indicated %g, expected %g; HIGHEST: %g, %g; LOWEST: " "%g, %g."), - h->fn, (double) data[0], (double) SYSMIS, + handle_get_filename (h), (double) data[0], (double) SYSMIS, (double) data[1], (double) FLT64_MAX, (double) data[2], (double) second_lowest_flt64); } @@ -534,7 +536,7 @@ read_header (struct file_handle * h, struct sfm_read_info * inf) if (0 != strncmp ("$FL2", hdr.rec_type, 4)) lose ((ME, _("%s: Bad magic. Proper system files begin with " "the four characters `$FL2'. This file will not be read."), - h->fn)); + handle_get_filename (h))); /* Check eye-catcher string. */ memcpy (prod_name, hdr.prod_name, sizeof hdr.prod_name); @@ -576,8 +578,8 @@ read_header (struct file_handle * h, struct sfm_read_info * inf) bswap_int32 (&hdr.layout_code); if (hdr.layout_code != 2) lose ((ME, _("%s: File layout code has unexpected value %d. Value " - "should be 2, in big-endian or little-endian format."), - h->fn, hdr.layout_code)); + "should be 2, in big-endian or little-endian format."), + handle_get_filename (h), hdr.layout_code)); ext->reverse_endian = 1; bswap_int32 (&hdr.case_size); @@ -592,25 +594,28 @@ read_header (struct file_handle * h, struct sfm_read_info * inf) if (hdr.case_size <= 0 || ext->case_size > (INT_MAX / (int) sizeof (union value) / 2)) lose ((ME, _("%s: Number of elements per case (%d) is not between 1 " - "and %d."), h->fn, hdr.case_size, INT_MAX / sizeof (union value) / 2)); + "and %d."), + handle_get_filename (h), hdr.case_size, + INT_MAX / sizeof (union value) / 2)); ext->compressed = hdr.compressed; ext->weight_index = hdr.weight_index - 1; if (hdr.weight_index < 0 || hdr.weight_index > hdr.case_size) lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 " - "and number of elements per case (%d)."), - h->fn, hdr.weight_index, ext->case_size)); + "and number of elements per case (%d)."), + handle_get_filename (h), hdr.weight_index, ext->case_size)); ext->ncases = hdr.ncases; if (ext->ncases < -1 || ext->ncases > INT_MAX / 2) lose ((ME, _("%s: Number of cases in file (%ld) is not between -1 and " - "%d."), h->fn, (long) ext->ncases, INT_MAX / 2)); + "%d."), handle_get_filename (h), (long) ext->ncases, INT_MAX / 2)); ext->bias = hdr.bias; if (ext->bias != 100.0) corrupt_msg (MW, _("%s: Compression bias (%g) is not the usual " - "value of 100."), h->fn, ext->bias); + "value of 100."), + handle_get_filename (h), ext->bias); /* Make a file label only on the condition that the given label is not all spaces or nulls. */ @@ -703,7 +708,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) if (sv.rec_type != 2) lose ((ME, _("%s: position %d: Bad record type (%d); " - "the expected value was 2."), h->fn, i, sv.rec_type)); + "the expected value was 2."), + handle_get_filename (h), i, sv.rec_type)); /* If there was a long string previously, make sure that the continuations are present; otherwise make sure there aren't @@ -712,7 +718,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) { if (sv.type != -1) lose ((ME, _("%s: position %d: String variable does not have " - "proper number of continuation records."), h->fn, i)); + "proper number of continuation records."), + handle_get_filename (h), i)); (*var_by_index)[i] = NULL; long_string_count--; @@ -720,32 +727,36 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) } else if (sv.type == -1) lose ((ME, _("%s: position %d: Superfluous long string continuation " - "record."), h->fn, i)); + "record."), + handle_get_filename (h), i)); /* Check fields for validity. */ if (sv.type < 0 || sv.type > 255) lose ((ME, _("%s: position %d: Bad variable type code %d."), - h->fn, i, sv.type)); + handle_get_filename (h), i, sv.type)); if (sv.has_var_label != 0 && sv.has_var_label != 1) lose ((ME, _("%s: position %d: Variable label indicator field is not " - "0 or 1."), h->fn, i)); + "0 or 1."), handle_get_filename (h), i)); if (sv.n_missing_values < -3 || sv.n_missing_values > 3 || sv.n_missing_values == -1) lose ((ME, _("%s: position %d: Missing value indicator field is not " - "-3, -2, 0, 1, 2, or 3."), h->fn, i)); + "-3, -2, 0, 1, 2, or 3."), handle_get_filename (h), i)); /* Copy first character of variable name. */ if (!isalpha ((unsigned char) sv.name[0]) && sv.name[0] != '@' && sv.name[0] != '#') lose ((ME, _("%s: position %d: Variable name begins with invalid " - "character."), h->fn, i)); + "character."), + handle_get_filename (h), i)); if (islower ((unsigned char) sv.name[0])) msg (MW, _("%s: position %d: Variable name begins with lowercase letter " - "%c."), h->fn, i, sv.name[0]); + "%c."), + handle_get_filename (h), i, sv.name[0]); if (sv.name[0] == '#') msg (MW, _("%s: position %d: Variable name begins with octothorpe " "(`#'). Scratch variables should not appear in system " - "files."), h->fn, i); + "files."), + handle_get_filename (h), i); name[0] = toupper ((unsigned char) (sv.name[0])); /* Copy remaining characters of variable name. */ @@ -758,7 +769,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) else if (islower (c)) { msg (MW, _("%s: position %d: Variable name character %d is " - "lowercase letter %c."), h->fn, i, j + 1, sv.name[j]); + "lowercase letter %c."), + handle_get_filename (h), i, j + 1, sv.name[j]); name[j] = toupper ((unsigned char) (c)); } else if (isalnum (c) || c == '.' || c == '@' @@ -766,7 +778,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) name[j] = c; else lose ((ME, _("%s: position %d: character `\\%03o' (%c) is not valid in a " - "variable name."), h->fn, i, c, c)); + "variable name."), + handle_get_filename (h), i, c, c)); } name[j] = 0; @@ -774,7 +787,7 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) vv = (*var_by_index)[i] = dict_create_var (dict, name, sv.type); if (vv == NULL) lose ((ME, _("%s: Duplicate variable name `%s' within system file."), - h->fn, name)); + handle_get_filename (h), name)); /* Case reading data. */ vv->get.fv = next_value; @@ -799,7 +812,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) /* Check len. */ if (len < 0 || len > 255) lose ((ME, _("%s: Variable %s indicates variable label of invalid " - "length %d."), h->fn, vv->name, len)); + "length %d."), + handle_get_filename (h), vv->name, len)); /* Read label into variable structure. */ vv->label = bufread (h, NULL, ROUND_UP (len, sizeof (int32)), len + 1); @@ -815,7 +829,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) if (vv->width > MAX_SHORT_STRING) lose ((ME, _("%s: Long string variable %s may not have missing " - "values."), h->fn, vv->name)); + "values."), + handle_get_filename (h), vv->name)); assertive_bufread (h, mv, sizeof *mv * abs (sv.n_missing_values), 0); @@ -839,7 +854,8 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) if (vv->type == ALPHA) lose ((ME, _("%s: String variable %s may not have missing " - "values specified as a range."), h->fn, vv->name)); + "values specified as a range."), + handle_get_filename (h), vv->name)); if (mv[0] == ext->lowest) { @@ -876,10 +892,12 @@ read_variables (struct file_handle * h, struct variable *** var_by_index) /* Some consistency checks. */ if (long_string_count != 0) lose ((ME, _("%s: Long string continuation records omitted at end of " - "dictionary."), h->fn)); + "dictionary."), + handle_get_filename (h))); if (next_value != ext->case_size) lose ((ME, _("%s: System file header indicates %d variable positions but " - "%d were read from file."), h->fn, ext->case_size, next_value)); + "%d were read from file."), + handle_get_filename (h), ext->case_size, next_value)); return 1; @@ -898,7 +916,7 @@ parse_format_spec (struct file_handle *h, int32 s, struct fmt_spec *v, struct va v->type = translate_fmt ((s >> 16) & 0xff); if (v->type == -1) lose ((ME, _("%s: Bad format specifier byte (%d)."), - h->fn, (s >> 16) & 0xff)); + handle_get_filename (h), (s >> 16) & 0xff)); v->w = (s >> 8) & 0xff; v->d = s & 0xff; @@ -906,10 +924,11 @@ parse_format_spec (struct file_handle *h, int32 s, struct fmt_spec *v, struct va if (v->type == -1) lose ((ME, _("%s: Bad format specifier byte (%d)."), - h->fn, (s >> 16) & 0xff)); + handle_get_filename (h), (s >> 16) & 0xff)); if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0)) lose ((ME, _("%s: %s variable %s has %s format specifier %s."), - h->fn, vv->type == ALPHA ? _("String") : _("Numeric"), + handle_get_filename (h), + vv->type == ALPHA ? _("String") : _("Numeric"), vv->name, formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"), formats[v->type].name)); @@ -988,7 +1007,8 @@ read_value_labels (struct file_handle * h, struct variable ** var_by_index) if (rec_type != 4) lose ((ME, _("%s: Variable index record (type 4) does not immediately " - "follow value label record (type 3) as it should."), h->fn)); + "follow value label record (type 3) as it should."), + handle_get_filename (h))); } /* Read number of variables associated with value label from type 4 @@ -998,8 +1018,8 @@ read_value_labels (struct file_handle * h, struct variable ** var_by_index) bswap_int32 (&n_vars); if (n_vars < 1 || n_vars > dict_get_var_cnt (ext->dict)) lose ((ME, _("%s: Number of variables associated with a value label (%d) " - "is not between 1 and the number of variables (%d)."), - h->fn, n_vars, dict_get_var_cnt (ext->dict))); + "is not between 1 and the number of variables (%d)."), + handle_get_filename (h), n_vars, dict_get_var_cnt (ext->dict))); /* Read the list of variables. */ var = xmalloc (n_vars * sizeof *var); @@ -1014,18 +1034,20 @@ read_value_labels (struct file_handle * h, struct variable ** var_by_index) bswap_int32 (&var_index); if (var_index < 1 || var_index > ext->case_size) lose ((ME, _("%s: Variable index associated with value label (%d) is " - "not between 1 and the number of values (%d)."), - h->fn, var_index, ext->case_size)); + "not between 1 and the number of values (%d)."), + handle_get_filename (h), var_index, ext->case_size)); /* Make sure it's a real variable. */ v = var_by_index[var_index - 1]; if (v == NULL) lose ((ME, _("%s: Variable index associated with value label (%d) " "refers to a continuation of a string variable, not to " - "an actual variable."), h->fn, var_index)); + "an actual variable."), + handle_get_filename (h), var_index)); if (v->type == ALPHA && v->width > MAX_SHORT_STRING) lose ((ME, _("%s: Value labels are not allowed on long string " - "variables (%s)."), h->fn, v->name)); + "variables (%s)."), + handle_get_filename (h), v->name)); /* Add it to the list of variables. */ var[i] = v; @@ -1035,8 +1057,9 @@ read_value_labels (struct file_handle * h, struct variable ** var_by_index) for (i = 1; i < n_vars; i++) if (var[i]->type != var[0]->type) lose ((ME, _("%s: Variables associated with value label are not all of " - "identical type. Variable %s has %s type, but variable %s has " - "%s type."), h->fn, + "identical type. Variable %s has %s type, but variable " + "%s has %s type."), + handle_get_filename (h), var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"), var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric"))); @@ -1075,11 +1098,12 @@ read_value_labels (struct file_handle * h, struct variable ** var_by_index) if (var[0]->type == NUMERIC) msg (MW, _("%s: File contains duplicate label for value %g for " - "variable %s."), h->fn, label->value.f, v->name); + "variable %s."), + handle_get_filename (h), label->value.f, v->name); else msg (MW, _("%s: File contains duplicate label for value `%.*s' " - "for variable %s."), - h->fn, v->width, label->value.s, v->name); + "for variable %s."), + handle_get_filename (h), v->width, label->value.s, v->name); } } @@ -1114,9 +1138,11 @@ bufread (struct file_handle * h, void *buf, size_t nbytes, size_t minalloc) if (1 != fread (buf, nbytes, 1, ext->file)) { if (ferror (ext->file)) - msg (ME, _("%s: Reading system file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Reading system file: %s."), + handle_get_filename (h), strerror (errno)); else - corrupt_msg (ME, _("%s: Unexpected end of file."), h->fn); + corrupt_msg (ME, _("%s: Unexpected end of file."), + handle_get_filename (h)); return NULL; } return buf; @@ -1134,13 +1160,15 @@ read_documents (struct file_handle * h) char *documents; if (dict_get_documents (dict) != NULL) - lose ((ME, _("%s: System file contains multiple type 6 (document) records."), - h->fn)); + lose ((ME, _("%s: System file contains multiple " + "type 6 (document) records."), + handle_get_filename (h))); assertive_bufread (h, &n_lines, sizeof n_lines, 0); if (n_lines <= 0) - lose ((ME, _("%s: Number of document lines (%ld) must be greater than 0."), - h->fn, (long) n_lines)); + lose ((ME, _("%s: Number of document lines (%ld) " + "must be greater than 0."), + handle_get_filename (h), (long) n_lines)); documents = bufread (h, NULL, 80 * n_lines, n_lines * 80 + 1); /* FIXME? Run through asciify. */ @@ -1251,7 +1279,8 @@ buffer_input (struct file_handle * h) amt = fread (ext->buf, sizeof *ext->buf, 128, ext->file); if (ferror (ext->file)) { - msg (ME, _("%s: Error reading file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Error reading file: %s."), + handle_get_filename (h), strerror (errno)); return 0; } ext->ptr = ext->buf; @@ -1290,7 +1319,8 @@ read_compressed_data (struct file_handle * h, flt64 * temp) /* Code 252 is end of file. */ if (temp_beg != temp) lose ((ME, _("%s: Compressed data is corrupted. Data ends " - "partway through a case."), h->fn)); + "in partial case."), + handle_get_filename (h))); goto lossage; case 253: /* Code 253 indicates that the value is stored explicitly @@ -1298,7 +1328,8 @@ read_compressed_data (struct file_handle * h, flt64 * temp) if (ext->ptr == NULL || ext->ptr >= ext->end) if (!buffer_input (h)) { - lose ((ME, _("%s: Unexpected end of file."), h->fn)); + lose ((ME, _("%s: Unexpected end of file."), + handle_get_filename (h))); goto lossage; } memcpy (temp++, ext->ptr++, sizeof *temp); @@ -1339,7 +1370,8 @@ read_compressed_data (struct file_handle * h, flt64 * temp) if (!buffer_input (h)) { if (temp_beg != temp) - lose ((ME, _("%s: Unexpected end of file."), h->fn)); + lose ((ME, _("%s: Unexpected end of file."), + handle_get_filename (h))); goto lossage; } memcpy (ext->x, ext->ptr++, sizeof *temp); @@ -1389,9 +1421,11 @@ sfm_read_case (struct file_handle * h, union value * perm, struct dictionary * d if (amt != nbytes) { if (ferror (ext->file)) - msg (ME, _("%s: Reading system file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Reading system file: %s."), + handle_get_filename (h), strerror (errno)); else if (amt != 0) - msg (ME, _("%s: Partial record at end of system file."), h->fn); + msg (ME, _("%s: Partial record at end of system file."), + handle_get_filename (h)); goto lossage; } } diff --git a/src/sfm-write.c b/src/sfm-write.c index fd500f45c0..64d364abb6 100644 --- a/src/sfm-write.c +++ b/src/sfm-write.c @@ -92,23 +92,25 @@ sfm_write_dictionary (struct sfm_write_info *inf) if (inf->h->class != NULL) { - msg (ME, _("Cannot write file %s as system file: already opened for %s."), - fh_handle_name (inf->h), inf->h->class->name); + msg (ME, _("Cannot write file %s as system file: " + "already opened for %s."), + handle_get_name (inf->h), inf->h->class->name); return 0; } msg (VM (1), _("%s: Opening system-file handle %s for writing."), - fh_handle_filename (inf->h), fh_handle_name (inf->h)); + handle_get_filename (inf->h), handle_get_name (inf->h)); /* Open the physical disk file. */ inf->h->class = &sfm_w_class; inf->h->ext = ext = xmalloc (sizeof (struct sfm_fhuser_ext)); - ext->file = fopen (inf->h->norm_fn, "wb"); + ext->file = fopen (handle_get_filename (inf->h), "wb"); ext->elem_type = NULL; if (ext->file == NULL) { msg (ME, _("An error occurred while opening \"%s\" for writing " - "as a system file: %s."), inf->h->fn, strerror (errno)); + "as a system file: %s."), + handle_get_filename (inf->h), strerror (errno)); err_cond_fail (); free (ext); return 0; @@ -600,7 +602,8 @@ bufwrite (struct file_handle * h, const void *buf, size_t nbytes) assert (buf); if (1 != fwrite (buf, nbytes, 1, ext->file)) { - msg (ME, _("%s: Writing system file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Writing system file: %s."), + handle_get_filename (h), strerror (errno)); return 0; } return 1; @@ -736,7 +739,8 @@ sfm_close (struct file_handle * h) } if (EOF == fclose (ext->file)) - msg (ME, _("%s: Closing system file: %s."), h->fn, strerror (errno)); + msg (ME, _("%s: Closing system file: %s."), + handle_get_filename (h), strerror (errno)); free (ext->buf); free (ext->elem_type); diff --git a/src/sort.c b/src/sort.c index ac70da1181..cc0a833f69 100644 --- a/src/sort.c +++ b/src/sort.c @@ -69,8 +69,6 @@ cmd_sort_cases (void) struct sort_cases_pgm *scp; int success; - lex_match_id ("SORT"); - lex_match_id ("CASES"); lex_match (T_BY); scp = parse_sort (); diff --git a/src/split-file.c b/src/split-file.c index bffcd5b180..6eba150b99 100644 --- a/src/split-file.c +++ b/src/split-file.c @@ -29,9 +29,6 @@ int cmd_split_file (void) { - lex_match_id ("SPLIT"); - lex_match_id ("FILE"); - if (lex_match_id ("OFF")) dict_set_split_vars (default_dict, NULL, 0); else diff --git a/src/sysfile-info.c b/src/sysfile-info.c index e4a2dce720..c67c184e2d 100644 --- a/src/sysfile-info.c +++ b/src/sysfile-info.c @@ -77,9 +77,6 @@ cmd_sysfile_info (void) int r, nr; int i; - lex_match_id ("SYSFILE"); - lex_match_id ("INFO"); - lex_match_id ("FILE"); lex_match ('='); @@ -95,7 +92,7 @@ cmd_sysfile_info (void) t = tab_create (2, 9, 0); tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, 8); tab_text (t, 0, 0, TAB_LEFT, _("File:")); - tab_text (t, 1, 0, TAB_LEFT, fh_handle_filename (h)); + tab_text (t, 1, 0, TAB_LEFT, handle_get_filename (h)); tab_text (t, 0, 1, TAB_LEFT, _("Label:")); { const char *label = dict_get_label (d); @@ -180,8 +177,6 @@ cmd_display (void) int n; struct variable **vl; - lex_match_id ("DISPLAY"); - if (lex_match_id ("MACROS")) display_macros (); else if (lex_match_id ("DOCUMENTS")) diff --git a/src/t-test.q b/src/t-test.q index c76bd2d669..8cef9053b1 100644 --- a/src/t-test.q +++ b/src/t-test.q @@ -213,12 +213,6 @@ cmd_t_test(void) struct ssbox stat_summary_box; struct trbox test_results_box; - if (!lex_force_match_id ("T")) - return CMD_FAILURE; - - lex_match ('-'); - lex_match_id ("TEST"); - if ( !parse_t_test(&cmd) ) return CMD_FAILURE; diff --git a/src/temporary.c b/src/temporary.c index 090ceafea1..70d86cab28 100644 --- a/src/temporary.c +++ b/src/temporary.c @@ -39,8 +39,6 @@ int temp_trns; int cmd_temporary (void) { - lex_match_id ("TEMPORARY"); - /* TEMPORARY is not allowed inside DO IF or LOOP. */ if (ctl_stack) { diff --git a/src/title.c b/src/title.c index dca7b47607..50a065b9d8 100644 --- a/src/title.c +++ b/src/title.c @@ -171,9 +171,6 @@ cmd_document (void) int cmd_drop_documents (void) { - lex_match_id ("DROP"); - lex_match_id ("DOCUMENTS"); - dict_set_documents (default_dict, NULL); return lex_end_of_command (); diff --git a/src/val-labs.c b/src/val-labs.c index 9d289cbce5..882f26e625 100644 --- a/src/val-labs.c +++ b/src/val-labs.c @@ -41,17 +41,12 @@ static int get_label (struct variable **vars, int var_cnt); int cmd_value_labels (void) { - lex_match_id ("VALUE"); - lex_match_id ("LABELS"); return do_value_labels (1); } int cmd_add_value_labels (void) { - lex_match_id ("ADD"); - lex_match_id ("VALUE"); - lex_match_id ("LABELS"); return do_value_labels (0); } @@ -82,6 +77,7 @@ do_value_labels (int erase) free (vars); } + free (vars); if (token != '.') { diff --git a/src/var-labs.c b/src/var-labs.c index bc22439669..ee9f4b786d 100644 --- a/src/var-labs.c +++ b/src/var-labs.c @@ -41,8 +41,6 @@ cmd_variable_labels (void) int i; - lex_match_id ("VARIABLE"); - lex_match_id ("LABELS"); lex_match ('/'); do { diff --git a/src/vector.c b/src/vector.c index 2296672dc2..99b3423af1 100644 --- a/src/vector.c +++ b/src/vector.c @@ -43,8 +43,6 @@ cmd_vector (void) /* Maximum allocated position for vecnames, plus one position. */ char *endp = NULL; - lex_match_id ("VECTOR"); - cp = vecnames = xmalloc (256); endp = &vecnames[256]; do diff --git a/src/vfm.c b/src/vfm.c index 0c152c09f7..b5d2a8d356 100644 --- a/src/vfm.c +++ b/src/vfm.c @@ -435,8 +435,12 @@ close_active_file (void) /* Old data sink becomes new data source. */ if (vfm_sink->class->make_source != NULL) vfm_source = vfm_sink->class->make_source (vfm_sink); - else - vfm_source = NULL; + else + { + if (vfm_sink->class->destroy != NULL) + vfm_sink->class->destroy (vfm_sink); + vfm_source = NULL; + } free_case_sink (vfm_sink); vfm_sink = NULL; diff --git a/src/weight.c b/src/weight.c index 41dc33f655..66b7e615ac 100644 --- a/src/weight.c +++ b/src/weight.c @@ -37,8 +37,6 @@ struct weight_trns int cmd_weight (void) { - lex_match_id ("WEIGHT"); - if (lex_match_id ("OFF")) dict_set_weight (default_dict, NULL); else diff --git a/tests/ChangeLog b/tests/ChangeLog index 447cb20be7..559bda0838 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +Sat Mar 20 18:11:15 2004 Ben Pfaff + + * command/list.sh: Update output. + + * command/print.sh: Ditto. + + * command/weight.sh: Ditto. + Sun Mar 14 23:04:14 2004 Ben Pfaff * command/sort.sh: Use numeric, not string, data to avoid spurious diff --git a/tests/command/list.sh b/tests/command/list.sh index c82dd27346..33297eb007 100755 --- a/tests/command/list.sh +++ b/tests/command/list.sh @@ -85,7 +85,7 @@ diff -b -B $TEMPDIR/pspp.list - <