1 /* Open and close files for Bison.
2 
3    Copyright (C) 1984, 1986, 1989, 1992, 2000-2015, 2018-2021 Free
4    Software Foundation, Inc.
5 
6    This file is part of Bison, the GNU Compiler Compiler.
7 
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
20 
21 #include <config.h>
22 #include "system.h"
23 
24 #include <configmake.h> /* PKGDATADIR */
25 #include <dirname.h>
26 #include <error.h>
27 #include <get-errno.h>
28 #include <gl_array_list.h>
29 #include <gl_xlist.h>
30 #include <quote.h>
31 #include <quotearg.h>
32 #include <relocatable.h> /* relocate2 */
33 #include <stdio-safer.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <xstrndup.h>
38 
39 #include "complain.h"
40 #include "files.h"
41 #include "getargs.h"
42 #include "gram.h"
43 
44 /* Initializing some values below (such SPEC_NAME_PREFIX to 'yy') is
45    tempting, but don't do that: for the time being our handling of the
46    %directive vs --option leaves precedence to the options by deciding
47    that if a %directive sets a variable which is really set (i.e., not
48    NULL), then the %directive is ignored.  As a result, %name-prefix,
49    for instance, will not be honored.  */
50 
51 char const *spec_outfile = NULL;       /* for -o. */
52 char const *spec_file_prefix = NULL;   /* for -b. */
53 location spec_file_prefix_loc = EMPTY_LOCATION_INIT;
54 char const *spec_name_prefix = NULL;   /* for -p. */
55 location spec_name_prefix_loc = EMPTY_LOCATION_INIT;
56 char *spec_verbose_file = NULL;  /* for --verbose. */
57 char *spec_graph_file = NULL;    /* for -g. */
58 char *spec_xml_file = NULL;      /* for -x. */
59 char *spec_header_file = NULL;  /* for --defines. */
60 char *spec_mapped_header_file = NULL;
61 char *parser_file_name;
62 
63 /* All computed output file names.  */
64 typedef struct generated_file
65 {
66   /** File name.  */
67   char *name;
68   /** Whether is a generated source file (e.g., *.c, *.java...), as
69       opposed to the report file (e.g., *.output).  When late errors
70       are detected, generated source files are removed.  */
71   bool is_source;
72 } generated_file;
73 static generated_file *generated_files = NULL;
74 static int generated_files_size = 0;
75 
76 uniqstr grammar_file = NULL;
77 
78 /* If --output=dir/foo.c was specified,
79    DIR_PREFIX gis 'dir/' and ALL_BUT_EXT and ALL_BUT_TAB_EXT are 'dir/foo'.
80 
81    If --output=dir/foo.tab.c was specified, DIR_PREFIX is 'dir/',
82    ALL_BUT_EXT is 'dir/foo.tab', and ALL_BUT_TAB_EXT is 'dir/foo'.
83 
84    If --output was not specified but --file-prefix=dir/foo was specified,
85    ALL_BUT_EXT = 'foo.tab' and ALL_BUT_TAB_EXT = 'foo'.
86 
87    If neither --output nor --file was specified but the input grammar
88    is name dir/foo.y, ALL_BUT_EXT and ALL_BUT_TAB_EXT are 'foo'.
89 
90    If neither --output nor --file was specified, DIR_PREFIX is the
91    empty string (meaning the current directory); otherwise it is
92    'dir/'.  */
93 
94 char *all_but_ext;
95 static char *all_but_tab_ext;
96 char *dir_prefix;
97 char *mapped_dir_prefix;
98 
99 /* C source file extension (the parser source).  */
100 static char *src_extension = NULL;
101 /* Header file extension (if option '`-d'' is specified).  */
102 static char *header_extension = NULL;
103 
104 struct prefix_map
105 {
106   char *oldprefix;
107   char *newprefix;
108 };
109 
110 static gl_list_t prefix_maps = NULL;
111 
112 /*-----------------------------------------------------------------.
113 | Return a newly allocated string composed of the concatenation of |
114 | STR1, and STR2.                                                  |
115 `-----------------------------------------------------------------*/
116 
117 static char *
concat2(char const * str1,char const * str2)118 concat2 (char const *str1, char const *str2)
119 {
120   size_t len = strlen (str1) + strlen (str2);
121   char *res = xmalloc (len + 1);
122   char *cp;
123   cp = stpcpy (res, str1);
124   cp = stpcpy (cp, str2);
125   return res;
126 }
127 
128 /*-----------------------------------------------------------------.
129 | Try to open file NAME with mode MODE, and print an error message |
130 | if fails.                                                        |
131 `-----------------------------------------------------------------*/
132 
133 FILE *
xfopen(const char * name,const char * mode)134 xfopen (const char *name, const char *mode)
135 {
136   FILE *res = fopen_safer (name, mode);
137   if (!res)
138     error (EXIT_FAILURE, get_errno (),
139            _("%s: cannot open"), quotearg_colon (name));
140 
141   return res;
142 }
143 
144 /*-------------------------------------------------------------.
145 | Try to close file PTR, and print an error message if fails.  |
146 `-------------------------------------------------------------*/
147 
148 void
xfclose(FILE * ptr)149 xfclose (FILE *ptr)
150 {
151   if (ptr == NULL)
152     return;
153 
154   if (ferror (ptr))
155     error (EXIT_FAILURE, 0, _("input/output error"));
156 
157   if (fclose (ptr) != 0)
158     error (EXIT_FAILURE, get_errno (), _("cannot close file"));
159 }
160 
161 
162 FILE *
xfdopen(int fd,char const * mode)163 xfdopen (int fd, char const *mode)
164 {
165   FILE *res = fdopen (fd, mode);
166   if (! res)
167     error (EXIT_FAILURE, get_errno (),
168            /* On a separate line to please the "unmarked_diagnostics"
169               syntax-check. */
170            "fdopen");
171   return res;
172 }
173 
174 /*  Given an input file path, returns a dynamically allocated string that
175     contains the path with the file prefix mapping rules applied, or NULL if
176     the input was NULL. */
177 char *
map_file_name(char const * filename)178 map_file_name (char const *filename)
179 {
180   if (!filename)
181     return NULL;
182 
183   struct prefix_map const *p = NULL;
184   if (prefix_maps)
185     {
186       void const *ptr;
187       gl_list_iterator_t iter = gl_list_iterator (prefix_maps);
188       while (gl_list_iterator_next (&iter, &ptr, NULL))
189         {
190           p = ptr;
191           if (strncmp (p->oldprefix, filename, strlen (p->oldprefix)) == 0)
192             break;
193           p = NULL;
194         }
195       gl_list_iterator_free (&iter);
196     }
197 
198   if (!p)
199     return xstrdup (filename);
200 
201   size_t oldprefix_len = strlen (p->oldprefix);
202   size_t newprefix_len = strlen (p->newprefix);
203   char *s = xmalloc (newprefix_len + strlen (filename) - oldprefix_len + 1);
204 
205   char *end = stpcpy (s, p->newprefix);
206   stpcpy (end, filename + oldprefix_len);
207 
208   return s;
209 }
210 
211 static void
prefix_map_free(struct prefix_map * p)212 prefix_map_free (struct prefix_map *p)
213 {
214   free (p->oldprefix);
215   free (p->newprefix);
216   free (p);
217 }
218 
219 /*  Adds a new file prefix mapping. If a file path starts with oldprefix, it
220     will be replaced with newprefix */
221 void
add_prefix_map(char const * oldprefix,char const * newprefix)222 add_prefix_map (char const *oldprefix, char const *newprefix)
223 {
224   if (!prefix_maps)
225     prefix_maps = gl_list_create_empty (GL_ARRAY_LIST,
226                                         /* equals */ NULL,
227                                         /* hashcode */ NULL,
228                                         (gl_listelement_dispose_fn) prefix_map_free,
229                                         true);
230 
231   struct prefix_map *p = xmalloc (sizeof (*p));
232   p->oldprefix = xstrdup (oldprefix);
233   p->newprefix = xstrdup (newprefix);
234 
235   gl_list_add_last (prefix_maps, p);
236 }
237 
238 /*------------------------------------------------------------------.
239 | Compute ALL_BUT_EXT, ALL_BUT_TAB_EXT and output files extensions. |
240 `------------------------------------------------------------------*/
241 
242 /* Compute extensions from the grammar file extension.  */
243 static void
compute_exts_from_gf(const char * ext)244 compute_exts_from_gf (const char *ext)
245 {
246   if (STREQ (ext, ".y"))
247     {
248       src_extension = xstrdup (language->src_extension);
249       header_extension = xstrdup (language->header_extension);
250     }
251   else
252     {
253       src_extension = xstrdup (ext);
254       header_extension = xstrdup (ext);
255       tr (src_extension, 'y', 'c');
256       tr (src_extension, 'Y', 'C');
257       tr (header_extension, 'y', 'h');
258       tr (header_extension, 'Y', 'H');
259     }
260 }
261 
262 /* Compute extensions from the given c source file extension.  */
263 static void
compute_exts_from_src(const char * ext)264 compute_exts_from_src (const char *ext)
265 {
266   /* We use this function when the user specifies `-o' or `--output',
267      so the extensions must be computed unconditionally from the file name
268      given by this option.  */
269   src_extension = xstrdup (ext);
270   header_extension = xstrdup (ext);
271   tr (header_extension, 'c', 'h');
272   tr (header_extension, 'C', 'H');
273 }
274 
275 
276 /* Decompose FILE_NAME in four parts: *BASE, *TAB, and *EXT, the fourth
277    part, (the directory) is ranging from FILE_NAME to the char before
278    *BASE, so we don't need an additional parameter.
279 
280    *EXT points to the last period in the basename, or NULL if none.
281 
282    If there is no *EXT, *TAB is NULL.  Otherwise, *TAB points to
283    '.tab' or '_tab' if present right before *EXT, or is NULL. *TAB
284    cannot be equal to *BASE.
285 
286    None are allocated, they are simply pointers to parts of FILE_NAME.
287    Examples:
288 
289    '/tmp/foo.tab.c' -> *BASE = 'foo.tab.c', *TAB = '.tab.c', *EXT =
290    '.c'
291 
292    'foo.c' -> *BASE = 'foo.c', *TAB = NULL, *EXT = '.c'
293 
294    'tab.c' -> *BASE = 'tab.c', *TAB = NULL, *EXT = '.c'
295 
296    '.tab.c' -> *BASE = '.tab.c', *TAB = NULL, *EXT = '.c'
297 
298    'foo.tab' -> *BASE = 'foo.tab', *TAB = NULL, *EXT = '.tab'
299 
300    'foo_tab' -> *BASE = 'foo_tab', *TAB = NULL, *EXT = NULL
301 
302    'foo' -> *BASE = 'foo', *TAB = NULL, *EXT = NULL.  */
303 
304 static void
file_name_split(const char * file_name,const char ** base,const char ** tab,const char ** ext)305 file_name_split (const char *file_name,
306                  const char **base, const char **tab, const char **ext)
307 {
308   *base = last_component (file_name);
309 
310   /* Look for the extension, i.e., look for the last dot. */
311   *ext = strrchr (*base, '.');
312   *tab = NULL;
313 
314   /* If there is an extension, check if there is a '.tab' part right
315      before.  */
316   if (*ext)
317     {
318       size_t baselen = *ext - *base;
319       size_t dottablen = sizeof (TAB_EXT) - 1;
320       if (dottablen < baselen
321           && STRPREFIX_LIT (TAB_EXT, *ext - dottablen))
322         *tab = *ext - dottablen;
323     }
324 }
325 
326 /* Compute ALL_BUT_EXT and ALL_BUT_TAB_EXT from SPEC_OUTFILE or
327    GRAMMAR_FILE.
328 
329    The precise -o name will be used for FTABLE.  For other output
330    files, remove the ".c" or ".tab.c" suffix.  */
331 
332 static void
compute_file_name_parts(void)333 compute_file_name_parts (void)
334 {
335   if (spec_outfile)
336     {
337       const char *base, *tab, *ext;
338       file_name_split (spec_outfile, &base, &tab, &ext);
339       dir_prefix = xstrndup (spec_outfile, base - spec_outfile);
340 
341       /* ALL_BUT_EXT goes up the EXT, excluding it. */
342       all_but_ext =
343         xstrndup (spec_outfile,
344                   (strlen (spec_outfile) - (ext ? strlen (ext) : 0)));
345 
346       /* ALL_BUT_TAB_EXT goes up to TAB, excluding it.  */
347       all_but_tab_ext =
348         xstrndup (spec_outfile,
349                   (strlen (spec_outfile)
350                    - (tab ? strlen (tab) : (ext ? strlen (ext) : 0))));
351 
352       if (ext)
353         compute_exts_from_src (ext);
354     }
355   else
356     {
357       const char *base, *tab, *ext;
358       file_name_split (grammar_file, &base, &tab, &ext);
359 
360       if (spec_file_prefix)
361         {
362           /* If --file-prefix=foo was specified, ALL_BUT_TAB_EXT = 'foo'.  */
363           dir_prefix =
364             xstrndup (spec_file_prefix,
365                       last_component (spec_file_prefix) - spec_file_prefix);
366           all_but_tab_ext = xstrdup (spec_file_prefix);
367         }
368       else if (! location_empty (yacc_loc))
369         {
370           /* If --yacc, then the output is 'y.tab.c'.  */
371           dir_prefix = xstrdup ("");
372           all_but_tab_ext = xstrdup ("y");
373         }
374       else
375         {
376           /* Otherwise, ALL_BUT_TAB_EXT is computed from the input
377              grammar: 'foo/bar.yy' => 'bar'.  */
378           dir_prefix = xstrdup ("");
379           all_but_tab_ext =
380             xstrndup (base, (strlen (base) - (ext ? strlen (ext) : 0)));
381         }
382 
383       if (language->add_tab)
384         all_but_ext = concat2 (all_but_tab_ext, TAB_EXT);
385       else
386         all_but_ext = xstrdup (all_but_tab_ext);
387 
388       /* Compute the extensions from the grammar file name.  */
389       if (ext && location_empty (yacc_loc))
390         compute_exts_from_gf (ext);
391     }
392 }
393 
394 
395 /* Compute the output file names.  Warn if we detect conflicting
396    outputs to the same file.  */
397 
398 void
compute_output_file_names(void)399 compute_output_file_names (void)
400 {
401   compute_file_name_parts ();
402 
403   /* If not yet done. */
404   if (!src_extension)
405     src_extension = xstrdup (".c");
406   if (!header_extension)
407     header_extension = xstrdup (".h");
408 
409   parser_file_name =
410     (spec_outfile
411      ? xstrdup (spec_outfile)
412      : concat2 (all_but_ext, src_extension));
413 
414   if (defines_flag)
415     {
416       if (! spec_header_file)
417         spec_header_file = concat2 (all_but_ext, header_extension);
418     }
419 
420   if (graph_flag)
421     {
422       if (! spec_graph_file)
423         spec_graph_file = concat2 (all_but_tab_ext,
424                                    304 <= required_version ? ".gv" : ".dot");
425       output_file_name_check (&spec_graph_file, false);
426     }
427 
428   if (xml_flag)
429     {
430       if (! spec_xml_file)
431         spec_xml_file = concat2 (all_but_tab_ext, ".xml");
432       output_file_name_check (&spec_xml_file, false);
433     }
434 
435   if (report_flag)
436     {
437       if (!spec_verbose_file)
438         spec_verbose_file = concat2 (all_but_tab_ext, OUTPUT_EXT);
439       output_file_name_check (&spec_verbose_file, false);
440     }
441 
442   spec_mapped_header_file = map_file_name (spec_header_file);
443   mapped_dir_prefix = map_file_name (dir_prefix);
444 
445   free (all_but_tab_ext);
446   free (src_extension);
447   free (header_extension);
448 }
449 
450 void
output_file_name_check(char ** file_name,bool source)451 output_file_name_check (char **file_name, bool source)
452 {
453   bool conflict = false;
454   if (STREQ (*file_name, grammar_file))
455     {
456       complain (NULL, complaint, _("refusing to overwrite the input file %s"),
457                 quote (*file_name));
458       conflict = true;
459     }
460   else
461     for (int i = 0; i < generated_files_size; i++)
462       if (STREQ (generated_files[i].name, *file_name))
463         {
464           complain (NULL, Wother, _("conflicting outputs to file %s"),
465                     quote (generated_files[i].name));
466           conflict = true;
467         }
468   if (conflict)
469     {
470       free (*file_name);
471       *file_name = strdup ("/dev/null");
472     }
473   else
474     {
475       generated_files = xnrealloc (generated_files, ++generated_files_size,
476                                    sizeof *generated_files);
477       generated_files[generated_files_size-1].name = xstrdup (*file_name);
478       generated_files[generated_files_size-1].is_source = source;
479     }
480 }
481 
482 void
unlink_generated_sources(void)483 unlink_generated_sources (void)
484 {
485   for (int i = 0; i < generated_files_size; i++)
486     if (generated_files[i].is_source)
487       /* Ignore errors.  The file might not even exist.  */
488       unlink (generated_files[i].name);
489 }
490 
491 /* Memory allocated by relocate2, to free.  */
492 static char *relocate_buffer = NULL;
493 
494 char const *
pkgdatadir(void)495 pkgdatadir (void)
496 {
497   if (relocate_buffer)
498     return relocate_buffer;
499   else
500     {
501       char const *cp = getenv ("BISON_PKGDATADIR");
502       return cp ? cp : relocate2 (PKGDATADIR, &relocate_buffer);
503     }
504 }
505 
506 char const *
m4path(void)507 m4path (void)
508 {
509   char const *m4 = getenv ("M4");
510   if (m4)
511     return m4;
512 
513   /* We don't use relocate2() to store the temporary buffer and re-use
514      it, because m4path() is only called once.  */
515   char const *m4_relocated = relocate (M4);
516   struct stat buf;
517   if (stat (m4_relocated, &buf) == 0)
518     return m4_relocated;
519 
520   return M4;
521 }
522 
523 void
output_file_names_free(void)524 output_file_names_free (void)
525 {
526   free (all_but_ext);
527   free (spec_verbose_file);
528   free (spec_graph_file);
529   free (spec_xml_file);
530   free (spec_header_file);
531   free (spec_mapped_header_file);
532   free (parser_file_name);
533   free (dir_prefix);
534   free (mapped_dir_prefix);
535   for (int i = 0; i < generated_files_size; i++)
536     free (generated_files[i].name);
537   free (generated_files);
538   free (relocate_buffer);
539 
540   if (prefix_maps)
541     gl_list_free (prefix_maps);
542 }
543