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