Fixed typo in comment
[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   size_t i;
149
150   /* Get supported formats as unordered set. */
151   string_set_init (&format_set);
152   output_get_supported_formats (&format_set);
153
154   /* Converted supported formats to sorted array. */
155   string_array_init (&format_array);
156   STRING_SET_FOR_EACH (format, node, &format_set)
157     string_array_append (&format_array, format);
158   string_array_sort (&format_array);
159   string_set_destroy (&format_set);
160
161   /* Converted supported formats to string. */
162   format_string = string_array_join (&format_array, " ");
163   string_array_destroy (&format_array);
164   return format_string;
165 }
166
167 static void
168 usage (void)
169 {
170   char *supported_formats = get_supported_formats ();
171   char *inc_path = string_array_join (include_path_default (), " ");
172
173   printf (_("\
174 PSPP, a program for statistical analysis of sampled data.\n\
175 Usage: %s [OPTION]... FILE...\n\
176 \n\
177 Arguments to long options also apply to equivalent short options.\n\
178 \n\
179 Output options:\n\
180   -o, --output=FILE         output to FILE, default format from FILE's name\n\
181   -O format=FORMAT          override format for previous -o\n\
182   -O OPTION=VALUE           set output option to customize previous -o\n\
183   -O device={terminal|listing}  override device type for previous -o\n\
184   -e, --error-file=FILE     append errors, warnings, and notes to FILE\n\
185   --no-output               disable default output driver\n\
186 Supported output formats: %s\n\
187 \n\
188 Language options:\n\
189   -I, --include=DIR         append DIR to search path\n\
190   -I-, --no-include         clear search path\n\
191   -r, --no-statrc           disable running rc file at startup\n\
192   -a, --algorithm={compatible|enhanced}\n\
193                             set to `compatible' if you want output\n\
194                             calculated from broken algorithms\n\
195   -x, --syntax={compatible|enhanced}\n\
196                             set to `compatible' to disable PSPP extensions\n\
197   -b, --batch               interpret syntax in batch mode\n\
198   -i, --interactive         interpret syntax in interactive mode\n\
199   --syntax-encoding=ENCODING  specify encoding for syntax files\n\
200   -s, --safer               don't allow some unsafe operations\n\
201 Default search path: %s\n\
202 \n\
203 Informative output:\n\
204   -h, --help                display this help and exit\n\
205   -V, --version             output version information and exit\n\
206 \n\
207 Non-option arguments are interpreted as syntax files to execute.\n"),
208           program_name, supported_formats, inc_path);
209
210   free (supported_formats);
211   free (inc_path);
212
213   emit_bug_reporting_address ();
214   exit (EXIT_SUCCESS);
215 }
216
217 static void
218 terminal_option_callback (int id, void *to_)
219 {
220   struct terminal_opts *to = to_;
221
222   switch (id)
223     {
224     case OPT_TESTING_MODE:
225       settings_set_testing_mode (true);
226       break;
227
228     case OPT_ERROR_FILE:
229       if (!strcmp (optarg, "none") || msglog_create (optarg))
230         to->has_error_file = true;
231       break;
232
233     case OPT_OUTPUT:
234       register_output_driver (to);
235       string_map_insert (&to->options, "output-file", optarg);
236       break;
237
238     case OPT_OUTPUT_OPTION:
239       parse_output_option (to, optarg);
240       break;
241
242     case OPT_NO_OUTPUT:
243       /* Pretend that we already have an output driver, which disables adding
244          one in terminal_opts_done() when we don't already have one. */
245       to->has_output_driver = true;
246       break;
247
248     case OPT_BATCH:
249       *to->syntax_mode = LEX_SYNTAX_BATCH;
250       break;
251
252     case OPT_INTERACTIVE:
253       *to->syntax_mode = LEX_SYNTAX_INTERACTIVE;
254       break;
255
256     case OPT_SYNTAX_ENCODING:
257       *to->syntax_encoding = optarg;
258       break;
259
260     case OPT_NO_STATRC:
261       *to->process_statrc = false;
262       break;
263
264     case OPT_HELP:
265       usage ();
266       exit (EXIT_SUCCESS);
267
268     case OPT_VERSION:
269       version_etc (stdout, "pspp", PACKAGE_NAME, PACKAGE_VERSION,
270                    "Ben Pfaff", "John Darrington", "Jason Stover",
271                    NULL_SENTINEL);
272       exit (EXIT_SUCCESS);
273
274     default:
275       NOT_REACHED ();
276     }
277 }
278
279 struct terminal_opts *
280 terminal_opts_init (struct argv_parser *ap,
281                     enum lex_syntax_mode *syntax_mode, bool *process_statrc,
282                     char **syntax_encoding)
283 {
284   struct terminal_opts *to;
285
286   *syntax_mode = LEX_SYNTAX_AUTO;
287   *process_statrc = true;
288   *syntax_encoding = "Auto";
289
290   to = xzalloc (sizeof *to);
291   to->syntax_mode = syntax_mode;
292   string_map_init (&to->options);
293   to->has_output_driver = false;
294   to->has_error_file = false;
295   to->syntax_mode = syntax_mode;
296   to->process_statrc = process_statrc;
297   to->syntax_encoding = syntax_encoding;
298
299   argv_parser_add_options (ap, terminal_argv_options, N_TERMINAL_OPTIONS,
300                            terminal_option_callback, to);
301   return to;
302 }
303
304 /* Return true iff the terminal appears to be an xterm with 
305    UTF-8 capabilities */
306 static bool
307 term_is_utf8_xterm (void)
308 {
309   char *s = NULL;
310
311   if ( (s = getenv ("TERM")) && (0 == strcmp ("xterm", s)) )
312     if ( (s = getenv ("XTERM_LOCALE")) )
313       return strcasestr (s, "utf8") || strcasestr (s, "utf-8");
314
315   return false;
316 }
317
318 void
319 terminal_opts_done (struct terminal_opts *to, int argc, char *argv[])
320 {
321   register_output_driver (to);
322   if (!to->has_output_driver)
323     {
324       if ((0 == strcmp (locale_charset (), "UTF-8"))
325           ||
326           (term_is_utf8_xterm ()) )
327         {
328           string_map_insert (&to->options, "box", "unicode");
329         }
330   
331       string_map_insert (&to->options, "output-file", "-");
332       string_map_insert (&to->options, "format", "txt");
333       register_output_driver (to);
334     }
335
336   if (!to->has_terminal_driver && !to->has_error_file)
337     msglog_create ("-");
338
339   string_map_destroy (&to->options);
340   free (to);
341 }