Completely rewrite src/data/format.[ch], to achieve better
[pspp-builds.git] / src / language / utilities / set.q
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 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 <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <time.h>
26
27 #include <data/dictionary.h>
28 #include <data/format.h>
29 #include <data/procedure.h>
30 #include <data/settings.h>
31 #include <data/variable.h>
32 #include <language/command.h>
33 #include <language/lexer/format-parser.h>
34 #include <language/lexer/lexer.h>
35 #include <language/line-buffer.h>
36 #include <libpspp/alloc.h>
37 #include <libpspp/compiler.h>
38 #include <libpspp/copyleft.h>
39 #include <libpspp/magic.h>
40 #include <libpspp/message.h>
41 #include <math/random.h>
42 #include <output/output.h>
43
44 #if HAVE_LIBTERMCAP
45 #if HAVE_TERMCAP_H
46 #include <termcap.h>
47 #else /* !HAVE_TERMCAP_H */
48 int tgetent (char *, const char *);
49 int tgetnum (const char *);
50 #endif /* !HAVE_TERMCAP_H */
51 #endif /* !HAVE_LIBTERMCAP */
52
53 #include "gettext.h"
54 #define _(msgid) gettext (msgid)
55
56 /* (specification)
57    "SET" (stc_):
58      blanks=custom;
59      block=string "x==1" "one character long";
60      boxstring=string "x==3 || x==11" "3 or 11 characters long";
61      case=size:upper/uplow;
62      cca=string;
63      ccb=string;
64      ccc=string;
65      ccd=string;
66      cce=string;
67      compression=compress:on/off;
68      cpi=integer "x>0" "%s must be greater than 0";
69      cprompt=string;
70      decimal=dec:dot/comma;
71      disk=custom;
72      dprompt=string;
73      echo=echo:on/off;
74      endcmd=string "x==1" "one character long";
75      epoch=custom;
76      errorbreak=errbrk:on/off;
77      errors=errors:on/off/terminal/listing/both/none;
78      format=custom;
79      headers=headers:no/yes/blank;
80      highres=hires:on/off;
81      histogram=string "x==1" "one character long";
82      include=inc:on/off;
83      journal=custom;
84      length=custom;
85      listing=custom;
86      lowres=lores:auto/on/off;
87      lpi=integer "x>0" "%s must be greater than 0";
88      menus=menus:standard/extended;
89      messages=messages:on/off/terminal/listing/both/none;
90      mexpand=mexp:on/off;
91      miterate=integer "x>0" "%s must be greater than 0";
92      mnest=integer "x>0" "%s must be greater than 0";
93      mprint=mprint:on/off;
94      mxerrs=integer "x >= 1" "%s must be at least 1";
95      mxloops=integer "x >=1" "%s must be at least 1";
96      mxmemory=integer;
97      mxwarns=integer;
98      nulline=null:on/off;
99      printback=prtbck:on/off;
100      prompt=string;
101      results=res:on/off/terminal/listing/both/none;
102      safer=safe:on;
103      scompression=scompress:on/off;
104      scripttab=string "x==1" "one character long";
105      seed=custom;
106      tb1=string "x==3 || x==11" "3 or 11 characters long";
107      tbfonts=string;
108      undefined=undef:warn/nowarn;
109      width=custom;
110      workspace=integer "x>=1024" "%s must be at least 1 MB";
111      xsort=xsort:yes/no.
112 */
113
114 /* (headers) */
115
116 /* (declarations) */
117
118 /* (functions) */
119
120 static bool do_cc (const char *cc_string, enum fmt_type);
121
122 int
123 cmd_set (struct dataset *ds)
124 {
125   struct cmd_set cmd;
126
127   if (!parse_set (ds, &cmd, NULL))
128     return CMD_FAILURE;
129
130   if (cmd.sbc_cca)
131     do_cc (cmd.s_cca, FMT_CCA);
132   if (cmd.sbc_ccb)
133     do_cc (cmd.s_ccb, FMT_CCB);
134   if (cmd.sbc_ccc)
135     do_cc (cmd.s_ccc, FMT_CCC);
136   if (cmd.sbc_ccd)
137     do_cc (cmd.s_ccd, FMT_CCD);
138   if (cmd.sbc_cce)
139     do_cc (cmd.s_cce, FMT_CCE);
140
141   if (cmd.sbc_prompt)
142     getl_set_prompt (GETL_PROMPT_FIRST, cmd.s_prompt);
143   if (cmd.sbc_cprompt)
144     getl_set_prompt (GETL_PROMPT_LATER, cmd.s_cprompt);
145   if (cmd.sbc_dprompt)
146     getl_set_prompt (GETL_PROMPT_DATA, cmd.s_dprompt);
147
148   if (cmd.sbc_decimal)
149     fmt_set_decimal (cmd.dec == STC_DOT ? '.' : ',');
150   if (cmd.sbc_echo)
151     set_echo (cmd.echo == STC_ON);
152   if (cmd.sbc_endcmd)
153     set_endcmd (cmd.s_endcmd[0]);
154   if (cmd.sbc_errorbreak)
155     set_errorbreak (cmd.errbrk == STC_ON);
156   if (cmd.sbc_include)
157     set_include (cmd.inc == STC_ON);
158   if (cmd.sbc_mxerrs)
159     set_mxerrs (cmd.n_mxerrs[0]);
160   if (cmd.sbc_mxwarns)
161     set_mxwarns (cmd.n_mxwarns[0]);
162   if (cmd.sbc_nulline)
163     set_nulline (cmd.null == STC_ON);
164   if (cmd.sbc_safer)
165     set_safer_mode ();
166   if (cmd.sbc_scompression)
167     set_scompression (cmd.scompress == STC_ON);
168   if (cmd.sbc_undefined)
169     set_undefined (cmd.undef == STC_WARN);
170   if (cmd.sbc_workspace)
171     set_workspace (cmd.n_workspace[0] * 1024L);
172
173   if (cmd.sbc_block)
174     msg (SW, _("%s is obsolete."), "BLOCK");
175   if (cmd.sbc_boxstring)
176     msg (SW, _("%s is obsolete."), "BOXSTRING");
177   if (cmd.sbc_histogram)
178     msg (SW, _("%s is obsolete."), "HISTOGRAM");
179   if (cmd.sbc_menus)
180     msg (SW, _("%s is obsolete."), "MENUS");
181   if (cmd.sbc_xsort)
182     msg (SW, _("%s is obsolete."), "XSORT");
183   if (cmd.sbc_mxmemory)
184     msg (SE, _("%s is obsolete."), "MXMEMORY");
185   if (cmd.sbc_scripttab)
186     msg (SE, _("%s is obsolete."), "SCRIPTTAB");
187   if (cmd.sbc_tbfonts)
188     msg (SW, _("%s is obsolete."), "TBFONTS");
189   if (cmd.sbc_tb1 && cmd.s_tb1)
190     msg (SW, _("%s is obsolete."), "TB1");
191
192   if (cmd.sbc_case)
193     msg (SW, _("%s is not implemented."), "CASE");
194
195   if (cmd.sbc_compression)
196     msg (SW, _("Active file compression is not implemented."));
197
198   return CMD_SUCCESS;
199 }
200
201 /* Find the grouping characters in CC_STRING and set CC's
202    grouping and decimal members appropriately.  Returns true if
203    successful, false otherwise. */
204 static bool
205 find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
206 {
207   const char *sp;
208   int comma_cnt, dot_cnt;
209   
210   /* Count commas and periods.  There must be exactly three of
211      one or the other, except that an apostrophe escapes a
212      following comma or period. */
213   comma_cnt = dot_cnt = 0;
214   for (sp = cc_string; *sp; sp++)
215     if (*sp == ',')
216       comma_cnt++;
217     else if (*sp == '.')
218       dot_cnt++;
219     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
220       sp++;
221   
222   if ((comma_cnt == 3) == (dot_cnt == 3))
223     return false;
224
225   if (comma_cnt == 3)
226     {
227       cc->decimal = '.';
228       cc->grouping = ',';
229     }
230   else
231     {
232       cc->decimal = ',';
233       cc->grouping = '.';
234     }
235   return true;
236 }
237
238 /* Extracts a token from IN into a newly allocated AFFIX.  Tokens
239    are delimited by GROUPING.  The token is truncated to at most
240    FMT_STYLE_AFFIX_MAX characters.  Returns the first character
241    following the token. */
242 static const char *
243 extract_cc_token (const char *in, int grouping, struct substring *affix) 
244 {
245   size_t ofs = 0;
246   ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX);
247   for (; *in != '\0' && *in != grouping; in++) 
248     {
249       if (*in == '\'' && in[1] == grouping)
250         in++;
251       if (ofs < FMT_STYLE_AFFIX_MAX) 
252         ss_data (*affix)[ofs++] = *in;
253     }
254   affix->length = ofs;
255
256   if (*in == grouping)
257     in++;
258   return in;
259 }
260
261 /* Sets custom currency specifier CC having name CC_NAME ('A' through
262    'E') to correspond to the settings in CC_STRING. */
263 static bool
264 do_cc (const char *cc_string, enum fmt_type type)
265 {
266   struct fmt_number_style *cc = fmt_number_style_create ();
267   
268   /* Determine separators. */
269   if (!find_cc_separators (cc_string, cc)) 
270     {
271       fmt_number_style_destroy (cc);
272       msg (SE, _("%s: Custom currency string `%s' does not contain "
273                  "exactly three periods or commas (or it contains both)."),
274            fmt_name (type), cc_string);
275       return false;
276     }
277   
278   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix);
279   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix);
280   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix);
281   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix);
282
283   fmt_set_style (type, cc);
284   
285   return true;
286 }
287
288 /* Parses the BLANKS subcommand, which controls the value that
289    completely blank fields in numeric data imply.  X, Wnd: Syntax is
290    SYSMIS or a numeric value. */
291 static int
292 stc_custom_blanks (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
293 {
294   lex_match ('=');
295   if ((token == T_ID && lex_id_match ("SYSMIS", tokid)))
296     {
297       lex_get ();
298       set_blanks (SYSMIS);
299     }
300   else
301     {
302       if (!lex_force_num ())
303         return 0;
304       set_blanks (lex_number ());
305       lex_get ();
306     }
307   return 1;
308 }
309
310 /* Parses the EPOCH subcommand, which controls the epoch used for
311    parsing 2-digit years. */
312 static int
313 stc_custom_epoch (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) 
314 {
315   lex_match ('=');
316   if (lex_match_id ("AUTOMATIC"))
317     set_epoch (-1);
318   else if (lex_is_integer ()) 
319     {
320       int new_epoch = lex_integer ();
321       lex_get ();
322       if (new_epoch < 1500) 
323         {
324           msg (SE, _("EPOCH must be 1500 or later."));
325           return 0;
326         }
327       set_epoch (new_epoch);
328     }
329   else 
330     {
331       lex_error (_("expecting AUTOMATIC or year"));
332       return 0;
333     }
334
335   return 1;
336 }
337
338 static int
339 stc_custom_length (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
340 {
341   int page_length;
342
343   lex_match ('=');
344   if (lex_match_id ("NONE"))
345     page_length = -1;
346   else
347     {
348       if (!lex_force_int ())
349         return 0;
350       if (lex_integer () < 1)
351         {
352           msg (SE, _("LENGTH must be at least 1."));
353           return 0;
354         }
355       page_length = lex_integer ();
356       lex_get ();
357     }
358
359   if (page_length != -1) 
360     set_viewlength (page_length);
361
362   return 1;
363 }
364
365 static int
366 stc_custom_seed (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
367 {
368   lex_match ('=');
369   if (lex_match_id ("RANDOM"))
370     set_rng (time (0));
371   else
372     {
373       if (!lex_force_num ())
374         return 0;
375       set_rng (lex_number ());
376       lex_get ();
377     }
378
379   return 1;
380 }
381
382 static int
383 stc_custom_width (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
384 {
385   lex_match ('=');
386   if (lex_match_id ("NARROW"))
387     set_viewwidth (79);
388   else if (lex_match_id ("WIDE"))
389     set_viewwidth (131);
390   else
391     {
392       if (!lex_force_int ())
393         return 0;
394       if (lex_integer () < 40)
395         {
396           msg (SE, _("WIDTH must be at least 40."));
397           return 0;
398         }
399       set_viewwidth (lex_integer ());
400       lex_get ();
401     }
402
403   return 1;
404 }
405
406 /* Parses FORMAT subcommand, which consists of a numeric format
407    specifier. */
408 static int
409 stc_custom_format (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
410 {
411   struct fmt_spec fmt;
412
413   lex_match ('=');
414   if (!parse_format_specifier (&fmt))
415     return 0;
416   if (fmt_is_string (fmt.type))
417     {
418       char str[FMT_STRING_LEN_MAX + 1];
419       msg (SE, _("FORMAT requires numeric output format as an argument.  "
420                  "Specified format %s is of type string."),
421            fmt_to_string (&fmt, str));
422       return 0;
423     }
424
425   set_format (&fmt);
426   return 1;
427 }
428
429 static int
430 stc_custom_journal (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
431 {
432   lex_match ('=');
433   if (!lex_match_id ("ON") && !lex_match_id ("OFF")) 
434     {
435       if (token == T_STRING)
436         lex_get ();
437       else
438         {
439           lex_error (NULL);
440           return 0;
441         }
442     }
443   return 1;
444 }
445
446 static int
447 stc_custom_listing (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
448 {
449   bool listing;
450
451   lex_match ('=');
452   if (lex_match_id ("ON") || lex_match_id ("YES"))
453     listing = true;
454   else if (lex_match_id ("OFF") || lex_match_id ("NO"))
455     listing = false;
456   else
457     {
458       /* FIXME */
459       return 0;
460     }
461   outp_enable_device (listing, OUTP_DEV_LISTING);
462
463   return 1;
464 }
465
466 static int
467 stc_custom_disk (struct dataset *ds, struct cmd_set *cmd UNUSED, void *aux)
468 {
469   return stc_custom_listing (ds, cmd, aux);
470 }
471 \f
472 static void
473 show_blanks (const struct dataset *ds UNUSED) 
474 {
475   if (get_blanks () == SYSMIS)
476     msg (SN, _("BLANKS is SYSMIS."));
477   else
478     msg (SN, _("BLANKS is %g."), get_blanks ());
479
480 }
481
482 static char *
483 format_cc (struct substring in, char grouping, char *out) 
484 {
485   while (!ss_is_empty (in)) 
486     {
487       char c = ss_get_char (&in);
488       if (c == grouping || c == '\'')
489         *out++ = '\'';
490       else if (c == '"')
491         *out++ = '"';
492       *out++ = c;
493     }
494   return out;
495 }
496
497 static void
498 show_cc (enum fmt_type type) 
499 {
500   const struct fmt_number_style *cc = fmt_get_style (type);
501   char cc_string[FMT_STYLE_AFFIX_MAX * 4 * 2 + 3 + 1];
502   char *out;
503
504   out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
505   *out++ = cc->grouping;
506   out = format_cc (cc->prefix, cc->grouping, out);
507   *out++ = cc->grouping;
508   out = format_cc (cc->suffix, cc->grouping, out);
509   *out++ = cc->grouping;
510   out = format_cc (cc->neg_suffix, cc->grouping, out);
511   *out = '\0';
512   
513   msg (SN, _("%s is \"%s\"."), fmt_name (type), cc_string);
514 }
515
516 static void
517 show_cca (const struct dataset *ds UNUSED) 
518 {
519   show_cc (FMT_CCA);
520 }
521
522 static void
523 show_ccb (const struct dataset *ds UNUSED) 
524 {
525   show_cc (FMT_CCB);
526 }
527
528 static void
529 show_ccc (const struct dataset *ds UNUSED) 
530 {
531   show_cc (FMT_CCC);
532 }
533
534 static void
535 show_ccd (const struct dataset *ds UNUSED) 
536 {
537   show_cc (FMT_CCD);
538 }
539
540 static void
541 show_cce (const struct dataset *ds UNUSED) 
542 {
543   show_cc (FMT_CCE);
544 }
545
546 static void
547 show_decimals (const struct dataset *ds UNUSED) 
548 {
549   msg (SN, _("DECIMAL is \"%c\"."), fmt_decimal_char (FMT_F));
550 }
551
552 static void
553 show_endcmd (const struct dataset *ds UNUSED) 
554 {
555   msg (SN, _("ENDCMD is \"%c\"."), get_endcmd ());
556 }
557
558 static void
559 show_format (const struct dataset *ds UNUSED) 
560 {
561   char str[FMT_STRING_LEN_MAX + 1];
562   msg (SN, _("FORMAT is %s."), fmt_to_string (get_format (), str));
563 }
564
565 static void
566 show_length (const struct dataset *ds UNUSED) 
567 {
568   msg (SN, _("LENGTH is %d."), get_viewlength ());
569 }
570
571 static void
572 show_mxerrs (const struct dataset *ds UNUSED) 
573 {
574   msg (SN, _("MXERRS is %d."), get_mxerrs ());
575 }
576
577 static void
578 show_mxloops (const struct dataset *ds UNUSED) 
579 {
580   msg (SN, _("MXLOOPS is %d."), get_mxloops ());
581 }
582
583 static void
584 show_mxwarns (const struct dataset *ds UNUSED) 
585 {
586   msg (SN, _("MXWARNS is %d."), get_mxwarns ());
587 }
588
589 static void
590 show_scompression (const struct dataset *ds UNUSED) 
591 {
592   if (get_scompression ())
593     msg (SN, _("SCOMPRESSION is ON."));
594   else
595     msg (SN, _("SCOMPRESSION is OFF."));
596 }
597
598 static void
599 show_undefined (const struct dataset *ds UNUSED) 
600 {
601   if (get_undefined ())
602     msg (SN, _("UNDEFINED is WARN."));
603   else
604     msg (SN, _("UNDEFINED is NOWARN."));
605 }
606
607 static void
608 show_weight (const struct dataset *ds) 
609 {
610   struct variable *var = dict_get_weight (dataset_dict (ds));
611   if (var == NULL)
612     msg (SN, _("WEIGHT is off."));
613   else
614     msg (SN, _("WEIGHT is variable %s."), var->name);
615 }
616
617 static void
618 show_width (const struct dataset *ds UNUSED) 
619 {
620   msg (SN, _("WIDTH is %d."), get_viewwidth ());
621 }
622
623 struct show_sbc 
624   {
625     const char *name;
626     void (*function) (const struct dataset *);
627   };
628
629 const struct show_sbc show_table[] = 
630   {
631     {"BLANKS", show_blanks},
632     {"CCA", show_cca},
633     {"CCB", show_ccb},
634     {"CCC", show_ccc},
635     {"CCD", show_ccd},
636     {"CCE", show_cce},
637     {"DECIMALS", show_decimals},
638     {"ENDCMD", show_endcmd},
639     {"FORMAT", show_format},
640     {"LENGTH", show_length},
641     {"MXERRS", show_mxerrs},
642     {"MXLOOPS", show_mxloops},
643     {"MXWARNS", show_mxwarns},
644     {"SCOMPRESSION", show_scompression},
645     {"UNDEFINED", show_undefined},
646     {"WEIGHT", show_weight},
647     {"WIDTH", show_width},
648   };
649
650 static void
651 show_all (const struct dataset *ds) 
652 {
653   size_t i;
654   
655   for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
656     show_table[i].function (ds);
657 }
658
659 static void
660 show_all_cc (void) 
661 {
662   int i;
663
664   for (i = 0; i < 5; i++)
665     show_cc (i);
666 }
667
668 static void
669 show_warranty (const struct dataset *ds UNUSED) 
670 {
671   msg (MN, lack_of_warranty);
672 }
673
674 static void
675 show_copying (const struct dataset *ds UNUSED) 
676 {
677   msg (MN, copyleft);
678 }
679
680 int
681 cmd_show (struct dataset *ds) 
682 {
683   if (token == '.') 
684     {
685       show_all (ds);
686       return CMD_SUCCESS;
687     }
688
689   do 
690     {
691       if (lex_match (T_ALL))
692         show_all (ds);
693       else if (lex_match_id ("CC")) 
694         show_all_cc ();
695       else if (lex_match_id ("WARRANTY"))
696         show_warranty (ds);
697       else if (lex_match_id ("COPYING"))
698         show_copying (ds);
699       else if (token == T_ID)
700         {
701           int i;
702
703           for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
704             if (lex_match_id (show_table[i].name)) 
705               {
706                 show_table[i].function (ds);
707                 goto found;
708               }
709           lex_error (NULL);
710           return CMD_FAILURE;
711
712         found: ;
713         }
714       else 
715         {
716           lex_error (NULL);
717           return CMD_FAILURE;
718         }
719
720       lex_match ('/');
721     }
722   while (token != '.');
723
724   return CMD_SUCCESS;
725 }
726
727 /*
728    Local Variables:
729    mode: c
730    End:
731 */