]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/XPDFCore.cc
Fix for a number of integer overflow bugs discovered by Chris Evans.
[evince.git] / pdf / xpdf / XPDFCore.cc
index 9e359dae97f2d5d946d4f9387973c51c5de64b60..3b9c21e47e819d31b48e84bc41c1023201576fce 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <X11/keysym.h>
 #include <X11/cursorfont.h>
+#include <string.h>
 #include "gmem.h"
 #include "GString.h"
 #include "GList.h"
@@ -24,7 +25,8 @@
 #include "GfxState.h"
 #include "PSOutputDev.h"
 #include "TextOutputDev.h"
-#include "XPixmapOutputDev.h"
+#include "SplashPattern.h"
+#include "XSplashOutputDev.h"
 #include "XPDFCore.h"
 
 // these macro defns conflict with xpdf's Object class
 
 //------------------------------------------------------------------------
 
-static int zoomDPI[maxZoom - minZoom + 1] = {
-  29, 35, 42, 50, 60,
-  72,
-  86, 104, 124, 149, 179
-};
-
-//------------------------------------------------------------------------
-
 GString *XPDFCore::currentSelection = NULL;
 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
 Atom XPDFCore::targetsAtom;
@@ -108,9 +102,10 @@ Atom XPDFCore::targetsAtom;
 //------------------------------------------------------------------------
 
 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
-                  Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
-                  GBool installCmap, int rgbCubeSize) {
+                  SplashRGB8 paperColorA, GBool fullScreenA,
+                  GBool reverseVideo, GBool installCmap, int rgbCubeSize) {
   GString *initialZoom;
+  SplashColor paperColor2;
   int i;
 
   shell = shellA;
@@ -146,10 +141,8 @@ XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
     zoom = zoomWidth;
   } else {
     zoom = atoi(initialZoom->getCString());
-    if (zoom < minZoom) {
-      zoom = minZoom;
-    } else if (zoom > maxZoom) {
-      zoom = maxZoom;
+    if (zoom <= 0) {
+      zoom = defZoom;
     }
   }
   delete initialZoom;
@@ -186,8 +179,9 @@ XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
   initWindow();
 
   // create the OutputDev
-  out = new XPixmapOutputDev(display, screenNum, visual, colormap,
-                            reverseVideo, paperColor,
+  paperColor2.rgb8 = paperColor;
+  out = new XSplashOutputDev(display, screenNum, visual, colormap,
+                            reverseVideo, paperColor2,
                             installCmap, rgbCubeSize, gTrue,
                             &outputDevRedrawCbk, this);
   out->startDoc(NULL);
@@ -212,10 +206,6 @@ XPDFCore::~XPDFCore() {
       delete history[i].fileName;
     }
   }
-  if (selectGC) {
-    XFreeGC(display, selectGC);
-    XFreeGC(display, highlightGC);
-  }
   if (drawAreaGC) {
     XFreeGC(display, drawAreaGC);
   }
@@ -397,11 +387,11 @@ void XPDFCore::resizeToPage(int pg) {
       height1 = doc->getPageHeight(pg);
     }
     if (zoom == zoomPage || zoom == zoomWidth) {
-      width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
-      height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+      width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
+      height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
     } else {
-      width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
-      height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+      width = (Dimension)(width1 * 0.01 * zoom + 0.5);
+      height = (Dimension)(height1 * 0.01 * zoom + 0.5);
     }
     if (width > displayW - 100) {
       width = displayW - 100;
@@ -441,13 +431,12 @@ void XPDFCore::clear() {
   redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
 }
 
-void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
+void XPDFCore::displayPage(int pageA, double zoomA, int rotateA,
                           GBool scrollToTop, GBool addToHist) {
   double hDPI, vDPI;
   int rot;
   XPDFHistory *h;
   GBool newZoom;
-  XGCValues gcValues;
   time_t newModTime;
   int oldScrollX, oldScrollY;
 
@@ -476,12 +465,6 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
     modTime = newModTime;
   }
 
-  // free the old GCs
-  if (selectGC) {
-    XFreeGC(display, selectGC);
-    XFreeGC(display, highlightGC);
-  }
-
   // new page number
   page = pageA;
 
@@ -525,10 +508,9 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
       dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
     }
   } else {
-    dpi = zoomDPI[zoom - minZoom];
+    dpi = 0.01 * zoom * 72;
   }
-  out->setWindow(XtWindow(drawArea));
-  doc->displayPage(out, page, dpi, rotate, gTrue);
+  doc->displayPage(out, page, dpi, dpi, rotate, gTrue, gTrue);
   oldScrollX = scrollX;
   oldScrollY = scrollY;
   updateScrollBars();
@@ -536,15 +518,6 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
     redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
   }
 
-  // allocate new GCs
-  gcValues.foreground = BlackPixel(display, screenNum) ^
-                        WhitePixel(display, screenNum);
-  gcValues.function = GXxor;
-  selectGC = XCreateGC(display, out->getPixmap(),
-                      GCForeground | GCFunction, &gcValues);
-  highlightGC = XCreateGC(display, out->getPixmap(),
-                      GCForeground | GCFunction, &gcValues);
-
 
   // add to history
   if (addToHist) {
@@ -576,7 +549,7 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
   setCursor(None);
 }
 
-void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
+void XPDFCore::displayDest(LinkDest *dest, double zoomA, int rotateA,
                           GBool addToHist) {
   Ref pageRef;
   int pg;
@@ -660,7 +633,7 @@ void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
   }
   if (page > 1) {
     if (!fullScreen && bottom) {
-      scrollY = out->getPixmapHeight() - drawAreaHeight;
+      scrollY = out->getBitmapHeight() - drawAreaHeight;
       if (scrollY < 0) {
        scrollY = 0;
       }
@@ -685,7 +658,7 @@ void XPDFCore::goForward() {
   }
   --historyFLen;
   ++historyBLen;
-  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
     if (loadFile(history[historyCur].fileName) != errNone) {
       XBell(display, 0);
       return;
@@ -704,7 +677,7 @@ void XPDFCore::goBackward() {
   }
   --historyBLen;
   ++historyFLen;
-  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
     if (loadFile(history[historyCur].fileName) != errNone) {
       XBell(display, 0);
       return;
@@ -738,7 +711,7 @@ void XPDFCore::scrollPageUp() {
 }
 
 void XPDFCore::scrollPageDown() {
-  if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
+  if (scrollY >= out->getBitmapHeight() - drawAreaHeight) {
     gotoNextPage(1, gTrue);
   } else {
     scrollTo(scrollX, scrollY + drawAreaHeight);
@@ -751,7 +724,7 @@ void XPDFCore::scrollTo(int x, int y) {
 
   needRedraw = gFalse;
 
-  maxPos = out ? out->getPixmapWidth() : 1;
+  maxPos = out ? out->getBitmapWidth() : 1;
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
@@ -769,7 +742,7 @@ void XPDFCore::scrollTo(int x, int y) {
     needRedraw = gTrue;
   }
 
-  maxPos = out ? out->getPixmapHeight() : 1;
+  maxPos = out ? out->getBitmapHeight() : 1;
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
@@ -798,28 +771,26 @@ void XPDFCore::scrollTo(int x, int y) {
 
 void XPDFCore::setSelection(int newXMin, int newYMin,
                            int newXMax, int newYMax) {
-  Pixmap pixmap;
   int x, y;
   GBool needRedraw, needScroll;
   GBool moveLeft, moveRight, moveTop, moveBottom;
-
-  pixmap = out->getPixmap();
+  SplashColor xorColor;
 
 
   // erase old selection on off-screen bitmap
   needRedraw = gFalse;
   if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    XFillRectangle(display, pixmap,
-                  selectGC, selectXMin, selectYMin,
-                  selectXMax - selectXMin, selectYMax - selectYMin);
+    xorColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff);
+    out->xorRectangle(selectXMin, selectYMin, selectXMax, selectYMax,
+                     new SplashSolidColor(xorColor));
     needRedraw = gTrue;
   }
 
   // draw new selection on off-screen bitmap
   if (newXMin < newXMax && newYMin < newYMax) {
-    XFillRectangle(display, pixmap,
-                  selectGC, newXMin, newYMin,
-                  newXMax - newXMin, newYMax - newYMin);
+    xorColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff);
+    out->xorRectangle(newXMin, newYMin, newXMax, newYMax,
+                     new SplashSolidColor(xorColor));
     needRedraw = gTrue;
   }
 
@@ -907,13 +878,13 @@ void XPDFCore::moveSelection(int mx, int my) {
   // clip mouse coords
   if (mx < 0) {
     mx = 0;
-  } else if (mx >= out->getPixmapWidth()) {
-    mx = out->getPixmapWidth() - 1;
+  } else if (mx >= out->getBitmapWidth()) {
+    mx = out->getBitmapWidth() - 1;
   }
   if (my < 0) {
     my = 0;
-  } else if (my >= out->getPixmapHeight()) {
-    my = out->getPixmapHeight() - 1;
+  } else if (my >= out->getBitmapHeight()) {
+    my = out->getBitmapHeight() - 1;
   }
 
   // move appropriate edges of selection
@@ -1045,7 +1016,7 @@ GString *XPDFCore::extractText(int pageNum,
     delete textOut;
     return NULL;
   }
-  doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
+  doc->displayPage(textOut, pageNum, dpi, dpi, rotate, gTrue, gFalse);
   s = textOut->getText(xMin, yMin, xMax, yMax);
   delete textOut;
   return s;
@@ -1055,7 +1026,7 @@ GString *XPDFCore::extractText(int pageNum,
 // hyperlinks
 //------------------------------------------------------------------------
 
-void XPDFCore::doLink(int mx, int my) {
+GBool XPDFCore::doLink(int mx, int my) {
   double x, y;
   LinkAction *action;
 
@@ -1063,7 +1034,9 @@ void XPDFCore::doLink(int mx, int my) {
   out->cvtDevToUser(mx, my, &x, &y);
   if ((action = doc->findLink(x, y))) {
     doAction(action);
+    return gTrue;
   }
+  return gFalse;
 }
 
 void XPDFCore::doAction(LinkAction *action) {
@@ -1271,26 +1244,9 @@ void XPDFCore::doAction(LinkAction *action) {
 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
   GString *cmd;
   char *s;
-  int i;
 
   if ((s = strstr(cmdFmt->getCString(), "%s"))) {
-    cmd = arg->copy();
-    // filter out any quote marks (' or ") to avoid a potential
-    // security hole
-    i = 0;
-    while (i < cmd->getLength()) {
-      if (cmd->getChar(i) == '"') {
-       cmd->del(i);
-       cmd->insert(i, "%22");
-       i += 3;
-      } else if (cmd->getChar(i) == '\'') {
-       cmd->del(i);
-       cmd->insert(i, "%27");
-       i += 3;
-      } else {
-       ++i;
-      }
-    }
+    cmd = mungeURL(arg);
     cmd->insert(0, cmdFmt->getCString(),
                s - cmdFmt->getCString());
     cmd->append(s + 2);
@@ -1308,18 +1264,43 @@ void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
   delete cmd;
 }
 
+// Escape any characters in a URL which might cause problems when
+// calling system().
+GString *XPDFCore::mungeURL(GString *url) {
+  static char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                         "abcdefghijklmnopqrstuvwxyz"
+                         "0123456789"
+                         "-_.~/?:@&=+,#%";
+  GString *newURL;
+  char c;
+  char buf[4];
+  int i;
+
+  newURL = new GString();
+  for (i = 0; i < url->getLength(); ++i) {
+    c = url->getChar(i);
+    if (strchr(allowed, c)) {
+      newURL->append(c);
+    } else {
+      sprintf(buf, "%%%02x", c & 0xff);
+      newURL->append(buf);
+    }
+  }
+  return newURL;
+}
+
 
 //------------------------------------------------------------------------
 // find
 //------------------------------------------------------------------------
 
-void XPDFCore::find(char *s) {
+void XPDFCore::find(char *s, GBool next) {
   Unicode *u;
   TextOutputDev *textOut;
   int xMin, yMin, xMax, yMax;
   double xMin1, yMin1, xMax1, yMax1;
   int pg;
-  GBool top;
+  GBool startAtTop;
   int len, i;
 
   // check for zero-length string
@@ -1341,15 +1322,12 @@ void XPDFCore::find(char *s) {
 #endif
 
   // search current page starting at current selection or top of page
-  xMin = yMin = xMax = yMax = 0;
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    xMin = selectXMax;
-    yMin = (selectYMin + selectYMax) / 2;
-    top = gFalse;
-  } else {
-    top = gTrue;
-  }
-  if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+  startAtTop = !next && !(selectXMin < selectXMax && selectYMin < selectYMax);
+  xMin = selectXMin + 1;
+  yMin = selectYMin + 1;
+  xMax = yMax = 0;
+  if (out->findText(u, len, startAtTop, gTrue, next, gFalse,
+                   &xMin, &yMin, &xMax, &yMax)) {
     goto found;
   }
 
@@ -1360,8 +1338,8 @@ void XPDFCore::find(char *s) {
     goto done;
   }
   for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
-    doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(u, len, gTrue, gTrue,
+    doc->displayPage(textOut, pg, 72, 72, 0, gTrue, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
@@ -1369,8 +1347,8 @@ void XPDFCore::find(char *s) {
 
   // search previous pages
   for (pg = 1; pg < page; ++pg) {
-    doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(u, len, gTrue, gTrue,
+    doc->displayPage(textOut, pg, 72, 72, 0, gTrue, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
@@ -1378,10 +1356,12 @@ void XPDFCore::find(char *s) {
   delete textOut;
 
   // search current page ending at current selection
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
+  if (!startAtTop) {
+    xMin = yMin = 0;
     xMax = selectXMin;
-    yMax = (selectYMin + selectYMax) / 2;
-    if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
+    yMax = selectYMin;
+    if (out->findText(u, len, gTrue, gFalse, gFalse, next,
+                     &xMin, &yMin, &xMax, &yMax)) {
       goto found;
     }
   }
@@ -1394,7 +1374,8 @@ void XPDFCore::find(char *s) {
  foundPage:
   delete textOut;
   displayPage(pg, zoom, rotate, gTrue, gTrue);
-  if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+  if (!out->findText(u, len, gTrue, gTrue, gFalse, gFalse,
+                    &xMin, &yMin, &xMax, &yMax)) {
     // this can happen if coalescing is bad
     goto done;
   }
@@ -1490,7 +1471,6 @@ void XPDFCore::initWindow() {
   XtManageChild(drawAreaFrame);
   n = 0;
   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
-  XtSetArg(args[n], XmNbackground, paperColor); ++n;
   XtSetArg(args[n], XmNwidth, 700); ++n;
   XtSetArg(args[n], XmNheight, 500); ++n;
   drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
@@ -1509,8 +1489,6 @@ void XPDFCore::initWindow() {
 
   // can't create a GC until the window gets mapped
   drawAreaGC = NULL;
-  selectGC = NULL;
-  highlightGC = NULL;
 }
 
 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
@@ -1640,18 +1618,18 @@ void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
     } else if (data->event->xbutton.button == 5) { // mouse wheel down
       if (core->fullScreen ||
          core->scrollY >=
-           core->out->getPixmapHeight() - core->drawAreaHeight) {
+           core->out->getBitmapHeight() - core->drawAreaHeight) {
        core->gotoNextPage(1, gTrue);
       } else {
        core->scrollDown(1);
       }
-    } else if (data->event->xbutton.button == 6) { // second mouse wheel right
+    } else if (data->event->xbutton.button == 6) { // second mouse wheel left
       if (!core->fullScreen) {
-       core->scrollRight(1);
+       core->scrollLeft(1);
       }
-    } else if (data->event->xbutton.button == 7) { // second mouse wheel left
+    } else if (data->event->xbutton.button == 7) { // second mouse wheel right
       if (!core->fullScreen) {
-       core->scrollLeft(1);
+       core->scrollRight(1);
       }
     } else {
       if (*core->mouseCbk) {
@@ -1775,8 +1753,8 @@ void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
     if (modifiers & ControlMask) {
       displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
     } else if (!fullScreen) {
-      scrollTo(out->getPixmapWidth() - drawAreaWidth,
-              out->getPixmapHeight() - drawAreaHeight);
+      scrollTo(out->getBitmapWidth() - drawAreaWidth,
+              out->getBitmapHeight() - drawAreaHeight);
     }
     return;
   case XK_Page_Up:
@@ -1854,23 +1832,22 @@ void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
   }
 
   // draw white background past the edges of the document
-  if (x + w > out->getPixmapWidth()) {
+  if (x + w > out->getBitmapWidth()) {
     XFillRectangle(display, drawAreaWin, drawAreaGC,
-                  out->getPixmapWidth() - scrollX, y - scrollY,
-                  x + w - out->getPixmapWidth(), h);
-    w = out->getPixmapWidth() - x;
+                  out->getBitmapWidth() - scrollX, y - scrollY,
+                  x + w - out->getBitmapWidth(), h);
+    w = out->getBitmapWidth() - x;
   }
-  if (y + h > out->getPixmapHeight()) {
+  if (y + h > out->getBitmapHeight()) {
     XFillRectangle(display, drawAreaWin, drawAreaGC,
-                  x - scrollX, out->getPixmapHeight() - scrollY,
-                  w, y + h - out->getPixmapHeight());
-    h = out->getPixmapHeight() - y;
+                  x - scrollX, out->getBitmapHeight() - scrollY,
+                  w, y + h - out->getBitmapHeight());
+    h = out->getBitmapHeight() - y;
   }
 
-  // redraw (checking to see if pixmap has been allocated yet)
-  if (out->getPixmapWidth() > 0) {
-    XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
-             x, y, w, h, x - scrollX, y - scrollY);
+  // redraw
+  if (w >= 0 && h >= 0) {
+    out->redraw(x, y, drawAreaWin, drawAreaGC, x - scrollX, y - scrollY, w, h);
   }
 }
 
@@ -1879,7 +1856,7 @@ void XPDFCore::updateScrollBars() {
   int n;
   int maxPos;
 
-  maxPos = out ? out->getPixmapWidth() : 1;
+  maxPos = out ? out->getBitmapWidth() : 1;
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
@@ -1894,7 +1871,7 @@ void XPDFCore::updateScrollBars() {
   XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
   XtSetValues(hScrollBar, args, n);
 
-  maxPos = out ? out->getPixmapHeight() : 1;
+  maxPos = out ? out->getBitmapHeight() : 1;
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
@@ -1942,7 +1919,7 @@ void XPDFCore::doErrorDialog(char *title, GString *msg) {
 
 GBool XPDFCore::doDialog(int type, GBool hasCancel,
                         char *title, GString *msg) {
-  Widget dialog;
+  Widget dialog, scroll, text;
   XtAppContext appContext;
   Arg args[20];
   int n;
@@ -1954,11 +1931,31 @@ GBool XPDFCore::doDialog(int type, GBool hasCancel,
   XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
   s1 = XmStringCreateLocalized(title);
   XtSetArg(args[n], XmNdialogTitle, s1); ++n;
-  s2 = XmStringCreateLocalized(msg->getCString());
-  XtSetArg(args[n], XmNmessageString, s2); ++n;
+  s2 = NULL; // make gcc happy
+  if (msg->getLength() <= 80) {
+    s2 = XmStringCreateLocalized(msg->getCString());
+    XtSetArg(args[n], XmNmessageString, s2); ++n;
+  }
   dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
   XmStringFree(s1);
-  XmStringFree(s2);
+  if (msg->getLength() <= 80) {
+    XmStringFree(s2);
+  } else {
+    n = 0;
+    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
+    if (drawAreaWidth > 300) {
+      XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
+    }
+    scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
+    XtManageChild(scroll);
+    n = 0;
+    XtSetArg(args[n], XmNeditable, False); ++n;
+    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
+    XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
+    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
+    text = XmCreateText(scroll, "text", args, n);
+    XtManageChild(text);
+  }
   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
   XtAddCallback(dialog, XmNokCallback,
                &dialogOkCbk, (XtPointer)this);