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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
#include <config.h>
-#include <assert.h>
+#include "error.h"
#include <stdlib.h>
#include "command.h"
+#include "data-in.h"
#include "error.h"
#include "lexer.h"
#include "magic.h"
#include "str.h"
#include "var.h"
-#undef DEBUGGING
-/*#define DEBUGGING 1*/
-#include "debug-print.h"
-
-#if DEBUGGING
-static void debug_print ();
-#endif
-
-/* Variables on MIS VAL. */
-static struct variable **v;
-static int nv;
-
-/* Type of the variables on MIS VAL. */
-static int type;
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
-/* Width of string variables on MIS VAL. */
-static size_t width;
-
-/* Items to fill-in var structs with. */
-static int miss_type;
-static union value missing[3];
+#include "debug-print.h"
-static int parse_varnames (void);
-static int parse_numeric (void);
-static int parse_alpha (void);
+static bool parse_number (double *, const struct fmt_spec *);
int
cmd_missing_values (void)
{
- int i;
+ struct variable **v;
+ int nv;
+
+ int retval = CMD_PART_SUCCESS_MAYBE;
+ bool deferred_errors = false;
- lex_match_id ("MISSING");
- lex_match_id ("VALUES");
while (token != '.')
{
- if (!parse_varnames ())
- goto fail;
+ int i;
+
- if (token != ')')
- {
- if ((type == NUMERIC && !parse_numeric ())
- || (type == ALPHA && !parse_alpha ()))
- goto fail;
- }
- else
- miss_type = MISSING_NONE;
+ if (!parse_variables (default_dict, &v, &nv, PV_NONE))
+ goto done;
- if (!lex_match (')'))
- {
- msg (SE, _("`)' expected after value specification."));
- goto fail;
- }
+ if (!lex_match ('('))
+ {
+ lex_error (_("expecting `('"));
+ goto done;
+ }
for (i = 0; i < nv; i++)
- {
- v[i]->miss_type = miss_type;
- memcpy (v[i]->missing, missing, sizeof v[i]->missing);
- }
+ mv_init (&v[i]->miss, v[i]->width);
+
+ if (!lex_match (')'))
+ {
+ struct missing_values mv;
+
+ for (i = 0; i < nv; i++)
+ if (v[i]->type != v[0]->type)
+ {
+ const struct variable *n = v[0]->type == NUMERIC ? v[0] : v[i];
+ const struct variable *s = v[0]->type == NUMERIC ? v[i] : v[0];
+ msg (SE, _("Cannot mix numeric variables (e.g. %s) and "
+ "string variables (e.g. %s) within a single list."),
+ n->name, s->name);
+ goto done;
+ }
+
+ if (v[0]->type == NUMERIC)
+ {
+ mv_init (&mv, 0);
+ while (!lex_match (')'))
+ {
+ double x;
+
+ if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
+ x = LOWEST;
+ else if (!parse_number (&x, &v[0]->print))
+ goto done;
+
+ if (lex_match_id ("THRU"))
+ {
+ double y;
+
+ if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
+ y = HIGHEST;
+ else if (!parse_number (&y, &v[0]->print))
+ goto done;
+
+ if (x == LOWEST && y == HIGHEST)
+ {
+ msg (SE, _("LO THRU HI is an invalid range."));
+ deferred_errors = true;
+ }
+ else if (!mv_add_num_range (&mv, x, y))
+ deferred_errors = true;
+ }
+ else
+ {
+ if (x == LOWEST)
+ {
+ msg (SE, _("LO or LOWEST must be part of a range."));
+ deferred_errors = true;
+ }
+ else if (!mv_add_num (&mv, x))
+ deferred_errors = true;
+ }
+
+ lex_match (',');
+ }
+ }
+ else
+ {
+ mv_init (&mv, MAX_SHORT_STRING);
+ while (!lex_match (')'))
+ {
+ if (!lex_force_string ())
+ {
+ deferred_errors = true;
+ break;
+ }
+
+ if (ds_length (&tokstr) > MAX_SHORT_STRING)
+ {
+ ds_truncate (&tokstr, MAX_SHORT_STRING);
+ msg (SE, _("Truncating missing value to short string "
+ "length (%d characters)."),
+ MAX_SHORT_STRING);
+ }
+ else
+ ds_rpad (&tokstr, MAX_SHORT_STRING, ' ');
+
+ if (!mv_add_str (&mv, ds_data (&tokstr)))
+ deferred_errors = true;
+
+ lex_get ();
+ lex_match (',');
+ }
+ }
+
+ for (i = 0; i < nv; i++)
+ {
+ if (!mv_is_resizable (&mv, v[i]->width))
+ {
+ msg (SE, _("Missing values provided are too long to assign "
+ "to variable of width %d."),
+ v[i]->width);
+ deferred_errors = true;
+ }
+ else
+ {
+ mv_copy (&v[i]->miss, &mv);
+ mv_resize (&v[i]->miss, v[i]->width);
+ }
+ }
+ }
lex_match ('/');
free (v);
+ v = NULL;
}
-
-#if DEBUGGING
- debug_print ();
-#endif
-
- return lex_end_of_command ();
-
-fail:
+ retval = lex_end_of_command ();
+
+ done:
free (v);
- return CMD_PART_SUCCESS_MAYBE;
+ if (deferred_errors)
+ retval = CMD_PART_SUCCESS_MAYBE;
+ return retval;
}
-static int
-parse_varnames (void)
+static bool
+parse_number (double *x, const struct fmt_spec *f)
{
- int i;
-
- if (!parse_variables (NULL, &v, &nv, PV_SAME_TYPE))
- return 0;
- if (!lex_match ('('))
+ if (lex_is_number ())
{
- msg (SE, _("`(' expected after variable name%s."), nv > 1 ? "s" : "");
- return 0;
- }
-
- type = v[0]->type;
- if (type == NUMERIC)
- return 1;
-
- width = v[0]->width;
- for (i = 1; i < nv; i++)
- if (v[i]->type == ALPHA && v[i]->nv != 1)
- {
- msg (SE, _("Long string value specified."));
- return 0;
- }
- else if (v[i]->type == ALPHA && (int) width != v[i]->width)
- {
- msg (SE, _("Short strings must be of equal width."));
- return 0;
- }
-
- return 1;
-}
-
-/* Number or range? */
-enum
- {
- MV_NOR_NOTHING, /* Empty. */
- MV_NOR_NUMBER, /* Single number. */
- MV_NOR_RANGE /* Range. */
- };
-
-/* A single value or a range. */
-struct num_or_range
- {
- int type; /* One of NOR_*. */
- double d[2]; /* d[0]=lower bound or value, d[1]=upper bound. */
- };
-
-/* Parses something of the form <num>, or LO[WEST] THRU <num>, or
- <num> THRU HI[GHEST], or <num> THRU <num>, and sets the appropriate
- members of NOR. Returns success. */
-static int
-parse_num_or_range (struct num_or_range * nor)
-{
- if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
- {
- nor->type = MV_NOR_RANGE;
- if (!lex_force_match_id ("THRU"))
- return 0;
- if (!lex_force_num ())
- return 0;
- nor->d[0] = LOWEST;
- nor->d[1] = tokval;
- }
- else if (token == T_NUM)
- {
- nor->d[0] = tokval;
+ *x = lex_number ();
lex_get ();
-
- if (lex_match_id ("THRU"))
- {
- nor->type = MV_NOR_RANGE;
- if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
- nor->d[1] = HIGHEST;
- else
- {
- if (!lex_force_num ())
- return 0;
- nor->d[1] = tokval;
- lex_get ();
-
- if (nor->d[0] > nor->d[1])
- {
- msg (SE, _("Range %g THRU %g is not valid because %g is "
- "greater than %g."),
- nor->d[0], nor->d[1], nor->d[0], nor->d[1]);
- return 0;
- }
- }
- }
- else
- nor->type = MV_NOR_NUMBER;
- }
- else
- return -1;
-
- return 1;
-}
-
-/* Parses a set of numeric missing values and stores them into
- `missing[]' and `miss_type' global variables. */
-static int
-parse_numeric (void)
-{
- struct num_or_range set[3];
- int r;
-
- set[1].type = set[2].type = MV_NOR_NOTHING;
-
- /* Get first number or range. */
- r = parse_num_or_range (&set[0]);
- if (r < 1)
- {
- if (r == -1)
- msg (SE, _("Number or range expected."));
- return 0;
+ return true;
}
-
- /* Get second and third optional number or range. */
- lex_match (',');
- r = parse_num_or_range (&set[1]);
- if (r == 1)
- {
- lex_match (',');
- r = parse_num_or_range (&set[2]);
- }
- if (r == 0)
- return 0;
-
- /* Force range, if present, into set[0]. */
- if (set[1].type == MV_NOR_RANGE)
- {
- struct num_or_range t = set[1];
- set[1] = set[0];
- set[0] = t;
- }
- if (set[2].type == MV_NOR_RANGE)
- {
- struct num_or_range t = set[2];
- set[2] = set[0];
- set[0] = t;
- }
-
- /* Ensure there's not more than one range, or one range
- plus one value. */
- if (set[1].type == MV_NOR_RANGE || set[2].type == MV_NOR_RANGE)
- {
- msg (SE, _("At most one range can exist in the missing values "
- "for any one variable."));
- return 0;
- }
- if (set[0].type == MV_NOR_RANGE && set[2].type != MV_NOR_NOTHING)
- {
- msg (SE, _("At most one individual value can be missing along "
- "with one range."));
- return 0;
- }
-
- /* Set missing[] from set[]. */
- if (set[0].type == MV_NOR_RANGE)
- {
- int x = 0;
-
- if (set[0].d[0] == LOWEST)
- {
- miss_type = MISSING_LOW;
- missing[x++].f = set[0].d[1];
- }
- else if (set[0].d[1] == HIGHEST)
- {
- miss_type = MISSING_HIGH;
- missing[x++].f = set[0].d[0];
- }
- else
- {
- miss_type = MISSING_RANGE;
- missing[x++].f = set[0].d[0];
- missing[x++].f = set[0].d[1];
- }
-
- if (set[1].type == MV_NOR_NUMBER)
- {
- miss_type += 3;
- missing[x].f = set[1].d[0];
- }
- }
- else
- {
- if (set[0].type == MV_NOR_NUMBER)
- {
- miss_type = MISSING_1;
- missing[0].f = set[0].d[0];
- }
- if (set[1].type == MV_NOR_NUMBER)
- {
- miss_type = MISSING_2;
- missing[1].f = set[1].d[0];
- }
- if (set[2].type == MV_NOR_NUMBER)
- {
- miss_type = MISSING_3;
- missing[2].f = set[2].d[0];
- }
- }
-
- return 1;
-}
-
-static int
-parse_alpha (void)
-{
- for (miss_type = 0; token == T_STRING && miss_type < 3; miss_type++)
- {
- if (ds_length (&tokstr) != width)
- {
- msg (SE, _("String is not of proper length."));
- return 0;
- }
- strncpy (missing[miss_type].s, ds_value (&tokstr), MAX_SHORT_STRING);
+ else if (token == T_STRING)
+ {
+ struct data_in di;
+ union value v;
+ di.s = ds_data (&tokstr);
+ di.e = ds_end (&tokstr);
+ di.v = &v;
+ di.flags = 0;
+ di.f1 = 1;
+ di.f2 = ds_length (&tokstr);
+ di.format = *f;
+ data_in (&di);
lex_get ();
- lex_match (',');
+ *x = v.f;
+ return true;
}
- if (miss_type < 1)
+ else
{
- msg (SE, _("String expected."));
- return 0;
+ lex_error (_("expecting number or data string"));
+ return false;
}
-
- return 1;
-}
-
-/* Copy the missing values from variable SRC to variable DEST. */
-void
-copy_missing_values (struct variable *dest, const struct variable *src)
-{
- static const int n_values[MISSING_COUNT] =
- {
- 0, 1, 2, 3, 2, 1, 1, 3, 2, 2,
- };
-
- assert (dest->width == src->width);
- assert (src->miss_type >= 0 && src->miss_type < MISSING_COUNT);
-
- {
- int i;
-
- dest->miss_type = src->miss_type;
- for (i = 0; i < n_values[src->miss_type]; i++)
- if (src->type == NUMERIC)
- dest->missing[i].f = src->missing[i].f;
- else
- memcpy (dest->missing[i].s, src->missing[i].s, src->width);
- }
}
-\f
-/* Debug output. */
-
-#if DEBUGGING
-static void
-debug_print (void)
-{
- int i, j;
-
- puts (_("Missing value:"));
- for (i = 0; i < nvar; i++)
- {
- printf (" %8s: ", var[i]->name);
- if (var[i]->type == ALPHA && var[i]->nv > 1)
- puts (_("(long string variable)"));
- else
- switch (var[i]->miss_type)
- {
- case MISSING_NONE:
- printf (_("(no missing values)\n"));
- break;
- case MISSING_1:
- case MISSING_2:
- case MISSING_3:
- printf ("(MISSING_%d)", var[i]->miss_type);
- for (j = 0; j < var[i]->miss_type; j++)
- if (var[i]->type == ALPHA)
- printf (" \"%.*s\"", var[i]->width, var[i]->missing[j].s);
- else
- printf (" %.2g", var[i]->missing[j].f);
- printf ("\n");
- break;
- case MISSING_RANGE:
- printf ("(MISSING_RANGE) %.2g THRU %.2g\n",
- var[i]->missing[0].f, var[i]->missing[1].f);
- break;
- case MISSING_RANGE_1:
- printf ("(MISSING_RANGE_1) %.2g THRU %.2g, %.2g\n",
- var[i]->missing[0].f, var[i]->missing[1].f,
- var[i]->missing[2].f);
- break;
- default:
- printf (_("(!!!INTERNAL ERROR--%d!!!)\n"), var[i]->miss_type);
- }
- }
-}
-#endif /* DEBUGGING */