From: Ben Pfaff Date: Fri, 3 Nov 2006 04:53:51 +0000 (+0000) Subject: Completely rewrite src/data/format.[ch], to achieve better X-Git-Tag: v0.6.0~706 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=3a61659a8fc11c51ad5af02b20f5613dcde50382 Completely rewrite src/data/format.[ch], to achieve better abstraction. Rewrite all references to formats in other files. See patch #5511. --- diff --git a/ChangeLog b/ChangeLog index add97b22..ddf1d6cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Tue Oct 31 19:55:52 2006 Ben Pfaff + + * Smake (GNULIB_MODULES): Add `mempcpy' module. + Tue Oct 31 19:29:05 2006 Ben Pfaff * configure.ac: Drop tests for strchr, strrchr because now we diff --git a/Smake b/Smake index 70385708..7aac5a86 100644 --- a/Smake +++ b/Smake @@ -27,6 +27,7 @@ GNULIB_MODULES = \ memcmp \ memmem \ memmove \ + mempcpy \ memset \ progname \ readlink \ diff --git a/src/data/ChangeLog b/src/data/ChangeLog index 9768a579..aa03bb95 100644 --- a/src/data/ChangeLog +++ b/src/data/ChangeLog @@ -1,3 +1,33 @@ +Tue Oct 31 19:58:27 2006 Ben Pfaff + + * format.c: Completely rewrite, to achieve better abstraction. + Rewrite all references to formats in other files. + + * format.def: Rewrite and reorganize. + + * settings.c: Move everything related to custom currency formats + into format.[ch], changing them in form, so as to group related + code and definitions better. Changed all references to use the + new functions. + (static var decimal) Removed. + (static var grouping) Removed. + (static var cc) Removed. + (get_decimal) Removed. + (set_decimal) Removed. + (get_grouping) Removed. + (set_grouping) Removed. + (get_cc) Removed. + (set_cc) Removed. + + * settings.h: (macro CC_CNT) Removed. + (macro CC_WIDTH) Removed. + (struct custom_currency) Removed. + +Tue Oct 31 19:56:19 2006 Ben Pfaff + + * data-in.c (data_in): Use switch statement instead of table, to + avoid dependence on the order of the FMT_* enums. + Tue Oct 31 19:35:36 2006 Ben Pfaff * data-out.c: (num_to_string) Removed, because it was dead code. diff --git a/src/data/data-in.c b/src/data/data-in.c index c5556a3c..3cf53a28 100644 --- a/src/data/data-in.c +++ b/src/data/data-in.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ #include #include "data-in.h" -#include +#include #include #include #include @@ -50,6 +50,7 @@ vdls_error (const struct data_in *i, const char *format, va_list args) { struct msg m; struct string text; + char format_string[FMT_STRING_LEN_MAX + 1]; if (i->flags & DI_IGNORE_ERROR) return; @@ -59,7 +60,8 @@ vdls_error (const struct data_in *i, const char *format, va_list args) ds_put_format (&text, _("(column %d"), i->f1); else ds_put_format (&text, _("(columns %d-%d"), i->f1, i->f2); - ds_put_format (&text, _(", field type %s) "), fmt_to_string (&i->format)); + ds_put_format (&text, _(", field type %s) "), + fmt_to_string (&i->format, format_string)); ds_put_vformat (&text, format, args); m.category = MSG_DATA; @@ -148,17 +150,9 @@ parse_numeric (struct data_in *i) } else sign = 1; - - if (type != FMT_DOT) - { - decimal = get_decimal(); - grouping = get_grouping(); - } - else - { - decimal = get_grouping(); - grouping = get_decimal(); - } + + decimal = fmt_decimal_char (type); + grouping = fmt_grouping_char (type); i->v->f = SYSMIS; num = 0.0; @@ -1342,10 +1336,8 @@ parse_MONTH (struct data_in *i) static void default_result (struct data_in *i) { - const struct fmt_desc *const fmt = &formats[i->format.type]; - /* Default to SYSMIS or blanks. */ - if (fmt->cat & FCAT_STRING) + if (fmt_is_string (i->format.type)) memset (i->v->s, ' ', i->format.w); else i->v->f = get_blanks(); @@ -1354,9 +1346,9 @@ default_result (struct data_in *i) bool data_in (struct data_in *i) { - const struct fmt_desc *const fmt = &formats[i->format.type]; + bool success; - assert (check_input_specifier (&i->format, 0)); + assert (fmt_check_input (&i->format)); /* Check that we've got a string to work with. */ if (i->e == i->s || i->format.w <= 0) @@ -1368,15 +1360,16 @@ data_in (struct data_in *i) i->f2 = i->f1 + (i->e - i->s) - 1; /* Make sure that the string isn't too long. */ - if (i->format.w > fmt->Imax_w) + if (i->format.w > fmt_max_input_width (i->format.type)) { dls_error (i, _("Field too long (%d characters). Truncated after " - "character %d."), - i->format.w, fmt->Imax_w); - i->format.w = fmt->Imax_w; + "character %d."), + i->format.w, fmt_max_input_width (i->format.type)); + i->format.w = fmt_max_input_width (i->format.type); } - if (fmt->cat & FCAT_BLANKS_SYSMIS) + if (!(fmt_get_category (i->format.type) + & (FMT_CAT_STRING | FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL))) { const char *cp; @@ -1394,33 +1387,102 @@ data_in (struct data_in *i) } } - { - typedef bool (*handler_t) (struct data_in *); - static const handler_t handlers[FMT_NUMBER_OF_FORMATS] = - { - parse_numeric, parse_N, parse_numeric, parse_numeric, - parse_numeric, parse_numeric, parse_numeric, - parse_Z, parse_A, parse_AHEX, parse_IB, parse_P, parse_PIB, - parse_PIBHEX, parse_PK, parse_RB, parse_RBHEX, - NULL, NULL, NULL, NULL, NULL, - parse_DATE, parse_EDATE, parse_SDATE, parse_ADATE, parse_JDATE, - parse_QYR, parse_MOYR, parse_WKYR, - parse_DATETIME, parse_TIME, parse_DTIME, - parse_WKDAY, parse_MONTH, - }; - - handler_t handler; - bool success; - - handler = handlers[i->format.type]; - assert (handler != NULL); - - success = handler (i); - if (!success) - default_result (i); - return success; - } + switch (i->format.type) + { + case FMT_F: + case FMT_COMMA: + case FMT_DOT: + case FMT_DOLLAR: + case FMT_PCT: + case FMT_E: + success = parse_numeric (i); + break; + case FMT_CCA: + case FMT_CCB: + case FMT_CCC: + case FMT_CCD: + case FMT_CCE: + NOT_REACHED (); + case FMT_N: + success = parse_N (i); + break; + case FMT_Z: + success = parse_Z (i); + break; + case FMT_P: + success = parse_P (i); + break; + case FMT_PK: + success = parse_PK (i); + break; + case FMT_IB: + success = parse_IB (i); + break; + case FMT_PIB: + success = parse_PIB (i); + break; + case FMT_PIBHEX: + success = parse_PIBHEX (i); + break; + case FMT_RB: + success = parse_RB (i); + break; + case FMT_RBHEX: + success = parse_RBHEX (i); + break; + case FMT_DATE: + success = parse_DATE (i); + break; + case FMT_ADATE: + success = parse_ADATE (i); + break; + case FMT_EDATE: + success = parse_EDATE (i); + break; + case FMT_JDATE: + success = parse_JDATE (i); + break; + case FMT_SDATE: + success = parse_SDATE (i); + break; + case FMT_QYR: + success = parse_QYR (i); + break; + case FMT_MOYR: + success = parse_MOYR (i); + break; + case FMT_WKYR: + success = parse_WKYR (i); + break; + case FMT_DATETIME: + success = parse_DATETIME (i); + break; + case FMT_TIME: + success = parse_TIME (i); + break; + case FMT_DTIME: + success = parse_DTIME (i); + break; + case FMT_WKDAY: + success = parse_WKDAY (i); + break; + case FMT_MONTH: + success = parse_MONTH (i); + break; + case FMT_A: + success = parse_A (i); + break; + case FMT_AHEX: + success = parse_AHEX (i); + break; + default: + NOT_REACHED (); + } + if (!success) + default_result (i); + + return success; } /* Utility function. */ diff --git a/src/data/data-out.c b/src/data/data-out.c index 49a1dbd6..74f97188 100644 --- a/src/data/data-out.c +++ b/src/data/data-out.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -58,17 +58,17 @@ static string_converter convert_A, convert_AHEX; bool data_out (char *s, const struct fmt_spec *fp, const union value *v) { - int cat = formats[fp->type].cat; int ok; - assert (check_output_specifier (fp, 0)); - if (!(cat & FCAT_STRING)) + assert (fmt_check_output (fp)); + if (fmt_is_numeric (fp->type)) { - /* Numeric formatting. */ + enum fmt_category category = fmt_get_category (fp->type); double number = v->f; /* Handle SYSMIS turning into blanks. */ - if ((cat & FCAT_BLANKS_SYSMIS) && number == SYSMIS) + if (!(category & (FMT_CAT_CUSTOM | FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL)) + && number == SYSMIS) { memset (s, ' ', fp->w); s[fp->w - fp->d - 1] = '.'; @@ -76,7 +76,9 @@ data_out (char *s, const struct fmt_spec *fp, const union value *v) } /* Handle decimal shift. */ - if ((cat & FCAT_SHIFT_DECIMAL) && number != SYSMIS && fp->d) + if ((category & (FMT_CAT_LEGACY | FMT_CAT_BINARY)) + && number != SYSMIS + && fp->d) number *= pow (10.0, fp->d); switch (fp->type) @@ -310,12 +312,11 @@ convert_E (char *dst, const struct fmt_spec *fp, double number) /* The C locale always uses a period `.' as a decimal point. Translate to comma if necessary. */ - if ((get_decimal() == ',' && fp->type != FMT_DOT) - || (get_decimal() == '.' && fp->type == FMT_DOT)) + if (fmt_decimal_char (fp->type) != '.') { char *cp = strchr (buf, '.'); if (cp) - *cp = ','; + *cp = fmt_decimal_char (fp->type); } memcpy (dst, buf, fp->w); @@ -925,7 +926,7 @@ insert_commas (char *dst, const char *src, const struct fmt_spec *fp) if (i % 3 == 0 && n_digits > i && n_items > n_reserved) { n_items--; - *dst++ = fp->type == FMT_COMMA ? get_grouping() : get_decimal(); + *dst++ = fmt_grouping_char (fp->type); } *dst++ = *sp++; } @@ -951,7 +952,7 @@ year4 (int year) static int try_CCx (char *dst, const struct fmt_spec *fp, double number) { - const struct custom_currency *cc = get_cc(fp->type - FMT_CCA); + const struct fmt_number_style *style = fmt_get_style (fp->type); struct fmt_spec f; @@ -961,10 +962,10 @@ try_CCx (char *dst, const struct fmt_spec *fp, double number) /* Determine length available, decimal character for number proper. */ - f.type = cc->decimal == get_decimal () ? FMT_COMMA : FMT_DOT; - f.w = fp->w - strlen (cc->prefix) - strlen (cc->suffix); + f.type = style->decimal == fmt_decimal_char (FMT_COMMA) ? FMT_COMMA : FMT_DOT; + f.w = fp->w - fmt_affix_width (style); if (number < 0) - f.w -= strlen (cc->neg_prefix) + strlen (cc->neg_suffix) - 1; + f.w -= fmt_neg_affix_width (style) - 1; else /* Convert -0 to +0. */ number = fabs (number); @@ -982,8 +983,9 @@ try_CCx (char *dst, const struct fmt_spec *fp, double number) /* Postprocess back into buf. */ cp = buf; if (number < 0) - cp = stpcpy (cp, cc->neg_prefix); - cp = stpcpy (cp, cc->prefix); + cp = mempcpy (cp, ss_data (style->neg_prefix), + ss_length (style->neg_prefix)); + cp = mempcpy (cp, ss_data (style->prefix), ss_length (style->prefix)); { char *bp = buf2; while (*bp == ' ') @@ -996,9 +998,10 @@ try_CCx (char *dst, const struct fmt_spec *fp, double number) memcpy (cp, bp, f.w - (bp - buf2)); cp += f.w - (bp - buf2); } - cp = stpcpy (cp, cc->suffix); + cp = mempcpy (cp, ss_data (style->suffix), ss_length (style->suffix)); if (number < 0) - cp = stpcpy (cp, cc->neg_suffix); + cp = mempcpy (cp, ss_data (style->neg_suffix), + ss_length (style->neg_suffix)); /* Copy into dst. */ assert (cp - buf <= fp->w); diff --git a/src/data/dictionary.c b/src/data/dictionary.c index f7e52e3f..ebe6a5e5 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -287,14 +287,14 @@ dict_create_var (struct dictionary *d, const char *name, int width) mv_init (&v->miss, width); if (v->type == NUMERIC) { - v->print = f8_2; + v->print = fmt_for_output (FMT_F, 8, 2); v->alignment = ALIGN_RIGHT; v->display_width = 8; v->measure = MEASURE_SCALE; } else { - v->print = make_output_format (FMT_A, v->width, 0); + v->print = fmt_for_output (FMT_A, v->width, 0); v->alignment = ALIGN_LEFT; v->display_width = 8; v->measure = MEASURE_NOMINAL; diff --git a/src/data/format.c b/src/data/format.c index 63f536ee..3ce41ee0 100644 --- a/src/data/format.c +++ b/src/data/format.c @@ -18,406 +18,901 @@ 02110-1301, USA. */ #include + #include "format.h" + #include -#include -#include #include + +#include +#include +#include +#include #include +#include #include -#include "identifier.h" #include -#include "variable.h" + +#include "minmax.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -#define DEFFMT(LABEL, NAME, N_ARGS, IMIN_W, IMAX_W, OMIN_W, OMAX_W, CAT, \ - OUTPUT, SPSS_FMT) \ - {NAME, N_ARGS, IMIN_W, IMAX_W, OMIN_W, OMAX_W, CAT, OUTPUT, SPSS_FMT}, -const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS + 1] = -{ -#include "format.def" - {"", -1, -1, -1, -1, -1, 0000, -1, -1}, -}; +static bool is_fmt_type (enum fmt_type); -/* Common formats. */ -const struct fmt_spec f8_2 = {FMT_F, 8, 2}; +static int min_width (enum fmt_type, bool for_input); +static int max_width (enum fmt_type); +static bool valid_width (enum fmt_type, int width, bool for_input); +static int max_decimals (enum fmt_type, int width, bool for_input); -/* Tries to parse NAME as a format type. - If successful, stores the type in *TYPE and returns true. - On failure, returns false. */ -bool -fmt_type_from_string (const char *name, int *type) +static int max_digits_for_bytes (int bytes); + +/* Initialize the format module. */ +void +fmt_init (void) { - int i; + static bool inited = false; + if (!inited) + { + inited = true; + fmt_set_decimal ('.'); + } +} - for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++) - if (!strcasecmp (name, formats[i].name)) - { - *type = i; - return true; - } - return false; +/* Deinitialize the format module. */ +void +fmt_done (void) +{ } -/* Converts F to its string representation (for instance, "F8.2") and - returns a pointer to a static buffer containing that string. - If F has decimals, then they are included in the output - string, even if F's format type does not, so that we can - accurately present incorrect formats to the user. */ -char * -fmt_to_string (const struct fmt_spec *f) +/* Returns an input format specification with type TYPE, width W, + and D decimals. */ +struct fmt_spec +fmt_for_input (enum fmt_type type, int w, int d) { - static char buf[32]; + struct fmt_spec f; + f.type = type; + f.w = w; + f.d = d; + assert (fmt_check_input (&f)); + return f; +} - if (formats[f->type].n_args >= 2 || f->d > 0) - sprintf (buf, "%s%d.%d", formats[f->type].name, f->w, f->d); - else - sprintf (buf, "%s%d", formats[f->type].name, f->w); - return buf; +/* Returns an output format specification with type TYPE, width + W, and D decimals. */ +struct fmt_spec +fmt_for_output (enum fmt_type type, int w, int d) +{ + struct fmt_spec f; + f.type = type; + f.w = w; + f.d = d; + assert (fmt_check_output (&f)); + return f; } -/* Does checks in common betwen check_input_specifier() and - check_output_specifier() and returns true if so. Otherwise, - emits an error message (if EMIT_ERROR is nonzero) and returns - false. */ -static bool -check_common_specifier (const struct fmt_spec *spec, bool emit_error) +/* Returns the output format specifier corresponding to input + format specifier INPUT. */ +struct fmt_spec +fmt_for_output_from_input (const struct fmt_spec *input) { - const struct fmt_desc *f ; - char *str; + struct fmt_spec output; + + assert (fmt_check_input (input)); - assert (spec->type < FMT_NUMBER_OF_FORMATS); - f = &formats[spec->type]; - str = fmt_to_string (spec); + output.type = fmt_input_to_output (input->type); + output.w = input->w; + if (output.w > fmt_max_output_width (output.type)) + output.w = fmt_max_output_width (output.type); + else if (output.w < fmt_min_output_width (output.type)) + output.w = fmt_min_output_width (output.type); + output.d = input->d; - if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2) + switch (input->type) { - if (emit_error) - msg (SE, _("Format %s specifies an odd width %d, but " - "an even width is required."), - str, spec->w); - return false; + case FMT_Z: + output.w++; + if (output.d > 0) + output.w++; + break; + + case FMT_F: + case FMT_COMMA: + case FMT_DOT: + case FMT_DOLLAR: + case FMT_PCT: + { + const struct fmt_number_style *style = fmt_get_style (input->type); + output.w += fmt_affix_width (style); + if (style->grouping != 0 && input->w - input->d >= 3) + output.w += (input->w - input->d - 1) / 3; + if (output.d > 0) + output.w++; + } + break; + + case FMT_N: + if (output.d > 0) + output.w++; + break; + + case FMT_E: + output.d = MAX (input->d, 3); + output.w = MAX (input->w, output.d + 7); + break; + + case FMT_PIBHEX: + output.w = max_digits_for_bytes (input->w / 2) + 1; + break; + + case FMT_RB: + case FMT_RBHEX: + output.w = 8; + output.d = 2; + break; + + case FMT_P: + case FMT_PK: + output.w = 2 * input->w + (input->d > 0); + break; + + case FMT_IB: + case FMT_PIB: + output.w = max_digits_for_bytes (input->w) + 1; + if (output.d > 0) + output.w++; + break; + + case FMT_CCA: + case FMT_CCB: + case FMT_CCC: + case FMT_CCD: + case FMT_CCE: + NOT_REACHED (); + + case FMT_A: + break; + + case FMT_AHEX: + output.w = input->w / 2; + break; + + case FMT_DATE: + case FMT_EDATE: + case FMT_SDATE: + case FMT_ADATE: + case FMT_JDATE: + case FMT_QYR: + case FMT_MOYR: + case FMT_WKYR: + case FMT_TIME: + case FMT_DTIME: + case FMT_DATETIME: + case FMT_WKDAY: + case FMT_MONTH: + break; + + default: + NOT_REACHED (); } - if (f->n_args > 1 && (spec->d < 0 || spec->d > 16)) + + if (output.w > fmt_max_output_width (output.type)) + output.w = fmt_max_output_width (output.type); + + assert (fmt_check_output (&output)); + return output; +} + +/* Checks whether SPEC is valid as an input format (if FOR_INPUT) + or an output format (otherwise) and returns nonzero if so. + Otherwise, emits an error message and returns zero. */ +bool +fmt_check (const struct fmt_spec *spec, bool for_input) +{ + const char *io_fmt = for_input ? _("Input format") : _("Output format"); + char str[FMT_STRING_LEN_MAX + 1]; + int min_w, max_w, max_d; + + assert (is_fmt_type (spec->type)); + fmt_to_string (spec, str); + + if (for_input && !fmt_usable_for_input (spec->type)) { - if (emit_error) - msg (SE, _("Format %s specifies a bad number of " - "implied decimal places %d. Format type %s allows " - "up to 16 implied decimal places."), str, spec->d, f->name); + msg (SE, _("Format %s may not be used for input."), str); return false; } - if (f->n_args <= 1 && spec->d) + + if (spec->w % fmt_step_width (spec->type)) { - if (emit_error) - msg (SE, _("Format %s specifies %d decimal places, but " - "format type %s does not allow for decimal places."), - str, spec->d, f->name); + assert (fmt_step_width (spec->type) == 2); + msg (SE, _("%s specifies width %d, but %s requires an even width."), + str, spec->w, fmt_name (spec->type)); return false; } - return true; -} -/* Checks whether SPEC is valid as an input format and returns - nonzero if so. Otherwise, emits an error message (if - EMIT_ERROR is nonzero) and returns zero. */ -int -check_input_specifier (const struct fmt_spec *spec, int emit_error) -{ - const struct fmt_desc *f; - char *str ; - - if (!check_common_specifier (spec, emit_error)) - return false; - - f = &formats[spec->type]; - str = fmt_to_string (spec); - if (f->cat & FCAT_OUTPUT_ONLY) + min_w = min_width (spec->type, for_input); + max_w = max_width (spec->type); + if (spec->w < min_w || spec->w > max_w) { - if (emit_error) - msg (SE, _("Format %s may not be used for input."), f->name); - return 0; + msg (SE, _("%s %s specifies width %d, but " + "%s requires a width between %d and %d."), + io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w); + return false; } - if (spec->w < f->Imin_w || spec->w > f->Imax_w) + + max_d = max_decimals (spec->type, spec->w, for_input); + if (!fmt_takes_decimals (spec->type) && spec->d != 0) { - if (emit_error) - msg (SE, _("Input format %s specifies a bad width %d. " - "Format %s requires a width between %d and %d."), - str, spec->w, f->name, f->Imin_w, f->Imax_w); - return 0; + msg (SE, ngettext ("%s %s specifies %d decimal place, but " + "%s does not allow any decimals.", + "%s %s specifies %d decimal places, but " + "%s does not allow any decimals.", + spec->d), + io_fmt, str, spec->d, fmt_name (spec->type)); + return false; } - if ((spec->type == FMT_F || spec->type == FMT_COMMA - || spec->type == FMT_DOLLAR) - && spec->d > spec->w) + else if (spec->d > max_d) { - if (emit_error) - msg (SE, _("Input format %s is invalid because it specifies more " - "decimal places than the field width."), str); - return 0; + if (max_d > 0) + msg (SE, ngettext ("%s %s specifies %d decimal place, but " + "the given width allows at most %d decimals.", + "%s %s specifies %d decimal places, but " + "the given width allows at most %d decimals.", + spec->d), + io_fmt, str, spec->d, max_d); + else + msg (SE, ngettext ("%s %s specifies %d decimal place, but " + "the given width does not allow for any decimals.", + "%s %s specifies %d decimal places, but " + "the given width does not allow for any decimals.", + spec->d), + io_fmt, str, spec->d); + return false; } - return 1; + + return true; } -/* Checks whether SPEC is valid as an output format and returns - nonzero if so. Otherwise, emits an error message (if - EMIT_ERROR is nonzero) and returns zero. */ -int -check_output_specifier (const struct fmt_spec *spec, int emit_error) +/* Checks whether SPEC is valid as an input format and returns + nonzero if so. Otherwise, emits an error message and returns + zero. */ +bool +fmt_check_input (const struct fmt_spec *spec) { - const struct fmt_desc *f; - char *str ; - - if (!check_common_specifier (spec, emit_error)) - return false; + return fmt_check (spec, true); +} - f = &formats[spec->type]; - str = fmt_to_string (spec); - if (spec->w < f->Omin_w || spec->w > f->Omax_w) - { - if (emit_error) - msg (SE, _("Output format %s specifies a bad width %d. " - "Format %s requires a width between %d and %d."), - str, spec->w, f->name, f->Omin_w, f->Omax_w); - return 0; - } - if ((spec->type == FMT_F || spec->type == FMT_COMMA - || spec->type == FMT_DOLLAR) - && spec->d >= spec->w) - { - if (emit_error) - msg (SE, _("Output format %s is invalid because it specifies as " - "many decimal places as the field width, which " - "fails to allow space for a decimal point. " - "Try %s%d.%d instead."), - str, f->name, spec->d + 1, spec->d); - return 0; - } - return 1; +/* Checks whether SPEC is valid as an output format and returns + true if so. Otherwise, emits an error message and returns false. */ +bool +fmt_check_output (const struct fmt_spec *spec) +{ + return fmt_check (spec, false); } /* Checks that FORMAT is appropriate for a variable of the given - TYPE and returns true if so. Otherwise returns false and (if - EMIT_ERROR is true) emits an error message. */ + TYPE and returns true if so. Otherwise returns false and + emits an error message. */ bool -check_specifier_type (const struct fmt_spec *format, - int type, bool emit_error) +fmt_check_type_compat (const struct fmt_spec *format, int var_type) { - const struct fmt_desc *f = &formats[format->type]; - assert (type == NUMERIC || type == ALPHA); - if ((type == ALPHA) != ((f->cat & FCAT_STRING) != 0)) + assert (var_type == NUMERIC || var_type == ALPHA); + if ((var_type == ALPHA) != (fmt_is_string (format->type) != 0)) { - if (emit_error) - msg (SE, _("%s variables are not compatible with %s format %s."), - type == ALPHA ? _("String") : _("Numeric"), - type == ALPHA ? _("numeric") : _("string"), - fmt_to_string (format)); + char str[FMT_STRING_LEN_MAX + 1]; + msg (SE, _("%s variables are not compatible with %s format %s."), + var_type == ALPHA ? _("String") : _("Numeric"), + var_type == ALPHA ? _("numeric") : _("string"), + fmt_to_string (format, str)); return false; } return true; } - + /* Checks that FORMAT is appropriate for a variable of the given - WIDTH and returns true if so. Otherwise returns false and (if - EMIT_ERROR is true) emits an error message. */ + WIDTH and returns true if so. Otherwise returns false and + emits an error message. */ bool -check_specifier_width (const struct fmt_spec *format, - int width, bool emit_error) +fmt_check_width_compat (const struct fmt_spec *format, int width) { - if (!check_specifier_type (format, width != 0 ? ALPHA : NUMERIC, emit_error)) + if (!fmt_check_type_compat (format, width != 0 ? ALPHA : NUMERIC)) return false; - if (get_format_var_width (format) != width) + if (fmt_var_width (format) != width) { - if (emit_error) - msg (SE, _("String variable with width %d not compatible with " - "format %s."), - width, fmt_to_string (format)); + char str[FMT_STRING_LEN_MAX + 1]; + msg (SE, _("String variable with width %d is not compatible with " + "format %s."), + width, fmt_to_string (format, str)); return false; } return true; } -/* Converts input format specifier INPUT into output format - specifier OUTPUT. */ -void -convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output) +/* Returns the width corresponding to the format specifier. The + return value is the value of the `width' member of a `struct + variable' for such an input format. */ +int +fmt_var_width (const struct fmt_spec *spec) { - assert (check_input_specifier (input, 0)); + return (spec->type == FMT_AHEX ? spec->w / 2 + : spec->type == FMT_A ? spec->w + : 0); +} - output->type = formats[input->type].output; - output->w = input->w; - if (output->w > formats[output->type].Omax_w) - output->w = formats[output->type].Omax_w; - output->d = input->d; +/* Converts F to its string representation (for instance, "F8.2") + in BUFFER. Returns BUFFER. - switch (input->type) + If F has decimals, they are included in the output string, + even if F's format type does not allow decimals, to allow + accurately presenting incorrect formats to the user. */ +char * +fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1]) +{ + if (fmt_takes_decimals (f->type) || f->d > 0) + snprintf (buffer, FMT_STRING_LEN_MAX + 1, + "%s%d.%d", fmt_name (f->type), f->w, f->d); + else + snprintf (buffer, FMT_STRING_LEN_MAX + 1, + "%s%d", fmt_name (f->type), f->w); + return buffer; +} + +/* Describes a display format. */ +struct fmt_desc + { + char name[9]; + int min_input_width, min_output_width; + int io; + enum fmt_category category; + }; + +static const struct fmt_desc *get_fmt_desc (enum fmt_type type); + +/* Returns the name of the given format TYPE. */ +const char * +fmt_name (enum fmt_type type) +{ + return get_fmt_desc (type)->name; +} + +/* Tries to parse NAME as a format type. + If successful, stores the type in *TYPE and returns true. + On failure, returns false. */ +bool +fmt_from_name (const char *name, enum fmt_type *type) +{ + int i; + + for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++) + if (!strcasecmp (name, get_fmt_desc (i)->name)) + { + *type = i; + return true; + } + return false; +} + +/* Returns true if TYPE accepts decimal places, + false otherwise. */ +bool +fmt_takes_decimals (enum fmt_type type) +{ + return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0; +} + +/* Returns the minimum acceptable width for an input field + formatted with the given TYPE. */ +int +fmt_min_input_width (enum fmt_type type) +{ + return get_fmt_desc (type)->min_input_width; +} + +/* Returns the maximum acceptable width for an input field + formatted with the given TYPE. */ +int +fmt_max_input_width (enum fmt_type type) +{ + return max_width (type); +} + +/* Returns the maximum number of decimal places allowed in an + input field of the given TYPE and WIDTH. */ +int +fmt_max_input_decimals (enum fmt_type type, int width) +{ + assert (valid_width (type, width, true)); + return max_decimals (type, width, true); +} + +/* Returns the minimum acceptable width for an output field + formatted with the given TYPE. */ +int +fmt_min_output_width (enum fmt_type type) +{ + return get_fmt_desc (type)->min_output_width; +} + +/* Returns the maximum acceptable width for an output field + formatted with the given TYPE. */ +int +fmt_max_output_width (enum fmt_type type) +{ + return max_width (type); +} + +/* Returns the maximum number of decimal places allowed in an + output field of the given TYPE and WIDTH. */ +int +fmt_max_output_decimals (enum fmt_type type, int width) +{ + assert (valid_width (type, width, false)); + return max_decimals (type, width, false); +} + +/* Returns the width step for a field formatted with the given + TYPE. Field width must be a multiple of the width step. */ +int +fmt_step_width (enum fmt_type type) +{ + return fmt_get_category (type) & FMT_CAT_HEXADECIMAL ? 2 : 1; +} + +/* Returns true if TYPE is used for string fields, + false if it is used for numeric fields. */ +bool +fmt_is_string (enum fmt_type type) +{ + return fmt_get_category (type) & FMT_CAT_STRING; +} + +/* Returns true if TYPE is used for numeric fields, + false if it is used for string fields. */ +bool +fmt_is_numeric (enum fmt_type type) +{ + return !fmt_is_string (type); +} + +/* Returns the format TYPE's category. + Each format type is in exactly one category, + and each category's value is bitwise disjoint from every other + category. Thus, the return value may be tested for equality + or compared bitwise against a mask of FMT_CAT_* values. */ +enum fmt_category +fmt_get_category (enum fmt_type type) +{ + return get_fmt_desc (type)->category; +} + +/* Returns the output format selected by default when TYPE is + used as an input format. */ +enum fmt_type +fmt_input_to_output (enum fmt_type type) +{ + enum fmt_category category = fmt_get_category (type); + return (category & FMT_CAT_STRING ? FMT_A + : category & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL) ? FMT_F + : type); +} + +/* Returns the SPSS format type corresponding to the given PSPP + format type. */ +int +fmt_to_io (enum fmt_type type) +{ + return get_fmt_desc (type)->io; +}; + +/* Determines the PSPP format corresponding to the given SPSS + format type. If successful, sets *FMT_TYPE to the PSPP format + and returns true. On failure, return false. */ +bool +fmt_from_io (int io, enum fmt_type *fmt_type) +{ + enum fmt_type type; + + for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++) + if (get_fmt_desc (type)->io == io) + { + *fmt_type = type; + return true; + } + return false; +} + +/* Returns true if TYPE may be used as an input format, + false otherwise. */ +bool +fmt_usable_for_input (enum fmt_type type) +{ + assert (is_fmt_type (type)); + return fmt_get_category (type) != FMT_CAT_CUSTOM; +} + +/* For time and date formats, returns a template used for input + and output. */ +const char * +fmt_date_template (enum fmt_type type) +{ + switch (type) + { + case FMT_DATE: + return "dd-mmm-yy"; + case FMT_ADATE: + return "mm/dd/yy"; + case FMT_EDATE: + return "dd.mm.yy"; + case FMT_JDATE: + return "yyddd"; + case FMT_SDATE: + return "yy/mm/dd"; + case FMT_QYR: + return "q Q yy"; + case FMT_MOYR: + return "mmm yy"; + case FMT_WKYR: + return "ww WK yy"; + case FMT_DATETIME: + return "dd-mmm-yyyy HH:MM"; + case FMT_TIME: + return "h:MM"; + case FMT_DTIME: + return "D HH:MM"; + default: + NOT_REACHED (); + } +} + +/* Returns true if TYPE is a valid format type, + false otherwise. */ +static bool +is_fmt_type (enum fmt_type type) +{ + return type < FMT_NUMBER_OF_FORMATS; +} + +/* Returns the minimum width of the given format TYPE, + for input if FOR_INPUT is true, + for output otherwise. */ +static int +min_width (enum fmt_type type, bool for_input) +{ + return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type); +} + +/* Returns the maximum width of the given format TYPE, + which is invariant between input and output. */ +static int +max_width (enum fmt_type type) +{ + assert (is_fmt_type (type)); + switch (type) + { + case FMT_P: + case FMT_PK: + case FMT_PIBHEX: + case FMT_RBHEX: + return 16; + + case FMT_IB: + case FMT_PIB: + case FMT_RB: + return 8; + + case FMT_A: + return MAX_STRING; + + case FMT_AHEX: + return 2 * MAX_STRING; + + default: + return 40; + } +} + +/* Returns true if WIDTH is a valid width for the given format + TYPE, + for input if FOR_INPUT is true, + for output otherwise. */ +static bool +valid_width (enum fmt_type type, int width, bool for_input) +{ + return (width >= min_width (type, for_input) + && width <= max_width (type)); +} + +/* Returns the maximum number of decimal places allowed for the + given format TYPE with a width of WIDTH places, + for input if FOR_INPUT is true, + for output otherwise. */ +static int +max_decimals (enum fmt_type type, int width, bool for_input) +{ + int max_d; + + switch (type) { case FMT_F: - case FMT_N: - if (output->d > 0) - output->w++; - break; - case FMT_E: - output->w = max (max (input->w, input->d+7), 10); - output->d = max (input->d, 3); - break; case FMT_COMMA: case FMT_DOT: - /* nothing is necessary */ + max_d = for_input ? width : width - 1; break; + case FMT_DOLLAR: case FMT_PCT: - if (output->w < 2) - output->w = 2; + max_d = for_input ? width : width - 2; break; - case FMT_PIBHEX: - { - static const int map[] = {4, 6, 9, 11, 14, 16, 18, 21}; - assert (input->w % 2 == 0 && input->w >= 2 && input->w <= 16); - output->w = map[input->w / 2 - 1]; - break; - } - case FMT_RBHEX: - output->w = 8, output->d = 2; /* FIXME */ - break; - case FMT_IB: - case FMT_PIB: - case FMT_P: - case FMT_PK: - case FMT_RB: - if (input->d < 1) - output->w = 8, output->d = 2; - else - output->w = 9 + input->d; + + case FMT_E: + max_d = for_input ? width : width - 7; break; + case FMT_CCA: case FMT_CCB: case FMT_CCC: case FMT_CCD: case FMT_CCE: - NOT_REACHED (); + assert (!for_input); + max_d = width - 1; + break; + + case FMT_N: case FMT_Z: - case FMT_A: - /* nothing is necessary */ + max_d = width; break; - case FMT_AHEX: - output->w = input->w / 2; + + case FMT_P: + max_d = width * 2 - 1; + break; + + case FMT_PK: + max_d = width * 2; + break; + + case FMT_IB: + case FMT_PIB: + max_d = max_digits_for_bytes (width); break; + + case FMT_PIBHEX: + max_d = 0; + break; + + case FMT_RB: + case FMT_RBHEX: + max_d = 16; + break; + case FMT_DATE: - case FMT_EDATE: - case FMT_SDATE: case FMT_ADATE: + case FMT_EDATE: case FMT_JDATE: - /* nothing is necessary */ - break; + case FMT_SDATE: case FMT_QYR: - if (output->w < 6) - output->w = 6; - break; case FMT_MOYR: - /* nothing is necessary */ - break; case FMT_WKYR: - if (output->w < 8) - output->w = 8; + max_d = 0; + break; + + case FMT_DATETIME: + max_d = width - 21; break; + case FMT_TIME: + max_d = width - 9; + break; + case FMT_DTIME: - case FMT_DATETIME: + max_d = width - 12; + break; + case FMT_WKDAY: case FMT_MONTH: - /* nothing is necessary */ + case FMT_A: + case FMT_AHEX: + max_d = 0; break; + default: NOT_REACHED (); } - assert (check_output_specifier (output, 0)); + if (max_d < 0) + max_d = 0; + else if (max_d > 16) + max_d = 16; + return max_d; } -/* Returns the width corresponding to the format specifier. The - return value is the value of the `width' member of a `struct - variable' for such an input format. */ +/* Returns the maximum number of decimal digits in an unsigned + binary number that is BYTES bytes long. */ +static int +max_digits_for_bytes (int bytes) +{ + int map[8] = {3, 5, 8, 10, 13, 15, 17, 20}; + assert (bytes > 0 && bytes <= sizeof map / sizeof *map); + return map[bytes - 1]; +} + +static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS]; + +/* Creates and returns a new struct fmt_number_style, + initializing all affixes to empty strings. */ +struct fmt_number_style * +fmt_number_style_create (void) +{ + struct fmt_number_style *style = xmalloc (sizeof *style); + style->neg_prefix = ss_empty (); + style->prefix = ss_empty (); + style->suffix = ss_empty (); + style->neg_suffix = ss_empty (); + style->decimal = '.'; + style->grouping = 0; + return style; +} + +/* Destroys a struct fmt_number_style. */ +void +fmt_number_style_destroy (struct fmt_number_style *style) +{ + if (style != NULL) + { + ss_dealloc (&style->neg_prefix); + ss_dealloc (&style->prefix); + ss_dealloc (&style->suffix); + ss_dealloc (&style->neg_suffix); + free (style); + } +} + +/* Returns the number formatting style associated with the given + format TYPE. */ +const struct fmt_number_style * +fmt_get_style (enum fmt_type type) +{ + assert (is_fmt_type (type)); + assert (styles[type] != NULL); + return styles[type]; +} + +/* Sets STYLE as the number formatting style associated with the + given format TYPE, transferring ownership of STYLE. */ +void +fmt_set_style (enum fmt_type type, struct fmt_number_style *style) +{ + assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX); + assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX); + assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX); + assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX); + assert (style->decimal == '.' || style->decimal == ','); + assert (style->grouping != style->decimal + && (style->grouping == '.' || style->grouping == ',' + || style->grouping == 0)); + + assert (fmt_get_category (type) == FMT_CAT_CUSTOM); + assert (styles[type] != NULL); + + fmt_number_style_destroy (styles[type]); + styles[type] = style; +} + +/* Returns the total width of the standard prefix and suffix for + STYLE. */ int -get_format_var_width (const struct fmt_spec *spec) +fmt_affix_width (const struct fmt_number_style *style) { - if (spec->type == FMT_AHEX) - return spec->w / 2; - else if (spec->type == FMT_A) - return spec->w; - else - return 0; + return ss_length (style->prefix) + ss_length (style->suffix); } -/* Returns the PSPP format corresponding to the given SPSS - format. */ +/* Returns the total width of the negative prefix and suffix for + STYLE. */ int -translate_fmt (int spss) +fmt_neg_affix_width (const struct fmt_number_style *style) { - int type; - - for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++) - if (formats[type].spss == spss) - return type; - return -1; + return ss_length (style->neg_prefix) + ss_length (style->neg_suffix); } -/* Returns an input format specification with type TYPE, width W, - and D decimals. */ -struct fmt_spec -make_input_format (int type, int w, int d) +/* Returns the decimal point character for the given format + TYPE. */ +int +fmt_decimal_char (enum fmt_type type) { - struct fmt_spec f; - f.type = type; - f.w = w; - f.d = d; - assert (check_input_specifier (&f, 0)); - return f; + return fmt_get_style (type)->decimal; } -/* Returns an output format specification with type TYPE, width - W, and D decimals. */ -struct fmt_spec -make_output_format (int type, int w, int d) +/* Returns the grouping character for the given format TYPE, or 0 + if the format type does not group digits. */ +int +fmt_grouping_char (enum fmt_type type) { - struct fmt_spec f; - f.type = type; - f.w = w; - f.d = d; - assert (check_output_specifier (&f, 0)); - return f; + return fmt_get_style (type)->grouping; } -/* Returns true if TYPE is a binary format, - false otherwise. */ -bool -fmt_is_binary (int type) +/* Sets the number style for TYPE to have the given standard + PREFIX and SUFFIX, "-" as prefix suffix, an empty negative + suffix, DECIMAL as the decimal point character, and GROUPING + as the grouping character. */ +static void +set_style (enum fmt_type type, + const char *prefix, const char *suffix, + char decimal, char grouping) { - switch (type) - { - case FMT_P: - case FMT_PK: - case FMT_IB: - case FMT_PIB: - case FMT_RB: - return true; + struct fmt_number_style *style; - default: - return false; - } + assert (is_fmt_type (type)); + + fmt_number_style_destroy (styles[type]); + + style = styles[type] = fmt_number_style_create (); + ss_alloc_substring (&style->neg_prefix, ss_cstr ("-")); + ss_alloc_substring (&style->prefix, ss_cstr (prefix)); + ss_alloc_substring (&style->suffix, ss_cstr (suffix)); + style->decimal = decimal; + style->grouping = grouping; } -bool -measure_is_valid(enum measure m) +/* Sets the number style for TYPE as with set_style, but only if + TYPE has not already been initialized. */ +static void +init_style (enum fmt_type type, + const char *prefix, const char *suffix, + char decimal, char grouping) { - if ( m <= 0 ) return false; - if ( m >= n_MEASURES) return false; - return true; + assert (is_fmt_type (type)); + if (styles[type] == NULL) + set_style (type, prefix, suffix, decimal, grouping); } -bool -alignment_is_valid(enum alignment a) +/* Sets the decimal point character to DECIMAL. */ +void +fmt_set_decimal (char decimal) { - if ( a >= n_ALIGN) return false; - return true; + int grouping = decimal == '.' ? ',' : '.'; + assert (decimal == '.' || decimal == ','); + + set_style (FMT_F, "", "", decimal, 0); + set_style (FMT_E, "", "", decimal, 0); + set_style (FMT_COMMA, "", "", decimal, grouping); + set_style (FMT_DOT, "", "", grouping, decimal); + set_style (FMT_DOLLAR, "$", "", decimal, grouping); + set_style (FMT_PCT, "", "%", decimal, 0); + + init_style (FMT_CCA, "", "", decimal, grouping); + init_style (FMT_CCB, "", "", decimal, grouping); + init_style (FMT_CCC, "", "", decimal, grouping); + init_style (FMT_CCD, "", "", decimal, grouping); + init_style (FMT_CCE, "", "", decimal, grouping); +} + +/* Returns true if M is a valid variable measurement level, + false otherwise. */ +bool +measure_is_valid (enum measure m) +{ + return m > 0 && m < n_MEASURES; +} + +/* Returns true if A is a valid alignment, + false otherwise. */ +bool +alignment_is_valid (enum alignment a) +{ + return a < n_ALIGN; +} + +/* Returns the struct fmt_desc for the given format TYPE. */ +static const struct fmt_desc * +get_fmt_desc (enum fmt_type type) +{ + static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] = + { +#define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \ + {#NAME, IMIN, OMIN, IO, CATEGORY}, +#include "format.def" + }; + + assert (is_fmt_type (type)); + return &formats[type]; } diff --git a/src/data/format.def b/src/data/format.def index 034b81d4..65c91dc7 100644 --- a/src/data/format.def +++ b/src/data/format.def @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -17,43 +17,53 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* Numeric and string formats. */ -DEFFMT (FMT_F, "F", 2, 1, 40, 1, 40, 0001, FMT_F, 5) -DEFFMT (FMT_N, "N", 2, 1, 40, 1, 40, 0011, FMT_F, 16) -DEFFMT (FMT_E, "E", 2, 1, 40, 6, 40, 0001, FMT_E, 17) -DEFFMT (FMT_COMMA, "COMMA", 2, 1, 40, 1, 40, 0001, FMT_COMMA, 3) -DEFFMT (FMT_DOT, "DOT", 2, 1, 40, 1, 40, 0001, FMT_DOT, 32) -DEFFMT (FMT_DOLLAR, "DOLLAR", 2, 1, 40, 2, 40, 0001, FMT_DOLLAR, 4) -DEFFMT (FMT_PCT, "PCT", 2, 1, 40, 2, 40, 0001, FMT_PCT, 31) -DEFFMT (FMT_Z, "Z", 2, 1, 40, 1, 40, 0011, FMT_F, 15) -DEFFMT (FMT_A, "A", 1, 1, MAX_STRING, 1, MAX_STRING, 0004, FMT_A, 1) -DEFFMT (FMT_AHEX, "AHEX", 1, 2, 510, 2, 510, 0006, FMT_A, 2) -DEFFMT (FMT_IB, "IB", 2, 1, 8, 1, 8, 0010, FMT_F, 6) -DEFFMT (FMT_P, "P", 2, 1, 16, 1, 16, 0011, FMT_F, 8) -DEFFMT (FMT_PIB, "PIB", 2, 1, 8, 1, 8, 0010, FMT_F, 9) -DEFFMT (FMT_PIBHEX, "PIBHEX", 2, 2, 16, 2, 16, 0002, FMT_F, 7) -DEFFMT (FMT_PK, "PK", 2, 1, 16, 1, 16, 0010, FMT_F, 10) -DEFFMT (FMT_RB, "RB", 1, 2, 8, 2, 8, 0002, FMT_F, 11) -DEFFMT (FMT_RBHEX, "RBHEX", 1, 4, 16, 4, 16, 0002, FMT_F, 12) - -/* Custom currency. */ -DEFFMT (FMT_CCA, "CCA", 2, -1, -1, 1, 40, 0020, FMT_CCA, 33) -DEFFMT (FMT_CCB, "CCB", 2, -1, -1, 1, 40, 0020, FMT_CCB, 34) -DEFFMT (FMT_CCC, "CCC", 2, -1, -1, 1, 40, 0020, FMT_CCC, 35) -DEFFMT (FMT_CCD, "CCD", 2, -1, -1, 1, 40, 0020, FMT_CCD, 36) -DEFFMT (FMT_CCE, "CCE", 2, -1, -1, 1, 40, 0020, FMT_CCE, 37) - -/* Date/time formats. */ -DEFFMT (FMT_DATE, "DATE", 1, 9, 40, 9, 40, 0001, FMT_DATE, 20) -DEFFMT (FMT_EDATE, "EDATE", 1, 8, 40, 8, 40, 0001, FMT_EDATE, 38) -DEFFMT (FMT_SDATE, "SDATE", 1, 8, 40, 8, 40, 0001, FMT_SDATE, 39) -DEFFMT (FMT_ADATE, "ADATE", 1, 8, 40, 8, 40, 0001, FMT_ADATE, 23) -DEFFMT (FMT_JDATE, "JDATE", 1, 5, 40, 5, 40, 0001, FMT_JDATE, 24) -DEFFMT (FMT_QYR, "QYR", 1, 4, 40, 6, 40, 0001, FMT_QYR, 29) -DEFFMT (FMT_MOYR, "MOYR", 1, 6, 40, 6, 40, 0001, FMT_MOYR, 28) -DEFFMT (FMT_WKYR, "WKYR", 1, 6, 40, 8, 40, 0001, FMT_WKYR, 30) -DEFFMT (FMT_DATETIME, "DATETIME", 2, 17, 40, 17, 40, 0001, FMT_DATETIME, 22) -DEFFMT (FMT_TIME, "TIME", 2, 5, 40, 5, 40, 0001, FMT_TIME, 21) -DEFFMT (FMT_DTIME, "DTIME", 2, 11, 40, 8, 40, 0001, FMT_DTIME, 25) -DEFFMT (FMT_WKDAY, "WKDAY", 1, 2, 40, 2, 40, 0001, FMT_WKDAY, 26) -DEFFMT (FMT_MONTH, "MONTH", 1, 3, 40, 3, 40, 0001, FMT_MONTH, 27) +/* Basic numeric formats. */ +FMT (F, number, 1, 1, 5, FMT_CAT_BASIC) +FMT (COMMA, number, 1, 1, 3, FMT_CAT_BASIC) +FMT (DOT, number, 1, 1, 32, FMT_CAT_BASIC) +FMT (DOLLAR, number, 1, 2, 4, FMT_CAT_BASIC) +FMT (PCT, number, 1, 2, 31, FMT_CAT_BASIC) +FMT (E, number, 1, 6, 17, FMT_CAT_BASIC) + +/* Custom currency formats. */ +FMT (CCA, number, -1, 2, 33, FMT_CAT_CUSTOM) +FMT (CCB, number, -1, 2, 34, FMT_CAT_CUSTOM) +FMT (CCC, number, -1, 2, 35, FMT_CAT_CUSTOM) +FMT (CCD, number, -1, 2, 36, FMT_CAT_CUSTOM) +FMT (CCE, number, -1, 2, 37, FMT_CAT_CUSTOM) + +/* Legacy numeric formats. */ +FMT (N, N, 1, 1, 16, FMT_CAT_LEGACY) +FMT (Z, Z, 1, 1, 16, FMT_CAT_LEGACY) + +/* Binary and hexadecimal formats. */ +FMT (P, P, 1, 1, 8, FMT_CAT_BINARY) +FMT (PK, PK, 1, 1, 10, FMT_CAT_BINARY) +FMT (IB, IB, 1, 1, 6, FMT_CAT_BINARY) +FMT (PIB, PIB, 1, 1, 9, FMT_CAT_BINARY) +FMT (PIBHEX, PIBHEX, 2, 2, 7, FMT_CAT_HEXADECIMAL) +FMT (RB, RB, 2, 2, 11, FMT_CAT_BINARY) +FMT (RBHEX, RBHEX, 4, 4, 12, FMT_CAT_HEXADECIMAL) + +/* Time and date formats. */ +FMT (DATE, date, 8, 9, 20, FMT_CAT_DATE) +FMT (ADATE, date, 8, 8, 23, FMT_CAT_DATE) +FMT (EDATE, date, 8, 8, 38, FMT_CAT_DATE) +FMT (JDATE, date, 5, 5, 24, FMT_CAT_DATE) +FMT (SDATE, date, 8, 8, 39, FMT_CAT_DATE) +FMT (QYR, date, 4, 6, 29, FMT_CAT_DATE) +FMT (MOYR, date, 6, 6, 28, FMT_CAT_DATE) +FMT (WKYR, date, 6, 8, 30, FMT_CAT_DATE) +FMT (DATETIME, date, 7, 17, 22, FMT_CAT_DATE) +FMT (TIME, date, 5, 5, 21, FMT_CAT_TIME) +FMT (DTIME, date, 8, 8, 25, FMT_CAT_TIME) + +/* Date component formats. */ +FMT (WKDAY, WKDAY, 2, 2, 26, FMT_CAT_DATE_COMPONENT) +FMT (MONTH, MONTH, 3, 3, 27, FMT_CAT_DATE_COMPONENT) + +/* String formats. */ +FMT (A, A, 1, 1, 1, FMT_CAT_STRING) +FMT (AHEX, AHEX, 2, 2, 2, FMT_CAT_STRING) + +#undef FMT diff --git a/src/data/format.h b/src/data/format.h index 2c7074d2..3678632e 100644 --- a/src/data/format.h +++ b/src/data/format.h @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -17,65 +17,133 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#if !format_h -#define format_h 1 +#ifndef FORMAT_H +#define FORMAT_H 1 /* Display format types. */ #include +#include +#include -/* See the definitions of these functions and variables when modifying - this list: - misc.c:convert_fmt_ItoO() - sys-file-reader.c:parse_format_spec() - data-in.c:parse_string_as_format() */ -#define DEFFMT(LABEL, NAME, N_ARGS, IMIN_W, IMAX_W, OMIN_W, OMAX_W, \ - CAT, OUTPUT, SPSS_FMT) \ - LABEL, -enum +/* Format type categories. */ +enum fmt_category { + /* Numeric formats. */ + FMT_CAT_BASIC = 0x001, /* Basic numeric formats. */ + FMT_CAT_CUSTOM = 0x002, /* Custom currency formats. */ + FMT_CAT_LEGACY = 0x004, /* Legacy numeric formats. */ + FMT_CAT_BINARY = 0x008, /* Binary formats. */ + FMT_CAT_HEXADECIMAL = 0x010, /* Hexadecimal formats. */ + FMT_CAT_DATE = 0x020, /* Date formats. */ + FMT_CAT_TIME = 0x040, /* Time formats. */ + FMT_CAT_DATE_COMPONENT = 0x080, /* Date component formats. */ + + /* String formats. */ + FMT_CAT_STRING = 0x100 /* String formats. */ + }; + +enum fmt_type + { +#define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) FMT_##NAME, #include "format.def" - FMT_NUMBER_OF_FORMATS + FMT_NUMBER_OF_FORMATS, }; -#undef DEFFMT /* Length of longest format specifier name, not including terminating null. */ #define FMT_TYPE_LEN_MAX 8 -/* Describes one of the display formats above. */ -struct fmt_desc - { - char name[FMT_TYPE_LEN_MAX + 1]; /* Name, in all caps. */ - int n_args; /* 1=width; 2=width.decimals. */ - int Imin_w, Imax_w; /* Bounds on input width. */ - int Omin_w, Omax_w; /* Bounds on output width. */ - int cat; /* Categories. */ - int output; /* Output format. */ - int spss; /* Equivalent SPSS output format. */ - }; - -/* Display format categories. */ -enum - { - FCAT_BLANKS_SYSMIS = 001, /* 1=All-whitespace means SYSMIS. */ - FCAT_EVEN_WIDTH = 002, /* 1=Width must be even. */ - FCAT_STRING = 004, /* 1=String input/output format. */ - FCAT_SHIFT_DECIMAL = 010, /* 1=Automatically shift decimal point - on output--used for fixed-point - formats. */ - FCAT_OUTPUT_ONLY = 020 /* 1=This is not an input format. */ - }; +/* Length of longest string representation of fmt_spec, + not including terminating null. */ +#define FMT_STRING_LEN_MAX 32 /* Display format. */ struct fmt_spec { - int type; /* One of the above constants. */ + enum fmt_type type; /* One of FMT_*. */ int w; /* Width. */ int d; /* Number of implied decimal places. */ }; +union value; + +/* Initialization. */ +void fmt_init (void); +void fmt_done (void); + +/* Constructing formats. */ +struct fmt_spec fmt_for_input (enum fmt_type, int w, int d) PURE_FUNCTION; +struct fmt_spec fmt_for_output (enum fmt_type, int w, int d) PURE_FUNCTION; +struct fmt_spec fmt_for_output_from_input (const struct fmt_spec *); + +/* Verifying formats. */ +bool fmt_check (const struct fmt_spec *, bool for_input); +bool fmt_check_input (const struct fmt_spec *); +bool fmt_check_output (const struct fmt_spec *); +bool fmt_check_type_compat (const struct fmt_spec *, int var_type); +bool fmt_check_width_compat (const struct fmt_spec *, int width); + +/* Working with formats. */ +int fmt_var_width (const struct fmt_spec *); +char *fmt_to_string (const struct fmt_spec *, char s[FMT_STRING_LEN_MAX + 1]); + +/* Format types. */ +const char *fmt_name (enum fmt_type) PURE_FUNCTION; +bool fmt_from_name (const char *name, enum fmt_type *); + +bool fmt_takes_decimals (enum fmt_type) PURE_FUNCTION; + +int fmt_min_input_width (enum fmt_type) PURE_FUNCTION; +int fmt_max_input_width (enum fmt_type) PURE_FUNCTION; +int fmt_max_input_decimals (enum fmt_type, int width) PURE_FUNCTION; +int fmt_min_output_width (enum fmt_type) PURE_FUNCTION; +int fmt_max_output_width (enum fmt_type) PURE_FUNCTION; +int fmt_max_output_decimals (enum fmt_type, int width) PURE_FUNCTION; +int fmt_step_width (enum fmt_type) PURE_FUNCTION; + +bool fmt_is_string (enum fmt_type) PURE_FUNCTION; +bool fmt_is_numeric (enum fmt_type) PURE_FUNCTION; +enum fmt_category fmt_get_category (enum fmt_type) PURE_FUNCTION; + +enum fmt_type fmt_input_to_output (enum fmt_type) PURE_FUNCTION; + +int fmt_to_io (enum fmt_type) PURE_FUNCTION; +bool fmt_from_io (int io, enum fmt_type *); + +bool fmt_usable_for_input (enum fmt_type) PURE_FUNCTION; +const char *fmt_date_template (enum fmt_type) PURE_FUNCTION; + +/* Maximum length of prefix or suffix string in + struct fmt_number_style. */ +#define FMT_STYLE_AFFIX_MAX 16 + +/* A numeric output style. */ +struct fmt_number_style + { + struct substring neg_prefix; /* Negative prefix. */ + struct substring prefix; /* Prefix. */ + struct substring suffix; /* Suffix. */ + struct substring neg_suffix; /* Negative suffix. */ + char decimal; /* Decimal point: '.' or ','. */ + char grouping; /* Grouping character: ',', '.', or 0. */ + }; + +struct fmt_number_style *fmt_number_style_create (void); +void fmt_number_style_destroy (struct fmt_number_style *); + +const struct fmt_number_style *fmt_get_style (enum fmt_type); +void fmt_set_style (enum fmt_type, struct fmt_number_style *); + +int fmt_affix_width (const struct fmt_number_style *); +int fmt_neg_affix_width (const struct fmt_number_style *); + +int fmt_decimal_char (enum fmt_type); +int fmt_grouping_char (enum fmt_type); +void fmt_set_decimal (char); + +/* Alignment of data for display. */ enum alignment { ALIGN_LEFT = 0, @@ -84,7 +152,7 @@ enum alignment n_ALIGN }; - +/* How data is measured. */ enum measure { MEASURE_NOMINAL=1, @@ -95,34 +163,9 @@ enum measure bool measure_is_valid(enum measure m); bool alignment_is_valid(enum alignment a); + +#include - -/* Descriptions of all the display formats above. */ -extern const struct fmt_desc formats[]; - -union value; - -/* Maximum length of formatted value, in characters. */ -#define MAX_FORMATTED_LEN 256 - -/* Common formats. */ -extern const struct fmt_spec f8_2; /* F8.2. */ - -int check_input_specifier (const struct fmt_spec *spec, int emit_error); -int check_output_specifier (const struct fmt_spec *spec, int emit_error); -bool check_specifier_type (const struct fmt_spec *, int type, bool emit_error); -bool check_specifier_width (const struct fmt_spec *, - int width, bool emit_error); -void convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output); -int get_format_var_width (const struct fmt_spec *); -int parse_string_as_format (const char *s, int len, const struct fmt_spec *fp, - int fc, union value *v); -int translate_fmt (int spss); bool data_out (char *s, const struct fmt_spec *fp, const union value *v); -bool fmt_type_from_string (const char *name, int *type); -char *fmt_to_string (const struct fmt_spec *); -struct fmt_spec make_input_format (int type, int w, int d); -struct fmt_spec make_output_format (int type, int w, int d); -bool fmt_is_binary (int type); -#endif /* !format_h */ +#endif /* format.h */ diff --git a/src/data/por-file-reader.c b/src/data/por-file-reader.c index fa3af602..4f087734 100644 --- a/src/data/por-file-reader.c +++ b/src/data/por-file-reader.c @@ -468,18 +468,25 @@ static void convert_format (struct pfm_reader *r, const int portable_format[3], struct fmt_spec *format, struct variable *v) { - format->type = translate_fmt (portable_format[0]); - if (format->type == -1) + bool ok; + + if (!fmt_from_io (portable_format[0], &format->type)) error (r, _("%s: Bad format specifier byte (%d)."), v->name, portable_format[0]); format->w = portable_format[1]; format->d = portable_format[2]; - if (!check_output_specifier (format, false) - || !check_specifier_width (format, v->width, false)) - error (r, _("%s variable %s has invalid format specifier %s."), - v->type == NUMERIC ? _("Numeric") : _("String"), - v->name, fmt_to_string (format)); + msg_disable (); + ok = fmt_check_output (format) && fmt_check_width_compat (format, v->width); + msg_enable (); + + if (!ok) + { + char fmt_string[FMT_STRING_LEN_MAX + 1]; + error (r, _("%s variable %s has invalid format specifier %s."), + v->type == NUMERIC ? _("Numeric") : _("String"), + v->name, fmt_to_string (format, fmt_string)); + } } static union value parse_value (struct pfm_reader *, struct variable *); diff --git a/src/data/por-file-writer.c b/src/data/por-file-writer.c index 202d4c28..b144dee9 100644 --- a/src/data/por-file-writer.c +++ b/src/data/por-file-writer.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -282,7 +282,7 @@ write_version_data (struct pfm_writer *w) static void write_format (struct pfm_writer *w, struct fmt_spec *f) { - write_int (w, formats[f->type].spss); + write_int (w, fmt_to_io (f->type)); write_int (w, f->w); write_int (w, f->d); } diff --git a/src/data/settings.c b/src/data/settings.c index 37fda600..844fdde3 100644 --- a/src/data/settings.c +++ b/src/data/settings.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -33,9 +33,6 @@ static bool long_view = false; static bool safer_mode = false; -static char decimal = '.'; -static char grouping = ','; - static bool echo = false; static bool include = true; @@ -64,16 +61,6 @@ static size_t workspace = 4L * 1024 * 1024; static struct fmt_spec default_format = {FMT_F, 8, 2}; -#define CC_INITIALIZER {"-", "", "", "", '.', ','} -static struct custom_currency cc[CC_CNT] = - { - CC_INITIALIZER, - CC_INITIALIZER, - CC_INITIALIZER, - CC_INITIALIZER, - CC_INITIALIZER, - }; - static bool testing_mode = false; static int global_algorithm = ENHANCED; @@ -192,41 +179,6 @@ set_safer_mode (void) safer_mode = true; } -/* The character used for a decimal point: ',' or '.'. Only - respected for data input and output. */ -char -get_decimal (void) -{ - return decimal; -} - -/* Sets the character used for a decimal point, which must be - either ',' or '.'. */ -void -set_decimal (char decimal_) -{ - assert (decimal_ == '.' || decimal_ == ','); - decimal = decimal_; -} - -/* The character used for grouping in numbers: '.' or ','; the - opposite of set_decimal. Only used in COMMA data input and - output. */ -char -get_grouping (void) -{ - return grouping; -} - -/* Sets the character used for grouping, which must be either ',' - or '.'. */ -void -set_grouping (char grouping_) -{ - assert (grouping_ == '.' || grouping_ == ','); - grouping = grouping_; -} - /* Echo commands to the listing file/printer? */ bool get_echo (void) @@ -466,22 +418,6 @@ set_format (const struct fmt_spec *default_format_) default_format = *default_format_; } -/* Gets the custom currency specification with the given IDX. */ -const struct custom_currency * -get_cc (int idx) -{ - assert (idx >= 0 && idx < CC_CNT); - return &cc[idx]; -} - -/* Gets custom currency specification IDX to CC. */ -void -set_cc (int idx, const struct custom_currency *cc_) -{ - assert (idx >= 0 && idx < CC_CNT); - cc[idx] = *cc_; -} - /* Are we in testing mode? (e.g. --testing-mode command line option) */ bool diff --git a/src/data/settings.h b/src/data/settings.h index 4bdc65d1..b9fc73ae 100644 --- a/src/data/settings.h +++ b/src/data/settings.h @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -45,11 +45,6 @@ void set_viewwidth (int); bool get_safer_mode (void); void set_safer_mode (void); -char get_decimal (void); -void set_decimal (char); -char get_grouping (void); -void set_grouping (char); - bool get_echo (void); void set_echo (bool); bool get_include (void); @@ -94,24 +89,6 @@ void set_workspace (size_t); const struct fmt_spec *get_format (void); void set_format (const struct fmt_spec *); -/* Maximum number of custom currency specifications */ -#define CC_CNT 5 - -/* One custom currency specification. */ -#define CC_WIDTH 16 -struct custom_currency - { - char neg_prefix[CC_WIDTH]; /* Negative prefix. */ - char prefix[CC_WIDTH]; /* Prefix. */ - char suffix[CC_WIDTH]; /* Suffix. */ - char neg_suffix[CC_WIDTH]; /* Negative suffix. */ - char decimal; /* Decimal point. */ - char grouping; /* Grouping character. */ - }; - -const struct custom_currency *get_cc (int idx); -void set_cc (int idx, const struct custom_currency *); - bool get_testing_mode (void); void set_testing_mode (bool); diff --git a/src/data/sys-file-reader.c b/src/data/sys-file-reader.c index 246448a1..24054393 100644 --- a/src/data/sys-file-reader.c +++ b/src/data/sys-file-reader.c @@ -1208,28 +1208,35 @@ static int parse_format_spec (struct sfm_reader *r, int32_t s, struct fmt_spec *f, const struct variable *v) { - f->type = translate_fmt ((s >> 16) & 0xff); - if (f->type == -1) + bool ok; + + if (!fmt_from_io ((s >> 16) & 0xff, &f->type)) lose ((ME, _("%s: Bad format specifier byte (%d)."), fh_get_file_name (r->fh), (s >> 16) & 0xff)); f->w = (s >> 8) & 0xff; f->d = s & 0xff; - if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0)) + if ((v->type == ALPHA) ^ (fmt_is_string (f->type) != 0)) lose ((ME, _("%s: %s variable %s has %s format specifier %s."), fh_get_file_name (r->fh), v->type == ALPHA ? _("String") : _("Numeric"), v->name, - formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"), - formats[f->type].name)); + fmt_is_string (f->type) ? _("string") : _("numeric"), + fmt_name (f->type))); - if (!check_output_specifier (f, false) - || !check_specifier_width (f, v->width, false)) + msg_disable (); + ok = fmt_check_output (f) && fmt_check_width_compat (f, v->width); + msg_enable (); + + if (!ok) { + char fmt_string[FMT_STRING_LEN_MAX + 1]; msg (ME, _("%s variable %s has invalid format specifier %s."), v->type == NUMERIC ? _("Numeric") : _("String"), - v->name, fmt_to_string (f)); - *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0); + v->name, fmt_to_string (f, fmt_string)); + *f = (v->type == NUMERIC + ? fmt_for_output (FMT_F, 8, 2) + : fmt_for_output (FMT_A, v->width, 0)); } return 1; diff --git a/src/data/sys-file-writer.c b/src/data/sys-file-writer.c index 1b18e1ed..37792101 100644 --- a/src/data/sys-file-writer.c +++ b/src/data/sys-file-writer.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -18,31 +18,35 @@ 02110-1301, USA. */ #include + #include "sys-file-writer.h" #include "sfm-private.h" -#include -#include + #include #include #include +#include #include #include #include + #include -#include "case.h" -#include "dictionary.h" -#include -#include "file-handle-def.h" #include #include +#include #include -#include "settings.h" -#include "stat-macros.h" #include +#include + +#include "case.h" +#include "dictionary.h" +#include "file-handle-def.h" +#include "settings.h" #include "value-labels.h" #include "variable.h" -#include -#include + +#include "stat-macros.h" +#include "minmax.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -437,8 +441,8 @@ write_header (struct sfm_writer *w, const struct dictionary *d) static inline void write_format_spec (const struct fmt_spec *src, int32_t *dest) { - assert(check_output_specifier(src, true)); - *dest = (formats[src->type].spss << 16) | (src->w << 8) | src->d; + assert (fmt_check_output (src)); + *dest = (fmt_to_io (src->type) << 16) | (src->w << 8) | src->d; } /* Write the variable record(s) for primary variable P and secondary diff --git a/src/language/data-io/ChangeLog b/src/language/data-io/ChangeLog index e9f90b5a..2fb050dc 100644 --- a/src/language/data-io/ChangeLog +++ b/src/language/data-io/ChangeLog @@ -1,3 +1,18 @@ +Tue Oct 31 20:04:06 2006 Ben Pfaff + + * placement-parser.c: (PRS_TYPE_T) Now that struct fmt_spec uses + an enum fmt_type for its type member, we can't depend on the + ability to put negative values into that member as out-of-band + values, because enum fmt_type might be an unsigned type. So use + values around SCHAR_MAX instead, because we know that SCHAR_MAX + will fit into any type, signed or unsigned, and there aren't + nearly that many format types. + (parse_var_placements) Add for_input parameter to specify whether + we're parsing input or output formats. Update all callers. + (fixed_parse_columns) Ditto. + (fixed_parse_fortran) Ditto. + + Tue Oct 31 18:21:48 2006 Ben Pfaff * print-space.c (print_space_trns_proc): Let dfm_put_record add diff --git a/src/language/data-io/data-list.c b/src/language/data-io/data-list.c index b541f56a..a1faffe4 100644 --- a/src/language/data-io/data-list.c +++ b/src/language/data-io/data-list.c @@ -302,7 +302,8 @@ parse_fixed (struct dictionary *dict, /* Parse everything. */ if (!parse_record_placement (&record, &column) || !parse_DATA_LIST_vars_pool (tmp_pool, &names, &name_cnt, PV_NONE) - || !parse_var_placements (tmp_pool, name_cnt, &formats, &format_cnt)) + || !parse_var_placements (tmp_pool, name_cnt, true, + &formats, &format_cnt)) return false; /* Create variables and var specs. */ @@ -318,13 +319,12 @@ parse_fixed (struct dictionary *dict, name = names[name_idx++]; /* Create variable. */ - width = get_format_var_width (f); + width = fmt_var_width (f); v = dict_create_var (dict, name, width); if (v != NULL) { /* Success. */ - struct fmt_spec output; - convert_fmt_ItoO (f, &output); + struct fmt_spec output = fmt_for_output_from_input (f); v->print = output; v->write = output; } @@ -415,11 +415,13 @@ dump_fixed_table (const struct ll_list *specs, row = 1; ll_for_each (spec, struct dls_var_spec, ll, specs) { + char fmt_string[FMT_STRING_LEN_MAX + 1]; tab_text (t, 0, row, TAB_LEFT, spec->name); tab_text (t, 1, row, TAT_PRINTF, "%d", spec->record); tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d", spec->first_column, spec->first_column + spec->input.w - 1); - tab_text (t, 3, row, TAB_LEFT | TAB_FIX, fmt_to_string (&spec->input)); + tab_text (t, 3, row, TAB_LEFT | TAB_FIX, + fmt_to_string (&spec->input, fmt_string)); row++; } @@ -451,15 +453,15 @@ parse_free (struct dictionary *dict, struct pool *tmp_pool, struct data_list_pgm if (lex_match ('(')) { if (!parse_format_specifier (&input) - || !check_input_specifier (&input, 1) + || !fmt_check_input (&input) || !lex_force_match (')')) return NULL; - convert_fmt_ItoO (&input, &output); + output = fmt_for_output_from_input (&input); } else { lex_match ('*'); - input = make_input_format (FMT_F, 8, 0); + input = fmt_for_input (FMT_F, 8, 0); output = *get_format (); } @@ -468,8 +470,7 @@ parse_free (struct dictionary *dict, struct pool *tmp_pool, struct data_list_pgm struct dls_var_spec *spec; struct variable *v; - v = dict_create_var (dict, name[i], - get_format_var_width (&input)); + v = dict_create_var (dict, name[i], fmt_var_width (&input)); if (v == NULL) { msg (SE, _("%s is a duplicate variable name."), name[i]); @@ -509,12 +510,13 @@ dump_free_table (const struct data_list_pgm *dls, tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, spec_cnt); tab_hline (t, TAL_2, 0, 1, 1); tab_dim (t, tab_natural_dimensions); - row = 1; ll_for_each (spec, struct dls_var_spec, ll, &dls->specs) { + char str[FMT_STRING_LEN_MAX + 1]; tab_text (t, 0, row, TAB_LEFT, spec->name); - tab_text (t, 1, row, TAB_LEFT | TAB_FIX, fmt_to_string (&spec->input)); + tab_text (t, 1, row, TAB_LEFT | TAB_FIX, + fmt_to_string (&spec->input, str)); row++; } @@ -740,7 +742,7 @@ read_from_data_list_list (const struct data_list_pgm *dls, struct ccase *c) spec->name); ll_for_each_continue (spec, struct dls_var_spec, ll, &dls->specs) { - int width = get_format_var_width (&spec->input); + int width = fmt_var_width (&spec->input); if (width == 0) case_data_rw (c, spec->fv)->f = SYSMIS; else diff --git a/src/language/data-io/get.c b/src/language/data-io/get.c index c7ba18a4..d79206d3 100644 --- a/src/language/data-io/get.c +++ b/src/language/data-io/get.c @@ -1089,7 +1089,7 @@ cmd_match_files (struct dataset *ds) goto error; } iter->in_var->print = iter->in_var->write - = make_output_format (FMT_F, 1, 0); + = fmt_for_output (FMT_F, 1, 0); } /* MATCH FILES performs an n-way merge on all its input files. diff --git a/src/language/data-io/list.q b/src/language/data-io/list.q index 8bb55106..b099a270 100644 --- a/src/language/data-io/list.q +++ b/src/language/data-io/list.q @@ -213,9 +213,9 @@ cmd_list (struct dataset *ds) strcpy (casenum_var.name, "Case#"); casenum_var.type = NUMERIC; casenum_var.fv = -1; - casenum_var.print = make_output_format (FMT_F, - (cmd.last == LONG_MAX - ? 5 : intlog10 (cmd.last)), 0); + casenum_var.print = fmt_for_output (FMT_F, + (cmd.last == LONG_MAX + ? 5 : intlog10 (cmd.last)), 0); /* Add the weight variable at the beginning of the variable list. */ cmd.n_variables++; @@ -664,7 +664,7 @@ list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds UN ds_put_char_multiple(&line_buffer, ' ', width - v->print.w); } - if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1) + if (fmt_is_string (v->print.type) || v->fv != -1) { data_out (ds_put_uninit(&line_buffer, v->print.w), &v->print, case_data (c, v->fv)); @@ -701,7 +701,7 @@ list_cases (const struct ccase *c, void *aux UNUSED, const struct dataset *ds UN struct variable *v = cmd.v_variables[column]; char buf[256]; - if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1) + if (fmt_is_string (v->print.type) || v->fv != -1) data_out (buf, &v->print, case_data (c, v->fv)); else { diff --git a/src/language/data-io/matrix-data.c b/src/language/data-io/matrix-data.c index a002e78d..5398cd69 100644 --- a/src/language/data-io/matrix-data.c +++ b/src/language/data-io/matrix-data.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -871,7 +871,7 @@ static int di.e = ss_end (s); di.v = (union value *) &token->number; di.f1 = dfm_get_column (reader, di.s); - di.format = make_output_format (FMT_F, token->length, 0); + di.format = fmt_for_output (FMT_F, token->length, 0); data_in (&di); } diff --git a/src/language/data-io/placement-parser.c b/src/language/data-io/placement-parser.c index b0c32936..82f9cd6c 100644 --- a/src/language/data-io/placement-parser.c +++ b/src/language/data-io/placement-parser.c @@ -39,14 +39,14 @@ placement. */ enum { - PRS_TYPE_T = -1, /* Tab to absolute column. */ - PRS_TYPE_X = -2, /* Skip columns. */ - PRS_TYPE_NEW_REC = -3 /* Next record. */ + PRS_TYPE_T = SCHAR_MAX - 3, /* Tab to absolute column. */ + PRS_TYPE_X, /* Skip columns. */ + PRS_TYPE_NEW_REC /* Next record. */ }; -static bool fixed_parse_columns (struct pool *, size_t var_cnt, +static bool fixed_parse_columns (struct pool *, size_t var_cnt, bool for_input, struct fmt_spec **, size_t *); -static bool fixed_parse_fortran (struct pool *, +static bool fixed_parse_fortran (struct pool *, bool for_input, struct fmt_spec **, size_t *); /* Parses Fortran-like or column-based specifications for placing @@ -65,23 +65,23 @@ static bool fixed_parse_fortran (struct pool *, Uses POOL for allocation. When the caller is finished interpreting *FORMATS, POOL may be destroyed. */ bool -parse_var_placements (struct pool *pool, size_t var_cnt, +parse_var_placements (struct pool *pool, size_t var_cnt, bool for_input, struct fmt_spec **formats, size_t *format_cnt) { assert (var_cnt > 0); if (lex_is_number ()) - return fixed_parse_columns (pool, var_cnt, formats, format_cnt); + return fixed_parse_columns (pool, var_cnt, for_input, formats, format_cnt); else if (lex_match ('(')) { size_t assignment_cnt; size_t i; - if (!fixed_parse_fortran (pool, formats, format_cnt)) + if (!fixed_parse_fortran (pool, for_input, formats, format_cnt)) return false; assignment_cnt = 0; for (i = 0; i < *format_cnt; i++) - assignment_cnt += (*formats)[i].type >= 0; + assignment_cnt += (*formats)[i].type < FMT_NUMBER_OF_FORMATS; if (assignment_cnt != var_cnt) { @@ -103,7 +103,7 @@ parse_var_placements (struct pool *pool, size_t var_cnt, /* Implements parse_var_placements for column-based formats. */ static bool -fixed_parse_columns (struct pool *pool, size_t var_cnt, +fixed_parse_columns (struct pool *pool, size_t var_cnt, bool for_input, struct fmt_spec **formats, size_t *format_cnt) { struct fmt_spec format; @@ -153,7 +153,7 @@ fixed_parse_columns (struct pool *pool, size_t var_cnt, format.type = FMT_F; format.d = 0; } - if (!check_input_specifier (&format, 1)) + if (!fmt_check (&format, for_input)) return false; *formats = pool_nalloc (pool, var_cnt + 1, sizeof **formats); @@ -167,7 +167,7 @@ fixed_parse_columns (struct pool *pool, size_t var_cnt, /* Implements parse_var_placements for Fortran-like formats. */ static bool -fixed_parse_fortran (struct pool *pool, +fixed_parse_fortran (struct pool *pool, bool for_input, struct fmt_spec **formats, size_t *format_cnt) { size_t formats_allocated = 0; @@ -195,7 +195,8 @@ fixed_parse_fortran (struct pool *pool, if (lex_match ('(')) { /* Call ourselves recursively to handle parentheses. */ - if (!fixed_parse_fortran (pool, &new_formats, &new_format_cnt)) + if (!fixed_parse_fortran (pool, for_input, + &new_formats, &new_format_cnt)) return false; } else @@ -221,12 +222,12 @@ fixed_parse_fortran (struct pool *pool, } else { - if (!fmt_type_from_string (type, &f.type)) + if (!fmt_from_name (type, &f.type)) { msg (SE, _("Unknown format type \"%s\"."), type); return false; } - if (!check_input_specifier (&f, 1)) + if (!fmt_check (&f, for_input)) return false; } } @@ -284,7 +285,7 @@ execute_placement_format (const struct fmt_spec *format, return true; default: - assert (format->type >= 0 && format->type < FMT_NUMBER_OF_FORMATS); + assert (format->type < FMT_NUMBER_OF_FORMATS); return false; } } diff --git a/src/language/data-io/placement-parser.h b/src/language/data-io/placement-parser.h index bb59a9ce..1204ceb8 100644 --- a/src/language/data-io/placement-parser.h +++ b/src/language/data-io/placement-parser.h @@ -27,7 +27,7 @@ struct fmt_spec; struct pool; bool parse_record_placement (int *record, int *column); -bool parse_var_placements (struct pool *, size_t var_cnt, +bool parse_var_placements (struct pool *, size_t var_cnt, bool for_input, struct fmt_spec **, size_t *format_cnt); bool execute_placement_format (const struct fmt_spec *, int *record, int *column); diff --git a/src/language/data-io/print.c b/src/language/data-io/print.c index 3c36b98b..2bc28a5d 100644 --- a/src/language/data-io/print.c +++ b/src/language/data-io/print.c @@ -317,7 +317,8 @@ parse_variable_argument (const struct dictionary *dict, if (lex_is_number () || token == '(') { - if (!parse_var_placements (tmp_pool, var_cnt, &formats, &format_cnt)) + if (!parse_var_placements (tmp_pool, var_cnt, false, + &formats, &format_cnt)) return false; add_space = false; } @@ -345,7 +346,7 @@ parse_variable_argument (const struct dictionary *dict, struct prt_out_spec *spec; var = vars[var_idx++]; - if (!check_specifier_width (f, var->width, true)) + if (!fmt_check_width_compat (f, var->width)) return false; spec = pool_alloc (trns->pool, sizeof *spec); @@ -362,7 +363,8 @@ parse_variable_argument (const struct dictionary *dict, that usually contains a period. */ spec->sysmis_as_spaces = (which_formats == WRITE && var->type == NUMERIC - && !fmt_is_binary (spec->format.type)); + && (fmt_get_category (spec->format.type) + != FMT_CAT_BINARY)); ll_push_tail (&trns->specs, &spec->ll); @@ -397,6 +399,7 @@ dump_table (struct print_trns *trns, const struct file_handle *fh) row = 1; ll_for_each (spec, struct prt_out_spec, ll, &trns->specs) { + char fmt_string[FMT_STRING_LEN_MAX + 1]; int width; switch (spec->type) { @@ -408,7 +411,7 @@ dump_table (struct print_trns *trns, const struct file_handle *fh) case PRT_VAR: tab_text (t, 0, row, TAB_LEFT, spec->var->name); tab_text (t, 3, row, TAB_LEFT | TAB_FIX, - fmt_to_string (&spec->format)); + fmt_to_string (&spec->format, fmt_string)); width = spec->format.w; break; default: diff --git a/src/language/dictionary/formats.c b/src/language/dictionary/formats.c index 995ee3fb..384e45a9 100644 --- a/src/language/dictionary/formats.c +++ b/src/language/dictionary/formats.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -93,8 +93,8 @@ internal_cmd_formats (struct dataset *ds, int which) goto fail; } if (!parse_format_specifier (&f) - || !check_output_specifier (&f, true) - || !check_specifier_type (&f, NUMERIC, true)) + || !fmt_check_output (&f) + || !fmt_check_type_compat (&f, NUMERIC)) goto fail; if (!lex_match (')')) diff --git a/src/language/dictionary/missing-values.c b/src/language/dictionary/missing-values.c index 5d121558..a330c46e 100644 --- a/src/language/dictionary/missing-values.c +++ b/src/language/dictionary/missing-values.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or diff --git a/src/language/dictionary/numeric.c b/src/language/dictionary/numeric.c index 5a1ab18d..b7a23315 100644 --- a/src/language/dictionary/numeric.c +++ b/src/language/dictionary/numeric.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -59,10 +59,11 @@ cmd_numeric (struct dataset *ds) { if (!parse_format_specifier (&f)) goto fail; - if (formats[f.type].cat & FCAT_STRING) + if (fmt_is_string (f.type)) { + char str[FMT_STRING_LEN_MAX + 1]; msg (SE, _("Format type %s may not be used with a numeric " - "variable."), fmt_to_string (&f)); + "variable."), fmt_to_string (&f, str)); goto fail; } @@ -129,10 +130,11 @@ cmd_string (struct dataset *ds) if (!lex_force_match ('(') || !parse_format_specifier (&f)) goto fail; - if (!(formats[f.type].cat & FCAT_STRING)) + if (!fmt_is_string (f.type)) { + char str[FMT_STRING_LEN_MAX + 1]; msg (SE, _("Format type %s may not be used with a string " - "variable."), fmt_to_string (&f)); + "variable."), fmt_to_string (&f, str)); goto fail; } @@ -142,17 +144,7 @@ cmd_string (struct dataset *ds) goto fail; } - switch (f.type) - { - case FMT_A: - width = f.w; - break; - case FMT_AHEX: - width = f.w / 2; - break; - default: - NOT_REACHED (); - } + width = fmt_var_width (&f); /* Create each variable. */ for (i = 0; i < nv; i++) diff --git a/src/language/dictionary/sys-file-info.c b/src/language/dictionary/sys-file-info.c index 5425e688..2a0439c2 100644 --- a/src/language/dictionary/sys-file-info.c +++ b/src/language/dictionary/sys-file-info.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -454,17 +454,19 @@ describe_variable (struct variable *v, struct tab_table *t, int r, int as) && v->print.w == v->write.w && v->print.d == v->write.d) { + char str[FMT_STRING_LEN_MAX + 1]; tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF, _("Format: %s"), - fmt_to_string (&v->print)); + fmt_to_string (&v->print, str)); r++; } else { + char str[FMT_STRING_LEN_MAX + 1]; tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF, - _("Print Format: %s"), fmt_to_string (&v->print)); + _("Print Format: %s"), fmt_to_string (&v->print, str)); r++; tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF, - _("Write Format: %s"), fmt_to_string (&v->write)); + _("Write Format: %s"), fmt_to_string (&v->write, str)); r++; } diff --git a/src/language/expressions/evaluate.c b/src/language/expressions/evaluate.c index 3ff879f2..c3807541 100644 --- a/src/language/expressions/evaluate.c +++ b/src/language/expressions/evaluate.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -304,9 +304,11 @@ expr_debug_print_postfix (const struct expression *e) op->string.string != NULL ? op->string.string : ""); break; case OP_format: - fprintf (stderr, "f<%s%d.%d>", - formats[op->format->type].name, - op->format->w, op->format->d); + { + char str[FMT_STRING_LEN_MAX + 1]; + fmt_to_string (op->format, str); + fprintf (stderr, "f<%s>", str); + } break; case OP_variable: fprintf (stderr, "v<%s>", op->variable->name); diff --git a/src/language/expressions/operations.def b/src/language/expressions/operations.def index e1ae2a53..303c1d5c 100644 --- a/src/language/expressions/operations.def +++ b/src/language/expressions/operations.def @@ -1,4 +1,23 @@ // -*- c -*- +// +// PSPP - computes sample statistics. +// Copyright (C) 2005, 2006 Free Software Foundation, Inc. +// Written by Ben Pfaff . +// +// 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 the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// 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., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. */ operator NEG (x) = -x; @@ -567,7 +586,7 @@ absorb_miss string function STRING (x, no_format f) v.f = x; dst = alloc_string (e, f->w); - assert ((formats[f->type].cat & FCAT_STRING) == 0); + assert (!fmt_is_string (f->type)); data_out (dst.string, f, &v); return dst; } diff --git a/src/language/expressions/parse.c b/src/language/expressions/parse.c index 0e7a17d5..43aa553d 100644 --- a/src/language/expressions/parse.c +++ b/src/language/expressions/parse.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -18,26 +18,29 @@ 02110-1301, USA. */ #include + #include "private.h" + #include #include #include #include -#include -#include + +#include "helpers.h" #include #include -#include -#include "helpers.h" +#include +#include #include #include #include +#include +#include #include +#include #include #include -#include #include -#include /* Declarations. */ @@ -351,25 +354,31 @@ type_coercion_core (struct expression *e, NOT_REACHED (); case OP_ni_format: + msg_disable (); if ((*node)->type == OP_format - && check_input_specifier (&(*node)->format.f, false) - && check_specifier_type (&(*node)->format.f, NUMERIC, false)) + && fmt_check_input (&(*node)->format.f) + && fmt_check_type_compat (&(*node)->format.f, NUMERIC)) { + msg_enable (); if (do_coercion) (*node)->type = OP_ni_format; return true; } + msg_enable (); break; case OP_no_format: + msg_disable (); if ((*node)->type == OP_format - && check_output_specifier (&(*node)->format.f, false) - && check_specifier_type (&(*node)->format.f, NUMERIC, false)) + && fmt_check_output (&(*node)->format.f) + && fmt_check_type_compat (&(*node)->format.f, NUMERIC)) { + msg_enable (); if (do_coercion) (*node)->type = OP_no_format; return true; } + msg_enable (); break; case OP_num_var: diff --git a/src/language/lexer/format-parser.c b/src/language/lexer/format-parser.c index 2541e5b5..939559cb 100644 --- a/src/language/lexer/format-parser.c +++ b/src/language/lexer/format-parser.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ #include -#include +#include "format-parser.h" #include #include @@ -27,6 +27,7 @@ #include "lexer.h" #include #include +#include #include #include #include @@ -105,7 +106,7 @@ parse_format_specifier (struct fmt_spec *format) if (!parse_abstract_format_specifier (type, &format->w, &format->d)) return false; - if (!fmt_type_from_string (type, &format->type)) + if (!fmt_from_name (type, &format->type)) { msg (SE, _("Unknown format type \"%s\"."), type); return false; @@ -117,14 +118,14 @@ parse_format_specifier (struct fmt_spec *format) /* Parses a token containing just the name of a format type and returns true if successful. */ bool -parse_format_specifier_name (int *type) +parse_format_specifier_name (enum fmt_type *type) { if (token != T_ID) { lex_error (_("expecting format type")); return false; } - if (!fmt_type_from_string (ds_cstr (&tokstr), type)) + if (!fmt_from_name (ds_cstr (&tokstr), type)) { msg (SE, _("Unknown format type \"%s\"."), ds_cstr (&tokstr)); return false; diff --git a/src/language/lexer/format-parser.h b/src/language/lexer/format-parser.h index 981a5485..046f57ab 100644 --- a/src/language/lexer/format-parser.h +++ b/src/language/lexer/format-parser.h @@ -29,6 +29,6 @@ struct fmt_spec; bool parse_abstract_format_specifier (char type[FMT_TYPE_LEN_MAX + 1], int *width, int *decimals); bool parse_format_specifier (struct fmt_spec *); -bool parse_format_specifier_name (int *type); +bool parse_format_specifier_name (enum fmt_type *type); #endif /* language/lexer/format-parser.h. */ diff --git a/src/language/lexer/range-parser.c b/src/language/lexer/range-parser.c index 2934bc8b..1682935c 100644 --- a/src/language/lexer/range-parser.c +++ b/src/language/lexer/range-parser.c @@ -1,3 +1,22 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ + #include #include "range-parser.h" #include diff --git a/src/language/stats/aggregate.c b/src/language/stats/aggregate.c index 2b1ae5a0..006936d7 100644 --- a/src/language/stats/aggregate.c +++ b/src/language/stats/aggregate.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -583,21 +583,24 @@ parse_aggregate_functions (const struct dictionary *dict, struct agr_proc *agr) destvar = dict_create_var (agr->dict, dest[i], 0); if (destvar != NULL) { + struct fmt_spec f; if ((func_index == N || func_index == NMISS) && dict_get_weight (dict) != NULL) - destvar->print = destvar->write = f8_2; + f = fmt_for_output (FMT_F, 8, 2); else - destvar->print = destvar->write = function->format; + f = function->format; + destvar->print = destvar->write = f; } } } else { + struct fmt_spec f; v->src = NULL; destvar = dict_create_var (agr->dict, dest[i], 0); - if (func_index == N_NO_VARS - && dict_get_weight (dict) != NULL) - destvar->print = destvar->write = f8_2; + if (func_index == N_NO_VARS && dict_get_weight (dict) != NULL) + f = fmt_for_output (FMT_F, 8, 2); else - destvar->print = destvar->write = function->format; + f = function->format; + destvar->print = destvar->write = f; } if (!destvar) diff --git a/src/language/stats/crosstabs.q b/src/language/stats/crosstabs.q index becc2edb..cf76b35b 100644 --- a/src/language/stats/crosstabs.q +++ b/src/language/stats/crosstabs.q @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -3186,7 +3186,7 @@ format_short (char *s, const struct fmt_spec *fp, const union value *v) struct fmt_spec fmt_subst; /* Limit to short string width. */ - if (formats[fp->type].cat & FCAT_STRING) + if (fmt_is_string (fp->type)) { fmt_subst = *fp; diff --git a/src/language/utilities/ChangeLog b/src/language/utilities/ChangeLog index eafc6885..23b9b19d 100644 --- a/src/language/utilities/ChangeLog +++ b/src/language/utilities/ChangeLog @@ -1,3 +1,13 @@ +Tue Oct 31 20:10:24 2006 Ben Pfaff + + * set.q (cmd_set): Drop the `ok' variable, which didn't do + anything useful. + (extract_cc_token) Adapt to new fmt_number_style. + (do_cc) Ditto. + (format_cc) Ditto. + (show_cc) Change parameter to be an enum fmt_type. Adjust all + callers. + Wed Apr 26 15:06:22 2006 Ben Pfaff * set.q: Use SN instead of MN for most output from SHOW, because diff --git a/src/language/utilities/set.q b/src/language/utilities/set.q index a7e6e2f0..25f09eb7 100644 --- a/src/language/utilities/set.q +++ b/src/language/utilities/set.q @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -115,29 +115,28 @@ int tgetnum (const char *); /* (declarations) */ -/* (_functions) */ +/* (functions) */ -static bool do_cc (const char *cc_string, int idx); +static bool do_cc (const char *cc_string, enum fmt_type); int cmd_set (struct dataset *ds) { struct cmd_set cmd; - bool ok = true; if (!parse_set (ds, &cmd, NULL)) return CMD_FAILURE; if (cmd.sbc_cca) - ok = ok && do_cc (cmd.s_cca, 0); + do_cc (cmd.s_cca, FMT_CCA); if (cmd.sbc_ccb) - ok = ok && do_cc (cmd.s_ccb, 1); + do_cc (cmd.s_ccb, FMT_CCB); if (cmd.sbc_ccc) - ok = ok && do_cc (cmd.s_ccc, 2); + do_cc (cmd.s_ccc, FMT_CCC); if (cmd.sbc_ccd) - ok = ok && do_cc (cmd.s_ccd, 3); + do_cc (cmd.s_ccd, FMT_CCD); if (cmd.sbc_cce) - ok = ok && do_cc (cmd.s_cce, 4); + do_cc (cmd.s_cce, FMT_CCE); if (cmd.sbc_prompt) getl_set_prompt (GETL_PROMPT_FIRST, cmd.s_prompt); @@ -147,7 +146,7 @@ cmd_set (struct dataset *ds) getl_set_prompt (GETL_PROMPT_DATA, cmd.s_dprompt); if (cmd.sbc_decimal) - set_decimal (cmd.dec == STC_DOT ? '.' : ','); + fmt_set_decimal (cmd.dec == STC_DOT ? '.' : ','); if (cmd.sbc_echo) set_echo (cmd.echo == STC_ON); if (cmd.sbc_endcmd) @@ -203,7 +202,7 @@ cmd_set (struct dataset *ds) grouping and decimal members appropriately. Returns true if successful, false otherwise. */ static bool -find_cc_separators (const char *cc_string, struct custom_currency *cc) +find_cc_separators (const char *cc_string, struct fmt_number_style *cc) { const char *sp; int comma_cnt, dot_cnt; @@ -236,23 +235,23 @@ find_cc_separators (const char *cc_string, struct custom_currency *cc) return true; } -/* Extracts a token from IN into TOKEn. Tokens are delimited by - GROUPING. The token is truncated to at most CC_WIDTH - characters (including null terminator). Returns the first - character following the token. */ +/* Extracts a token from IN into a newly allocated AFFIX. Tokens + are delimited by GROUPING. The token is truncated to at most + FMT_STYLE_AFFIX_MAX characters. Returns the first character + following the token. */ static const char * -extract_cc_token (const char *in, int grouping, char token[CC_WIDTH]) +extract_cc_token (const char *in, int grouping, struct substring *affix) { - char *out = token; - + size_t ofs = 0; + ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX); for (; *in != '\0' && *in != grouping; in++) { if (*in == '\'' && in[1] == grouping) in++; - if (out < &token[CC_WIDTH - 1]) - *out++ = *in; + if (ofs < FMT_STYLE_AFFIX_MAX) + ss_data (*affix)[ofs++] = *in; } - *out = '\0'; + affix->length = ofs; if (*in == grouping) in++; @@ -262,25 +261,26 @@ extract_cc_token (const char *in, int grouping, char token[CC_WIDTH]) /* Sets custom currency specifier CC having name CC_NAME ('A' through 'E') to correspond to the settings in CC_STRING. */ static bool -do_cc (const char *cc_string, int idx) +do_cc (const char *cc_string, enum fmt_type type) { - struct custom_currency cc; + struct fmt_number_style *cc = fmt_number_style_create (); /* Determine separators. */ - if (!find_cc_separators (cc_string, &cc)) + if (!find_cc_separators (cc_string, cc)) { - msg (SE, _("CC%c: Custom currency string `%s' does not contain " - "exactly three periods or commas (not both)."), - "ABCDE"[idx], cc_string); + fmt_number_style_destroy (cc); + msg (SE, _("%s: Custom currency string `%s' does not contain " + "exactly three periods or commas (or it contains both)."), + fmt_name (type), cc_string); return false; } - cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_prefix); - cc_string = extract_cc_token (cc_string, cc.grouping, cc.prefix); - cc_string = extract_cc_token (cc_string, cc.grouping, cc.suffix); - cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_suffix); + cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix); + cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix); + cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix); + cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix); - set_cc (idx, &cc); + fmt_set_style (type, cc); return true; } @@ -413,11 +413,12 @@ stc_custom_format (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void * lex_match ('='); if (!parse_format_specifier (&fmt)) return 0; - if ((formats[fmt.type].cat & FCAT_STRING) != 0) + if (fmt_is_string (fmt.type)) { + char str[FMT_STRING_LEN_MAX + 1]; msg (SE, _("FORMAT requires numeric output format as an argument. " "Specified format %s is of type string."), - fmt_to_string (&fmt)); + fmt_to_string (&fmt, str)); return 0; } @@ -479,22 +480,25 @@ show_blanks (const struct dataset *ds UNUSED) } static char * -format_cc (const char *in, char grouping, char *out) +format_cc (struct substring in, char grouping, char *out) { - while (*in != '\0') + while (!ss_is_empty (in)) { - if (*in == grouping || *in == '\'') + char c = ss_get_char (&in); + if (c == grouping || c == '\'') *out++ = '\''; - *out++ = *in++; + else if (c == '"') + *out++ = '"'; + *out++ = c; } return out; } static void -show_cc (int idx) +show_cc (enum fmt_type type) { - const struct custom_currency *cc = get_cc (idx); - char cc_string[CC_WIDTH * 4 * 2 + 3 + 1]; + const struct fmt_number_style *cc = fmt_get_style (type); + char cc_string[FMT_STYLE_AFFIX_MAX * 4 * 2 + 3 + 1]; char *out; out = format_cc (cc->neg_prefix, cc->grouping, cc_string); @@ -506,44 +510,43 @@ show_cc (int idx) out = format_cc (cc->neg_suffix, cc->grouping, out); *out = '\0'; - msg (SN, _("CC%c is \"%s\"."), "ABCDE"[idx], cc_string); + msg (SN, _("%s is \"%s\"."), fmt_name (type), cc_string); } - static void show_cca (const struct dataset *ds UNUSED) { - show_cc (0); + show_cc (FMT_CCA); } static void show_ccb (const struct dataset *ds UNUSED) { - show_cc (1); + show_cc (FMT_CCB); } static void show_ccc (const struct dataset *ds UNUSED) { - show_cc (2); + show_cc (FMT_CCC); } static void show_ccd (const struct dataset *ds UNUSED) { - show_cc (3); + show_cc (FMT_CCD); } static void show_cce (const struct dataset *ds UNUSED) { - show_cc (4); + show_cc (FMT_CCE); } static void show_decimals (const struct dataset *ds UNUSED) { - msg (SN, _("DECIMAL is \"%c\"."), get_decimal ()); + msg (SN, _("DECIMAL is \"%c\"."), fmt_decimal_char (FMT_F)); } static void @@ -555,7 +558,8 @@ show_endcmd (const struct dataset *ds UNUSED) static void show_format (const struct dataset *ds UNUSED) { - msg (SN, _("FORMAT is %s."), fmt_to_string (get_format ())); + char str[FMT_STRING_LEN_MAX + 1]; + msg (SN, _("FORMAT is %s."), fmt_to_string (get_format (), str)); } static void diff --git a/src/output/table.c b/src/output/table.c index be2c2136..029fd5d0 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -18,22 +18,27 @@ 02110-1301, USA. */ #include + #include "table.h" + #include #include #include #include + +#include "output.h" +#include "manager.h" + +#include +#include #include #include #include -#include #include #include -#include "minmax.h" -#include "output.h" #include -#include "manager.h" -#include + +#include "minmax.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -558,7 +563,7 @@ tab_float (struct tab_table *table, int c, int r, unsigned char opt, assert (r >= 0); assert (r < table->nr); - f = make_output_format (FMT_F, w, d); + f = fmt_for_output (FMT_F, w, d); #if DEBUGGING if (c + table->col_ofs < 0 || r + table->row_ofs < 0 diff --git a/src/ui/gui/psppire-var-store.c b/src/ui/gui/psppire-var-store.c index cf38b60d..eb97401e 100644 --- a/src/ui/gui/psppire-var-store.c +++ b/src/ui/gui/psppire-var-store.c @@ -504,9 +504,12 @@ text_for_column(const struct PsppireVariable *pv, gint c, GError **err) case FMT_A: return g_locale_to_utf8(gettext(type_label[VT_STRING]), -1, 0, 0, err); break; - default: - g_warning("Unknown format: \"%s\"\n", - fmt_to_string(write_spec)); + default: + { + char str[FMT_STRING_LEN_MAX + 1]; + g_warning("Unknown format: \"%s\"\n", + fmt_to_string(write_spec, str)); + } break; } } diff --git a/src/ui/gui/psppire-variable.c b/src/ui/gui/psppire-variable.c index 5c884a3f..0b0c2491 100644 --- a/src/ui/gui/psppire-variable.c +++ b/src/ui/gui/psppire-variable.c @@ -1,6 +1,6 @@ /* PSPPIRE --- A Graphical User Interface for PSPP - Copyright (C) 2004 Free Software Foundation + Copyright (C) 2004, 2006 Free Software Foundation Written by John Darrington This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -175,17 +176,17 @@ psppire_variable_set_format(struct PsppireVariable *pv, struct fmt_spec *fmt) g_return_val_if_fail(pv->dict, FALSE); g_return_val_if_fail(pv->v, FALSE); - if ( check_output_specifier(fmt, false) - && - check_specifier_type(fmt, pv->v->type, false) - && - check_specifier_width(fmt, pv->v->width, false) - ) + msg_disable (); + if ( fmt_check_output(fmt) + && fmt_check_type_compat (fmt, pv->v->type) + && fmt_check_width_compat (fmt, pv->v->width)) { + msg_enable (); pv->v->write = pv->v->print = *fmt; psppire_dict_var_changed(pv->dict, pv->v->index); return TRUE; } + msg_enable (); return FALSE; } diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c index 1f510e24..8ae97f15 100644 --- a/src/ui/gui/psppire.c +++ b/src/ui/gui/psppire.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -114,6 +115,7 @@ main(int argc, char *argv[]) + fmt_init(); settings_init(); message_dialog_init(); diff --git a/src/ui/gui/var-type-dialog.c b/src/ui/gui/var-type-dialog.c index 304ed371..12180433 100644 --- a/src/ui/gui/var-type-dialog.c +++ b/src/ui/gui/var-type-dialog.c @@ -1,6 +1,6 @@ /* PSPPIRE --- A Graphical User Interface for PSPP - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2006 Free Software Foundation Written by John Darrington This program is free software; you can redistribute it and/or modify @@ -37,6 +37,7 @@ #include #include +#include struct tgs @@ -291,7 +292,8 @@ preview_custom(GtkWidget *w, gpointer data) text = gtk_entry_get_text(GTK_ENTRY(dialog->entry_width)); dialog->fmt_l.w = atoi(text); - if ( ! check_output_specifier(&dialog->fmt_l, 0)) + msg_disable (); + if ( ! fmt_check_output(&dialog->fmt_l)) { gtk_label_set_text(GTK_LABEL(dialog->label_psample), "---"); gtk_label_set_text(GTK_LABEL(dialog->label_nsample), "---"); @@ -311,6 +313,7 @@ preview_custom(GtkWidget *w, gpointer data) gtk_label_set_text(GTK_LABEL(dialog->label_nsample), sample_text); g_free(sample_text); } + msg_enable (); } /* Callback for when a treeview row is changed. @@ -539,10 +542,11 @@ var_type_dialog_create(GladeXML *xml) list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); - for ( i = 0 ; i < CC_CNT ; ++i ) + for ( i = 0 ; i < 5 ; ++i ) { + enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE}; gchar text[4]; - g_snprintf(text, 4, "CC%c", 'A' + i); + g_snprintf(text, 4, "%s", fmt_name (cc_fmts[i])); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, text, @@ -636,9 +640,11 @@ select_treeview_from_format(GtkTreeView *treeview, const struct fmt_spec *fmt) gtk_tree_view_set_cursor(treeview, path, 0, 0); gtk_tree_path_free(path); } - else - g_warning("Unusual date format: %s\n", fmt_to_string(fmt)); - + else + { + char str[FMT_STRING_LEN_MAX + 1]; + g_warning("Unusual date format: %s\n", fmt_to_string(fmt, str)); + } } @@ -788,7 +794,7 @@ make_output_format_try (struct fmt_spec *f, int type, int w, int d) f->type = type; f->w = w; f->d = d; - return check_output_specifier (f, true); + return fmt_check_output (f); } @@ -838,7 +844,7 @@ on_var_type_ok_clicked(GtkWidget *w, gpointer data) break; case BUTTON_DATE: case BUTTON_CUSTOM: - g_assert(check_output_specifier(&dialog->fmt_l, TRUE)); + g_assert(fmt_check_output(&dialog->fmt_l)); result = memcpy(&spec, &dialog->fmt_l, sizeof(struct fmt_spec)); break; case BUTTON_DOLLAR: diff --git a/src/ui/terminal/main.c b/src/ui/terminal/main.c index 6a7decd9..6998f773 100644 --- a/src/ui/terminal/main.c +++ b/src/ui/terminal/main.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,7 @@ main (int argc, char **argv) fpu_init (); gsl_set_error_handler_off (); + fmt_init (); outp_init (); msg_ui_init (); fn_init ();