From 97466647616875cca6662b50b5eb467c8ef8c617 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 21 Mar 2021 11:59:30 -0700 Subject: [PATCH] SET: Reimplement parser without q2c. --- src/language/lexer/lexer.c | 41 +- src/language/lexer/lexer.h | 4 +- src/language/utilities/automake.mk | 8 +- src/language/utilities/{set.q => set.c} | 999 ++++++++++++++---------- 4 files changed, 631 insertions(+), 421 deletions(-) rename src/language/utilities/{set.q => set.c} (55%) diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 34b697efe2..b69bc9a5d0 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -269,23 +269,40 @@ lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...) va_end (args); } -/* Prints a syntax error message saying that OPTION0 or one of the other - strings following it, up to the first NULL, is expected. */ +/* Prints a syntax error message saying that one of the strings provided as + varargs, up to the first NULL, is expected. */ void -(lex_error_expecting) (struct lexer *lexer, const char *option0, ...) +(lex_error_expecting) (struct lexer *lexer, ...) { - enum { MAX_OPTIONS = 8 }; - const char *options[MAX_OPTIONS + 1]; va_list args; - int n; - va_start (args, option0); - options[0] = option0; - n = 0; - while (n + 1 < MAX_OPTIONS && options[n] != NULL) - options[++n] = va_arg (args, const char *); + va_start (args, lexer); + lex_error_expecting_valist (lexer, args); va_end (args); +} + +/* Prints a syntax error message saying that one of the options provided in + ARGS, up to the first NULL, is expected. */ +void +lex_error_expecting_valist (struct lexer *lexer, va_list args) +{ + enum { MAX_OPTIONS = 9 }; + const char *options[MAX_OPTIONS]; + int n = 0; + while (n < MAX_OPTIONS) + { + const char *option = va_arg (args, const char *); + if (!option) + break; + options[n++] = option; + } + lex_error_expecting_array (lexer, options, n); +} + +void +lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n) +{ switch (n) { case 0: @@ -334,7 +351,7 @@ void break; default: - NOT_REACHED (); + lex_error (lexer, NULL); } } diff --git a/src/language/lexer/lexer.h b/src/language/lexer/lexer.h index 91200ce6cd..e98dac00f5 100644 --- a/src/language/lexer/lexer.h +++ b/src/language/lexer/lexer.h @@ -161,9 +161,11 @@ void lex_next_error (struct lexer *, int n0, int n1, const char *, ...) PRINTF_FORMAT (4, 5); int lex_end_of_command (struct lexer *); -void lex_error_expecting (struct lexer *, const char *, ...) SENTINEL(0); +void lex_error_expecting (struct lexer *, ...) SENTINEL(0); #define lex_error_expecting(...) \ lex_error_expecting(__VA_ARGS__, NULL_SENTINEL) +void lex_error_expecting_valist (struct lexer *, va_list); +void lex_error_expecting_array (struct lexer *, const char **, size_t n); void lex_sbc_only_once (const char *); void lex_sbc_missing (const char *); diff --git a/src/language/utilities/automake.mk b/src/language/utilities/automake.mk index f9e6a4a668..7752792377 100644 --- a/src/language/utilities/automake.mk +++ b/src/language/utilities/automake.mk @@ -17,20 +17,14 @@ ## Process this file with automake to produce Makefile.in -*- makefile -*- -src_language_utilities_built_sources = \ - src/language/utilities/set.c - language_utilities_sources = \ src/language/utilities/cache.c \ src/language/utilities/cd.c \ src/language/utilities/date.c \ src/language/utilities/echo.c \ src/language/utilities/host.c \ + src/language/utilities/set.c \ src/language/utilities/title.c \ src/language/utilities/include.c \ src/language/utilities/output.c \ src/language/utilities/permissions.c - -all_q_sources += $(src_language_utilities_built_sources:.c=.q) -EXTRA_DIST += $(src_language_utilities_built_sources:.c=.q) -CLEANFILES += $(src_language_utilities_built_sources) diff --git a/src/language/utilities/set.q b/src/language/utilities/set.c similarity index 55% rename from src/language/utilities/set.q rename to src/language/utilities/set.c index aa7f8b8034..89a7052600 100644 --- a/src/language/utilities/set.q +++ b/src/language/utilities/set.c @@ -37,6 +37,7 @@ #include "language/command.h" #include "language/lexer/format-parser.h" #include "language/lexer/lexer.h" +#include "libpspp/assertion.h" #include "libpspp/compiler.h" #include "libpspp/copyleft.h" #include "libpspp/temp-file.h" @@ -50,256 +51,115 @@ #include "output/journal.h" #include "output/pivot-table.h" -#if HAVE_LIBTERMCAP -#if HAVE_TERMCAP_H -#include -#else /* !HAVE_TERMCAP_H */ -int tgetent (char *, const char *); -int tgetnum (const char *); -#endif /* !HAVE_TERMCAP_H */ -#endif /* !HAVE_LIBTERMCAP */ - -#include "xalloc.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* (specification) - "SET" (stc_): - blanks=custom; - block=string; - boxstring=string; - case=size:upper/uplow; - cca=string; - ccb=string; - ccc=string; - ccd=string; - cce=string; - compression=compress:on/off; - cpi=integer; - decimal=dec:dot/comma; - epoch=custom; - errors=custom; - format=custom; - fuzzbits=integer; - headers=headers:no/yes/blank; - highres=hires:on/off; - histogram=string; - include=inc:on/off; - journal=custom; - log=custom; - length=custom; - locale=custom; - lowres=lores:auto/on/off; - lpi=integer; - menus=menus:standard/extended; - messages=custom; - mexpand=mexp:on/off; - miterate=integer; - mnest=integer; - mprint=mprint:on/off; - mxerrs=integer; - mxloops=integer; - mxmemory=integer; - mxwarns=integer; - printback=custom; - results=custom; - rib=rib:msbfirst/lsbfirst/vax/native; - rrb=rrb:native/isl/isb/idl/idb/vf/vd/vg/zs/zl; - safer=safe:on; - scompression=scompress:on/off; - scripttab=string; - seed=custom; - small=double; - tnumbers=custom; - tvars=custom; - tb1=string; - tbfonts=string; - tlook=custom; - undefined=undef:warn/nowarn; - wib=wib:msbfirst/lsbfirst/vax/native; - wrb=wrb:native/isl/isb/idl/idb/vf/vd/vg/zs/zl; - width=custom; - workspace=integer; - xsort=xsort:yes/no. -*/ +static bool +match_subcommand (struct lexer *lexer, const char *name) +{ + if (lex_match_id (lexer, name)) + { + lex_match (lexer, T_EQUALS); + return true; + } + else + return false; +} -/* (headers) */ +static int +parse_enum_valist (struct lexer *lexer, va_list args) +{ + for (;;) + { + const char *name = va_arg (args, char *); + if (!name) + return -1; + int value = va_arg (args, int); -/* (declarations) */ + if (lex_match_id (lexer, name)) + return value; + } +} -/* (functions) */ +#define parse_enum(...) parse_enum (__VA_ARGS__, NULL_SENTINEL) +static int SENTINEL(0) +(parse_enum) (struct lexer *lexer, ...) +{ + va_list args; -static enum integer_format stc_to_integer_format (int stc); -static enum float_format stc_to_float_format (int stc); + va_start (args, lexer); + int retval = parse_enum_valist (lexer, args); + va_end (args); -int -cmd_set (struct lexer *lexer, struct dataset *ds) + return retval; +} + +#define force_parse_enum(...) force_parse_enum (__VA_ARGS__, NULL_SENTINEL) +static int SENTINEL(0) +(force_parse_enum) (struct lexer *lexer, ...) { - struct cmd_set cmd; + va_list args; - if (!parse_set (lexer, ds, &cmd, NULL)) - { - return CMD_FAILURE; - } + va_start (args, lexer); + int retval = parse_enum_valist (lexer, args); + va_end (args); - if (cmd.sbc_cca) - settings_set_cc ( cmd.s_cca, FMT_CCA); - if (cmd.sbc_ccb) - settings_set_cc ( cmd.s_ccb, FMT_CCB); - if (cmd.sbc_ccc) - settings_set_cc ( cmd.s_ccc, FMT_CCC); - if (cmd.sbc_ccd) - settings_set_cc ( cmd.s_ccd, FMT_CCD); - if (cmd.sbc_cce) - settings_set_cc ( cmd.s_cce, FMT_CCE); - - if (cmd.sbc_decimal) - settings_set_decimal_char (cmd.dec == STC_DOT ? '.' : ','); - if (cmd.sbc_fuzzbits) + if (retval == -1) { - int fuzzbits = cmd.n_fuzzbits[0]; - if (fuzzbits >= 0 && fuzzbits <= 20) - settings_set_fuzzbits (fuzzbits); - else - msg (SE, _("%s must be between 0 and 20."), "FUZZBITS"); - } + enum { MAX_OPTIONS = 9 }; + const char *options[MAX_OPTIONS]; + int n = 0; - if (cmd.sbc_include) - settings_set_include (cmd.inc == STC_ON); - if (cmd.sbc_mxerrs) - { - if (cmd.n_mxerrs[0] >= 1) - settings_set_max_messages (MSG_S_ERROR, cmd.n_mxerrs[0]); - else - msg (SE, _("%s must be at least 1."), "MXERRS"); - } - if (cmd.sbc_mxloops) - { - if (cmd.n_mxloops[0] >= 1) - settings_set_mxloops (cmd.n_mxloops[0]); - else - msg (SE, _("%s must be at least 1."), "MXLOOPS"); - } - if (cmd.sbc_mxwarns) - { - if (cmd.n_mxwarns[0] >= 0) - settings_set_max_messages (MSG_S_WARNING, cmd.n_mxwarns[0]); - else - msg (SE, _("%s must not be negative."), "MXWARNS"); - } - if (cmd.sbc_rib) - settings_set_input_integer_format (stc_to_integer_format (cmd.rib)); - if (cmd.sbc_rrb) - settings_set_input_float_format (stc_to_float_format (cmd.rrb)); - if (cmd.sbc_safer) - settings_set_safer_mode (); - if (cmd.sbc_scompression) - settings_set_scompression (cmd.scompress == STC_ON); - if (cmd.sbc_small) - settings_set_small (cmd.n_small[0]); - if (cmd.sbc_undefined) - settings_set_undefined (cmd.undef == STC_WARN); - if (cmd.sbc_wib) - settings_set_output_integer_format (stc_to_integer_format (cmd.wib)); - if (cmd.sbc_wrb) - settings_set_output_float_format (stc_to_float_format (cmd.wrb)); - if (cmd.sbc_workspace) - { - if ( cmd.n_workspace[0] < 1024 && ! settings_get_testing_mode ()) - msg (SE, _("%s must be at least 1MB"), "WORKSPACE"); - else if (cmd.n_workspace[0] <= 0) - msg (SE, _("%s must be positive"), "WORKSPACE"); - else - settings_set_workspace (cmd.n_workspace[0] * 1024L); - } + va_start (args, lexer); + while (n < MAX_OPTIONS) + { + const char *name = va_arg (args, char *); + if (!name) + break; + va_arg (args, int); + + options[n++] = name; + } + va_end (args); - if (cmd.sbc_block) - msg (SW, _("%s is obsolete."), "BLOCK"); - if (cmd.sbc_boxstring) - msg (SW, _("%s is obsolete."), "BOXSTRING"); - if (cmd.sbc_cpi) - msg (SW, _("%s is obsolete."), "CPI"); - if (cmd.sbc_histogram) - msg (SW, _("%s is obsolete."), "HISTOGRAM"); - if (cmd.sbc_lpi) - msg (SW, _("%s is obsolete."), "LPI"); - if (cmd.sbc_menus) - msg (SW, _("%s is obsolete."), "MENUS"); - if (cmd.sbc_xsort) - msg (SW, _("%s is obsolete."), "XSORT"); - if (cmd.sbc_mxmemory) - msg (SE, _("%s is obsolete."), "MXMEMORY"); - if (cmd.sbc_scripttab) - msg (SE, _("%s is obsolete."), "SCRIPTTAB"); - if (cmd.sbc_tbfonts) - msg (SW, _("%s is obsolete."), "TBFONTS"); - if (cmd.sbc_tb1 && cmd.s_tb1) - msg (SW, _("%s is obsolete."), "TB1"); - - if (cmd.sbc_case) - msg (SW, _("%s is not yet implemented."), "CASE"); - - if (cmd.sbc_compression) - msg (SW, _("Active file compression is not implemented.")); - - free_set (&cmd); + lex_error_expecting_array (lexer, options, n); + } - return CMD_SUCCESS; + return retval; } -/* Returns the integer_format value corresponding to STC, - which should be the value of cmd.rib or cmd.wib. */ -static enum integer_format -stc_to_integer_format (int stc) +static int +parse_bool (struct lexer *lexer) { - return (stc == STC_MSBFIRST ? INTEGER_MSB_FIRST - : stc == STC_LSBFIRST ? INTEGER_LSB_FIRST - : stc == STC_VAX ? INTEGER_VAX - : INTEGER_NATIVE); + return parse_enum (lexer, + "ON", true, "YES", true, + "OFF", false, "NO", false); } -/* Returns the float_format value corresponding to STC, - which should be the value of cmd.rrb or cmd.wrb. */ -static enum float_format -stc_to_float_format (int stc) +static int +force_parse_bool (struct lexer *lexer) { - switch (stc) - { - case STC_NATIVE: - return FLOAT_NATIVE_DOUBLE; - - case STC_ISL: - return FLOAT_IEEE_SINGLE_LE; - case STC_ISB: - return FLOAT_IEEE_SINGLE_BE; - case STC_IDL: - return FLOAT_IEEE_DOUBLE_LE; - case STC_IDB: - return FLOAT_IEEE_DOUBLE_BE; - - case STC_VF: - return FLOAT_VAX_F; - case STC_VD: - return FLOAT_VAX_D; - case STC_VG: - return FLOAT_VAX_G; - - case STC_ZS: - return FLOAT_Z_SHORT; - case STC_ZL: - return FLOAT_Z_LONG; - } + return force_parse_enum (lexer, + "ON", true, "YES", true, + "OFF", false, "NO", false); +} - NOT_REACHED (); +static bool +force_parse_int (struct lexer *lexer, int *integerp) +{ + if (!lex_force_int (lexer)) + return false; + *integerp = lex_integer (lexer); + lex_get (lexer); + return true; } -static int -set_output_routing (struct lexer *lexer, enum settings_output_type type) +static bool +parse_output_routing (struct lexer *lexer, enum settings_output_type type) { enum settings_output_devices devices; - - lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "ON") || lex_match_id (lexer, "BOTH")) devices = SETTINGS_DEVICE_LISTING | SETTINGS_DEVICE_TERMINAL; else if (lex_match_id (lexer, "TERMINAL")) @@ -311,120 +171,175 @@ set_output_routing (struct lexer *lexer, enum settings_output_type type) else { lex_error (lexer, NULL); - return 0; + return false; } settings_set_output_routing (type, devices); - return 1; + return true; } -/* Parses the BLANKS subcommand, which controls the value that - completely blank fields in numeric data imply. X, Wnd: Syntax is - SYSMIS or a numeric value. */ -static int -stc_custom_blanks (struct lexer *lexer, - struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_integer_format (struct lexer *lexer, + void (*set_format) (enum integer_format)) +{ + int value = force_parse_enum (lexer, + "MSBFIRST", INTEGER_MSB_FIRST, + "LSBFIRST", INTEGER_LSB_FIRST, + "VAX", INTEGER_VAX, + "NATIVE", INTEGER_NATIVE); + if (value >= 0) + set_format (value); + return value >= 0; +} + +static bool +parse_real_format (struct lexer *lexer, + void (*set_format) (enum float_format)) +{ + int value = force_parse_enum (lexer, + "NATIVE", FLOAT_NATIVE_DOUBLE, + "ISL", FLOAT_IEEE_SINGLE_LE, + "ISB", FLOAT_IEEE_SINGLE_BE, + "IDL", FLOAT_IEEE_DOUBLE_LE, + "IDB", FLOAT_IEEE_DOUBLE_BE, + "VF", FLOAT_VAX_F, + "VD", FLOAT_VAX_D, + "VG", FLOAT_VAX_G, + "ZS", FLOAT_Z_SHORT, + "ZL", FLOAT_Z_LONG); + if (value >= 0) + set_format (value); + return value >= 0; +} + +static bool +parse_unimplemented (struct lexer *lexer, const char *name) +{ + msg (SW, _("%s is not yet implemented."), name); + if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD) + lex_get (lexer); + return true; +} + +static bool +parse_ccx (struct lexer *lexer, enum fmt_type ccx) +{ + if (!lex_force_string (lexer)) + return false; + + settings_set_cc (lex_tokcstr (lexer), ccx); + lex_get (lexer); + return true; +} + +static bool +parse_BASETEXTDIRECTION (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "BASETEXTDIRECTION"); +} + +static bool +parse_BLANKS (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "SYSMIS")) - { - lex_get (lexer); - settings_set_blanks (SYSMIS); - } + settings_set_blanks (SYSMIS); else { if (!lex_force_num (lexer)) - return 0; + return false; settings_set_blanks (lex_number (lexer)); lex_get (lexer); } - return 1; + return true; } -static int -stc_custom_tnumbers (struct lexer *lexer, - struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_BLOCK (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); + return parse_unimplemented (lexer, "BLOCK"); +} - if (lex_match_id (lexer, "VALUES")) - settings_set_show_values (SETTINGS_VALUE_SHOW_VALUE); - else if (lex_match_id (lexer, "LABELS")) - settings_set_show_values (SETTINGS_VALUE_SHOW_LABEL); - else if (lex_match_id (lexer, "BOTH")) - settings_set_show_values (SETTINGS_VALUE_SHOW_BOTH); - else - { - lex_error_expecting (lexer, "VALUES", "LABELS", "BOTH"); - return 0; - } +static bool +parse_BOX (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "BOX"); +} - return 1; +static bool +parse_CACHE (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "CACHE"); } +static bool +parse_CCA (struct lexer *lexer) +{ + return parse_ccx (lexer, FMT_CCA); +} -static int -stc_custom_tvars (struct lexer *lexer, - struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_CCB (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); + return parse_ccx (lexer, FMT_CCB); +} - if (lex_match_id (lexer, "NAMES")) - settings_set_show_variables (SETTINGS_VALUE_SHOW_VALUE); - else if (lex_match_id (lexer, "LABELS")) - settings_set_show_variables (SETTINGS_VALUE_SHOW_LABEL); - else if (lex_match_id (lexer, "BOTH")) - settings_set_show_variables (SETTINGS_VALUE_SHOW_BOTH); - else - { - lex_error_expecting (lexer, "NAMES", "LABELS", "BOTH"); - return 0; - } +static bool +parse_CCC (struct lexer *lexer) +{ + return parse_ccx (lexer, FMT_CCC); +} - return 1; +static bool +parse_CCD (struct lexer *lexer) +{ + return parse_ccx (lexer, FMT_CCD); } -static int -stc_custom_tlook (struct lexer *lexer, - struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_CCE (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); + return parse_ccx (lexer, FMT_CCE); +} - if (lex_match_id (lexer, "NONE")) - pivot_table_look_set_default (pivot_table_look_builtin_default ()); - else if (lex_is_string (lexer)) - { - struct pivot_table_look *look; - char *error = pivot_table_look_read (lex_tokcstr (lexer), &look); - lex_get (lexer); +static bool +parse_CELLSBREAK (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "CELLSBREAK"); +} - if (error) - { - msg (SE, "%s", error); - free (error); - return 0; - } +static bool +parse_CMPTRANS (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "CMPTRANS"); +} - pivot_table_look_set_default (look); - pivot_table_look_unref (look); - } +static bool +parse_COMPRESSION (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "COMPRESSION"); +} - return 1; +static bool +parse_CTEMPLATE (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "CTEMPLATE"); } -/* Parses the EPOCH subcommand, which controls the epoch used for - parsing 2-digit years. */ -static int -stc_custom_epoch (struct lexer *lexer, - struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_DECIMAL (struct lexer *lexer) +{ + int decimal_char = force_parse_enum (lexer, + "DOT", '.', + "COMMA", ','); + if (decimal_char != -1) + settings_set_decimal_char (decimal_char); + return decimal_char != -1; +} + +static bool +parse_EPOCH (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "AUTOMATIC")) settings_set_epoch (-1); else if (lex_is_integer (lexer)) @@ -434,42 +349,120 @@ stc_custom_epoch (struct lexer *lexer, if (new_epoch < 1500) { msg (SE, _("%s must be 1500 or later."), "EPOCH"); - return 0; + return false; } settings_set_epoch (new_epoch); } else { lex_error (lexer, _("expecting %s or year"), "AUTOMATIC"); - return 0; + return false; } - return 1; + return true; } -static int -stc_custom_errors (struct lexer *lexer, struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_ERRORS (struct lexer *lexer) { - return set_output_routing (lexer, SETTINGS_OUTPUT_ERROR); + return parse_output_routing (lexer, SETTINGS_OUTPUT_ERROR); } -static int -stc_custom_length (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_FORMAT (struct lexer *lexer) { - int page_length; + struct fmt_spec fmt; lex_match (lexer, T_EQUALS); + if (!parse_format_specifier (lexer, &fmt)) + return false; + + if (!fmt_check_output (&fmt)) + return false; + + if (fmt_is_string (fmt.type)) + { + char str[FMT_STRING_LEN_MAX + 1]; + msg (SE, _("%s requires numeric output format as an argument. " + "Specified format %s is of type string."), + "FORMAT", + fmt_to_string (&fmt, str)); + return false; + } + + settings_set_format (&fmt); + return true; +} + +static bool +parse_FUZZBITS (struct lexer *lexer) +{ + if (!lex_force_int (lexer)) + return false; + int fuzzbits = lex_integer (lexer); + lex_get (lexer); + + if (fuzzbits >= 0 && fuzzbits <= 20) + settings_set_fuzzbits (fuzzbits); + else + msg (SE, _("%s must be between 0 and 20."), "FUZZBITS"); + return true; +} + +static bool +parse_HEADER (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "HEADER"); +} + +static bool +parse_INCLUDE (struct lexer *lexer) +{ + int include = force_parse_bool (lexer); + if (include != -1) + settings_set_include (include); + return include != -1; +} + +static bool +parse_JOURNAL (struct lexer *lexer) +{ + int b = parse_bool (lexer); + if (b == true) + journal_enable (); + else if (b == false) + journal_disable (); + else if (lex_is_string (lexer) || lex_token (lexer) == T_ID) + { + char *filename = utf8_to_filename (lex_tokcstr (lexer)); + journal_set_file_name (filename); + free (filename); + + lex_get (lexer); + } + else + { + lex_error (lexer, NULL); + return false; + } + return true; +} + +static bool +parse_LENGTH (struct lexer *lexer) +{ + int page_length; + if (lex_match_id (lexer, "NONE")) page_length = -1; else { if (!lex_force_int (lexer)) - return 0; + return false; if (lex_integer (lexer) < 1) { msg (SE, _("%s must be at least %d."), "LENGTH", 1); - return 0; + return false; } page_length = lex_integer (lexer); lex_get (lexer); @@ -478,83 +471,244 @@ stc_custom_length (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_se if (page_length != -1) settings_set_viewlength (page_length); - return 1; + return true; } -static int -stc_custom_locale (struct lexer *lexer, struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_LOCALE (struct lexer *lexer) { - const char *s; - - lex_match (lexer, T_EQUALS); - - if ( !lex_force_string (lexer)) - return 0; - - s = lex_tokcstr (lexer); + if (!lex_force_string (lexer)) + return false; - /* First try this string as an encoding name */ - if ( valid_encoding (s)) + /* Try the argument as an encoding name, then as a locale name or alias. */ + const char *s = lex_tokcstr (lexer); + if (valid_encoding (s)) set_default_encoding (s); - - /* Now try as a locale name (or alias) */ - else if (set_encoding_from_locale (s)) - { - } - else + else if (!set_encoding_from_locale (s)) { msg (ME, _("%s is not a recognized encoding or locale name"), s); - return 0; + return false; } lex_get (lexer); + return true; +} - return 1; +static bool +parse_MESSAGES (struct lexer *lexer) +{ + return parse_output_routing (lexer, SETTINGS_OUTPUT_NOTE); } -static int -stc_custom_messages (struct lexer *lexer, struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_MEXPAND (struct lexer *lexer) { - return set_output_routing (lexer, SETTINGS_OUTPUT_NOTE); + return parse_unimplemented (lexer, "MEXPAND"); } -static int -stc_custom_printback (struct lexer *lexer, struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_MITERATE (struct lexer *lexer) { - return set_output_routing (lexer, SETTINGS_OUTPUT_SYNTAX); + return parse_unimplemented (lexer, "MITERATE"); } -static int -stc_custom_results (struct lexer *lexer, struct dataset *ds UNUSED, - struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_MNEST (struct lexer *lexer) { - return set_output_routing (lexer, SETTINGS_OUTPUT_RESULT); + return parse_unimplemented (lexer, "MNEST"); } -static int -stc_custom_seed (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_MPRINT (struct lexer *lexer) +{ + return parse_unimplemented (lexer, "MPRINT"); +} + +static bool +parse_MXERRS (struct lexer *lexer) +{ + int n; + if (!force_parse_int (lexer, &n)) + return false; + + if (n >= 1) + settings_set_max_messages (MSG_S_ERROR, n); + else + msg (SE, _("%s must be at least 1."), "MXERRS"); + return true; +} + +static bool +parse_MXLOOPS (struct lexer *lexer) +{ + int n; + if (!force_parse_int (lexer, &n)) + return false; + + if (n >= 1) + settings_set_mxloops (n); + else + msg (SE, _("%s must be at least 1."), "MXLOOPS"); + return true; +} + +static bool +parse_MXWARNS (struct lexer *lexer) +{ + int n; + if (!force_parse_int (lexer, &n)) + return false; + + if (n >= 0) + settings_set_max_messages (MSG_S_WARNING, n); + else + msg (SE, _("%s must not be negative."), "MXWARNS"); + return true; +} + +static bool +parse_PRINTBACK (struct lexer *lexer) +{ + return parse_output_routing (lexer, SETTINGS_OUTPUT_SYNTAX); +} + +static bool +parse_RESULTS (struct lexer *lexer) +{ + return parse_output_routing (lexer, SETTINGS_OUTPUT_RESULT); +} + +static bool +parse_RIB (struct lexer *lexer) +{ + return parse_integer_format (lexer, settings_set_input_integer_format); +} + +static bool +parse_RRB (struct lexer *lexer) +{ + return parse_real_format (lexer, settings_set_input_float_format); +} + +static bool +parse_SAFER (struct lexer *lexer) +{ + bool ok = force_parse_enum (lexer, "ON", true, "YES", true) != -1; + if (ok) + settings_set_safer_mode (); + return ok; +} + +static bool +parse_SCOMPRESSION (struct lexer *lexer) +{ + int value = force_parse_bool (lexer); + if (value >= 0) + settings_set_scompression (value); + return value >= 0; +} + +static bool +parse_SEED (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "RANDOM")) set_rng (time (0)); else { if (!lex_force_num (lexer)) - return 0; + return false; set_rng (lex_number (lexer)); lex_get (lexer); } - return 1; + return true; } -static int -stc_custom_width (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_SMALL (struct lexer *lexer) +{ + if (!lex_force_num (lexer)) + return false; + settings_set_small (lex_number (lexer)); + lex_get (lexer); + return true; +} + +static bool +parse_TNUMBERS (struct lexer *lexer) +{ + int value = force_parse_enum (lexer, + "LABELS", SETTINGS_VALUE_SHOW_LABEL, + "VALUES", SETTINGS_VALUE_SHOW_VALUE, + "BOTH", SETTINGS_VALUE_SHOW_BOTH); + if (value >= 0) + settings_set_show_values (value); + return value >= 0; +} + +static bool +parse_TVARS (struct lexer *lexer) +{ + int value = force_parse_enum (lexer, + "LABELS", SETTINGS_VALUE_SHOW_LABEL, + "NAMES", SETTINGS_VALUE_SHOW_VALUE, + "BOTH", SETTINGS_VALUE_SHOW_BOTH); + if (value >= 0) + settings_set_show_variables (value); + return value >= 0; +} + +static bool +parse_TLOOK (struct lexer *lexer) +{ + if (lex_match_id (lexer, "NONE")) + pivot_table_look_set_default (pivot_table_look_builtin_default ()); + else if (lex_is_string (lexer)) + { + struct pivot_table_look *look; + char *error = pivot_table_look_read (lex_tokcstr (lexer), &look); + lex_get (lexer); + + if (error) + { + msg (SE, "%s", error); + free (error); + return false; + } + + pivot_table_look_set_default (look); + pivot_table_look_unref (look); + } + + return true; +} + +static bool +parse_UNDEFINED (struct lexer *lexer) +{ + int value = force_parse_enum (lexer, + "WARN", true, + "NOWARN", false); + if (value >= 0) + settings_set_undefined (value); + return value >= 0; +} + +static bool +parse_WIB (struct lexer *lexer) +{ + return parse_integer_format (lexer, settings_set_output_integer_format); +} + +static bool +parse_WRB (struct lexer *lexer) +{ + return parse_real_format (lexer, settings_set_output_float_format); +} + +static bool +parse_WIDTH (struct lexer *lexer) { - lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "NARROW")) settings_set_viewwidth (79); else if (lex_match_id (lexer, "WIDE")) @@ -575,62 +729,105 @@ stc_custom_width (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set return 1; } -/* Parses FORMAT subcommand, which consists of a numeric format - specifier. */ -static int -stc_custom_format (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) +static bool +parse_WORKSPACE (struct lexer *lexer) { - struct fmt_spec fmt; - - lex_match (lexer, T_EQUALS); - if (!parse_format_specifier (lexer, &fmt)) - return 0; - - if (!fmt_check_output (&fmt)) - return 0; + if (!lex_force_int (lexer)) + return false; + int workspace = lex_integer (lexer); + lex_get (lexer); - if (fmt_is_string (fmt.type)) + if (workspace < 1024 && !settings_get_testing_mode ()) + msg (SE, _("%s must be at least 1MB"), "WORKSPACE"); + else if (workspace <= 0) + msg (SE, _("%s must be positive"), "WORKSPACE"); + else + settings_set_workspace (workspace * 1024L); + return true; +} + +static bool +parse_setting (struct lexer *lexer) +{ + struct setting { - char str[FMT_STRING_LEN_MAX + 1]; - msg (SE, _("%s requires numeric output format as an argument. " - "Specified format %s is of type string."), - "FORMAT", - fmt_to_string (&fmt, str)); - return 0; - } + const char *name; + bool (*function) (struct lexer *); + }; + const struct setting settings[] = { + { "BASETEXTDIRECTION", parse_BASETEXTDIRECTION }, + { "BLANKS", parse_BLANKS }, + { "BLOCK", parse_BLOCK }, + { "BOX", parse_BOX }, + { "CACHE", parse_CACHE }, + { "CCA", parse_CCA }, + { "CCB", parse_CCB }, + { "CCC", parse_CCC }, + { "CCD", parse_CCD }, + { "CCE", parse_CCE }, + { "CELLSBREAK", parse_CELLSBREAK }, + { "CMPTRANS", parse_CMPTRANS }, + { "COMPRESSION", parse_COMPRESSION }, + { "CTEMPLATE", parse_CTEMPLATE }, + { "DECIMAL", parse_DECIMAL }, + { "EPOCH", parse_EPOCH }, + { "ERRORS", parse_ERRORS }, + { "FORMAT", parse_FORMAT }, + { "FUZZBITS", parse_FUZZBITS }, + { "HEADER", parse_HEADER }, + { "INCLUDE", parse_INCLUDE }, + { "JOURNAL", parse_JOURNAL }, + { "LENGTH", parse_LENGTH }, + { "LOCALE", parse_LOCALE }, + { "MESSAGES", parse_MESSAGES }, + { "MEXPAND", parse_MEXPAND }, + { "MITERATE", parse_MITERATE }, + { "MNEST", parse_MNEST }, + { "MPRINT", parse_MPRINT }, + { "MXERRS", parse_MXERRS }, + { "MXLOOPS", parse_MXLOOPS }, + { "MXWARNS", parse_MXWARNS }, + { "PRINTBACK", parse_PRINTBACK }, + { "RESULTS", parse_RESULTS }, + { "RIB", parse_RIB }, + { "RRB", parse_RRB }, + { "SAFER", parse_SAFER }, + { "SCOMPRESSION", parse_SCOMPRESSION }, + { "SEED", parse_SEED }, + { "SMALL", parse_SMALL }, + { "TNUMBERS", parse_TNUMBERS }, + { "TVARS", parse_TVARS }, + { "TLOOK", parse_TLOOK }, + { "UNDEFINED", parse_UNDEFINED }, + { "WIB", parse_WIB }, + { "WRB", parse_WRB }, + { "WIDTH", parse_WIDTH }, + { "WORKSPACE", parse_WORKSPACE }, + }; + enum { N_SETTINGS = sizeof settings / sizeof *settings }; - settings_set_format (&fmt); - return 1; + for (size_t i = 0; i < N_SETTINGS; i++) + if (match_subcommand (lexer, settings[i].name)) + return settings[i].function (lexer); + + lex_error (lexer, NULL); + return false; } -static int -stc_custom_journal (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) +int +cmd_set (struct lexer *lexer, struct dataset *ds UNUSED) { - lex_match (lexer, T_EQUALS); - if (lex_match_id (lexer, "ON") || lex_match_id (lexer, "YES")) - journal_enable (); - else if (lex_match_id (lexer, "OFF") || lex_match_id (lexer, "NO")) - journal_disable (); - else if (lex_is_string (lexer) || lex_token (lexer) == T_ID) + for (;;) { - char *filename = utf8_to_filename (lex_tokcstr (lexer)); - journal_set_file_name (filename); - free (filename); + lex_match (lexer, T_SLASH); + if (lex_token (lexer) == T_ENDCMD) + break; - lex_get (lexer); - } - else - { - lex_error (lexer, NULL); - return 0; + if (!parse_setting (lexer)) + return CMD_FAILURE; } - return 1; -} -static int -stc_custom_log (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) -{ - return stc_custom_journal (lexer, ds, cmd, aux); + return CMD_SUCCESS; } static char * -- 2.30.2