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