str: Add function xstrdup_if_nonnull() and introduce many users.
[pspp] / src / data / format.c
index 8aa16e65fffc63f045a4d60e205a59dd9bb2536b..a4c52b788ebd811541b9e3ac7d0e25beab016d81 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <ctype.h>
 #include <stdlib.h>
+#include <time.h>
 #include <uniwidth.h>
 
 #include "data/identifier.h"
@@ -62,12 +63,13 @@ fmt_settings_uninit (struct fmt_settings *settings)
     fmt_number_style_destroy (settings->ccs[i]);
 }
 
-void
-fmt_settings_copy (struct fmt_settings *new, const struct fmt_settings *old)
+struct fmt_settings
+fmt_settings_copy (const struct fmt_settings *old)
 {
-  new->decimal = old->decimal;
+  struct fmt_settings new = *old;
   for (int i = 0; i < FMT_N_CCS; i++)
-    new->ccs[i] = fmt_number_style_clone (old->ccs[i]);
+    new.ccs[i] = fmt_number_style_clone (old->ccs[i]);
+  return new;
 }
 
 static size_t
@@ -147,6 +149,25 @@ fmt_settings_get_style (const struct fmt_settings *settings,
     }
 }
 
+static int
+default_epoch (void)
+{
+  static int epoch = 0;
+  if (!epoch)
+    {
+      time_t t = time (0);
+      struct tm *tm = localtime (&t);
+      epoch = (tm != NULL ? tm->tm_year + 1900 : 2000) - 69;
+    }
+  return epoch;
+}
+
+int
+fmt_settings_get_epoch (const struct fmt_settings *settings)
+{
+  return !settings->epoch ? default_epoch () : settings->epoch;
+}
+
 void
 fmt_settings_set_cc (struct fmt_settings *settings, enum fmt_type type,
                      struct fmt_number_style *style)
@@ -187,7 +208,8 @@ fmt_for_output (enum fmt_type type, int w, int d)
 /* Returns the output format specifier corresponding to input
    format specifier INPUT. */
 struct fmt_spec
-fmt_for_output_from_input (const struct fmt_spec *input)
+fmt_for_output_from_input (const struct fmt_spec *input,
+                           const struct fmt_settings *settings)
 {
   struct fmt_spec output;
 
@@ -216,7 +238,7 @@ fmt_for_output_from_input (const struct fmt_spec *input)
     case FMT_PCT:
       {
         const struct fmt_number_style *style =
-         settings_get_style (input->type);
+         fmt_settings_get_style (settings, input->type);
 
         output.w += fmt_affix_width (style);
         if (style->grouping != 0 && input->w - input->d >= 3)
@@ -1135,7 +1157,7 @@ static struct fmt_affix
 fmt_affix_clone (const struct fmt_affix *old)
 {
   return (struct fmt_affix) {
-    .s = old->s ? xstrdup (old->s) : NULL,
+    .s = xstrdup_if_nonnull (old->s),
     .width = old->width,
   };
 }
@@ -1218,6 +1240,34 @@ fmt_number_style_from_string (const char *s)
   return style;
 }
 
+static void
+format_cc (struct string *out, const char *in, char grouping)
+{
+  while (*in != '\0')
+    {
+      char c = *in++;
+      if (c == grouping || c == '\'')
+        ds_put_byte (out, '\'');
+      else if (c == '"')
+        ds_put_byte (out, '"');
+      ds_put_byte (out, c);
+    }
+}
+
+char *
+fmt_number_style_to_string (const struct fmt_number_style *cc)
+{
+  struct string out = DS_EMPTY_INITIALIZER;
+  format_cc (&out, cc->neg_prefix.s, cc->grouping);
+  ds_put_byte (&out, cc->grouping);
+  format_cc (&out, cc->prefix.s, cc->grouping);
+  ds_put_byte (&out, cc->grouping);
+  format_cc (&out, cc->suffix.s, cc->grouping);
+  ds_put_byte (&out, cc->grouping);
+  format_cc (&out, cc->neg_suffix.s, cc->grouping);
+  return ds_steal_cstr (&out);
+}
+
 struct fmt_number_style *
 fmt_number_style_clone (const struct fmt_number_style *old)
 {