Encapsulated msg_location inside msg_emit
[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, enum getl_prompt_style);
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 enum getl_prompt_style get_prompt_style (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                                              enum getl_prompt_style)) 
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                                            enum getl_prompt_style)) 
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
381 static int nfile_loc, mfile_loc;
382 \f
383 /* Close getl. */
384 void
385 getl_uninitialize (void)
386 {
387   while (cur_source != NULL)
388     close_source ();
389   ds_destroy (&getl_buf);
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   abort ();
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 static 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 /* Reads a single line into getl_buf.
528    Returns true when a line has been read, false at end of input.
529    If INTERACTIVE is non-null, then when true is returned
530    *INTERACTIVE will be set to true if the line was obtained
531    interactively, false otherwise. */
532 bool
533 getl_read_line (bool *interactive)
534 {
535   return do_read_line (&getl_buf, interactive);
536 }
537 \f
538 /* Current prompts in each style. */
539 static char *prompts[GETL_PROMPT_CNT];
540
541 /* Current prompting style. */
542 static enum getl_prompt_style current_style;
543
544 /* Initializes prompts. */
545 static void
546 init_prompts (void) 
547 {
548   prompts[GETL_PROMPT_FIRST] = xstrdup ("PSPP> ");
549   prompts[GETL_PROMPT_LATER] = xstrdup ("    > ");
550   prompts[GETL_PROMPT_DATA] = xstrdup ("data> ");
551   current_style = GETL_PROMPT_FIRST;
552 }
553
554 /* Frees prompts. */
555 static void
556 uninit_prompts (void) 
557 {
558   int i;
559
560   for (i = 0; i < GETL_PROMPT_CNT; i++) 
561     {
562       free (prompts[i]);
563       prompts[i] = NULL;
564     }
565 }
566
567 /* Gets the command prompt for the given STYLE. */
568 const char * 
569 getl_get_prompt (enum getl_prompt_style style)
570 {
571   assert (style < GETL_PROMPT_CNT);
572   return prompts[style];
573 }
574
575 /* Sets the given STYLE's prompt to STRING. */
576 void
577 getl_set_prompt (enum getl_prompt_style style, const char *string)
578 {
579   assert (style < GETL_PROMPT_CNT);
580   free (prompts[style]);
581   prompts[style] = xstrdup (string);
582 }
583
584 /* Sets STYLE as the current prompt style. */
585 void
586 getl_set_prompt_style (enum getl_prompt_style style) 
587 {
588   assert (style < GETL_PROMPT_CNT);
589   current_style = style;
590 }
591
592 /* Returns the current prompt. */
593 static enum getl_prompt_style
594 get_prompt_style (void) 
595 {
596   return current_style;
597 }