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