*** empty log message ***
[pspp] / src / getl.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 "getl.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "command.h"
28 #include "error.h"
29 #include "filename.h"
30 #include "lexer.h"
31 #include "repeat.h"
32 #include "settings.h"
33 #include "str.h"
34 #include "tab.h"
35 #include "var.h"
36 #include "version.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40
41 static struct string getl_include_path;
42
43 /* Number of levels of DO REPEAT structures we're nested inside.  If
44    this is greater than zero then DO REPEAT macro substitutions are
45    performed. */
46 static int DO_REPEAT_level;
47
48 struct string getl_buf;
49
50
51 /* Initialize getl. */
52 void
53 getl_initialize (void)
54 {
55   ds_create (&getl_include_path,
56              fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
57   ds_init (&getl_buf, 256);
58 }
59
60 /* Close getl. */
61 void
62 getl_uninitialize (void)
63 {
64   getl_close_all();
65   ds_destroy (&getl_buf);
66   ds_destroy (&getl_include_path);
67 }
68
69
70 struct getl_script *getl_head;
71 struct getl_script *getl_tail;
72
73
74 /* Returns a string that represents the directory that the syntax file
75    currently being read resides in.  If there is no syntax file then
76    returns the OS current working directory.  Return value must be
77    free()'d. */
78 char *
79 getl_get_current_directory (void)
80 {
81   return getl_head ? fn_dirname (getl_head->fn) : fn_get_cwd ();
82 }
83
84 /* Delete everything from the include path. */
85 void
86 getl_clear_include_path (void)
87 {
88   ds_clear (&getl_include_path);
89 }
90
91 /* Add to the include path. */
92 void
93 getl_add_include_dir (const char *path)
94 {
95   if (ds_length (&getl_include_path))
96     ds_putc (&getl_include_path, PATH_DELIMITER);
97
98   ds_puts (&getl_include_path, path);
99 }
100
101 /* Adds FN to the tail end of the list of script files to execute.
102    OPTIONS is the value to stick in the options field of the
103    getl_script struct.  If WHERE is zero then the file is added after
104    all other files; otherwise it is added before all other files (this
105    can be done only if parsing has not yet begun). */
106 void
107 getl_add_file (const char *fn, int separate, int where)
108 {
109   struct getl_script *n = xmalloc (sizeof *n);
110
111   assert (fn != NULL);
112   n->next = NULL;
113   if (getl_tail == NULL)
114     getl_head = getl_tail = n;
115   else if (!where)
116     getl_tail = getl_tail->next = n;
117   else
118     {
119       assert (getl_head->f == NULL);
120       n->next = getl_head;
121       getl_head = n;
122     }
123   n->included_from = n->includes = NULL;
124   n->fn = xstrdup (fn);
125   n->ln = 0;
126   n->f = NULL;
127   n->separate = separate;
128   n->first_line = NULL;
129 }
130
131 /* Inserts the given file with filename FN into the current file after
132    the current line. */
133 void
134 getl_include (const char *fn)
135 {
136   struct getl_script *n;
137   char *real_fn;
138
139   {
140     char *cur_dir = getl_get_current_directory ();
141     real_fn = fn_search_path (fn, ds_c_str (&getl_include_path), cur_dir);
142     free (cur_dir);
143   }
144
145   if (!real_fn)
146     {
147       msg (SE, _("Can't find `%s' in include file search path."), fn);
148       return;
149     }
150
151   if (!getl_head)
152     {
153       getl_add_file (real_fn, 0, 0);
154       free (real_fn);
155     }
156   else
157     {
158       n = xmalloc (sizeof *n);
159       n->included_from = getl_head;
160       getl_head = getl_head->includes = n;
161       n->includes = NULL;
162       n->next = NULL;
163       n->fn = real_fn;
164       n->ln = 0;
165       n->f = NULL;
166       n->separate = 0;
167       n->first_line = NULL;
168     }
169 }
170
171 /* Add the virtual file FILE to the list of files to be processed.
172    The first_line field in FILE must already have been initialized. */
173 void 
174 getl_add_virtual_file (struct getl_script *file)
175 {
176   if (getl_tail == NULL)
177     getl_head = getl_tail = file;
178   else
179     getl_tail = getl_tail->next = file;
180   file->included_from = file->includes = NULL;
181   file->next = NULL;
182   file->fn = file->first_line->line;
183   file->ln = -file->first_line->len - 1;
184   file->separate = 0;
185   file->f = NULL;
186   file->cur_line = NULL;
187   file->remaining_loops = 1;
188   file->loop_index = -1;
189   file->macros = NULL;
190 }
191
192 /* Causes the DO REPEAT virtual file passed in FILE to be included in
193    the current file.  The first_line, cur_line, remaining_loops,
194    loop_index, and macros fields in FILE must already have been
195    initialized. */
196 void
197 getl_add_DO_REPEAT_file (struct getl_script *file)
198 {
199   assert (getl_head);
200
201   DO_REPEAT_level++;
202   file->included_from = getl_head;
203   getl_head = getl_head->includes = file;
204   file->includes = NULL;
205   file->next = NULL;
206   assert (file->first_line->len < 0);
207   file->fn = file->first_line->line;
208   file->ln = -file->first_line->len - 1;
209   file->separate = 0;
210   file->f = NULL;
211 }
212
213 /* Reads a single line from the line buffer associated with getl_head.
214    Returns 1 if a line was successfully read or 0 if no more lines are
215    available. */
216 int
217 getl_handle_line_buffer (void)
218 {
219   struct getl_script *s = getl_head;
220
221   /* Check that we're not all done. */
222   do
223     {
224       if (s->cur_line == NULL)
225         {
226           s->loop_index++;
227           if (s->remaining_loops-- == 0)
228             return 0;
229           s->cur_line = s->first_line;
230         }
231
232       if (s->cur_line->len < 0)
233         {
234           s->ln = -s->cur_line->len - 1;
235           s->fn = s->cur_line->line;
236           s->cur_line = s->cur_line->next;
237           continue;
238         }
239     }
240   while (s->cur_line == NULL);
241
242   ds_concat (&getl_buf, s->cur_line->line, s->cur_line->len);
243
244   /* Advance pointers. */
245   s->cur_line = s->cur_line->next;
246   s->ln++;
247
248   return 1;
249 }
250
251 /* Closes the current file, whether it be a main file or included
252    file, then moves getl_head to the next file in the chain. */
253 void
254 getl_close_file (void)
255 {
256   struct getl_script *s = getl_head;
257
258   if (!s)
259     return;
260   assert (getl_tail != NULL);
261
262   if (s->first_line)
263     {
264       struct getl_line_list *cur, *next;
265
266       s->fn = NULL; /* It will be freed below. */
267       for (cur = s->first_line; cur; cur = next)
268         {
269           next = cur->next;
270           free (cur->line);
271           free (cur);
272         }
273
274       DO_REPEAT_level--;
275     }
276   
277   if (s->f && EOF == fn_close (s->fn, s->f))
278     msg (MW, _("Closing `%s': %s."), s->fn, strerror (errno));
279   free (s->fn);
280
281   if (s->included_from)
282     {
283       getl_head = s->included_from;
284       getl_head->includes = NULL;
285     }
286   else
287     {
288       getl_head = s->next;
289       if (NULL == getl_head)
290         getl_tail = NULL;
291     }
292   
293   free (s);
294 }
295
296 /* Closes all files. */
297 void
298 getl_close_all (void)
299 {
300   while (getl_head)
301     getl_close_file ();
302 }
303
304 bool
305 getl_is_separate(void)
306 {
307   return (getl_head && getl_head->separate);
308 }
309
310 void
311 getl_set_separate(bool sep)
312 {
313   assert (getl_head);
314
315   getl_head->separate = sep ;
316 }
317
318
319 /* Puts the current file and line number in *FN and *LN, respectively,
320    or NULL and -1 if none. */
321 void
322 getl_location (const char **fn, int *ln)
323 {
324   if (fn != NULL)
325     *fn = getl_head ? getl_head->fn : NULL;
326   if (ln != NULL)
327     *ln = getl_head ? getl_head->ln : -1;
328 }
329
330 bool 
331 getl_reading_script (void)
332 {
333   return (getl_head != NULL);
334 }
335