texinfo-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[5703] avoid crashes when resizing very small


From: Gavin D. Smith
Subject: [5703] avoid crashes when resizing very small
Date: Mon, 07 Jul 2014 01:04:04 +0000

Revision: 5703
          http://svn.sv.gnu.org/viewvc/?view=rev&root=texinfo&revision=5703
Author:   gavin
Date:     2014-07-07 01:04:02 +0000 (Mon, 07 Jul 2014)
Log Message:
-----------
avoid crashes when resizing very small

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/info/display.c
    trunk/info/signals.c
    trunk/info/signals.h
    trunk/info/window.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog     2014-07-06 19:34:32 UTC (rev 5702)
+++ trunk/ChangeLog     2014-07-07 01:04:02 UTC (rev 5703)
@@ -1,3 +1,24 @@
+2014-07-07  Gavin Smith  <address@hidden>
+
+       info: Avoid crashes when resizing screen very small, based on report
+       from Samuel Marshall 2014-07-06.
+
+       * info/signals.c (signal_block_winch, signal_unblock_winch)
+       (sigwinch_block_count): New functions and variable.
+       (info_signal_proc) <SIGWINCH>: Don't call the saved signal handler.
+       Increment signwinch_block_count. Only unblock the SIGWINCH signal once
+       we have finished our business.
+       * info/display.c (display_clear_display, display_update_display)
+       (display_update_one_window):
+       Block SIGWINCH signals.
+       (display_node_text, display_update_one_window): Check that we are
+       accessing array representing display within its bounds.
+       * info/window.c (window_new_screen_size): Don't shrink a window below 1
+       line.  When screen height is very small, set the height of one window
+       left so that it will have the right height when the screen is bigger
+       again.
+       (echo_area_required): Removed.
+
 2014-07-06  Gavin Smith  <address@hidden>
 
        * info/dir.c (lookup_dir_entry): Free return value of get_dir_node.

Modified: trunk/info/display.c
===================================================================
--- trunk/info/display.c        2014-07-06 19:34:32 UTC (rev 5702)
+++ trunk/info/display.c        2014-07-07 01:04:02 UTC (rev 5703)
@@ -22,6 +22,7 @@
 #include "info.h"
 #include "display.h"
 #include "tag.h"
+#include "signal.h"
 
 extern int info_any_buffered_input_p (void); /* Found in session.c. */
 
@@ -50,12 +51,14 @@
 {
   register int i;
 
+  signal_block_winch ();
   for (i = 0; display[i]; i++)
     {
       display[i]->text[0] = '\0';
       display[i]->textlen = 0;
       display[i]->inverse = 0;
     }
+  signal_unblock_winch ();
 }
 
 /* Non-zero if we didn't completely redisplay a window. */
@@ -68,6 +71,10 @@
 {
   register WINDOW *win;
 
+  /* Block window resize signals (SIGWINCH) while accessing the the_display
+     object, because the signal handler may reallocate it out from under our
+     feet. */
+  signal_block_winch ();
   display_was_interrupted_p = 0;
 
   /* For every window in the list, check contents against the display. */
@@ -86,6 +93,7 @@
 
   /* Always update the echo area. */
   display_update_one_window (the_echo_area);
+  signal_unblock_winch ();
 }
 
 static int
@@ -114,8 +122,14 @@
                    size_t pl_bytes, size_t pl_chars)
 {
   DISPLAY_LINE **display = the_display;
-  DISPLAY_LINE *entry = display[win->first_row + pl_num];
+  DISPLAY_LINE *entry;
 
+  /* This might happen if the screen was resized very small. */
+  if (win->first_row + pl_num > screenheight)
+    return 0;
+
+ entry = display[win->first_row + pl_num];
+
   /* We have the exact line as it should appear on the screen.
      Check to see if this line matches the one already appearing
      on the screen. */
@@ -202,6 +216,8 @@
   size_t line_index = 0;
   DISPLAY_LINE **display = the_display;
 
+  signal_block_winch ();
+
   /* If display is inhibited, that counts as an interrupted display. */
   if (display_inhibited)
     display_was_interrupted_p = 1;
@@ -213,14 +229,14 @@
      since historically this has often been the culprit for crashes, do
      our best to be doubly safe.  */
   if (win->height <= 0 || win->width <= 0 || display_inhibited)
-    return;
+    goto funexit;
 
   /* If the window's first row doesn't appear in the_screen, then it
      cannot be displayed.  This can happen when the_echo_area is the
      window to be displayed, and the screen has shrunk to less than one
      line. */
   if ((win->first_row < 0) || (win->first_row > the_screen->height))
-    return;
+    goto funexit;
 
   if (win->node && win->line_starts)
     {
@@ -229,7 +245,7 @@
                                         + win->line_starts[win->pagetop],
                                      display_node_text);
       if (display_was_interrupted_p)
-       return;
+       goto funexit;
     }
 
   /* We have reached the end of the node or the end of the window.  If it
@@ -260,8 +276,10 @@
 
       /* This display line must both be in inverse, and have the same
          contents. */
-      if ((!display[line_index]->inverse) ||
-          (strcmp (display[line_index]->text, win->modeline) != 0))
+      if ((!display[line_index]->inverse
+           || (strcmp (display[line_index]->text, win->modeline) != 0))
+          /* Check screen isn't very small. */
+          && line_index < the_screen->height)
         {
           terminal_goto_xy (0, line_index);
           terminal_begin_inverse ();
@@ -277,6 +295,8 @@
 
   /* Okay, this window doesn't need updating anymore. */
   win->flags &= ~W_UpdateWindow;
+funexit:
+  signal_unblock_winch ();
 }
 
 /* Scroll the region of the_display starting at START, ending at END, and

Modified: trunk/info/signals.c
===================================================================
--- trunk/info/signals.c        2014-07-06 19:34:32 UTC (rev 5702)
+++ trunk/info/signals.c        2014-07-07 01:04:02 UTC (rev 5703)
@@ -177,6 +177,29 @@
   redisplay_after_signal ();
 }
 
+/* Number of times we were told to ignore SIGWINCH. */
+static sigwinch_block_count = 0;
+
+void
+signal_block_winch (void)
+{
+#if defined (SIGWINCH)
+  if (sigwinch_block_count == 0)
+    BLOCK_SIGNAL (SIGWINCH);
+  sigwinch_block_count++;
+#endif
+}
+
+void
+signal_unblock_winch (void)
+{
+#if defined (SIGWINCH)
+  sigwinch_block_count--;
+  if (sigwinch_block_count == 0)
+    UNBLOCK_SIGNAL (SIGWINCH);
+#endif
+}
+
 static RETSIGTYPE
 info_signal_proc (int sig)
 {
@@ -268,14 +291,31 @@
        terminal_goto_xy (0, 0);
        fflush (stdout);
        terminal_unprep_terminal (); /* needless? */
+
+        /* This seems risky: what if we receive a (real) signal before
+           the next line is reached? */
+#if 0
        restore_termsig (sig, old_signal_handler);
-       UNBLOCK_SIGNAL (sig);
        kill (getpid (), sig);
+#endif
 
        /* After our old signal handler returns... */
        set_termsig (sig, old_signal_handler); /* needless? */
+
+        if (sigwinch_block_count != 0)
+          abort ();
+
+        /* Avoid any of the code unblocking the signal too early.  This
+           should set the variable to 1 because we shouldn't be here if
+           sigwinch_block_count > 0. */
+        sigwinch_block_count++;
+
        terminal_prep_terminal ();
        reset_info_window_sizes ();
+
+        sigwinch_block_count--;
+        /* Don't unblock the signal until after we've finished. */
+       UNBLOCK_SIGNAL (sig);
       }
       break;
 #endif /* SIGWINCH || SIGUSR1 */
@@ -293,4 +333,6 @@
 #endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */
 #endif /* !HAVE_SIGACTION */
 }
+
+
 /* vim: set sw=2 cino={1s>2sn-s^-se-s: */

Modified: trunk/info/signals.h
===================================================================
--- trunk/info/signals.h        2014-07-06 19:34:32 UTC (rev 5702)
+++ trunk/info/signals.h        2014-07-07 01:04:02 UTC (rev 5703)
@@ -25,6 +25,9 @@
 #include <sys/types.h>
 #include <signal.h>
 
+void signal_block_winch (void);
+void signal_unblock_winch (void);
+
 /* For sysV68 address@hidden  */
 #if !defined (SIGCHLD) && defined (SIGCLD)
 #define SIGCHLD SIGCLD

Modified: trunk/info/window.c
===================================================================
--- trunk/info/window.c 2014-07-06 19:34:32 UTC (rev 5702)
+++ trunk/info/window.c 2014-07-07 01:04:02 UTC (rev 5703)
@@ -44,10 +44,6 @@
    size of the screen. */
 #define ECHO_AREA_HEIGHT 1
 
-/* Macro returns the amount of space that the echo area truly requires relative
-   to the entire screen. */
-#define echo_area_required (1 + the_echo_area->height)
-
 /* Show malformed multibyte sequences */
 int show_malformed_multibyte_p = 0;
 
@@ -98,33 +94,49 @@
   if (width == the_screen->width && height == the_screen->height)
     return;
 
-  /* If the new window height is too small, make it be zero. */
-  if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
-    height = 0;
-  if (width < 0)
-    width = 0;
+  /* The screen has changed height and width. */
+  delta_height = height - the_screen->height;
+  the_screen->height = height;
+  the_screen->width = width;
 
-  /* Find out how many windows will change. */
-  for (numwins = 0, win = windows; win; win = win->next, numwins++);
+  /* Set the start of the echo area. */
+  the_echo_area->first_row = height - the_echo_area->height;
+  the_echo_area->width = width;
 
+  /* Count number of windows. */
+  numwins = 0;
+  for (win = windows; win; win = win->next)
+    numwins++;
+
+  if (numwins == 0)
+    return; /* There is nothing to do. */
+
+  /* Divide the change in height among the available windows.  This
+     doesn't work very well because if we are getting a resize signal
+     every time the screen resizes by 1 line, delta_height will always
+     be 1. */
+  delta_each = delta_height / numwins;
+  delta_leftover = delta_height - (delta_each * numwins);
+
   /* See if some windows will need to be deleted.  This is the case if
      the screen is getting smaller, and the available space divided by
      the number of windows is less than WINDOW_MIN_SIZE.  In that case,
      delete some windows and try again until there is either enough
      space to divy up among the windows, or until there is only one
      window left. */
-  while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
+  while (height - 1 <= WINDOW_MIN_SIZE * numwins)
     {
-      /* If only one window, make the size of it be zero, and return
-         immediately. */
+      /* If only one window left, give up. */
       if (!windows->next)
         {
-          windows->height = 0;
-          free (windows->line_starts);
-         free (windows->log_line_no);
-          windows->line_starts = NULL;
-          windows->line_count = 0;
-          break;
+          /* Keep track of the height so that when the screen gets bigger
+             again, it can be resized properly.  The -2 is for the window
+             information bar and the echo area. */
+          windows->height = height - 2;
+          windows->width = width;
+          free (windows->modeline);
+          windows->modeline = xmalloc (1 + width);
+          return;
         }
 
       /* If we have some temporary windows, delete one of them. */
@@ -137,28 +149,10 @@
         win = windows;
 
       forget_window_and_nodes (win);
-
       window_delete_window (win);
       numwins--;
     }
 
-  /* The screen has changed height and width. */
-  delta_height = height - the_screen->height;   /* This is how much. */
-  the_screen->height = height;                  /* This is the new height. */
-  the_screen->width = width;                    /* This is the new width. */
-
-  /* Set the start of the echo area. */
-  the_echo_area->first_row = height - the_echo_area->height;
-  the_echo_area->width = width;
-
-  /* Check to see if the screen can really be changed this way. */
-  if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
-    return;
-
-  /* Divide the change in height among the available windows. */
-  delta_each = delta_height / numwins;
-  delta_leftover = delta_height - (delta_each * numwins);
-
   /* Change the height of each window in the chain by delta_each.  Change
      the height of the last window in the chain by delta_each and by the
      leftover amount of change.  Change the width of each window to be
@@ -172,26 +166,25 @@
           win->modeline = xmalloc (1 + width);
         }
 
-      win->height += delta_each;
+      /* Don't resize a window to be smaller than one line. */
+      if (win->height + delta_each >= 1)
+        win->height += delta_each;
+      else
+        delta_leftover += delta_each;
 
-      /* If the previous height of this window was zero, it was the only
-         window, and it was not visible.  Thus we need to compensate for
-         the echo_area. */
-      if (win->height == delta_each)
-        win->height -= (1 + the_echo_area->height);
+      /* Try to use up the extra space. */
+      if (delta_leftover != 0 && win->height + delta_leftover >= 1)
+        {
+          win->height += delta_leftover;
+          delta_leftover = 0;
+        }
 
-      /* If this is not the first window in the chain, then change the
-         first row of it.  We cannot just add delta_each to the first row,
-         since this window's first row is the sum of the collective increases
-         that have gone before it.  So we just add one to the location of the
+      /* If this is not the first window in the chain, set the
+         first row of it by adding one to the location of the
          previous window's modeline. */
       if (win->prev)
         win->first_row = (win->prev->first_row + win->prev->height) + 1;
 
-      /* The last window in the chain gets the extra space (or shrinkage). */
-      if (!win->next)
-        win->height += delta_leftover;
-
       if (win->node)
         recalculate_line_starts (win);
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]