Adopt use of gnulib for portability.
[pspp-builds.git] / src / getl.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 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 #include "getl.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "command.h"
28 #include "error.h"
29 #include "filename.h"
30 #include "lexer.h"
31 #include "repeat.h"
32 #include "settings.h"
33 #include "str.h"
34 #include "tab.h"
35 #include "var.h"
36 #include "version.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40
41 /* Global variables. */
42 struct string getl_buf;
43 struct getl_script *getl_head;
44 struct getl_script *getl_tail;
45 int getl_interactive;
46 int getl_welcomed;
47 int getl_mode;
48 int getl_prompt;
49
50 #if HAVE_LIBREADLINE
51 #include <readline/readline.h>
52 #endif
53
54 #if HAVE_LIBHISTORY
55 static char *history_file;
56
57 #if HAVE_READLINE_HISTORY_H
58 #include <readline/history.h>
59 #else /* no readline/history.h */
60 extern void add_history (char *);
61 extern void using_history (void);
62 extern int read_history (char *);
63 extern void stifle_history (int);
64 extern int write_history (char *);
65 #endif /* no readline/history.h */
66 #endif /* -lhistory */
67
68
69 extern struct cmd_set cmd;
70
71 static struct string getl_include_path;
72
73 /* Number of levels of DO REPEAT structures we're nested inside.  If
74    this is greater than zero then DO REPEAT macro substitutions are
75    performed. */
76 static int DO_REPEAT_level;
77
78 static int read_console (void);
79
80 /* Initialize getl. */
81 void
82 getl_initialize (void)
83 {
84   ds_create (&getl_include_path,
85              fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
86   ds_init (&getl_buf, 256);
87 #if HAVE_LIBREADLINE 
88   rl_completion_entry_function = pspp_completion_function;
89 #endif
90 }
91
92 /* Close getl. */
93 void
94 getl_uninitialize (void)
95 {
96   getl_close_all();
97 #if HAVE_LIBHISTORY && defined (unix)
98   if (history_file)
99     write_history (history_file);
100 #endif
101   ds_destroy (&getl_buf);
102   ds_destroy (&getl_include_path);
103 }
104
105 /* Returns a string that represents the directory that the syntax file
106    currently being read resides in.  If there is no syntax file then
107    returns the OS current working directory.  Return value must be
108    free()'d. */
109 char *
110 getl_get_current_directory (void)
111 {
112   return getl_head ? fn_dirname (getl_head->fn) : fn_get_cwd ();
113 }
114
115 /* Delete everything from the include path. */
116 void
117 getl_clear_include_path (void)
118 {
119   ds_clear (&getl_include_path);
120 }
121
122 /* Add to the include path. */
123 void
124 getl_add_include_dir (const char *path)
125 {
126   if (ds_length (&getl_include_path))
127     ds_putc (&getl_include_path, PATH_DELIMITER);
128
129   ds_puts (&getl_include_path, path);
130 }
131
132 /* Adds FN to the tail end of the list of script files to execute.
133    OPTIONS is the value to stick in the options field of the
134    getl_script struct.  If WHERE is zero then the file is added after
135    all other files; otherwise it is added before all other files (this
136    can be done only if parsing has not yet begun). */
137 void
138 getl_add_file (const char *fn, int separate, int where)
139 {
140   struct getl_script *n = xmalloc (sizeof *n);
141
142   assert (fn != NULL);
143   n->next = NULL;
144   if (getl_tail == NULL)
145     getl_head = getl_tail = n;
146   else if (!where)
147     getl_tail = getl_tail->next = n;
148   else
149     {
150       assert (getl_head->f == NULL);
151       n->next = getl_head;
152       getl_head = n;
153     }
154   n->included_from = n->includes = NULL;
155   n->fn = xstrdup (fn);
156   n->ln = 0;
157   n->f = NULL;
158   n->separate = separate;
159   n->first_line = NULL;
160 }
161
162 /* Inserts the given file with filename FN into the current file after
163    the current line. */
164 void
165 getl_include (const char *fn)
166 {
167   struct getl_script *n;
168   char *real_fn;
169
170   {
171     char *cur_dir = getl_get_current_directory ();
172     real_fn = fn_search_path (fn, ds_c_str (&getl_include_path), cur_dir);
173     free (cur_dir);
174   }
175
176   if (!real_fn)
177     {
178       msg (SE, _("Can't find `%s' in include file search path."), fn);
179       return;
180     }
181
182   if (!getl_head)
183     {
184       getl_add_file (real_fn, 0, 0);
185       free (real_fn);
186     }
187   else
188     {
189       n = xmalloc (sizeof *n);
190       n->included_from = getl_head;
191       getl_head = getl_head->includes = n;
192       n->includes = NULL;
193       n->next = NULL;
194       n->fn = real_fn;
195       n->ln = 0;
196       n->f = NULL;
197       n->separate = 0;
198       n->first_line = NULL;
199     }
200 }
201
202 /* Add the virtual file FILE to the list of files to be processed.
203    The first_line field in FILE must already have been initialized. */
204 void 
205 getl_add_virtual_file (struct getl_script *file)
206 {
207   if (getl_tail == NULL)
208     getl_head = getl_tail = file;
209   else
210     getl_tail = getl_tail->next = file;
211   file->included_from = file->includes = NULL;
212   file->next = NULL;
213   file->fn = file->first_line->line;
214   file->ln = -file->first_line->len - 1;
215   file->separate = 0;
216   file->f = NULL;
217   file->cur_line = NULL;
218   file->remaining_loops = 1;
219   file->loop_index = -1;
220   file->macros = NULL;
221 }
222
223 /* Causes the DO REPEAT virtual file passed in FILE to be included in
224    the current file.  The first_line, cur_line, remaining_loops,
225    loop_index, and macros fields in FILE must already have been
226    initialized. */
227 void
228 getl_add_DO_REPEAT_file (struct getl_script *file)
229 {
230   /* getl_head == NULL can't happen. */
231   assert (getl_head);
232
233   DO_REPEAT_level++;
234   file->included_from = getl_head;
235   getl_head = getl_head->includes = file;
236   file->includes = NULL;
237   file->next = NULL;
238   assert (file->first_line->len < 0);
239   file->fn = file->first_line->line;
240   file->ln = -file->first_line->len - 1;
241   file->separate = 0;
242   file->f = NULL;
243 }
244
245 /* Display a welcoming message. */
246 static void
247 welcome (void)
248 {
249   getl_welcomed = 1;
250   fputs ("PSPP is free software and you are welcome to distribute copies of "
251          "it\nunder certain conditions; type \"show copying.\" to see the "
252          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
253          "warranty.\" for details.\n", stdout);
254   puts (stat_version);
255 }
256
257 /* Reads a single line from the user's terminal. */
258
259 /* From repeat.c. */
260 extern void perform_DO_REPEAT_substitutions (void);
261   
262 /* Reads a single line from the line buffer associated with getl_head.
263    Returns 1 if a line was successfully read or 0 if no more lines are
264    available. */
265 static int
266 handle_line_buffer (void)
267 {
268   struct getl_script *s = getl_head;
269
270   /* Check that we're not all done. */
271   do
272     {
273       if (s->cur_line == NULL)
274         {
275           s->loop_index++;
276           if (s->remaining_loops-- == 0)
277             return 0;
278           s->cur_line = s->first_line;
279         }
280
281       if (s->cur_line->len < 0)
282         {
283           s->ln = -s->cur_line->len - 1;
284           s->fn = s->cur_line->line;
285           s->cur_line = s->cur_line->next;
286           continue;
287         }
288     }
289   while (s->cur_line == NULL);
290
291   ds_concat (&getl_buf, s->cur_line->line, s->cur_line->len);
292
293   /* Advance pointers. */
294   s->cur_line = s->cur_line->next;
295   s->ln++;
296
297   return 1;
298 }
299
300 /* Reads a single line into getl_buf from the list of files.  Will not
301    read from the eof of one file to the beginning of another unless
302    the options field on the new file's getl_script is nonzero.  Return
303    zero on eof. */
304 int
305 getl_read_line (void)
306 {
307   getl_mode = GETL_MODE_BATCH;
308   
309   while (getl_head)
310     {
311       struct getl_script *s = getl_head;
312
313       ds_clear (&getl_buf);
314       if (s->separate)
315         return 0;
316
317       if (s->first_line)
318         {
319           if (!handle_line_buffer ())
320             {
321               getl_close_file ();
322               continue;
323             }
324           perform_DO_REPEAT_substitutions ();
325           if (getl_head->print)
326             tab_output_text (TAB_LEFT | TAT_FIX | TAT_PRINTF, "+%s",
327                              ds_c_str (&getl_buf));
328           return 1;
329         }
330       
331       if (s->f == NULL)
332         {
333           msg (VM (1), _("%s: Opening as syntax file."), s->fn);
334           s->f = fn_open (s->fn, "r");
335
336           if (s->f == NULL)
337             {
338               msg (ME, _("Opening `%s': %s."), s->fn, strerror (errno));
339               getl_close_file ();
340               continue;
341             }
342         }
343
344       if (!ds_gets (&getl_buf, s->f))
345         {
346           if (ferror (s->f))
347             msg (ME, _("Reading `%s': %s."), s->fn, strerror (errno));
348           getl_close_file ();
349           continue;
350         }
351       if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == '\n')
352         ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
353
354       if (get_echo())
355         tab_output_text (TAB_LEFT | TAT_FIX, ds_c_str (&getl_buf));
356
357       getl_head->ln++;
358
359       /* Allows shebang invocation: `#! /usr/local/bin/pspp'. */
360       if (ds_c_str (&getl_buf)[0] == '#'
361           && ds_c_str (&getl_buf)[1] == '!')
362         continue;
363
364       return 1;
365     }
366
367   if (getl_interactive == 0)
368     return 0;
369
370   getl_mode = GETL_MODE_INTERACTIVE;
371   
372   if (getl_welcomed == 0)
373     welcome ();
374
375   return read_console ();
376 }
377
378 /* Closes the current file, whether it be a main file or included
379    file, then moves getl_head to the next file in the chain. */
380 void
381 getl_close_file (void)
382 {
383   struct getl_script *s = getl_head;
384
385   if (!s)
386     return;
387   assert (getl_tail != NULL);
388
389   if (s->first_line)
390     {
391       struct getl_line_list *cur, *next;
392
393       s->fn = NULL; /* It will be freed below. */
394       for (cur = s->first_line; cur; cur = next)
395         {
396           next = cur->next;
397           free (cur->line);
398           free (cur);
399         }
400
401       DO_REPEAT_level--;
402     }
403   
404   if (s->f && EOF == fn_close (s->fn, s->f))
405     msg (MW, _("Closing `%s': %s."), s->fn, strerror (errno));
406   free (s->fn);
407
408   if (s->included_from)
409     {
410       getl_head = s->included_from;
411       getl_head->includes = NULL;
412     }
413   else
414     {
415       getl_head = s->next;
416       if (NULL == getl_head)
417         getl_tail = NULL;
418     }
419   
420   free (s);
421 }
422
423 /* PORTME: Adapt to your local system's idea of the terminal. */
424 #if HAVE_LIBREADLINE
425
426 #if HAVE_READLINE_READLINE_H
427 #include <readline/readline.h>
428 #else /* no readline/readline.h */
429 extern char *readline (char *);
430 #endif /* no readline/readline.h */
431
432 static int
433 read_console (void)
434 {
435   char *line;
436   char *prompt;
437
438   err_error_count = err_warning_count = 0;
439   err_already_flagged = 0;
440
441 #if HAVE_LIBHISTORY
442   if (!history_file)
443     {
444 #ifdef unix
445       history_file = tilde_expand (HISTORY_FILE);
446 #endif
447       using_history ();
448       read_history (history_file);
449       stifle_history (MAX_HISTORY);
450     }
451 #endif /* -lhistory */
452
453   switch (getl_prompt)
454     {
455     case GETL_PRPT_STANDARD:
456       prompt = get_prompt();
457       break;
458
459     case GETL_PRPT_CONTINUATION:
460       prompt = get_cprompt();
461       break;
462
463     case GETL_PRPT_DATA:
464       prompt = get_dprompt();
465       break;
466
467     default:
468       assert (0);
469       abort ();
470     }
471
472   line = readline (prompt);
473   if (!line)
474     return 0;
475
476 #if HAVE_LIBHISTORY
477   if (*line)
478     add_history (line);
479 #endif
480
481   ds_clear (&getl_buf);
482   ds_puts (&getl_buf, line);
483
484   free (line);
485
486   return 1;
487 }
488 #else /* no -lreadline */
489 static int
490 read_console (void)
491 {
492   err_error_count = err_warning_count = 0;
493   err_already_flagged = 0;
494
495   fputs (getl_prompt ? get_cprompt() : get_prompt(), stdout);
496   ds_clear (&getl_buf);
497   if (ds_gets (&getl_buf, stdin))
498     return 1;
499
500   if (ferror (stdin))
501     msg (FE, "stdin: fgets(): %s.", strerror (errno));
502
503   return 0;
504 }
505 #endif /* no -lreadline */
506
507 /* Closes all files. */
508 void
509 getl_close_all (void)
510 {
511   while (getl_head)
512     getl_close_file ();
513 }
514
515 /* Sets the options flag of the current script to 0, thus allowing it
516    to be read in.  Returns nonzero if this action was taken, zero
517    otherwise. */
518 int
519 getl_perform_delayed_reset (void)
520 {
521   if (getl_head && getl_head->separate)
522     {
523       getl_head->separate = 0;
524       discard_variables ();
525       lex_reset_eof ();
526       return 1;
527     }
528   return 0;
529 }
530
531 /* Puts the current file and line number in *FN and *LN, respectively,
532    or NULL and -1 if none. */
533 void
534 getl_location (const char **fn, int *ln)
535 {
536   if (fn != NULL)
537     *fn = getl_head ? getl_head->fn : NULL;
538   if (ln != NULL)
539     *ln = getl_head ? getl_head->ln : -1;
540 }