Changed DFM from open-at-first-access to explicit-open. Before,
[pspp-builds.git] / src / sysfile-info.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 #include <config.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include "algorithm.h"
25 #include "alloc.h"
26 #include "command.h"
27 #include "error.h"
28 #include "file-handle.h"
29 #include "hash.h"
30 #include "lexer.h"
31 #include "misc.h"
32 #include "output.h"
33 #include "sfm.h"
34 #include "som.h"
35 #include "tab.h"
36 #include "value-labels.h"
37 #include "var.h"
38
39 /* Constants for DISPLAY utility. */
40 enum
41   {
42     AS_NAMES = 0,
43     AS_INDEX,
44     AS_VARIABLES,
45     AS_LABELS,
46     AS_DICTIONARY,
47     AS_SCRATCH,
48     AS_VECTOR
49   };
50
51 int describe_variable (struct variable *v, struct tab_table *t, int r, int as);
52      
53 /* Sets the widths of all the columns and heights of all the rows in
54    table T for driver D. */
55 static void
56 sysfile_info_dim (struct tab_table *t, struct outp_driver *d)
57 {
58   static const int max[] = {20, 5, 35, 3, 0};
59   const int *p;
60   int i;
61
62   for (p = max; *p; p++)
63     t->w[p - max] = min (tab_natural_width (t, d, p - max),
64                          *p * d->prop_em_width);
65   for (i = 0; i < t->nr; i++)
66     t->h[i] = tab_natural_height (t, d, i);
67 }
68
69 /* SYSFILE INFO utility. */
70 int
71 cmd_sysfile_info (void)
72 {
73   struct file_handle *h;
74   struct dictionary *d;
75   struct tab_table *t;
76   struct sfm_read_info inf;
77   int r, nr;
78   int i;
79
80   lex_match_id ("FILE");
81   lex_match ('=');
82
83   h = fh_parse_file_handle ();
84   if (!h)
85     return CMD_FAILURE;
86
87   d = sfm_read_dictionary (h, &inf);
88   fh_close_handle (h);
89   if (!d)
90     return CMD_FAILURE;
91
92   t = tab_create (2, 9, 0);
93   tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, 8);
94   tab_text (t, 0, 0, TAB_LEFT, _("File:"));
95   tab_text (t, 1, 0, TAB_LEFT, handle_get_filename (h));
96   tab_text (t, 0, 1, TAB_LEFT, _("Label:"));
97   {
98     const char *label = dict_get_label (d);
99     if (label == NULL)
100       label = _("No label.");
101     tab_text (t, 1, 1, TAB_LEFT, label);
102   }
103   tab_text (t, 0, 2, TAB_LEFT, _("Created:"));
104   tab_text (t, 1, 2, TAB_LEFT | TAT_PRINTF, "%s %s by %s",
105                 inf.creation_date, inf.creation_time, inf.product);
106   tab_text (t, 0, 3, TAB_LEFT, _("Endian:"));
107   tab_text (t, 1, 3, TAB_LEFT, inf.bigendian ? _("Big.") : _("Little."));
108   tab_text (t, 0, 4, TAB_LEFT, _("Variables:"));
109   tab_text (t, 1, 4, TAB_LEFT | TAT_PRINTF, "%d",
110                 dict_get_var_cnt (d));
111   tab_text (t, 0, 5, TAB_LEFT, _("Cases:"));
112   tab_text (t, 1, 5, TAB_LEFT | TAT_PRINTF,
113                 inf.ncases == -1 ? _("Unknown") : "%d", inf.ncases);
114   tab_text (t, 0, 6, TAB_LEFT, _("Type:"));
115   tab_text (t, 1, 6, TAB_LEFT, _("System File."));
116   tab_text (t, 0, 7, TAB_LEFT, _("Weight:"));
117   {
118     struct variable *weight_var = dict_get_weight (d);
119     tab_text (t, 1, 7, TAB_LEFT,
120               weight_var != NULL ? weight_var->name : _("Not weighted.")); 
121   }
122   tab_text (t, 0, 8, TAB_LEFT, _("Mode:"));
123   tab_text (t, 1, 8, TAB_LEFT | TAT_PRINTF,
124                 _("Compression %s."), inf.compressed ? _("on") : _("off"));
125   tab_dim (t, tab_natural_dimensions);
126   tab_submit (t);
127
128   nr = 1 + 2 * dict_get_var_cnt (d);
129   t = tab_create (4, nr, 1);
130   tab_dim (t, sysfile_info_dim);
131   tab_headers (t, 0, 0, 1, 0);
132   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
133   tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
134   tab_text (t, 3, 0, TAB_LEFT | TAT_TITLE, _("Position"));
135   tab_hline (t, TAL_2, 0, 3, 1);
136   for (r = 1, i = 0; i < dict_get_var_cnt (d); i++)
137     {
138       struct variable *v = dict_get_var (d, i);
139       int nvl = val_labs_count (v->val_labs);
140       
141       if (r + 10 + nvl > nr)
142         {
143           nr = max (nr * dict_get_var_cnt (d) / (i + 1), nr);
144           nr += 10 + nvl;
145           tab_realloc (t, 4, nr);
146         }
147
148       r = describe_variable (v, t, r, AS_DICTIONARY);
149     }
150   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, r);
151   tab_vline (t, TAL_1, 0, 0, r);
152   tab_vline (t, TAL_1, 1, 0, r);
153   tab_vline (t, TAL_1, 3, 0, r);
154   tab_resize (t, -1, r);
155   tab_flags (t, SOMF_NO_TITLE);
156   tab_submit (t);
157
158   dict_destroy (d);
159   
160   return lex_end_of_command ();
161 }
162 \f
163 /* DISPLAY utility. */
164
165 static void display_macros (void);
166 static void display_documents (void);
167 static void display_variables (struct variable **, int, int);
168 static void display_vectors (int sorted);
169
170 int
171 cmd_display (void)
172 {
173   /* Whether to sort the list of variables alphabetically. */
174   int sorted;
175
176   /* Variables to display. */
177   int n;
178   struct variable **vl;
179
180   if (lex_match_id ("MACROS"))
181     display_macros ();
182   else if (lex_match_id ("DOCUMENTS"))
183     display_documents ();
184   else if (lex_match_id ("FILE"))
185     {
186       som_blank_line ();
187       if (!lex_force_match_id ("LABEL"))
188         return CMD_FAILURE;
189       if (dict_get_label (default_dict) == NULL)
190         tab_output_text (TAB_LEFT,
191                          _("The active file does not have a file label."));
192       else
193         {
194           tab_output_text (TAB_LEFT | TAT_TITLE, _("File label:"));
195           tab_output_text (TAB_LEFT | TAT_FIX, dict_get_label (default_dict));
196         }
197     }
198   else
199     {
200       static const char *sbc[] =
201         {"NAMES", "INDEX", "VARIABLES", "LABELS",
202          "DICTIONARY", "SCRATCH", "VECTORS", NULL};
203       const char **cp;
204       int as;
205
206       sorted = lex_match_id ("SORTED");
207
208       for (cp = sbc; *cp; cp++)
209         if (token == T_ID && lex_id_match (*cp, tokid))
210           {
211             lex_get ();
212             break;
213           }
214       as = cp - sbc;
215
216       if (*cp == NULL)
217         as = AS_NAMES;
218
219       if (as == AS_VECTOR)
220         {
221           display_vectors (sorted);
222           return CMD_SUCCESS;
223         }
224
225       lex_match ('/');
226       lex_match_id ("VARIABLES");
227       lex_match ('=');
228
229       if (token != '.')
230         {
231           if (!parse_variables (default_dict, &vl, &n, PV_NONE))
232             {
233               free (vl);
234               return CMD_FAILURE;
235             }
236           as = AS_DICTIONARY;
237         }
238       else
239         dict_get_vars (default_dict, &vl, &n, 0);
240
241       if (as == AS_SCRATCH)
242         {
243           int i, m;
244           for (i = 0, m = n; i < n; i++)
245             if (dict_class_from_id (vl[i]->name) != DC_SCRATCH)
246               {
247                 vl[i] = NULL;
248                 m--;
249               }
250           as = AS_NAMES;
251           n = m;
252         }
253
254       if (n == 0)
255         {
256           msg (SW, _("No variables to display."));
257           return CMD_FAILURE;
258         }
259
260       if (sorted)
261         sort (vl, n, sizeof *vl, compare_variables, NULL);
262
263       display_variables (vl, n, as);
264
265       free (vl);
266     }
267
268   return lex_end_of_command ();
269 }
270
271 static void
272 display_macros (void)
273 {
274   som_blank_line ();
275   tab_output_text (TAB_LEFT, _("Macros not supported."));
276 }
277
278 static void
279 display_documents (void)
280 {
281   const char *documents = dict_get_documents (default_dict);
282
283   som_blank_line ();
284   if (documents == NULL)
285     tab_output_text (TAB_LEFT, _("The active file dictionary does not "
286                                  "contain any documents."));
287   else
288     {
289       size_t n_lines = strlen (documents) / 80;
290       char buf[81];
291       size_t i;
292
293       tab_output_text (TAB_LEFT | TAT_TITLE,
294                        _("Documents in the active file:"));
295       som_blank_line ();
296       buf[80] = 0;
297       for (i = 0; i < n_lines; i++)
298         {
299           int len = 79;
300
301           memcpy (buf, &documents[i * 80], 80);
302           while ((isspace ((unsigned char) buf[len]) || buf[len] == 0)
303                  && len > 0)
304             len--;
305           buf[len + 1] = 0;
306           tab_output_text (TAB_LEFT | TAT_FIX | TAT_NOWRAP, buf);
307         }
308     }
309 }
310
311 static int _as;
312
313 /* Sets the widths of all the columns and heights of all the rows in
314    table T for driver D. */
315 static void
316 variables_dim (struct tab_table *t, struct outp_driver *d)
317 {
318   int pc;
319   int i;
320   
321   t->w[0] = tab_natural_width (t, d, 0);
322   if (_as == AS_DICTIONARY || _as == AS_VARIABLES || _as == AS_LABELS)
323     {
324       t->w[1] = max (tab_natural_width (t, d, 1), d->prop_em_width * 5);
325       t->w[2] = max (tab_natural_width (t, d, 2), d->prop_em_width * 35);
326       pc = 3;
327     }
328   else pc = 1;
329   if (_as != AS_NAMES)
330     t->w[pc] = tab_natural_width (t, d, pc);
331
332   for (i = 0; i < t->nr; i++)
333     t->h[i] = tab_natural_height (t, d, i);
334 }
335   
336 static void
337 display_variables (struct variable **vl, int n, int as)
338 {
339   struct variable **vp = vl;            /* Variable pointer. */
340   struct tab_table *t;
341   int nc;                       /* Number of columns. */
342   int nr;                       /* Number of rows. */
343   int pc;                       /* `Position column' */
344   int r;                        /* Current row. */
345   int i;
346
347   _as = as;
348   switch (as)
349     {
350     case AS_INDEX:
351       nc = 2;
352       break;
353     case AS_NAMES:
354       nc = 1;
355       break;
356     default:
357       nc = 4;
358       break;
359     }
360
361   t = tab_create (nc, n + 5, 1);
362   tab_headers (t, 0, 0, 1, 0);
363   nr = n + 5;
364   tab_hline (t, TAL_2, 0, nc - 1, 1);
365   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
366   pc = (as == AS_INDEX ? 1 : 3);
367   if (as != AS_NAMES)
368     tab_text (t, pc, 0, TAB_LEFT | TAT_TITLE, _("Position"));
369   if (as == AS_DICTIONARY || as == AS_VARIABLES)
370     tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
371   else if (as == AS_LABELS)
372     tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Label"));
373   tab_dim (t, variables_dim);
374     
375   for (i = r = 1; i <= n; i++)
376     {
377       struct variable *v;
378
379       while (*vp == NULL)
380         vp++;
381       v = *vp++;
382
383       if (as == AS_DICTIONARY || as == AS_VARIABLES)
384         {
385           int nvl = val_labs_count (v->val_labs);
386       
387           if (r + 10 + nvl > nr)
388             {
389               nr = max (nr * n / (i + 1), nr);
390               nr += 10 + nvl;
391               tab_realloc (t, nc, nr);
392             }
393
394           r = describe_variable (v, t, r, as);
395         } else {
396           tab_text (t, 0, r, TAB_LEFT, v->name);
397           if (as == AS_LABELS)
398             tab_joint_text (t, 1, r, 2, r, TAB_LEFT,
399                             v->label == NULL ? "(no label)" : v->label);
400           if (as != AS_NAMES)
401             {
402               tab_text (t, pc, r, TAT_PRINTF, "%d", v->index + 1);
403               tab_hline (t, TAL_1, 0, nc - 1, r);
404             }
405           r++;
406         }
407     }
408   tab_hline (t, as == AS_NAMES ? TAL_1 : TAL_2, 0, nc - 1, 1);
409   if (as != AS_NAMES)
410     {
411       tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, r - 1);
412       tab_vline (t, TAL_1, 1, 0, r - 1);
413     }
414   else
415     tab_flags (t, SOMF_NO_TITLE);
416   if (as == AS_DICTIONARY || as == AS_VARIABLES || as == AS_LABELS)
417     tab_vline (t, TAL_1, 3, 0, r - 1);
418   tab_resize (t, -1, r);
419   tab_columns (t, TAB_COL_DOWN, 1);
420   tab_submit (t);
421 }
422 \f
423 /* Puts a description of variable V into table T starting at row R.
424    The variable will be described in the format AS.  Returns the next
425    row available for use in the table. */
426 int 
427 describe_variable (struct variable *v, struct tab_table *t, int r, int as)
428 {
429   /* Put the name, var label, and position into the first row. */
430   tab_text (t, 0, r, TAB_LEFT, v->name);
431   tab_text (t, 3, r, TAT_PRINTF, "%d", v->index + 1);
432
433   if (as == AS_DICTIONARY && v->label)
434     {
435       tab_joint_text (t, 1, r, 2, r, TAB_LEFT, v->label);
436       r++;
437     }
438   
439   /* Print/write format, or print and write formats. */
440   if (v->print.type == v->write.type
441       && v->print.w == v->write.w
442       && v->print.d == v->write.d)
443     {
444       tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF, _("Format: %s"),
445                       fmt_to_string (&v->print));
446       r++;
447     }
448   else
449     {
450       tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
451                       _("Print Format: %s"), fmt_to_string (&v->print));
452       r++;
453       tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
454                       _("Write Format: %s"), fmt_to_string (&v->write));
455       r++;
456     }
457
458   /* Missing values if any. */
459   if (v->miss_type != MISSING_NONE)
460     {
461       char buf[80];
462       char *cp = stpcpy (buf, _("Missing Values: "));
463
464       if (v->type == NUMERIC)
465         switch (v->miss_type)
466           {
467           case MISSING_1:
468             sprintf (cp, "%g", v->missing[0].f);
469             break;
470           case MISSING_2:
471             sprintf (cp, "%g; %g", v->missing[0].f, v->missing[1].f);
472             break;
473           case MISSING_3:
474             sprintf (cp, "%g; %g; %g", v->missing[0].f,
475                      v->missing[1].f, v->missing[2].f);
476             break;
477           case MISSING_RANGE:
478             sprintf (cp, "%g THRU %g", v->missing[0].f, v->missing[1].f);
479             break;
480           case MISSING_LOW:
481             sprintf (cp, "LOWEST THRU %g", v->missing[0].f);
482             break;
483           case MISSING_HIGH:
484             sprintf (cp, "%g THRU HIGHEST", v->missing[0].f);
485             break;
486           case MISSING_RANGE_1:
487             sprintf (cp, "%g THRU %g; %g",
488                      v->missing[0].f, v->missing[1].f, v->missing[2].f);
489             break;
490           case MISSING_LOW_1:
491             sprintf (cp, "LOWEST THRU %g; %g",
492                      v->missing[0].f, v->missing[1].f);
493             break;
494           case MISSING_HIGH_1:
495             sprintf (cp, "%g THRU HIGHEST; %g",
496                      v->missing[0].f, v->missing[1].f);
497             break;
498           default:
499             assert (0);
500           }
501       else
502         {
503           int i;
504
505           for (i = 0; i < v->miss_type; i++)
506             {
507               if (i != 0)
508                 cp = stpcpy (cp, "; ");
509               *cp++ = '"';
510               memcpy (cp, v->missing[i].s, v->width);
511               cp += v->width;
512               *cp++ = '"';
513             }
514           *cp = 0;
515         }
516
517       tab_joint_text (t, 1, r, 2, r, TAB_LEFT, buf);
518       r++;
519     }
520
521   /* Value labels. */
522   if (as == AS_DICTIONARY && val_labs_count (v->val_labs))
523     {
524       struct val_labs_iterator *i;
525       struct val_lab *vl;
526       int orig_r = r;
527
528 #if 0
529       tab_text (t, 1, r, TAB_LEFT, _("Value"));
530       tab_text (t, 2, r, TAB_LEFT, _("Label"));
531       r++;
532 #endif
533
534       tab_hline (t, TAL_1, 1, 2, r);
535       for (vl = val_labs_first_sorted (v->val_labs, &i); vl != NULL;
536            vl = val_labs_next (v->val_labs, &i))
537         {
538           char buf[128];
539
540           if (v->type == ALPHA)
541             {
542               memcpy (buf, vl->value.s, v->width);
543               buf[v->width] = 0;
544             }
545           else
546             sprintf (buf, "%g", vl->value.f);
547
548           tab_text (t, 1, r, TAB_NONE, buf);
549           tab_text (t, 2, r, TAB_LEFT, vl->label);
550           r++;
551         }
552
553       tab_vline (t, TAL_1, 2, orig_r, r - 1);
554     }
555
556   /* Draw a line below the last row of information on this variable. */
557   tab_hline (t, TAL_1, 0, 3, r);
558
559   return r;
560 }
561
562 static int
563 compare_vectors_by_name (const void *a_, const void *b_)
564 {
565   struct vector *const *pa = a_;
566   struct vector *const *pb = b_;
567   struct vector *a = *pa;
568   struct vector *b = *pb;
569   
570   return strcmp (a->name, b->name);
571 }
572
573 /* Display a list of vectors.  If SORTED is nonzero then they are
574    sorted alphabetically. */
575 static void
576 display_vectors (int sorted)
577 {
578   const struct vector **vl;
579   int i;
580   struct tab_table *t;
581   size_t nvec;
582   
583   nvec = dict_get_vector_cnt (default_dict);
584   if (nvec == 0)
585     {
586       msg (SW, _("No vectors defined."));
587       return;
588     }
589
590   vl = xmalloc (sizeof *vl * nvec);
591   for (i = 0; i < nvec; i++)
592     vl[i] = dict_get_vector (default_dict, i);
593   if (sorted)
594     qsort (vl, nvec, sizeof *vl, compare_vectors_by_name);
595
596   t = tab_create (1, nvec + 1, 0);
597   tab_headers (t, 0, 0, 1, 0);
598   tab_columns (t, TAB_COL_DOWN, 1);
599   tab_dim (t, tab_natural_dimensions);
600   tab_hline (t, TAL_1, 0, 0, 1);
601   tab_text (t, 0, 0, TAT_TITLE | TAB_LEFT, _("Vector"));
602   tab_flags (t, SOMF_NO_TITLE);
603   for (i = 0; i < nvec; i++)
604     tab_text (t, 0, i + 1, TAB_LEFT, vl[i]->name);
605   tab_submit (t);
606
607   free (vl);
608 }