Apply patch #5225, assertions.
[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 struct string getl_buf;
102
103 static void close_source (void);
104
105 static void init_prompts (void);
106 static void uninit_prompts (void);
107 static enum getl_prompt_style get_prompt_style (void);
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   ds_init_empty (&getl_buf);
116   init_prompts ();
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_buf);
391   ds_destroy (&getl_include_path);
392   free(file_loc);
393   file_loc = NULL;
394   nfile_loc = mfile_loc = 0;
395   uninit_prompts ();
396 }
397
398
399 /* File locator stack functions. */
400
401 /* Pushes F onto the stack of file locations. */
402 void
403 msg_push_msg_locator (const struct msg_locator *loc)
404 {
405   if (nfile_loc >= mfile_loc)
406     {
407       if (mfile_loc == 0)
408         mfile_loc = 8;
409       else
410         mfile_loc *= 2;
411
412       file_loc = xnrealloc (file_loc, mfile_loc, sizeof *file_loc);
413     }
414
415   file_loc[nfile_loc++] = loc;
416 }
417
418 /* Pops F off the stack of file locations.
419    Argument F is only used for verification that that is actually the
420    item on top of the stack. */
421 void
422 msg_pop_msg_locator (const struct msg_locator *loc)
423 {
424   assert (nfile_loc >= 0 && file_loc[nfile_loc - 1] == loc);
425   nfile_loc--;
426 }
427
428 /* Puts the current file and line number into LOC, or NULL and -1 if
429    none. */
430 void
431 get_msg_location (struct msg_locator *loc)
432 {
433   if (nfile_loc)
434     *loc = *file_loc[nfile_loc - 1];
435   else
436     getl_location (&loc->file_name, &loc->line_number);
437 }
438
439 /* Reads a line from syntax file source S into LINE.
440    Returns true if successful, false at end of file. */
441 static bool
442 read_syntax_file (struct string *line, struct getl_source *s)
443 {
444   /* Open file, if not yet opened. */
445   if (s->u.syntax_file == NULL)
446     {
447       verbose_msg (1, _("opening \"%s\" as syntax file"), s->fn);
448       s->u.syntax_file = fn_open (s->fn, "r");
449
450       if (s->u.syntax_file == NULL)
451         {
452           msg (ME, _("Opening `%s': %s."), s->fn, strerror (errno));
453           return false;
454         }
455     }
456
457   /* Read line from file and remove new-line.
458      Skip initial "#! /usr/bin/pspp" line. */
459   do 
460     {
461       s->ln++;
462       if (!ds_read_line (line, s->u.syntax_file))
463         {
464           if (ferror (s->u.syntax_file))
465             msg (ME, _("Reading `%s': %s."), s->fn, strerror (errno));
466           return false;
467         }
468       ds_chomp (line, '\n');
469     }
470   while (s->ln == 1 && !memcmp (ds_cstr (line), "#!", 2));
471
472   /* Echo to listing file, if configured to do so. */
473   if (get_echo ())
474     tab_output_text (TAB_LEFT | TAB_FIX, ds_cstr (line));
475
476   return true;
477 }
478
479 /* Reads a line from source S into LINE.
480    Returns true if successful, false at end of file. */
481 static bool
482 read_line_from_source (struct string *line, struct getl_source *s)
483 {
484   ds_clear (line);
485   switch (s->type) 
486     {
487     case SYNTAX_FILE:
488       return read_syntax_file (line, s);
489     case FILTER:
490       return false;
491     case FUNCTION:
492       return s->u.function.read (line, &s->fn, &s->ln, s->u.function.aux);
493     case INTERACTIVE:
494       return s->u.interactive (line, get_prompt_style ());
495     }
496
497   NOT_REACHED ();
498 }
499
500 /* Reads a single line into LINE.
501    Returns true when a line has been read, false at end of input.
502    If INTERACTIVE is non-null, then when true is returned
503    *INTERACTIVE will be set to true if the line was obtained
504    interactively, false otherwise. */
505 static bool
506 do_read_line (struct string *line, bool *interactive)
507 {
508   while (cur_source != NULL)
509     {
510       struct getl_source *s = cur_source;
511       if (read_line_from_source (line, s))
512         {
513           if (interactive != NULL)
514             *interactive = s->type == INTERACTIVE;
515
516           while ((s = s->included_from) != NULL)
517             if (s->type == FILTER)
518               s->u.filter.filter (line, s->u.filter.aux);
519
520           return true;
521         }
522       close_source ();
523     }
524
525   return false;
526 }
527
528 /* Reads a single line into getl_buf.
529    Returns true when a line has been read, false at end of input.
530    If INTERACTIVE is non-null, then when true is returned
531    *INTERACTIVE will be set to true if the line was obtained
532    interactively, false otherwise. */
533 bool
534 getl_read_line (bool *interactive)
535 {
536   return do_read_line (&getl_buf, interactive);
537 }
538 \f
539 /* Current prompts in each style. */
540 static char *prompts[GETL_PROMPT_CNT];
541
542 /* Current prompting style. */
543 static enum getl_prompt_style current_style;
544
545 /* Initializes prompts. */
546 static void
547 init_prompts (void) 
548 {
549   prompts[GETL_PROMPT_FIRST] = xstrdup ("PSPP> ");
550   prompts[GETL_PROMPT_LATER] = xstrdup ("    > ");
551   prompts[GETL_PROMPT_DATA] = xstrdup ("data> ");
552   current_style = GETL_PROMPT_FIRST;
553 }
554
555 /* Frees prompts. */
556 static void
557 uninit_prompts (void) 
558 {
559   int i;
560
561   for (i = 0; i < GETL_PROMPT_CNT; i++) 
562     {
563       free (prompts[i]);
564       prompts[i] = NULL;
565     }
566 }
567
568 /* Gets the command prompt for the given STYLE. */
569 const char * 
570 getl_get_prompt (enum getl_prompt_style style)
571 {
572   assert (style < GETL_PROMPT_CNT);
573   return prompts[style];
574 }
575
576 /* Sets the given STYLE's prompt to STRING. */
577 void
578 getl_set_prompt (enum getl_prompt_style style, const char *string)
579 {
580   assert (style < GETL_PROMPT_CNT);
581   free (prompts[style]);
582   prompts[style] = xstrdup (string);
583 }
584
585 /* Sets STYLE as the current prompt style. */
586 void
587 getl_set_prompt_style (enum getl_prompt_style style) 
588 {
589   assert (style < GETL_PROMPT_CNT);
590   current_style = style;
591 }
592
593 /* Returns the current prompt. */
594 static enum getl_prompt_style
595 get_prompt_style (void) 
596 {
597   return current_style;
598 }