Change license from GPLv2+ to GPLv3+.
[pspp-builds.git] / src / libpspp / getl.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <stdlib.h>
18 #include <config.h>
19
20 #include "getl.h"
21
22 #include <libpspp/str.h>
23 #include <libpspp/ll.h>
24 #include <libpspp/version.h>
25 #include <libpspp/alloc.h>
26
27 struct getl_source
28   {
29     struct getl_source *included_from;  /* File that this is nested inside. */
30     struct getl_source *includes;       /* File nested inside this file. */
31
32     struct ll  ll;   /* Element in the sources list */
33
34     struct getl_interface *interface;
35   };
36
37 struct source_stream
38   {
39     struct ll_list sources ;  /* List of source files. */
40
41     struct string the_include_path;
42   };
43
44 const char *
45 getl_include_path (const struct source_stream *ss)
46 {
47   return ds_cstr (&ss->the_include_path);
48 }
49
50 static struct getl_source *
51 current_source (const struct source_stream *ss)
52 {
53   const struct ll *ll = ll_head (&ss->sources);
54   return ll_data (ll, struct getl_source, ll );
55 }
56
57 /* Initialize getl. */
58 struct source_stream *
59 create_source_stream (const char *initial_include_path)
60 {
61   struct source_stream *ss = xzalloc (sizeof (*ss));
62   ll_init (&ss->sources);
63 #if 0
64   ds_init_cstr (&ss->the_include_path,
65                 fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
66 #endif
67   ds_init_cstr (&ss->the_include_path, initial_include_path);
68
69   return ss;
70 }
71
72 /* Delete everything from the include path. */
73 void
74 getl_clear_include_path (struct source_stream *ss)
75 {
76   ds_clear (&ss->the_include_path);
77 }
78
79 /* Add to the include path. */
80 void
81 getl_add_include_dir (struct source_stream *ss, const char *path)
82 {
83   if (ds_length (&ss->the_include_path))
84     ds_put_char (&ss->the_include_path, ':');
85
86   ds_put_cstr (&ss->the_include_path, path);
87 }
88
89 /* Appends source S to the list of source files. */
90 void
91 getl_append_source (struct source_stream *ss, struct getl_interface *i)
92 {
93   struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
94
95   s->interface = i ;
96
97   ll_push_head (&ss->sources, &s->ll);
98 }
99
100 /* Nests source S within the current source file. */
101 void
102 getl_include_source (struct source_stream *ss, struct getl_interface *i)
103 {
104   struct getl_source *current = current_source (ss);
105   struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
106
107   s->interface = i;
108
109   s->included_from = current ;
110   s->includes  = NULL;
111   current->includes = s;
112
113   ll_push_head (&ss->sources, &s->ll);
114 }
115
116 /* Closes the current source, and move  the current source to the
117    next file in the chain. */
118 static void
119 close_source (struct source_stream *ss)
120 {
121   struct getl_source *s = current_source (ss);
122
123   if ( s->interface->close )
124     s->interface->close (s->interface);
125
126   ll_pop_head (&ss->sources);
127
128   if (s->included_from != NULL)
129     current_source (ss)->includes = NULL;
130
131   free (s);
132 }
133
134 /* Closes all sources until an interactive source is
135    encountered. */
136 void
137 getl_abort_noninteractive (struct source_stream *ss)
138 {
139   while ( ! ll_is_empty (&ss->sources))
140     {
141       const struct getl_source *s = current_source (ss);
142
143       if ( !s->interface->interactive (s->interface) )
144         close_source (ss);
145     }
146 }
147
148 /* Returns true if the current source is interactive,
149    false otherwise. */
150 bool
151 getl_is_interactive (const struct source_stream *ss)
152 {
153   const struct getl_source *s = current_source (ss);
154
155   if (ll_is_empty (&ss->sources) )
156     return false;
157
158   return s->interface->interactive (s->interface);
159 }
160
161 /* Returns the name of the current source, or NULL if there is no
162    current source */
163 const char *
164 getl_source_name (const struct source_stream *ss)
165 {
166   const struct getl_source *s = current_source (ss);
167
168   if ( ll_is_empty (&ss->sources) )
169     return NULL;
170
171   if ( ! s->interface->name )
172     return NULL;
173
174   return s->interface->name (s->interface);
175 }
176
177 /* Returns the location within the current source, or -1 if there is
178    no current source */
179 int
180 getl_source_location (const struct source_stream *ss)
181 {
182   const struct getl_source *s = current_source (ss);
183
184   if ( ll_is_empty (&ss->sources) )
185     return -1;
186
187   if ( !s->interface->location )
188     return -1;
189
190   return s->interface->location (s->interface);
191 }
192
193
194 /* Close getl. */
195 void
196 destroy_source_stream (struct source_stream *ss)
197 {
198   while ( !ll_is_empty (&ss->sources))
199     close_source (ss);
200   ds_destroy (&ss->the_include_path);
201
202   free (ss);
203 }
204
205
206 /* Reads a single line into LINE.
207    Returns true when a line has been read, false at end of input.
208    On success, sets *SYNTAX to the style of the syntax read. */
209 bool
210 getl_read_line (struct source_stream *ss, struct string *line,
211                 enum getl_syntax *syntax)
212 {
213   while (!ll_is_empty (&ss->sources))
214     {
215       struct getl_source *s = current_source (ss);
216
217       ds_clear (line);
218       if (s->interface->read (s->interface, line, syntax))
219         {
220           while (s)
221             {
222               if (s->interface->filter)
223                 s->interface->filter (s->interface, line, *syntax);
224               s = s->included_from;
225             }
226
227           return true;
228         }
229       close_source (ss);
230     }
231
232   return false;
233 }