+ 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;
+}
+\f
+/* 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 ();
+ }
+}
+\f
+/* 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)