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