Adopt use of gnulib for portability.
[pspp-builds.git] / src / recode.c
index 146e8a36a5b28e102558638a16b85bd18cd9cf8b..b25ac4567ea621609825abf39f1752ccd7adc6a3 100644 (file)
 
    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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
-#include <assert.h>
+#include "error.h"
 #include <ctype.h>
+#include <math.h>
 #include <stdlib.h>
 #include "alloc.h"
-#include "approx.h"
-#include "cases.h"
+#include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "magic.h"
 #include "str.h"
 #include "var.h"
 
-#include "debug-print.h"
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 \f
 /* Definitions. */
 
+/* Type of source value for RECODE. */
 enum
   {
     RCD_END,                   /* sentinel value */
@@ -66,7 +69,7 @@ struct rcd_var
 
     struct variable *src;      /* Source variable. */
     struct variable *dest;     /* Destination variable. */
-    char dest_name[9];         /* Name of dest variable if we're creating it. */
+    char dest_name[LONG_NAME_LEN + 1];         /* Name of dest variable if we're creating it. */
 
     int has_sysmis;            /* Do we recode for SYSMIS? */
     union value sysmis;                /* Coding for SYSMIS (if src is numeric). */
@@ -105,23 +108,12 @@ struct recode_trns
 static int parse_dest_spec (struct rcd_var * rcd, union value *v,
                            size_t *max_dst_width);
 static int parse_src_spec (struct rcd_var * rcd, int type, size_t max_src_width);
-static int recode_trns_proc (struct trns_header *, struct ccase *);
-static void recode_trns_free (struct trns_header *);
-static double convert_to_double (char *, int);
-
-#if DEBUGGING
-static void debug_print (struct rcd_var * head);
-#endif
+static trns_proc_func recode_trns_proc;
+static trns_free_func recode_trns_free;
+static double convert_to_double (const char *, int);
 \f
 /* Parser. */
 
-/* First transformation in the list.  rcd is in this list. */
-static struct rcd_var *head;
-
-/* Variables in the current part of the recoding. */
-struct variable **v;
-int nv;
-
 /* Parses the RECODE transformation. */
 int
 cmd_recode (void)
@@ -148,10 +140,16 @@ cmd_recode (void)
      rcd_var's. */
   struct recode_trns *trns;
 
-  lex_match_id ("RECODE");
+  /* First transformation in the list.  rcd is in this list. */
+  struct rcd_var *head;
+
+  /* Variables in the current part of the recoding. */
+  struct variable **v;
+  int nv;
 
   /* Parses each specification between slashes. */
   head = rcd = xmalloc (sizeof *rcd);
+  v = NULL;
   for (;;)
     {
       /* Whether we've already encountered a specification for SYSMIS. */
@@ -165,7 +163,7 @@ cmd_recode (void)
       rcd->sysmis.f = 0;
 
       /* Parse variable names. */
-      if (!parse_variables (NULL, &v, &nv, PV_SAME_TYPE))
+      if (!parse_variables (default_dict, &v, &nv, PV_SAME_TYPE))
        goto lossage;
 
       /* Ensure all variables are same type; find length of longest
@@ -239,7 +237,7 @@ cmd_recode (void)
              else
                {
                  for (i = mark; i < rcd->nmap; i++)
-                   rcd->map[i].t.c = xstrdup (output.c);
+                   rcd->map[i].t.c = (output.c?xstrdup (output.c):NULL);
                  free (output.c);
                }
            }
@@ -295,7 +293,7 @@ cmd_recode (void)
          if ((rcd->flags & RCD_DEST_MASK) == RCD_DEST_STRING)
            for (i = 0, iter = rcd; i < nv; i++, iter = iter->next)
              {
-               struct variable *v = find_variable (names[i]);
+               struct variable *v = dict_lookup_var (default_dict, names[i]);
 
                if (!v)
                  {
@@ -321,7 +319,7 @@ cmd_recode (void)
          else
            for (i = 0, iter = rcd; i < nv; i++, iter = iter->next)
              {
-               struct variable *v = find_variable (names[i]);
+               struct variable *v = dict_lookup_var (default_dict, names[i]);
 
                if (v)
                  {
@@ -383,7 +381,7 @@ cmd_recode (void)
                    /* The NULL is only really necessary for the
                       debugging code. */
                    char *repl = xmalloc (max_dst_width + 1);
-                   st_pad_copy (repl, cp->t.c, max_dst_width + 1);
+                   str_copy_rpad (repl, max_dst_width + 1, cp->t.c);
                    free (cp->t.c);
                    cp->t.c = repl;
                  }
@@ -395,13 +393,14 @@ cmd_recode (void)
          
        }
 
+      free (v);
+      v = NULL;
+
       if (!lex_match ('/'))
        break;
       while (rcd->next)
        rcd = rcd->next;
       rcd = rcd->next = xmalloc (sizeof *rcd);
-
-      free (v);
     }
 
   if (token != '.')
@@ -413,17 +412,14 @@ cmd_recode (void)
   for (rcd = head; rcd; rcd = rcd->next)
     if (rcd->dest_name[0])
       {
-       rcd->dest = create_variable (&default_dict, rcd->dest_name,
-                                    NUMERIC, 0);
+       rcd->dest = dict_create_var (default_dict, rcd->dest_name, 0);
        if (!rcd->dest)
          {
-           /* This can occur if a destname is duplicated.  We could
-              give an error at parse time but I don't care enough. */
-           rcd->dest = find_variable (rcd->dest_name);
-           assert (rcd->dest != NULL);
+           /* FIXME: This can fail if a destname is duplicated.
+              We could give an error at parse time but I don't
+              care enough. */
+           rcd->dest = dict_lookup_var_assert (default_dict, rcd->dest_name);
          }
-       else
-         envector (rcd->dest);
       }
 
   trns = xmalloc (sizeof *trns);
@@ -432,13 +428,10 @@ cmd_recode (void)
   trns->codings = head;
   add_transformation ((struct trns_header *) trns);
 
-#if DEBUGGING
-  debug_print (head);
-#endif
-
   return CMD_SUCCESS;
 
  lossage:
+  free (v);
   {
     struct recode_trns t;
 
@@ -455,7 +448,7 @@ parse_dest_spec (struct rcd_var * rcd, union value * v, size_t *max_dst_width)
 
   v->c = NULL;
 
-  if (token == T_NUM)
+  if (lex_is_number ())
     {
       v->f = tokval;
       lex_get ();
@@ -473,7 +466,7 @@ parse_dest_spec (struct rcd_var * rcd, union value * v, size_t *max_dst_width)
       if (toklen > max)
        max = toklen;
       v->c = xmalloc (max + 1);
-      st_pad_copy (v->c, ds_value (&tokstr), max + 1);
+      str_copy_rpad (v->c, max + 1, ds_c_str (&tokstr));
       flags = RCD_DEST_STRING;
       *max_dst_width = max;
       lex_get ();
@@ -491,6 +484,11 @@ parse_dest_spec (struct rcd_var * rcd, union value * v, size_t *max_dst_width)
          v->c = NULL;
        }
     }
+  else 
+    {
+      lex_error (_("expecting output value"));
+      return 0;
+    }
 
   if ((rcd->flags & RCD_DEST_MASK) == RCD_DEST_ERROR)
     rcd->flags |= flags;
@@ -545,7 +543,7 @@ parse_src_spec (struct rcd_var * rcd, int type, size_t max_src_width)
                    return 0;
                  if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
                    c->type = RCD_ELSE;
-                 else if (token == T_NUM)
+                 else if (lex_is_number ())
                    {
                      c->type = RCD_LOW;
                      c->f1.f = tokval;
@@ -573,7 +571,7 @@ parse_src_spec (struct rcd_var * rcd, int type, size_t max_src_width)
                  return 0;
                }
            }
-         else if (token == T_NUM)
+         else if (lex_is_number ())
            {
              c->f1.f = tokval;
              lex_get ();
@@ -581,7 +579,7 @@ parse_src_spec (struct rcd_var * rcd, int type, size_t max_src_width)
                {
                  if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
                    c->type = RCD_HIGH;
-                 else if (token == T_NUM)
+                 else if (lex_is_number ())
                    {
                      c->type = RCD_RANGE;
                      c->f2.f = tokval;
@@ -631,7 +629,7 @@ parse_src_spec (struct rcd_var * rcd, int type, size_t max_src_width)
              if (!lex_force_string ())
                return 0;
              c->f1.c = xmalloc (max_src_width + 1);
-             st_pad_copy (c->f1.c, ds_value (&tokstr), max_src_width + 1);
+             str_copy_rpad (c->f1.c, max_src_width + 1, ds_c_str (&tokstr));
              lex_get ();
            }
        }
@@ -694,7 +692,7 @@ recode_trns_free (struct trns_header * t)
 static inline struct coding *
 find_src_numeric (struct rcd_var * v, struct ccase * c)
 {
-  double cmp = c->data[v->src->fv].f;
+  double cmp = case_num (c, v->src->fv);
   struct coding *cp;
 
   if (cmp == SYSMIS)
@@ -702,9 +700,9 @@ find_src_numeric (struct rcd_var * v, struct ccase * c)
       if (v->sysmis.f != -SYSMIS)
        {
          if ((v->flags & RCD_DEST_MASK) == RCD_DEST_NUMERIC)
-           c->data[v->dest->fv].f = v->sysmis.f;
+            case_data_rw (c, v->dest->fv)->f = v->sysmis.f;
          else
-           memcpy (c->data[v->dest->fv].s, v->sysmis.c,
+           memcpy (case_data_rw (c, v->dest->fv)->s, v->sysmis.s,
                    v->dest->width);
        }
       return NULL;
@@ -720,19 +718,19 @@ find_src_numeric (struct rcd_var * v, struct ccase * c)
          return cp;
        break;
       case RCD_SINGLE:
-       if (approx_eq (cmp, cp->f1.f))
+       if (cmp == cp->f1.f)
          return cp;
        break;
       case RCD_HIGH:
-       if (approx_ge (cmp, cp->f1.f))
+       if (cmp >= cp->f1.f)
          return cp;
        break;
       case RCD_LOW:
-       if (approx_le (cmp, cp->f1.f))
+       if (cmp <= cp->f1.f)
          return cp;
        break;
       case RCD_RANGE:
-       if (approx_in_range (cmp, cp->f1.f, cp->f2.f))
+       if (cmp >= cp->f1.f && cmp <= cp->f2.f)
          return cp;
        break;
       case RCD_ELSE:
@@ -745,7 +743,7 @@ find_src_numeric (struct rcd_var * v, struct ccase * c)
 static inline struct coding *
 find_src_string (struct rcd_var * v, struct ccase * c)
 {
-  char *cmp = c->data[v->src->fv].s;
+  const char *cmp = case_str (c, v->src->fv);
   int w = v->src->width;
   struct coding *cp;
 
@@ -765,7 +763,7 @@ find_src_string (struct rcd_var * v, struct ccase * c)
          double f = convert_to_double (cmp, w);
          if (f != -SYSMIS)
            {
-             c->data[v->dest->fv].f = f;
+              case_data_rw (c, v->dest->fv)->f = f;
              return NULL;
            }
          break;
@@ -776,13 +774,15 @@ find_src_string (struct rcd_var * v, struct ccase * c)
 }
 
 static int
-recode_trns_proc (struct trns_header * t, struct ccase * c)
+recode_trns_proc (struct trns_header * t, struct ccase * c,
+                  int case_idx UNUSED)
 {
   struct rcd_var *v;
-  struct coding *cp;
 
   for (v = ((struct recode_trns *) t)->codings; v; v = v->next)
     {
+      struct coding *cp;
+
       switch (v->flags & RCD_SRC_MASK)
        {
        case RCD_SRC_NUMERIC:
@@ -791,6 +791,9 @@ recode_trns_proc (struct trns_header * t, struct ccase * c)
        case RCD_SRC_STRING:
          cp = find_src_string (v, c);
          break;
+        default:
+          assert (0);
+          abort ();
        }
       if (!cp)
        continue;
@@ -799,144 +802,42 @@ recode_trns_proc (struct trns_header * t, struct ccase * c)
       if ((v->flags & RCD_DEST_MASK) == RCD_DEST_NUMERIC)
        {
          double val = cp->t.f;
+          double *out = &case_data_rw (c, v->dest->fv)->f;
          if (val == -SYSMIS)
-           c->data[v->dest->fv].f = c->data[v->src->fv].f;
+           *out = case_num (c, v->src->fv);
          else
-           c->data[v->dest->fv].f = val;
+           *out = val;
        }
       else
        {
          char *val = cp->t.c;
-         if (val == NULL)
-           st_bare_pad_len_copy (c->data[v->dest->fv].s,
-                                 c->data[v->src->fv].c,
-                                 v->dest->width, v->src->width);
+         if (val == NULL) 
+            {
+              if (v->dest->fv != v->src->fv)
+                buf_copy_rpad (case_data_rw (c, v->dest->fv)->s,
+                               v->dest->width,
+                               case_str (c, v->src->fv), v->src->width); 
+            }
          else
-           memcpy (c->data[v->dest->fv].s, cp->t.c, v->dest->width);
+           memcpy (case_data_rw (c, v->dest->fv)->s, cp->t.c, v->dest->width);
        }
     }
 
   return -1;
 }
-\f
-/* Debug output. */
-
-#if DEBUGGING
-static void
-dump_dest (struct rcd_var * v, union value * c)
-{
-  if ((v->flags & RCD_DEST_MASK) == RCD_DEST_NUMERIC)
-    if (c->f == SYSMIS)
-      printf ("=SYSMIS");
-    else if (c->f == -SYSMIS)
-      printf ("=COPY");
-    else
-      printf ("=%g", c->f);
-  else if (c->c)
-    printf ("=\"%s\"", c->c);
-  else
-    printf ("=COPY");
-}
-
-static void
-debug_print (struct rcd_var * head)
-{
-  struct rcd_var *iter, *start;
-  struct coding *c;
-
-  printf ("RECODE\n");
-  for (iter = head; iter; iter = iter->next)
-    {
-      start = iter;
-      printf ("  %s%s", iter == head ? "" : "/", iter->src->name);
-      while (iter->next && (iter->next->flags & RCD_MISC_DUPLICATE))
-       {
-         iter = iter->next;
-         printf (" %s", iter->src->name);
-       }
-      if (iter->has_sysmis)
-       {
-         printf ("(SYSMIS");
-         dump_dest (iter, &iter->sysmis);
-         printf (")");
-       }
-      for (c = iter->map; c->type != RCD_END; c++)
-       {
-         printf ("(");
-         if ((iter->flags & RCD_SRC_MASK) == RCD_SRC_NUMERIC)
-           switch (c->type)
-             {
-             case RCD_END:
-               printf (_("!!END!!"));
-               break;
-             case RCD_USER:
-               printf ("MISSING");
-               break;
-             case RCD_SINGLE:
-               printf ("%g", c->f1.f);
-               break;
-             case RCD_HIGH:
-               printf ("%g THRU HIGH", c->f1.f);
-               break;
-             case RCD_LOW:
-               printf ("LOW THRU %g", c->f1.f);
-               break;
-             case RCD_RANGE:
-               printf ("%g THRU %g", c->f1.f, c->f2.f);
-               break;
-             case RCD_ELSE:
-               printf ("ELSE");
-               break;
-             default:
-               printf (_("!!ERROR!!"));
-               break;
-             }
-         else
-           switch (c->type)
-             {
-             case RCD_SINGLE:
-               printf ("\"%s\"", c->f1.c);
-               break;
-             case RCD_ELSE:
-               printf ("ELSE");
-               break;
-             case RCD_CONVERT:
-               printf ("CONVERT");
-               break;
-             default:
-               printf (_("!!ERROR!!"));
-               break;
-             }
-         if (c->type != RCD_CONVERT)
-           dump_dest (iter, &c->t);
-         printf (")");
-       }
-      printf ("\n    INTO");
-      for (;;)
-       {
-         printf (" %s",
-               start->dest_name[0] ? start->dest_name : start->dest->name);
-         if (start == iter)
-           break;
-         start = start->next;
-       }
-      printf ("\n");
-    }
-}
-#endif
 
 /* Convert NPTR to a `long int' in base 10.  Returns the long int on
    success, NOT_LONG on failure.  On success stores a pointer to the
    first character after the number into *ENDPTR.  From the GNU C
    library. */
-long int
-string_to_long (char *nptr, int width, char **endptr)
+static long int
+string_to_long (const char *nptr, int width, const char **endptr)
 {
   int negative;
   register unsigned long int cutoff;
   register unsigned int cutlim;
   register unsigned long int i;
-  register char *s;
+  register const char *s;
   register unsigned char c;
   const char *save;
 
@@ -1006,7 +907,7 @@ string_to_long (char *nptr, int width, char **endptr)
    found, or -SYSMIS if there was no valid number in s.  WIDTH is the
    length of string S.  From the GNU C library. */
 static double
-convert_to_double (char *s, int width)
+convert_to_double (const char *s, int width)
 {
   register const char *end = &s[width];