Plugged some memory leaks.
[pspp-builds.git] / src / getline.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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "getline.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "error.h"
28 #include "filename.h"
29 #include "lexer.h"
30 #include "repeat.h"
31 #include "settings.h"
32 #include "str.h"
33 #include "tab.h"
34 #include "var.h"
35 #include "version.h"
36
37 /* Global variables. */
38 struct string getl_buf;
39 struct getl_script *getl_head;
40 struct getl_script *getl_tail;
41 int getl_interactive;
42 int getl_welcomed;
43 int getl_mode;
44 int getl_prompt;
45
46 #if HAVE_LIBHISTORY
47 static char *history_file;
48
49 #if HAVE_READLINE_HISTORY_H
50 #include <readline/history.h>
51 #else /* no readline/history.h */
52 extern void add_history (char *);
53 extern void using_history (void);
54 extern int read_history (char *);
55 extern void stifle_history (int);
56 extern int write_history (char *);
57 #endif /* no readline/history.h */
58 #endif /* -lhistory */
59
60 extern struct cmd_set cmd;
61
62 static struct string getl_include_path;
63
64 /* Number of levels of DO REPEAT structures we're nested inside.  If
65    this is greater than zero then DO REPEAT macro substitutions are
66    performed. */
67 static int DO_REPEAT_level;
68
69 static int read_console (void);
70
71 /* Initialize getline. */
72 void
73 getl_initialize (void)
74 {
75   ds_create (&getl_include_path,
76              fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
77   ds_init (&getl_buf, 256);
78 }
79
80 /* Close getline. */
81 void
82 getl_uninitialize (void)
83 {
84 #if HAVE_LIBHISTORY && defined (unix)
85   if (history_file)
86     write_history (history_file);
87 #endif
88   ds_destroy (&getl_buf);
89   ds_destroy (&getl_include_path);
90 }
91
92 /* Returns a string that represents the directory that the syntax file
93    currently being read resides in.  If there is no syntax file then
94    returns the OS current working directory.  Return value must be
95    free()'d. */
96 char *
97 getl_get_current_directory (void)
98 {
99   return getl_head ? fn_dirname (getl_head->fn) : fn_get_cwd ();
100 }
101
102 /* Delete everything from the include path. */
103 void
104 getl_clear_include_path (void)
105 {
106   ds_clear (&getl_include_path);
107 }
108
109 /* Add to the include path. */
110 void
111 getl_add_include_dir (const char *path)
112 {
113   if (ds_length (&getl_include_path))
114     ds_putc (&getl_include_path, PATH_DELIMITER);
115
116   ds_puts (&getl_include_path, path);
117 }
118
119 /* Adds FN to the tail end of the list of script files to execute.
120    OPTIONS is the value to stick in the options field of the
121    getl_script struct.  If WHERE is zero then the file is added after
122    all other files; otherwise it is added before all other files (this
123    can be done only if parsing has not yet begun). */
124 void
125 getl_add_file (const char *fn, int separate, int where)
126 {
127   struct getl_script *n = xmalloc (sizeof *n);
128
129   assert (fn != NULL);
130   n->next = NULL;
131   if (getl_tail == NULL)
132     getl_head = getl_tail = n;
133   else if (!where)
134     getl_tail = getl_tail->next = n;
135   else
136     {
137       assert (getl_head->f == NULL);
138       n->next = getl_head;
139       getl_head = n;
140     }
141   n->included_from = n->includes = NULL;
142   n->fn = xstrdup (fn);
143   n->ln = 0;
144   n->f = NULL;
145   n->separate = separate;
146   n->first_line = NULL;
147 }
148
149 /* Inserts the given file with filename FN into the current file after
150    the current line. */
151 void
152 getl_include (const char *fn)
153 {
154   struct getl_script *n;
155   char *real_fn;
156
157   {
158     char *cur_dir = getl_get_current_directory ();
159     real_fn = fn_search_path (fn, ds_c_str (&getl_include_path), cur_dir);
160     free (cur_dir);
161   }
162
163   if (!real_fn)
164     {
165       msg (SE, _("Can't find `%s' in include file search path."), fn);
166       return;
167     }
168
169   if (!getl_head)
170     {
171       getl_add_file (real_fn, 0, 0);
172       free (real_fn);
173     }
174   else
175     {
176       n = xmalloc (sizeof *n);
177       n->included_from = getl_head;
178       getl_head = getl_head->includes = n;
179       n->includes = NULL;
180       n->next = NULL;
181       n->fn = real_fn;
182       n->ln = 0;
183       n->f = NULL;
184       n->separate = 0;
185       n->first_line = NULL;
186     }
187 }
188
189 /* Add the virtual file FILE to the list of files to be processed.
190    The first_line field in FILE must already have been initialized. */
191 void 
192 getl_add_virtual_file (struct getl_script *file)
193 {
194   if (getl_tail == NULL)
195     getl_head = getl_tail = file;
196   else
197     getl_tail = getl_tail->next = file;
198   file->included_from = file->includes = NULL;
199   file->next = NULL;
200   file->fn = file->first_line->line;
201   file->ln = -file->first_line->len - 1;
202   file->separate = 0;
203   file->f = NULL;
204   file->cur_line = NULL;
205   file->remaining_loops = 1;
206   file->loop_index = -1;
207   file->macros = NULL;
208 }
209
210 /* Causes the DO REPEAT virtual file passed in FILE to be included in
211    the current file.  The first_line, cur_line, remaining_loops,
212    loop_index, and macros fields in FILE must already have been
213    initialized. */
214 void
215 getl_add_DO_REPEAT_file (struct getl_script *file)
216 {
217   /* getl_head == NULL can't happen. */
218   assert (getl_head);
219
220   DO_REPEAT_level++;
221   file->included_from = getl_head;
222   getl_head = getl_head->includes = file;
223   file->includes = NULL;
224   file->next = NULL;
225   assert (file->first_line->len < 0);
226   file->fn = file->first_line->line;
227   file->ln = -file->first_line->len - 1;
228   file->separate = 0;
229   file->f = NULL;
230 }
231
232 /* Display a welcoming message. */
233 static void
234 welcome (void)
235 {
236   getl_welcomed = 1;
237   fputs ("PSPP is free software and you are welcome to distribute copies of "
238          "it\nunder certain conditions; type \"show copying.\" to see the "
239          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
240          "warranty.\" for details.\n", stdout);
241   puts (stat_version);
242 }
243
244 /* Reads a single line from the user's terminal. */
245
246 /* From repeat.c. */
247 extern void perform_DO_REPEAT_substitutions (void);
248   
249 /* Reads a single line from the line buffer associated with getl_head.
250    Returns 1 if a line was successfully read or 0 if no more lines are
251    available. */
252 static int
253 handle_line_buffer (void)
254 {
255   struct getl_script *s = getl_head;
256
257   /* Check that we're not all done. */
258   do
259     {
260       if (s->cur_line == NULL)
261         {
262           s->loop_index++;
263           if (s->remaining_loops-- == 0)
264             return 0;
265           s->cur_line = s->first_line;
266         }
267
268       if (s->cur_line->len < 0)
269         {
270           s->ln = -s->cur_line->len - 1;
271           s->fn = s->cur_line->line;
272           s->cur_line = s->cur_line->next;
273           continue;
274         }
275     }
276   while (s->cur_line == NULL);
277
278   ds_concat (&getl_buf, s->cur_line->line, s->cur_line->len);
279
280   /* Advance pointers. */
281   s->cur_line = s->cur_line->next;
282   s->ln++;
283
284   return 1;
285 }
286
287 /* Reads a single line into getl_buf from the list of files.  Will not
288    read from the eof of one file to the beginning of another unless
289    the options field on the new file's getl_script is nonzero.  Return
290    zero on eof. */
291 int
292 getl_read_line (void)
293 {
294   getl_mode = GETL_MODE_BATCH;
295   
296   while (getl_head)
297     {
298       struct getl_script *s = getl_head;
299
300       ds_clear (&getl_buf);
301       if (s->separate)
302         return 0;
303
304       if (s->first_line)
305         {
306           if (!handle_line_buffer ())
307             {
308               getl_close_file ();
309               continue;
310             }
311           perform_DO_REPEAT_substitutions ();
312           if (getl_head->print)
313             tab_output_text (TAB_LEFT | TAT_FIX | TAT_PRINTF, "+%s",
314                              ds_c_str (&getl_buf));
315           return 1;
316         }
317       
318       if (s->f == NULL)
319         {
320           msg (VM (1), _("%s: Opening as syntax file."), s->fn);
321           s->f = fn_open (s->fn, "r");
322
323           if (s->f == NULL)
324             {
325               msg (ME, _("Opening `%s': %s."), s->fn, strerror (errno));
326               getl_close_file ();
327               continue;
328             }
329         }
330
331       if (!ds_gets (&getl_buf, s->f))
332         {
333           if (ferror (s->f))
334             msg (ME, _("Reading `%s': %s."), s->fn, strerror (errno));
335           getl_close_file ();
336           continue;
337         }
338       if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == '\n')
339         ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
340
341       if (get_echo())
342         tab_output_text (TAB_LEFT | TAT_FIX, ds_c_str (&getl_buf));
343
344       getl_head->ln++;
345
346       /* Allows shebang invocation: `#! /usr/local/bin/pspp'. */
347       if (ds_c_str (&getl_buf)[0] == '#'
348           && ds_c_str (&getl_buf)[1] == '!')
349         continue;
350
351       return 1;
352     }
353
354   if (getl_interactive == 0)
355     return 0;
356
357   getl_mode = GETL_MODE_INTERACTIVE;
358   
359   if (getl_welcomed == 0)
360     welcome ();
361
362   return read_console ();
363 }
364
365 /* Closes the current file, whether it be a main file or included
366    file, then moves getl_head to the next file in the chain. */
367 void
368 getl_close_file (void)
369 {
370   struct getl_script *s = getl_head;
371
372   if (!s)
373     return;
374   assert (getl_tail != NULL);
375
376   if (s->first_line)
377     {
378       struct getl_line_list *cur, *next;
379
380       s->fn = NULL; /* It will be freed below. */
381       for (cur = s->first_line; cur; cur = next)
382         {
383           next = cur->next;
384           free (cur->line);
385           free (cur);
386         }
387
388       DO_REPEAT_level--;
389     }
390   
391   if (s->f && EOF == fn_close (s->fn, s->f))
392     msg (MW, _("Closing `%s': %s."), s->fn, strerror (errno));
393   free (s->fn);
394
395   if (s->included_from)
396     {
397       getl_head = s->included_from;
398       getl_head->includes = NULL;
399     }
400   else
401     {
402       getl_head = s->next;
403       if (NULL == getl_head)
404         getl_tail = NULL;
405     }
406   
407   free (s);
408 }
409
410 /* PORTME: Adapt to your local system's idea of the terminal. */
411 #if HAVE_LIBREADLINE
412
413 #if HAVE_READLINE_READLINE_H
414 #include <readline/readline.h>
415 #else /* no readline/readline.h */
416 extern char *readline (char *);
417 #endif /* no readline/readline.h */
418
419 static int
420 read_console (void)
421 {
422   char *line;
423   char *prompt;
424
425   err_error_count = err_warning_count = 0;
426   err_already_flagged = 0;
427
428 #if HAVE_LIBHISTORY
429   if (!history_file)
430     {
431 #ifdef unix
432       history_file = tilde_expand (HISTORY_FILE);
433 #endif
434       using_history ();
435       read_history (history_file);
436       stifle_history (MAX_HISTORY);
437     }
438 #endif /* -lhistory */
439
440   switch (getl_prompt)
441     {
442     case GETL_PRPT_STANDARD:
443       prompt = get_prompt();
444       break;
445
446     case GETL_PRPT_CONTINUATION:
447       prompt = get_cprompt();
448       break;
449
450     case GETL_PRPT_DATA:
451       prompt = get_dprompt();
452       break;
453
454     default:
455       assert (0);
456       abort ();
457     }
458
459   line = readline (prompt);
460   if (!line)
461     return 0;
462
463 #if HAVE_LIBHISTORY
464   if (*line)
465     add_history (line);
466 #endif
467
468   ds_clear (&getl_buf);
469   ds_puts (&getl_buf, line);
470
471   return 1;
472 }
473 #else /* no -lreadline */
474 static int
475 read_console (void)
476 {
477   err_error_count = err_warning_count = 0;
478   err_already_flagged = 0;
479
480   fputs (getl_prompt ? get_cprompt() : get_prompt(), stdout);
481   ds_clear (&getl_buf);
482   if (ds_gets (&getl_buf, stdin))
483     return 1;
484
485   if (ferror (stdin))
486     msg (FE, "stdin: fgets(): %s.", strerror (errno));
487
488   return 0;
489 }
490 #endif /* no -lreadline */
491
492 /* Closes all files. */
493 void
494 getl_close_all (void)
495 {
496   while (getl_head)
497     getl_close_file ();
498 }
499
500 /* Sets the options flag of the current script to 0, thus allowing it
501    to be read in.  Returns nonzero if this action was taken, zero
502    otherwise. */
503 int
504 getl_perform_delayed_reset (void)
505 {
506   if (getl_head && getl_head->separate)
507     {
508       getl_head->separate = 0;
509       discard_variables ();
510       lex_reset_eof ();
511       return 1;
512     }
513   return 0;
514 }
515
516 /* Puts the current file and line number in *FN and *LN, respectively,
517    or NULL and -1 if none. */
518 void
519 getl_location (const char **fn, int *ln)
520 {
521   if (fn != NULL)
522     *fn = getl_head ? getl_head->fn : NULL;
523   if (ln != NULL)
524     *ln = getl_head ? getl_head->ln : -1;
525 }