pspp-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Pspp-cvs] pspp ChangeLog Smake src/data/ChangeLog src/dat...


From: Ben Pfaff
Subject: [Pspp-cvs] pspp ChangeLog Smake src/data/ChangeLog src/dat...
Date: Fri, 03 Nov 2006 04:53:52 +0000

CVSROOT:        /cvsroot/pspp
Module name:    pspp
Changes by:     Ben Pfaff <blp> 06/11/03 04:53:52

Modified files:
        .              : ChangeLog Smake 
        src/data       : ChangeLog data-in.c data-out.c dictionary.c 
                         format.c format.def format.h por-file-reader.c 
                         por-file-writer.c settings.c settings.h 
                         sys-file-reader.c sys-file-writer.c 
        src/language/data-io: ChangeLog data-list.c get.c list.q 
                              matrix-data.c placement-parser.c 
                              placement-parser.h print.c 
        src/language/dictionary: formats.c missing-values.c numeric.c 
                                 sys-file-info.c 
        src/language/expressions: evaluate.c operations.def parse.c 
        src/language/lexer: format-parser.c format-parser.h 
                            range-parser.c 
        src/language/stats: aggregate.c crosstabs.q 
        src/language/utilities: ChangeLog set.q 
        src/output     : table.c 
        src/ui/gui     : psppire-var-store.c psppire-variable.c 
                         psppire.c var-type-dialog.c 
        src/ui/terminal: main.c 

Log message:
        Completely rewrite src/data/format.[ch], to achieve better
        abstraction.  Rewrite all references to formats in other files.
        See patch #5511.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/pspp/ChangeLog?cvsroot=pspp&r1=1.64&r2=1.65
http://cvs.savannah.gnu.org/viewcvs/pspp/Smake?cvsroot=pspp&r1=1.39&r2=1.40
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/ChangeLog?cvsroot=pspp&r1=1.69&r2=1.70
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/data-in.c?cvsroot=pspp&r1=1.15&r2=1.16
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/data-out.c?cvsroot=pspp&r1=1.8&r2=1.9
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/dictionary.c?cvsroot=pspp&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/format.c?cvsroot=pspp&r1=1.10&r2=1.11
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/format.def?cvsroot=pspp&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/format.h?cvsroot=pspp&r1=1.7&r2=1.8
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/por-file-reader.c?cvsroot=pspp&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/por-file-writer.c?cvsroot=pspp&r1=1.4&r2=1.5
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/settings.c?cvsroot=pspp&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/settings.h?cvsroot=pspp&r1=1.1&r2=1.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/sys-file-reader.c?cvsroot=pspp&r1=1.24&r2=1.25
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/sys-file-writer.c?cvsroot=pspp&r1=1.14&r2=1.15
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/ChangeLog?cvsroot=pspp&r1=1.30&r2=1.31
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/data-list.c?cvsroot=pspp&r1=1.22&r2=1.23
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/get.c?cvsroot=pspp&r1=1.19&r2=1.20
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/list.q?cvsroot=pspp&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/matrix-data.c?cvsroot=pspp&r1=1.15&r2=1.16
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/placement-parser.c?cvsroot=pspp&r1=1.1&r2=1.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/placement-parser.h?cvsroot=pspp&r1=1.1&r2=1.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/data-io/print.c?cvsroot=pspp&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/dictionary/formats.c?cvsroot=pspp&r1=1.11&r2=1.12
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/dictionary/missing-values.c?cvsroot=pspp&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/dictionary/numeric.c?cvsroot=pspp&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/dictionary/sys-file-info.c?cvsroot=pspp&r1=1.12&r2=1.13
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/expressions/evaluate.c?cvsroot=pspp&r1=1.8&r2=1.9
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/expressions/operations.def?cvsroot=pspp&r1=1.6&r2=1.7
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/expressions/parse.c?cvsroot=pspp&r1=1.16&r2=1.17
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/lexer/format-parser.c?cvsroot=pspp&r1=1.5&r2=1.6
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/lexer/format-parser.h?cvsroot=pspp&r1=1.1&r2=1.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/lexer/range-parser.c?cvsroot=pspp&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/stats/aggregate.c?cvsroot=pspp&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/stats/crosstabs.q?cvsroot=pspp&r1=1.19&r2=1.20
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/utilities/ChangeLog?cvsroot=pspp&r1=1.3&r2=1.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/utilities/set.q?cvsroot=pspp&r1=1.15&r2=1.16
http://cvs.savannah.gnu.org/viewcvs/pspp/src/output/table.c?cvsroot=pspp&r1=1.14&r2=1.15
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire-var-store.c?cvsroot=pspp&r1=1.12&r2=1.13
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire-variable.c?cvsroot=pspp&r1=1.6&r2=1.7
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire.c?cvsroot=pspp&r1=1.18&r2=1.19
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/var-type-dialog.c?cvsroot=pspp&r1=1.5&r2=1.6
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/terminal/main.c?cvsroot=pspp&r1=1.17&r2=1.18

Patches:
Index: ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/ChangeLog,v
retrieving revision 1.64
retrieving revision 1.65
diff -u -b -r1.64 -r1.65
--- ChangeLog   1 Nov 2006 03:33:01 -0000       1.64
+++ ChangeLog   3 Nov 2006 04:53:51 -0000       1.65
@@ -1,3 +1,7 @@
+Tue Oct 31 19:55:52 2006  Ben Pfaff  <address@hidden>
+
+       * Smake (GNULIB_MODULES): Add `mempcpy' module.
+
 Tue Oct 31 19:29:05 2006  Ben Pfaff  <address@hidden>
 
        * configure.ac: Drop tests for strchr, strrchr because now we

Index: Smake
===================================================================
RCS file: /cvsroot/pspp/pspp/Smake,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -b -r1.39 -r1.40
--- Smake       29 Oct 2006 22:09:41 -0000      1.39
+++ Smake       3 Nov 2006 04:53:51 -0000       1.40
@@ -27,6 +27,7 @@
        memcmp \
        memmem \
        memmove \
+       mempcpy \
        memset \
        progname \
        readlink \

Index: src/data/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/ChangeLog,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -b -r1.69 -r1.70
--- src/data/ChangeLog  1 Nov 2006 03:36:09 -0000       1.69
+++ src/data/ChangeLog  3 Nov 2006 04:53:51 -0000       1.70
@@ -1,3 +1,33 @@
+Tue Oct 31 19:58:27 2006  Ben Pfaff  <address@hidden>
+
+       * 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  <address@hidden>
+
+       * 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  <address@hidden>
 
        * data-out.c: (num_to_string) Removed, because it was dead code.

Index: src/data/data-in.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/data-in.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -b -r1.15 -r1.16
--- src/data/data-in.c  1 Nov 2006 02:12:25 -0000       1.15
+++ src/data/data-in.c  3 Nov 2006 04:53:51 -0000       1.16
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
 
 #include <config.h>
 #include "data-in.h"
-#include <libpspp/message.h>
+#include <libpspp/assertion.h>
 #include <math.h>
 #include <ctype.h>
 #include <stdarg.h>
@@ -50,6 +50,7 @@
 {
   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 @@
     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;
@@ -149,16 +151,8 @@
   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 @@
 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 @@
 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 @@
   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;
+                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 @@
        }
     }
   
-  {
-    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);
+  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. */

Index: src/data/data-out.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/data-out.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -b -r1.8 -r1.9
--- src/data/data-out.c 1 Nov 2006 03:36:10 -0000       1.8
+++ src/data/data-out.c 3 Nov 2006 04:53:51 -0000       1.9
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -58,17 +58,17 @@
 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 @@
         }
 
       /* 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 @@
 
   /* 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 @@
       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 @@
 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 @@
 
   /* 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 @@
   /* 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 @@
     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);

Index: src/data/dictionary.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/dictionary.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- src/data/dictionary.c       29 Oct 2006 09:51:35 -0000      1.17
+++ src/data/dictionary.c       3 Nov 2006 04:53:51 -0000       1.18
@@ -287,14 +287,14 @@
   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;

Index: src/data/format.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/format.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -b -r1.10 -r1.11
--- src/data/format.c   29 Oct 2006 00:01:52 -0000      1.10
+++ src/data/format.c   3 Nov 2006 04:53:51 -0000       1.11
@@ -18,406 +18,901 @@
    02110-1301, USA. */
 
 #include <config.h>
+
 #include "format.h"
+
 #include <ctype.h>
-#include <libpspp/assertion.h>
-#include <libpspp/message.h>
 #include <stdlib.h>
+
+#include <data/identifier.h>
+#include <data/settings.h>
+#include <data/variable.h>
+#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
-#include "identifier.h"
 #include <libpspp/str.h>
-#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) 
-{
-  int i;
+static int max_digits_for_bytes (int bytes);
 
-  for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
-    if (!strcasecmp (name, formats[i].name))
+/* Initialize the format module. */
+void
+fmt_init (void)
+{
+  static bool inited = false;
+  if (!inited)
       {
-        *type = i;
-        return true;
+      inited = true;
+      fmt_set_decimal ('.');
       }
-  return false;
 }
 
-/* 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)
+/* Deinitialize the format module. */
+void
+fmt_done (void)
 {
-  static char buf[32];
+}
 
-  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 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)
+{
+  struct fmt_spec f;
+  f.type = type;
+  f.w = w;
+  f.d = d;
+  assert (fmt_check_input (&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 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)
 {
-  const struct fmt_desc *f ; 
-  char *str;
+  struct fmt_spec f;
+  f.type = type;
+  f.w = w;
+  f.d = d;
+  assert (fmt_check_output (&f));
+  return f;
+}
+
+/* Returns the output format specifier corresponding to input
+   format specifier INPUT. */
+struct fmt_spec
+fmt_for_output_from_input (const struct fmt_spec *input)
+{
+  struct fmt_spec output;
 
-  assert (spec->type < FMT_NUMBER_OF_FORMATS);
-  f = &formats[spec->type];
-  str = fmt_to_string (spec);
+  assert (fmt_check_input (input));
+
+  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;
+
+  switch (input->type)
+    {
+    case FMT_Z:
+      output.w++;
+      if (output.d > 0)
+       output.w++;
+      break;
 
-  if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
+    case FMT_F:
+    case FMT_COMMA:
+    case FMT_DOT:
+    case FMT_DOLLAR:
+    case FMT_PCT:
     {
-      if (emit_error)
-        msg (SE, _("Format %s specifies an odd width %d, but "
-                   "an even width is required."),
-             str, spec->w);
+        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 (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))
+    {
+      msg (SE, _("Format %s may not be used for input."), str);
       return false;
     }
-  if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
+
+  if (spec->w % fmt_step_width (spec->type))
+    {
+      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;
+    }
+
+  min_w = min_width (spec->type, for_input);
+  max_w = max_width (spec->type);
+  if (spec->w < min_w || spec->w > max_w)
+    {
+      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;
+    }
+
+  max_d = max_decimals (spec->type, spec->w, for_input);
+  if (!fmt_takes_decimals (spec->type) && spec->d != 0)
     {
-      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, 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 (f->n_args <= 1 && spec->d) 
+  else if (spec->d > max_d)
     {
-      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);
+      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 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)
+   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;
-
-  f = &formats[spec->type];
-  str = fmt_to_string (spec);
-  if (f->cat & FCAT_OUTPUT_ONLY)
-    {
-      if (emit_error)
-        msg (SE, _("Format %s may not be used for input."), f->name);
-      return 0;
-    }
-  if (spec->w < f->Imin_w || spec->w > f->Imax_w)
-    {
-      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;
-    }
-  if ((spec->type == FMT_F || spec->type == FMT_COMMA
-         || spec->type == FMT_DOLLAR)
-      && spec->d > spec->w)
-    {
-      if (emit_error)
-        msg (SE, _("Input format %s is invalid because it specifies more "
-                   "decimal places than the field width."), str);
-      return 0;
-    }
-  return 1;
+  return fmt_check (spec, 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)
+   true if so.  Otherwise, emits an error message and returns false. */
+bool
+fmt_check_output (const struct fmt_spec *spec)
 {
-  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 (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;
+  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)
+      char str[FMT_STRING_LEN_MAX + 1];
         msg (SE, _("%s variables are not compatible with %s format %s."),
-             type == ALPHA ? _("String") : _("Numeric"),
-             type == ALPHA ? _("numeric") : _("string"),
-             fmt_to_string (format));
+           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 "
+      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));
+           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;
-      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 */
+      max_d = for_input ? width : width - 2;
       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;
+  return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
+}
   
-  for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
-    if (formats[type].spss == spss)
-      return type;
-  return -1;
+/* Returns the decimal point character for the given format
+   TYPE. */
+int
+fmt_decimal_char (enum fmt_type type)
+{
+  return fmt_get_style (type)->decimal;
 }
 
-/* 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 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_input_specifier (&f, 0));
-  return f;
+  return fmt_get_style (type)->grouping;
 }
 
-/* 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)
+/* 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)
 {
-  struct fmt_spec f;
-  f.type = type;
-  f.w = w;
-  f.d = d;
-  assert (check_output_specifier (&f, 0));
-  return f;
+  struct fmt_number_style *style;
+
+  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;
 }
 
-/* Returns true if TYPE is a binary format,
-   false otherwise. */
-bool
-fmt_is_binary (int type)
+/* 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)
 {
-  switch (type) 
-    {
-    case FMT_P:
-    case FMT_PK:
-    case FMT_IB:
-    case FMT_PIB:
-    case FMT_RB:
-      return true;
+  assert (is_fmt_type (type));
+  if (styles[type] == NULL)
+    set_style (type, prefix, suffix, decimal, grouping);
+}
 
-    default:
-      return false;
-    }
+/* Sets the decimal point character to DECIMAL. */
+void
+fmt_set_decimal (char decimal)
+{
+  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)
+measure_is_valid (enum measure m)
 {
-  if ( m <= 0 ) return false;
-  if ( m >= n_MEASURES) return false;
-  return true;
+  return m > 0 && m < n_MEASURES;
 }
 
+/* Returns true if A is a valid alignment,
+   false otherwise. */
 bool 
-alignment_is_valid(enum alignment a)
+alignment_is_valid (enum alignment a)
 {
-  if ( a >= n_ALIGN) return false;
-  return true;
+  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];
 }

Index: src/data/format.def
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/format.def,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- src/data/format.def 18 Jul 2006 04:57:01 -0000      1.3
+++ src/data/format.def 3 Nov 2006 04:53:51 -0000       1.4
@@ -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 <address@hidden>.
 
    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

Index: src/data/format.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/format.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -b -r1.7 -r1.8
--- src/data/format.h   1 Nov 2006 03:36:10 -0000       1.7
+++ src/data/format.h   3 Nov 2006 04:53:51 -0000       1.8
@@ -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 <address@hidden>.
 
    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 <stdbool.h>
+#include <stddef.h>
+#include <libpspp/str.h>
 
-/* 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 @@
     n_ALIGN
   };
 
-
+/* How data is measured. */
 enum measure
   {
     MEASURE_NOMINAL=1,
@@ -96,33 +164,8 @@
 bool measure_is_valid(enum measure m);
 bool alignment_is_valid(enum alignment a);
 
+#include <libpspp/legacy-encoding.h>
 
-/* 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 */

Index: src/data/por-file-reader.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/por-file-reader.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- src/data/por-file-reader.c  9 Jun 2006 22:51:23 -0000       1.11
+++ src/data/por-file-reader.c  3 Nov 2006 04:53:51 -0000       1.12
@@ -468,18 +468,25 @@
 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))
+  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));
+             v->name, fmt_to_string (format, fmt_string)); 
+    }
 }
 
 static union value parse_value (struct pfm_reader *, struct variable *);

Index: src/data/por-file-writer.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/por-file-writer.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -b -r1.4 -r1.5
--- src/data/por-file-writer.c  17 Apr 2006 01:54:15 -0000      1.4
+++ src/data/por-file-writer.c  3 Nov 2006 04:53:51 -0000       1.5
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -282,7 +282,7 @@
 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);
 }

Index: src/data/settings.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/settings.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- src/data/settings.c 30 May 2006 12:01:33 -0000      1.3
+++ src/data/settings.c 3 Nov 2006 04:53:51 -0000       1.4
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -33,9 +33,6 @@
 
 static bool safer_mode = false;
 
-static char decimal = '.';
-static char grouping = ',';
-
 static bool echo = false;
 static bool include = true;
 
@@ -64,16 +61,6 @@
 
 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 @@
   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 @@
   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

Index: src/data/settings.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/settings.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- src/data/settings.h 4 Mar 2006 01:11:57 -0000       1.1
+++ src/data/settings.h 3 Nov 2006 04:53:51 -0000       1.2
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -45,11 +45,6 @@
 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 @@
 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);
 

Index: src/data/sys-file-reader.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/sys-file-reader.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -b -r1.24 -r1.25
--- src/data/sys-file-reader.c  29 Oct 2006 09:51:36 -0000      1.24
+++ src/data/sys-file-reader.c  3 Nov 2006 04:53:51 -0000       1.25
@@ -1208,28 +1208,35 @@
 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)));
+
+  msg_disable ();
+  ok = fmt_check_output (f) && fmt_check_width_compat (f, v->width);
+  msg_enable ();
 
-  if (!check_output_specifier (f, false)
-      || !check_specifier_width (f, v->width, false)) 
+  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;
 

Index: src/data/sys-file-writer.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/sys-file-writer.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -b -r1.14 -r1.15
--- src/data/sys-file-writer.c  29 Oct 2006 11:16:07 -0000      1.14
+++ src/data/sys-file-writer.c  3 Nov 2006 04:53:51 -0000       1.15
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -18,31 +18,35 @@
    02110-1301, USA. */
 
 #include <config.h>
+
 #include "sys-file-writer.h"
 #include "sfm-private.h"
-#include <libpspp/message.h>
-#include <stdlib.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
+
 #include <libpspp/alloc.h>
-#include "case.h"
-#include "dictionary.h"
-#include <libpspp/message.h>
-#include "file-handle-def.h"
 #include <libpspp/hash.h>
 #include <libpspp/magic.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
-#include "settings.h"
-#include "stat-macros.h"
 #include <libpspp/str.h>
+#include <libpspp/version.h>
+
+#include "case.h"
+#include "dictionary.h"
+#include "file-handle-def.h"
+#include "settings.h"
 #include "value-labels.h"
 #include "variable.h"
-#include <libpspp/version.h>
-#include <minmax.h>
+
+#include "stat-macros.h"
+#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -437,8 +441,8 @@
 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

Index: src/language/data-io/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/ChangeLog,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -b -r1.30 -r1.31
--- src/language/data-io/ChangeLog      1 Nov 2006 02:22:35 -0000       1.30
+++ src/language/data-io/ChangeLog      3 Nov 2006 04:53:51 -0000       1.31
@@ -1,3 +1,18 @@
+Tue Oct 31 20:04:06 2006  Ben Pfaff  <address@hidden>
+
+       * 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  <address@hidden>
 
        * print-space.c (print_space_trns_proc): Let dfm_put_record add

Index: src/language/data-io/data-list.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/data-list.c,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -b -r1.22 -r1.23
--- src/language/data-io/data-list.c    26 Oct 2006 06:16:36 -0000      1.22
+++ src/language/data-io/data-list.c    3 Nov 2006 04:53:51 -0000       1.23
@@ -302,7 +302,8 @@
       /* 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 @@
             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 @@
   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 @@
       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 @@
           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 @@
   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 @@
                 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

Index: src/language/data-io/get.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/get.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -b -r1.19 -r1.20
--- src/language/data-io/get.c  26 Oct 2006 06:16:36 -0000      1.19
+++ src/language/data-io/get.c  3 Nov 2006 04:53:51 -0000       1.20
@@ -1089,7 +1089,7 @@
             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.

Index: src/language/data-io/list.q
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/list.q,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- src/language/data-io/list.q 26 Oct 2006 06:16:36 -0000      1.17
+++ src/language/data-io/list.q 3 Nov 2006 04:53:51 -0000       1.18
@@ -213,7 +213,7 @@
       strcpy (casenum_var.name, "Case#");
       casenum_var.type = NUMERIC;
       casenum_var.fv = -1;
-      casenum_var.print = make_output_format (FMT_F,
+      casenum_var.print = fmt_for_output (FMT_F,
                                               (cmd.last == LONG_MAX
                                                ? 5 : intlog10 (cmd.last)), 0);
 
@@ -664,7 +664,7 @@
                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 @@
            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 
               {

Index: src/language/data-io/matrix-data.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/matrix-data.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -b -r1.15 -r1.16
--- src/language/data-io/matrix-data.c  29 Oct 2006 09:51:36 -0000      1.15
+++ src/language/data-io/matrix-data.c  3 Nov 2006 04:53:51 -0000       1.16
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -871,7 +871,7 @@
          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);
        }

Index: src/language/data-io/placement-parser.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/placement-parser.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- src/language/data-io/placement-parser.c     18 Jul 2006 04:57:01 -0000      
1.1
+++ src/language/data-io/placement-parser.c     3 Nov 2006 04:53:51 -0000       
1.2
@@ -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 @@
    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 @@
 
 /* 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 @@
       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 @@
 
 /* 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 @@
       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 @@
                 }
               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 @@
       return true;
 
     default:
-      assert (format->type >= 0 && format->type < FMT_NUMBER_OF_FORMATS);
+      assert (format->type < FMT_NUMBER_OF_FORMATS);
       return false;
     }
 }

Index: src/language/data-io/placement-parser.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/placement-parser.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- src/language/data-io/placement-parser.h     18 Jul 2006 04:57:01 -0000      
1.1
+++ src/language/data-io/placement-parser.h     3 Nov 2006 04:53:51 -0000       
1.2
@@ -27,7 +27,7 @@
 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);

Index: src/language/data-io/print.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/print.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- src/language/data-io/print.c        29 Oct 2006 00:01:53 -0000      1.17
+++ src/language/data-io/print.c        3 Nov 2006 04:53:51 -0000       1.18
@@ -317,7 +317,8 @@
 
   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 @@
         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 @@
            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 @@
   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 @@
         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:

Index: src/language/dictionary/formats.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/dictionary/formats.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- src/language/dictionary/formats.c   26 Oct 2006 06:16:36 -0000      1.11
+++ src/language/dictionary/formats.c   3 Nov 2006 04:53:51 -0000       1.12
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -93,8 +93,8 @@
          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 (')'))

Index: src/language/dictionary/missing-values.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/dictionary/missing-values.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- src/language/dictionary/missing-values.c    26 Oct 2006 06:16:36 -0000      
1.9
+++ src/language/dictionary/missing-values.c    3 Nov 2006 04:53:51 -0000       
1.10
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or

Index: src/language/dictionary/numeric.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/dictionary/numeric.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- src/language/dictionary/numeric.c   26 Oct 2006 06:16:36 -0000      1.13
+++ src/language/dictionary/numeric.c   3 Nov 2006 04:53:51 -0000       1.14
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -59,10 +59,11 @@
        {
          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 @@
 
       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 @@
          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++)

Index: src/language/dictionary/sys-file-info.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/dictionary/sys-file-info.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -b -r1.12 -r1.13
--- src/language/dictionary/sys-file-info.c     2 Nov 2006 04:51:41 -0000       
1.12
+++ src/language/dictionary/sys-file-info.c     3 Nov 2006 04:53:51 -0000       
1.13
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -454,17 +454,19 @@
       && 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++;
     }
 

Index: src/language/expressions/evaluate.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/expressions/evaluate.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -b -r1.8 -r1.9
--- src/language/expressions/evaluate.c 26 Oct 2006 06:16:36 -0000      1.8
+++ src/language/expressions/evaluate.c 3 Nov 2006 04:53:51 -0000       1.9
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -304,9 +304,11 @@
                    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);

Index: src/language/expressions/operations.def
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/expressions/operations.def,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -b -r1.6 -r1.7
--- src/language/expressions/operations.def     26 Oct 2006 06:16:36 -0000      
1.6
+++ src/language/expressions/operations.def     3 Nov 2006 04:53:51 -0000       
1.7
@@ -1,4 +1,23 @@
 // -*- c -*-
+//
+// PSPP - computes sample statistics.
+// Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+// Written by Ben Pfaff <address@hidden>.
+// 
+// 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 @@
 
   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;
 }

Index: src/language/expressions/parse.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/expressions/parse.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -b -r1.16 -r1.17
--- src/language/expressions/parse.c    26 Oct 2006 06:16:36 -0000      1.16
+++ src/language/expressions/parse.c    3 Nov 2006 04:53:51 -0000       1.17
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -18,26 +18,29 @@
    02110-1301, USA. */
 
 #include <config.h>
+
 #include "private.h"
+
 #include <ctype.h>
 #include <float.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <libpspp/array.h>
-#include <libpspp/alloc.h>
+
+#include "helpers.h"
 #include <data/case.h>
 #include <data/dictionary.h>
-#include <libpspp/message.h>
-#include "helpers.h"
+#include <data/settings.h>
+#include <data/variable.h>
 #include <language/lexer/format-parser.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
+#include <libpspp/alloc.h>
+#include <libpspp/array.h>
 #include <libpspp/assertion.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
-#include <data/settings.h>
 #include <libpspp/str.h>
-#include <data/variable.h>
 
 /* Declarations. */
 
@@ -351,25 +354,31 @@
       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:

Index: src/language/lexer/format-parser.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/lexer/format-parser.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- src/language/lexer/format-parser.c  18 Jul 2006 04:57:01 -0000      1.5
+++ src/language/lexer/format-parser.c  3 Nov 2006 04:53:51 -0000       1.6
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
 
 #include <config.h>
 
-#include <language/lexer/format-parser.h>
+#include "format-parser.h"
 
 #include <ctype.h>
 #include <stdlib.h>
@@ -27,6 +27,7 @@
 #include "lexer.h"
 #include <data/format.h>
 #include <data/variable.h>
+#include <language/lexer/format-parser.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
@@ -105,7 +106,7 @@
   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 @@
 /* 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;

Index: src/language/lexer/format-parser.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/lexer/format-parser.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- src/language/lexer/format-parser.h  18 Jul 2006 04:57:01 -0000      1.1
+++ src/language/lexer/format-parser.h  3 Nov 2006 04:53:51 -0000       1.2
@@ -29,6 +29,6 @@
 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. */

Index: src/language/lexer/range-parser.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/lexer/range-parser.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- src/language/lexer/range-parser.c   1 Nov 2006 02:12:25 -0000       1.3
+++ src/language/lexer/range-parser.c   3 Nov 2006 04:53:51 -0000       1.4
@@ -1,3 +1,22 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <address@hidden>.
+
+   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 <config.h>
 #include "range-parser.h"
 #include <stdbool.h>

Index: src/language/stats/aggregate.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/stats/aggregate.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- src/language/stats/aggregate.c      26 Oct 2006 06:16:36 -0000      1.17
+++ src/language/stats/aggregate.c      3 Nov 2006 04:53:51 -0000       1.18
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -583,21 +583,24 @@
                     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)

Index: src/language/stats/crosstabs.q
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/stats/crosstabs.q,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -b -r1.19 -r1.20
--- src/language/stats/crosstabs.q      29 Oct 2006 09:51:36 -0000      1.19
+++ src/language/stats/crosstabs.q      3 Nov 2006 04:53:51 -0000       1.20
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -3186,7 +3186,7 @@
   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;
 

Index: src/language/utilities/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/utilities/ChangeLog,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- src/language/utilities/ChangeLog    26 Apr 2006 22:06:48 -0000      1.3
+++ src/language/utilities/ChangeLog    3 Nov 2006 04:53:51 -0000       1.4
@@ -1,3 +1,13 @@
+Tue Oct 31 20:10:24 2006  Ben Pfaff  <address@hidden>
+
+       * 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  <address@hidden>
 
        * set.q: Use SN instead of MN for most output from SHOW, because

Index: src/language/utilities/set.q
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/utilities/set.q,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -b -r1.15 -r1.16
--- src/language/utilities/set.q        1 Nov 2006 02:12:25 -0000       1.15
+++ src/language/utilities/set.q        3 Nov 2006 04:53:51 -0000       1.16
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -115,29 +115,28 @@
 
 /* (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 @@
     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 @@
    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 @@
   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 @@
 /* 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 @@
   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 @@
 }
 
 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 @@
   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 @@
 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

Index: src/output/table.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/output/table.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -b -r1.14 -r1.15
--- src/output/table.c  22 Oct 2006 11:07:52 -0000      1.14
+++ src/output/table.c  3 Nov 2006 04:53:52 -0000       1.15
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -18,22 +18,27 @@
    02110-1301, USA. */
 
 #include <config.h>
+
 #include "table.h"
+
 #include <ctype.h>
 #include <stdarg.h>
 #include <limits.h>
 #include <stdlib.h>
+
+#include "output.h"
+#include "manager.h"
+
+#include <data/format.h>
+#include <data/variable.h>
 #include <libpspp/alloc.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
-#include <data/format.h>
 #include <libpspp/magic.h>
 #include <libpspp/misc.h>
-#include "minmax.h"
-#include "output.h"
 #include <libpspp/pool.h>
-#include "manager.h"
-#include <data/variable.h>
+
+#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -558,7 +563,7 @@
   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

Index: src/ui/gui/psppire-var-store.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-var-store.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -b -r1.12 -r1.13
--- src/ui/gui/psppire-var-store.c      19 Jun 2006 10:16:51 -0000      1.12
+++ src/ui/gui/psppire-var-store.c      3 Nov 2006 04:53:52 -0000       1.13
@@ -505,8 +505,11 @@
            return g_locale_to_utf8(gettext(type_label[VT_STRING]), -1, 0, 0, 
err);
            break;
          default:
+            {
+              char str[FMT_STRING_LEN_MAX + 1];
            g_warning("Unknown format: \"%s\"\n", 
-                     fmt_to_string(write_spec));
+                        fmt_to_string(write_spec, str)); 
+            }
            break;
          }
       }

Index: src/ui/gui/psppire-variable.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-variable.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -b -r1.6 -r1.7
--- src/ui/gui/psppire-variable.c       17 Jul 2006 10:45:43 -0000      1.6
+++ src/ui/gui/psppire-variable.c       3 Nov 2006 04:53:52 -0000       1.7
@@ -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 <data/missing-values.h>
 #include <data/value-labels.h>
 #include <data/format.h>
+#include <libpspp/message.h>
 
 #include <libpspp/misc.h>
 
@@ -175,17 +176,17 @@
   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;
 }

Index: src/ui/gui/psppire.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -b -r1.18 -r1.19
--- src/ui/gui/psppire.c        1 Nov 2006 03:25:36 -0000       1.18
+++ src/ui/gui/psppire.c        3 Nov 2006 04:53:52 -0000       1.19
@@ -25,6 +25,7 @@
 
 #include <libpspp/version.h>
 #include <libpspp/copyleft.h>
+#include <data/format.h>
 #include <data/settings.h>
 
 #include <getopt.h>
@@ -114,6 +115,7 @@
 
 
 
+  fmt_init();
   settings_init();
 
   message_dialog_init();

Index: src/ui/gui/var-type-dialog.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/var-type-dialog.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- src/ui/gui/var-type-dialog.c        1 Nov 2006 03:25:36 -0000       1.5
+++ src/ui/gui/var-type-dialog.c        3 Nov 2006 04:53:52 -0000       1.6
@@ -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 <data/variable.h>
 #include <data/settings.h>
+#include <libpspp/message.h>
 
 
 struct tgs
@@ -291,7 +292,8 @@
   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 @@
       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 @@
   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,
@@ -637,8 +641,10 @@
       gtk_tree_path_free(path);
     }
   else
-    g_warning("Unusual date format: %s\n", fmt_to_string(fmt));
-
+    {
+      char str[FMT_STRING_LEN_MAX + 1];
+      g_warning("Unusual date format: %s\n", fmt_to_string(fmt, str));
+    }
 }
 
 
@@ -788,7 +794,7 @@
   f->type = type;
   f->w = w;
   f->d = d;
-  return check_output_specifier (f, true);
+  return fmt_check_output (f);
 }
 
 
@@ -838,7 +844,7 @@
        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:

Index: src/ui/terminal/main.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/terminal/main.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- src/ui/terminal/main.c      28 Oct 2006 08:31:23 -0000      1.17
+++ src/ui/terminal/main.c      3 Nov 2006 04:53:52 -0000       1.18
@@ -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 <address@hidden>.
 
    This program is free software; you can redistribute it and/or
@@ -30,6 +30,7 @@
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include <data/file-name.h>
+#include <data/format.h>
 #include <data/procedure.h>
 #include <data/settings.h>
 #include <data/variable.h>
@@ -87,6 +88,7 @@
   fpu_init ();
   gsl_set_error_handler_off ();
 
+  fmt_init ();
   outp_init ();
   msg_ui_init ();
   fn_init ();




reply via email to

[Prev in Thread] Current Thread [Next in Thread]