render: Fix bug measuring spanned cells.
[pspp] / src / ui / terminal / terminal-opts.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2010  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "terminal-opts.h"
20
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25
26 #include "data/settings.h"
27 #include "data/file-name.h"
28 #include "language/lexer/include-path.h"
29 #include "libpspp/argv-parser.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/cast.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/llx.h"
34 #include "libpspp/str.h"
35 #include "libpspp/string-array.h"
36 #include "libpspp/string-map.h"
37 #include "libpspp/string-set.h"
38 #include "libpspp/version.h"
39 #include "output/driver.h"
40 #include "output/driver-provider.h"
41 #include "output/msglog.h"
42
43 #include "gl/error.h"
44 #include "gl/localcharset.h"
45 #include "gl/progname.h"
46 #include "gl/version-etc.h"
47 #include "gl/xmemdup0.h"
48 #include "gl/xalloc.h"
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) msgid
53
54 struct terminal_opts
55   {
56     struct string_map options;  /* Output driver options. */
57     bool has_output_driver;
58     bool has_terminal_driver;
59     bool has_error_file;
60     enum lex_syntax_mode *syntax_mode;
61     bool *process_statrc;
62     char **syntax_encoding;
63   };
64
65 enum
66   {
67     OPT_TESTING_MODE,
68     OPT_ERROR_FILE,
69     OPT_OUTPUT,
70     OPT_OUTPUT_OPTION,
71     OPT_NO_OUTPUT,
72     OPT_BATCH,
73     OPT_INTERACTIVE,
74     OPT_SYNTAX_ENCODING,
75     OPT_NO_STATRC,
76     OPT_HELP,
77     OPT_VERSION,
78     N_TERMINAL_OPTIONS
79   };
80
81 static struct argv_option terminal_argv_options[N_TERMINAL_OPTIONS] =
82   {
83     {"testing-mode", 0, no_argument, OPT_TESTING_MODE},
84     {"error-file", 'e', required_argument, OPT_ERROR_FILE},
85     {"output", 'o', required_argument, OPT_OUTPUT},
86     {NULL, 'O', required_argument, OPT_OUTPUT_OPTION},
87     {"no-output", 0, no_argument, OPT_NO_OUTPUT},
88     {"batch", 'b', no_argument, OPT_BATCH},
89     {"interactive", 'i', no_argument, OPT_INTERACTIVE},
90     {"syntax-encoding", 0, required_argument, OPT_SYNTAX_ENCODING},
91     {"no-statrc", 'r', no_argument, OPT_NO_STATRC},
92     {"help", 'h', no_argument, OPT_HELP},
93     {"version", 'V', no_argument, OPT_VERSION},
94   };
95
96 static void
97 register_output_driver (struct terminal_opts *to)
98 {
99   if (!string_map_is_empty (&to->options))
100     {
101       struct output_driver *driver;
102
103       driver = output_driver_create (&to->options);
104       if (driver != NULL)
105         {
106           output_driver_register (driver);
107
108           to->has_output_driver = true;
109           if (driver->device_type == SETTINGS_DEVICE_TERMINAL)
110             to->has_terminal_driver = true;
111         }
112       string_map_clear (&to->options);
113     }
114 }
115
116 static void
117 parse_output_option (struct terminal_opts *to, const char *option)
118 {
119   const char *equals;
120   char *key, *value;
121
122   equals = strchr (option, '=');
123   if (equals == NULL)
124     {
125       error (0, 0, _("%s: output option missing `='"), option);
126       return;
127     }
128
129   key = xmemdup0 (option, equals - option);
130   if (string_map_contains (&to->options, key))
131     {
132       error (0, 0, _("%s: output option specified more than once"), key);
133       free (key);
134       return;
135     }
136
137   value = xmemdup0 (equals + 1, strlen (equals + 1));
138   string_map_insert_nocopy (&to->options, key, value);
139 }
140
141 static char *
142 get_supported_formats (void)
143 {
144   const struct string_set_node *node;
145   struct string_array format_array;
146   struct string_set format_set;
147   char *format_string;
148   const char *format;
149   size_t i;
150
151   /* Get supported formats as unordered set. */
152   string_set_init (&format_set);
153   output_get_supported_formats (&format_set);
154
155   /* Converted supported formats to sorted array. */
156   string_array_init (&format_array);
157   STRING_SET_FOR_EACH (format, node, &format_set)
158     string_array_append (&format_array, format);
159   string_array_sort (&format_array);
160   string_set_destroy (&format_set);
161
162   /* Converted supported formats to string. */
163   format_string = string_array_join (&format_array, " ");
164   string_array_destroy (&format_array);
165   return format_string;
166 }
167
168 static void
169 usage (void)
170 {
171   char *supported_formats = get_supported_formats ();
172   char *inc_path = string_array_join (include_path_default (), " ");
173
174   printf (_("\
175 PSPP, a program for statistical analysis of sample data.\n\
176 Usage: %s [OPTION]... FILE...\n\
177 \n\
178 Arguments to long options also apply to equivalent short options.\n\
179 \n\
180 Output options:\n\
181   -o, --output=FILE         output to FILE, default format from FILE's name\n\
182   -O format=FORMAT          override format for previous -o\n\
183   -O OPTION=VALUE           set output option to customize previous -o\n\
184   -O device={terminal|listing}  override device type for previous -o\n\
185   -e, --error-file=FILE     append errors, warnings, and notes to FILE\n\
186   --no-output               disable default output driver\n\
187 Supported output formats: %s\n\
188 \n\
189 Language options:\n\
190   -I, --include=DIR         append DIR to search path\n\
191   -I-, --no-include         clear search path\n\
192   -r, --no-statrc           disable running rc file at startup\n\
193   -a, --algorithm={compatible|enhanced}\n\
194                             set to `compatible' if you want output\n\
195                             calculated from broken algorithms\n\
196   -x, --syntax={compatible|enhanced}\n\
197                             set to `compatible' to disable PSPP extensions\n\
198   -b, --batch               interpret syntax in batch mode\n\
199   -i, --interactive         interpret syntax in interactive mode\n\
200   --syntax-encoding=ENCODING  specify encoding for syntax files\n\
201   -s, --safer               don't allow some unsafe operations\n\
202 Default search path: %s\n\
203 \n\
204 Informative output:\n\
205   -h, --help                display this help and exit\n\
206   -V, --version             output version information and exit\n\
207 \n\
208 Non-option arguments are interpreted as syntax files to execute.\n"),
209           program_name, supported_formats, inc_path);
210
211   free (supported_formats);
212   free (inc_path);
213
214   emit_bug_reporting_address ();
215   exit (EXIT_SUCCESS);
216 }
217
218 static void
219 terminal_option_callback (int id, void *to_)
220 {
221   struct terminal_opts *to = to_;
222
223   switch (id)
224     {
225     case OPT_TESTING_MODE:
226       settings_set_testing_mode (true);
227       break;
228
229     case OPT_ERROR_FILE:
230       if (!strcmp (optarg, "none") || msglog_create (optarg))
231         to->has_error_file = true;
232       break;
233
234     case OPT_OUTPUT:
235       register_output_driver (to);
236       string_map_insert (&to->options, "output-file", optarg);
237       break;
238
239     case OPT_OUTPUT_OPTION:
240       parse_output_option (to, optarg);
241       break;
242
243     case OPT_NO_OUTPUT:
244       /* Pretend that we already have an output driver, which disables adding
245          one in terminal_opts_done() when we don't already have one. */
246       to->has_output_driver = true;
247       break;
248
249     case OPT_BATCH:
250       *to->syntax_mode = LEX_SYNTAX_BATCH;
251       break;
252
253     case OPT_INTERACTIVE:
254       *to->syntax_mode = LEX_SYNTAX_INTERACTIVE;
255       break;
256
257     case OPT_SYNTAX_ENCODING:
258       *to->syntax_encoding = optarg;
259       break;
260
261     case OPT_NO_STATRC:
262       *to->process_statrc = false;
263       break;
264
265     case OPT_HELP:
266       usage ();
267       exit (EXIT_SUCCESS);
268
269     case OPT_VERSION:
270       version_etc (stdout, "pspp", PACKAGE_NAME, PACKAGE_VERSION,
271                    "Ben Pfaff", "John Darrington", "Jason Stover",
272                    NULL_SENTINEL);
273       exit (EXIT_SUCCESS);
274
275     default:
276       NOT_REACHED ();
277     }
278 }
279
280 struct terminal_opts *
281 terminal_opts_init (struct argv_parser *ap,
282                     enum lex_syntax_mode *syntax_mode, bool *process_statrc,
283                     char **syntax_encoding)
284 {
285   struct terminal_opts *to;
286
287   *syntax_mode = LEX_SYNTAX_AUTO;
288   *process_statrc = true;
289   *syntax_encoding = "Auto";
290
291   to = xzalloc (sizeof *to);
292   to->syntax_mode = syntax_mode;
293   string_map_init (&to->options);
294   to->has_output_driver = false;
295   to->has_error_file = false;
296   to->syntax_mode = syntax_mode;
297   to->process_statrc = process_statrc;
298   to->syntax_encoding = syntax_encoding;
299
300   argv_parser_add_options (ap, terminal_argv_options, N_TERMINAL_OPTIONS,
301                            terminal_option_callback, to);
302   return to;
303 }
304
305 /* Return true iff the terminal appears to be an xterm with 
306    UTF-8 capabilities */
307 static bool
308 term_is_utf8_xterm (void)
309 {
310   char *s = NULL;
311
312   if ( (s = getenv ("TERM")) && (0 == strcmp ("xterm", s)) )
313     if ( (s = getenv ("XTERM_LOCALE")) )
314       return strcasestr (s, "utf8") || strcasestr (s, "utf-8");
315
316   return false;
317 }
318
319 void
320 terminal_opts_done (struct terminal_opts *to, int argc, char *argv[])
321 {
322   register_output_driver (to);
323   if (!to->has_output_driver)
324     {
325       if ((0 == strcmp (locale_charset (), "UTF-8"))
326           ||
327           (term_is_utf8_xterm ()) )
328         {
329           string_map_insert (&to->options, "box", "unicode");
330         }
331   
332       string_map_insert (&to->options, "output-file", "-");
333       string_map_insert (&to->options, "format", "txt");
334       register_output_driver (to);
335     }
336
337   if (!to->has_terminal_driver && !to->has_error_file)
338     msglog_create ("-");
339
340   string_map_destroy (&to->options);
341   free (to);
342 }