Blame SOURCES/fltk-1.3.4-improve-x11-selection-data-transfer-protocol.patch

694158
From 7ed06a7b65c6f91da50b6664c742208ce520a983 Mon Sep 17 00:00:00 2001
694158
From: Albrecht Schlosser <albrechts.fltk@online.de>
694158
Date: Sat, 2 Jul 2022 16:08:20 +0200
694158
Subject: [PATCH] Improve X11 selection data transfer (INCR) protocol (issue
694158
 451)
694158
694158
This improves reading large selections and fixes one more bug.
694158
694158
Backported from 'master' (fltk 1.4.0). For more information see
694158
  commit c5556291624eec58ed9de186474dfcc858dca691,
694158
  commit ef72df0dc7c3c48373c52085a855ac6ce6df4868 and issue 451
694158
  https://github.com/fltk/fltk/issues/451
694158
---
694158
 src/Fl_x.cxx | 64 ++++++++++++++++++++++++++++++++++++++++------------
694158
 1 file changed, 49 insertions(+), 15 deletions(-)
694158
694158
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
694158
index c8b9c06..a3ec6f2 100644
694158
--- a/src/Fl_x.cxx
694158
+++ b/src/Fl_x.cxx
694158
@@ -1275,13 +1275,25 @@ static bool getNextEvent(XEvent *event_return)
694158
 
694158
 static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lower_bound) {
694158
   // fprintf(stderr,"Incremental transfer starting due to INCR property\n");
694158
+  // fprintf(stderr, "[getIncrData:%d] lower_bound [in ] =%10ld\n", __LINE__, lower_bound);
694158
+  const size_t alloc_min =   4 * 1024 * 1024; // min. initial allocation
694158
+  const size_t alloc_max = 200 * 1024 * 1024; // max. initial allocation
694158
+  const size_t alloc_inc =   4 * 1024 * 1024; // (min.) increase if necessary
694158
   size_t total = 0;
694158
+  size_t data_size = lower_bound + 1;
694158
+  if (data_size < alloc_min) {
694158
+    data_size = alloc_min;
694158
+  } else if (data_size > alloc_max) {
694158
+    data_size = alloc_max;
694158
+  }
694158
+  // fprintf(stderr, "[getIncrData:%d] initial alloc.    =%10ld\n", __LINE__, data_size);
694158
+
694158
   XEvent event;
694158
   XDeleteProperty(fl_display, selevent.requestor, selevent.property);
694158
-  data = (uchar*)realloc(data, lower_bound);
694158
+  data = (uchar*)realloc(data, data_size);
694158
   if (!data) {
694158
-    // fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, lower_bound);
694158
-    Fl::fatal("Clipboard data transfer failed, size %ld too large.", lower_bound);
694158
+    // fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
694158
+    Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
694158
   }
694158
   for (;;) {
694158
     if (!getNextEvent(&event)) {
694158
@@ -1297,20 +1309,24 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lo
694158
       unsigned long bytes_after;
694158
       unsigned char* prop = 0;
694158
       long offset = 0;
694158
-      size_t num_bytes;
694158
+      size_t num_bytes = 0;
694158
       //size_t slice_size = 0;
694158
-      do
694158
-      {
694158
+      do {
694158
 	XGetWindowProperty(fl_display, selevent.requestor, selevent.property, offset, 70000, True,
694158
 			   AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
694158
 	num_bytes = nitems * (actual_format / 8);
694158
 	offset += num_bytes/4;
694158
 	// slice_size += num_bytes;
694158
-        if (total + num_bytes > lower_bound) {
694158
-	  data = (uchar*)realloc(data, total + num_bytes);
694158
+        if (total + num_bytes + bytes_after + 1 > data_size) {
694158
+          data_size += alloc_inc;
694158
+          if (total + num_bytes + bytes_after + 1 > data_size)
694158
+            data_size = total + num_bytes + bytes_after + 1;
694158
+          // printf(" -- realloc(%9ld), total=%10ld, num_bytes=%7ld, bytes_after=%7ld (%7ld), required=%10ld\n",
694158
+          //        data_size, total, num_bytes, bytes_after, num_bytes + bytes_after, total + num_bytes + bytes_after + 1);
694158
+          data = (uchar*)realloc(data, data_size);
694158
           if (!data) {
694158
-            // fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, total + num_bytes);
694158
-            Fl::fatal("Clipboard data transfer failed, size %ld too large.", total + num_bytes);
694158
+            // fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
694158
+            Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
694158
 	  }
694158
 	}
694158
         memcpy(data + total, prop, num_bytes);
694158
@@ -1340,6 +1356,7 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lo
694158
     }
694158
   }
694158
   XDeleteProperty(fl_display, selevent.requestor, selevent.property);
694158
+  // fprintf(stderr, "[getIncrData:%d] total data  [out] =%10ld\n", __LINE__, (long)total);
694158
   return (long)total;
694158
 }
694158
 
694158
@@ -1488,20 +1505,37 @@ fprintf(stderr,"\n");*/
694158
 	return true;
694158
       }
694158
 	if (actual == fl_INCR) {
694158
-	  // an X11 "integer" (32 bit), the "lower bound" of the clipboard size (see ICCCM)
694158
-	  size_t lower_bound = (*(unsigned long *)portion) & 0xFFFFFFFF;
694158
-	  // fprintf(stderr, "[fl_handle:%d] INCR: lower_bound = %ld\n", __LINE__, lower_bound);
694158
+	  // From ICCCM: "The contents of the INCR property will be an integer, which
694158
+	  // represents a lower bound on the number of bytes of data in the selection."
694158
+	  //
694158
+	  // However, some X clients don't set the integer ("lower bound") in the INCR
694158
+	  // property, hence 'count' below is zero and we must not access '*portion'.
694158
+	  // Debug:
694158
+#if (0)
694158
+	  fprintf(stderr,
694158
+	        "[fl_handle(SelectionNotify/INCR):%d] actual=%ld (INCR), format=%d, count=%ld, remaining=%ld",
694158
+	        __LINE__, actual, format, count, remaining);
694158
+	  if (portion && count > 0) {
694158
+	  fprintf(stderr,
694158
+	          ", portion=%p (%ld)", portion, *(long*)portion);
694158
+	  }
694158
+	  fprintf(stderr, "\n");
694158
+#endif
694158
+	  size_t lower_bound = 0;
694158
+	  if (portion && count > 0) {
694158
+	    lower_bound = *(unsigned long *)portion;
694158
+	  }
694158
 	  bytesread = getIncrData(sn_buffer, xevent.xselection, lower_bound);
694158
 	  XFree(portion);
694158
 	  break;
694158
 	}
694158
 	// Make sure we got something sane...
694158
       if ((portion == NULL) || (format != 8) || (count == 0)) {
694158
-	if (portion) { XFree(portion); portion = 0; }
694158
+	if (portion) XFree(portion);
694158
         return true;
694158
       }
694158
       sn_buffer = (unsigned char*)realloc(sn_buffer, bytesread+count+remaining+1);
694158
-      memcpy(sn_buffer+bytesread, portion, count);
694158
+      memcpy(sn_buffer + bytesread, portion, count);
694158
       if (portion) { XFree(portion); portion = 0; }
694158
       bytesread += count;
694158
       // Cannot trust data to be null terminated