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