Adopt use of gnulib for portability.
[pspp] / src / command.c
index 37608055890abbc3533e30babcfa47615831b2dd..4203ac32d0ea7f478ed1dd65a51bae61ed58f13a 100644 (file)
@@ -14,8 +14,8 @@
 
    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 "error.h"
 #include <ctype.h>
 #include <errno.h>
 #include "alloc.h"
+#include "dictionary.h"
 #include "error.h"
-#include "getline.h"
+#include "glob.h"
+#include "getl.h"
 #include "lexer.h"
 #include "main.h"
 #include "settings.h"
 #if HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 \f
 /* Global variables. */
 
@@ -62,22 +68,67 @@ struct command
     int (*func) (void);                /* Function to call. */
     int skip_entire_name;       /* If zero, we don't skip the
                                    final token in the command name. */
+    short debug;                /* Set if this cmd available only in test mode*/
   };
 
 /* Define the command array. */
 #define DEFCMD(NAME, T1, T2, T3, T4, FUNC)             \
-       {NAME, {T1, T2, T3, T4}, FUNC, 1},
+       {NAME, {T1, T2, T3, T4}, FUNC, 1, 0},
+#define DBGCMD(NAME, T1, T2, T3, T4, FUNC)             \
+       {NAME, {T1, T2, T3, T4}, FUNC, 1, 1},
 #define SPCCMD(NAME, T1, T2, T3, T4, FUNC)             \
-       {NAME, {T1, T2, T3, T4}, FUNC, 0},
-#define UNIMPL(NAME, T1, T2, T3, T4)                   \
-       {NAME, {T1, T2, T3, T4}, NULL, 1},
+       {NAME, {T1, T2, T3, T4}, FUNC, 0, 0},
+#define UNIMPL(NAME, T1, T2, T3, T4, DESC)             \
+       {NAME, {T1, T2, T3, T4}, NULL, 1, 0},
 static const struct command commands[] = 
   {
 #include "command.def"
   };
 #undef DEFCMD
+#undef DBGCMD
 #undef UNIMPL
 
+
+/* Complete the line using the name of a command, 
+ * based upon the current prg_state
+ */
+char * 
+pspp_completion_function (const char *text,   int state)
+{
+  static int skip=0;
+  const struct command *cmd = 0;
+  
+  for(;;)
+    {
+      if ( state + skip >= sizeof(commands)/ sizeof(struct command))
+       {
+         skip = 0;
+         return 0;
+       }
+
+      cmd = &commands[state + skip];
+  
+      if ( cmd->transition[pgm_state] == STATE_ERROR || ( cmd->debug  &&  ! test_mode ) ) 
+       {
+         skip++; 
+         continue;
+       }
+      
+      if ( text == 0 || 0 == strncasecmp (cmd->name, text, strlen(text)))
+       {
+         break;
+       }
+
+      skip++;
+    }
+  
+
+  return xstrdup(cmd->name);
+
+}
+
+
+
 #define COMMAND_CNT (sizeof commands / sizeof *commands)
 \f
 /* Command parser. */
@@ -87,7 +138,8 @@ static const struct command *parse_command_name (void);
 /* Determines whether command C is appropriate to call in this
    part of a FILE TYPE structure. */
 static int
-FILE_TYPE_okay (const struct command *c)
+FILE_TYPE_okay (const struct command *c UNUSED)
+#if 0
 {
   int okay = 0;
   
@@ -96,7 +148,6 @@ FILE_TYPE_okay (const struct command *c)
       && c->func != cmd_repeating_data
       && c->func != cmd_end_file_type)
     msg (SE, _("%s not allowed inside FILE TYPE/END FILE TYPE."), c->name);
-#if 0
   /* FIXME */
   else if (c->func == cmd_repeating_data && fty.type == FTY_GROUPED)
     msg (SE, _("%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."),
@@ -104,17 +155,19 @@ FILE_TYPE_okay (const struct command *c)
   else if (!fty.had_rec_type && c->func != cmd_record_type)
     msg (SE, _("RECORD TYPE must be the first command inside a "
                      "FILE TYPE structure."));
-#endif
   else
     okay = 1;
 
-#if 0
   if (c->func == cmd_record_type)
     fty.had_rec_type = 1;
-#endif
 
   return okay;
 }
+#else
+{
+  return 1;
+}
+#endif
 
 /* Parses an entire PSPP command.  This includes everything from the
    command name to the terminating dot.  Does most of its work by
@@ -136,7 +189,7 @@ cmd_parse (void)
     return CMD_SUCCESS;
 
   /* Parse comments. */
-  if ((token == T_ID && !strcmp (tokid, "COMMENT"))
+  if ((token == T_ID && !strcasecmp (tokid, "COMMENT"))
       || token == T_EXP || token == '*' || token == '[')
     {
       lex_skip_comment ();
@@ -234,7 +287,7 @@ match_strings (const char *a, size_t a_len,
   while (a_len > 0 && b_len > 0) 
     {
       /* Mismatch always returns zero. */
-      if (*a++ != *b++)
+      if (toupper ((unsigned char) *a++) != toupper ((unsigned char) *b++))
         return 0;
 
       /* Advance. */
@@ -256,7 +309,7 @@ static const char *
 find_word (const char *string, size_t *word_len) 
 {
   /* Skip whitespace and asterisks. */
-  while (isspace (*string))
+  while (isspace ((unsigned char) *string))
     string++;
 
   /* End of string? */
@@ -293,9 +346,15 @@ conflicting_3char_prefixes (const char *a, const char *b)
   bw = find_word (b, &bw_len);
   assert (aw != NULL && bw != NULL);
 
+  /* Words that are the same don't conflict. */
+  if (aw_len == bw_len && !buf_compare_case (aw, bw, aw_len))
+    return 0;
+  
+  /* Words that are otherwise the same in the first three letters
+     do conflict. */
   return ((aw_len > 3 && bw_len > 3)
           || (aw_len == 3 && bw_len > 3)
-          || (bw_len == 3 && aw_len > 3)) && !memcmp (aw, bw, 3);
+          || (bw_len == 3 && aw_len > 3)) && !buf_compare_case (aw, bw, 3);
 }
 
 /* Returns nonzero if CMD can be confused with another command
@@ -337,7 +396,7 @@ cmd_match_words (const struct command *cmd,
        word != NULL && word_idx < word_cnt;
        word = find_word (word + word_len, &word_len), word_idx++)
     if (word_len != strlen (words[word_idx])
-        || memcmp (word, words[word_idx], word_len))
+        || buf_compare_case (word, words[word_idx], word_len))
       {
         size_t match_chars = match_strings (word, word_len,
                                             words[word_idx],
@@ -480,7 +539,7 @@ parse_command_name (void)
       
       assert (word_cnt < sizeof words / sizeof *words);
       if (token == T_ID)
-        words[word_cnt++] = xstrdup (ds_value (&tokstr));
+        words[word_cnt++] = xstrdup (ds_c_str (&tokstr));
       else
         words[word_cnt++] = xstrdup ("-");
 
@@ -495,6 +554,8 @@ parse_command_name (void)
             {
               if (command->skip_entire_name)
                 lex_get ();
+             if ( command->debug & !test_mode ) 
+               goto error;
               free_words (words, word_cnt);
               return command;
             }
@@ -538,10 +599,14 @@ parse_command_name (void)
           free (words[word_cnt]);
         }
 
+      if ( command->debug && !test_mode ) 
+       goto error;
+
       free_words (words, word_cnt);
       return command;
     }
 
+error:
   unknown_command_error (words, word_cnt);
   free_words (words, word_cnt);
   return NULL;
@@ -585,108 +650,6 @@ cmd_finish (void)
   return CMD_SUCCESS;
 }
 
-/* Extracts a null-terminated 8-or-fewer-character PREFIX from STRING.
-   PREFIX is converted to lowercase.  Removes trailing spaces from
-   STRING as a side effect.  */
-static void
-extract_prefix (char *string, char *prefix)
-{
-  /* Length of STRING. */
-  int len;
-
-  /* Points to the null terminator in STRING (`end pointer'). */
-  char *ep;
-
-  /* Strip spaces from end of STRING. */
-  len = strlen (string);
-  while (len && isspace ((unsigned char) string[len - 1]))
-    string[--len] = 0;
-
-  /* Find null terminator. */
-  ep = memchr (string, '\0', 8);
-  if (!ep)
-    ep = &string[8];
-
-  /* Copy prefix, converting to lowercase. */
-  while (string < ep)
-    *prefix++ = tolower ((unsigned char) (*string++));
-  *prefix = 0;
-}
-
-/* Prints STRING on the console and to the listing file, replacing \n
-   by newline. */
-static void
-output_line (char *string)
-{
-  /* Location of \n in line read in. */
-  char *cp;
-
-  cp = strstr (string, "\\n");
-  while (cp)
-    {
-      *cp = 0;
-      tab_output_text (TAB_LEFT | TAT_NOWRAP, string);
-      string = &cp[2];
-      cp = strstr (string, "\\n");
-    }
-  tab_output_text (TAB_LEFT | TAT_NOWRAP, string);
-}
-
-/* Parse and execute REMARK command. */
-int
-cmd_remark ()
-{
-  /* Points to the line read in. */
-  char *s;
-
-  /* Index into s. */
-  char *cp;
-
-  /* 8-character sentinel used to terminate remark. */
-  char sentinel[9];
-
-  /* Beginning of line used to compare with SENTINEL. */
-  char prefix[9];
-
-  som_blank_line ();
-  
-  s = lex_rest_of_line (NULL);
-  if (*s == '-')
-    {
-      output_line (&s[1]);
-      return CMD_SUCCESS;
-    }
-
-  /* Read in SENTINEL from end of current line. */
-  cp = s;
-  while (isspace ((unsigned char) *cp))
-    cp++;
-  extract_prefix (cp, sentinel);
-  if (sentinel[0] == 0)
-    {
-      msg (SE, _("The sentinel may not be the empty string."));
-      return CMD_FAILURE;
-    }
-
-  /* Read in other lines until we encounter the sentinel. */
-  while (getl_read_line ())
-    {
-      extract_prefix (ds_value (&getl_buf), prefix);
-      if (!strcmp (sentinel, prefix))
-       break;
-
-      /* Output the line. */
-      output_line (ds_value (&getl_buf));
-    }
-
-  /* Calling lex_entire_line() forces the sentinel line to be
-     discarded. */
-  getl_prompt = GETL_PRPT_STANDARD;
-  lex_entire_line ();
-
-  return CMD_SUCCESS;
-}
-
 /* Parses the N command. */
 int
 cmd_n_of_cases (void)
@@ -728,10 +691,10 @@ cmd_erase (void)
   if (!lex_force_string ())
     return CMD_FAILURE;
 
-  if (remove (ds_value (&tokstr)) == -1)
+  if (remove (ds_c_str (&tokstr)) == -1)
     {
       msg (SW, _("Error removing `%s': %s."),
-          ds_value (&tokstr), strerror (errno));
+          ds_c_str (&tokstr), strerror (errno));
       return CMD_FAILURE;
     }
 
@@ -797,7 +760,7 @@ shell (void)
 static int
 run_command (void)
 {
-  char *cmd;
+  const char *cmd;
   int string;
 
   /* Handle either a string argument or a full-line argument. */
@@ -809,12 +772,13 @@ run_command (void)
        lex_get ();
        if (!lex_force_string ())
          return CMD_FAILURE;
-       cmd = ds_value (&tokstr);
+       cmd = ds_c_str (&tokstr);
        string = 1;
       }
     else
       {
        cmd = lex_rest_of_line (NULL);
+        lex_discard_line ();
        string = 0;
       }
   }
@@ -866,11 +830,11 @@ cmd_host (void)
   /* Make sure that the system has a command interpreter, then run a
      command. */
   if (system (NULL) != 0)
-    success = run_command ();
+    code = run_command ();
   else
     {
       msg (SE, _("No operating system support for this command."));
-      success = CMD_FAILURE;
+      code = CMD_FAILURE;
     }
 #endif /* !unix */