/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2009, 2010 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#include <math.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdbool.h>
-#include <limits.h>
#include "calendar.h"
+#include "dictionary.h"
+#include "format.h"
#include "identifier.h"
+#include "libpspp/assertion.h"
+#include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
+#include "libpspp/integer-format.h"
+#include "libpspp/legacy-encoding.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/str.h"
#include "settings.h"
#include "value.h"
-#include "format.h"
-#include "dictionary.h"
-#include <libpspp/assertion.h>
-#include <libpspp/legacy-encoding.h>
-#include <libpspp/i18n.h>
-#include <libpspp/compiler.h>
-#include <libpspp/integer-format.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include "c-ctype.h"
-#include "c-strtod.h"
-#include "minmax.h"
-#include "xalloc.h"
+#include "gl/c-ctype.h"
+#include "gl/c-strtod.h"
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
/* Information about parsing one data field. */
struct data_in
{
- const char *src_enc; /* Encoding of source. */
struct substring input; /* Source. */
enum fmt_type format; /* Input format. */
- int implied_decimals; /* Number of implied decimal places. */
union value *output; /* Destination. */
int width; /* Output width. */
int last_column; /* Last column. */
};
-
-
typedef bool data_in_parser_func (struct data_in *);
#define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
static data_in_parser_func parse_##METHOD;
#include "format.def"
-static void vdata_warning (const struct data_in *, const char *, va_list)
- PRINTF_FORMAT (2, 0);
static void data_warning (const struct data_in *, const char *, ...)
PRINTF_FORMAT (2, 3);
-static void apply_implied_decimals (struct data_in *);
static void default_result (struct data_in *);
static bool trim_spaces_and_check_missing (struct data_in *);
static int hexit_value (int c);
\f
/* Parses the characters in INPUT, which are encoded in the given
- ENCODING, according to FORMAT. Stores the parsed
- representation in OUTPUT, which the caller must have
- initialized with the given WIDTH (0 for a numeric field,
- otherwise the string width).
- Iff FORMAT is a string format, then DICT must be a pointer
- to the dictionary associated with OUTPUT. Otherwise, DICT
- may be null.
-
- If no decimal point is included in a numeric format, then
- IMPLIED_DECIMALS decimal places are implied. Specify 0 if no
- decimal places should be implied.
-
- If FIRST_COLUMN and LAST_COLUMN are nonzero, then they should
- be the 1-based column number of the first and
- one-past-the-last-character in INPUT, for use in error
- messages. (LAST_COLUMN cannot always be calculated from
- FIRST_COLUMN plus the length of the input because of the
- possibility of escaped quotes in strings, etc.) */
+ INPUT_ENCODING, according to FORMAT.
+
+ Stores the parsed representation in OUTPUT, which the caller must have
+ initialized with the given WIDTH (0 for a numeric field, otherwise the
+ string width). If FORMAT is FMT_A, then OUTPUT_ENCODING must specify the
+ correct encoding for OUTPUT (normally obtained via dict_get_encoding()). */
bool
-data_in (struct substring input, const char *encoding,
- enum fmt_type format, int implied_decimals,
- int first_column, int last_column,
- const struct dictionary *dict,
- union value *output, int width)
+data_in (struct substring input, const char *input_encoding,
+ enum fmt_type format, int first_column, int last_column,
+ union value *output, int width, const char *output_encoding)
{
static data_in_parser_func *const handlers[FMT_NUMBER_OF_FORMATS] =
{
struct data_in i;
+ enum fmt_category cat;
+ const char *dest_encoding;
+ char *s;
bool ok;
assert ((width != 0) == fmt_is_string (format));
i.format = format;
- i.implied_decimals = implied_decimals;
i.output = output;
i.width = width;
i.first_column = first_column;
i.last_column = last_column;
- i.src_enc = encoding;
if (ss_is_empty (input))
{
return true;
}
- if (fmt_get_category (format) & ( FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL | FMT_CAT_LEGACY))
+ cat = fmt_get_category (format);
+ if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL
+ | FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))
{
- i.input = input;
+ /* We're going to parse these into numbers. For this purpose we want to
+ deal with them in the local "C" encoding. Any character not in that
+ encoding wouldn't be valid anyhow. */
+ dest_encoding = LEGACY_NATIVE;
+ }
+ else if (cat & (FMT_CAT_BINARY | FMT_CAT_LEGACY))
+ {
+ /* Don't recode these binary formats at all, since they are not text. */
+ dest_encoding = NULL;
}
else
{
- const char *dest_encoding;
- char *s = NULL;
- if ( dict == NULL)
- {
- assert (0 == (fmt_get_category (format) & (FMT_CAT_BINARY | FMT_CAT_STRING)));
- dest_encoding = LEGACY_NATIVE;
- }
+ assert (cat == FMT_CAT_STRING);
+ if (format == FMT_AHEX)
+ {
+ /* We want the hex digits in the local "C" encoding, even though the
+ result may not be in that encoding. */
+ dest_encoding = LEGACY_NATIVE;
+ }
else
- dest_encoding = dict_get_encoding (dict);
+ {
+ /* Use the final output encoding. */
+ dest_encoding = output_encoding;
+ }
+ }
- s = recode_string (dest_encoding, i.src_enc, ss_data (input), ss_length (input));
- ss_alloc_uninit (&i.input, strlen (s));
- memcpy (ss_data (i.input), s, ss_length (input));
- free (s);
+ if (dest_encoding != NULL)
+ {
+ i.input = recode_substring_pool (dest_encoding, input_encoding, input,
+ NULL);
+ s = i.input.string;
+ }
+ else
+ {
+ i.input = input;
+ s = NULL;
}
ok = handlers[i.format] (&i);
if (!ok)
default_result (&i);
+ free (s);
+
return ok;
}
+static bool
+number_has_implied_decimals (const char *s, enum fmt_type type)
+{
+ int decimal = settings_get_style (type)->decimal;
+ bool got_digit = false;
+ for (;;)
+ {
+ switch (*s)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ got_digit = true;
+ break;
+
+ case '+': case '-':
+ if (got_digit)
+ return false;
+ break;
+
+ case 'e': case 'E': case 'd': case 'D':
+ return false;
+
+ case '.': case ',':
+ if (*s == decimal)
+ return false;
+ break;
+
+ case '\0':
+ return true;
+
+ default:
+ break;
+ }
+
+ s++;
+ }
+}
+
+static bool
+has_implied_decimals (struct substring input, const char *input_encoding,
+ enum fmt_type format)
+{
+ bool retval;
+ char *s;
+
+ switch (format)
+ {
+ case FMT_F:
+ case FMT_COMMA:
+ case FMT_DOT:
+ case FMT_DOLLAR:
+ case FMT_PCT:
+ case FMT_E:
+ case FMT_Z:
+ break;
+
+ case FMT_N:
+ case FMT_IB:
+ case FMT_PIB:
+ case FMT_P:
+ case FMT_PK:
+ return true;
+
+ default:
+ return false;
+ }
+
+ s = recode_string (LEGACY_NATIVE, input_encoding,
+ ss_data (input), ss_length (input));
+ retval = (format == FMT_Z
+ ? strchr (s, '.') == NULL
+ : number_has_implied_decimals (s, format));
+ free (s);
+
+ return retval;
+}
+
+/* In some cases, when no decimal point is explicitly included in numeric
+ input, its position is implied by the number of decimal places in the input
+ format. In such a case, this function may be called just after data_in().
+ Its arguments are a subset of that function's arguments plus D, the number
+ of decimal places associated with FORMAT.
+
+ If it is appropriate, this function modifies the numeric value in OUTPUT. */
+void
+data_in_imply_decimals (struct substring input, const char *input_encoding,
+ enum fmt_type format, int d, union value *output)
+{
+ if (d > 0 && output->f != SYSMIS
+ && has_implied_decimals (input, input_encoding, format))
+ output->f /= pow (10., d);
+}
\f
/* Format parsers. */
else
{
errno = save_errno;
- if (!explicit_decimals)
- apply_implied_decimals (i);
}
ds_destroy (&tmp);
i->output->f = i->output->f * 10.0 + (c - '0');
}
- apply_implied_decimals (i);
return true;
}
}
}
else
- {
- errno = save_errno;
- if (!got_dot)
- apply_implied_decimals (i);
- }
+ errno = save_errno;
ds_destroy (&tmp);
return true;
i->output->f = -(double) -value;
}
- apply_implied_decimals (i);
-
return true;
}
i->output->f = integer_get (settings_get_input_integer_format (), ss_data (i->input),
MIN (8, ss_length (i->input)));
- apply_implied_decimals (i);
-
return true;
}
else if (low_nibble == 0xb || low_nibble == 0xd)
i->output->f = -i->output->f;
- apply_implied_decimals (i);
-
return true;
}
i->output->f = (100 * i->output->f) + (10 * high_nibble) + low_nibble;
}
- apply_implied_decimals (i);
-
return true;
}
return false;
}
- if (0 != strcmp (i->src_enc, LEGACY_NATIVE))
- {
- hi = legacy_to_native (i->src_enc, hi);
- lo = legacy_to_native (i->src_enc, lo);
- }
if (!c_isxdigit (hi) || !c_isxdigit (lo))
{
data_warning (i, _("Field must contain only hex digits."));
if (ss_is_empty (i->input))
return true;
- data_warning (i, _("Trailing garbage \"%.*s\" following date."),
+ data_warning (i, _("Trailing garbage `%.*s' following date."),
(int) ss_length (i->input), ss_data (i->input));
return false;
}
\f
/* Date & time formats. */
-/* Helper function for passing to
- calendar_gregorian_to_offset. */
-static void
-calendar_error (void *i_, const char *format, ...)
-{
- struct data_in *i = i_;
- va_list args;
-
- va_start (args, format);
- vdata_warning (i, format, args);
- va_end (args);
-}
-
/* Parses WKDAY format. */
static bool
parse_WKDAY (struct data_in *i)
if (year != INT_MIN)
{
- double ofs = calendar_gregorian_to_offset (year, month, day,
- calendar_error, i);
+ char *error;
+ double ofs;
+
+ ofs = calendar_gregorian_to_offset (year, month, day, &error);
if (ofs == SYSMIS)
- return false;
+ {
+ data_warning (i, "%s", error);
+ free (error);
+ return false;
+ }
date = (yday - 1 + ofs) * 60. * 60. * 24.;
}
else
return true;
}
+
\f
/* Utility functions. */
-/* Outputs FORMAT with the given ARGS as a warning for input
- I. */
+/* Outputs FORMAT with as a warning for input I. */
static void
-vdata_warning (const struct data_in *i, const char *format, va_list args)
+data_warning (const struct data_in *i, const char *format, ...)
{
+ va_list args;
struct msg m;
struct string text;
ds_init_empty (&text);
ds_put_char (&text, '(');
- if (i->first_column != 0)
- {
- if (i->first_column == i->last_column - 1)
- ds_put_format (&text, _("column %d"), i->first_column);
- else
- ds_put_format (&text, _("columns %d-%d"),
- i->first_column, i->last_column - 1);
- ds_put_cstr (&text, ", ");
- }
ds_put_format (&text, _("%s field) "), fmt_name (i->format));
+
+ va_start (args, format);
ds_put_vformat (&text, format, args);
+ va_end (args);
- m.category = MSG_DATA;
- m.severity = MSG_WARNING;
+ m.category = MSG_C_DATA;
+ m.severity = MSG_S_WARNING;
m.text = ds_cstr (&text);
+ m.where.file_name = NULL;
+ m.where.line_number = 0;
+ m.where.first_column = i->first_column;
+ m.where.last_column = i->last_column;
msg_emit (&m);
}
-/* Outputs FORMAT with the given ARGS as a warning for input
- I. */
-static void
-data_warning (const struct data_in *i, const char *format, ...)
-{
- va_list args;
-
- va_start (args, format);
- vdata_warning (i, format, args);
- va_end (args);
-}
-
-/* Apply implied decimal places to output. */
-static void
-apply_implied_decimals (struct data_in *i)
-{
- if (i->implied_decimals > 0)
- i->output->f /= pow (10., i->implied_decimals);
-}
-
/* Sets the default result for I.
For a numeric format, this is the value set on SET BLANKS
(typically system-missing); for a string format, it is all