security fix: replaced sprintf with snprintf
[xboard.git] / xboard.c
index d6dced0..e9925ae 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -2,7 +2,7 @@
  * xboard.c -- X front end for XBoard
  *
  * Copyright 1991 by Digital Equipment Corporation, Maynard,
- * Massachusetts. 
+ * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
@@ -233,6 +233,8 @@ typedef struct {
 } Menu;
 
 int main P((int argc, char **argv));
+FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
+               char *init_path, char *mode, int (*show_entry)(), char **name_return));
 RETSIGTYPE CmailSigHandler P((int sig));
 RETSIGTYPE IntSigHandler P((int sig));
 RETSIGTYPE TermSizeSigHandler P((int sig));
@@ -260,6 +262,10 @@ void AnimateUserMove P((Widget w, XEvent * event,
                     String * params, Cardinal * nParams));
 void HandlePV P((Widget w, XEvent * event,
                     String * params, Cardinal * nParams));
+void SelectPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
+void StopPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
 void WhiteClock P((Widget w, XEvent *event,
                   String *prms, Cardinal *nprms));
 void BlackClock P((Widget w, XEvent *event,
@@ -268,6 +274,8 @@ void DrawPositionProc P((Widget w, XEvent *event,
                     String *prms, Cardinal *nprms));
 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
                     Board board));
+void CommentClick P((Widget w, XEvent * event,
+                  String * params, Cardinal * nParams));
 void CommentPopUp P((char *title, char *label));
 void CommentPopDown P((void));
 void CommentCallback P((Widget w, XtPointer client_data,
@@ -361,11 +369,13 @@ void StopObservingProc P((Widget w, XEvent *event, String *prms,
                          Cardinal *nprms));
 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
                          Cardinal *nprms));
+void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
                         Cardinal *nprms));
 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
@@ -478,6 +488,8 @@ Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
   ICSInputShell, fileNameShell, askQuestionShell;
 Widget historyShell, evalGraphShell, gameListShell;
+int hOffset; // [HGM] dual
+XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
 Font clockFontID, coordFontID, countFontID;
@@ -640,6 +652,7 @@ MenuItem actionMenu[] = {
     {"----", NothingProc},
     {N_("Stop Observing"), StopObservingProc},
     {N_("Stop Examining"), StopExaminingProc},
+    {N_("Upload to Examine"), UploadProc},
     {"----", NothingProc},
     {N_("Adjudicate to White"), AdjuWhiteProc},
     {N_("Adjudicate to Black"), AdjuBlackProc},
@@ -653,6 +666,7 @@ MenuItem stepMenu[] = {
     {N_("Back to Start"), ToStartProc},
     {N_("Forward to End"), ToEndProc},
     {N_("Revert"), RevertProc},
+    {N_("Annotate"), AnnotateProc},
     {N_("Truncate Game"), TruncateGameProc},
     {"----", NothingProc},
     {N_("Move Now"), MoveNowProc},
@@ -738,23 +752,23 @@ MenuItem buttonBar[] = {
 #define PIECE_MENU_SIZE 18
 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
-      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
+      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
       N_("Empty square"), N_("Clear board") },
     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
-      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
+      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
       N_("Empty square"), N_("Clear board") }
 };
 /* must be in same order as PieceMenuStrings! */
 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
        WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
-       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
+       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
        BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
-       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
+       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
 };
 
@@ -842,7 +856,8 @@ XtActionsRec boardActions[] = {
     { "HandleUserMove", HandleUserMove },
     { "AnimateUserMove", AnimateUserMove },
     { "HandlePV", HandlePV },
-    { "UnLoadPV", UnLoadPV },
+    { "SelectPV", SelectPV },
+    { "StopPV", StopPV },
     { "FileNameAction", FileNameAction },
     { "AskQuestionProc", AskQuestionProc },
     { "AskQuestionReplyAction", AskQuestionReplyAction },
@@ -851,6 +866,7 @@ XtActionsRec boardActions[] = {
     { "BlackClock", BlackClock },
     { "Iconify", Iconify },
     { "ResetProc", ResetProc },
+    { "NewVariantProc", NewVariantProc },
     { "LoadGameProc", LoadGameProc },
     { "LoadNextGameProc", LoadNextGameProc },
     { "LoadPrevGameProc", LoadPrevGameProc },
@@ -904,14 +920,19 @@ XtActionsRec boardActions[] = {
     { "DownKeyProc", DownKeyProc },
     { "StopObservingProc", StopObservingProc },
     { "StopExaminingProc", StopExaminingProc },
+    { "UploadProc", UploadProc },
     { "BackwardProc", BackwardProc },
     { "ForwardProc", ForwardProc },
     { "ToStartProc", ToStartProc },
     { "ToEndProc", ToEndProc },
     { "RevertProc", RevertProc },
+    { "AnnotateProc", AnnotateProc },
     { "TruncateGameProc", TruncateGameProc },
     { "MoveNowProc", MoveNowProc },
     { "RetractMoveProc", RetractMoveProc },
+    { "EngineMenuProc", (XtActionProc) EngineMenuProc },
+    { "UciMenuProc", (XtActionProc) UciMenuProc },
+    { "TimeControlProc", (XtActionProc) TimeControlProc },
     { "AlwaysQueenProc", AlwaysQueenProc },
     { "AnimateDraggingProc", AnimateDraggingProc },
     { "AnimateMovingProc", AnimateMovingProc },
@@ -951,6 +972,7 @@ XtActionsRec boardActions[] = {
     { "AboutProc", AboutProc },
     { "DebugProc", DebugProc },
     { "NothingProc", NothingProc },
+    { "CommentClick", (XtActionProc) CommentClick },
     { "CommentPopDown", (XtActionProc) CommentPopDown },
     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
     { "TagsPopDown", (XtActionProc) TagsPopDown },
@@ -974,26 +996,64 @@ XtActionsRec boardActions[] = {
 };
 
 char globalTranslations[] =
-  ":<Key>R: ResignProc() \n \
-   :<Key>r: ResetProc() \n \
-   :<Key>g: LoadGameProc() \n \
-   :<Key>N: LoadNextGameProc() \n \
-   :<Key>P: LoadPrevGameProc() \n \
-   :<Key>Q: QuitProc() \n \
-   :<Key>F: ToEndProc() \n \
-   :<Key>f: ForwardProc() \n \
-   :<Key>B: ToStartProc() \n \
-   :<Key>b: BackwardProc() \n \
-   :<Key>p: PauseProc() \n \
-   :<Key>d: DrawProc() \n \
-   :<Key>t: CallFlagProc() \n \
-   :<Key>i: Iconify() \n \
-   :<Key>c: Iconify() \n \
-   :<Key>v: FlipViewProc() \n \
-   <KeyDown>Control_L: BackwardProc() \n \
-   <KeyUp>Control_L: ForwardProc() \n \
-   <KeyDown>Control_R: BackwardProc() \n \
-   <KeyUp>Control_R: ForwardProc() \n \
+  ":<Key>F9: ResignProc() \n \
+   :Ctrl<Key>n: ResetProc() \n \
+   :Meta<Key>V: NewVariantProc() \n \
+   :Ctrl<Key>o: LoadGameProc() \n \
+   :Meta<Key>Next: LoadNextGameProc() \n \
+   :Meta<Key>Prior: LoadPrevGameProc() \n \
+   :Ctrl<Key>s: SaveGameProc() \n \
+   :Ctrl<Key>c: CopyGameProc() \n \
+   :Ctrl<Key>v: PasteGameProc() \n \
+   :Ctrl<Key>O: LoadPositionProc() \n \
+   :Shift Meta<Key>Next: LoadNextPositionProc() \n \
+   :Shift Meta<Key>Prior: LoadPrevPositionProc() \n \
+   :Ctrl<Key>S: SavePositionProc() \n \
+   :Ctrl<Key>C: CopyPositionProc() \n \
+   :Ctrl<Key>V: PastePositionProc() \n \
+   :Ctrl<Key>q: QuitProc() \n \
+   :Ctrl<Key>w: MachineWhiteProc() \n \
+   :Ctrl<Key>b: MachineBlackProc() \n \
+   :Ctrl<Key>t: TwoMachinesProc() \n \
+   :Ctrl<Key>a: AnalysisModeProc() \n \
+   :Ctrl<Key>f: AnalyzeFileProc() \n \
+   :Ctrl<Key>e: EditGameProc() \n \
+   :Ctrl<Key>E: EditPositionProc() \n \
+   :Meta<Key>O: EngineOutputProc() \n \
+   :Meta<Key>E: EvalGraphProc() \n \
+   :Meta<Key>G: ShowGameListProc() \n \
+   :Meta<Key>H: ShowMoveListProc() \n \
+   :<Key>Pause: PauseProc() \n \
+   :<Key>F3: AcceptProc() \n \
+   :<Key>F4: DeclineProc() \n \
+   :<Key>F12: RematchProc() \n \
+   :<Key>F5: CallFlagProc() \n \
+   :<Key>F6: DrawProc() \n \
+   :<Key>F7: AdjournProc() \n \
+   :<Key>F8: AbortProc() \n \
+   :<Key>F10: StopObservingProc() \n \
+   :<Key>F11: StopExaminingProc() \n \
+   :Meta Ctrl<Key>F12: DebugProc() \n \
+   :Meta<Key>End: ToEndProc() \n \
+   :Meta<Key>Right: ForwardProc() \n \
+   :Meta<Key>Home: ToStartProc() \n \
+   :Meta<Key>Left: BackwardProc() \n \
+   :Ctrl<Key>m: MoveNowProc() \n \
+   :Ctrl<Key>x: RetractMoveProc() \n \
+   :Meta<Key>J: EngineMenuProc() \n \
+   :Meta<Key>U: UciMenuProc() \n \
+   :Meta<Key>T: TimeControlProc() \n \
+   :Ctrl<Key>Q: AlwaysQueenProc() \n \
+   :Ctrl<Key>F: AutoflagProc() \n \
+   :Ctrl<Key>A: AnimateMovingProc() \n \
+   :Ctrl<Key>P: PonderNextMoveProc() \n \
+   :Ctrl<Key>L: TestLegalityProc() \n \
+   :Ctrl<Key>H: HideThinkingProc() \n \
+   :<Key>-: Iconify() \n \
+   :<Key>F1: ManProc() \n \
+   :<Key>F2: FlipViewProc() \n \
+   <KeyDown>.: BackwardProc() \n \
+   <KeyUp>.: ForwardProc() \n \
    Shift<Key>1: AskQuestionProc(\"Direct command\",\
                                 \"Send to chess program:\",,1) \n \
    Shift<Key>2: AskQuestionProc(\"Direct command\",\
@@ -1022,6 +1082,10 @@ char ICSInputTranslations[] =
     "<Key>Down: DownKeyProc() \n "
     "<Key>Return: EnterKeyProc() \n";
 
+// [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
+//             as the widget is destroyed before the up-click can call extend-end
+char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
+
 String xboardResources[] = {
     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
@@ -1289,10 +1353,10 @@ extern char *crWhite, * crBlack;
 
 void *
 colorVariable[] = {
-  &appData.whitePieceColor, 
-  &appData.blackPieceColor, 
+  &appData.whitePieceColor,
+  &appData.blackPieceColor,
   &appData.lightSquareColor,
-  &appData.darkSquareColor, 
+  &appData.darkSquareColor,
   &appData.highlightSquareColor,
   &appData.premoveHighlightColor,
   &appData.lowTimeWarningColor,
@@ -1362,7 +1426,7 @@ ParseColor(int n, char *name)
 
 void
 ParseTextAttribs(ColorClass cc, char *s)
-{   
+{
     (&appData.colorShout)[cc] = strdup(s);
 }
 
@@ -1408,7 +1472,7 @@ SaveFontArg(FILE *f, ArgDescriptor *ad)
        break;
   }
   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
-    fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]); 
+    fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
 }
 
 void
@@ -1451,7 +1515,7 @@ GetActualPlacement(Widget wg, WindowPlacement *wp)
   int i;
 
   if(!wg) return;
-  
+
     i = 0;
     XtSetArg(args[i], XtNx, &x); i++;
     XtSetArg(args[i], XtNy, &y); i++;
@@ -1488,7 +1552,9 @@ int
 MySearchPath(char *installDir, char *name, char *fullname)
 { // just append installDir and name. Perhaps ExpandPath should be used here?
   name = ExpandPathName(name);
-  if(name && name[0] == '/') strcpy(fullname, name); else {
+  if(name && name[0] == '/')
+    safeStrCpy(fullname, name, MSG_SIZ );
+  else {
     sprintf(fullname, "%s%c%s", installDir, '/', name);
   }
   return 1;
@@ -1498,7 +1564,7 @@ int
 MyGetFullPathName(char *name, char *fullname)
 { // should use ExpandPath?
   name = ExpandPathName(name);
-  strcpy(fullname, name);
+  safeStrCpy(fullname, name, MSG_SIZ );
   return 1;
 }
 
@@ -1528,8 +1594,9 @@ ConvertToLine(int argc, char **argv)
   for(i=1; i<argc; i++) {
     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
        && argv[i][0] != '{' )
-         sprintf(buf, "{%s} ", argv[i]);
-    else sprintf(buf, "%s ", argv[i]);
+      snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
+    else
+      snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
     strcat(line, buf);
   }
     line[strlen(line)-1] = NULLCHAR;
@@ -1538,6 +1605,8 @@ ConvertToLine(int argc, char **argv)
 
 //--------------------------------------------------------------------------------------------
 
+extern Boolean twoBoards, partnerUp;
+
 #ifdef IDSIZES
   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
 #else
@@ -1558,7 +1627,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     shellArgs[1].value = (XtArgVal) &h;
     XtGetValues(shellWidget, shellArgs, 2);
 
-    shellArgs[4].value = 2*w; shellArgs[2].value = 10;
+    shellArgs[4].value = 3*w; shellArgs[2].value = 10;
     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
     XtSetValues(shellWidget, &shellArgs[2], 4);
 
@@ -1568,6 +1637,12 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
     CreateGrid();
+    hOffset = boardWidth + 10;
+    for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
+       secondSegments[i] = gridSegments[i];
+       secondSegments[i].x1 += hOffset;
+       secondSegments[i].x2 += hOffset;
+    }
 
     XtSetArg(args[0], XtNwidth, boardWidth);
     XtSetArg(args[1], XtNheight, boardHeight);
@@ -1606,7 +1681,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     /*
      * Inhibit shell resizing.
      */
-    shellArgs[0].value = w = (XtArgVal) boardWidth + marginW;
+    shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
     shellArgs[4].value = shellArgs[2].value = w;
     shellArgs[5].value = shellArgs[3].value = h;
@@ -1674,19 +1749,6 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
 }
 #endif
 
-void EscapeExpand(char *p, char *q)
-{      // [HGM] initstring: routine to shape up string arguments
-       while(*p++ = *q++) if(p[-1] == '\\')
-           switch(*q++) {
-               case 'n': p[-1] = '\n'; break;
-               case 'r': p[-1] = '\r'; break;
-               case 't': p[-1] = '\t'; break;
-               case '\\': p[-1] = '\\'; break;
-               case 0: *p = 0; return;
-               default: p[-1] = q[-1]; break;
-           }
-}
-
 int
 main(argc, argv)
      int argc;
@@ -1992,7 +2054,7 @@ XBoard square size (hint): %d\n\
     if (forceMono) {
       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
              programName);
-      
+
       if (appData.bitmapDirectory == NULL ||
              appData.bitmapDirectory[0] == NULLCHAR)
            appData.bitmapDirectory = DEF_BITMAP_DIR;
@@ -2002,7 +2064,7 @@ XBoard square size (hint): %d\n\
       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
       vFrom.size = strlen(appData.lowTimeWarningColor);
       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
-      if (vTo.addr == NULL) 
+      if (vTo.addr == NULL)
                appData.monoMode = True;
       else
                lowTimeWarningColor = *(Pixel *) vTo.addr;
@@ -2484,11 +2546,11 @@ XBoard square size (hint): %d\n\
       HistoryPopUp();
     }
 
-    if( wpEvalGraph.visible ) 
+    if( wpEvalGraph.visible )
       {
        EvalGraphPopUp();
       };
-    
+
     if( wpEngineOutput.visible ) {
       EngineOutputPopUp();
     }
@@ -2514,6 +2576,7 @@ XBoard square size (hint): %d\n\
     }
     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
     InitPosition(TRUE);
+    XtSetKeyboardFocus(shellWidget, formWidget);
 
     XtAppMainLoop(appContext);
     if (appData.debugMode) fclose(debugFP); // [DM] debug
@@ -2583,10 +2646,10 @@ ICSInitScript()
     if (f == NULL) {
        p = getenv("HOME");
        if (p != NULL) {
-           strcpy(buf, p);
-           strcat(buf, "/");
-           strcat(buf, appData.icsLogon);
-           f = fopen(buf, "r");
+         safeStrCpy(buf, p, sizeof(buf)/sizeof(buf[0]) );
+         strcat(buf, "/");
+         strcat(buf, appData.icsLogon);
+         f = fopen(buf, "r");
        }
     }
     if (f != NULL)
@@ -2619,6 +2682,12 @@ GreyRevert(grey)
     } else {
       XtSetSensitive(w, !grey);
     }
+    w = XtNameToWidget(menuBarWidget, "menuStep.Annotate");
+    if (w == NULL) {
+      DisplayError("menuStep.Annotate", 0);
+    } else {
+      XtSetSensitive(w, !grey);
+    }
 }
 
 void
@@ -2654,6 +2723,7 @@ Enables icsEnables[] = {
     { "menuOptions.Hide Thinking", False },
     { "menuOptions.Ponder Next Move", False },
 #endif
+    { "menuStep.Annotate", False },
     { NULL, False }
 };
 
@@ -2669,6 +2739,7 @@ Enables ncpEnables[] = {
     { "menuMode.ICS Input Box", False },
     { "Action", False },
     { "menuStep.Revert", False },
+    { "menuStep.Annotate", False },
     { "menuStep.Move Now", False },
     { "menuStep.Retract Move", False },
     { "menuOptions.Auto Comment", False },
@@ -2697,7 +2768,9 @@ Enables gnuEnables[] = {
     { "menuAction.Adjourn", False },
     { "menuAction.Stop Examining", False },
     { "menuAction.Stop Observing", False },
+    { "menuAction.Upload to Examine", False },
     { "menuStep.Revert", False },
+    { "menuStep.Annotate", False },
     { "menuOptions.Auto Comment", False },
     { "menuOptions.Auto Observe", False },
     { "menuOptions.Auto Raise Board", False },
@@ -2791,7 +2864,7 @@ void SetICSMode()
 {
   SetMenuEnables(icsEnables);
 
-#ifdef ZIPPY
+#if ZIPPY
   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
 #endif
@@ -2914,7 +2987,8 @@ NextInHistory()
  * The return value should be freed with XtFree when no
  * longer needed.
  */
-char *FindFont(pattern, targetPxlSize)
+char *
+FindFont(pattern, targetPxlSize)
      char *pattern;
      int targetPxlSize;
 {
@@ -2929,7 +3003,7 @@ char *FindFont(pattern, targetPxlSize)
     XFontStruct **fnt_list;
 
     base_fnt_lst = calloc(1, strlen(pattern) + 3);
-    sprintf(strInt, "%d", targetPxlSize);
+    snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
     p = strstr(pattern, "--");
     strncpy(base_fnt_lst, pattern, p - pattern + 2);
     strcat(base_fnt_lst, strInt);
@@ -2988,8 +3062,8 @@ char *FindFont(pattern, targetPxlSize)
        while (isdigit(*scalableTail)) scalableTail++;
        sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
     } else {
-        p = (char *) XtMalloc(strlen(best) + 1);
-        strcpy(p, best);
+        p = (char *) XtMalloc(strlen(best) + 2);
+        safeStrCpy(p, best, strlen(best)+1 );
     }
     if (appData.debugMode) {
         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
@@ -3375,7 +3449,7 @@ void CreateXPMPieces()
                        exit(1);
                    }
                }
-               if(piece <= (int) WhiteKing) 
+               if(piece <= (int) WhiteKing)
                    xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
            }
        }
@@ -3429,12 +3503,12 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
-           sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
-                   pieceBitmapNames[piece],
-                   ss, kind == SOLID ? 's' : 'o');
-           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3457,13 +3531,13 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
-           sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
-                   pieceBitmapNames[piece],
-                   ss, kind == SOLID ? 's' : 'o');
-           ReadBitmap(&pieceBitmap2[kind][piece], buf,
-                      bib->bits[kind][piece], ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf,
+                    bib->bits[kind][piece], ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3484,7 +3558,7 @@ void ReadBitmap(pm, name, bits, wreq, hreq)
     char msg[MSG_SIZ], fullname[MSG_SIZ];
 
     if (*appData.bitmapDirectory != NULLCHAR) {
-        strcpy(fullname, appData.bitmapDirectory);
+      safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
        strcat(fullname, "/");
        strcat(fullname, name);
        errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
@@ -3606,7 +3680,7 @@ Widget CreateMenuBar(mb)
                             formWidget, args, j);
 
     while (mb->name != NULL) {
-       strcpy(menuName, "menu");
+        safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
        strcat(menuName, mb->name);
        j = 0;
        XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
@@ -3767,7 +3841,7 @@ void PieceMenuPopup(w, event, params, num_params)
 {
     String whichMenu; int menuNr;
     if (event->type == ButtonRelease)
-        menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); 
+        menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
     else if (event->type == ButtonPress)
         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
     switch(menuNr) {
@@ -4168,6 +4242,8 @@ void DrawSquare(row, column, piece, do_flash)
          (squareSize + lineGap);
     }
 
+    if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
+
     square_color = SquareColor(row, column);
 
     if ( // [HGM] holdings: blank out area between board and holdings
@@ -4253,8 +4329,8 @@ void DrawSquare(row, column, piece, do_flash)
                        x + 2, y + font_ascent + 1, string, 1);
        }
     }
-    if(marker[row][column]) {
-       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC, 
+    if(!partnerUp && marker[row][column]) {
+       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
                x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
     }
 }
@@ -4273,6 +4349,11 @@ void EventProc(widget, unused, event)
       case Expose:
        if (event->xexpose.count > 0) return;  /* no clipping is done */
        XDrawPosition(widget, True, NULL);
+       if(twoBoards) { // [HGM] dual: draw other board in other orientation
+           flipView = !flipView; partnerUp = !partnerUp;
+           XDrawPosition(widget, True, NULL);
+           flipView = !flipView; partnerUp = !partnerUp;
+       }
        break;
       case MotionNotify:
         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;\r
@@ -4356,7 +4437,7 @@ static int check_castle_draw(newb, oldb, rrow, rcol)
     return 0;
 }
 
-// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph 
+// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
 void DrawSeekAxis( int x, int y, int xTo, int yTo )
 {
       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
@@ -4382,11 +4463,11 @@ void DrawSeekDot(int x, int y, int colorNr)
        XFillRectangle(xDisplay, xBoardWindow, color,
                x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
     else
-       XFillArc(xDisplay, xBoardWindow, color, 
+       XFillArc(xDisplay, xBoardWindow, color,
                x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
 }
 
-static int damage[BOARD_RANKS][BOARD_FILES];
+static int damage[2][BOARD_RANKS][BOARD_FILES];
 
 /*
  * event handler for redrawing the board
@@ -4398,18 +4479,19 @@ void XDrawPosition(w, repaint, board)
 {
     int i, j, do_flash;
     static int lastFlipView = 0;
-    static int lastBoardValid = 0;
-    static Board lastBoard;
+    static int lastBoardValid[2] = {0, 0};
+    static Board lastBoard[2];
     Arg args[16];
     int rrow, rcol;
+    int nr = twoBoards*partnerUp;
 
     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
 
     if (board == NULL) {
        if (!lastBoardValid) return;
-       board = lastBoard;
+       board = lastBoard[nr];
     }
-    if (!lastBoardValid || lastFlipView != flipView) {
+    if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
        XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
        XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
                    args, 1);
@@ -4420,19 +4502,19 @@ void XDrawPosition(w, repaint, board)
      * but this causes a very distracting flicker.
      */
 
-    if (!repaint && lastBoardValid && lastFlipView == flipView) {
+    if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
 
        /* If too much changes (begin observing new game, etc.), don't
           do flashing */
-       do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
+       do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
 
        /* Special check for castling so we don't flash both the king
           and the rook (just flash the king). */
        if (do_flash) {
-           if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
+           if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
                /* Draw rook with NO flashing. King will be drawn flashing later */
                DrawSquare(rrow, rcol, board[rrow][rcol], 0);
-               lastBoard[rrow][rcol] = board[rrow][rcol];
+               lastBoard[nr][rrow][rcol] = board[rrow][rcol];
            }
        }
 
@@ -4441,32 +4523,34 @@ void XDrawPosition(w, repaint, board)
           is flashing on its new square */
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++)
-           if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
-               || damage[i][j]) {
+           if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
+               || damage[nr][i][j]) {
                DrawSquare(i, j, board[i][j], 0);
-               damage[i][j] = False;
+               damage[nr][i][j] = False;
            }
 
        /* Second pass -- Draw piece(s) in new position and flash them */
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++)
-           if (board[i][j] != lastBoard[i][j]) {
+           if (board[i][j] != lastBoard[nr][i][j]) {
                DrawSquare(i, j, board[i][j], do_flash);
            }
     } else {
        if (lineGap > 0)
          XDrawSegments(xDisplay, xBoardWindow, lineGC,
+                       twoBoards & partnerUp ? secondSegments : // [HGM] dual
                        gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
 
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++) {
              DrawSquare(i, j, board[i][j], 0);
-             damage[i][j] = False;
+             damage[nr][i][j] = False;
          }
     }
 
-    CopyBoard(lastBoard, board);
-    lastBoardValid = 1;
+    CopyBoard(lastBoard[nr], board);
+    lastBoardValid[nr] = 1;
+  if(nr == 0) { // [HGM] dual: no highlights on second board yet
     lastFlipView = flipView;
 
     /* Draw highlights */
@@ -4482,7 +4566,7 @@ void XDrawPosition(w, repaint, board)
     if (hi2X >= 0 && hi2Y >= 0) {
       drawHighlight(hi2X, hi2Y, highlineGC);
     }
-
+  }
     /* If piece being dragged around board, must redraw that too */
     DrawDragPiece();
 
@@ -4601,6 +4685,7 @@ Widget CommentCreate(name, text, mutable, callback, lines)
     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
     edit =
       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
+    XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
 
     if (mutable) {
        j = 0;
@@ -4809,6 +4894,20 @@ Widget MiscCreate(name, text, mutable, callback, lines)
 
 static int savedIndex;  /* gross that this is global */
 
+void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+       String val;
+       XawTextPosition index, dummy;
+       Arg arg;
+
+       XawTextGetSelectionPos(w, &index, &dummy);
+       XtSetArg(arg, XtNstring, &val);
+       XtGetValues(w, &arg, 1);
+       ReplaceComment(savedIndex, val);
+       if(savedIndex != currentMove) ToNrEvent(savedIndex);
+       LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
+}
+
 void EditCommentPopUp(index, title, text)
      int index;
      char *title, *text;
@@ -4970,6 +5069,7 @@ void CommentPopUp(title, text)
     int j;
     Widget edit;
 
+    savedIndex = currentMove; // [HGM] vari
     if (commentShell == NULL) {
        commentShell =
          CommentCreate(title, text, False, CommentCallback, 4);
@@ -5045,45 +5145,14 @@ void FileNamePopUp(label, def, proc, openMode)
 
     fileProc = proc;           /* I can't see a way not */
     fileOpenMode = openMode;   /*   to use globals here */
-
-    i = 0;
-    XtSetArg(args[i], XtNresizable, True); i++;
-    XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
-    XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
-    fileNameShell = popup =
-      XtCreatePopupShell("File name prompt", transientShellWidgetClass,
-                        shellWidget, args, i);
-
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
-                           layoutArgs, XtNumber(layoutArgs));
-
-    i = 0;
-    XtSetArg(args[i], XtNlabel, label); i++;
-    XtSetArg(args[i], XtNvalue, def); i++;
-    XtSetArg(args[i], XtNborderWidth, 0); i++;
-    dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
-                                  layout, args, i);
-
-    XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
-                      (XtPointer) dialog);
-
-    XtRealizeWidget(popup);
-    CatchDeleteWindow(popup, "FileNamePopDown");
-
-    XQueryPointer(xDisplay, xBoardWindow, &root, &child,
-                 &x, &y, &win_x, &win_y, &mask);
-
-    XtSetArg(args[0], XtNx, x - 10);
-    XtSetArg(args[1], XtNy, y - 30);
-    XtSetValues(popup, args, 2);
-
-    XtPopup(popup, XtGrabExclusive);
-    filenameUp = True;
-
-    edit = XtNameToWidget(dialog, "*value");
-    XtSetKeyboardFocus(popup, edit);
+    {   // [HGM] use file-selector dialog stolen from Ghostview
+       char *name;
+       int index; // this is not supported yet
+       FILE *f;
+       if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
+           def, openMode, NULL, &name))
+               (void) (*fileProc)(f, index=0, name);
+    }
 }
 
 void FileNamePopDown()
@@ -5128,7 +5197,7 @@ void FileNameAction(w, event, prms, nprms)
     name = XawDialogGetValueString(w = XtParent(w));
 
     if ((name != NULL) && (*name != NULLCHAR)) {
-       strcpy(buf, name);
+        safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
        XtPopdown(w = XtParent(XtParent(w)));
        XtDestroyWidget(w);
        filenameUp = False;
@@ -5204,8 +5273,8 @@ void PromotionPopUp()
       XawDialogAddButton(dialog, _("King"), PromotionCallback,
                         (XtPointer) dialog);
     }
-    if(gameInfo.variant == VariantCapablanca || 
-       gameInfo.variant == VariantGothic || 
+    if(gameInfo.variant == VariantCapablanca ||
+       gameInfo.variant == VariantGothic ||
        gameInfo.variant == VariantCapaRandom) {
       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
                         (XtPointer) dialog);
@@ -5658,7 +5727,7 @@ SendPositionSelection(Widget w, Atom *selection, Atom *target,
      * automatically call XtFree on the value returned.  So have to
      * make a copy of it allocated with XtMalloc */
     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
-    strcpy(selection_tmp, selected_fen_position);
+    safeStrCpy(selection_tmp, selected_fen_position, sizeof(selection_tmp)/sizeof(selection_tmp[0]) );
 
     *value_return=selection_tmp;
     *length_return=strlen(selection_tmp);
@@ -5733,7 +5802,7 @@ void PastePositionProc(w, event, prms, nprms)
   String *prms;
   Cardinal *nprms;
 {
-    XtGetSelectionValue(menuBarWidget, 
+    XtGetSelectionValue(menuBarWidget,
       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
       /* (XtSelectionCallbackProc) */ PastePositionCB,
       NULL, /* client_data passed to PastePositionCB */
@@ -5923,7 +5992,7 @@ void AnalyzeModeProc(w, event, prms, nprms)
     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
     if (appData.icsActive) {
         if (gameMode != IcsObserving) {
-            sprintf(buf,_("You are not observing a game"));
+         snprintf(buf, MSG_SIZ, _("You are not observing a game"));
             DisplayError(buf, 0);
             /* secure check */
             if (appData.icsEngineAnalyze) {
@@ -6219,6 +6288,15 @@ void StopExaminingProc(w, event, prms, nprms)
     StopExaminingEvent();
 }
 
+void UploadProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+    UploadGameEvent();
+}
+
 
 void ForwardProc(w, event, prms, nprms)
      Widget w;
@@ -6263,7 +6341,16 @@ void RevertProc(w, event, prms, nprms)
      String *prms;
      Cardinal *nprms;
 {
-    RevertEvent();
+    RevertEvent(False);
+}
+
+void AnnotateProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+    RevertEvent(True);
 }
 
 void TruncateGameProc(w, event, prms, nprms)
@@ -6964,23 +7051,23 @@ void DisplayMessage(message, extMessage)
      char *message, *extMessage;
 {
   /* display a message in the message widget */
-  
+
   char buf[MSG_SIZ];
   Arg arg;
-  
-  if (extMessage) 
+
+  if (extMessage)
     {
-      if (*message) 
+      if (*message)
        {
          snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
          message = buf;
-       } 
-      else 
+       }
+      else
        {
          message = extMessage;
        };
     };
-  
+
   /* need to test if messageWidget already exists, since this function
      can also be called during the startup, if for example a Xresource
      is not set up correctly */
@@ -6989,7 +7076,7 @@ void DisplayMessage(message, extMessage)
       XtSetArg(arg, XtNlabel, message);
       XtSetValues(messageWidget, &arg, 1);
     };
-  
+
   return;
 }
 
@@ -7010,8 +7097,8 @@ void DisplayTitle(text)
     }
 
     if (*text != NULLCHAR) {
-       strcpy(icon, text);
-       strcpy(title, text);
+      safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
     } else if (appData.icsActive) {
         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
        snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
@@ -7021,19 +7108,19 @@ void DisplayTitle(text)
 #ifdef GOTHIC
     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
     } else if (gameInfo.variant == VariantGothic) {
-       strcpy(icon, programName);
-       strcpy(title, GOTHIC);
+      safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
 #endif
 #ifdef FALCON
     } else if (gameInfo.variant == VariantFalcon) {
-       strcpy(icon, programName);
-       strcpy(title, FALCON);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
 #endif
     } else if (appData.noChessProgram) {
-       strcpy(icon, programName);
-       strcpy(title, programName);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
     } else {
-       strcpy(icon, first.tidy);
+      safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
        snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
     }
     i = 0;
@@ -7043,7 +7130,8 @@ void DisplayTitle(text)
 }
 
 
-void DisplayError(message, error)
+void
+DisplayError(message, error)
      String message;
      int error;
 {
@@ -7191,7 +7279,7 @@ void AskQuestionReplyAction(w, event, prms, nprms)
     String reply;
 
     reply = XawDialogGetValueString(w = XtParent(w));
-    strcpy(buf, pendingReplyPrefix);
+    safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
     if (*buf) strcat(buf, " ");
     strcat(buf, reply);
     strcat(buf, "\n");
@@ -7229,7 +7317,7 @@ void AskQuestion(title, question, replyPrefix, pr)
     int win_x, win_y;
     unsigned int mask;
 
-    strcpy(pendingReplyPrefix, replyPrefix);
+    safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
     pendingReplyPR = pr;
 
     i = 0;
@@ -7346,18 +7434,18 @@ Colorize(cc, continuation)
 
     if (textColors[(int)cc].bg > 0) {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].fg, textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].fg, textColors[(int)cc].bg);
        } else {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].bg);
        }
     } else {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
                    textColors[(int)cc].fg);
        } else {
-           sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
+         snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
        }
     }
     count = strlen(buf);
@@ -7407,11 +7495,12 @@ char *UserName()
     return getpwuid(getuid())->pw_name;
 }
 
-static char *ExpandPathName(path)
+static char *
+ExpandPathName(path)
      char *path;
 {
-    static char static_buf[2000];
-    char *d, *s, buf[2000];
+    static char static_buf[4*MSG_SIZ];
+    char *d, *s, buf[4*MSG_SIZ];
     struct passwd *pwd;
 
     s = path;
@@ -7427,25 +7516,25 @@ static char *ExpandPathName(path)
 
     if (*s == '~') {
        if (*(s+1) == '/') {
-           strcpy(d, getpwuid(getuid())->pw_dir);
-           strcat(d, s+1);
+         safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
+         strcat(d, s+1);
        }
        else {
-           strcpy(buf, s+1);
-           *strchr(buf, '/') = 0;
-           pwd = getpwnam(buf);
-           if (!pwd)
-             {
-                 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
-                         buf, path);
-                 return NULL;
-             }
-           strcpy(d, pwd->pw_dir);
-           strcat(d, strchr(s+1, '/'));
+         safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
+         *strchr(buf, '/') = 0;
+         pwd = getpwnam(buf);
+         if (!pwd)
+           {
+             fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
+                     buf, path);
+             return NULL;
+           }
+         safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
+         strcat(d, strchr(s+1, '/'));
        }
     }
     else
-      strcpy(d, s);
+      safeStrCpy(d, s, 4*MSG_SIZ );
 
     return static_buf;
 }
@@ -7620,16 +7709,16 @@ DisplayTimerLabel(w, color, timer, highlight)
     Pixel foregroundOrWarningColor = timerForegroundPixel;
 
     if (timer > 0 &&
-        appData.lowTimeWarning && 
+        appData.lowTimeWarning &&
         (timer / 1000) < appData.icsAlarmTime)
       foregroundOrWarningColor = lowTimeWarningColor;
 
     if (appData.clockMode) {
-       sprintf(buf, "%s: %s", color, TimeString(timer));
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
+      XtSetArg(args[0], XtNlabel, buf);
     } else {
-       sprintf(buf, "%s  ", color);
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s  ", color);
+      XtSetArg(args[0], XtNlabel, buf);
     }
 
     if (highlight) {
@@ -7710,7 +7799,7 @@ int StartChildProcess(cmdLine, dir, pr)
        most simple-minded way possible.
        */
     i = 0;
-    strcpy(buf, cmdLine);
+    safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
     p = buf;
     for (;;) {
        while(*p == ' ') p++;
@@ -8668,8 +8757,8 @@ AnimateMove(board, fromX, fromY, toX, toY)
   if (!appData.animate || appData.blindfold)
     return;
 
-  if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
-     board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
+  if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
+     board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
        return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
 
   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
@@ -8712,7 +8801,7 @@ AnimateMove(board, fromX, fromY, toX, toY)
   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
 
   /* Be sure end square is redrawn */
-  damage[toY][toX] = True;
+  damage[0][toY][toX] = True;
 }
 
 void
@@ -8752,7 +8841,7 @@ DragPieceBegin(x, y)
            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
                     corner.x, corner.y, squareSize, squareSize,
                     0, 0); // [HGM] zh: unstack in stead of grab
-       damage[boardY][boardX] = True;
+       damage[0][boardY][boardX] = True;
     } else {
        player.dragActive = False;
     }
@@ -8806,7 +8895,7 @@ DragPieceEnd(x, y)
     EndAnimation(&player, &corner);
 
     /* Be sure end square is redrawn */
-    damage[boardY][boardX] = True;
+    damage[0][boardY][boardX] = True;
 
     /* This prevents weird things happening with fast successive
        clicks which on my Sun at least can cause motion events
@@ -8829,7 +8918,7 @@ DrawDragPiece ()
   BlankSquare(player.startSquare.x, player.startSquare.y,
                player.startColor, EmptySquare, xBoardWindow);
   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
-  damage[player.startBoardY][player.startBoardX] = TRUE;
+  damage[0][player.startBoardY][player.startBoardX] = TRUE;
 }
 
 #include <sys/ioctl.h>