checkin of 0.3.0
[pspp-builds.git] / src / print.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 /* AIX requires this to be the first thing in the file.  */
21 #include <config.h>
22 #if __GNUC__
23 #define alloca __builtin_alloca
24 #else
25 #if HAVE_ALLOCA_H
26 #include <alloca.h>
27 #else
28 #ifdef _AIX
29 #pragma alloca
30 #else
31 #ifndef alloca                  /* predefined by HP cc +Olibcalls */
32 char *alloca ();
33 #endif
34 #endif
35 #endif
36 #endif
37
38 #include <assert.h>
39 #include <stdlib.h>
40 #include "alloc.h"
41 #include "command.h"
42 #include "dfm.h"
43 #include "error.h"
44 #include "expr.h"
45 #include "file-handle.h"
46 #include "lexer.h"
47 #include "misc.h"
48 #include "som.h"
49 #include "tab.h"
50 #include "var.h"
51
52 #undef DEBUGGING
53 /*#define DEBUGGING 1*/
54 #include "debug-print.h"
55
56 /* Describes what to do when an output field is encountered. */
57 enum
58   {
59     PRT_ERROR,                  /* Invalid value. */
60     PRT_NEWLINE,                /* Newline. */
61     PRT_CONST,                  /* Constant string. */
62     PRT_VAR,                    /* Variable. */
63     PRT_SPACE                   /* A single space. */
64   };
65
66 /* Describes how to output one field. */
67 struct prt_out_spec
68   {
69     struct prt_out_spec *next;
70     int type;                   /* PRT_* constant. */
71     int fc;                     /* 0-based first column. */
72     union
73       {
74         char *c;                /* PRT_CONST: Associated string. */
75         struct
76           {
77             struct variable *v; /* PRT_VAR: Associated variable. */
78             struct fmt_spec f;  /* PRT_VAR: Output spec. */
79           }
80         v;
81       }
82     u;
83   };
84
85 /* Enums for use with print_trns's `options' field. */
86 enum
87   {
88     PRT_CMD_MASK = 1,           /* Command type mask. */
89     PRT_PRINT = 0,              /* PRINT transformation identifier. */
90     PRT_WRITE = 1,              /* WRITE transformation identifier. */
91     PRT_EJECT = 002             /* Can be combined with CMD_PRINT only. */
92   };
93
94 /* PRINT, PRINT EJECT, WRITE private data structure. */
95 struct print_trns
96   {
97     struct trns_header h;
98     struct file_handle *handle; /* Output file, NULL=listing file. */
99     int options;                /* PRT_* bitmapped field. */
100     struct prt_out_spec *spec;  /* Output specifications. */
101     int max_width;              /* Maximum line width including null. */
102 #if !PAGED_STACK
103     char *line;                 /* Buffer for sticking lines in. */
104 #endif
105   };
106
107 /* PRT_PRINT or PRT_WRITE. */
108 int which_cmd;
109
110 /* Holds information on parsing the data file. */
111 static struct print_trns prt;
112
113 /* Last prt_out_spec in the chain.  Used for building the linked-list. */
114 static struct prt_out_spec *next;
115
116 /* Number of records. */
117 static int nrec;
118
119 static int internal_cmd_print (int flags);
120 static int print_trns_proc (struct trns_header *, struct ccase *);
121 static void print_trns_free (struct trns_header *);
122 static int parse_specs (void);
123 static void dump_table (void);
124 static void append_var_spec (struct prt_out_spec *spec);
125 static void alloc_line (void);
126
127 #if DEBUGGING
128 void debug_print (void);
129 #endif
130 \f
131 /* Basic parsing. */
132
133 /* Parses PRINT command. */
134 int
135 cmd_print (void)
136 {
137   lex_match_id ("PRINT");
138   return internal_cmd_print (PRT_PRINT);
139 }
140
141 /* Parses PRINT EJECT command. */
142 int
143 cmd_print_eject (void)
144 {
145   lex_match_id ("EJECT");
146   return internal_cmd_print (PRT_PRINT | PRT_EJECT);
147 }
148
149 /* Parses WRITE command. */
150 int
151 cmd_write (void)
152 {
153   lex_match_id ("WRITE");
154   return internal_cmd_print (PRT_WRITE);
155 }
156
157 /* Parses the output commands.  F is PRT_PRINT, PRT_WRITE, or
158    PRT_PRINT|PRT_EJECT. */
159 static int
160 internal_cmd_print (int f)
161 {
162   /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
163   int table = 0;
164
165   /* malloc()'d transformation. */
166   struct print_trns *trns;
167
168   /* Fill in prt to facilitate error-handling. */
169   prt.h.proc = print_trns_proc;
170   prt.h.free = print_trns_free;
171   prt.handle = NULL;
172   prt.options = f;
173   prt.spec = NULL;
174 #if !PAGED_STACK
175   prt.line = NULL;
176 #endif
177   next = NULL;
178   nrec = 0;
179
180   which_cmd = f & PRT_CMD_MASK;
181
182   /* Parse the command options. */
183   while (!lex_match ('/'))
184     {
185       if (lex_match_id ("OUTFILE"))
186         {
187           lex_match ('=');
188
189           prt.handle = fh_parse_file_handle ();
190           if (!prt.handle)
191             goto lossage;
192         }
193       else if (lex_match_id ("RECORDS"))
194         {
195           lex_match ('=');
196           lex_match ('(');
197           if (!lex_force_int ())
198             goto lossage;
199           nrec = lex_integer ();
200           lex_get ();
201           lex_match (')');
202         }
203       else if (lex_match_id ("TABLE"))
204         table = 1;
205       else if (lex_match_id ("NOTABLE"))
206         table = 0;
207       else
208         {
209           lex_error (_("expecting a valid subcommand"));
210           goto lossage;
211         }
212     }
213
214   /* Parse variables and strings. */
215   if (!parse_specs ())
216     goto lossage;
217   
218   /* Output the variable table if requested. */
219   if (table)
220     dump_table ();
221
222   /* Count the maximum line width.  Allocate linebuffer if
223      applicable. */
224   alloc_line ();
225
226   /* Put the transformation in the queue. */
227   trns = xmalloc (sizeof *trns);
228   memcpy (trns, &prt, sizeof *trns);
229   add_transformation ((struct trns_header *) trns);
230
231 #if DEBUGGING
232   debug_print ();
233 #endif
234
235   return CMD_SUCCESS;
236
237  lossage:
238   print_trns_free ((struct trns_header *) & prt);
239   return CMD_FAILURE;
240 }
241
242 /* Appends the field output specification SPEC to the list maintained
243    in prt. */
244 static void
245 append_var_spec (struct prt_out_spec *spec)
246 {
247   if (next == 0)
248     prt.spec = next = xmalloc (sizeof *spec);
249   else
250     next = next->next = xmalloc (sizeof *spec);
251
252   memcpy (next, spec, sizeof *spec);
253   next->next = NULL;
254 }
255 \f
256 /* Field parsing.  Mostly stolen from data-list.c. */
257
258 /* Used for chaining together fortran-like format specifiers. */
259 struct fmt_list
260 {
261   struct fmt_list *next;
262   int count;
263   struct fmt_spec f;
264   struct fmt_list *down;
265 };
266
267 /* Used as "local" variables among the fixed-format parsing funcs.  If
268    it were guaranteed that PSPP were going to be compiled by gcc,
269    I'd make all these functions a single set of nested functions. */
270 static struct
271   {
272     struct variable **v;                /* variable list */
273     int nv;                     /* number of variables in list */
274     int cv;                     /* number of variables from list used up so far
275                                    by the FORTRAN-like format specifiers */
276
277     int recno;                  /* current 1-based record number */
278     int sc;                     /* 1-based starting column for next variable */
279
280     struct prt_out_spec spec;           /* next format spec to append to list */
281     int fc, lc;                 /* first, last 1-based column number of current
282                                    var */
283
284     int level;                  /* recursion level for FORTRAN-like format
285                                    specifiers */
286   }
287 fx;
288
289 static int fixed_parse_compatible (void);
290 static struct fmt_list *fixed_parse_fortran (void);
291
292 static int parse_string_argument (void);
293 static int parse_variable_argument (void);
294
295 /* Parses all the variable and string specifications on a single
296    PRINT, PRINT EJECT, or WRITE command into the prt structure.
297    Returns success. */
298 static int
299 parse_specs (void)
300 {
301   /* Return code from called function. */
302   int code;
303
304   fx.recno = 1;
305   fx.sc = 1;
306
307   while (token != '.')
308     {
309       while (lex_match ('/'))
310         {
311           int prev_recno = fx.recno;
312
313           fx.recno++;
314           if (token == T_NUM)
315             {
316               if (!lex_force_int ())
317                 return 0;
318               if (lex_integer () < fx.recno)
319                 {
320                   msg (SE, _("The record number specified, %ld, is "
321                              "before the previous record, %d.  Data "
322                              "fields must be listed in order of "
323                              "increasing record number."),
324                        lex_integer (), fx.recno - 1);
325                   return 0;
326                 }
327               fx.recno = lex_integer ();
328               lex_get ();
329             }
330
331           fx.spec.type = PRT_NEWLINE;
332           while (prev_recno++ < fx.recno)
333             append_var_spec (&fx.spec);
334
335           fx.sc = 1;
336         }
337
338       if (token == T_STRING)
339         code = parse_string_argument ();
340       else
341         code = parse_variable_argument ();
342       if (!code)
343         return 0;
344     }
345   fx.spec.type = PRT_NEWLINE;
346   append_var_spec (&fx.spec);
347
348   if (!nrec)
349     nrec = fx.recno;
350   else if (fx.recno > nrec)
351     {
352       msg (SE, _("Variables are specified on records that "
353                  "should not exist according to RECORDS subcommand."));
354       return 0;
355     }
356       
357   if (token != '.')
358     {
359       lex_error (_("expecting end of command"));
360       return 0;
361     }
362   
363   return 1;
364 }
365
366 /* Parses a string argument to the PRINT commands.  Returns success. */
367 static int
368 parse_string_argument (void)
369 {
370   fx.spec.type = PRT_CONST;
371   fx.spec.fc = fx.sc - 1;
372   fx.spec.u.c = xstrdup (ds_value (&tokstr));
373   lex_get ();
374
375   /* Parse the included column range. */
376   if (token == T_NUM)
377     {
378       /* Width of column range in characters. */
379       int c_len;
380
381       /* Width of constant string in characters. */
382       int s_len;
383
384       /* 1-based index of last column in range. */
385       int lc;
386
387       if (!lex_integer_p () || lex_integer () <= 0)
388         {
389           msg (SE, _("%g is not a valid column location."), tokval);
390           goto fail;
391         }
392       fx.spec.fc = lex_integer () - 1;
393
394       lex_get ();
395       lex_negative_to_dash ();
396       if (lex_match ('-'))
397         {
398           if (!lex_integer_p ())
399             {
400               msg (SE, _("Column location expected following `%d-'."),
401                    fx.spec.fc + 1);
402               goto fail;
403             }
404           if (lex_integer () <= 0)
405             {
406               msg (SE, _("%g is not a valid column location."), tokval);
407               goto fail;
408             }
409           if (lex_integer () < fx.spec.fc + 1)
410             {
411               msg (SE, _("%d-%ld is not a valid column range.  The second "
412                    "column must be greater than or equal to the first."),
413                    fx.spec.fc + 1, lex_integer ());
414               goto fail;
415             }
416           lc = lex_integer () - 1;
417
418           lex_get ();
419         }
420       else
421         /* If only a starting location is specified then the field is
422            the width of the provided string. */
423         lc = fx.spec.fc + strlen (fx.spec.u.c) - 1;
424
425       /* Apply the range. */
426       c_len = lc - fx.spec.fc + 1;
427       s_len = strlen (fx.spec.u.c);
428       if (s_len > c_len)
429         fx.spec.u.c[c_len] = 0;
430       else if (s_len < c_len)
431         {
432           fx.spec.u.c = xrealloc (fx.spec.u.c, c_len + 1);
433           memset (&fx.spec.u.c[s_len], ' ', c_len - s_len);
434           fx.spec.u.c[c_len] = 0;
435         }
436
437       fx.sc = lc + 1;
438     }
439   else
440     /* If nothing is provided then the field is the width of the
441        provided string. */
442     fx.sc += strlen (fx.spec.u.c);
443
444   append_var_spec (&fx.spec);
445   return 1;
446
447 fail:
448   free (fx.spec.u.c);
449   return 0;
450 }
451
452 /* Parses a variable argument to the PRINT commands by passing it off
453    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
454    Returns success. */
455 static int
456 parse_variable_argument (void)
457 {
458   if (!parse_variables (NULL, &fx.v, &fx.nv, PV_DUPLICATE))
459     return 0;
460
461   if (token == T_NUM)
462     {
463       if (!fixed_parse_compatible ())
464         goto fail;
465     }
466   else if (token == '(')
467     {
468       fx.level = 0;
469       fx.cv = 0;
470       if (!fixed_parse_fortran ())
471         goto fail;
472     }
473   else
474     {
475       /* User wants dictionary format specifiers. */
476       int i;
477
478       lex_match ('*');
479       for (i = 0; i < fx.nv; i++)
480         {
481           /* Variable. */
482           fx.spec.type = PRT_VAR;
483           fx.spec.fc = fx.sc - 1;
484           fx.spec.u.v.v = fx.v[i];
485           fx.spec.u.v.f = fx.v[i]->print;
486           append_var_spec (&fx.spec);
487           fx.sc += fx.v[i]->print.w;
488
489           /* Space. */
490           fx.spec.type = PRT_SPACE;
491           fx.spec.fc = fx.sc - 1;
492           append_var_spec (&fx.spec);
493           fx.sc++;
494         }
495     }
496
497   free (fx.v);
498   return 1;
499
500 fail:
501   free (fx.v);
502   return 0;
503 }
504
505 /* Parses a column specification for parse_specs(). */
506 static int
507 fixed_parse_compatible (void)
508 {
509   int dividend;
510   int type;
511   int i;
512
513   type = fx.v[0]->type;
514   for (i = 1; i < fx.nv; i++)
515     if (type != fx.v[i]->type)
516       {
517         msg (SE, _("%s is not of the same type as %s.  To specify "
518                    "variables of different types in the same variable "
519                    "list, use a FORTRAN-like format specifier."),
520              fx.v[i]->name, fx.v[0]->name);
521         return 0;
522       }
523
524   if (!lex_force_int ())
525     return 0;
526   fx.fc = lex_integer () - 1;
527   if (fx.fc < 0)
528     {
529       msg (SE, _("Column positions for fields must be positive."));
530       return 0;
531     }
532   lex_get ();
533
534   lex_negative_to_dash ();
535   if (lex_match ('-'))
536     {
537       if (!lex_force_int ())
538         return 0;
539       fx.lc = lex_integer () - 1;
540       if (fx.lc < 0)
541         {
542           msg (SE, _("Column positions for fields must be positive."));
543           return 0;
544         }
545       else if (fx.lc < fx.fc)
546         {
547           msg (SE, _("The ending column for a field must not "
548                      "be less than the starting column."));
549           return 0;
550         }
551       lex_get ();
552     }
553   else
554     fx.lc = fx.fc;
555
556   fx.spec.u.v.f.w = fx.lc - fx.fc + 1;
557   if (lex_match ('('))
558     {
559       struct fmt_desc *fdp;
560
561       if (token == T_ID)
562         {
563           const char *cp;
564
565           fx.spec.u.v.f.type = parse_format_specifier_name (&cp, 0);
566           if (fx.spec.u.v.f.type == -1)
567             return 0;
568           if (*cp)
569             {
570               msg (SE, _("A format specifier on this line "
571                          "has extra characters on the end."));
572               return 0;
573             }
574           lex_get ();
575           lex_match (',');
576         }
577       else
578         fx.spec.u.v.f.type = FMT_F;
579
580       if (token == T_NUM)
581         {
582           if (!lex_force_int ())
583             return 0;
584           if (lex_integer () < 1)
585             {
586               msg (SE, _("The value for number of decimal places "
587                          "must be at least 1."));
588               return 0;
589             }
590           fx.spec.u.v.f.d = lex_integer ();
591           lex_get ();
592         }
593       else
594         fx.spec.u.v.f.d = 0;
595
596       fdp = &formats[fx.spec.u.v.f.type];
597       if (fdp->n_args < 2 && fx.spec.u.v.f.d)
598         {
599           msg (SE, _("Input format %s doesn't accept decimal places."),
600                fdp->name);
601           return 0;
602         }
603       if (fx.spec.u.v.f.d > 16)
604         fx.spec.u.v.f.d = 16;
605
606       if (!lex_force_match (')'))
607         return 0;
608     }
609   else
610     {
611       fx.spec.u.v.f.type = FMT_F;
612       fx.spec.u.v.f.d = 0;
613     }
614
615   fx.sc = fx.lc + 1;
616
617   if ((fx.lc - fx.fc + 1) % fx.nv)
618     {
619       msg (SE, _("The %d columns %d-%d can't be evenly divided into %d "
620                  "fields."), fx.lc - fx.fc + 1, fx.fc + 1, fx.lc + 1, fx.nv);
621       return 0;
622     }
623
624   dividend = (fx.lc - fx.fc + 1) / fx.nv;
625   fx.spec.u.v.f.w = dividend;
626   if (!check_output_specifier (&fx.spec.u.v.f))
627     return 0;
628   if ((type == ALPHA) ^ (formats[fx.spec.u.v.f.type].cat & FCAT_STRING))
629     {
630       msg (SE, _("%s variables cannot be displayed with format %s."),
631            type == ALPHA ? _("String") : _("Numeric"),
632            fmt_to_string (&fx.spec.u.v.f));
633       return 0;
634     }
635
636   /* Check that, for string variables, the user didn't specify a width
637      longer than an actual string width. */
638   if (type == ALPHA)
639     {
640       /* Minimum width of all the string variables specified. */
641       int min_len = fx.v[0]->width;
642
643       for (i = 1; i < fx.nv; i++)
644         min_len = min (min_len, fx.v[i]->width);
645       if (!check_string_specifier (&fx.spec.u.v.f, min_len))
646         return 0;
647     }
648
649   fx.spec.type = PRT_VAR;
650   for (i = 0; i < fx.nv; i++)
651     {
652       fx.spec.fc = fx.fc + dividend * i;
653       fx.spec.u.v.v = fx.v[i];
654       append_var_spec (&fx.spec);
655     }
656   return 1;
657 }
658
659 /* Destroy a format list and, optionally, all its sublists. */
660 static void
661 destroy_fmt_list (struct fmt_list * f, int recurse)
662 {
663   struct fmt_list *next;
664
665   for (; f; f = next)
666     {
667       next = f->next;
668       if (recurse && f->f.type == FMT_DESCEND)
669         destroy_fmt_list (f->down, 1);
670       free (f);
671     }
672 }
673
674 /* Recursively puts the format list F (which represents a set of
675    FORTRAN-like format specifications, like 4(F10,2X)) into the
676    structure prt. */
677 static int
678 dump_fmt_list (struct fmt_list * f)
679 {
680   int i;
681
682   for (; f; f = f->next)
683     if (f->f.type == FMT_X)
684       fx.sc += f->count;
685     else if (f->f.type == FMT_T)
686       fx.sc = f->f.w;
687     else if (f->f.type == FMT_NEWREC)
688       {
689         fx.recno += f->count;
690         fx.sc = 1;
691         fx.spec.type = PRT_NEWLINE;
692         for (i = 0; i < f->count; i++)
693           append_var_spec (&fx.spec);
694       }
695     else
696       for (i = 0; i < f->count; i++)
697         if (f->f.type == FMT_DESCEND)
698           {
699             if (!dump_fmt_list (f->down))
700               return 0;
701           }
702         else
703           {
704             struct variable *v;
705
706             if (fx.cv >= fx.nv)
707               {
708                 msg (SE, _("The number of format "
709                            "specifications exceeds the number of variable "
710                            "names given."));
711                 return 0;
712               }
713
714             v = fx.v[fx.cv++];
715             if ((v->type == ALPHA) ^ (formats[f->f.type].cat & FCAT_STRING))
716               {
717                 msg (SE, _("Display format %s may not be used with a "
718                            "%s variable."), fmt_to_string (&f->f),
719                      v->type == ALPHA ? _("string") : _("numeric"));
720                 return 0;
721               }
722             if (!check_string_specifier (&f->f, v->width))
723               return 0;
724
725             fx.spec.type = PRT_VAR;
726             fx.spec.u.v.v = v;
727             fx.spec.u.v.f = f->f;
728             fx.spec.fc = fx.sc - 1;
729             append_var_spec (&fx.spec);
730
731             fx.sc += f->f.w;
732           }
733   return 1;
734 }
735
736 /* Recursively parses a list of FORTRAN-like format specifiers.  Calls
737    itself to parse nested levels of parentheses.  Returns to its
738    original caller NULL, to indicate error, non-NULL, but nothing
739    useful, to indicate success (it returns a free()'d block). */
740 static struct fmt_list *
741 fixed_parse_fortran (void)
742 {
743   struct fmt_list *head;
744   struct fmt_list *fl = NULL;
745
746   lex_get ();                   /* skip opening parenthesis */
747   while (token != ')')
748     {
749       if (fl)
750         fl = fl->next = xmalloc (sizeof *fl);
751       else
752         head = fl = xmalloc (sizeof *fl);
753
754       if (token == T_NUM)
755         {
756           if (!lex_integer_p ())
757             goto fail;
758           fl->count = lex_integer ();
759           lex_get ();
760         }
761       else
762         fl->count = 1;
763
764       if (token == '(')
765         {
766           fl->f.type = FMT_DESCEND;
767           fx.level++;
768           fl->down = fixed_parse_fortran ();
769           fx.level--;
770           if (!fl->down)
771             goto fail;
772         }
773       else if (lex_match ('/'))
774         fl->f.type = FMT_NEWREC;
775       else if (!parse_format_specifier (&fl->f, 1)
776                || !check_output_specifier (&fl->f))
777         goto fail;
778
779       lex_match (',');
780     }
781   fl->next = NULL;
782   lex_get ();
783
784   if (fx.level)
785     return head;
786
787   fl->next = NULL;
788   dump_fmt_list (head);
789   destroy_fmt_list (head, 1);
790   if (fx.cv < fx.nv)
791     {
792       msg (SE, _("There aren't enough format specifications "
793            "to match the number of variable names given."));
794       goto fail;
795     }
796   return head;
797
798 fail:
799   fl->next = NULL;
800   destroy_fmt_list (head, 0);
801
802   return NULL;
803 }
804
805 /* Prints the table produced by the TABLE subcommand to the listing
806    file. */
807 static void
808 dump_table (void)
809 {
810   struct prt_out_spec *spec;
811   const char *filename;
812   struct tab_table *t;
813   int recno;
814   int nspec;
815
816   for (nspec = 0, spec = prt.spec; spec; spec = spec->next)
817     if (spec->type == PRT_CONST || spec->type == PRT_VAR)
818       nspec++;
819   t = tab_create (4, nspec + 1, 0);
820   tab_columns (t, TAB_COL_DOWN, 1);
821   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, nspec);
822   tab_hline (t, TAL_2, 0, 3, 1);
823   tab_headers (t, 0, 0, 1, 0);
824   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
825   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
826   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
827   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
828   tab_dim (t, tab_natural_dimensions);
829   for (nspec = recno = 0, spec = prt.spec; spec; spec = spec->next)
830     switch (spec->type)
831       {
832       case PRT_NEWLINE:
833         recno++;
834         break;
835       case PRT_CONST:
836         {
837           int len = strlen (spec->u.c);
838           nspec++;
839           tab_text (t, 0, nspec, TAB_LEFT | TAT_FIX | TAT_PRINTF,
840                         "\"%s\"", spec->u.c);
841           tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
842           tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
843                         spec->fc + 1, spec->fc + len);
844           tab_text (t, 3, nspec, TAB_LEFT | TAT_FIX | TAT_PRINTF,
845                         "A%d", len);
846           break;
847         }
848       case PRT_VAR:
849         {
850           nspec++;
851           tab_text (t, 0, nspec, TAB_LEFT, spec->u.v.v->name);
852           tab_text (t, 1, nspec, TAT_PRINTF, "%d", recno + 1);
853           tab_text (t, 2, nspec, TAT_PRINTF, "%3d-%3d",
854                         spec->fc + 1, spec->fc + spec->u.v.f.w);
855           tab_text (t, 3, nspec, TAB_LEFT | TAT_FIX,
856                         fmt_to_string (&spec->u.v.f));
857           break;
858         }
859       case PRT_SPACE:
860         break;
861       case PRT_ERROR:
862         assert (0);
863       }
864
865   filename = fh_handle_name (prt.handle);
866   tab_title (t, 1, (prt.handle != NULL
867                     ? _("Writing %3d records to file %s.")
868                     : _("Writing %3d records to the listing file.")),
869              recno, filename);
870   tab_submit (t);
871   fh_handle_name (NULL);
872 }
873
874 /* PORTME: The number of characters in a line terminator. */
875 #if __MSDOS__ 
876 #define LINE_END_WIDTH 2        /* \r\n */
877 #else
878 #define LINE_END_WIDTH 1        /* \n */
879 #endif
880
881 /* Calculates the maximum possible line width and allocates a buffer
882    big enough to contain it, if necessary (otherwise sets max_width).
883    (The action taken depends on compiler & OS as detected by pref.h.) */
884 static void
885 alloc_line (void)
886 {
887   /* Cumulative maximum line width (excluding null terminator) so far. */
888   int w = 0;
889
890   /* Width required by current this prt_out_spec. */
891   int pot_w;                    /* Potential w. */
892
893   /* Iterator. */
894   struct prt_out_spec *i;
895
896   for (i = prt.spec; i; i = i->next)
897     {
898       switch (i->type)
899         {
900         case PRT_NEWLINE:
901           pot_w = 0;
902           break;
903         case PRT_CONST:
904           pot_w = i->fc + strlen (i->u.c);
905           break;
906         case PRT_VAR:
907           pot_w = i->fc + i->u.v.f.w;
908           break;
909         case PRT_SPACE:
910           pot_w = i->fc + 1;
911           break;
912         case PRT_ERROR:
913           assert (0);
914           break;
915         }
916       if (pot_w > w)
917         w = pot_w;
918     }
919   prt.max_width = w + LINE_END_WIDTH + 1;
920 #if !PAGED_STACK
921   prt.line = xmalloc (prt.max_width);
922 #endif
923 }
924 \f
925 /* Transformation. */
926
927 /* Performs the transformation inside print_trns T on case C. */
928 static int
929 print_trns_proc (struct trns_header * trns, struct ccase * c)
930 {
931   /* Transformation. */
932   struct print_trns *t = (struct print_trns *) trns;
933
934   /* Iterator. */
935   struct prt_out_spec *i;
936
937   /* Line buffer. */
938 #if PAGED_STACK
939 #if __GNUC__ && !__STRICT_ANSI__
940   char buf[t->max_width];
941 #else /* !__GNUC__ */
942   char *buf = alloca (t->max_width);
943 #endif /* !__GNUC__ */
944 #else /* !PAGED_STACK */
945   char *buf = t->line;
946 #endif /* !PAGED_STACK */
947
948   /* Length of the line in buf. */
949   int len = 0;
950   memset (buf, ' ', t->max_width);
951
952   if (t->options & PRT_EJECT)
953     som_eject_page ();
954
955   /* Note that a field written to a place where a field has already
956      been written truncates the record.  `PRINT /A B (T10,F8,T1,F8).'
957      only outputs B.  This is an example of bug-for-bug compatibility,
958      in the author's opinion. */
959   for (i = t->spec; i; i = i->next)
960     switch (i->type)
961       {
962       case PRT_NEWLINE:
963         if (t->handle == NULL)
964           {
965             buf[len] = 0;
966             tab_output_text (TAT_FIX | TAT_NOWRAP, buf);
967           }
968         else
969           {
970             if ((t->options & PRT_CMD_MASK) == PRT_PRINT
971                 || t->handle->mode != FH_MD_BINARY)
972               {
973                 /* PORTME: Line ends. */
974 #if __MSDOS__
975                 buf[len++] = '\r';
976 #endif
977                 buf[len++] = '\n';
978               }
979
980             dfm_put_record (t->handle, buf, len);
981           }
982
983         memset (buf, ' ', t->max_width);
984         len = 0;
985         break;
986
987       case PRT_CONST:
988         /* FIXME: Should be revised to keep track of the string's
989            length outside the loop, probably in i->u.c[0]. */
990         memcpy (&buf[i->fc], i->u.c, strlen (i->u.c));
991         len = i->fc + strlen (i->u.c);
992         break;
993
994       case PRT_VAR:
995         if (i->u.v.v->type == NUMERIC)
996           data_out (&buf[i->fc], &i->u.v.f, &c->data[i->u.v.v->fv]);
997         else
998           {
999             union value t;
1000             t.c = c->data[i->u.v.v->fv].s;
1001             data_out (&buf[i->fc], &i->u.v.f, &t);
1002           }
1003         len = i->fc + i->u.v.f.w;
1004         break;
1005
1006       case PRT_SPACE:
1007         /* PRT_SPACE always immediately follows PRT_VAR. */
1008         buf[len++] = ' ';
1009         break;
1010
1011       case PRT_ERROR:
1012         assert (0);
1013         break;
1014       }
1015
1016   return -1;
1017 }
1018
1019 /* Frees all the data inside print_trns T.  Does not free T. */
1020 static void
1021 print_trns_free (struct trns_header * t)
1022 {
1023   struct prt_out_spec *i, *n;
1024
1025   for (i = ((struct print_trns *) t)->spec; i; i = n)
1026     {
1027       switch (i->type)
1028         {
1029         case PRT_CONST:
1030           free (i->u.c);
1031           /* fall through */
1032         case PRT_NEWLINE:
1033         case PRT_VAR:
1034         case PRT_SPACE:
1035           /* nothing to do */
1036           break;
1037         case PRT_ERROR:
1038           assert (0);
1039           break;
1040         }
1041       n = i->next;
1042       free (i);
1043     }
1044 #if !PAGED_STACK
1045   free (((struct print_trns *) t)->line);
1046 #endif
1047 }
1048 \f
1049 /* PRINT SPACE. */
1050
1051 /* PRINT SPACE transformation. */
1052 struct print_space_trns
1053 {
1054   struct trns_header h;
1055
1056   struct file_handle *handle;   /* Output file, NULL=listing file. */
1057   struct expression *e;         /* Number of lines; NULL=1. */
1058 }
1059 print_space_trns;
1060
1061 static int print_space_trns_proc (struct trns_header *, struct ccase *);
1062 static void print_space_trns_free (struct trns_header *);
1063
1064 int
1065 cmd_print_space (void)
1066 {
1067   struct print_space_trns *t;
1068   struct file_handle *handle;
1069   struct expression *e;
1070
1071   lex_match_id ("SPACE");
1072   if (lex_match_id ("OUTFILE"))
1073     {
1074       lex_match ('=');
1075
1076       if (token == T_ID)
1077         handle = fh_get_handle_by_name (tokid);
1078       else if (token == T_STRING)
1079         handle = fh_get_handle_by_filename (tokid);
1080       else
1081         {
1082           msg (SE, _("A file name or handle was expected in the "
1083                      "OUTFILE subcommand."));
1084           return CMD_FAILURE;
1085         }
1086       
1087       if (!handle)
1088         return CMD_FAILURE;
1089       lex_get ();
1090     }
1091   else
1092     handle = NULL;
1093
1094   if (token != '.')
1095     {
1096       e = expr_parse (PXP_NUMERIC);
1097       if (token != '.')
1098         {
1099           expr_free (e);
1100           lex_error (_("expecting end of command"));
1101           return CMD_FAILURE;
1102         }
1103     }
1104   else
1105     e = NULL;
1106
1107   t = xmalloc (sizeof *t);
1108   t->h.proc = print_space_trns_proc;
1109   if (e)
1110     t->h.free = print_space_trns_free;
1111   else
1112     t->h.free = NULL;
1113   t->handle = handle;
1114   t->e = e;
1115
1116   add_transformation ((struct trns_header *) t);
1117   return CMD_SUCCESS;
1118 }
1119
1120 static int
1121 print_space_trns_proc (struct trns_header * trns, struct ccase * c)
1122 {
1123   struct print_space_trns *t = (struct print_space_trns *) trns;
1124   int n;
1125
1126   if (t->e)
1127     {
1128       union value v;
1129
1130       expr_evaluate (t->e, c, &v);
1131       n = v.f;
1132       if (n < 0)
1133         {
1134           msg (SW, _("The expression on PRINT SPACE evaluated to %d.  It's "
1135                      "not possible to PRINT SPACE a negative number of "
1136                      "lines."),
1137                n);
1138           n = 1;
1139         }
1140     }
1141   else
1142     n = 1;
1143
1144   if (t->handle == NULL)
1145     while (n--)
1146       som_blank_line ();
1147   else
1148     {
1149       char buf[LINE_END_WIDTH];
1150
1151       /* PORTME: Line ends. */
1152 #if __MSDOS__
1153       buf[0] = '\r';
1154       buf[1] = '\n';
1155 #else
1156       buf[0] = '\n';
1157 #endif
1158       while (n--)
1159         dfm_put_record (t->handle, buf, LINE_END_WIDTH);
1160     }
1161
1162   return -1;
1163 }
1164
1165 static void
1166 print_space_trns_free (struct trns_header * trns)
1167 {
1168   expr_free (((struct print_space_trns *) trns)->e);
1169 }
1170 \f
1171 /* Debugging code. */
1172
1173 #if DEBUGGING
1174 void
1175 debug_print (void)
1176 {
1177   struct prt_out_spec *p;
1178
1179   if (prt.handle == NULL)
1180     {
1181       printf ("PRINT");
1182       if (prt.eject)
1183         printf (" EJECT");
1184     }
1185   else
1186     printf ("WRITE OUTFILE=%s", handle_name (prt.handle));
1187   printf (" MAX_WIDTH=%d", prt.max_width);
1188   printf (" /");
1189   for (p = prt.spec; p; p = p->next)
1190     switch (p->type)
1191       {
1192       case PRT_ERROR:
1193         printf (_("<ERROR>"));
1194         break;
1195       case PRT_NEWLINE:
1196         printf ("\n /");
1197         break;
1198       case PRT_CONST:
1199         printf (" \"%s\" %d-%d", p->u.c, p->fc + 1, p->fc + strlen (p->u.c));
1200         break;
1201       case PRT_VAR:
1202         printf (" %s %d %d-%d (%s)", p->u.v.v->name, p->u.v.v->fv, p->fc + 1,
1203                 p->fc + p->u.v.v->print.w, fmt_to_string (&p->u.v.v->print));
1204         break;
1205       case PRT_SPACE:
1206         printf (" \" \" %d", p->fc + 1);
1207         break;
1208       }
1209   printf (".\n");
1210 }
1211 #endif /* DEBUGGING */