VECTOR: Eliminate VAR_NAME_LEN limit for variable names.
[pspp-builds.git] / src / language / dictionary / vector.c
index 050886d9effd334d203b627e37d6d2e91c6595d4..8eb7ef16f17bce3ca77f119d85d342fc4fa22afe 100644 (file)
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2010, 2011 Free Software Foundation, Inc.
 
-   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 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 3 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.
+   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. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
 #include <stdlib.h>
 
+#include <data/format.h>
 #include <data/procedure.h>
 #include <data/dictionary.h>
 #include <data/variable.h>
 #include <language/command.h>
+#include <language/lexer/format-parser.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
-#include <libpspp/alloc.h>
 #include <libpspp/assertion.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
+#include <libpspp/pool.h>
 #include <libpspp/str.h>
 
+#include "intprops.h"
+#include "xalloc.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 int
-cmd_vector (void)
+cmd_vector (struct lexer *lexer, struct dataset *ds)
 {
-  /* Just to be different, points to a set of null terminated strings
-     containing the names of the vectors to be created.  The list
-     itself is terminated by a empty string.  So a list of three
-     elements, A B C, would look like this: "A\0B\0C\0\0". */
-  char *vecnames;
-
-  /* vecnames iterators. */
-  char *cp, *cp2;
+  struct dictionary *dict = dataset_dict (ds);
+  struct pool *pool = pool_create ();
 
-  /* Maximum allocated position for vecnames, plus one position. */
-  char *endp = NULL;
-
-  cp = vecnames = xmalloc (256);
-  endp = &vecnames[256];
   do
     {
+      char **vectors;
+      size_t vector_cnt, vector_cap;
+
       /* Get the name(s) of the new vector(s). */
-      if (!lex_force_id ())
+      if (!lex_force_id (lexer))
        return CMD_CASCADING_FAILURE;
-      while (token == T_ID)
+
+      vectors = NULL;
+      vector_cnt = vector_cap = 0;
+      while (lex_token (lexer) == T_ID)
        {
-         if (cp + 16 > endp)
+          size_t i;
+
+         if (dict_lookup_vector (dict, lex_tokcstr (lexer)))
            {
-             char *old_vecnames = vecnames;
-             vecnames = xrealloc (vecnames, endp - vecnames + 256);
-             cp = (cp - old_vecnames) + vecnames;
-             endp = (endp - old_vecnames) + vecnames + 256;
+             msg (SE, _("A vector named %s already exists."),
+                   lex_tokcstr (lexer));
+             goto fail;
            }
 
-         for (cp2 = cp; cp2 < cp; cp2 += strlen (cp))
-           if (!strcasecmp (cp2, tokid))
+          for (i = 0; i < vector_cnt; i++)
+            if (!strcasecmp (vectors[i], lex_tokcstr (lexer)))
              {
-               msg (SE, _("Vector name %s is given twice."), tokid);
+               msg (SE, _("Vector name %s is given twice."),
+                     lex_tokcstr (lexer));
                goto fail;
              }
 
-         if (dict_lookup_vector (dataset_dict (current_dataset), tokid))
-           {
-             msg (SE, _("There is already a vector with name %s."), tokid);
-             goto fail;
-           }
+          if (vector_cnt == vector_cap)
+            vectors = pool_2nrealloc (pool,
+                                       vectors, &vector_cap, sizeof *vectors);
+          vectors[vector_cnt++] = pool_strdup (pool, lex_tokcstr (lexer));
 
-         cp = stpcpy (cp, tokid) + 1;
-         lex_get ();
-         lex_match (',');
+         lex_get (lexer);
+         lex_match (lexer, T_COMMA);
        }
-      *cp++ = 0;
 
       /* Now that we have the names it's time to check for the short
          or long forms. */
-      if (lex_match ('='))
+      if (lex_match (lexer, T_EQUALS))
        {
          /* Long form. */
           struct variable **v;
           size_t nv;
 
-         if (strchr (vecnames, '\0')[1])
+         if (vector_cnt > 1)
            {
-             /* There's more than one vector name. */
-             msg (SE, _("A slash must be used to separate each vector "
-                         "specification when using the long form.  Commands "
-                         "such as VECTOR A,B=Q1 TO Q20 are not supported."));
+             msg (SE, _("A slash must separate each vector "
+                         "specification in VECTOR's long form."));
              goto fail;
            }
 
-         if (!parse_variables (dataset_dict (current_dataset), &v, &nv,
-                                PV_SAME_TYPE | PV_DUPLICATE))
+         if (!parse_variables_pool (lexer, pool, dict, &v, &nv,
+                                     PV_SAME_WIDTH | PV_DUPLICATE))
            goto fail;
 
-          dict_create_vector (dataset_dict (current_dataset), vecnames, v, nv);
-          free (v);
+          dict_create_vector (dict, vectors[0], v, nv);
        }
-      else if (lex_match ('('))
+      else if (lex_match (lexer, T_LPAREN))
        {
-         int i;
-
-         /* Maximum number of digits in a number to add to the base
-            vecname. */
-         int ndig;
-
-         /* Name of an individual variable to be created. */
-         char name[SHORT_NAME_LEN + 1];
-
-          /* Vector variables. */
-          struct variable **v;
-          int nv;
-
-         if (!lex_force_int ())
-           return CMD_CASCADING_FAILURE;
-         nv = lex_integer ();
-         lex_get ();
-         if (nv <= 0)
-           {
-             msg (SE, _("Vectors must have at least one element."));
-             goto fail;
-           }
-         if (!lex_force_match (')'))
-           goto fail;
-
-         /* First check that all the generated variable names
-            are LONG_NAME_LEN characters or shorter. */
-         ndig = intlog10 (nv);
-         for (cp = vecnames; *cp;)
-           {
-             int len = strlen (cp);
-             if (len + ndig > LONG_NAME_LEN)
-               {
-                 msg (SE, _("%s%d is too long for a variable name."), cp, nv);
-                 goto fail;
-               }
-             cp += len + 1;
-           }
-
-         /* Next check that none of the variables exist. */
-         for (cp = vecnames; *cp;)
+          /* Short form. */
+          struct fmt_spec format;
+          bool seen_format = false;
+
+          struct variable **vars;
+          int var_cnt;
+
+          size_t i;
+
+          var_cnt = 0;
+          format = fmt_for_output (FMT_F, 8, 2);
+          seen_format = false;
+          while (!lex_match (lexer, T_RPAREN))
+            {
+              if (lex_is_integer (lexer) && var_cnt == 0)
+                {
+                  var_cnt = lex_integer (lexer);
+                  lex_get (lexer);
+                  if (var_cnt <= 0)
+                    {
+                      msg (SE, _("Vectors must have at least one element."));
+                      goto fail;
+                    }
+                }
+              else if (lex_token (lexer) == T_ID && !seen_format)
+                {
+                  seen_format = true;
+                  if (!parse_format_specifier (lexer, &format)
+                      || !fmt_check_output (&format)
+                      || !fmt_check_type_compat (&format, VAL_NUMERIC))
+                    goto fail;
+                }
+              else
+                {
+                  lex_error (lexer, NULL);
+                  goto fail;
+                }
+              lex_match (lexer, T_COMMA);
+            }
+          if (var_cnt == 0)
+            {
+              lex_error (lexer, _("expecting vector length"));
+              goto fail;
+            }
+
+         /* Check that none of the variables exist and that
+             their names are no more than VAR_NAME_LEN bytes
+             long. */
+          for (i = 0; i < vector_cnt; i++)
            {
-             for (i = 0; i < nv; i++)
+              int j;
+             for (j = 0; j < var_cnt; j++)
                {
-                 sprintf (name, "%s%d", cp, i + 1);
-                 if (dict_lookup_var (dataset_dict (current_dataset), name))
+                  char *name = xasprintf ("%s%d", vectors[i], j + 1);
+                  if (strlen (name) > VAR_NAME_LEN)
+                    {
+                      free (name);
+                      msg (SE, _("%s is too long for a variable name."), name);
+                      goto fail;
+                    }
+                  if (dict_lookup_var (dict, name))
                    {
-                     msg (SE, _("There is already a variable named %s."),
-                           name);
+                      free (name);
+                     msg (SE, _("%s is an existing variable name."), name);
                      goto fail;
                    }
+                  free (name);
                }
-             cp += strlen (cp) + 1;
            }
 
          /* Finally create the variables and vectors. */
-          v = xmalloc (nv * sizeof *v);
-         for (cp = vecnames; *cp;)
+          vars = pool_nmalloc (pool, var_cnt, sizeof *vars);
+          for (i = 0; i < vector_cnt; i++)
            {
-             for (i = 0; i < nv; i++)
+              int j;
+             for (j = 0; j < var_cnt; j++)
                {
-                 sprintf (name, "%s%d", cp, i + 1);
-                 v[i] = dict_create_var_assert (dataset_dict (current_dataset), name, 0);
+                  char *name = xasprintf ("%s%d", vectors[i], j + 1);
+                 vars[j] = dict_create_var_assert (dict, name, 0);
+                  var_set_both_formats (vars[j], &format);
+                  free (name);
                }
-              if (!dict_create_vector (dataset_dict (current_dataset), cp, v, nv))
-                NOT_REACHED ();
-             cp += strlen (cp) + 1;
+              dict_create_vector_assert (dict, vectors[i], vars, var_cnt);
            }
-          free (v);
        }
       else
        {
-         msg (SE, _("The syntax for this command does not match "
-              "the expected syntax for either the long form "
-              "or the short form of VECTOR."));
+          lex_error (lexer, NULL);
          goto fail;
        }
-
-      free (vecnames);
-      vecnames = NULL;
     }
-  while (lex_match ('/'));
+  while (lex_match (lexer, T_SLASH));
 
-  if (token != '.')
-    {
-      lex_error (_("expecting end of command"));
-      goto fail;
-    }
-  return CMD_SUCCESS;
+  pool_destroy (pool);
+  return lex_end_of_command (lexer);
 
 fail:
-  free (vecnames);
+  pool_destroy (pool);
   return CMD_FAILURE;
 }