1 /*
2 * This file is part of the Yices SMT Solver.
3 * Copyright (C) 2017 SRI International.
4 *
5 * Yices is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Yices is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Yices. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * File reader: keeps track of filename, position, current character.
21 * String reader: same thing but reads from a null-terminated string.
22 */
23
24 #if 0
25 #define _GNU_SOURCE
26 #include <wchar.h>
27 #endif
28
29 #include <stdbool.h>
30 #include <assert.h>
31
32 #include "io/reader.h"
33
34
35
36 /*
37 * It seems that we need an explicit declaration of getc_unlocked on
38 * Solaris (to avoid a compilation warning). Not sure whether this
39 * is required on all Solaris versions?
40 */
41 #if defined(SOLARIS)
42 extern int getc_unlocked(FILE *);
43 #endif
44
45
46 /*
47 * Read and return the next char from a stream reader
48 * - update pos, line, column
49 */
file_reader_next_char(reader_t * reader)50 static int file_reader_next_char(reader_t *reader) {
51 assert(reader->is_stream);
52
53 if (reader->current == EOF) {
54 return EOF;
55 }
56
57 if (reader->current == '\n') {
58 reader->line ++;
59 reader->column = 0;
60 }
61
62 // getc_unlocked is unsafe in multithreading applications
63 // but it's much faster.
64 #if defined(MINGW)
65 reader->current = getc(reader->input.stream);
66 #else
67 reader->current = getc_unlocked(reader->input.stream);
68 #endif
69 reader->pos ++;
70 reader->column ++;
71
72 return reader->current;
73 }
74
75
76
77 /*
78 * Read and return the next char from a string reader
79 * - update pos, line, column
80 */
string_reader_next_char(reader_t * reader)81 static int string_reader_next_char(reader_t *reader) {
82 char c;
83
84 assert(! reader->is_stream);
85
86 if (reader->current == EOF) {
87 return EOF;
88 }
89
90 if (reader->current == '\n') {
91 reader->line ++;
92 reader->column = 0;
93 }
94
95 c = reader->input.data[reader->pos];
96 reader->current = c;
97 if (c == '\0') {
98 reader->current = EOF;
99 }
100 reader->pos ++;
101 reader->column ++;
102
103 return reader->current;
104 }
105
106
107
108
109
110 /*
111 * Initialize reader for file of the given name
112 * - return -1 if the file could not be open
113 * or 0 otherwise
114 * - if the file can't be opened, current is set to EOF,
115 * any subsequent attempt to read will return EOF
116 * - if the file can be opened, current is set to '\n'
117 */
init_file_reader(reader_t * reader,const char * filename)118 int32_t init_file_reader(reader_t *reader, const char *filename) {
119 FILE *f;
120
121 f = fopen(filename, "r");
122 reader->input.stream = f; // keep it NULL if there's an error
123 reader->pos = 0;
124 reader->line = 0;
125 reader->column = 1;
126 reader->is_stream = true;
127 reader->read = file_reader_next_char;
128 reader->name = filename;
129
130 if (f == NULL) {
131 reader->current = EOF;
132 return -1;
133 }
134
135 reader->current = '\n';
136 return 0;
137 }
138
139 /*
140 * Initialize reader for an already opened stream
141 * - set filename to name
142 */
init_stream_reader(reader_t * reader,FILE * f,const char * name)143 void init_stream_reader(reader_t *reader, FILE *f, const char *name) {
144 reader->current = '\n';
145 reader->input.stream = f;
146 reader->pos = 0;
147 reader->line = 0;
148 reader->column = 1;
149 reader->is_stream = true;
150 reader->read = file_reader_next_char;
151 reader->name = name;
152 }
153
154
155 /*
156 * Initialize reader for string data
157 */
init_string_reader(reader_t * reader,const char * data,const char * name)158 void init_string_reader(reader_t *reader, const char *data, const char *name) {
159 reader->current = '\n';
160 reader->input.data = data;
161 reader->pos = 0;
162 reader->line = 0;
163 reader->column = 1;
164 reader->is_stream = false;
165 reader->read = string_reader_next_char;
166 reader->name = name;
167 }
168
169
170 /*
171 * Reset: change the input string
172 */
reset_string_reader(reader_t * reader,const char * data)173 void reset_string_reader(reader_t *reader, const char *data) {
174 assert(! reader->is_stream);
175 assert(reader->read == string_reader_next_char);
176
177 reader->current = '\n';
178 reader->input.data = data;
179 reader->pos = 0;
180 reader->line = 0;
181 reader->column = 1;
182 }
183
184
185
186 /*
187 * Close reader: return EOF on error, 0 otherwise
188 */
close_reader(reader_t * reader)189 int close_reader(reader_t *reader) {
190 if (reader->is_stream) {
191 return fclose(reader->input.stream);
192 } else {
193 return 0;
194 }
195 }
196
197
198 #if 0
199 /*
200 * Experimental variant: use wide characters
201 * (we assume UTF-8 encoding)
202 *
203 * We assume that int is large enough to store any character.
204 * To do this properly, we should use wint_t.
205 */
206 static int file_reader_next_wchar(reader_t *reader) {
207 wint_t c;
208
209 assert(reader->is_stream);
210
211 if (reader->current == EOF) {
212 return EOF;
213 }
214
215 if (reader->current == '\n') { // this should works in UTF-8?
216 reader->line ++;
217 reader->column ++;
218 }
219
220 #if defined(LINUX)
221 c = fgetwc_unlocked(reader->input.stream);
222 #else
223 c = fgetwc(reader->input.stream);
224 #endif
225
226 if (c == WEOF) {
227 reader->current = EOF;
228 } else {
229 reader->current = c;
230 reader->pos ++;
231 reader->column += wcwidth(c);
232 }
233
234 return c;
235 }
236
237
238 /*
239 * Experimental: try to support wide characters (UTF-8)
240 */
241 int32_t init_wide_file_reader(reader_t *reader, const char *filename) {
242 FILE *f;
243
244 f = fopen(filename, "r");
245 reader->input.stream = f; // keep it NULL if there's an error
246 reader->pos = 0;
247 reader->line = 0;
248 reader->column = 1;
249 reader->is_stream = true;
250 reader->read = file_reader_next_wchar;
251 reader->name = filename;
252
253 if (f == NULL) {
254 reader->current = EOF;
255 return -1;
256 }
257
258 reader->current = '\n';
259 return 0;
260 }
261
262 void init_wide_stream_reader(reader_t *reader, FILE *f, const char *name) {
263 reader->current = '\n';
264 reader->input.stream = f;
265 reader->pos = 0;
266 reader->line = 0;
267 reader->column = 1;
268 reader->is_stream = true;
269 reader->read = file_reader_next_wchar;
270 reader->name = name;
271 }
272
273 #endif
274