1/* input_file.c - Deal with Input Files -
2   Copyright (C) 1987-2017 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.  */
32int 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#define BUFFER_SIZE (32 * 1024)
41
42/* We use static data: the data area is not sharable.  */
43
44static FILE *f_in;
45static const char *file_name;
46
47/* Struct for saving the state of this module for file includes.  */
48struct 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
58void
59input_file_begin (void)
60{
61  f_in = (FILE *) 0;
62}
63
64void
65input_file_end (void)
66{
67}
68
69/* Return BUFFER_SIZE.  */
70size_t
71input_file_buffer_size (void)
72{
73  return (BUFFER_SIZE);
74}
75
76/* Push the state of our input, returning a pointer to saved info that
77   can be restored with input_file_pop ().  */
78
79char *
80input_file_push (void)
81{
82  struct saved_file *saved;
83
84  saved = XNEW (struct saved_file);
85
86  saved->f_in = f_in;
87  saved->file_name = file_name;
88  saved->preprocess = preprocess;
89  if (preprocess)
90    saved->app_save = app_push ();
91
92  /* Initialize for new file.  */
93  input_file_begin ();
94
95  return (char *) saved;
96}
97
98void
99input_file_pop (char *arg)
100{
101  struct saved_file *saved = (struct saved_file *) arg;
102
103  input_file_end ();		/* Close out old file.  */
104
105  f_in = saved->f_in;
106  file_name = saved->file_name;
107  preprocess = saved->preprocess;
108  if (preprocess)
109    app_pop (saved->app_save);
110
111  free (arg);
112}
113
114/* Open the specified file, "" means stdin.  Filename must not be null.  */
115
116void
117input_file_open (const char *filename,
118		 int pre)
119{
120  int c;
121  char buf[80];
122
123  preprocess = pre;
124
125  gas_assert (filename != 0);	/* Filename may not be NULL.  */
126  if (filename[0])
127    {
128      f_in = fopen (filename, FOPEN_RT);
129      file_name = filename;
130    }
131  else
132    {
133      /* Use stdin for the input file.  */
134      f_in = stdin;
135      /* For error messages.  */
136      file_name = _("{standard input}");
137    }
138
139  if (f_in == NULL)
140    {
141      as_bad (_("can't open %s for reading: %s"),
142	      file_name, xstrerror (errno));
143      return;
144    }
145
146  c = getc (f_in);
147
148  if (ferror (f_in))
149    {
150      as_bad (_("can't read from %s: %s"),
151	      file_name, xstrerror (errno));
152
153      fclose (f_in);
154      f_in = NULL;
155      return;
156    }
157
158  /* Check for an empty input file.  */
159  if (feof (f_in))
160    {
161      fclose (f_in);
162      f_in = NULL;
163      return;
164    }
165  gas_assert (c != EOF);
166
167  if (c == '#')
168    {
169      /* Begins with comment, may not want to preprocess.  */
170      c = getc (f_in);
171      if (c == 'N')
172	{
173	  if (fgets (buf, sizeof (buf), f_in)
174	      && !strncmp (buf, "O_APP", 5) && ISSPACE (buf[5]))
175	    preprocess = 0;
176	  if (!strchr (buf, '\n'))
177	    ungetc ('#', f_in);	/* It was longer.  */
178	  else
179	    ungetc ('\n', f_in);
180	}
181      else if (c == 'A')
182	{
183	  if (fgets (buf, sizeof (buf), f_in)
184	      && !strncmp (buf, "PP", 2) && ISSPACE (buf[2]))
185	    preprocess = 1;
186	  if (!strchr (buf, '\n'))
187	    ungetc ('#', f_in);
188	  else
189	    ungetc ('\n', f_in);
190	}
191      else if (c == '\n')
192	ungetc ('\n', f_in);
193      else
194	ungetc ('#', f_in);
195    }
196  else
197    ungetc (c, f_in);
198}
199
200/* Close input file.  */
201
202void
203input_file_close (void)
204{
205  /* Don't close a null file pointer.  */
206  if (f_in != NULL)
207    fclose (f_in);
208
209  f_in = 0;
210}
211
212/* This function is passed to do_scrub_chars.  */
213
214static size_t
215input_file_get (char *buf, size_t buflen)
216{
217  size_t size;
218
219  if (feof (f_in))
220    return 0;
221
222  size = fread (buf, sizeof (char), buflen, f_in);
223  if (ferror (f_in))
224    as_bad (_("can't read from %s: %s"), file_name, xstrerror (errno));
225  return size;
226}
227
228/* Read a buffer from the input file.  */
229
230char *
231input_file_give_next_buffer (char *where /* Where to place 1st character of new buffer.  */)
232{
233  char *return_value;		/* -> Last char of what we read, + 1.  */
234  size_t size;
235
236  if (f_in == (FILE *) 0)
237    return 0;
238  /* fflush (stdin); could be done here if you want to synchronise
239     stdin and stdout, for the case where our input file is stdin.
240     Since the assembler shouldn't do any output to stdout, we
241     don't bother to synch output and input.  */
242  if (preprocess)
243    size = do_scrub_chars (input_file_get, where, BUFFER_SIZE);
244  else
245    size = input_file_get (where, BUFFER_SIZE);
246
247  if (size)
248    return_value = where + size;
249  else
250    {
251      if (fclose (f_in))
252	as_warn (_("can't close %s: %s"), file_name, xstrerror (errno));
253
254      f_in = (FILE *) 0;
255      return_value = 0;
256    }
257
258  return return_value;
259}
260