? format.patch
? p4
Index: ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/ChangeLog,v
retrieving revision 1.64
diff -u -p -r1.64 ChangeLog
--- ChangeLog	1 Nov 2006 03:33:01 -0000	1.64
+++ ChangeLog	1 Nov 2006 04:17:13 -0000
@@ -1,3 +1,7 @@
+Tue Oct 31 19:55:52 2006  Ben Pfaff  <blp@gnu.org>
+
+	* Smake (GNULIB_MODULES): Add `mempcpy' module.
+
 Tue Oct 31 19:29:05 2006  Ben Pfaff  <blp@gnu.org>
 
 	* configure.ac: Drop tests for strchr, strrchr because now we
Index: Smake
===================================================================
RCS file: /cvsroot/pspp/pspp/Smake,v
retrieving revision 1.39
diff -u -p -r1.39 Smake
--- Smake	29 Oct 2006 22:09:41 -0000	1.39
+++ Smake	1 Nov 2006 04:17:13 -0000
@@ -27,6 +27,7 @@ GNULIB_MODULES = \
 	memcmp \
 	memmem \
 	memmove \
+	mempcpy \
 	memset \
 	progname \
 	readlink \
Index: src/data/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/ChangeLog,v
retrieving revision 1.69
diff -u -p -r1.69 ChangeLog
--- src/data/ChangeLog	1 Nov 2006 03:36:09 -0000	1.69
+++ src/data/ChangeLog	1 Nov 2006 04:17:13 -0000
@@ -1,3 +1,33 @@
+Tue Oct 31 19:58:27 2006  Ben Pfaff  <blp@gnu.org>
+
+	* 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  <blp@gnu.org>
+
+	* 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  <blp@gnu.org>
 
 	* 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
diff -u -p -r1.15 data-in.c
--- src/data/data-in.c	1 Nov 2006 02:12:25 -0000	1.15
+++ src/data/data-in.c	1 Nov 2006 04:17:14 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ vdls_error (const struct data_in *i, con
 {
   struct msg m;
   struct string text;
+  char format_string[FMT_STRING_LEN_MAX + 1];
 
   if (i->flags & DI_IGNORE_ERROR)
     return;
@@ -59,7 +60,8 @@ vdls_error (const struct data_in *i, con
     ds_put_format (&text, _("(column %d"), i->f1);
   else
     ds_put_format (&text, _("(columns %d-%d"), i->f1, i->f2);
-  ds_put_format (&text, _(", field type %s) "), fmt_to_string (&i->format));
+  ds_put_format (&text, _(", field type %s) "),
+                 fmt_to_string (&i->format, format_string));
   ds_put_vformat (&text, format, args);
 
   m.category = MSG_DATA;
@@ -148,17 +150,9 @@ parse_numeric (struct data_in *i)
     }
   else
     sign = 1;
-  
-  if (type != FMT_DOT)
-    {
-      decimal = get_decimal();
-      grouping = get_grouping();
-    }
-  else
-    {
-      decimal = get_grouping();
-      grouping = get_decimal();
-    }
+
+  decimal = fmt_decimal_char (type);
+  grouping = fmt_grouping_char (type);
 
   i->v->f = SYSMIS;
   num = 0.0;
@@ -1342,10 +1336,8 @@ parse_MONTH (struct data_in *i)
 static void
 default_result (struct data_in *i)
 {
-  const struct fmt_desc *const fmt = &formats[i->format.type];
-
   /* Default to SYSMIS or blanks. */
-  if (fmt->cat & FCAT_STRING)
+  if (fmt_is_string (i->format.type))
     memset (i->v->s, ' ', i->format.w);
   else
     i->v->f = get_blanks();
@@ -1354,9 +1346,9 @@ default_result (struct data_in *i)
 bool
 data_in (struct data_in *i)
 {
-  const struct fmt_desc *const fmt = &formats[i->format.type];
+  bool success;
 
-  assert (check_input_specifier (&i->format, 0));
+  assert (fmt_check_input (&i->format));
 
   /* Check that we've got a string to work with. */
   if (i->e == i->s || i->format.w <= 0)
@@ -1368,15 +1360,16 @@ data_in (struct data_in *i)
   i->f2 = i->f1 + (i->e - i->s) - 1;
 
   /* Make sure that the string isn't too long. */
-  if (i->format.w > fmt->Imax_w)
+  if (i->format.w > fmt_max_input_width (i->format.type))
     {
       dls_error (i, _("Field too long (%d characters).  Truncated after "
-		   "character %d."),
-		 i->format.w, fmt->Imax_w);
-      i->format.w = fmt->Imax_w;
+                      "character %d."),
+		 i->format.w, fmt_max_input_width (i->format.type));
+      i->format.w = fmt_max_input_width (i->format.type);
     }
 
-  if (fmt->cat & FCAT_BLANKS_SYSMIS)
+  if (!(fmt_get_category (i->format.type)
+        & (FMT_CAT_STRING | FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL)))
     {
       const char *cp;
 
@@ -1394,33 +1387,102 @@ data_in (struct data_in *i)
 	}
     }
   
-  {
-    typedef bool (*handler_t) (struct data_in *);
-    static const handler_t handlers[FMT_NUMBER_OF_FORMATS] = 
-      {
-	parse_numeric, parse_N, parse_numeric, parse_numeric,
-	parse_numeric, parse_numeric, parse_numeric,
-	parse_Z, parse_A, parse_AHEX, parse_IB, parse_P, parse_PIB,
-	parse_PIBHEX, parse_PK, parse_RB, parse_RBHEX,
-	NULL, NULL, NULL, NULL, NULL,
-	parse_DATE, parse_EDATE, parse_SDATE, parse_ADATE, parse_JDATE,
-	parse_QYR, parse_MOYR, parse_WKYR,
-	parse_DATETIME, parse_TIME, parse_DTIME,
-	parse_WKDAY, parse_MONTH,
-      };
 
-    handler_t handler;
-    bool success;
-
-    handler = handlers[i->format.type];
-    assert (handler != NULL);
-
-    success = handler (i);
-    if (!success)
-      default_result (i);
+  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;
-  }
+  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
diff -u -p -r1.8 data-out.c
--- src/data/data-out.c	1 Nov 2006 03:36:10 -0000	1.8
+++ src/data/data-out.c	1 Nov 2006 04:17:15 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -58,17 +58,17 @@ static string_converter convert_A, conve
 bool
 data_out (char *s, const struct fmt_spec *fp, const union value *v)
 {
-  int cat = formats[fp->type].cat;
   int ok;
 
-  assert (check_output_specifier (fp, 0));
-  if (!(cat & FCAT_STRING)) 
+  assert (fmt_check_output (fp));
+  if (fmt_is_numeric (fp->type)) 
     {
-      /* Numeric formatting. */
+      enum fmt_category category = fmt_get_category (fp->type);
       double number = v->f;
 
       /* Handle SYSMIS turning into blanks. */
-      if ((cat & FCAT_BLANKS_SYSMIS) && number == SYSMIS)
+      if (!(category & (FMT_CAT_CUSTOM | FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL))
+          && number == SYSMIS)
         {
           memset (s, ' ', fp->w);
           s[fp->w - fp->d - 1] = '.';
@@ -76,7 +76,9 @@ data_out (char *s, const struct fmt_spec
         }
 
       /* Handle decimal shift. */
-      if ((cat & FCAT_SHIFT_DECIMAL) && number != SYSMIS && fp->d)
+      if ((category & (FMT_CAT_LEGACY | FMT_CAT_BINARY))
+          && number != SYSMIS
+          && fp->d)
         number *= pow (10.0, fp->d);
 
       switch (fp->type) 
@@ -310,12 +312,11 @@ convert_E (char *dst, const struct fmt_s
 
   /* The C locale always uses a period `.' as a decimal point.
      Translate to comma if necessary. */
-  if ((get_decimal() == ',' && fp->type != FMT_DOT)
-      || (get_decimal() == '.' && fp->type == FMT_DOT))
+  if (fmt_decimal_char (fp->type) != '.')
     {
       char *cp = strchr (buf, '.');
       if (cp)
-	*cp = ',';
+	*cp = fmt_decimal_char (fp->type);
     }
 
   memcpy (dst, buf, fp->w);
@@ -925,7 +926,7 @@ insert_commas (char *dst, const char *sr
       if (i % 3 == 0 && n_digits > i && n_items > n_reserved)
 	{
 	  n_items--;
-	  *dst++ = fp->type == FMT_COMMA ? get_grouping() : get_decimal();
+	  *dst++ = fmt_grouping_char (fp->type);
 	}
       *dst++ = *sp++;
     }
@@ -951,7 +952,7 @@ year4 (int year)
 static int
 try_CCx (char *dst, const struct fmt_spec *fp, double number)
 {
-  const struct custom_currency *cc = get_cc(fp->type - FMT_CCA);
+  const struct fmt_number_style *style = fmt_get_style (fp->type);
 
   struct fmt_spec f;
 
@@ -961,10 +962,10 @@ try_CCx (char *dst, const struct fmt_spe
 
   /* Determine length available, decimal character for number
      proper. */
-  f.type = cc->decimal == get_decimal () ? FMT_COMMA : FMT_DOT;
-  f.w = fp->w - strlen (cc->prefix) - strlen (cc->suffix);
+  f.type = style->decimal == fmt_decimal_char (FMT_COMMA) ? FMT_COMMA : FMT_DOT;
+  f.w = fp->w - fmt_affix_width (style);
   if (number < 0)
-    f.w -= strlen (cc->neg_prefix) + strlen (cc->neg_suffix) - 1;
+    f.w -= fmt_neg_affix_width (style) - 1;
   else
     /* Convert -0 to +0. */
     number = fabs (number);
@@ -982,8 +983,9 @@ try_CCx (char *dst, const struct fmt_spe
   /* Postprocess back into buf. */
   cp = buf;
   if (number < 0)
-    cp = stpcpy (cp, cc->neg_prefix);
-  cp = stpcpy (cp, cc->prefix);
+    cp = mempcpy (cp, ss_data (style->neg_prefix),
+                  ss_length (style->neg_prefix));
+  cp = mempcpy (cp, ss_data (style->prefix), ss_length (style->prefix));
   {
     char *bp = buf2;
     while (*bp == ' ')
@@ -996,9 +998,10 @@ try_CCx (char *dst, const struct fmt_spe
     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
diff -u -p -r1.17 dictionary.c
--- src/data/dictionary.c	29 Oct 2006 09:51:35 -0000	1.17
+++ src/data/dictionary.c	1 Nov 2006 04:17:16 -0000
@@ -287,14 +287,14 @@ dict_create_var (struct dictionary *d, c
   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
diff -u -p -r1.10 format.c
--- src/data/format.c	29 Oct 2006 00:01:52 -0000	1.10
+++ src/data/format.c	1 Nov 2006 04:17:16 -0000
@@ -18,406 +18,892 @@
    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] =
+static bool is_fmt_type (enum fmt_type);
+
+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);
+
+static int max_digits_for_bytes (int bytes);
+
+/* Initialize the format module. */
+void
+fmt_init (void)
 {
-#include "format.def"
-  {"",         -1, -1,  -1, -1,   -1, 0000, -1, -1},
-};
+  static bool inited = false;
+  if (!inited)
+    {
+      inited = true;
+      fmt_set_decimal ('.');
+    }
+}
 
-/* Common formats. */
-const struct fmt_spec f8_2 = {FMT_F, 8, 2};
+/* Deinitialize the format module. */
+void
+fmt_done (void)
+{
+}
 
-/* 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) 
+/* 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)
 {
-  int i;
+  struct fmt_spec f;
+  f.type = type;
+  f.w = w;
+  f.d = d;
+  assert (fmt_check_input (&f));
+  return f;
+}
 
-  for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
-    if (!strcasecmp (name, formats[i].name))
-      {
-        *type = i;
-        return true;
-      }
-  return false;
+/* Returns an output format specification with type TYPE, width
+   W, and D decimals. */
+struct fmt_spec
+fmt_for_output (enum fmt_type type, int w, int d)
+{
+  struct fmt_spec f;
+  f.type = type;
+  f.w = w;
+  f.d = d;
+  assert (fmt_check_output (&f));
+  return f;
 }
 
-/* Converts F to its string representation (for instance, "F8.2") and
-   returns a pointer to a static buffer containing that string.
-   If F has decimals, then they are included in the output
-   string, even if F's format type does not, so that we can
-   accurately present incorrect formats to the user. */
-char *
-fmt_to_string (const struct fmt_spec *f)
+/* Returns the output format specifier corresponding to input
+   format specifier INPUT. */
+struct fmt_spec
+fmt_for_output_from_input (const struct fmt_spec *input)
 {
-  static char buf[32];
+  struct fmt_spec output;
 
-  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;
+  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;
+
+    case FMT_F:
+    case FMT_COMMA:
+    case FMT_DOT:
+    case FMT_DOLLAR:
+    case FMT_PCT:
+      {
+        const struct fmt_number_style *style = fmt_get_style (input->type);
+        output.w += fmt_affix_width (style);
+        if (style->grouping != 0 && input->w - input->d >= 3)
+          output.w += (input->w - input->d - 1) / 3;
+        if (output.d > 0)
+          output.w++;
+      }
+      break;
+
+    case FMT_N:
+      if (output.d > 0)
+        output.w++;
+      break;
+
+    case FMT_E:
+      output.d = MAX (input->d, 3);
+      output.w = MAX (input->w, output.d + 7);
+      break;
+
+    case FMT_PIBHEX:
+      output.w = max_digits_for_bytes (input->w / 2) + 1;
+      break;
+
+    case FMT_RB:
+    case FMT_RBHEX:
+      output.w = 8;
+      output.d = 2;
+      break;
+
+    case FMT_P:
+    case FMT_PK:
+      output.w = 2 * input->w + (input->d > 0);
+      break;
+
+    case FMT_IB:
+    case FMT_PIB:
+      output.w = max_digits_for_bytes (input->w) + 1;
+      if (output.d > 0)
+        output.w++;
+      break;
+
+    case FMT_CCA:
+    case FMT_CCB:
+    case FMT_CCC:
+    case FMT_CCD:
+    case FMT_CCE:
+      NOT_REACHED ();
+
+    case FMT_A:
+      break;
+
+    case FMT_AHEX:
+      output.w = input->w / 2;
+      break;
+
+    case FMT_DATE:
+    case FMT_EDATE:
+    case FMT_SDATE:
+    case FMT_ADATE:
+    case FMT_JDATE:
+    case FMT_QYR:
+    case FMT_MOYR:
+    case FMT_WKYR:
+    case FMT_TIME:
+    case FMT_DTIME:
+    case FMT_DATETIME:
+    case FMT_WKDAY:
+    case FMT_MONTH:
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+
+  if (output.w > fmt_max_output_width (output.type))
+    output.w = fmt_max_output_width (output.type);
+
+  assert (fmt_check_output (&output));
+  return output;
 }
 
-/* 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)
+/* 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 struct fmt_desc *f ; 
-  char *str;
+  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 (spec->type < FMT_NUMBER_OF_FORMATS);
-  f = &formats[spec->type];
-  str = fmt_to_string (spec);
+  assert (is_fmt_type (spec->type));
+  fmt_to_string (spec, str);
 
-  if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
+  if (for_input && !fmt_usable_for_input (spec->type))
     {
-      if (emit_error)
-        msg (SE, _("Format %s specifies an odd width %d, but "
-                   "an even width is required."),
-             str, spec->w);
+      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))
     {
-      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);
+      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;
     }
-  if (f->n_args <= 1 && spec->d) 
+
+  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)
+    {
+      msg (SE, _("%s %s specifies %d decimal places, but "
+                 "%s does not allow any decimals."),
+           io_fmt, str, spec->d, fmt_name (spec->type));
+      return false;
+    }
+  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, _("%s %s specifies %d decimal places, but "
+                   "the given width allows at most %d decimals."),
+             io_fmt, str, spec->d, max_d);
+      else
+        msg (SE, _("%s %s specifies %d decimal places, but "
+                   "the given width does not allow for any decimals."),
+             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))
-    {
-      if (emit_error)
-        msg (SE, _("%s variables are not compatible with %s format %s."),
-             type == ALPHA ? _("String") : _("Numeric"),
-             type == ALPHA ? _("numeric") : _("string"),
-             fmt_to_string (format));
+  assert (var_type == NUMERIC || var_type == ALPHA);
+  if ((var_type == ALPHA) != (fmt_is_string (format->type) != 0))
+    {
+      char str[FMT_STRING_LEN_MAX + 1];
+      msg (SE, _("%s variables are not compatible with %s format %s."),
+           var_type == ALPHA ? _("String") : _("Numeric"),
+           var_type == ALPHA ? _("numeric") : _("string"),
+           fmt_to_string (format, str));
       return false;
     }
   return true;
 }
-  
+
 /* Checks that FORMAT is appropriate for a variable of the given
-   WIDTH and returns true if so.  Otherwise returns false and (if
-   EMIT_ERROR is true) emits an error message. */
+   WIDTH and returns true if so.  Otherwise returns false and
+   emits an error message. */
 bool
-check_specifier_width (const struct fmt_spec *format,
-                       int width, bool emit_error) 
+fmt_check_width_compat (const struct fmt_spec *format, int width)
 {
-  if (!check_specifier_type (format, width != 0 ? ALPHA : NUMERIC, emit_error))
+  if (!fmt_check_type_compat (format, width != 0 ? ALPHA : NUMERIC))
     return false;
-  if (get_format_var_width (format) != width)
+  if (fmt_var_width (format) != width)
     {
-      if (emit_error)
-        msg (SE, _("String variable with width %d not compatible with "
-                   "format %s."),
-             width, fmt_to_string (format));
+      char str[FMT_STRING_LEN_MAX + 1];
+      msg (SE, _("String variable with width %d is not compatible with "
+                 "format %s."),
+           width, fmt_to_string (format, str));
       return false;
     }
   return true;
 }
 
-/* Converts input format specifier INPUT into output format
-   specifier OUTPUT. */
-void
-convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output)
+/* Returns the width corresponding to the format specifier.  The
+   return value is the value of the `width' member of a `struct
+   variable' for such an input format. */
+int
+fmt_var_width (const struct fmt_spec *spec)
 {
-  assert (check_input_specifier (input, 0));
+  return (spec->type == FMT_AHEX ? spec->w / 2
+          : spec->type == FMT_A ? spec->w
+          : 0);
+}
 
-  output->type = formats[input->type].output;
-  output->w = input->w;
-  if (output->w > formats[output->type].Omax_w)
-    output->w = formats[output->type].Omax_w;
-  output->d = input->d;
+/* Converts F to its string representation (for instance, "F8.2")
+   in BUFFER.  Returns BUFFER.
 
-  switch (input->type)
+   If F has decimals, they are included in the output string,
+   even if F's format type does not allow decimals, to allow
+   accurately presenting incorrect formats to the user. */
+char *
+fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
+{
+  if (fmt_takes_decimals (f->type) || f->d > 0)
+    snprintf (buffer, FMT_STRING_LEN_MAX + 1,
+              "%s%d.%d", fmt_name (f->type), f->w, f->d);
+  else
+    snprintf (buffer, FMT_STRING_LEN_MAX + 1,
+              "%s%d", fmt_name (f->type), f->w);
+  return buffer;
+}
+
+/* Describes a display format. */
+struct fmt_desc
+  {
+    char name[9];
+    int min_input_width, min_output_width;
+    int io;
+    enum fmt_category category;
+  };
+
+static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
+
+/* Returns the name of the given format TYPE. */
+const char *
+fmt_name (enum fmt_type type)
+{
+  return get_fmt_desc (type)->name;
+}
+
+/* Tries to parse NAME as a format type.
+   If successful, stores the type in *TYPE and returns true.
+   On failure, returns false. */
+bool
+fmt_from_name (const char *name, enum fmt_type *type)
+{
+  int i;
+
+  for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
+    if (!strcasecmp (name, get_fmt_desc (i)->name))
+      {
+        *type = i;
+        return true;
+      }
+  return false;
+}
+
+/* Returns true if TYPE accepts decimal places,
+   false otherwise. */
+bool
+fmt_takes_decimals (enum fmt_type type)
+{
+  return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
+}
+
+/* Returns the minimum acceptable width for an input field
+   formatted with the given TYPE. */
+int
+fmt_min_input_width (enum fmt_type type)
+{
+  return get_fmt_desc (type)->min_input_width;
+}
+
+/* Returns the maximum acceptable width for an input field
+   formatted with the given TYPE. */
+int
+fmt_max_input_width (enum fmt_type type)
+{
+  return max_width (type);
+}
+
+/* Returns the maximum number of decimal places allowed in an
+   input field of the given TYPE and WIDTH. */
+int
+fmt_max_input_decimals (enum fmt_type type, int width)
+{
+  assert (valid_width (type, width, true));
+  return max_decimals (type, width, true);
+}
+
+/* Returns the minimum acceptable width for an output field
+   formatted with the given TYPE. */
+int
+fmt_min_output_width (enum fmt_type type)
+{
+  return get_fmt_desc (type)->min_output_width;
+}
+
+/* Returns the maximum acceptable width for an output field
+   formatted with the given TYPE. */
+int
+fmt_max_output_width (enum fmt_type type)
+{
+  return max_width (type);
+}
+
+/* Returns the maximum number of decimal places allowed in an
+   output field of the given TYPE and WIDTH. */
+int
+fmt_max_output_decimals (enum fmt_type type, int width)
+{
+  assert (valid_width (type, width, false));
+  return max_decimals (type, width, false);
+}
+
+/* Returns the width step for a field formatted with the given
+   TYPE.  Field width must be a multiple of the width step. */
+int
+fmt_step_width (enum fmt_type type)
+{
+  return fmt_get_category (type) & FMT_CAT_HEXADECIMAL ? 2 : 1;
+}
+
+/* Returns true if TYPE is used for string fields,
+   false if it is used for numeric fields. */
+bool
+fmt_is_string (enum fmt_type type)
+{
+  return fmt_get_category (type) & FMT_CAT_STRING;
+}
+
+/* Returns true if TYPE is used for numeric fields,
+   false if it is used for string fields. */
+bool
+fmt_is_numeric (enum fmt_type type)
+{
+  return !fmt_is_string (type);
+}
+
+/* Returns the format TYPE's category.
+   Each format type is in exactly one category,
+   and each category's value is bitwise disjoint from every other
+   category.  Thus, the return value may be tested for equality
+   or compared bitwise against a mask of FMT_CAT_* values. */
+enum fmt_category
+fmt_get_category (enum fmt_type type) 
+{
+  return get_fmt_desc (type)->category;
+}
+
+/* Returns the output format selected by default when TYPE is
+   used as an input format. */
+enum fmt_type
+fmt_input_to_output (enum fmt_type type)
+{
+  enum fmt_category category = fmt_get_category (type);
+  return (category & FMT_CAT_STRING ? FMT_A
+          : category & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL) ? FMT_F
+          : type);
+}
+
+/* Returns the SPSS format type corresponding to the given PSPP
+   format type. */
+int
+fmt_to_io (enum fmt_type type)
+{
+  return get_fmt_desc (type)->io;
+};
+
+/* Determines the PSPP format corresponding to the given SPSS
+   format type.  If successful, sets *FMT_TYPE to the PSPP format
+   and returns true.  On failure, return false. */
+bool
+fmt_from_io (int io, enum fmt_type *fmt_type)
+{
+  enum fmt_type type;
+
+  for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
+    if (get_fmt_desc (type)->io == io)
+      {
+        *fmt_type = type;
+        return true;
+      }
+  return false;
+}
+
+/* Returns true if TYPE may be used as an input format,
+   false otherwise. */
+bool
+fmt_usable_for_input (enum fmt_type type)
+{
+  assert (is_fmt_type (type));
+  return fmt_get_category (type) != FMT_CAT_CUSTOM;
+}
+
+/* For time and date formats, returns a template used for input
+   and output. */
+const char *
+fmt_date_template (enum fmt_type type)
+{
+  switch (type)
+    {
+    case FMT_DATE:
+      return "dd-mmm-yy";
+    case FMT_ADATE:
+      return "mm/dd/yy";
+    case FMT_EDATE:
+      return "dd.mm.yy";
+    case FMT_JDATE:
+      return "yyddd";
+    case FMT_SDATE:
+      return "yy/mm/dd";
+    case FMT_QYR:
+      return "q Q yy";
+    case FMT_MOYR:
+      return "mmm yy";
+    case FMT_WKYR:
+      return "ww WK yy";
+    case FMT_DATETIME:
+      return "dd-mmm-yyyy HH:MM";
+    case FMT_TIME:
+      return "h:MM";
+    case FMT_DTIME:
+      return "D HH:MM";
+    default:
+      NOT_REACHED ();
+    }
+}
+
+/* Returns true if TYPE is a valid format type,
+   false otherwise. */
+static bool
+is_fmt_type (enum fmt_type type)
+{
+  return type < FMT_NUMBER_OF_FORMATS;
+}
+
+/* Returns the minimum width of the given format TYPE,
+   for input if FOR_INPUT is true,
+   for output otherwise. */
+static int
+min_width (enum fmt_type type, bool for_input)
+{
+  return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
+}
+
+/* Returns the maximum width of the given format TYPE,
+   which is invariant between input and output. */
+static int
+max_width (enum fmt_type type)
+{
+  assert (is_fmt_type (type));
+  switch (type)
+    {
+    case FMT_P:
+    case FMT_PK:
+    case FMT_PIBHEX:
+    case FMT_RBHEX:
+      return 16;
+
+    case FMT_IB:
+    case FMT_PIB:
+    case FMT_RB:
+      return 8;
+
+    case FMT_A:
+      return MAX_STRING;
+
+    case FMT_AHEX:
+      return 2 * MAX_STRING;
+
+    default:
+      return 40;
+    }
+}
+
+/* Returns true if WIDTH is a valid width for the given format
+   TYPE,
+   for input if FOR_INPUT is true,
+   for output otherwise. */
+static bool
+valid_width (enum fmt_type type, int width, bool for_input)
+{
+  return (width >= min_width (type, for_input)
+          && width <= max_width (type));
+}
+
+/* Returns the maximum number of decimal places allowed for the
+   given format TYPE with a width of WIDTH places,
+   for input if FOR_INPUT is true,
+   for output otherwise. */
+static int
+max_decimals (enum fmt_type type, int width, bool for_input)
+{
+  int max_d;
+
+  switch (type)
     {
     case FMT_F:
-    case FMT_N:
-      if (output->d > 0)
-	output->w++;
-      break;
-    case FMT_E:
-      output->w = max (max (input->w, input->d+7), 10);
-      output->d = max (input->d, 3);
-      break;
     case FMT_COMMA:
     case FMT_DOT:
-      /* nothing is necessary */
+      max_d = for_input ? width : width - 1;
       break;
+
     case FMT_DOLLAR:
     case FMT_PCT:
-      if (output->w < 2)
-	output->w = 2;
+      max_d = for_input ? width : width - 2;
       break;
-    case FMT_PIBHEX:
-      {
-	static const int map[] = {4, 6, 9, 11, 14, 16, 18, 21};
-	assert (input->w % 2 == 0 && input->w >= 2 && input->w <= 16);
-	output->w = map[input->w / 2 - 1];
-	break;
-      }
-    case FMT_RBHEX:
-      output->w = 8, output->d = 2;	/* FIXME */
-      break;
-    case FMT_IB:
-    case FMT_PIB:
-    case FMT_P:
-    case FMT_PK:
-    case FMT_RB:
-      if (input->d < 1)
-	output->w = 8, output->d = 2;
-      else
-	output->w = 9 + input->d;
+
+    case FMT_E:
+      max_d = for_input ? width : width - 7;
       break;
+
     case FMT_CCA:
     case FMT_CCB:
     case FMT_CCC:
     case FMT_CCD:
     case FMT_CCE:
-      NOT_REACHED ();
+      assert (!for_input);
+      max_d = width - 1;
+      break;
+
+    case FMT_N:
     case FMT_Z:
-    case FMT_A:
-      /* nothing is necessary */
+      max_d = width;
       break;
-    case FMT_AHEX:
-      output->w = input->w / 2;
+
+    case FMT_P:
+      max_d = width * 2 - 1;
+      break;
+
+    case FMT_PK:
+      max_d = width * 2;
+      break;
+
+    case FMT_IB:
+    case FMT_PIB:
+      max_d = max_digits_for_bytes (width);
       break;
+
+    case FMT_PIBHEX:
+      max_d = 0;
+      break;
+
+    case FMT_RB:
+    case FMT_RBHEX:
+      max_d = 16;
+      break;
+
     case FMT_DATE:
-    case FMT_EDATE:
-    case FMT_SDATE:
     case FMT_ADATE:
+    case FMT_EDATE:
     case FMT_JDATE:
-      /* nothing is necessary */
-      break;
+    case FMT_SDATE:
     case FMT_QYR:
-      if (output->w < 6)
-	output->w = 6;
-      break;
     case FMT_MOYR:
-      /* nothing is necessary */
-      break;
     case FMT_WKYR:
-      if (output->w < 8)
-	output->w = 8;
+      max_d = 0;
       break;
+
+    case FMT_DATETIME:
+      max_d = width - 21;
+      break;
+
     case FMT_TIME:
+      max_d = width - 9;
+      break;
+
     case FMT_DTIME:
-    case FMT_DATETIME:
+      max_d = width - 12;
+      break;
+
     case FMT_WKDAY:
     case FMT_MONTH:
-      /* nothing is necessary */
+    case FMT_A:
+    case FMT_AHEX:
+      max_d = 0;
       break;
+
     default:
       NOT_REACHED ();
     }
 
-  assert (check_output_specifier (output, 0));
+  if (max_d < 0)
+    max_d = 0;
+  else if (max_d > 16)
+    max_d = 16;
+  return max_d;
+}
+
+/* Returns the 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;
 }
 
-/* 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. */
+/* Destroys a struct fmt_number_style. */
+void
+fmt_number_style_destroy (struct fmt_number_style *style)
+{
+  if (style != NULL)
+    {
+      ss_dealloc (&style->neg_prefix);
+      ss_dealloc (&style->prefix);
+      ss_dealloc (&style->suffix);
+      ss_dealloc (&style->neg_suffix);
+      free (style);
+    }
+}
+
+/* Returns the number formatting style associated with the given
+   format TYPE. */
+const struct fmt_number_style *
+fmt_get_style (enum fmt_type type)
+{
+  assert (is_fmt_type (type));
+  assert (styles[type] != NULL);
+  return styles[type];
+}
+
+/* Sets STYLE as the number formatting style associated with the
+   given format TYPE, transferring ownership of STYLE.  */
+void
+fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
+{
+  assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
+  assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
+  assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
+  assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
+  assert (style->decimal == '.' || style->decimal == ',');
+  assert (style->grouping != style->decimal
+          && (style->grouping == '.' || style->grouping == ','
+              || style->grouping == 0));
+
+  assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
+  assert (styles[type] != NULL);
+
+  fmt_number_style_destroy (styles[type]);
+  styles[type] = style;
+}
+
+/* Returns the total width of the standard prefix and suffix for
+   STYLE. */
 int
-get_format_var_width (const struct fmt_spec *spec) 
+fmt_affix_width (const struct fmt_number_style *style)
 {
-  if (spec->type == FMT_AHEX)
-    return spec->w / 2;
-  else if (spec->type == FMT_A)
-    return spec->w;
-  else
-    return 0;
+  return ss_length (style->prefix) + ss_length (style->suffix);
 }
 
-/* Returns the PSPP format corresponding to the given SPSS
-   format. */
+/* Returns the total width of the negative prefix and suffix for
+   STYLE. */
 int
-translate_fmt (int spss) 
+fmt_neg_affix_width (const struct fmt_number_style *style)
 {
-  int type;
-  
-  for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
-    if (formats[type].spss == spss)
-      return type;
-  return -1;
+  return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
 }
 
-/* Returns an input format specification with type TYPE, width W,
-   and D decimals. */
-struct fmt_spec
-make_input_format (int type, int w, int d) 
+/* Returns the decimal point character for the given format
+   TYPE. */
+int
+fmt_decimal_char (enum fmt_type type)
 {
-  struct fmt_spec f;
-  f.type = type;
-  f.w = w;
-  f.d = d;
-  assert (check_input_specifier (&f, 0));
-  return f;
+  return fmt_get_style (type)->decimal;
 }
 
-/* Returns an output format specification with type TYPE, width
-   W, and D decimals. */
-struct fmt_spec
-make_output_format (int type, int w, int d)
+/* Returns the grouping character for the given format TYPE, or 0
+   if the format type does not group digits. */
+int
+fmt_grouping_char (enum fmt_type type)
 {
-  struct fmt_spec f;
-  f.type = type;
-  f.w = w;
-  f.d = d;
-  assert (check_output_specifier (&f, 0));
-  return f;
+  return fmt_get_style (type)->grouping;
 }
 
-/* Returns true if TYPE is a binary format,
-   false otherwise. */
-bool
-fmt_is_binary (int type)
+/* Sets the number style for TYPE to have the given standard
+   PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
+   suffix, DECIMAL as the decimal point character, and GROUPING
+   as the grouping character. */
+static void
+set_style (enum fmt_type type,
+           const char *prefix, const char *suffix,
+           char decimal, char grouping)
 {
-  switch (type) 
-    {
-    case FMT_P:
-    case FMT_PK:
-    case FMT_IB:
-    case FMT_PIB:
-    case FMT_RB:
-      return true;
+  struct fmt_number_style *style;
 
-    default:
-      return false;
-    }
+  assert (is_fmt_type (type));
+
+  fmt_number_style_destroy (styles[type]);
+
+  style = styles[type] = fmt_number_style_create ();
+  ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
+  ss_alloc_substring (&style->prefix, ss_cstr (prefix));
+  ss_alloc_substring (&style->suffix, ss_cstr (suffix));
+  style->decimal = decimal;
+  style->grouping = grouping;
 }
 
-bool 
-measure_is_valid(enum measure m)
+/* Sets the number style for TYPE as with set_style, but only if
+   TYPE has not already been initialized. */
+static void
+init_style (enum fmt_type type,
+            const char *prefix, const char *suffix,
+            char decimal, char grouping)
 {
-  if ( m <= 0 ) return false;
-  if ( m >= n_MEASURES) return false;
-  return true;
+  assert (is_fmt_type (type));
+  if (styles[type] == NULL)
+    set_style (type, prefix, suffix, decimal, grouping);
 }
 
-bool 
-alignment_is_valid(enum alignment a)
+/* Sets the decimal point character to DECIMAL. */
+void
+fmt_set_decimal (char decimal)
 {
-  if ( a >= n_ALIGN) return false;
-  return true;
+  int grouping = decimal == '.' ? ',' : '.';
+  assert (decimal == '.' || decimal == ',');
+
+  set_style (FMT_F, "", "", decimal, 0);
+  set_style (FMT_E, "", "", decimal, 0);
+  set_style (FMT_COMMA, "", "", decimal, grouping);
+  set_style (FMT_DOT, "", "", grouping, decimal);
+  set_style (FMT_DOLLAR, "$", "", decimal, grouping);
+  set_style (FMT_PCT, "", "%", decimal, 0);
+
+  init_style (FMT_CCA, "", "", decimal, grouping);
+  init_style (FMT_CCB, "", "", decimal, grouping);
+  init_style (FMT_CCC, "", "", decimal, grouping);
+  init_style (FMT_CCD, "", "", decimal, grouping);
+  init_style (FMT_CCE, "", "", decimal, grouping);
+}
+
+/* Returns true if M is a valid variable measurement level,
+   false otherwise. */
+bool
+measure_is_valid (enum measure m)
+{
+  return m > 0 && m < n_MEASURES;
+}
+
+/* Returns true if A is a valid alignment,
+   false otherwise. */
+bool
+alignment_is_valid (enum alignment a)
+{
+  return a < n_ALIGN;
+}
+
+/* Returns the struct fmt_desc for the given format TYPE. */
+static const struct fmt_desc *
+get_fmt_desc (enum fmt_type type)
+{
+  static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
+    {
+#define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
+	{#NAME, IMIN, OMIN, IO, CATEGORY},
+#include "format.def"
+    };
+
+  assert (is_fmt_type (type));
+  return &formats[type];
 }
Index: src/data/format.def
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/format.def,v
retrieving revision 1.3
diff -u -p -r1.3 format.def
--- src/data/format.def	18 Jul 2006 04:57:01 -0000	1.3
+++ src/data/format.def	1 Nov 2006 04:17:16 -0000
@@ -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 <blp@gnu.org>.
 
    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
diff -u -p -r1.7 format.h
--- src/data/format.h	1 Nov 2006 03:36:10 -0000	1.7
+++ src/data/format.h	1 Nov 2006 04:17:16 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ enum alignment 
     n_ALIGN
   };
 
-
+/* How data is measured. */
 enum measure
   {
     MEASURE_NOMINAL=1,
@@ -95,34 +163,9 @@ enum measure
 
 bool measure_is_valid(enum measure m);
 bool alignment_is_valid(enum alignment a);
+
+#include <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
diff -u -p -r1.11 por-file-reader.c
--- src/data/por-file-reader.c	9 Jun 2006 22:51:23 -0000	1.11
+++ src/data/por-file-reader.c	1 Nov 2006 04:17:17 -0000
@@ -468,18 +468,25 @@ static void
 convert_format (struct pfm_reader *r, const int portable_format[3],
                 struct fmt_spec *format, struct variable *v)
 {
-  format->type = translate_fmt (portable_format[0]);
-  if (format->type == -1)
+  bool ok;
+
+  if (!fmt_from_io (portable_format[0], &format->type))
     error (r, _("%s: Bad format specifier byte (%d)."),
            v->name, portable_format[0]);
   format->w = portable_format[1];
   format->d = portable_format[2];
 
-  if (!check_output_specifier (format, false)
-      || !check_specifier_width (format, v->width, false))
-    error (r, _("%s variable %s has invalid format specifier %s."),
-           v->type == NUMERIC ? _("Numeric") : _("String"),
-           v->name, fmt_to_string (format));
+  msg_disable ();
+  ok = fmt_check_output (format) && fmt_check_width_compat (format, v->width);
+  msg_enable ();
+
+  if (!ok)
+    {
+      char fmt_string[FMT_STRING_LEN_MAX + 1];
+      error (r, _("%s variable %s has invalid format specifier %s."),
+             v->type == NUMERIC ? _("Numeric") : _("String"),
+             v->name, fmt_to_string (format, fmt_string)); 
+    }
 }
 
 static union value parse_value (struct pfm_reader *, struct variable *);
Index: src/data/por-file-writer.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/por-file-writer.c,v
retrieving revision 1.4
diff -u -p -r1.4 por-file-writer.c
--- src/data/por-file-writer.c	17 Apr 2006 01:54:15 -0000	1.4
+++ src/data/por-file-writer.c	1 Nov 2006 04:17:19 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -282,7 +282,7 @@ write_version_data (struct pfm_writer *w
 static void
 write_format (struct pfm_writer *w, struct fmt_spec *f)
 {
-  write_int (w, formats[f->type].spss);
+  write_int (w, fmt_to_io (f->type));
   write_int (w, f->w);
   write_int (w, f->d);
 }
Index: src/data/settings.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/settings.c,v
retrieving revision 1.3
diff -u -p -r1.3 settings.c
--- src/data/settings.c	30 May 2006 12:01:33 -0000	1.3
+++ src/data/settings.c	1 Nov 2006 04:17:19 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -33,9 +33,6 @@ static bool long_view = false;
 
 static bool safer_mode = false;
 
-static char decimal = '.';
-static char grouping = ',';
-
 static bool echo = false;
 static bool include = true;
 
@@ -64,16 +61,6 @@ static size_t workspace = 4L * 1024 * 10
 
 static struct fmt_spec default_format = {FMT_F, 8, 2};
 
-#define CC_INITIALIZER {"-", "", "", "", '.', ','}
-static struct custom_currency cc[CC_CNT] = 
-  {
-    CC_INITIALIZER,
-    CC_INITIALIZER,
-    CC_INITIALIZER,
-    CC_INITIALIZER,
-    CC_INITIALIZER,
-  };
-
 static bool testing_mode = false;
 
 static int global_algorithm = ENHANCED;
@@ -192,41 +179,6 @@ set_safer_mode (void)
   safer_mode = true;
 }
 
-/* The character used for a decimal point: ',' or '.'.  Only
-   respected for data input and output. */
-char 
-get_decimal (void)
-{
-  return decimal;
-}
-
-/* Sets the character used for a decimal point, which must be
-   either ',' or '.'. */
-void
-set_decimal (char decimal_) 
-{
-  assert (decimal_ == '.' || decimal_ == ',');
-  decimal = decimal_;
-}
-
-/* The character used for grouping in numbers: '.' or ','; the
-   opposite of set_decimal.  Only used in COMMA data input and
-   output. */
-char
-get_grouping (void)
-{
-  return grouping;
-}
-
-/* Sets the character used for grouping, which must be either ','
-   or '.'. */
-void
-set_grouping (char grouping_) 
-{
-  assert (grouping_ == '.' || grouping_ == ',');
-  grouping = grouping_;
-}
- 
 /* Echo commands to the listing file/printer? */
 bool
 get_echo (void)
@@ -466,22 +418,6 @@ set_format (const struct fmt_spec *defau
   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
diff -u -p -r1.1 settings.h
--- src/data/settings.h	4 Mar 2006 01:11:57 -0000	1.1
+++ src/data/settings.h	1 Nov 2006 04:17:19 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -45,11 +45,6 @@ void set_viewwidth (int);
 bool get_safer_mode (void);
 void set_safer_mode (void);
 
-char get_decimal (void);
-void set_decimal (char);
-char get_grouping (void);
-void set_grouping (char);
-
 bool get_echo (void);
 void set_echo (bool);
 bool get_include (void);
@@ -94,24 +89,6 @@ void set_workspace (size_t);
 const struct fmt_spec *get_format (void);
 void set_format (const struct fmt_spec *);
 
-/* Maximum number of custom currency specifications */
-#define CC_CNT 5
-
-/* One custom currency specification. */
-#define CC_WIDTH 16
-struct custom_currency
-  {
-    char neg_prefix[CC_WIDTH];	/* Negative prefix. */
-    char prefix[CC_WIDTH];	/* Prefix. */
-    char suffix[CC_WIDTH];	/* Suffix. */
-    char neg_suffix[CC_WIDTH];	/* Negative suffix. */
-    char decimal;		/* Decimal point. */
-    char grouping;		/* Grouping character. */
-  };
-
-const struct custom_currency *get_cc (int idx);
-void set_cc (int idx, const struct custom_currency *);
-
 bool get_testing_mode (void);
 void set_testing_mode (bool);
 
Index: src/data/sys-file-reader.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/sys-file-reader.c,v
retrieving revision 1.24
diff -u -p -r1.24 sys-file-reader.c
--- src/data/sys-file-reader.c	29 Oct 2006 09:51:36 -0000	1.24
+++ src/data/sys-file-reader.c	1 Nov 2006 04:17:19 -0000
@@ -1208,28 +1208,35 @@ static int
 parse_format_spec (struct sfm_reader *r, int32_t s,
                    struct fmt_spec *f, const struct variable *v)
 {
-  f->type = translate_fmt ((s >> 16) & 0xff);
-  if (f->type == -1)
+  bool ok;
+  
+  if (!fmt_from_io ((s >> 16) & 0xff, &f->type))
     lose ((ME, _("%s: Bad format specifier byte (%d)."),
 	   fh_get_file_name (r->fh), (s >> 16) & 0xff));
   f->w = (s >> 8) & 0xff;
   f->d = s & 0xff;
 
-  if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0))
+  if ((v->type == ALPHA) ^ (fmt_is_string (f->type) != 0))
     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
 	   fh_get_file_name (r->fh),
            v->type == ALPHA ? _("String") : _("Numeric"),
 	   v->name,
-	   formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"),
-	   formats[f->type].name));
+	   fmt_is_string (f->type) ? _("string") : _("numeric"),
+	   fmt_name (f->type)));
 
-  if (!check_output_specifier (f, false)
-      || !check_specifier_width (f, v->width, false)) 
+  msg_disable ();
+  ok = fmt_check_output (f) && fmt_check_width_compat (f, v->width);
+  msg_enable ();
+  
+  if (!ok) 
     {
+      char fmt_string[FMT_STRING_LEN_MAX + 1];
       msg (ME, _("%s variable %s has invalid format specifier %s."),
            v->type == NUMERIC ? _("Numeric") : _("String"),
-           v->name, fmt_to_string (f));
-      *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0);
+           v->name, fmt_to_string (f, fmt_string));
+      *f = (v->type == NUMERIC
+            ? fmt_for_output (FMT_F, 8, 2) 
+            : fmt_for_output (FMT_A, v->width, 0));
     }
   return 1;
 
Index: src/data/sys-file-writer.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/sys-file-writer.c,v
retrieving revision 1.14
diff -u -p -r1.14 sys-file-writer.c
--- src/data/sys-file-writer.c	29 Oct 2006 11:16:07 -0000	1.14
+++ src/data/sys-file-writer.c	1 Nov 2006 04:17:19 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ write_header (struct sfm_writer *w, cons
 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
diff -u -p -r1.30 ChangeLog
--- src/language/data-io/ChangeLog	1 Nov 2006 02:22:35 -0000	1.30
+++ src/language/data-io/ChangeLog	1 Nov 2006 04:17:20 -0000
@@ -1,3 +1,18 @@
+Tue Oct 31 20:04:06 2006  Ben Pfaff  <blp@gnu.org>
+
+	* 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  <blp@gnu.org>
 
 	* 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
diff -u -p -r1.22 data-list.c
--- src/language/data-io/data-list.c	26 Oct 2006 06:16:36 -0000	1.22
+++ src/language/data-io/data-list.c	1 Nov 2006 04:17:20 -0000
@@ -302,7 +302,8 @@ parse_fixed (struct dictionary *dict, 
       /* Parse everything. */
       if (!parse_record_placement (&record, &column)
           || !parse_DATA_LIST_vars_pool (tmp_pool, &names, &name_cnt, PV_NONE)
-          || !parse_var_placements (tmp_pool, name_cnt, &formats, &format_cnt))
+          || !parse_var_placements (tmp_pool, name_cnt, true,
+                                    &formats, &format_cnt))
         return false;
 
       /* Create variables and var specs. */
@@ -318,13 +319,12 @@ parse_fixed (struct dictionary *dict, 
             name = names[name_idx++];
 
             /* Create variable. */
-            width = get_format_var_width (f);
+            width = fmt_var_width (f);
             v = dict_create_var (dict, name, width);
             if (v != NULL)
               {
                 /* Success. */
-                struct fmt_spec output;
-                convert_fmt_ItoO (f, &output);
+                struct fmt_spec output = fmt_for_output_from_input (f);
                 v->print = output;
                 v->write = output;
               }
@@ -415,11 +415,13 @@ dump_fixed_table (const struct ll_list *
   row = 1;
   ll_for_each (spec, struct dls_var_spec, ll, specs)
     {
+      char fmt_string[FMT_STRING_LEN_MAX + 1];
       tab_text (t, 0, row, TAB_LEFT, spec->name);
       tab_text (t, 1, row, TAT_PRINTF, "%d", spec->record);
       tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d",
                 spec->first_column, spec->first_column + spec->input.w - 1);
-      tab_text (t, 3, row, TAB_LEFT | TAB_FIX, fmt_to_string (&spec->input));
+      tab_text (t, 3, row, TAB_LEFT | TAB_FIX,
+                fmt_to_string (&spec->input, fmt_string));
       row++;
     }
 
@@ -451,15 +453,15 @@ parse_free (struct dictionary *dict, str
       if (lex_match ('('))
 	{
 	  if (!parse_format_specifier (&input)
-              || !check_input_specifier (&input, 1)
+              || !fmt_check_input (&input)
               || !lex_force_match (')')) 
             return NULL;
-	  convert_fmt_ItoO (&input, &output);
+	  output = fmt_for_output_from_input (&input);
 	}
       else
 	{
 	  lex_match ('*');
-          input = make_input_format (FMT_F, 8, 0);
+          input = fmt_for_input (FMT_F, 8, 0);
 	  output = *get_format ();
 	}
 
@@ -468,8 +470,7 @@ parse_free (struct dictionary *dict, str
           struct dls_var_spec *spec;
 	  struct variable *v;
 
-	  v = dict_create_var (dict, name[i],
-                               get_format_var_width (&input));
+	  v = dict_create_var (dict, name[i], fmt_var_width (&input));
 	  if (v == NULL)
 	    {
 	      msg (SE, _("%s is a duplicate variable name."), name[i]);
@@ -509,12 +510,13 @@ dump_free_table (const struct data_list_
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, spec_cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
   tab_dim (t, tab_natural_dimensions);
-
   row = 1;
   ll_for_each (spec, struct dls_var_spec, ll, &dls->specs)
     {
+      char str[FMT_STRING_LEN_MAX + 1];
       tab_text (t, 0, row, TAB_LEFT, spec->name);
-      tab_text (t, 1, row, TAB_LEFT | TAB_FIX, fmt_to_string (&spec->input));
+      tab_text (t, 1, row, TAB_LEFT | TAB_FIX,
+                fmt_to_string (&spec->input, str));
       row++;
     }
 
@@ -740,7 +742,7 @@ read_from_data_list_list (const struct d
 		 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
diff -u -p -r1.19 get.c
--- src/language/data-io/get.c	26 Oct 2006 06:16:36 -0000	1.19
+++ src/language/data-io/get.c	1 Nov 2006 04:17:21 -0000
@@ -1089,7 +1089,7 @@ cmd_match_files (struct dataset *ds)
             goto error;
           }
         iter->in_var->print = iter->in_var->write
-          = make_output_format (FMT_F, 1, 0);
+          = fmt_for_output (FMT_F, 1, 0);
       }
     
   /* MATCH FILES performs an n-way merge on all its input files.
Index: src/language/data-io/list.q
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/list.q,v
retrieving revision 1.17
diff -u -p -r1.17 list.q
--- src/language/data-io/list.q	26 Oct 2006 06:16:36 -0000	1.17
+++ src/language/data-io/list.q	1 Nov 2006 04:17:21 -0000
@@ -213,9 +213,9 @@ cmd_list (struct dataset *ds)
       strcpy (casenum_var.name, "Case#");
       casenum_var.type = NUMERIC;
       casenum_var.fv = -1;
-      casenum_var.print = make_output_format (FMT_F,
-                                              (cmd.last == LONG_MAX
-                                               ? 5 : intlog10 (cmd.last)), 0);
+      casenum_var.print = fmt_for_output (FMT_F,
+                                          (cmd.last == LONG_MAX
+                                           ? 5 : intlog10 (cmd.last)), 0);
 
       /* Add the weight variable at the beginning of the variable list. */
       cmd.n_variables++;
@@ -664,7 +664,7 @@ list_cases (const struct ccase *c, void 
 		ds_put_char_multiple(&line_buffer, ' ', width - v->print.w);
 	      }
 
-            if ((formats[v->print.type].cat & FCAT_STRING) || v->fv != -1)
+            if (fmt_is_string (v->print.type) || v->fv != -1)
 	      {
                 data_out (ds_put_uninit(&line_buffer, v->print.w),
 			  &v->print, case_data (c, v->fv));
@@ -701,7 +701,7 @@ list_cases (const struct ccase *c, void 
 	    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
diff -u -p -r1.15 matrix-data.c
--- src/language/data-io/matrix-data.c	29 Oct 2006 09:51:36 -0000	1.15
+++ src/language/data-io/matrix-data.c	1 Nov 2006 04:17:23 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -871,7 +871,7 @@ static int
 	  di.e = ss_end (s);
 	  di.v = (union value *) &token->number;
 	  di.f1 = dfm_get_column (reader, di.s);
-	  di.format = make_output_format (FMT_F, token->length, 0);
+	  di.format = fmt_for_output (FMT_F, token->length, 0);
 
 	  data_in (&di);
 	}
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
diff -u -p -r1.1 placement-parser.c
--- src/language/data-io/placement-parser.c	18 Jul 2006 04:57:01 -0000	1.1
+++ src/language/data-io/placement-parser.c	1 Nov 2006 04:17:23 -0000
@@ -39,14 +39,14 @@
    placement. */
 enum 
   {
-    PRS_TYPE_T = -1,            /* Tab to absolute column. */
-    PRS_TYPE_X = -2,            /* Skip columns. */
-    PRS_TYPE_NEW_REC = -3       /* Next record. */
+    PRS_TYPE_T = SCHAR_MAX - 3, /* Tab to absolute column. */
+    PRS_TYPE_X,                 /* Skip columns. */
+    PRS_TYPE_NEW_REC            /* Next record. */
   };
 
-static bool fixed_parse_columns (struct pool *, size_t var_cnt,
+static bool fixed_parse_columns (struct pool *, size_t var_cnt, bool for_input,
                                  struct fmt_spec **, size_t *);
-static bool fixed_parse_fortran (struct pool *,
+static bool fixed_parse_fortran (struct pool *, bool for_input,
                                  struct fmt_spec **, size_t *);
 
 /* Parses Fortran-like or column-based specifications for placing
@@ -65,23 +65,23 @@ static bool fixed_parse_fortran (struct 
    Uses POOL for allocation.  When the caller is finished
    interpreting *FORMATS, POOL may be destroyed. */
 bool
-parse_var_placements (struct pool *pool, size_t var_cnt,
+parse_var_placements (struct pool *pool, size_t var_cnt, bool for_input,
                       struct fmt_spec **formats, size_t *format_cnt) 
 {
   assert (var_cnt > 0);
   if (lex_is_number ())
-    return fixed_parse_columns (pool, var_cnt, formats, format_cnt);
+    return fixed_parse_columns (pool, var_cnt, for_input, formats, format_cnt);
   else if (lex_match ('(')) 
     {
       size_t assignment_cnt;
       size_t i;
 
-      if (!fixed_parse_fortran (pool, formats, format_cnt))
+      if (!fixed_parse_fortran (pool, for_input, formats, format_cnt))
         return false; 
 
       assignment_cnt = 0;
       for (i = 0; i < *format_cnt; i++)
-        assignment_cnt += (*formats)[i].type >= 0;
+        assignment_cnt += (*formats)[i].type < FMT_NUMBER_OF_FORMATS;
 
       if (assignment_cnt != var_cnt)
         {
@@ -103,7 +103,7 @@ parse_var_placements (struct pool *pool,
 
 /* Implements parse_var_placements for column-based formats. */
 static bool
-fixed_parse_columns (struct pool *pool, size_t var_cnt,
+fixed_parse_columns (struct pool *pool, size_t var_cnt, bool for_input,
                      struct fmt_spec **formats, size_t *format_cnt)
 {
   struct fmt_spec format;
@@ -153,7 +153,7 @@ fixed_parse_columns (struct pool *pool, 
       format.type = FMT_F;
       format.d = 0;
     }
-  if (!check_input_specifier (&format, 1))
+  if (!fmt_check (&format, for_input))
     return false;
 
   *formats = pool_nalloc (pool, var_cnt + 1, sizeof **formats);
@@ -167,7 +167,7 @@ fixed_parse_columns (struct pool *pool, 
 
 /* Implements parse_var_placements for Fortran-like formats. */
 static bool
-fixed_parse_fortran (struct pool *pool,
+fixed_parse_fortran (struct pool *pool, bool for_input,
                      struct fmt_spec **formats, size_t *format_cnt)
 {
   size_t formats_allocated = 0;
@@ -195,7 +195,8 @@ fixed_parse_fortran (struct pool *pool,
       if (lex_match ('('))
         {
           /* Call ourselves recursively to handle parentheses. */
-          if (!fixed_parse_fortran (pool, &new_formats, &new_format_cnt))
+          if (!fixed_parse_fortran (pool, for_input,
+                                    &new_formats, &new_format_cnt))
             return false;
         }
       else
@@ -221,12 +222,12 @@ fixed_parse_fortran (struct pool *pool,
                 }
               else 
                 {
-                  if (!fmt_type_from_string (type, &f.type)) 
+                  if (!fmt_from_name (type, &f.type)) 
                     {
                       msg (SE, _("Unknown format type \"%s\"."), type);
                       return false;
                     }
-                  if (!check_input_specifier (&f, 1))
+                  if (!fmt_check (&f, for_input))
                     return false;
                 }
             } 
@@ -284,7 +285,7 @@ execute_placement_format (const struct f
       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
diff -u -p -r1.1 placement-parser.h
--- src/language/data-io/placement-parser.h	18 Jul 2006 04:57:01 -0000	1.1
+++ src/language/data-io/placement-parser.h	1 Nov 2006 04:17:23 -0000
@@ -27,7 +27,7 @@ struct fmt_spec;
 struct pool;
 
 bool parse_record_placement (int *record, int *column);
-bool parse_var_placements (struct pool *, size_t var_cnt,
+bool parse_var_placements (struct pool *, size_t var_cnt, bool for_input,
                            struct fmt_spec **, size_t *format_cnt);
 bool execute_placement_format (const struct fmt_spec *,
                                int *record, int *column);
Index: src/language/data-io/print.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/print.c,v
retrieving revision 1.17
diff -u -p -r1.17 print.c
--- src/language/data-io/print.c	29 Oct 2006 00:01:53 -0000	1.17
+++ src/language/data-io/print.c	1 Nov 2006 04:17:23 -0000
@@ -317,7 +317,8 @@ parse_variable_argument (const struct di
 
   if (lex_is_number () || token == '(')
     {
-      if (!parse_var_placements (tmp_pool, var_cnt, &formats, &format_cnt))
+      if (!parse_var_placements (tmp_pool, var_cnt, false,
+                                 &formats, &format_cnt))
         return false;
       add_space = false;
     }
@@ -345,7 +346,7 @@ parse_variable_argument (const struct di
         struct prt_out_spec *spec;
 
         var = vars[var_idx++];
-        if (!check_specifier_width (f, var->width, true))
+        if (!fmt_check_width_compat (f, var->width))
           return false;
 
         spec = pool_alloc (trns->pool, sizeof *spec);
@@ -362,7 +363,8 @@ parse_variable_argument (const struct di
            that usually contains a period. */ 
         spec->sysmis_as_spaces = (which_formats == WRITE
                                   && var->type == NUMERIC
-                                  && !fmt_is_binary (spec->format.type));
+                                  && (fmt_get_category (spec->format.type)
+                                      != FMT_CAT_BINARY));
 
         ll_push_tail (&trns->specs, &spec->ll);
 
@@ -397,6 +399,7 @@ dump_table (struct print_trns *trns, con
   row = 1;
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs) 
     {
+      char fmt_string[FMT_STRING_LEN_MAX + 1];
       int width;
       switch (spec->type)
         {
@@ -408,7 +411,7 @@ dump_table (struct print_trns *trns, con
         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
diff -u -p -r1.11 formats.c
--- src/language/dictionary/formats.c	26 Oct 2006 06:16:36 -0000	1.11
+++ src/language/dictionary/formats.c	1 Nov 2006 04:17:23 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -93,8 +93,8 @@ internal_cmd_formats (struct dataset *ds
 	  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
diff -u -p -r1.9 missing-values.c
--- src/language/dictionary/missing-values.c	26 Oct 2006 06:16:36 -0000	1.9
+++ src/language/dictionary/missing-values.c	1 Nov 2006 04:17:23 -0000
@@ -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 <blp@gnu.org>.
 
    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
diff -u -p -r1.13 numeric.c
--- src/language/dictionary/numeric.c	26 Oct 2006 06:16:36 -0000	1.13
+++ src/language/dictionary/numeric.c	1 Nov 2006 04:17:24 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -59,10 +59,11 @@ cmd_numeric (struct dataset *ds)
 	{
 	  if (!parse_format_specifier (&f))
 	    goto fail;
-	  if (formats[f.type].cat & FCAT_STRING)
+	  if (fmt_is_string (f.type))
 	    {
+              char str[FMT_STRING_LEN_MAX + 1];
 	      msg (SE, _("Format type %s may not be used with a numeric "
-		   "variable."), fmt_to_string (&f));
+                         "variable."), fmt_to_string (&f, str));
 	      goto fail;
 	    }
 
@@ -129,10 +130,11 @@ cmd_string (struct dataset *ds)
 
       if (!lex_force_match ('(') || !parse_format_specifier (&f))
 	goto fail;
-      if (!(formats[f.type].cat & FCAT_STRING))
+      if (!fmt_is_string (f.type))
 	{
+          char str[FMT_STRING_LEN_MAX + 1];
 	  msg (SE, _("Format type %s may not be used with a string "
-	       "variable."), fmt_to_string (&f));
+                     "variable."), fmt_to_string (&f, str));
 	  goto fail;
 	}
 
@@ -142,17 +144,7 @@ cmd_string (struct dataset *ds)
 	  goto fail;
 	}
 
-      switch (f.type)
-	{
-	case FMT_A:
-	  width = f.w;
-	  break;
-	case FMT_AHEX:
-	  width = f.w / 2;
-	  break;
-	default:
-          NOT_REACHED ();
-	}
+      width = fmt_var_width (&f);
 
       /* Create each variable. */
       for (i = 0; i < nv; i++)
Index: src/language/dictionary/sys-file-info.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/dictionary/sys-file-info.c,v
retrieving revision 1.11
diff -u -p -r1.11 sys-file-info.c
--- src/language/dictionary/sys-file-info.c	26 Oct 2006 06:16:36 -0000	1.11
+++ src/language/dictionary/sys-file-info.c	1 Nov 2006 04:17:24 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -454,17 +454,19 @@ describe_variable (struct variable *v, s
       && 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
diff -u -p -r1.8 evaluate.c
--- src/language/expressions/evaluate.c	26 Oct 2006 06:16:36 -0000	1.8
+++ src/language/expressions/evaluate.c	1 Nov 2006 04:17:24 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -304,9 +304,11 @@ expr_debug_print_postfix (const struct e
                    op->string.string != NULL ? op->string.string : "");
           break;
         case OP_format:
-          fprintf (stderr, "f<%s%d.%d>",
-                  formats[op->format->type].name,
-                  op->format->w, op->format->d);
+          {
+            char str[FMT_STRING_LEN_MAX + 1];
+            fmt_to_string (op->format, str);
+            fprintf (stderr, "f<%s>", str); 
+          }
           break;
         case OP_variable:
           fprintf (stderr, "v<%s>", op->variable->name);
Index: src/language/expressions/operations.def
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/expressions/operations.def,v
retrieving revision 1.6
diff -u -p -r1.6 operations.def
--- src/language/expressions/operations.def	26 Oct 2006 06:16:36 -0000	1.6
+++ src/language/expressions/operations.def	1 Nov 2006 04:17:25 -0000
@@ -1,4 +1,23 @@
 // -*- c -*-
+//
+// PSPP - computes sample statistics.
+// Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+// Written by Ben Pfaff <blp@gnu.org>.
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA. */
 
 operator NEG (x) = -x;
 
@@ -567,7 +586,7 @@ absorb_miss string function STRING (x, n
 
   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
diff -u -p -r1.16 parse.c
--- src/language/expressions/parse.c	26 Oct 2006 06:16:36 -0000	1.16
+++ src/language/expressions/parse.c	1 Nov 2006 04:17:26 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ type_coercion_core (struct expression *e
       NOT_REACHED ();
 
     case OP_ni_format:
+      msg_disable ();
       if ((*node)->type == OP_format
-          && check_input_specifier (&(*node)->format.f, false)
-          && check_specifier_type (&(*node)->format.f, NUMERIC, false))
+          && fmt_check_input (&(*node)->format.f)
+          && fmt_check_type_compat (&(*node)->format.f, NUMERIC))
         {
+          msg_enable ();
           if (do_coercion)
             (*node)->type = OP_ni_format;
           return true;
         }
+      msg_enable ();
       break;
 
     case OP_no_format:
+      msg_disable ();
       if ((*node)->type == OP_format
-          && check_output_specifier (&(*node)->format.f, false)
-          && check_specifier_type (&(*node)->format.f, NUMERIC, false))
+          && fmt_check_output (&(*node)->format.f)
+          && fmt_check_type_compat (&(*node)->format.f, NUMERIC))
         {
+          msg_enable ();
           if (do_coercion)
             (*node)->type = OP_no_format;
           return true;
         }
+      msg_enable ();
       break;
 
     case OP_num_var:
Index: src/language/lexer/format-parser.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/lexer/format-parser.c,v
retrieving revision 1.5
diff -u -p -r1.5 format-parser.c
--- src/language/lexer/format-parser.c	18 Jul 2006 04:57:01 -0000	1.5
+++ src/language/lexer/format-parser.c	1 Nov 2006 04:17:26 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ parse_format_specifier (struct fmt_spec 
   if (!parse_abstract_format_specifier (type, &format->w, &format->d))
     return false;
 
-  if (!fmt_type_from_string (type, &format->type))
+  if (!fmt_from_name (type, &format->type))
     {
       msg (SE, _("Unknown format type \"%s\"."), type);
       return false;
@@ -117,14 +118,14 @@ parse_format_specifier (struct fmt_spec 
 /* 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
diff -u -p -r1.1 format-parser.h
--- src/language/lexer/format-parser.h	18 Jul 2006 04:57:01 -0000	1.1
+++ src/language/lexer/format-parser.h	1 Nov 2006 04:17:26 -0000
@@ -29,6 +29,6 @@ struct fmt_spec;
 bool parse_abstract_format_specifier (char type[FMT_TYPE_LEN_MAX + 1],
                                       int *width, int *decimals);
 bool parse_format_specifier (struct fmt_spec *);
-bool parse_format_specifier_name (int *type);
+bool parse_format_specifier_name (enum fmt_type *type);
 
 #endif /* language/lexer/format-parser.h. */
Index: src/language/lexer/range-parser.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/lexer/range-parser.c,v
retrieving revision 1.3
diff -u -p -r1.3 range-parser.c
--- src/language/lexer/range-parser.c	1 Nov 2006 02:12:25 -0000	1.3
+++ src/language/lexer/range-parser.c	1 Nov 2006 04:17:26 -0000
@@ -1,3 +1,22 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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
diff -u -p -r1.17 aggregate.c
--- src/language/stats/aggregate.c	26 Oct 2006 06:16:36 -0000	1.17
+++ src/language/stats/aggregate.c	1 Nov 2006 04:17:27 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -583,21 +583,24 @@ parse_aggregate_functions (const struct 
                     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
diff -u -p -r1.19 crosstabs.q
--- src/language/stats/crosstabs.q	29 Oct 2006 09:51:36 -0000	1.19
+++ src/language/stats/crosstabs.q	1 Nov 2006 04:17:28 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -3186,7 +3186,7 @@ format_short (char *s, const struct fmt_
   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
diff -u -p -r1.3 ChangeLog
--- src/language/utilities/ChangeLog	26 Apr 2006 22:06:48 -0000	1.3
+++ src/language/utilities/ChangeLog	1 Nov 2006 04:17:28 -0000
@@ -1,3 +1,13 @@
+Tue Oct 31 20:10:24 2006  Ben Pfaff  <blp@gnu.org>
+
+	* 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  <blp@gnu.org>
 
 	* 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
diff -u -p -r1.15 set.q
--- src/language/utilities/set.q	1 Nov 2006 02:12:25 -0000	1.15
+++ src/language/utilities/set.q	1 Nov 2006 04:17:29 -0000
@@ -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 <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -115,29 +115,28 @@ int tgetnum (const char *);
 
 /* (declarations) */
 
-/* (_functions) */
+/* (functions) */
 
-static bool do_cc (const char *cc_string, int idx);
+static bool do_cc (const char *cc_string, enum fmt_type);
 
 int
 cmd_set (struct dataset *ds)
 {
   struct cmd_set cmd;
-  bool ok = true;
 
   if (!parse_set (ds, &cmd, NULL))
     return CMD_FAILURE;
 
   if (cmd.sbc_cca)
-    ok = ok && do_cc (cmd.s_cca, 0);
+    do_cc (cmd.s_cca, FMT_CCA);
   if (cmd.sbc_ccb)
-    ok = ok && do_cc (cmd.s_ccb, 1);
+    do_cc (cmd.s_ccb, FMT_CCB);
   if (cmd.sbc_ccc)
-    ok = ok && do_cc (cmd.s_ccc, 2);
+    do_cc (cmd.s_ccc, FMT_CCC);
   if (cmd.sbc_ccd)
-    ok = ok && do_cc (cmd.s_ccd, 3);
+    do_cc (cmd.s_ccd, FMT_CCD);
   if (cmd.sbc_cce)
-    ok = ok && do_cc (cmd.s_cce, 4);
+    do_cc (cmd.s_cce, FMT_CCE);
 
   if (cmd.sbc_prompt)
     getl_set_prompt (GETL_PROMPT_FIRST, cmd.s_prompt);
@@ -147,7 +146,7 @@ cmd_set (struct dataset *ds)
     getl_set_prompt (GETL_PROMPT_DATA, cmd.s_dprompt);
 
   if (cmd.sbc_decimal)
-    set_decimal (cmd.dec == STC_DOT ? '.' : ',');
+    fmt_set_decimal (cmd.dec == STC_DOT ? '.' : ',');
   if (cmd.sbc_echo)
     set_echo (cmd.echo == STC_ON);
   if (cmd.sbc_endcmd)
@@ -203,7 +202,7 @@ cmd_set (struct dataset *ds)
    grouping and decimal members appropriately.  Returns true if
    successful, false otherwise. */
 static bool
-find_cc_separators (const char *cc_string, struct custom_currency *cc)
+find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
 {
   const char *sp;
   int comma_cnt, dot_cnt;
@@ -236,23 +235,23 @@ find_cc_separators (const char *cc_strin
   return true;
 }
 
-/* Extracts a token from IN into TOKEn.  Tokens are delimited by
-   GROUPING.  The token is truncated to at most CC_WIDTH
-   characters (including null terminator).  Returns the first
-   character following the token. */
+/* Extracts a token from IN into a newly allocated AFFIX.  Tokens
+   are delimited by GROUPING.  The token is truncated to at most
+   FMT_STYLE_AFFIX_MAX characters.  Returns the first character
+   following the token. */
 static const char *
-extract_cc_token (const char *in, int grouping, char token[CC_WIDTH]) 
+extract_cc_token (const char *in, int grouping, struct substring *affix) 
 {
-  char *out = token;
-  
+  size_t ofs = 0;
+  ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX);
   for (; *in != '\0' && *in != grouping; in++) 
     {
       if (*in == '\'' && in[1] == grouping)
         in++;
-      if (out < &token[CC_WIDTH - 1])
-        *out++ = *in;
+      if (ofs < FMT_STYLE_AFFIX_MAX) 
+        ss_data (*affix)[ofs++] = *in;
     }
-  *out = '\0';
+  affix->length = ofs;
 
   if (*in == grouping)
     in++;
@@ -262,25 +261,26 @@ extract_cc_token (const char *in, int gr
 /* Sets custom currency specifier CC having name CC_NAME ('A' through
    'E') to correspond to the settings in CC_STRING. */
 static bool
-do_cc (const char *cc_string, int idx)
+do_cc (const char *cc_string, enum fmt_type type)
 {
-  struct custom_currency cc;
+  struct fmt_number_style *cc = fmt_number_style_create ();
   
   /* Determine separators. */
-  if (!find_cc_separators (cc_string, &cc)) 
+  if (!find_cc_separators (cc_string, cc)) 
     {
-      msg (SE, _("CC%c: Custom currency string `%s' does not contain "
-                 "exactly three periods or commas (not both)."),
-           "ABCDE"[idx], cc_string);
+      fmt_number_style_destroy (cc);
+      msg (SE, _("%s: Custom currency string `%s' does not contain "
+                 "exactly three periods or commas (or it contains both)."),
+           fmt_name (type), cc_string);
       return false;
     }
   
-  cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_prefix);
-  cc_string = extract_cc_token (cc_string, cc.grouping, cc.prefix);
-  cc_string = extract_cc_token (cc_string, cc.grouping, cc.suffix);
-  cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_suffix);
+  cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix);
+  cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix);
+  cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix);
+  cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix);
 
-  set_cc (idx, &cc);
+  fmt_set_style (type, cc);
   
   return true;
 }
@@ -413,11 +413,12 @@ stc_custom_format (struct dataset *ds UN
   lex_match ('=');
   if (!parse_format_specifier (&fmt))
     return 0;
-  if ((formats[fmt.type].cat & FCAT_STRING) != 0)
+  if (fmt_is_string (fmt.type))
     {
+      char str[FMT_STRING_LEN_MAX + 1];
       msg (SE, _("FORMAT requires numeric output format as an argument.  "
 		 "Specified format %s is of type string."),
-	   fmt_to_string (&fmt));
+	   fmt_to_string (&fmt, str));
       return 0;
     }
 
@@ -479,22 +480,25 @@ show_blanks (const struct dataset *ds UN
 }
 
 static char *
-format_cc (const char *in, char grouping, char *out) 
+format_cc (struct substring in, char grouping, char *out) 
 {
-  while (*in != '\0') 
+  while (!ss_is_empty (in)) 
     {
-      if (*in == grouping || *in == '\'')
+      char c = ss_get_char (&in);
+      if (c == grouping || c == '\'')
         *out++ = '\'';
-      *out++ = *in++;
+      else if (c == '"')
+        *out++ = '"';
+      *out++ = c;
     }
   return out;
 }
 
 static void
-show_cc (int idx) 
+show_cc (enum fmt_type type) 
 {
-  const struct custom_currency *cc = get_cc (idx);
-  char cc_string[CC_WIDTH * 4 * 2 + 3 + 1];
+  const struct fmt_number_style *cc = fmt_get_style (type);
+  char cc_string[FMT_STYLE_AFFIX_MAX * 4 * 2 + 3 + 1];
   char *out;
 
   out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
@@ -506,44 +510,43 @@ show_cc (int idx) 
   out = format_cc (cc->neg_suffix, cc->grouping, out);
   *out = '\0';
   
-  msg (SN, _("CC%c is \"%s\"."), "ABCDE"[idx], cc_string);
+  msg (SN, _("%s is \"%s\"."), fmt_name (type), cc_string);
 }
 
-
 static void
 show_cca (const struct dataset *ds UNUSED) 
 {
-  show_cc (0);
+  show_cc (FMT_CCA);
 }
 
 static void
 show_ccb (const struct dataset *ds UNUSED) 
 {
-  show_cc (1);
+  show_cc (FMT_CCB);
 }
 
 static void
 show_ccc (const struct dataset *ds UNUSED) 
 {
-  show_cc (2);
+  show_cc (FMT_CCC);
 }
 
 static void
 show_ccd (const struct dataset *ds UNUSED) 
 {
-  show_cc (3);
+  show_cc (FMT_CCD);
 }
 
 static void
 show_cce (const struct dataset *ds UNUSED) 
 {
-  show_cc (4);
+  show_cc (FMT_CCE);
 }
 
 static void
 show_decimals (const struct dataset *ds UNUSED) 
 {
-  msg (SN, _("DECIMAL is \"%c\"."), get_decimal ());
+  msg (SN, _("DECIMAL is \"%c\"."), fmt_decimal_char (FMT_F));
 }
 
 static void
@@ -555,7 +558,8 @@ show_endcmd (const struct dataset *ds UN
 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
diff -u -p -r1.14 table.c
--- src/output/table.c	22 Oct 2006 11:07:52 -0000	1.14
+++ src/output/table.c	1 Nov 2006 04:17:30 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ tab_float (struct tab_table *table, int 
   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
diff -u -p -r1.12 psppire-var-store.c
--- src/ui/gui/psppire-var-store.c	19 Jun 2006 10:16:51 -0000	1.12
+++ src/ui/gui/psppire-var-store.c	1 Nov 2006 04:17:30 -0000
@@ -504,9 +504,12 @@ text_for_column(const struct PsppireVari
 	  case FMT_A:
 	    return g_locale_to_utf8(gettext(type_label[VT_STRING]), -1, 0, 0, err);
 	    break;
-	  default:
-	    g_warning("Unknown format: \"%s\"\n", 
-		      fmt_to_string(write_spec));
+	  default: 
+            {
+              char str[FMT_STRING_LEN_MAX + 1];
+              g_warning("Unknown format: \"%s\"\n", 
+                        fmt_to_string(write_spec, str)); 
+            }
 	    break;
 	  }
       }
Index: src/ui/gui/psppire-variable.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-variable.c,v
retrieving revision 1.6
diff -u -p -r1.6 psppire-variable.c
--- src/ui/gui/psppire-variable.c	17 Jul 2006 10:45:43 -0000	1.6
+++ src/ui/gui/psppire-variable.c	1 Nov 2006 04:17:30 -0000
@@ -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 @@ psppire_variable_set_format(struct Psppi
   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
diff -u -p -r1.18 psppire.c
--- src/ui/gui/psppire.c	1 Nov 2006 03:25:36 -0000	1.18
+++ src/ui/gui/psppire.c	1 Nov 2006 04:17:31 -0000
@@ -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 @@ main(int argc, char *argv[]) 
 
 
 
+  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
diff -u -p -r1.5 var-type-dialog.c
--- src/ui/gui/var-type-dialog.c	1 Nov 2006 03:25:36 -0000	1.5
+++ src/ui/gui/var-type-dialog.c	1 Nov 2006 04:17:31 -0000
@@ -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 @@ preview_custom(GtkWidget *w, gpointer da
   text = gtk_entry_get_text(GTK_ENTRY(dialog->entry_width));
   dialog->fmt_l.w = atoi(text);
 
-  if ( ! check_output_specifier(&dialog->fmt_l, 0))
+  msg_disable ();
+  if ( ! fmt_check_output(&dialog->fmt_l))
     {
       gtk_label_set_text(GTK_LABEL(dialog->label_psample), "---");
       gtk_label_set_text(GTK_LABEL(dialog->label_nsample), "---");
@@ -311,6 +313,7 @@ preview_custom(GtkWidget *w, gpointer da
       gtk_label_set_text(GTK_LABEL(dialog->label_nsample), sample_text);
       g_free(sample_text);
     }
+  msg_enable ();
 }
 
 /* Callback for when a treeview row is changed.
@@ -539,10 +542,11 @@ var_type_dialog_create(GladeXML *xml)
   list_store = gtk_list_store_new (2, G_TYPE_STRING, 
 						 G_TYPE_POINTER);
 
-  for ( i = 0 ; i < CC_CNT ; ++i ) 
+  for ( i = 0 ; i < 5 ; ++i ) 
     {
+      enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE};
       gchar text[4];
-      g_snprintf(text, 4, "CC%c", 'A' + i);
+      g_snprintf(text, 4, "%s", fmt_name (cc_fmts[i]));
       gtk_list_store_append (list_store, &iter);
       gtk_list_store_set (list_store, &iter,
                           0, text,
@@ -636,9 +640,11 @@ select_treeview_from_format(GtkTreeView 
       gtk_tree_view_set_cursor(treeview, path, 0, 0);
       gtk_tree_path_free(path);
     }
-  else
-    g_warning("Unusual date format: %s\n", fmt_to_string(fmt));
-
+  else 
+    {
+      char str[FMT_STRING_LEN_MAX + 1];
+      g_warning("Unusual date format: %s\n", fmt_to_string(fmt, str));
+    }
 }
 
 
@@ -788,7 +794,7 @@ make_output_format_try (struct fmt_spec 
   f->type = type;
   f->w = w;
   f->d = d;
-  return check_output_specifier (f, true);
+  return fmt_check_output (f);
 }
 
 
@@ -838,7 +844,7 @@ on_var_type_ok_clicked(GtkWidget *w, gpo
 	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
diff -u -p -r1.17 main.c
--- src/ui/terminal/main.c	28 Oct 2006 08:31:23 -0000	1.17
+++ src/ui/terminal/main.c	1 Nov 2006 04:17:31 -0000
@@ -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 <blp@gnu.org>.
 
    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 @@ main (int argc, char **argv)
   fpu_init ();
   gsl_set_error_handler_off ();
 
+  fmt_init ();
   outp_init ();
   msg_ui_init ();
   fn_init ();
