Rewrite expression code.
[pspp-builds.git] / src / print.c
index 70a41763c4a5d7b52e4d3d5fec9adc6a5f43364c..d8f4d77040834a375d9fa31bdb16734b99137abc 100644 (file)
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-/* AIX requires this to be the first thing in the file.  */
-#include <config.h>
-#if __GNUC__
-#define alloca __builtin_alloca
-#else
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-#pragma alloca
-#else
-#ifndef alloca                 /* predefined by HP cc +Olibcalls */
-char *alloca ();
-#endif
-#endif
-#endif
-#endif
+/* FIXME: seems like a lot of code duplication with data-list.c. */
 
-#include <assert.h>
+#include <config.h>
+#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
+#include "case.h"
 #include "command.h"
-#include "dfm.h"
+#include "dfm-write.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "file-handle.h"
 #include "lexer.h"
 #include "misc.h"
@@ -49,10 +35,6 @@ char *alloca ();
 #include "tab.h"
 #include "var.h"
 
-#undef DEBUGGING
-/*#define DEBUGGING 1*/
-#include "debug-print.h"
-
 /* Describes what to do when an output field is encountered. */
 enum
   {
@@ -88,20 +70,19 @@ enum
     PRT_CMD_MASK = 1,          /* Command type mask. */
     PRT_PRINT = 0,             /* PRINT transformation identifier. */
     PRT_WRITE = 1,             /* WRITE transformation identifier. */
-    PRT_EJECT = 002            /* Can be combined with CMD_PRINT only. */
+    PRT_EJECT = 002,           /* Can be combined with CMD_PRINT only. */
+    PRT_BINARY = 004            /* File is binary, omit newlines. */
   };
 
 /* PRINT, PRINT EJECT, WRITE private data structure. */
 struct print_trns
   {
     struct trns_header h;
-    struct file_handle *handle;        /* Output file, NULL=listing file. */
+    struct dfm_writer *writer; /* Output file, NULL=listing file. */
     int options;               /* PRT_* bitmapped field. */
     struct prt_out_spec *spec; /* Output specifications. */
     int max_width;             /* Maximum line width including null. */
-#if !PAGED_STACK
     char *line;                        /* Buffer for sticking lines in. */
-#endif
   };
 
 /* PRT_PRINT or PRT_WRITE. */
@@ -117,16 +98,12 @@ static struct prt_out_spec *next;
 static int nrec;
 
 static int internal_cmd_print (int flags);
-static int print_trns_proc (struct trns_header *, struct ccase *);
-static void print_trns_free (struct trns_header *);
+static trns_proc_func print_trns_proc;
+static trns_free_func print_trns_free;
 static int parse_specs (void);
-static void dump_table (void);
-static void append_var_spec (struct prt_out_spec *spec);
+static void dump_table (const struct file_handle *);
+static void append_var_spec (struct prt_out_spec *);
 static void alloc_line (void);
-
-#if DEBUGGING
-void debug_print (void);
-#endif
 \f
 /* Basic parsing. */
 
@@ -134,7 +111,6 @@ void debug_print (void);
 int
 cmd_print (void)
 {
-  lex_match_id ("PRINT");
   return internal_cmd_print (PRT_PRINT);
 }
 
@@ -142,7 +118,6 @@ cmd_print (void)
 int
 cmd_print_eject (void)
 {
-  lex_match_id ("EJECT");
   return internal_cmd_print (PRT_PRINT | PRT_EJECT);
 }
 
@@ -150,7 +125,6 @@ cmd_print_eject (void)
 int
 cmd_write (void)
 {
-  lex_match_id ("WRITE");
   return internal_cmd_print (PRT_WRITE);
 }
 
@@ -159,21 +133,17 @@ cmd_write (void)
 static int
 internal_cmd_print (int f)
 {
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = 0;
-
-  /* malloc()'d transformation. */
-  struct print_trns *trns;
+  int table = 0;                /* Print table? */
+  struct print_trns *trns;      /* malloc()'d transformation. */
+  struct file_handle *fh = NULL;
 
   /* Fill in prt to facilitate error-handling. */
   prt.h.proc = print_trns_proc;
   prt.h.free = print_trns_free;
-  prt.handle = NULL;
+  prt.writer = NULL;
   prt.options = f;
   prt.spec = NULL;
-#if !PAGED_STACK
   prt.line = NULL;
-#endif
   next = NULL;
   nrec = 0;
 
@@ -186,16 +156,16 @@ internal_cmd_print (int f)
        {
          lex_match ('=');
 
-         prt.handle = fh_parse_file_handle ();
-         if (!prt.handle)
-           goto lossage;
+         fh = fh_parse ();
+         if (fh == NULL)
+           goto error;
        }
       else if (lex_match_id ("RECORDS"))
        {
          lex_match ('=');
          lex_match ('(');
          if (!lex_force_int ())
-           goto lossage;
+           goto error;
          nrec = lex_integer ();
          lex_get ();
          lex_match (')');
@@ -207,17 +177,27 @@ internal_cmd_print (int f)
       else
        {
          lex_error (_("expecting a valid subcommand"));
-         goto lossage;
+         goto error;
        }
     }
 
   /* Parse variables and strings. */
   if (!parse_specs ())
-    goto lossage;
-  
+    goto error;
+
+  if (fh != NULL)
+    {
+      prt.writer = dfm_open_writer (fh);
+      if (prt.writer == NULL)
+        goto error;
+
+      if (handle_get_mode (fh) == MODE_BINARY)
+        prt.options |= PRT_BINARY;
+    }
+
   /* Output the variable table if requested. */
   if (table)
-    dump_table ();
+    dump_table (fh);
 
   /* Count the maximum line width.  Allocate linebuffer if
      applicable. */
@@ -228,13 +208,9 @@ internal_cmd_print (int f)
   memcpy (trns, &prt, sizeof *trns);
   add_transformation ((struct trns_header *) trns);
 
-#if DEBUGGING
-  debug_print ();
-#endif
-
   return CMD_SUCCESS;
 
lossage:
error:
   print_trns_free ((struct trns_header *) & prt);
   return CMD_FAILURE;
 }
@@ -369,7 +345,7 @@ parse_string_argument (void)
 {
   fx.spec.type = PRT_CONST;
   fx.spec.fc = fx.sc - 1;
-  fx.spec.u.c = xstrdup (ds_value (&tokstr));
+  fx.spec.u.c = xstrdup (ds_c_str (&tokstr));
   lex_get ();
 
   /* Parse the included column range. */
@@ -455,7 +431,7 @@ fail:
 static int
 parse_variable_argument (void)
 {
-  if (!parse_variables (NULL, &fx.v, &fx.nv, PV_DUPLICATE))
+  if (!parse_variables (default_dict, &fx.v, &fx.nv, PV_DUPLICATE))
     return 0;
 
   if (token == T_NUM)
@@ -623,7 +599,7 @@ fixed_parse_compatible (void)
 
   dividend = (fx.lc - fx.fc + 1) / fx.nv;
   fx.spec.u.v.f.w = dividend;
-  if (!check_output_specifier (&fx.spec.u.v.f))
+  if (!check_output_specifier (&fx.spec.u.v.f, 1))
     return 0;
   if ((type == ALPHA) ^ (formats[fx.spec.u.v.f.type].cat & FCAT_STRING))
     {
@@ -740,7 +716,7 @@ dump_fmt_list (struct fmt_list * f)
 static struct fmt_list *
 fixed_parse_fortran (void)
 {
-  struct fmt_list *head;
+  struct fmt_list *head = NULL;
   struct fmt_list *fl = NULL;
 
   lex_get ();                  /* skip opening parenthesis */
@@ -772,8 +748,8 @@ fixed_parse_fortran (void)
        }
       else if (lex_match ('/'))
        fl->f.type = FMT_NEWREC;
-      else if (!parse_format_specifier (&fl->f, 1)
-              || !check_output_specifier (&fl->f))
+      else if (!parse_format_specifier (&fl->f, FMTP_ALLOW_XT)
+              || !check_output_specifier (&fl->f, 1))
        goto fail;
 
       lex_match (',');
@@ -805,10 +781,9 @@ fail:
 /* Prints the table produced by the TABLE subcommand to the listing
    file. */
 static void
-dump_table (void)
+dump_table (const struct file_handle *fh)
 {
   struct prt_out_spec *spec;
-  const char *filename;
   struct tab_table *t;
   int recno;
   int nspec;
@@ -862,25 +837,23 @@ dump_table (void)
        assert (0);
       }
 
-  filename = fh_handle_name (prt.handle);
-  tab_title (t, 1, (prt.handle != NULL
-                   ? _("Writing %3d records to file %s.")
-                   : _("Writing %3d records to the listing file.")),
-            recno, filename);
+  if (fh != NULL)
+    tab_title (t, 1, _("Writing %d record(s) to file %s."),
+               recno, handle_get_filename (fh));
+  else
+    tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno);
   tab_submit (t);
-  fh_handle_name (NULL);
 }
 
 /* PORTME: The number of characters in a line terminator. */
-#if __MSDOS__ 
+#ifdef __MSDOS__ 
 #define LINE_END_WIDTH 2       /* \r\n */
 #else
 #define LINE_END_WIDTH 1       /* \n */
 #endif
 
 /* Calculates the maximum possible line width and allocates a buffer
-   big enough to contain it, if necessary (otherwise sets max_width).
-   (The action taken depends on compiler & OS as detected by pref.h.) */
+   big enough to contain it */
 static void
 alloc_line (void)
 {
@@ -910,23 +883,23 @@ alloc_line (void)
          pot_w = i->fc + 1;
          break;
        case PRT_ERROR:
+        default:
          assert (0);
-         break;
+          abort ();
        }
       if (pot_w > w)
        w = pot_w;
     }
   prt.max_width = w + LINE_END_WIDTH + 1;
-#if !PAGED_STACK
   prt.line = xmalloc (prt.max_width);
-#endif
 }
 \f
 /* Transformation. */
 
 /* Performs the transformation inside print_trns T on case C. */
 static int
-print_trns_proc (struct trns_header * trns, struct ccase * c)
+print_trns_proc (struct trns_header * trns, struct ccase * c,
+                 int case_num UNUSED)
 {
   /* Transformation. */
   struct print_trns *t = (struct print_trns *) trns;
@@ -935,15 +908,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c)
   struct prt_out_spec *i;
 
   /* Line buffer. */
-#if PAGED_STACK
-#if __GNUC__ && !__STRICT_ANSI__
-  char buf[t->max_width];
-#else /* !__GNUC__ */
-  char *buf = alloca (t->max_width);
-#endif /* !__GNUC__ */
-#else /* !PAGED_STACK */
   char *buf = t->line;
-#endif /* !PAGED_STACK */
 
   /* Length of the line in buf. */
   int len = 0;
@@ -952,15 +917,14 @@ print_trns_proc (struct trns_header * trns, struct ccase * c)
   if (t->options & PRT_EJECT)
     som_eject_page ();
 
-  /* Note that a field written to a place where a field has already
-     been written truncates the record.  `PRINT /A B (T10,F8,T1,F8).'
-     only outputs B.  This is an example of bug-for-bug compatibility,
-     in the author's opinion. */
+  /* Note that a field written to a place where a field has
+     already been written truncates the record.  `PRINT /A B
+     (T10,F8,T1,F8).' only outputs B.  */
   for (i = t->spec; i; i = i->next)
     switch (i->type)
       {
       case PRT_NEWLINE:
-       if (t->handle == NULL)
+       if (t->writer == NULL)
          {
            buf[len] = 0;
            tab_output_text (TAT_FIX | TAT_NOWRAP, buf);
@@ -968,16 +932,16 @@ print_trns_proc (struct trns_header * trns, struct ccase * c)
        else
          {
            if ((t->options & PRT_CMD_MASK) == PRT_PRINT
-               || t->handle->mode != FH_MD_BINARY)
+                || !(t->options & PRT_BINARY))
              {
                /* PORTME: Line ends. */
-#if __MSDOS__
+#ifdef __MSDOS__
                buf[len++] = '\r';
 #endif
                buf[len++] = '\n';
              }
 
-           dfm_put_record (t->handle, buf, len);
+           dfm_put_record (t->writer, buf, len);
          }
 
        memset (buf, ' ', t->max_width);
@@ -992,14 +956,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c)
        break;
 
       case PRT_VAR:
-       if (i->u.v.v->type == NUMERIC)
-         data_out (&buf[i->fc], &i->u.v.f, &c->data[i->u.v.v->fv]);
-       else
-         {
-           union value t;
-           t.c = c->data[i->u.v.v->fv].s;
-           data_out (&buf[i->fc], &i->u.v.f, &t);
-         }
+        data_out (&buf[i->fc], &i->u.v.f, case_data (c, i->u.v.v->fv));
        len = i->fc + i->u.v.f.w;
        break;
 
@@ -1020,9 +977,10 @@ print_trns_proc (struct trns_header * trns, struct ccase * c)
 static void
 print_trns_free (struct trns_header * t)
 {
+  struct print_trns *prt = (struct print_trns *) t;
   struct prt_out_spec *i, *n;
 
-  for (i = ((struct print_trns *) t)->spec; i; i = n)
+  for (i = prt->spec; i; i = n)
     {
       switch (i->type)
        {
@@ -1041,9 +999,9 @@ print_trns_free (struct trns_header * t)
       n = i->next;
       free (i);
     }
-#if !PAGED_STACK
-  free (((struct print_trns *) t)->line);
-#endif
+  if (prt->writer != NULL)
+    dfm_close_writer (prt->writer);
+  free (prt->line);
 }
 \f
 /* PRINT SPACE. */
@@ -1053,47 +1011,37 @@ struct print_space_trns
 {
   struct trns_header h;
 
-  struct file_handle *handle;  /* Output file, NULL=listing file. */
+  struct dfm_writer *writer;    /* Output data file. */
   struct expression *e;                /* Number of lines; NULL=1. */
 }
 print_space_trns;
 
-static int print_space_trns_proc (struct trns_header *, struct ccase *);
-static void print_space_trns_free (struct trns_header *);
+static trns_proc_func print_space_trns_proc;
+static trns_free_func print_space_trns_free;
 
 int
 cmd_print_space (void)
 {
   struct print_space_trns *t;
-  struct file_handle *handle;
+  struct file_handle *fh;
   struct expression *e;
+  struct dfm_writer *writer;
 
-  lex_match_id ("SPACE");
   if (lex_match_id ("OUTFILE"))
     {
       lex_match ('=');
 
-      if (token == T_ID)
-       handle = fh_get_handle_by_name (tokid);
-      else if (token == T_STRING)
-       handle = fh_get_handle_by_filename (tokid);
-      else
-       {
-         msg (SE, _("A file name or handle was expected in the "
-                    "OUTFILE subcommand."));
-         return CMD_FAILURE;
-       }
-      
-      if (!handle)
+      fh = fh_parse ();
+      if (fh == NULL)
        return CMD_FAILURE;
       lex_get ();
     }
   else
-    handle = NULL;
+    fh = NULL;
 
   if (token != '.')
     {
-      e = expr_parse (PXP_NUMERIC);
+      e = expr_parse (default_dict, EXPR_NUMBER);
       if (token != '.')
        {
          expr_free (e);
@@ -1104,13 +1052,25 @@ cmd_print_space (void)
   else
     e = NULL;
 
+  if (fh != NULL)
+    {
+      writer = dfm_open_writer (fh);
+      if (writer == NULL) 
+        {
+          expr_free (e);
+          return CMD_FAILURE;
+        } 
+    }
+  else
+    writer = NULL;
+  
   t = xmalloc (sizeof *t);
   t->h.proc = print_space_trns_proc;
   if (e)
     t->h.free = print_space_trns_free;
   else
     t->h.free = NULL;
-  t->handle = handle;
+  t->writer = writer;
   t->e = e;
 
   add_transformation ((struct trns_header *) t);
@@ -1118,30 +1078,24 @@ cmd_print_space (void)
 }
 
 static int
-print_space_trns_proc (struct trns_header * trns, struct ccase * c)
+print_space_trns_proc (struct trns_header * trns, struct ccase * c,
+                       int case_num UNUSED)
 {
   struct print_space_trns *t = (struct print_space_trns *) trns;
-  int n;
+  double n = 1.;
 
   if (t->e)
     {
-      union value v;
-
-      expr_evaluate (t->e, c, &v);
-      n = v.f;
-      if (n < 0)
-       {
-         msg (SW, _("The expression on PRINT SPACE evaluated to %d.  It's "
-                    "not possible to PRINT SPACE a negative number of "
-                    "lines."),
-              n);
-         n = 1;
-       }
+      n = expr_evaluate_num (t->e, c, case_num);
+      if (n == SYSMIS) 
+        msg (SW, _("The expression on PRINT SPACE evaluated to the "
+                   "system-missing value."));
+      else if (n < 0)
+        msg (SW, _("The expression on PRINT SPACE evaluated to %g."), n);
+      n = 1.;
     }
-  else
-    n = 1;
 
-  if (t->handle == NULL)
+  if (t->writer == NULL)
     while (n--)
       som_blank_line ();
   else
@@ -1149,14 +1103,14 @@ print_space_trns_proc (struct trns_header * trns, struct ccase * c)
       char buf[LINE_END_WIDTH];
 
       /* PORTME: Line ends. */
-#if __MSDOS__
+#ifdef __MSDOS__
       buf[0] = '\r';
       buf[1] = '\n';
 #else
       buf[0] = '\n';
 #endif
       while (n--)
-       dfm_put_record (t->handle, buf, LINE_END_WIDTH);
+       dfm_put_record (t->writer, buf, LINE_END_WIDTH);
     }
 
   return -1;
@@ -1167,45 +1121,3 @@ print_space_trns_free (struct trns_header * trns)
 {
   expr_free (((struct print_space_trns *) trns)->e);
 }
-\f
-/* Debugging code. */
-
-#if DEBUGGING
-void
-debug_print (void)
-{
-  struct prt_out_spec *p;
-
-  if (prt.handle == NULL)
-    {
-      printf ("PRINT");
-      if (prt.eject)
-       printf (" EJECT");
-    }
-  else
-    printf ("WRITE OUTFILE=%s", handle_name (prt.handle));
-  printf (" MAX_WIDTH=%d", prt.max_width);
-  printf (" /");
-  for (p = prt.spec; p; p = p->next)
-    switch (p->type)
-      {
-      case PRT_ERROR:
-       printf (_("<ERROR>"));
-       break;
-      case PRT_NEWLINE:
-       printf ("\n /");
-       break;
-      case PRT_CONST:
-       printf (" \"%s\" %d-%d", p->u.c, p->fc + 1, p->fc + strlen (p->u.c));
-       break;
-      case PRT_VAR:
-       printf (" %s %d %d-%d (%s)", p->u.v.v->name, p->u.v.v->fv, p->fc + 1,
-               p->fc + p->u.v.v->print.w, fmt_to_string (&p->u.v.v->print));
-       break;
-      case PRT_SPACE:
-       printf (" \" \" %d", p->fc + 1);
-       break;
-      }
-  printf (".\n");
-}
-#endif /* DEBUGGING */