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