From: Ben Pfaff Date: Tue, 7 Mar 2023 01:00:13 +0000 (-0800) Subject: DATA LIST: Fix crash with a bare / at the end of the command. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d937975bed157a254ff16ae5cd30067a9638bd77;p=pspp DATA LIST: Fix crash with a bare / at the end of the command. Embarrassingly, this was in an example from the manual. Reported by knassen@chartermi.net. --- diff --git a/doc/data-io.texi b/doc/data-io.texi index 9f5b8dfbee..a325dde385 100644 --- a/doc/data-io.texi +++ b/doc/data-io.texi @@ -394,6 +394,7 @@ applies to later FORTRAN and columnar specifiers. @enumerate @item +@c Update the corresponding test in tests/language/commands/data-list.at if you change this. @example DATA LIST TABLE /NAME 1-10 (A) INFO1 TO INFO3 12-17 (1). @@ -435,8 +436,9 @@ The @code{TABLE} keyword causes @pspp{} to print out a table describing the four variables defined. @item +@c Update the corresponding test in tests/language/commands/data-list.at if you change this. @example -DAT LIS FIL="survey.dat" +DATA LIST FILE="survey.dat" /ID 1-5 NAME 7-36 (A) SURNAME 38-67 (A) MINITIAL 69 (A) /Q01 TO Q50 7-56 /. @@ -470,9 +472,6 @@ record. Cases are separated by a blank record. Data is read from file @file{survey.dat} in the current directory. - -This example shows keywords abbreviated to their first 3 letters. - @end enumerate @node DATA LIST FREE diff --git a/src/language/commands/data-list.c b/src/language/commands/data-list.c index 35f153e4f8..d6a5d0fd55 100644 --- a/src/language/commands/data-list.c +++ b/src/language/commands/data-list.c @@ -341,12 +341,36 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, int record = 0; int column = 1; - do + int start = lex_ofs (lexer); + while (lex_token (lexer) != T_ENDCMD) { - /* Parse everything. */ - int records_start = lex_ofs (lexer); - if (!parse_record_placement (lexer, &record, &column)) - return false; + if (lex_match (lexer, T_SLASH)) + { + int records_start = lex_ofs (lexer) - 1; + if (lex_is_number (lexer)) + { + if (!lex_force_int_range (lexer, NULL, record + 1, INT_MAX)) + return false; + record = lex_integer (lexer); + lex_get (lexer); + } + else + record++; + column = 1; + + if (max_records && record > max_records) + { + lex_ofs_error (lexer, records_start, lex_ofs (lexer) - 1, + _("Cannot advance to record %d when " + "RECORDS=%d is specified."), + record, data_parser_get_records (parser)); + return false; + } + if (record > data_parser_get_records (parser)) + data_parser_set_records (parser, record); + + continue; + } int vars_start = lex_ofs (lexer); char **names; @@ -411,7 +435,7 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, if (max_records && record > max_records) { - lex_ofs_error (lexer, records_start, vars_end, + lex_ofs_error (lexer, vars_start, placements_end, _("Cannot place variable %s on record %d when " "RECORDS=%d is specified."), var_get_name (v), record, @@ -427,7 +451,14 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, } assert (name_idx == n_names); } - while (lex_token (lexer) != T_ENDCMD); + + if (!data_parser_any_fields (parser)) + { + lex_ofs_error (lexer, start, lex_ofs (lexer) - 1, + _("No fields were specified. " + "At least one is required.")); + return false; + } return true; } diff --git a/tests/language/commands/data-list.at b/tests/language/commands/data-list.at index 165b4df722..052481f1c5 100644 --- a/tests/language/commands/data-list.at +++ b/tests/language/commands/data-list.at @@ -446,7 +446,9 @@ INPUT PROGRAM. DATA LIST FIXED/y 2 (a). DATA LIST FIXED/y 3-4 (a). END INPUT PROGRAM. -DATA LIST FIXED RECORDS=1/2 x 1-2. +DATA LIST FIXED RECORDS=1/x y(F2/F3). +DATA LIST FIXED RECORDS=1//. +DATA LIST FIXED RECORDS=1/. ]) AT_CHECK([pspp --testing-mode -O format=csv insert.sps], [1], [dnl @@ -574,9 +576,120 @@ y,1,2-2,A1 39 | DATA LIST FIXED/y 3-4 (a). | ^~~~~~~~~" -"data-list.sps:41.26-41.29: error: DATA LIST: Cannot place variable x on record 2 when RECORDS=1 is specified. - 41 | DATA LIST FIXED RECORDS=1/2 x 1-2. - | ^~~~" +"data-list.sps:41.27-41.36: error: DATA LIST: Cannot place variable y on record 2 when RECORDS=1 is specified. + 41 | DATA LIST FIXED RECORDS=1/x y(F2/F3). + | ^~~~~~~~~~" + +"data-list.sps:42.27: error: DATA LIST: Cannot advance to record 2 when RECORDS=1 is specified. + 42 | DATA LIST FIXED RECORDS=1//. + | ^" + +"data-list.sps:43.26: error: DATA LIST: No fields were specified. At least one is required. + 43 | DATA LIST FIXED RECORDS=1/. + | ^" +]) +AT_CLEANUP + +AT_SETUP([DATA LIST FIXED manual example 1]) +AT_DATA([data-list.sps], [dnl +DATA LIST TABLE /NAME 1-10 (A) INFO1 TO INFO3 12-17 (1). + +BEGIN DATA. +John Smith 102311 +Bob Arnold 122015 +Bill Yates 918 6 +END DATA. + +LIST. +]) +AT_CHECK([pspp data-list.sps -O box=unicode], [0], [dnl + Reading 1 record from INLINE. +╭────────┬──────┬───────┬──────╮ +│Variable│Record│Columns│Format│ +├────────┼──────┼───────┼──────┤ +│NAME │ 1│1-10 │A10 │ +│INFO1 │ 1│12-13 │F2.1 │ +│INFO2 │ 1│14-15 │F2.1 │ +│INFO3 │ 1│16-17 │F2.1 │ +╰────────┴──────┴───────┴──────╯ + + Data List +╭──────────┬─────┬─────┬─────╮ +│ NAME │INFO1│INFO2│INFO3│ +├──────────┼─────┼─────┼─────┤ +│John Smith│ 1.0│ 2.3│ 1.1│ +│Bob Arnold│ 1.2│ 2.0│ 1.5│ +│Bill Yates│ .9│ 1.8│ .6│ +╰──────────┴─────┴─────┴─────╯ ]) +AT_CLEANUP +AT_SETUP([DATA LIST FIXED manual example 2]) +AT_DATA([data-list.sps], [dnl +DATA LIST + /ID 1-5 NAME 7-36 (A) SURNAME 38-67 (A) MINITIAL 69 (A) + /Q01 TO Q50 7-56 + /. +]) +AT_CHECK([pspp data-list.sps -O box=unicode], [0], [dnl + Reading 3 records from INLINE. +╭────────┬──────┬───────┬──────╮ +│Variable│Record│Columns│Format│ +├────────┼──────┼───────┼──────┤ +│ID │ 1│1-5 │F5.0 │ +│NAME │ 1│7-36 │A30 │ +│SURNAME │ 1│38-67 │A30 │ +│MINITIAL│ 1│69-69 │A1 │ +│Q01 │ 2│7-7 │F1.0 │ +│Q02 │ 2│8-8 │F1.0 │ +│Q03 │ 2│9-9 │F1.0 │ +│Q04 │ 2│10-10 │F1.0 │ +│Q05 │ 2│11-11 │F1.0 │ +│Q06 │ 2│12-12 │F1.0 │ +│Q07 │ 2│13-13 │F1.0 │ +│Q08 │ 2│14-14 │F1.0 │ +│Q09 │ 2│15-15 │F1.0 │ +│Q10 │ 2│16-16 │F1.0 │ +│Q11 │ 2│17-17 │F1.0 │ +│Q12 │ 2│18-18 │F1.0 │ +│Q13 │ 2│19-19 │F1.0 │ +│Q14 │ 2│20-20 │F1.0 │ +│Q15 │ 2│21-21 │F1.0 │ +│Q16 │ 2│22-22 │F1.0 │ +│Q17 │ 2│23-23 │F1.0 │ +│Q18 │ 2│24-24 │F1.0 │ +│Q19 │ 2│25-25 │F1.0 │ +│Q20 │ 2│26-26 │F1.0 │ +│Q21 │ 2│27-27 │F1.0 │ +│Q22 │ 2│28-28 │F1.0 │ +│Q23 │ 2│29-29 │F1.0 │ +│Q24 │ 2│30-30 │F1.0 │ +│Q25 │ 2│31-31 │F1.0 │ +│Q26 │ 2│32-32 │F1.0 │ +│Q27 │ 2│33-33 │F1.0 │ +│Q28 │ 2│34-34 │F1.0 │ +│Q29 │ 2│35-35 │F1.0 │ +│Q30 │ 2│36-36 │F1.0 │ +│Q31 │ 2│37-37 │F1.0 │ +│Q32 │ 2│38-38 │F1.0 │ +│Q33 │ 2│39-39 │F1.0 │ +│Q34 │ 2│40-40 │F1.0 │ +│Q35 │ 2│41-41 │F1.0 │ +│Q36 │ 2│42-42 │F1.0 │ +│Q37 │ 2│43-43 │F1.0 │ +│Q38 │ 2│44-44 │F1.0 │ +│Q39 │ 2│45-45 │F1.0 │ +│Q40 │ 2│46-46 │F1.0 │ +│Q41 │ 2│47-47 │F1.0 │ +│Q42 │ 2│48-48 │F1.0 │ +│Q43 │ 2│49-49 │F1.0 │ +│Q44 │ 2│50-50 │F1.0 │ +│Q45 │ 2│51-51 │F1.0 │ +│Q46 │ 2│52-52 │F1.0 │ +│Q47 │ 2│53-53 │F1.0 │ +│Q48 │ 2│54-54 │F1.0 │ +│Q49 │ 2│55-55 │F1.0 │ +│Q50 │ 2│56-56 │F1.0 │ +╰────────┴──────┴───────┴──────╯ +]) AT_CLEANUP