PR gas/15295
authorNick Clifton <nickc@redhat.com>
Tue, 26 Mar 2013 14:16:59 +0000 (14:16 +0000)
committerNick Clifton <nickc@redhat.com>
Tue, 26 Mar 2013 14:16:59 +0000 (14:16 +0000)
* listing.c (rebuffer_line): Rewrite to avoid seeking back to the
start of the file each time.

gas/ChangeLog
gas/listing.c

index 583655cacd15df117af6da4955d8b19731d8b0bf..bb9367d3a5ab975cc6fbb74b1df4ed1da8b5674f 100644 (file)
@@ -1,5 +1,9 @@
 2013-03-26  Nick Clifton  <nickc@redhat.com>
 
+       PR gas/15295
+       * listing.c (rebuffer_line): Rewrite to avoid seeking back to the
+       start of the file each time.
+
        PR gas/15178
        * config/tc-sparc.h (ELF_TARGET_FORMAT): Set to elf32-sparc for
        FreeBSD targets.
index 38518e40f17485f683c2dc31d06bd7153c108204..182d5046912709d4c5483a82b8726caa97d49a49 100644 (file)
@@ -553,7 +553,8 @@ buffer_line (file_info_type *file, char *line, unsigned int size)
 
 /* This function rewinds the requested file back to the line requested,
    reads it in again into the buffer provided and then restores the file
-   back to its original location.  */
+   back to its original location.  Returns the buffer pointer upon success
+   or an empty string if an error occurs.  */
 
 static char *
 rebuffer_line (file_info_type *  file,
@@ -562,13 +563,15 @@ rebuffer_line (file_info_type *  file,
               unsigned int      size)
 {
   unsigned int count = 0;
-  unsigned int current_line = 1;
+  unsigned int current_line;
   char * p = buffer;
   long pos;
+  long pos2;
   int c;
+  bfd_boolean found = FALSE;
 
   /* Sanity checks.  */
-  if (file == NULL || buffer == NULL || size == 0 || file->linenum <= linenum)
+  if (file == NULL || buffer == NULL || size <= 1 || file->linenum <= linenum)
     return "";
 
   /* Check the cache and see if we last used this file.  */
@@ -596,39 +599,72 @@ rebuffer_line (file_info_type *  file,
     }
 
   /* Remember where we are in the current file.  */
-  pos = ftell (last_open_file);
+  pos2 = pos = ftell (last_open_file);
+  if (pos < 3)
+    return "";
+  current_line = file->linenum;
 
-  /* Go back to the beginning.  */
-  fseek (last_open_file, 0, SEEK_SET);
+  /* Leave room for the nul at the end of the buffer.  */
+  size -= 1;
+  buffer[size] = 0;
 
-  /* Skip lines prior to the one we are interested in.  */
-  while (current_line < linenum)
+  /* Increment the current line count by one.
+     This is to allow for the fact that we are searching for the
+     start of a previous line, but we do this by detecting end-of-line
+     character(s) not start-of-line characters.  */
+  ++ current_line;
+
+  while (pos2 > 0 && ! found)
     {
-      /* fgets only stops on newlines and has a size limit,
-        so we read one character at a time instead.  */
-      do
+      char * ptr;
+
+      /* Move backwards through the file, looking for earlier lines.  */
+      pos2 = (long) size > pos2 ? 0 : pos2 - size;
+      fseek (last_open_file, pos2, SEEK_SET);
+
+      /* Our caller has kindly provided us with a buffer, so we use it.  */
+      if (fread (buffer, 1, size, last_open_file) != size)
        {
-         c = fgetc (last_open_file);
+         as_warn (_("unable to rebuffer file: %s\n"), file->filename);
+         return "";
        }
-      while (c != EOF && c != '\n' && c != '\r');
 
-      ++ current_line;
-
-      if (c == '\r' || c == '\n')
+      for (ptr = buffer + size; ptr >= buffer; -- ptr)
        {
-         int next = fgetc (last_open_file);
+         if (*ptr == '\n')
+           {
+             -- current_line;
 
-         /* If '\r' is followed by '\n', swallow that.  Likewise, if '\n'
-            is followed by '\r', swallow that as well.  */
-         if ((c == '\r' && next != '\n')
-             || (c == '\n' && next != '\r'))
-           ungetc (next, last_open_file);
+             if (current_line == linenum)
+               {
+                 /* We have found the start of the line we seek.  */
+                 found = TRUE;
+
+                 /* FIXME: We could skip the read-in-the-line code
+                    below if we know that we already have the whole
+                    line in the buffer.  */
+
+                 /* Advance pos2 to the newline character we have just located.  */
+                 pos2 += (ptr - buffer);
+
+                 /* Skip the newline and, if present, the carriage return.  */
+                 if (ptr + 1 == buffer + size)
+                   {
+                     ++pos2;
+                     if (fgetc (last_open_file) == '\r')
+                       ++ pos2;
+                   }
+                 else
+                   pos2 += (ptr[1] == '\r' ? 2 : 1);
+
+                 /* Move the file pointer to this location.  */
+                 fseek (last_open_file, pos2, SEEK_SET);
+                 break;
+               }
+           }
        }
     }
 
-  /* Leave room for the nul at the end of the buffer.  */
-  size -= 1;
-
   /* Read in the line.  */
   c = fgetc (last_open_file);