/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012 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 "libpspp/compiler.h"
#include "libpspp/i18n.h"
#include "libpspp/integer-format.h"
-#include "libpspp/legacy-encoding.h"
#include "libpspp/misc.h"
#include "libpspp/str.h"
#include "settings.h"
/* Information about parsing one data field. */
struct data_in
{
+ const struct fmt_settings *settings;
+
struct substring input; /* Source. */
enum fmt_type format; /* Input 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()). */
+ correct encoding for OUTPUT (normally obtained via dict_get_encoding()).
+
+ If successful NULL is the return value. Otherwise a string describing
+ the problem is returned. The caller must free this string.
+ */
char *
data_in (struct substring input, const char *input_encoding,
- enum fmt_type format,
+ enum fmt_type format, const struct fmt_settings *settings,
union value *output, int width, const char *output_encoding)
{
static data_in_parser_func *const handlers[FMT_NUMBER_OF_FORMATS] =
assert ((width != 0) == fmt_is_string (format));
+ i.settings = settings;
+
i.format = format;
i.output = output;
}
cat = fmt_get_category (format);
- if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL
+ if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL | FMT_CAT_CUSTOM
| FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))
{
/* We're going to parse these into numbers. For this purpose we want to
bool
data_in_msg (struct substring input, const char *input_encoding,
- enum fmt_type format,
+ enum fmt_type format, const struct fmt_settings *settings,
union value *output, int width, const char *output_encoding)
{
- char *error = data_in (input, input_encoding, format,
+ char *error = data_in (input, input_encoding, format, settings,
output, width, output_encoding);
if (error != NULL)
{
- msg (SW,_("Data is not valid as format %s: %s"),
+ msg (SW, _("Data is not valid as format %s: %s"),
fmt_name (format), error);
free (error);
return false;
}
static bool
-number_has_implied_decimals (const char *s, enum fmt_type type)
+number_has_implied_decimals (const struct fmt_settings *settings,
+ const char *s, enum fmt_type type)
{
- int decimal = settings_get_style (type)->decimal;
+ int decimal = fmt_settings_get_style (settings, type)->decimal;
bool got_digit = false;
for (;;)
{
static bool
has_implied_decimals (struct substring input, const char *input_encoding,
- enum fmt_type format)
+ enum fmt_type format,
+ const struct fmt_settings *settings)
{
bool retval;
char *s;
ss_data (input), ss_length (input));
retval = (format == FMT_Z
? strchr (s, '.') == NULL
- : number_has_implied_decimals (s, format));
+ : number_has_implied_decimals (settings, s, format));
free (s);
return retval;
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)
+ enum fmt_type format, int d,
+ const struct fmt_settings *settings,
+ union value *output)
{
if (d > 0 && output->f != SYSMIS
- && has_implied_decimals (input, input_encoding, format))
+ && has_implied_decimals (input, input_encoding, format, settings))
output->f /= pow (10., d);
}
\f
static char *
parse_number (struct data_in *i)
{
- const struct fmt_number_style *style =
- settings_get_style (i->format);
+ const struct fmt_number_style *style = fmt_settings_get_style (
+ i->settings,
+ fmt_get_category (i->format) == FMT_CAT_CUSTOM ? FMT_F : i->format);
struct string tmp;
- bool explicit_decimals = false;
int save_errno;
char *tail;
- if (fmt_get_category (i->format) == FMT_CAT_CUSTOM)
- {
- style = settings_get_style (FMT_F);
- }
-
/* Trim spaces and check for missing value representation. */
if (trim_spaces_and_check_missing (i))
return NULL;
ds_extend (&tmp, 64);
/* Prefix character may precede sign. */
- if (!ss_is_empty (style->prefix))
+ if (style->prefix.s[0] != '\0')
{
- ss_match_byte (&i->input, ss_first (style->prefix));
+ ss_match_byte (&i->input, style->prefix.s[0]);
ss_ltrim (&i->input, ss_cstr (CC_SPACES));
}
}
/* Prefix character may follow sign. */
- if (!ss_is_empty (style->prefix))
+ if (style->prefix.s[0] != '\0')
{
- ss_match_byte (&i->input, ss_first (style->prefix));
+ ss_match_byte (&i->input, style->prefix.s[0]);
ss_ltrim (&i->input, ss_cstr (CC_SPACES));
}
/* Decimal point and following digits. */
if (ss_match_byte (&i->input, style->decimal))
{
- explicit_decimals = true;
ds_put_byte (&tmp, '.');
while (c_isdigit (ss_first (i->input)))
ds_put_byte (&tmp, ss_get_byte (&i->input));
&& !ss_is_empty (i->input)
&& strchr ("eEdD-+", ss_first (i->input)))
{
- explicit_decimals = true;
ds_put_byte (&tmp, 'e');
if (strchr ("eEdD", ss_first (i->input)))
}
/* Suffix character. */
- if (!ss_is_empty (style->suffix))
- ss_match_byte (&i->input, ss_first (style->suffix));
+ if (style->suffix.s[0] != '\0')
+ ss_match_byte (&i->input, style->suffix.s[0]);
if (!ss_is_empty (i->input))
{
{
/* This is equivalent to buf_copy_rpad, except that we posibly
do a character set recoding in the middle. */
- uint8_t *dst = value_str_rw (i->output, i->width);
+ uint8_t *dst = i->output->s;
size_t dst_size = i->width;
const char *src = ss_data (i->input);
size_t src_size = ss_length (i->input);
static char *
parse_AHEX (struct data_in *i)
{
- uint8_t *s = value_str_rw (i->output, i->width);
+ uint8_t *s = i->output->s;
size_t j;
for (j = 0; ; j++)
return xasprintf (_("Day (%ld) must be between 1 and 31."), *day);
}
-/* Parses an integer from the beginning of I.
- Adds SECONDS_PER_UNIT times the absolute value of the integer
- to *TIME.
- If *TIME_SIGN is SIGN_NO_TIME, allows a sign to precede the
- time and sets *TIME_SIGN. Otherwise, does not allow a sign.
- Returns true if successful, false if no integer was present. */
-static char *
-parse_time_units (struct data_in *i, double seconds_per_unit,
- enum time_sign *time_sign, double *time)
-
+/* If *TIME_SIGN is SIGN_NO_TIME, allows a sign to precede the
+ time and sets *TIME_SIGN. Otherwise, does not allow a sign. */
+static void
+parse_time_sign (struct data_in *i, enum time_sign *time_sign)
{
- char *error;
- long units;
-
if (*time_sign == SIGN_NO_TIME)
{
if (ss_match_byte (&i->input, '-'))
*time_sign = SIGN_POSITIVE;
}
}
+}
+
+/* Parses an integer from the beginning of I.
+ Adds SECONDS_PER_UNIT times the absolute value of the integer
+ to *TIME.
+ Returns true if successful, false if no integer was present. */
+static char *
+parse_time_units (struct data_in *i, double seconds_per_unit, double *time)
+
+{
+ char *error;
+ long units;
+
error = parse_int (i, &units, SIZE_MAX);
if (error != NULL)
return error;
if (*year >= 0 && *year <= 99)
{
- int epoch = settings_get_epoch ();
+ int epoch = fmt_settings_get_epoch (i->settings);
int epoch_century = ROUND_DOWN (epoch, 100);
int epoch_offset = epoch - epoch_century;
if (*year >= epoch_offset)
else
*year += epoch_century + 100;
}
- if (*year >= 1582 || *year <= 19999)
+ if (*year >= 1582 && *year <= 19999)
return NULL;
return xasprintf (_("Year (%ld) must be between 1582 and 19999."), *year);
error = parse_int (i, &minute, SIZE_MAX);
if (error != NULL)
return error;
- if (minute < 0 || minute > 59)
+ if (i->format != FMT_MTIME && (minute < 0 || minute > 59))
return xasprintf (_("Minute (%ld) must be between 0 and 59."), minute);
*time += 60. * minute;
cp = buf;
while (c_isdigit (ss_first (i->input)))
*cp++ = ss_get_byte (&i->input);
- if (ss_match_byte (&i->input, settings_get_decimal_char (FMT_F)))
+ if (ss_match_byte (&i->input, i->settings->decimal))
*cp++ = '.';
while (c_isdigit (ss_first (i->input)))
*cp++ = ss_get_byte (&i->input);
*cp = '\0';
- *time += strtod (buf, NULL);
+ *time += c_strtod (buf, NULL);
return NULL;
}
static char *
parse_WKDAY (struct data_in *i)
{
- long weekday;
+ long weekday = 0;
char *error;
if (trim_spaces_and_check_missing (i))
}
/* Parses DATE, ADATE, EDATE, JDATE, SDATE, QYR, MOYR, KWYR,
- DATETIME, TIME and DTIME formats. */
+ DATETIME, YMDHMS, MTIME, TIME, and DTIME formats. */
static char *
parse_date (struct data_in *i)
{
double time = 0, date = 0;
enum time_sign time_sign = SIGN_NO_TIME;
- const char *template = fmt_date_template (i->format);
+ const char *template = fmt_date_template (i->format, 0);
size_t template_width = strlen (template);
char *error;
error = parse_week (i, &yday);
break;
case 'D':
- error = parse_time_units (i, 60. * 60. * 24., &time_sign, &time);
+ parse_time_sign (i, &time_sign);
+ error = parse_time_units (i, 60. * 60. * 24., &time);
break;
case 'H':
- error = parse_time_units (i, 60. * 60., &time_sign, &time);
+ parse_time_sign (i, &time_sign);
+ error = parse_time_units (i, 60. * 60., &time);
break;
case 'M':
+ if (i->format == FMT_MTIME)
+ parse_time_sign (i, &time_sign);
error = parse_minute_second (i, &time);
break;
case '-':
case '/':
case '.':
- case 'X':
error = parse_date_delimiter (i);
break;
case ':':
error = parse_time_delimiter (i);
+ break;
case ' ':
- parse_spaces (i);
- error = NULL;
+ if (i->format != FMT_MOYR)
+ {
+ parse_spaces (i);
+ error = NULL;
+ }
+ else
+ error = parse_date_delimiter (i);
break;
default:
assert (count == 1);
char *error;
double ofs;
- ofs = calendar_gregorian_to_offset (year, month, day, &error);
+ ofs = calendar_gregorian_to_offset (
+ year, month, day, settings_get_fmt_settings (), &error);
if (ofs == SYSMIS)
return error;
date = (yday - 1 + ofs) * 60. * 60. * 24.;
default_result (struct data_in *i)
{
if (fmt_is_string (i->format))
- memset (value_str_rw (i->output, i->width), ' ', i->width);
+ memset (i->output->s, ' ', i->width);
else
i->output->f = settings_get_blanks ();
}