John's original code for patch #6210.
[pspp] / src / ui / terminal / main.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
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 <signal.h>
20 #include <stdio.h>
21
22 #include <ui/debugger.h>
23 #include "command-line.h"
24 #include "msg-ui.h"
25 #include "progname.h"
26 #include "read-line.h"
27
28 #include <data/dictionary.h>
29 #include <data/file-handle-def.h>
30 #include <libpspp/getl.h>
31 #include <data/file-name.h>
32 #include <data/format.h>
33 #include <data/procedure.h>
34 #include <data/settings.h>
35 #include <data/variable.h>
36 #include <gsl/gsl_errno.h>
37 #include <language/command.h>
38 #include <language/lexer/lexer.h>
39 #include <language/prompt.h>
40 #include <libpspp/compiler.h>
41 #include <libpspp/message.h>
42 #include <libpspp/version.h>
43 #include <math/random.h>
44 #include <output/output.h>
45
46 #if HAVE_FPU_CONTROL_H
47 #include <fpu_control.h>
48 #endif
49
50 #if HAVE_LOCALE_H
51 #include <locale.h>
52 #endif
53
54 #if HAVE_FENV_H
55 #include <fenv.h>
56 #endif
57
58 #if HAVE_IEEEFP_H
59 #include <ieeefp.h>
60 #endif
61
62 #include "gettext.h"
63 #define _(msgid) gettext (msgid)
64
65 #include <stdlib.h>
66
67 static void i18n_init (void);
68 static void fpu_init (void);
69 static void terminate (bool success) NO_RETURN;
70
71 /* If a segfault happens, issue a message to that effect and halt */
72 void bug_handler(int sig);
73
74 /* Handle quit/term/int signals */
75 void interrupt_handler(int sig);
76 static struct dataset * the_dataset = NULL;
77
78 static struct lexer *the_lexer;
79 static struct source_stream *the_source_stream ;
80
81 static int view_length = -1;
82 static int view_width = -1;
83
84 static void get_termcap_viewport (int);
85
86
87 /* Program entry point. */
88 int
89 main (int argc, char **argv)
90 {
91   signal (SIGABRT, bug_handler);
92   signal (SIGSEGV, bug_handler);
93   signal (SIGFPE, bug_handler);
94   signal (SIGINT, interrupt_handler);
95   signal (SIGWINCH, get_termcap_viewport);
96
97   set_program_name (argv[0]);
98
99   i18n_init ();
100   fpu_init ();
101   gsl_set_error_handler_off ();
102
103   fmt_init ();
104   outp_init ();
105   fn_init ();
106   fh_init ();
107   the_source_stream =
108     create_source_stream (
109                           fn_getenv_default ("STAT_INCLUDE_PATH", include_path)
110                           );
111   prompt_init ();
112   readln_initialize ();
113   get_termcap_viewport (0);
114   settings_init (&view_width, &view_length);
115   random_init ();
116
117   the_dataset = create_dataset ();
118
119   if (parse_command_line (argc, argv, the_source_stream))
120     {
121       msg_ui_init (the_source_stream);
122       if (!get_testing_mode ())
123         outp_read_devices ();
124       else
125         outp_configure_driver_line (
126           ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
127                    "output-file=\"pspp.list\" emphasis=none "
128                    "headers=off paginate=off squeeze=on "
129                    "top-margin=0 bottom-margin=0"));
130       the_lexer = lex_create (the_source_stream);
131
132       for (;;)
133         {
134           int result = cmd_parse (the_lexer, the_dataset);
135
136           if (result == CMD_EOF || result == CMD_FINISH)
137             break;
138           if (result == CMD_CASCADING_FAILURE &&
139               !getl_is_interactive (the_source_stream))
140             {
141               msg (SE, _("Stopping syntax file processing here to avoid "
142                          "a cascade of dependent command failures."));
143               getl_abort_noninteractive (the_source_stream);
144             }
145           else
146             check_msg_count (the_source_stream);
147         }
148     }
149
150   terminate (!any_errors ());
151 }
152 \f
153 static void
154 i18n_init (void)
155 {
156 #if ENABLE_NLS
157 #if HAVE_LC_MESSAGES
158   setlocale (LC_MESSAGES, "");
159 #endif
160 #if HAVE_LC_PAPER
161   setlocale (LC_PAPER, "");
162 #endif
163   bindtextdomain (PACKAGE, locale_dir);
164   textdomain (PACKAGE);
165 #endif /* ENABLE_NLS */
166 }
167
168 static void
169 fpu_init (void)
170 {
171 #if HAVE_FEHOLDEXCEPT
172   fenv_t foo;
173   feholdexcept (&foo);
174 #elif HAVE___SETFPUCW && defined(_FPU_IEEE)
175   __setfpucw (_FPU_IEEE);
176 #elif HAVE_FPSETMASK
177   fpsetmask (0);
178 #endif
179 }
180
181 /* If a segfault happens, issue a message to that effect and halt */
182 void
183 bug_handler(int sig)
184 {
185 #if DEBUGGING
186   connect_debugger ();
187 #endif
188   switch (sig)
189     {
190     case SIGABRT:
191       request_bug_report_and_abort("Assertion Failure/Abort");
192     case SIGFPE:
193       request_bug_report_and_abort("Floating Point Exception");
194     case SIGSEGV:
195       request_bug_report_and_abort("Segmentation Violation");
196     default:
197       request_bug_report_and_abort("Unknown");
198     }
199 }
200
201 void
202 interrupt_handler(int sig UNUSED)
203 {
204   terminate (false);
205 }
206
207
208 /* Terminate PSPP.  SUCCESS should be true to exit successfully,
209    false to exit as a failure.  */
210 static void
211 terminate (bool success)
212 {
213   static bool terminating = false;
214   if (!terminating)
215     {
216       terminating = true;
217
218       destroy_dataset (the_dataset);
219
220       random_done ();
221       settings_done ();
222       fh_done ();
223       lex_destroy (the_lexer);
224       destroy_source_stream (the_source_stream);
225       prompt_done ();
226       readln_uninitialize ();
227
228       outp_done ();
229       msg_ui_done ();
230       fmt_done ();
231     }
232   exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
233 }
234
235 \f
236
237 #include "error.h"
238
239 #include "gettext.h"
240 #define _(msgid) gettext (msgid)
241
242 static void
243 set_fallback_viewport (void)
244 {
245   if (view_width < 0 && getenv ("COLUMNS") != NULL)
246     view_width = atoi (getenv ("COLUMNS"));
247
248   if (view_length < 0 && getenv ("LINES") != NULL)
249     view_length = atoi (getenv ("LINES"));
250
251   if (view_width < 0)
252     view_width = 79;
253
254   if (view_length < 0)
255     view_length = 24;
256 }
257
258 /* Code that interfaces to ncurses.  This must be at the very end
259    of this file because curses.h redefines "bool" on some systems
260    (e.g. OpenBSD), causing declaration mismatches with functions
261    that have parameters or return values of type "bool". */
262 #if HAVE_LIBNCURSES
263 #include <curses.h>
264 #include <term.h>
265
266 static void
267 get_termcap_viewport (int sig UNUSED)
268 {
269   char term_buffer [16384];
270
271   if (getenv ("TERM") == NULL)
272     goto fallback;
273
274   else if (tgetent (term_buffer, getenv ("TERM")) <= 0)
275     {
276       error (0,0, _("could not access definition for terminal `%s'"),
277              getenv ("TERM"));
278       goto fallback;
279     }
280
281   if (tgetnum ("li") > 0)
282     view_length = tgetnum ("li");
283
284   if (tgetnum ("co") > 1)
285     view_width = tgetnum ("co") - 1;
286
287  fallback:
288   set_fallback_viewport ();
289 }
290
291 #else /* !HAVE_LIBNCURSES */
292
293 static void
294 get_termcap_viewport (int sig UNUSED)
295 {
296   set_fallback_viewport ();
297 }
298
299 #endif /* !HAVE_LIBNCURSES */
300
301