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