Eliminated global variable getl_buf
[pspp-builds.git] / src / language / line-buffer.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
22 #include <language/line-buffer.h>
23
24 #include <stdio.h>
25 #include <errno.h>
26 #include <stdlib.h>
27
28 #include <data/file-name.h>
29 #include <data/settings.h>
30 #include <data/variable.h>
31 #include <language/command.h>
32 #include <language/lexer/lexer.h>
33 #include <libpspp/alloc.h>
34 #include <libpspp/assertion.h>
35 #include <libpspp/message.h>
36 #include <libpspp/message.h>
37 #include <libpspp/str.h>
38 #include <libpspp/verbose-msg.h>
39 #include <libpspp/version.h>
40 #include <output/table.h>
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 /* Source file. */
46 struct getl_source
47   {
48     struct getl_source *included_from;  /* File that this is nested inside. */
49     struct getl_source *includes;       /* File nested inside this file. */
50     struct getl_source *next;           /* Next file in list. */
51
52     /* Current location. */
53     char *fn;                           /* File name. */
54     int ln;                             /* Line number. */
55
56     enum getl_source_type
57       {
58         SYNTAX_FILE,
59         FILTER,
60         FUNCTION,
61         INTERACTIVE
62       }
63     type;
64
65     union 
66       {
67         /* SYNTAX_FILE. */
68         FILE *syntax_file;
69
70         /* FILTER. */
71         struct 
72           {
73             void (*filter) (struct string *line, void *aux);
74             void (*close) (void *aux);
75             void *aux;
76           }
77         filter;
78
79         /* FUNCTION. */
80         struct 
81           {
82             bool (*read) (struct string *line, char **fn, int *ln, void *aux);
83             void (*close) (void *aux);
84             void *aux;
85           }
86         function;
87
88         /* INTERACTIVE. */
89         bool (*interactive) (struct string *line, enum getl_prompt_style);
90       }
91     u;
92
93   };
94
95 /* List of source files. */
96 static struct getl_source *cur_source;
97 static struct getl_source *last_source;
98
99 static struct string getl_include_path;
100
101
102 static void close_source (void);
103
104 static void init_prompts (void);
105 static void uninit_prompts (void);
106 static enum getl_prompt_style get_prompt_style (void);
107
108
109 /* Initialize getl. */
110 void
111 getl_initialize (void)
112 {
113   ds_init_cstr (&getl_include_path,
114                 fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
115   init_prompts ();
116 }
117
118
119 /* Delete everything from the include path. */
120 void
121 getl_clear_include_path (void)
122 {
123   ds_clear (&getl_include_path);
124 }
125
126 /* Add to the include path. */
127 void
128 getl_add_include_dir (const char *path)
129 {
130   if (ds_length (&getl_include_path))
131     ds_put_char (&getl_include_path, ':');
132
133   ds_put_cstr (&getl_include_path, path);
134 }
135
136 /* Appends source S to the list of source files. */
137 static void
138 append_source (struct getl_source *s) 
139 {
140   s->included_from = s->includes = s->next = NULL;
141   if (last_source == NULL)
142     cur_source = s;
143   else
144     last_source->next = s;
145   last_source = s;
146 }
147
148 /* Nests source S within the current source file. */
149 static void
150 include_source (struct getl_source *s) 
151 {
152   if (last_source == NULL)
153     append_source (s);
154   else 
155     {
156       s->included_from = cur_source;
157       s->includes = s->next = NULL;
158       s->next = NULL;
159       cur_source->includes = s;
160       cur_source = s;
161     }
162 }
163
164 /* Creates a source of the given TYPE.
165    Type-specific data must be initialized by the caller. */
166 static struct getl_source *
167 create_source (enum getl_source_type type) 
168 {
169   struct getl_source *s = xmalloc (sizeof *s);
170   s->fn = NULL;
171   s->ln = 0;
172   s->type = type;
173   return s;
174 }
175
176 /* Creates a syntax file source with file name FN. */
177 static struct getl_source *
178 create_syntax_file_source (const char *fn) 
179 {
180   struct getl_source *s = create_source (SYNTAX_FILE);
181   s->fn = xstrdup (fn);
182   s->u.syntax_file = NULL;
183   return s;
184 }
185
186 /* Creates a filter source with the given FILTER and CLOSE
187    functions that receive auxiliary data AUX. */
188 static struct getl_source *
189 create_filter_source (void (*filter) (struct string *, void *aux),
190                       void (*close) (void *aux),
191                       void *aux)
192 {
193   struct getl_source *s = create_source (FILTER);
194   s->u.filter.filter = filter;
195   s->u.filter.close = close;
196   s->u.filter.aux = aux;
197   return s;
198 }
199
200 /* Creates a function source with the given READ and CLOSE
201    functions that receive auxiliary data AUX. */
202 static struct getl_source *
203 create_function_source (bool (*read) (struct string *line,
204                                       char **fn, int *ln, void *aux),
205                         void (*close) (void *aux),
206                         void *aux)
207 {
208   struct getl_source *s = create_source (FUNCTION);
209   s->u.function.read = read;
210   s->u.function.close = close;
211   s->u.function.aux = aux;
212   return s;
213 }
214
215 /* Creates an interactive source with the given FUNCTION. */
216 static struct getl_source *
217 create_interactive_source (bool (*function) (struct string *line,
218                                              enum getl_prompt_style)) 
219 {
220   struct getl_source *s = xmalloc (sizeof *s);
221   s->fn = NULL;
222   s->ln = 0;
223   s->type = INTERACTIVE;
224   s->u.interactive = function;
225   return s;
226 }
227
228 /* Adds FN to the tail end of the list of source files to
229    execute. */
230 void
231 getl_append_syntax_file (const char *fn)
232 {
233   append_source (create_syntax_file_source (fn));
234 }
235
236 /* Inserts the given file with name FN into the current file
237    after the current line. */
238 void
239 getl_include_syntax_file (const char *fn)
240 {
241   if (cur_source != NULL) 
242     {
243       char *found_fn = fn_search_path (fn, ds_cstr (&getl_include_path),
244                                        fn_dir_name (cur_source->fn));
245       if (found_fn != NULL) 
246         {
247           include_source (create_syntax_file_source (found_fn));
248           free (found_fn); 
249         }
250       else
251         msg (SE, _("Can't find `%s' in include file search path."), fn);
252     }
253   else 
254     getl_append_syntax_file (fn); 
255 }
256
257 /* Inserts the given filter into the current file after the
258    current line.  Each line read while the filter is in place
259    will be passed through FILTER, which may modify it as
260    necessary.  When the filter is closed, CLOSE will be called.
261    AUX will be passed to both functions.
262
263    The filter cannot itself output any new lines, and it will be
264    closed as soon as any line would be read from it.  This means
265    that, for a filter to be useful, another source must be nested
266    inside it with, e.g., getl_include_syntax_file(). */
267 void
268 getl_include_filter (void (*filter) (struct string *, void *aux),
269                      void (*close) (void *aux),
270                      void *aux) 
271 {
272   include_source (create_filter_source (filter, close, aux));
273 }
274
275 /* Inserts the given functional source into the current file
276    after the current line.  Lines are read by calling READ, which
277    should write the next line in LINE, store the file name and
278    line number of the line in *FN and *LN, and return true.  The
279    string stored in *FN will not be freed by getl.  When no lines
280    are left, READ should return false.
281
282    When the source is closed, CLOSE will be called.
283
284    AUX will be passed to both READ and CLOSE. */
285 void
286 getl_include_function (bool (*read) (struct string *line,
287                                      char **fn, int *ln, void *aux),
288                        void (*close) (void *aux),
289                        void *aux) 
290 {
291   include_source (create_function_source (read, close, aux));
292 }
293
294 /* Adds an interactive source to the end of the list of sources.
295    FUNCTION will be called to obtain a line.  It should store the
296    line in LINE.  PROMPT is the prompt to be displayed to the
297    user.  FUNCTION should return true when a line has been
298    obtained or false at end of file. */
299 void
300 getl_append_interactive (bool (*function) (struct string *line,
301                                            enum getl_prompt_style)) 
302 {
303   append_source (create_interactive_source (function));
304 }
305
306 /* Closes all sources until an interactive source is
307    encountered. */
308 void
309 getl_abort_noninteractive (void) 
310 {
311   while (cur_source != NULL && cur_source->type != INTERACTIVE)
312     close_source ();
313 }
314
315 /* Returns true if the current source is interactive,
316    false otherwise. */
317 bool
318 getl_is_interactive (void) 
319 {
320   return cur_source != NULL && cur_source->type == INTERACTIVE;
321 }
322
323 /* Closes the current file, whether it be a main file or included
324    file, then moves cur_source to the next file in the chain. */
325 static void
326 close_source (void)
327 {
328   struct getl_source *s;
329
330   s = cur_source;
331   switch (s->type) 
332     {
333     case SYNTAX_FILE:
334       if (s->u.syntax_file && EOF == fn_close (s->fn, s->u.syntax_file))
335         msg (MW, _("Closing `%s': %s."), s->fn, strerror (errno));
336       free (s->fn);
337       break;
338
339     case FILTER:
340       if (s->u.filter.close != NULL)
341         s->u.filter.close (s->u.filter.aux);
342       break;
343
344     case FUNCTION:
345       if (s->u.function.close != NULL)
346         s->u.function.close (s->u.function.aux);
347       break;
348
349     case INTERACTIVE:
350       break;
351     }
352
353   if (s->included_from != NULL)
354     {
355       cur_source = s->included_from;
356       cur_source->includes = NULL;
357     }
358   else
359     {
360       cur_source = s->next;
361       if (cur_source == NULL)
362         last_source = NULL;
363     }
364
365   free (s);
366 }
367
368 /* Puts the current file and line number in *FN and *LN, respectively,
369    or NULL and -1 if none. */
370 void
371 getl_location (const char **fn, int *ln)
372 {
373   if (fn != NULL)
374     *fn = cur_source ? cur_source->fn : "";
375   if (ln != NULL)
376     *ln = cur_source ? cur_source->ln : -1;
377 }
378
379 /* File locator stack. */
380 static const struct msg_locator **file_loc;
381
382 static int nfile_loc, mfile_loc;
383 \f
384 /* Close getl. */
385 void
386 getl_uninitialize (void)
387 {
388   while (cur_source != NULL)
389     close_source ();
390   ds_destroy (&getl_include_path);
391   free(file_loc);
392   file_loc = NULL;
393   nfile_loc = mfile_loc = 0;
394   uninit_prompts ();
395 }
396
397
398 /* File locator stack functions. */
399
400 /* Pushes F onto the stack of file locations. */
401 void
402 msg_push_msg_locator (const struct msg_locator *loc)
403 {
404   if (nfile_loc >= mfile_loc)
405     {
406       if (mfile_loc == 0)
407         mfile_loc = 8;
408       else
409         mfile_loc *= 2;
410
411       file_loc = xnrealloc (file_loc, mfile_loc, sizeof *file_loc);
412     }
413
414   file_loc[nfile_loc++] = loc;
415 }
416
417 /* Pops F off the stack of file locations.
418    Argument F is only used for verification that that is actually the
419    item on top of the stack. */
420 void
421 msg_pop_msg_locator (const struct msg_locator *loc)
422 {
423   assert (nfile_loc >= 0 && file_loc[nfile_loc - 1] == loc);
424   nfile_loc--;
425 }
426
427 /* Puts the current file and line number into LOC, or NULL and -1 if
428    none. */
429 void
430 get_msg_location (struct msg_locator *loc)
431 {
432   if (nfile_loc)
433     *loc = *file_loc[nfile_loc - 1];
434   else
435     getl_location (&loc->file_name, &loc->line_number);
436 }
437
438 /* Reads a line from syntax file source S into LINE.
439    Returns true if successful, false at end of file. */
440 static bool
441 read_syntax_file (struct string *line, struct getl_source *s)
442 {
443   /* Open file, if not yet opened. */
444   if (s->u.syntax_file == NULL)
445     {
446       verbose_msg (1, _("opening \"%s\" as syntax file"), s->fn);
447       s->u.syntax_file = fn_open (s->fn, "r");
448
449       if (s->u.syntax_file == NULL)
450         {
451           msg (ME, _("Opening `%s': %s."), s->fn, strerror (errno));
452           return false;
453         }
454     }
455
456   /* Read line from file and remove new-line.
457      Skip initial "#! /usr/bin/pspp" line. */
458   do 
459     {
460       s->ln++;
461       if (!ds_read_line (line, s->u.syntax_file))
462         {
463           if (ferror (s->u.syntax_file))
464             msg (ME, _("Reading `%s': %s."), s->fn, strerror (errno));
465           return false;
466         }
467       ds_chomp (line, '\n');
468     }
469   while (s->ln == 1 && !memcmp (ds_cstr (line), "#!", 2));
470
471   /* Echo to listing file, if configured to do so. */
472   if (get_echo ())
473     tab_output_text (TAB_LEFT | TAB_FIX, ds_cstr (line));
474
475   return true;
476 }
477
478 /* Reads a line from source S into LINE.
479    Returns true if successful, false at end of file. */
480 static bool
481 read_line_from_source (struct string *line, struct getl_source *s)
482 {
483   ds_clear (line);
484   switch (s->type) 
485     {
486     case SYNTAX_FILE:
487       return read_syntax_file (line, s);
488     case FILTER:
489       return false;
490     case FUNCTION:
491       return s->u.function.read (line, &s->fn, &s->ln, s->u.function.aux);
492     case INTERACTIVE:
493       return s->u.interactive (line, get_prompt_style ());
494     }
495
496   NOT_REACHED ();
497 }
498
499 /* Reads a single line into LINE.
500    Returns true when a line has been read, false at end of input.
501    If INTERACTIVE is non-null, then when true is returned
502    *INTERACTIVE will be set to true if the line was obtained
503    interactively, false otherwise. */
504 bool
505 do_read_line (struct string *line, bool *interactive)
506 {
507   while (cur_source != NULL)
508     {
509       struct getl_source *s = cur_source;
510       if (read_line_from_source (line, s))
511         {
512           if (interactive != NULL)
513             *interactive = s->type == INTERACTIVE;
514
515           while ((s = s->included_from) != NULL)
516             if (s->type == FILTER)
517               s->u.filter.filter (line, s->u.filter.aux);
518
519           return true;
520         }
521       close_source ();
522     }
523
524   return false;
525 }
526
527 \f
528 /* Current prompts in each style. */
529 static char *prompts[GETL_PROMPT_CNT];
530
531 /* Current prompting style. */
532 static enum getl_prompt_style current_style;
533
534 /* Initializes prompts. */
535 static void
536 init_prompts (void) 
537 {
538   prompts[GETL_PROMPT_FIRST] = xstrdup ("PSPP> ");
539   prompts[GETL_PROMPT_LATER] = xstrdup ("    > ");
540   prompts[GETL_PROMPT_DATA] = xstrdup ("data> ");
541   current_style = GETL_PROMPT_FIRST;
542 }
543
544 /* Frees prompts. */
545 static void
546 uninit_prompts (void) 
547 {
548   int i;
549
550   for (i = 0; i < GETL_PROMPT_CNT; i++) 
551     {
552       free (prompts[i]);
553       prompts[i] = NULL;
554     }
555 }
556
557 /* Gets the command prompt for the given STYLE. */
558 const char * 
559 getl_get_prompt (enum getl_prompt_style style)
560 {
561   assert (style < GETL_PROMPT_CNT);
562   return prompts[style];
563 }
564
565 /* Sets the given STYLE's prompt to STRING. */
566 void
567 getl_set_prompt (enum getl_prompt_style style, const char *string)
568 {
569   assert (style < GETL_PROMPT_CNT);
570   free (prompts[style]);
571   prompts[style] = xstrdup (string);
572 }
573
574 /* Sets STYLE as the current prompt style. */
575 void
576 getl_set_prompt_style (enum getl_prompt_style style) 
577 {
578   assert (style < GETL_PROMPT_CNT);
579   current_style = style;
580 }
581
582 /* Returns the current prompt. */
583 static enum getl_prompt_style
584 get_prompt_style (void) 
585 {
586   return current_style;
587 }