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