https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98882

--- Comment #2 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
It isn't just on empty files, but on any files that don't end with newline.

I believe the
      /* Files always end in a newline.  We rely on this for
         character peeking safety.  */
      gcc_assert (buffer->rlimit[-1] == '\n');
assert is just wrong.
It isn't buffer->rlimit[-1] that should be '\n', it is actually
buffer->rlimit[0] that should be end of line terminator, buffer->rlimit[-1] is
the last character of the buffer, so whatever is in the buffer (and empty files
have buffer->cur == buffer->rlimit and thus have even nothing there at all so
rlimit[-1] access is invalid).

Another problem is though that even buffer->rlimit[0] == '\n' is not
guaranteed.

libcpp/charset.c has:

  /* If the file is using old-school Mac line endings (\r only),
     terminate with another \r, not an \n, so that we do not mistake
     the \r\n sequence for a single DOS line ending and erroneously
     issue the "No newline at end of file" diagnostic.  */
  if (to.len && to.text[to.len - 1] == '\r')
    to.text[to.len] = '\r';
  else
    to.text[to.len] = '\n';

So, I think we can assert that buffer->rlimit[0] == '\n' || buffer->rlimit[0]
== '\r' and in the
            case '\r': /* MAC line ending, or Windows \r\n  */
              if (*pos == '\n')
                pos++;
              /* FALLTHROUGH */
and
              else if (*pos == '\r')
                {
                  if (pos[1] == '\n')
                    pos++;
                  pos++;
                  goto next_line;
                }
              goto dflt;
and
                      case '\r':
                        if (*pos == '\n')
                          pos++;
(twice) cases unfortunately need to compare pos against limit.

Reply via email to