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