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