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