/src/binutils-gdb/gas/input-file.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* input_file.c - Deal with Input Files - |
2 | | Copyright (C) 1987-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | /* Confines all details of reading source bytes to this module. |
22 | | All O/S specific crocks should live here. |
23 | | What we lose in "efficiency" we gain in modularity. |
24 | | Note we don't need to #include the "as.h" file. No common coupling! */ |
25 | | |
26 | | #include "as.h" |
27 | | #include "input-file.h" |
28 | | #include "safe-ctype.h" |
29 | | |
30 | | /* This variable is non-zero if the file currently being read should be |
31 | | preprocessed by app. It is zero if the file can be read straight in. */ |
32 | | int preprocess = 0; |
33 | | |
34 | | /* This code opens a file, then delivers BUFFER_SIZE character |
35 | | chunks of the file on demand. |
36 | | BUFFER_SIZE is supposed to be a number chosen for speed. |
37 | | The caller only asks once what BUFFER_SIZE is, and asks before |
38 | | the nature of the input files (if any) is known. */ |
39 | | |
40 | 2.36k | #define BUFFER_SIZE (32 * 1024) |
41 | | |
42 | | /* We use static data: the data area is not sharable. */ |
43 | | |
44 | | static FILE *f_in; |
45 | | static const char *file_name; |
46 | | |
47 | | /* Struct for saving the state of this module for file includes. */ |
48 | | struct saved_file |
49 | | { |
50 | | FILE * f_in; |
51 | | const char * file_name; |
52 | | int preprocess; |
53 | | char * app_save; |
54 | | }; |
55 | | |
56 | | /* These hooks accommodate most operating systems. */ |
57 | | |
58 | | void |
59 | | input_file_begin (void) |
60 | 2.79k | { |
61 | 2.79k | f_in = (FILE *) 0; |
62 | 2.79k | } |
63 | | |
64 | | void |
65 | | input_file_end (void) |
66 | 2.79k | { |
67 | 2.79k | } |
68 | | |
69 | | /* Return BUFFER_SIZE. */ |
70 | | size_t |
71 | | input_file_buffer_size (void) |
72 | 1.62k | { |
73 | 1.62k | return (BUFFER_SIZE); |
74 | 1.62k | } |
75 | | |
76 | | /* Push the state of our input, returning a pointer to saved info that |
77 | | can be restored with input_file_pop (). */ |
78 | | |
79 | | char * |
80 | | input_file_push (void) |
81 | 1.38k | { |
82 | 1.38k | struct saved_file *saved; |
83 | | |
84 | 1.38k | saved = XNEW (struct saved_file); |
85 | | |
86 | 1.38k | saved->f_in = f_in; |
87 | 1.38k | saved->file_name = file_name; |
88 | 1.38k | saved->preprocess = preprocess; |
89 | 1.38k | if (preprocess) |
90 | 1.38k | saved->app_save = app_push (); |
91 | | |
92 | | /* Initialize for new file. */ |
93 | 1.38k | input_file_begin (); |
94 | | |
95 | 1.38k | return (char *) saved; |
96 | 1.38k | } |
97 | | |
98 | | void |
99 | | input_file_pop (char *arg) |
100 | 1.38k | { |
101 | 1.38k | struct saved_file *saved = (struct saved_file *) arg; |
102 | | |
103 | 1.38k | input_file_end (); /* Close out old file. */ |
104 | | |
105 | 1.38k | f_in = saved->f_in; |
106 | 1.38k | file_name = saved->file_name; |
107 | 1.38k | preprocess = saved->preprocess; |
108 | 1.38k | if (preprocess) |
109 | 1.38k | app_pop (saved->app_save); |
110 | | |
111 | 1.38k | free (arg); |
112 | 1.38k | } |
113 | | |
114 | | /* Open the specified file, "" means stdin. Filename must not be null. */ |
115 | | |
116 | | void |
117 | | input_file_open (const char *filename, |
118 | | int pre) |
119 | 28 | { |
120 | 28 | int c; |
121 | 28 | char buf[80]; |
122 | | |
123 | 28 | preprocess = pre; |
124 | | |
125 | 28 | gas_assert (filename != 0); /* Filename may not be NULL. */ |
126 | 28 | if (filename[0]) |
127 | 28 | { |
128 | 28 | f_in = fopen (filename, FOPEN_RT); |
129 | 28 | file_name = filename; |
130 | 28 | } |
131 | 0 | else |
132 | 0 | { |
133 | | /* Use stdin for the input file. */ |
134 | 0 | f_in = stdin; |
135 | | /* For error messages. */ |
136 | 0 | file_name = _("{standard input}"); |
137 | 0 | } |
138 | | |
139 | 28 | if (f_in == NULL) |
140 | 0 | { |
141 | 0 | as_bad (_("can't open %s for reading: %s"), |
142 | 0 | file_name, xstrerror (errno)); |
143 | 0 | return; |
144 | 0 | } |
145 | | |
146 | 28 | c = getc (f_in); |
147 | | |
148 | 28 | if (ferror (f_in)) |
149 | 0 | { |
150 | 0 | as_bad (_("can't read from %s: %s"), |
151 | 0 | file_name, xstrerror (errno)); |
152 | |
|
153 | 0 | fclose (f_in); |
154 | 0 | f_in = NULL; |
155 | 0 | return; |
156 | 0 | } |
157 | | |
158 | | /* Check for an empty input file. */ |
159 | 28 | if (feof (f_in)) |
160 | 0 | { |
161 | 0 | fclose (f_in); |
162 | 0 | f_in = NULL; |
163 | 0 | return; |
164 | 0 | } |
165 | 28 | gas_assert (c != EOF); |
166 | | |
167 | 28 | if (strchr (line_comment_chars, '#') |
168 | 28 | ? c == '#' |
169 | 28 | : c && strchr (line_comment_chars, c)) |
170 | 1 | { |
171 | | /* Begins with comment, may not want to preprocess. */ |
172 | 1 | int lead = c; |
173 | | |
174 | 1 | c = getc (f_in); |
175 | 1 | if (c == 'N') |
176 | 1 | { |
177 | 1 | char *p = fgets (buf, sizeof (buf), f_in); |
178 | 1 | if (p && startswith (p, "O_APP") && is_end_of_line (p[5])) |
179 | 1 | preprocess = 0; |
180 | 1 | if (!p || !strchr (p, '\n')) |
181 | 0 | ungetc (lead, f_in); |
182 | 1 | else |
183 | 1 | ungetc ('\n', f_in); |
184 | 1 | } |
185 | 0 | else if (c == 'A') |
186 | 0 | { |
187 | 0 | char *p = fgets (buf, sizeof (buf), f_in); |
188 | 0 | if (p && startswith (p, "PP") && is_end_of_line (p[2])) |
189 | 0 | preprocess = 1; |
190 | 0 | if (!p || !strchr (p, '\n')) |
191 | 0 | ungetc (lead, f_in); |
192 | 0 | else |
193 | 0 | ungetc ('\n', f_in); |
194 | 0 | } |
195 | 0 | else if (c == '\n') |
196 | 0 | ungetc ('\n', f_in); |
197 | 0 | else |
198 | 0 | ungetc (lead, f_in); |
199 | 1 | } |
200 | 27 | else |
201 | 27 | ungetc (c, f_in); |
202 | 28 | } |
203 | | |
204 | | /* Close input file. */ |
205 | | |
206 | | void |
207 | | input_file_close (void) |
208 | 28 | { |
209 | | /* Don't close a null file pointer. */ |
210 | 28 | if (f_in != NULL) |
211 | 0 | fclose (f_in); |
212 | | |
213 | 28 | f_in = 0; |
214 | 28 | } |
215 | | |
216 | | /* This function is passed to do_scrub_chars. */ |
217 | | |
218 | | static size_t |
219 | | input_file_get (char *buf, size_t buflen) |
220 | 933 | { |
221 | 933 | size_t size; |
222 | | |
223 | 933 | if (feof (f_in)) |
224 | 53 | return 0; |
225 | | |
226 | 880 | size = fread (buf, sizeof (char), buflen, f_in); |
227 | 880 | if (ferror (f_in)) |
228 | 0 | as_bad (_("can't read from %s: %s"), file_name, xstrerror (errno)); |
229 | 880 | return size; |
230 | 933 | } |
231 | | |
232 | | /* Read a buffer from the input file. */ |
233 | | |
234 | | char * |
235 | | input_file_give_next_buffer (char *where /* Where to place 1st character of new buffer. */) |
236 | 744 | { |
237 | 744 | char *return_value; /* -> Last char of what we read, + 1. */ |
238 | 744 | size_t size; |
239 | | |
240 | 744 | if (f_in == (FILE *) 0) |
241 | 4 | return 0; |
242 | | /* fflush (stdin); could be done here if you want to synchronise |
243 | | stdin and stdout, for the case where our input file is stdin. |
244 | | Since the assembler shouldn't do any output to stdout, we |
245 | | don't bother to synch output and input. */ |
246 | 740 | if (preprocess) |
247 | 708 | size = do_scrub_chars (input_file_get, where, BUFFER_SIZE, |
248 | 708 | multibyte_handling == multibyte_warn); |
249 | 32 | else |
250 | 32 | { |
251 | 32 | size = input_file_get (where, BUFFER_SIZE); |
252 | | |
253 | 32 | if (multibyte_handling == multibyte_warn) |
254 | 0 | { |
255 | 0 | const unsigned char *start = (const unsigned char *) where; |
256 | |
|
257 | 0 | (void) scan_for_multibyte_characters (start, start + size, |
258 | 0 | true /* Generate warnings */); |
259 | 0 | } |
260 | 32 | } |
261 | | |
262 | 740 | if (size) |
263 | 712 | return_value = where + size; |
264 | 28 | else |
265 | 28 | { |
266 | 28 | if (fclose (f_in)) |
267 | 0 | as_warn (_("can't close %s: %s"), file_name, xstrerror (errno)); |
268 | | |
269 | 28 | f_in = (FILE *) 0; |
270 | 28 | return_value = 0; |
271 | 28 | } |
272 | | |
273 | 740 | return return_value; |
274 | 744 | } |