Blame src/location.c

Packit d6cc65
/* Locations for Bison
Packit d6cc65
Packit d6cc65
   Copyright (C) 2002, 2005-2015 Free Software Foundation, Inc.
Packit d6cc65
Packit d6cc65
   This file is part of Bison, the GNU Compiler Compiler.
Packit d6cc65
Packit d6cc65
   This program is free software: you can redistribute it and/or modify
Packit d6cc65
   it under the terms of the GNU General Public License as published by
Packit d6cc65
   the Free Software Foundation, either version 3 of the License, or
Packit d6cc65
   (at your option) any later version.
Packit d6cc65
Packit d6cc65
   This program is distributed in the hope that it will be useful,
Packit d6cc65
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d6cc65
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit d6cc65
   GNU General Public License for more details.
Packit d6cc65
Packit d6cc65
   You should have received a copy of the GNU General Public License
Packit d6cc65
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit d6cc65
Packit d6cc65
#include <config.h>
Packit d6cc65
#include "system.h"
Packit d6cc65
Packit d6cc65
#include <mbswidth.h>
Packit d6cc65
#include <quotearg.h>
Packit d6cc65
Packit d6cc65
#include "complain.h"
Packit d6cc65
#include "location.h"
Packit d6cc65
Packit d6cc65
location const empty_location = EMPTY_LOCATION_INIT;
Packit d6cc65
Packit d6cc65
/* If BUF is null, add BUFSIZE (which in this case must be less than
Packit d6cc65
   INT_MAX) to COLUMN; otherwise, add mbsnwidth (BUF, BUFSIZE, 0) to
Packit d6cc65
   COLUMN.  If an overflow occurs, or might occur but is undetectable,
Packit d6cc65
   return INT_MAX.  Assume COLUMN is nonnegative.  */
Packit d6cc65
Packit d6cc65
static inline int
Packit d6cc65
add_column_width (int column, char const *buf, size_t bufsize)
Packit d6cc65
{
Packit d6cc65
  size_t width;
Packit d6cc65
  unsigned int remaining_columns = INT_MAX - column;
Packit d6cc65
Packit d6cc65
  if (buf)
Packit d6cc65
    {
Packit d6cc65
      if (INT_MAX / 2 <= bufsize)
Packit d6cc65
        return INT_MAX;
Packit d6cc65
      width = mbsnwidth (buf, bufsize, 0);
Packit d6cc65
    }
Packit d6cc65
  else
Packit d6cc65
    width = bufsize;
Packit d6cc65
Packit d6cc65
  return width <= remaining_columns ? column + width : INT_MAX;
Packit d6cc65
}
Packit d6cc65
Packit d6cc65
/* Set *LOC and adjust scanner cursor to account for token TOKEN of
Packit d6cc65
   size SIZE.  */
Packit d6cc65
Packit d6cc65
void
Packit d6cc65
location_compute (location *loc, boundary *cur, char const *token, size_t size)
Packit d6cc65
{
Packit d6cc65
  int line = cur->line;
Packit d6cc65
  int column = cur->column;
Packit d6cc65
  char const *p0 = token;
Packit d6cc65
  char const *p = token;
Packit d6cc65
  char const *lim = token + size;
Packit d6cc65
Packit d6cc65
  loc->start = *cur;
Packit d6cc65
Packit d6cc65
  for (p = token; p < lim; p++)
Packit d6cc65
    switch (*p)
Packit d6cc65
      {
Packit d6cc65
      case '\n':
Packit d6cc65
        line += line < INT_MAX;
Packit d6cc65
        column = 1;
Packit d6cc65
        p0 = p + 1;
Packit d6cc65
        break;
Packit d6cc65
Packit d6cc65
      case '\t':
Packit d6cc65
        column = add_column_width (column, p0, p - p0);
Packit d6cc65
        column = add_column_width (column, NULL, 8 - ((column - 1) & 7));
Packit d6cc65
        p0 = p + 1;
Packit d6cc65
        break;
Packit d6cc65
Packit d6cc65
      default:
Packit d6cc65
        break;
Packit d6cc65
      }
Packit d6cc65
Packit d6cc65
  cur->line = line;
Packit d6cc65
  cur->column = column = add_column_width (column, p0, p - p0);
Packit d6cc65
Packit d6cc65
  loc->end = *cur;
Packit d6cc65
Packit d6cc65
  if (line == INT_MAX && loc->start.line != INT_MAX)
Packit d6cc65
    complain (loc, Wother, _("line number overflow"));
Packit d6cc65
  if (column == INT_MAX && loc->start.column != INT_MAX)
Packit d6cc65
    complain (loc, Wother, _("column number overflow"));
Packit d6cc65
}
Packit d6cc65
Packit d6cc65
Packit d6cc65
unsigned
Packit d6cc65
location_print (location loc, FILE *out)
Packit d6cc65
{
Packit d6cc65
  unsigned res = 0;
Packit d6cc65
  int end_col = 0 != loc.end.column ? loc.end.column - 1 : 0;
Packit d6cc65
  res += fprintf (out, "%s",
Packit d6cc65
                  quotearg_n_style (3, escape_quoting_style, loc.start.file));
Packit d6cc65
  if (0 <= loc.start.line)
Packit d6cc65
    {
Packit d6cc65
      res += fprintf (out, ":%d", loc.start.line);
Packit d6cc65
      if (0 <= loc.start.column)
Packit d6cc65
        res += fprintf (out, ".%d", loc.start.column);
Packit d6cc65
    }
Packit d6cc65
  if (loc.start.file != loc.end.file)
Packit d6cc65
    {
Packit d6cc65
      res += fprintf (out, "-%s",
Packit d6cc65
                      quotearg_n_style (3, escape_quoting_style,
Packit d6cc65
                                        loc.end.file));
Packit d6cc65
      if (0 <= loc.end.line)
Packit d6cc65
        {
Packit d6cc65
          res += fprintf (out, ":%d", loc.end.line);
Packit d6cc65
          if (0 <= end_col)
Packit d6cc65
            res += fprintf (out, ".%d", end_col);
Packit d6cc65
        }
Packit d6cc65
    }
Packit d6cc65
  else if (0 <= loc.end.line)
Packit d6cc65
    {
Packit d6cc65
      if (loc.start.line < loc.end.line)
Packit d6cc65
        {
Packit d6cc65
          res += fprintf (out, "-%d", loc.end.line);
Packit d6cc65
          if (0 <= end_col)
Packit d6cc65
            res += fprintf (out, ".%d", end_col);
Packit d6cc65
        }
Packit d6cc65
      else if (0 <= end_col && loc.start.column < end_col)
Packit d6cc65
        res += fprintf (out, "-%d", end_col);
Packit d6cc65
    }
Packit d6cc65
Packit d6cc65
  return res;
Packit d6cc65
}
Packit d6cc65
Packit d6cc65
Packit d6cc65
/* Persistant data used by location_caret to avoid reopening and rereading the
Packit d6cc65
   same file all over for each error.  */
Packit d6cc65
struct caret_info
Packit d6cc65
{
Packit d6cc65
  FILE *source;
Packit d6cc65
  size_t line;
Packit d6cc65
  size_t offset;
Packit d6cc65
};
Packit d6cc65
Packit d6cc65
static struct caret_info caret_info = { NULL, 1, 0 };
Packit d6cc65
Packit d6cc65
void
Packit d6cc65
cleanup_caret ()
Packit d6cc65
{
Packit d6cc65
  if (caret_info.source)
Packit d6cc65
    fclose (caret_info.source);
Packit d6cc65
  caret_info.source = NULL;
Packit d6cc65
  caret_info.line = 1;
Packit d6cc65
  caret_info.offset = 0;
Packit d6cc65
}
Packit d6cc65
Packit d6cc65
void
Packit d6cc65
location_caret (location loc, FILE *out)
Packit d6cc65
{
Packit d6cc65
  /* FIXME: find a way to support multifile locations, and only open once each
Packit d6cc65
     file. That would make the procedure future-proof.  */
Packit d6cc65
  if (! (caret_info.source
Packit d6cc65
         || (caret_info.source = fopen (loc.start.file, "r")))
Packit d6cc65
      || loc.start.column == -1 || loc.start.line == -1)
Packit d6cc65
    return;
Packit d6cc65
Packit d6cc65
  /* If the line we want to quote is seekable (the same line as the previous
Packit d6cc65
     location), just seek it. If it was a previous line, we lost track of it,
Packit d6cc65
     so return to the start of file.  */
Packit d6cc65
  if (caret_info.line <= loc.start.line)
Packit d6cc65
    fseek (caret_info.source, caret_info.offset, SEEK_SET);
Packit d6cc65
  else
Packit d6cc65
    {
Packit d6cc65
      caret_info.line = 1;
Packit d6cc65
      caret_info.offset = 0;
Packit d6cc65
      fseek (caret_info.source, caret_info.offset, SEEK_SET);
Packit d6cc65
    }
Packit d6cc65
Packit d6cc65
  /* Advance to the line's position, keeping track of the offset.  */
Packit d6cc65
  while (caret_info.line < loc.start.line)
Packit d6cc65
    caret_info.line += getc (caret_info.source) == '\n';
Packit d6cc65
  caret_info.offset = ftell (caret_info.source);
Packit d6cc65
Packit d6cc65
  /* Read the actual line.  Don't update the offset, so that we keep a pointer
Packit d6cc65
     to the start of the line.  */
Packit d6cc65
  {
Packit d6cc65
    int c = getc (caret_info.source);
Packit d6cc65
    if (c != EOF)
Packit d6cc65
      {
Packit d6cc65
        /* Quote the file, indent by a single column.  */
Packit d6cc65
        putc (' ', out);
Packit d6cc65
        do
Packit d6cc65
          putc (c, out);
Packit d6cc65
        while ((c = getc (caret_info.source)) != EOF && c != '\n');
Packit d6cc65
        putc ('\n', out);
Packit d6cc65
Packit d6cc65
        {
Packit d6cc65
          /* The caret of a multiline location ends with the first line.  */
Packit d6cc65
          size_t len = loc.start.line != loc.end.line
Packit d6cc65
            ? ftell (caret_info.source) - caret_info.offset
Packit d6cc65
            : loc.end.column;
Packit d6cc65
          int i;
Packit d6cc65
Packit d6cc65
          /* Print the carets (at least one), with the same indent as above.*/
Packit d6cc65
          fprintf (out, " %*s", loc.start.column - 1, "");
Packit d6cc65
          for (i = loc.start.column; i == loc.start.column || i < len; ++i)
Packit d6cc65
            putc ('^', out);
Packit d6cc65
          }
Packit d6cc65
        putc ('\n', out);
Packit d6cc65
      }
Packit d6cc65
  }
Packit d6cc65
}
Packit d6cc65
Packit d6cc65
void
Packit d6cc65
boundary_set_from_string (boundary *bound, char *loc_str)
Packit d6cc65
{
Packit d6cc65
  /* Must search in reverse since the file name field may
Packit d6cc65
   * contain '.' or ':'.  */
Packit d6cc65
  char *delim = strrchr (loc_str, '.');
Packit d6cc65
  aver (delim);
Packit d6cc65
  *delim = '\0';
Packit d6cc65
  bound->column = atoi (delim+1);
Packit d6cc65
  delim = strrchr (loc_str, ':');
Packit d6cc65
  aver (delim);
Packit d6cc65
  *delim = '\0';
Packit d6cc65
  bound->line = atoi (delim+1);
Packit d6cc65
  bound->file = uniqstr_new (loc_str);
Packit d6cc65
}