UNIXworkcode

1 /******************************************************************************* 2 * * 3 * nedit.c -- Nirvana Editor main program * 4 * * 5 * Copyright (C) 1999 Mark Edel * 6 * * 7 * This is free software; you can redistribute it and/or modify it under the * 8 * terms of the GNU General Public License as published by the Free Software * 9 * Foundation; either version 2 of the License, or (at your option) any later * 10 * version. In addition, you may distribute versions of this program linked to * 11 * Motif or Open Motif. See README for details. * 12 * * 13 * This software is distributed in the hope that it will be useful, but WITHOUT * 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * 16 * for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along with * 19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple * 20 * Place, Suite 330, Boston, MA 02111-1307 USA * 21 * * 22 * Nirvana Text Editor * 23 * May 10, 1991 * 24 * * 25 * Written by Mark Edel * 26 * * 27 * Modifications: * 28 * * 29 * 8/18/93 - Mark Edel & Joy Kyriakopulos - Ported to VMS * 30 * * 31 *******************************************************************************/ 32 33 #ifdef HAVE_CONFIG_H 34 #include "../config.h" 35 #endif 36 37 #include "nedit.h" 38 /* #include "textBuf.h" */ 39 #include "file.h" 40 #include "preferences.h" 41 #include "editorconfig.h" 42 #include "regularExp.h" 43 #include "selection.h" 44 #include "tags.h" 45 #include "menu.h" 46 #include "macro.h" 47 #include "search.h" 48 #include "server.h" 49 #include "interpret.h" 50 #include "parse.h" 51 #include "help.h" 52 #include "text.h" /* TextWidgetClassInit */ 53 #include "../util/misc.h" 54 #include "../util/printUtils.h" 55 #include "../util/fileUtils.h" 56 #include "../util/filedialog.h" 57 #include "../util/getfiles.h" 58 #include "../util/motif.h" 59 #include "../util/nedit_malloc.h" 60 #include "../util/xdnd.h" 61 #include "filter.h" 62 63 #include <ctype.h> 64 #include <limits.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 #include <errno.h> 70 #include <langinfo.h> 71 72 #ifndef NO_XMIM 73 #include <X11/Xlocale.h> 74 #else 75 #include <locale.h> 76 #endif 77 78 #include <X11/Intrinsic.h> 79 #include <Xm/Xm.h> 80 #include <Xm/XmP.h> 81 82 #if XmVersion >= 1002 83 #include <Xm/RepType.h> 84 #endif 85 86 87 #ifndef __MVS__ 88 #include <sys/param.h> 89 #endif 90 91 #ifdef HAVE_DEBUG_H 92 #include "../debug.h" 93 #endif 94 95 static void nextArg(int argc, char **argv, int *argIndex); 96 static int checkDoMacroArg(const char *macro); 97 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure); 98 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs); 99 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs); 100 static void fixupBrokenXKeysymDB(void); 101 static void patchResourcesForVisual(void); 102 static void patchResourcesForKDEbug(void); 103 static unsigned char* sanitizeVirtualKeyBindings(void); 104 static int sortAlphabetical(const void* k1, const void* k2); 105 static int virtKeyBindingsAreInvalid(const unsigned char* bindings); 106 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings); 107 static void noWarningFilter(String); 108 static void showWarningFilter(String); 109 static void dndOpenFileCB(Widget w, XtPointer value, XtPointer data); 110 111 static XrmDatabase defaultResourceDB; 112 113 WindowInfo *WindowList = NULL; 114 Display *TheDisplay = NULL; 115 char *ArgV0 = NULL; 116 Boolean IsServer = False; 117 Boolean BackgroundRun = False; 118 Widget TheAppShell; 119 120 /* Reasons for choice of default font qualifications: 121 122 iso8859 appears to be necessary for newer versions of XFree86 that 123 default to Unicode encoding, which doesn't quite work with Motif. 124 Otherwise Motif puts up garbage (square blocks). 125 126 (This of course, is a stupid default because there are far more iso8859 127 apps than Unicode apps. But the X folks insist it's a client bug. Hah.) 128 129 RedHat 7.3 won't default to '-1' for an encoding, if left with a *, 130 and so reverts to "fixed". Yech. */ 131 132 #define NEDIT_DEFAULT_FONT "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-iso8859-1," \ 133 "-*-helvetica-bold-r-normal-*-14-*-*-*-*-*-iso8859-1=BOLD," \ 134 "-*-helvetica-medium-o-normal-*-14-*-*-*-*-*-iso8859-1=ITALIC" 135 136 #define NEDIT_FIXED_FONT "-*-courier-medium-r-normal-*-14-*-*-*-*-*-iso8859-1," \ 137 "-*-courier-bold-r-normal-*-14-*-*-*-*-*-iso8859-1=BOLD," \ 138 "-*-courier-medium-o-normal-*-14-*-*-*-*-*-iso8859-1=ITALIC" 139 140 #define NEDIT_DEFAULT_BG "#b3b3b3" 141 142 #define NEDIT_TEXT_TRANSLATIONS "#override\\n" \ 143 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \ 144 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \ 145 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \ 146 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n" 147 148 #define NEDIT_XFT_DEFAULT_FONT "Sans" 149 #define NEDIT_XFT_FIXED_FONT "Monospace" 150 151 static char *fallbackResources[] = { 152 /* Try to avoid Motif's horrificly ugly default colors and fonts, 153 if the user's environment provides no usable defaults. We try 154 to choose a Windows-y default color setting here. Editable text 155 fields are forced to a fixed-pitch font for usability. 156 157 By using the VendorShell fontList resources, Motif automatically 158 groups the fonts into the right classes. It's then easier for 159 the user or environment to override this sensibly: 160 161 xnedit -xrm '*textFontList: myfont' 162 163 This is broken in recent versions of LessTif. 164 165 If using OpenMotif 2.3.3 or better, support XFT fonts. XFT is 166 claimed supported in OpenMotif 2.3.0, but doesn't work very well, 167 as insensitive text buttons are blank. That bug is fixed in 2.3.3. 168 */ 169 170 #if (XmVersion >= 2003 && XmUPDATE_LEVEL >= 3 && USE_XFT == 1) 171 "*buttonRenderTable: defaultRT", 172 "*labelRenderTable: defaultRT", 173 "*textRenderTable: fixedRT", 174 "*defaultRT.fontType: FONT_IS_XFT", 175 "*defaultRT.fontName: " NEDIT_XFT_DEFAULT_FONT, 176 "*defaultRT.fontSize: 9", 177 "*fixedRT.fontType: FONT_IS_XFT", 178 "*fixedRT.fontName: Monospace", 179 "*fixedRT.fontSize: 9", 180 #elif LESSTIF_VERSION 181 "*FontList: " NEDIT_DEFAULT_FONT, 182 "*XmText.FontList: " NEDIT_FIXED_FONT, 183 "*XmTextField.FontList: " NEDIT_FIXED_FONT, 184 "*XmList.FontList: " NEDIT_FIXED_FONT, 185 "*XmFileSelectionBox*XmList.FontList: " NEDIT_FIXED_FONT, 186 #else 187 "*buttonFontList: " NEDIT_DEFAULT_FONT, 188 "*labelFontList: " NEDIT_DEFAULT_FONT, 189 "*textFontList: " NEDIT_FIXED_FONT, 190 #endif 191 #ifdef XNE_TEXTFIELD 192 "*XmTextField.XftFont: Monospace:size=10", 193 #endif 194 195 /* 196 "*background: " NEDIT_DEFAULT_BG, 197 "*foreground: " NEDIT_DEFAULT_FG, 198 */ 199 "*XmText.foreground: " NEDIT_DEFAULT_FG, 200 "*XmText.background: " NEDIT_DEFAULT_TEXT_BG, 201 "*XmList.foreground: " NEDIT_DEFAULT_FG, 202 "*XmList.background: " NEDIT_DEFAULT_TEXT_BG, 203 "*XmTextField.foreground: " NEDIT_DEFAULT_FG, 204 "*XmTextField.background: " NEDIT_DEFAULT_TEXT_BG, 205 206 "*XmContainer.background: " NEDIT_DEFAULT_TEXT_BG, 207 "*XmIconGadget.background: " NEDIT_DEFAULT_TEXT_BG, 208 209 "*pbbutton.shadowThickness: 1", 210 "*pbbutton.highlightThickness: 1", 211 212 "*tabButton.shadowThickness: 1", 213 214 "*XmContainer.outlineIndentation: 30", 215 "*XmContainer.outlineColumnWidth: 6cm", 216 "*XmContainer.detailTabList: +3cm,+3cm,+3cm", 217 218 /* Use baseTranslations as per Xt Programmer's Manual, 10.2.12 */ 219 "*XmText.baseTranslations: " NEDIT_TEXT_TRANSLATIONS, 220 "*XmTextField.baseTranslations: " NEDIT_TEXT_TRANSLATIONS, 221 222 "*XmLFolder.highlightThickness: 0", 223 "*XmLFolder.shadowThickness: 1", 224 "*XmLFolder.maxTabWidth: 150", 225 "*XmLFolder.traversalOn: False", 226 "*XmLFolder.inactiveForeground: #666" , 227 "*tab.alignment: XmALIGNMENT_BEGINNING", 228 "*tab.marginWidth: 0", 229 "*tab.marginHeight: 1", 230 231 /* Prevent the file selection box from acting stupid. */ 232 "*XmFileSelectionBox.resizePolicy: XmRESIZE_NONE", 233 "*XmFileSelectionBox.textAccelerators:", 234 "*XmFileSelectionBox.pathMode: XmPATH_MODE_RELATIVE", 235 "*XmFileSelectionBox.width: 500", 236 "*XmFileSelectionBox.height: 400", 237 238 /* NEdit-specific widgets. Theses things should probably be manually 239 jammed into the database, rather than fallbacks. We really want 240 the accelerators to be there even if someone creates an app-defaults 241 file against our wishes. */ 242 243 "*newFolder.labelString: New Folder", 244 "*newFolder.labelType: XmPIXMAP", 245 246 "*text.lineNumForeground: " NEDIT_DEFAULT_LINENO_FG, 247 "*text.background: " NEDIT_DEFAULT_TEXT_BG, 248 "*text.foreground: " NEDIT_DEFAULT_FG, 249 "*text.highlightForeground: " NEDIT_DEFAULT_HI_FG, 250 "*text.highlightBackground: " NEDIT_DEFAULT_HI_BG, 251 "*textFrame.shadowThickness: 1", 252 "*menuBar.marginHeight: 0", 253 "*menuBar.shadowThickness: 1", 254 "*pane.sashHeight: 11", 255 "*pane.sashWidth: 11", 256 "*pane.marginWidth: 0", 257 "*pane.marginHeight: 0", 258 "*scrolledW*spacing: 0", 259 "*text.selectionArrayCount: 3", 260 "*helpText.background: " NEDIT_DEFAULT_HELP_BG, 261 "*helpText.foreground: " NEDIT_DEFAULT_HELP_FG, 262 "*helpText.selectBackground: " NEDIT_DEFAULT_BG, 263 "*statsLine.background: " NEDIT_DEFAULT_BG, 264 "*statsLine.FontList: " NEDIT_DEFAULT_FONT, 265 "*calltip.background: LemonChiffon1", 266 "*calltip.foreground: black", 267 "*iSearchForm*highlightThickness: 1", 268 "*fileMenu.tearOffModel: XmTEAR_OFF_ENABLED", 269 "*editMenu.tearOffModel: XmTEAR_OFF_ENABLED", 270 "*searchMenu.tearOffModel: XmTEAR_OFF_ENABLED", 271 "*preferencesMenu.tearOffModel: XmTEAR_OFF_ENABLED", 272 "*windowsMenu.tearOffModel: XmTEAR_OFF_ENABLED", 273 "*shellMenu.tearOffModel: XmTEAR_OFF_ENABLED", 274 "*macroMenu.tearOffModel: XmTEAR_OFF_ENABLED", 275 "*helpMenu.tearOffModel: XmTEAR_OFF_ENABLED", 276 "*fileMenu.mnemonic: F", 277 "*fileMenu.new.accelerator: Ctrl<Key>n", 278 "*fileMenu.new.acceleratorText: Ctrl+N", 279 "*fileMenu.newOpposite.accelerator: Shift Ctrl<Key>n", 280 "*fileMenu.newOpposite.acceleratorText: Shift+Ctrl+N", 281 "*fileMenu.open.accelerator: Ctrl<Key>o", 282 "*fileMenu.open.acceleratorText: Ctrl+O", 283 "*fileMenu.openSelected.accelerator: Ctrl<Key>y", 284 "*fileMenu.openSelected.acceleratorText: Ctrl+Y", 285 "*fileMenu.close.accelerator: Ctrl<Key>w", 286 "*fileMenu.close.acceleratorText: Ctrl+W", 287 "*fileMenu.save.accelerator: Ctrl<Key>s", 288 "*fileMenu.save.acceleratorText: Ctrl+S", 289 "*fileMenu.includeFile.accelerator: Alt<Key>i", 290 "*fileMenu.includeFile.acceleratorText: Alt+I", 291 "*fileMenu.print.accelerator: Ctrl<Key>p", 292 "*fileMenu.print.acceleratorText: Ctrl+P", 293 "*fileMenu.exit.accelerator: Ctrl<Key>q", 294 "*fileMenu.exit.acceleratorText: Ctrl+Q", 295 "*editMenu.mnemonic: E", 296 "*editMenu.undo.accelerator: Ctrl<Key>z", 297 "*editMenu.undo.acceleratorText: Ctrl+Z", 298 "*editMenu.redo.accelerator: Shift Ctrl<Key>z", 299 "*editMenu.redo.acceleratorText: Shift+Ctrl+Z", 300 /* Clipboard accelerators prevent the use of the clipboard in iSearch's 301 XmText, so they are left out. Their job is done by translations in 302 the main text widget, so the acceleratorText is still kept. */ 303 "*editMenu.cut.acceleratorText: Ctrl+X", 304 "*editMenu.copy.acceleratorText: Ctrl+C", 305 "*editMenu.paste.acceleratorText: Ctrl+V", 306 "*editMenu.pasteColumn.accelerator: Shift Ctrl<Key>v", 307 "*editMenu.pasteColumn.acceleratorText: Ctrl+Shift+V", 308 "*editMenu.delete.acceleratorText: Del", 309 "*editMenu.selectAll.accelerator: Ctrl<Key>a", 310 "*editMenu.selectAll.acceleratorText: Ctrl+A", 311 "*editMenu.shiftLeft.accelerator: Ctrl<Key>9", 312 "*editMenu.shiftLeft.acceleratorText: [Shift]Ctrl+9", 313 "*editMenu.shiftLeftShift.accelerator: Shift Ctrl<Key>9", 314 "*editMenu.shiftRight.accelerator: Ctrl<Key>0", 315 "*editMenu.shiftRight.acceleratorText: [Shift]Ctrl+0", 316 "*editMenu.shiftRightShift.accelerator: Shift Ctrl<Key>0", 317 "*editMenu.upperCase.accelerator: Ctrl<Key>6", 318 "*editMenu.upperCase.acceleratorText: Ctrl+6", 319 "*editMenu.lowerCase.accelerator: Shift Ctrl<Key>6", 320 "*editMenu.lowerCase.acceleratorText: Shift+Ctrl+6", 321 "*editMenu.fillParagraph.accelerator: Ctrl<Key>j", 322 "*editMenu.fillParagraph.acceleratorText: Ctrl+J", 323 "*editMenu.insertFormFeed.accelerator: Alt Ctrl<Key>l", 324 "*editMenu.insertFormFeed.acceleratorText: Alt+Ctrl+L", 325 "*editMenu.insertCtrlCode.accelerator: Alt Ctrl<Key>i", 326 "*editMenu.insertCtrlCode.acceleratorText: Alt+Ctrl+I", 327 "*editMenu.insertUnicode.accelerator: Alt Ctrl<Key>u", 328 "*editMenu.insertUnicode.acceleratorText: Alt+Ctrl+U", 329 "*searchMenu.mnemonic: S", 330 "*searchMenu.find.accelerator: Ctrl<Key>f", 331 "*searchMenu.find.acceleratorText: [Shift]Ctrl+F", 332 "*searchMenu.findShift.accelerator: Shift Ctrl<Key>f", 333 "*searchMenu.findAgain.accelerator: Ctrl<Key>g", 334 "*searchMenu.findAgain.acceleratorText: [Shift]Ctrl+G", 335 "*searchMenu.findAgainShift.accelerator: Shift Ctrl<Key>g", 336 "*searchMenu.findSelection.accelerator: Ctrl<Key>h", 337 "*searchMenu.findSelection.acceleratorText: [Shift]Ctrl+H", 338 "*searchMenu.findSelectionShift.accelerator: Shift Ctrl<Key>h", 339 "*searchMenu.findIncremental.accelerator: Ctrl<Key>i", 340 "*searchMenu.findIncrementalShift.accelerator: Shift Ctrl<Key>i", 341 "*searchMenu.findIncremental.acceleratorText: [Shift]Ctrl+I", 342 "*searchMenu.replace.accelerator: Ctrl<Key>r", 343 "*searchMenu.replace.acceleratorText: [Shift]Ctrl+R", 344 "*searchMenu.replaceShift.accelerator: Shift Ctrl<Key>r", 345 "*searchMenu.findReplace.accelerator: Ctrl<Key>r", 346 "*searchMenu.findReplace.acceleratorText: [Shift]Ctrl+R", 347 "*searchMenu.findReplaceShift.accelerator: Shift Ctrl<Key>r", 348 "*searchMenu.replaceFindAgain.accelerator: Ctrl<Key>t", 349 "*searchMenu.replaceFindAgain.acceleratorText: [Shift]Ctrl+T", 350 "*searchMenu.replaceFindAgainShift.accelerator: Shift Ctrl<Key>t", 351 "*searchMenu.replaceAgain.accelerator: Alt<Key>t", 352 "*searchMenu.replaceAgain.acceleratorText: [Shift]Alt+T", 353 "*searchMenu.replaceAgainShift.accelerator: Shift Alt<Key>t", 354 "*searchMenu.gotoLineNumber.accelerator: Ctrl<Key>l", 355 "*searchMenu.gotoLineNumber.acceleratorText: Ctrl+L", 356 "*searchMenu.gotoSelected.accelerator: Ctrl<Key>e", 357 "*searchMenu.gotoSelected.acceleratorText: Ctrl+E", 358 "*searchMenu.mark.accelerator: Alt<Key>m", 359 "*searchMenu.mark.acceleratorText: Alt+M a-z", 360 "*searchMenu.gotoMark.accelerator: Alt<Key>g", 361 "*searchMenu.gotoMark.acceleratorText: [Shift]Alt+G a-z", 362 "*searchMenu.gotoMarkShift.accelerator: Shift Alt<Key>g", 363 "*searchMenu.gotoMatching.accelerator: Ctrl<Key>m", 364 "*searchMenu.gotoMatching.acceleratorText: [Shift]Ctrl+M", 365 "*searchMenu.gotoMatchingShift.accelerator: Shift Ctrl<Key>m", 366 "*searchMenu.findDefinition.accelerator: Ctrl<Key>d", 367 "*searchMenu.findDefinition.acceleratorText: Ctrl+D", 368 "*searchMenu.showCalltip.accelerator: Ctrl<Key>apostrophe", 369 "*searchMenu.showCalltip.acceleratorText: Ctrl+''", 370 "*preferencesMenu.mnemonic: P", 371 "*preferencesMenu.statisticsLine.accelerator: Alt<Key>a", 372 "*preferencesMenu.statisticsLine.acceleratorText: Alt+A", 373 "*preferencesMenu.overtype.acceleratorText: Insert", 374 "*shellMenu.mnemonic: l", 375 "*shellMenu.filterSelection.accelerator: Alt<Key>r", 376 "*shellMenu.filterSelection.acceleratorText: Alt+R", 377 "*shellMenu.executeCommand.accelerator: Alt<Key>x", 378 "*shellMenu.executeCommand.acceleratorText: Alt+X", 379 "*shellMenu.executeCommandLine.accelerator: Ctrl<Key>KP_Enter", 380 "*shellMenu.executeCommandLine.acceleratorText: Ctrl+KP Enter", 381 "*shellMenu.cancelShellCommand.accelerator: Ctrl<Key>period", 382 "*shellMenu.cancelShellCommand.acceleratorText: Ctrl+.", 383 "*macroMenu.mnemonic: c", 384 "*macroMenu.learnKeystrokes.accelerator: Alt<Key>k", 385 "*macroMenu.learnKeystrokes.acceleratorText: Alt+K", 386 "*macroMenu.finishLearn.accelerator: Alt<Key>k", 387 "*macroMenu.finishLearn.acceleratorText: Alt+K", 388 "*macroMenu.cancelLearn.accelerator: Ctrl<Key>period", 389 "*macroMenu.cancelLearn.acceleratorText: Ctrl+.", 390 "*macroMenu.replayKeystrokes.accelerator: Ctrl<Key>k", 391 "*macroMenu.replayKeystrokes.acceleratorText: Ctrl+K", 392 "*macroMenu.repeat.accelerator: Ctrl<Key>comma", 393 "*macroMenu.repeat.acceleratorText: Ctrl+,", 394 "*windowsMenu.mnemonic: W", 395 "*windowsMenu.splitPane.accelerator: Ctrl<Key>2", 396 "*windowsMenu.splitPane.acceleratorText: Ctrl+2", 397 "*windowsMenu.closePane.accelerator: Ctrl<Key>1", 398 "*windowsMenu.closePane.acceleratorText: Ctrl+1", 399 "*helpMenu.mnemonic: H", 400 "?.help.helpForm.sw.helpText*baseTranslations: #override\ 401 <Key>Tab:help-focus-buttons()\\n\ 402 <Key>Return:help-button-action(\"close\")\\n\ 403 Ctrl<Key>F:help-button-action(\"find\")\\n\ 404 Ctrl<Key>G:help-button-action(\"findAgain\")\\n\ 405 <KeyPress>osfCancel:help-button-action(\"close\")\\n\ 406 ~Meta~Ctrl~Shift<Btn1Down>:\ 407 grab-focus() help-hyperlink()\\n\ 408 ~Meta~Ctrl~Shift<Btn1Up>:\ 409 help-hyperlink(\"current\", \"process-cancel\", \"extend-end\")\\n\ 410 ~Meta~Ctrl~Shift<Btn2Down>:\ 411 process-bdrag() help-hyperlink()\\n\ 412 ~Meta~Ctrl~Shift<Btn2Up>:\ 413 help-hyperlink(\"new\", \"process-cancel\", \"copy-to\")", 414 NULL 415 }; 416 417 static const char cmdLineHelp[] = 418 "Usage: xnedit [-read] [-create] [-line n | +n] [-server] [-do command]\n\ 419 [-tags file] [-tabs n] [-wrap] [-nowrap] [-autowrap]\n\ 420 [-autoindent] [-noautoindent] [-autosave] [-noautosave]\n\ 421 [-lm languagemode] [-rows n] [-columns n] [-font font]\n\ 422 [-geometry geometry] [-iconic] [-noiconic] [-svrname name]\n\ 423 [-display [host]:server[.screen] [-xrm resourcestring]\n\ 424 [-import file] [-background color] [-foreground color]\n\ 425 [-tabbed] [-untabbed] [-group] [-bgrun] [-V|-version]\n\ 426 [-h|-help] [--] [file...]\n"; 427 428 /* This constant will be used in preference keys. Hence, for now we do not 429 * change it for maintaining backwards compatibility to NEdit preferences 430 * TODO: maybe we should decouple AppName and preference keys 431 */ 432 static char *XNEditAppName = "nedit"; 433 434 char* GetAppName(void) 435 { 436 return XNEditAppName; 437 } 438 439 int main(int argc, char **argv) 440 { 441 int i, lineNum, nRead, fileSpecified = FALSE, editFlags = CREATE; 442 int gotoLine = False, macroFileRead = False, opts = True; 443 int iconic=False, tabbed = -1, group = 0, isTabbed; 444 char *toDoCommand = NULL, *geometry = NULL, *langMode = NULL; 445 char filename[MAXPATHLEN], pathname[MAXPATHLEN]; 446 XtAppContext context; 447 XrmDatabase prefDB; 448 WindowInfo *window = NULL, *lastFile = NULL; 449 static const char *protectedKeywords[] = {"-iconic", "-icon", "-geometry", 450 "-g", "-rv", "-reverse", "-bd", "-bordercolor", "-borderwidth", 451 "-bw", "-title", NULL}; 452 unsigned char* invalidBindings = NULL; 453 454 /* Warn user if this has been compiled wrong. */ 455 enum MotifStability stability = GetMotifStability(); 456 457 char *appNameVar = getenv("XNEDIT_APPNAME"); 458 if(appNameVar) { 459 XNEditAppName = appNameVar; 460 } 461 462 if (stability == MotifKnownBad) { 463 fputs("xnedit: WARNING: This version of XNEdit is built incorrectly, and will be unstable.\n", 464 stderr); 465 #ifndef BUILD_BROKEN_NEDIT 466 /* Dear maintainers who are patching this; please have mercy on your users and link against 467 a stable version of OpenMotif. */ 468 exit(EXIT_FAILURE); 469 #endif 470 } 471 472 /* Save the command which was used to invoke xnedit for restart command */ 473 ArgV0 = argv[0]; 474 475 for (i=1; i<argc; i++) { 476 if(opts && !strcmp(argv[1], "--")) { 477 opts = False; 478 continue; 479 } else if (opts && !strcmp(argv[i], "-bgrun")) { 480 BackgroundRun = True; 481 break; 482 } 483 } 484 opts = True; 485 486 int retpipe[2]; 487 pid_t pid = -1; 488 if(BackgroundRun) { 489 if(pipe(retpipe)) { 490 perror("pipe"); 491 fprintf(stderr, "Abort.\n"); 492 return 1; 493 } 494 pid = fork(); 495 if(pid < 0) { 496 perror("fork"); 497 fprintf(stderr, "Abort.\n"); 498 return 1; 499 } else if(pid > 0) { 500 int ret = 0; 501 if(read(retpipe[0], &ret, sizeof(int)) != sizeof(int)) { 502 return 1; 503 } 504 return ret; 505 } 506 } 507 508 509 /* Set locale for C library, X, and Motif input functions. 510 Reverts to "C" if requested locale not available. */ 511 XtSetLanguageProc(NULL, neditLanguageProc, NULL); 512 513 /* Initialize X toolkit (does not open display yet) */ 514 XtToolkitInitialize(); 515 context = XtCreateApplicationContext(); 516 517 /* Set up a warning handler to trap obnoxious Xt grab warnings */ 518 SuppressPassiveGrabWarnings(); 519 520 /* Set up a handler to suppress X warning messages by default */ 521 XtAppSetWarningHandler(context, noWarningFilter); 522 523 /* Set up default resources if no app-defaults file is found */ 524 XtAppSetFallbackResources(context, fallbackResources); 525 526 #if XmVersion >= 1002 527 /* Allow users to change tear off menus with X resources */ 528 XmRepTypeInstallTearOffModelConverter(); 529 #endif 530 531 #ifdef __EMX__ 532 /* expand wildcards if necessary */ 533 _wildcard(&argc, &argv); 534 #endif 535 536 /* Read the preferences file and command line into a database */ 537 prefDB = CreateNEditPrefDB(&argc, argv); 538 539 /* Open the display and read X database and remaining command line args. 540 XtOpenDisplay must be allowed to process some of the resource arguments 541 with its inaccessible internal option table, but others, like -geometry 542 and -iconic are per-window and it should not be allowed to consume them, 543 so we temporarily masked them out. */ 544 maskArgvKeywords(argc, argv, protectedKeywords); 545 /* X.Org 6.8 and above add support for ARGB visuals (with alpha-channel), 546 typically with a 32-bit color depth. By default, NEdit uses the visual 547 with the largest color depth. However, both OpenMotif and Lesstif 548 cannot handle ARGB visuals (crashes, weird display effects, ...), so 549 NEdit should avoid selecting such a visual. 550 Unfortunatly, there appears to be no reliable way to identify 551 ARGB visuals that doesn't require some of the recent X.Org 552 extensions. Luckily, the X.Org developers have provided a mechanism 553 that can hide these problematic visuals from the application. This can 554 be achieved by setting the XLIB_SKIP_ARGB_VISUALS environment variable. 555 Users can set this variable before starting NEdit, but it is much 556 more convenient that NEdit takes care of this. This must be done before 557 the display is opened (empirically verified). */ 558 putenv("XLIB_SKIP_ARGB_VISUALS=1"); 559 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS, 560 NULL, 0, &argc, argv); 561 unmaskArgvKeywords(argc, argv, protectedKeywords); 562 if (!TheDisplay) { 563 /* Respond to -V or -version even if there is no display */ 564 for (i = 1; i < argc && strcmp(argv[i], "--"); i++) 565 { 566 if (0 == strcmp(argv[i], "-V") || 0 == strcmp(argv[i], "-version")) 567 { 568 PrintVersion(); 569 exit(EXIT_SUCCESS); 570 } 571 } 572 fputs ("XNEdit: Can''t open display\n", stderr); 573 exit(EXIT_FAILURE); 574 } 575 576 defaultResourceDB = XtScreenDatabase(DefaultScreenOfDisplay(TheDisplay)); //XrmGetDatabase(TheDisplay); 577 578 /* Enable Xdnd */ 579 XdndInit(TheDisplay, context, dndOpenFileCB, NULL); 580 581 /* Must be done before creating widgets */ 582 fixupBrokenXKeysymDB(); 583 patchResourcesForVisual(); 584 patchResourcesForKDEbug(); 585 586 /* Initialize global symbols and subroutines used in the macro language */ 587 InitMacroGlobals(); 588 RegisterMacroSubroutines(); 589 590 /* init xdnd */ 591 592 593 /* Initialize TextWidget */ 594 TextWidgetClassInit(TheDisplay, NEDIT_XFT_FIXED_FONT ":size=11"); 595 596 /* Store preferences from the command line and .nedit file, 597 and set the appropriate preferences */ 598 RestoreNEditPrefs(prefDB, XtDatabase(TheDisplay)); 599 600 /* Intercept syntactically invalid virtual key bindings BEFORE we 601 create any shells. */ 602 invalidBindings = sanitizeVirtualKeyBindings(); 603 604 /* Create a hidden application shell that is the parent of all the 605 main editor windows. Realize it so it the window can act as 606 group leader. */ 607 TheAppShell = CreateShellWithBestVis(APP_NAME, 608 APP_CLASS, 609 applicationShellWidgetClass, 610 TheDisplay, 611 NULL, 612 0); 613 614 /* Restore the original bindings ASAP such that other apps are not affected. */ 615 restoreInsaneVirtualKeyBindings(invalidBindings); 616 617 XtSetMappedWhenManaged(TheAppShell, False); 618 XtRealizeWidget(TheAppShell); 619 620 #ifndef NO_SESSION_RESTART 621 AttachSessionMgrHandler(TheAppShell); 622 #endif 623 624 /* More preference stuff */ 625 LoadPrintPreferences(XtDatabase(TheDisplay), APP_NAME, APP_CLASS, True); 626 SetDeleteRemap(GetPrefMapDelete()); 627 SetPointerCenteredDialogs(GetPrefRepositionDialogs()); 628 629 /* Set up action procedures for menu item commands */ 630 InstallMenuActions(context); 631 632 /* Add Actions for following hyperlinks in the help window */ 633 InstallHelpLinkActions(context); 634 /* Add actions for mouse wheel support in scrolled windows (except text 635 area) */ 636 InstallMouseWheelActions(context); 637 638 /* Install word delimiters for regular expression matching */ 639 SetREDefaultWordDelimiters(GetPrefDelimiters()); 640 641 /* Read the nedit dynamic database of files for the Open Previous 642 command (and eventually other information as well) */ 643 ReadNEditDB(); 644 645 /* Read database of search/replace history. It's called here 646 for enabling the Find/Replace Again menu items. */ 647 ReadSearchHistory(); 648 649 /* Process -import command line argument before others which might 650 open windows (loading preferences doesn't update menu settings, 651 which would then be out of sync with the real preference settings) */ 652 for (i=1; i<argc; i++) { 653 if(!strcmp(argv[i], "--")) { 654 break; /* treat all remaining arguments as filenames */ 655 } else if (!strcmp(argv[i], "-import")) { 656 nextArg(argc, argv, &i); 657 ImportPrefFile(argv[i], False); 658 } else if (!strcmp(argv[i], "-importold")) { 659 nextArg(argc, argv, &i); 660 ImportPrefFile(argv[i], True); 661 } 662 } 663 664 /* Load the default tags file. Don't complain if it doesn't load, the tag 665 file resource is intended to be set and forgotten. Running nedit in a 666 directory without a tags should not cause it to spew out errors. */ 667 if (*GetPrefTagFile() != '\0') { 668 AddTagsFile(GetPrefTagFile(), TAG); 669 } 670 671 if (strcmp(GetPrefServerName(), "") != 0) { 672 IsServer = True; 673 } 674 675 /* Process any command line arguments (-tags, -do, -read, -create, 676 +<line_number>, -line, -server, and files to edit) not already 677 processed by RestoreNEditPrefs. */ 678 fileSpecified = FALSE; 679 for (i=1; i<argc; i++) { 680 if (opts && !strcmp(argv[i], "--")) { 681 opts = False; /* treat all remaining arguments as filenames */ 682 continue; 683 } else if (opts && !strcmp(argv[i], "-tags")) { 684 nextArg(argc, argv, &i); 685 if (!AddTagsFile(argv[i], TAG)) 686 fprintf(stderr, "XNEdit: Unable to load tags file\n"); 687 } else if (opts && !strcmp(argv[i], "-do")) { 688 nextArg(argc, argv, &i); 689 if (checkDoMacroArg(argv[i])) 690 toDoCommand = argv[i]; 691 } else if (opts && !strcmp(argv[i], "-read")) { 692 editFlags |= PREF_READ_ONLY; 693 } else if (opts && !strcmp(argv[i], "-create")) { 694 editFlags |= SUPPRESS_CREATE_WARN; 695 } else if (opts && !strcmp(argv[i], "-tabbed")) { 696 tabbed = 1; 697 group = 0; /* override -group option */ 698 } else if (opts && !strcmp(argv[i], "-untabbed")) { 699 tabbed = 0; 700 group = 0; /* override -group option */ 701 } else if (opts && !strcmp(argv[i], "-group")) { 702 group = 2; /* 2: start new group, 1: in group */ 703 } else if (opts && !strcmp(argv[i], "-line")) { 704 nextArg(argc, argv, &i); 705 nRead = sscanf(argv[i], "%d", &lineNum); 706 if (nRead != 1) 707 fprintf(stderr, "XNEdit: argument to line should be a number\n"); 708 else 709 gotoLine = True; 710 } else if (opts && (*argv[i] == '+')) { 711 nRead = sscanf((argv[i]+1), "%d", &lineNum); 712 if (nRead != 1) 713 fprintf(stderr, "XNEdit: argument to + should be a number\n"); 714 else 715 gotoLine = True; 716 } else if (opts && !strcmp(argv[i], "-server")) { 717 IsServer = True; 718 } else if (opts && !strcmp(argv[i], "-bgrun")) { 719 /* noop */ 720 } else if (opts && !strcmp(argv[i], "-xwarn")) { 721 XtAppSetWarningHandler(context, showWarningFilter); 722 } else if (opts && (!strcmp(argv[i], "-iconic") || 723 !strcmp(argv[i], "-icon"))) { 724 iconic = True; 725 } else if (opts && !strcmp(argv[i], "-noiconic")) { 726 iconic = False; 727 } else if (opts && (!strcmp(argv[i], "-geometry") || 728 !strcmp(argv[i], "-g"))) { 729 nextArg(argc, argv, &i); 730 geometry = argv[i]; 731 } else if (opts && !strcmp(argv[i], "-lm")) { 732 nextArg(argc, argv, &i); 733 langMode = argv[i]; 734 } else if (opts && !strcmp(argv[i], "-import")) { 735 nextArg(argc, argv, &i); /* already processed, skip */ 736 } else if (opts && (!strcmp(argv[i], "-V") || 737 !strcmp(argv[i], "-version"))) { 738 PrintVersion(); 739 exit(EXIT_SUCCESS); 740 } else if (opts && (!strcmp(argv[i], "-h") || 741 !strcmp(argv[i], "-help"))) { 742 fprintf(stderr, "%s", cmdLineHelp); 743 exit(EXIT_SUCCESS); 744 } else if (opts && (*argv[i] == '-')) { 745 fprintf(stderr, "xnedit: Unrecognized option %s\n%s", argv[i], 746 cmdLineHelp); 747 exit(EXIT_FAILURE); 748 } else { 749 if (ParseFilename(argv[i], filename, pathname) == 0 ) { 750 /* determine if file is to be openned in new tab, by 751 factoring the options -group, -tabbed & -untabbed */ 752 if (group == 2) { 753 isTabbed = 0; /* start a new window for new group */ 754 group = 1; /* next file will be within group */ 755 } else if (group == 1) { 756 isTabbed = 1; /* new tab for file in group */ 757 } else { /* not in group */ 758 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed; 759 } 760 761 /* determine filter */ 762 size_t pathlen = strlen(pathname); 763 size_t namelen = strlen(filename); 764 char *fullpath = NEditMalloc(pathlen + namelen + 1); 765 memcpy(fullpath, pathname, pathlen); 766 memcpy(fullpath+pathlen, filename, namelen); 767 fullpath[pathlen+namelen] = '\0'; 768 IOFilter *filter = GetFilterForPath(fullpath); 769 const char *filter_name = filter ? filter->name : NULL; 770 NEditFree(fullpath); 771 772 /* Files are opened in background to improve opening speed 773 by defering certain time consuiming task such as syntax 774 highlighting. At the end of the file-opening loop, the 775 last file opened will be raised to restore those deferred 776 items. The current file may also be raised if there're 777 macros to execute on. */ 778 window = EditExistingFile(WindowList, filename, pathname, NULL, 779 filter_name, editFlags, geometry, iconic, langMode, isTabbed, 780 True); 781 fileSpecified = TRUE; 782 if (window) { 783 CleanUpTabBarExposeQueue(window); 784 785 /* raise the last tab of previous window */ 786 if (lastFile && window->shell != lastFile->shell) { 787 CleanUpTabBarExposeQueue(lastFile); 788 RaiseDocument(lastFile); 789 } 790 791 if (!macroFileRead) { 792 ReadMacroInitFile(WindowList); 793 macroFileRead = True; 794 } 795 if (gotoLine) 796 SelectNumberedLine(window, lineNum); 797 if (toDoCommand != NULL) { 798 DoMacro(window, toDoCommand, "-do macro"); 799 toDoCommand = NULL; 800 if (!IsValidWindow(window)) 801 window = NULL; /* window closed by macro */ 802 if (lastFile && !IsValidWindow(lastFile)) 803 lastFile = NULL; /* window closed by macro */ 804 } 805 } 806 807 /* register last opened file for later use */ 808 if (window) 809 lastFile = window; 810 } else { 811 fprintf(stderr, "xnedit: file name too long: %s\n", argv[i]); 812 } 813 814 /* -line/+n does only affect the file following this switch */ 815 gotoLine = False; 816 } 817 } 818 819 /* Raise the last file opened */ 820 if (lastFile) { 821 CleanUpTabBarExposeQueue(lastFile); 822 RaiseDocument(lastFile); 823 } 824 CheckCloseDim(); 825 826 /* If no file to edit was specified, open a window to edit "Untitled" */ 827 if (!fileSpecified) { 828 EditNewFile(NULL, geometry, iconic, langMode, NULL); 829 ReadMacroInitFile(WindowList); 830 CheckCloseDim(); 831 if (toDoCommand != NULL) 832 DoMacro(WindowList, toDoCommand, "-do macro"); 833 } 834 835 /* Begin remembering last command invoked for "Repeat" menu item */ 836 AddLastCommandActionHook(context); 837 838 /* Set up communication port and write ~/.nedit_server_process file */ 839 if (IsServer) 840 InitServerCommunication(); 841 842 if (BackgroundRun) { 843 if(pid != 0) { 844 /* Tell the parent process to return */ 845 close(0); 846 close(1); 847 close(2); 848 } 849 int ret = 0; 850 write(retpipe[1], &ret, sizeof(int)); 851 close(retpipe[0]); 852 close(retpipe[1]); 853 } 854 855 /* Process events. */ 856 if (IsServer) 857 ServerMainLoop(context); 858 else 859 XtAppMainLoop(context); 860 861 /* Not reached but this keeps some picky compilers happy */ 862 return EXIT_SUCCESS; 863 } 864 865 static void nextArg(int argc, char **argv, int *argIndex) 866 { 867 if (*argIndex + 1 >= argc) { 868 fprintf(stderr, "XNEdit: %s requires an argument\n%s", argv[*argIndex], 869 cmdLineHelp); 870 exit(EXIT_FAILURE); 871 } 872 (*argIndex)++; 873 } 874 875 /* 876 ** Return True if -do macro is valid, otherwise write an error on stderr 877 */ 878 static int checkDoMacroArg(const char *macro) 879 { 880 Program *prog; 881 char *errMsg, *stoppedAt, *tMacro; 882 int macroLen; 883 884 /* Add a terminating newline (which command line users are likely to omit 885 since they are typically invoking a single routine) */ 886 macroLen = strlen(macro); 887 tMacro = (char*)NEditMalloc(strlen(macro)+2); 888 strncpy(tMacro, macro, macroLen); 889 tMacro[macroLen] = '\n'; 890 tMacro[macroLen+1] = '\0'; 891 892 /* Do a test parse */ 893 prog = ParseMacro(tMacro, &errMsg, &stoppedAt); 894 NEditFree(tMacro); 895 if (prog == NULL) { 896 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg); 897 return False; 898 } 899 FreeProgram(prog); 900 return True; 901 } 902 903 /* 904 ** maskArgvKeywords and unmaskArgvKeywords mangle selected keywords by 905 ** replacing the '-' with a space, for the purpose of hiding them from 906 ** XtOpenDisplay's option processing. Why this silly scheme? XtOpenDisplay 907 ** really needs to see command line arguments, particularly -display, but 908 ** there's no way to change the option processing table it uses, to keep 909 ** it from consuming arguments which are meant to apply per-window, like 910 ** -geometry and -iconic. 911 */ 912 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs) 913 { 914 int i, k; 915 916 for (i=1; i<argc; i++) 917 for (k=0; maskArgs[k]!=NULL; k++) 918 if (!strcmp(argv[i], maskArgs[k])) 919 argv[i][0] = ' '; 920 } 921 922 923 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs) 924 { 925 int i, k; 926 927 for (i=1; i<argc; i++) 928 for (k=0; maskArgs[k]!=NULL; k++) 929 if (argv[i][0]==' ' && !strcmp(&argv[i][1], &maskArgs[k][1])) 930 argv[i][0] = '-'; 931 } 932 933 934 /* 935 ** Some Linux distros ship with XKEYSYMDB set to a bogus filename which 936 ** breaks all Motif applications. Ignore that, and let X fall back on the 937 ** default which is far more likely to work. 938 */ 939 static void fixupBrokenXKeysymDB(void) 940 { 941 const char *keysym = getenv("XKEYSYMDB"); 942 943 if (keysym != NULL && access(keysym, F_OK) != 0) 944 putenv("XKEYSYMDB"); 945 } 946 947 /* 948 ** If we're not using the default visual, then some default resources in 949 ** the database (colors) are not valid, because they are indexes into the 950 ** default colormap. If we used them blindly, then we'd get "random" 951 ** unreadable colors. So we inspect the resource list, and use the 952 ** fallback "grey" color instead if this is the case. 953 */ 954 static void patchResourcesForVisual(void) 955 { 956 Cardinal i; 957 Visual *visual; 958 int depth; 959 Colormap map; 960 Boolean usingDefaultVisual; 961 XrmDatabase db; 962 963 if (!TheDisplay) 964 return; 965 966 db = XtDatabase(TheDisplay); 967 968 usingDefaultVisual = FindBestVisual(TheDisplay, 969 APP_NAME, 970 APP_CLASS, 971 &visual, 972 &depth, 973 &map); 974 975 if (!usingDefaultVisual) 976 { 977 #ifndef LESSTIF_VERSION 978 /* 979 For non-Lesstif versions, we have to put non-default visuals etc. 980 in the resource data base to make sure that all (shell) widgets 981 inherit them, especially Motif's own shells (eg, drag icons). 982 983 For Lesstif, this doesn't work, but luckily, Lesstif handles 984 non-default visuals etc. properly for its own shells and 985 we can take care of things for our shells (eg, call tips) through 986 our shell creation wrappers in misc.c. 987 */ 988 989 XrmValue value; 990 value.addr = (XPointer)&visual; 991 value.size = sizeof(visual); 992 XrmPutResource(&db, "*visual", "Visual", &value); 993 value.addr = (XPointer)&map; 994 value.size = sizeof(map); 995 XrmPutResource(&db, "*colormap", "Colormap", &value); 996 value.addr = (XPointer)&depth; 997 value.size = sizeof(depth); 998 XrmPutResource(&db, "*depth", "Int", &value); 999 1000 /* Drag-and-drop visuals do not work well when using a different 1001 visual. One some systems, you'll just get a funny-looking icon 1002 (maybe all-black) but on other systems it crashes with a BadMatch 1003 error. This appears to be an OSF Motif bug. It would be nicer 1004 to just disable the visual itself, instead of the entire drag 1005 operation. 1006 1007 Update: this is no longer necessary since all problems with 1008 non-default visuals should now be solved. 1009 1010 XrmPutStringResource(&db, "*dragInitiatorProtocolStyle", "DRAG_NONE"); 1011 */ 1012 #endif 1013 1014 for (i = 1; i < XtNumber(fallbackResources); ++i) 1015 { 1016 Cardinal resIndex = i - 1; 1017 1018 if (strstr(fallbackResources[resIndex], "*background:") || 1019 strstr(fallbackResources[resIndex], "*foreground:")) 1020 { 1021 /* Qualify by application name to prevent them from being 1022 converted against the wrong colormap. */ 1023 char buf[1024]; 1024 snprintf(buf, 2, "*%s", APP_NAME); 1025 strcat(buf, fallbackResources[resIndex]); 1026 XrmPutLineResource(&db, buf); 1027 } 1028 } 1029 } 1030 } 1031 1032 /* 1033 ** Several KDE version (2.x and 3.x) ship with a template application-default 1034 ** file for NEdit in which several strings have to be substituted in order to 1035 ** make it a valid .ad file. However, for some reason (a KDE bug?), the 1036 ** template sometimes ends up in the resource db unmodified, such that several 1037 ** invalid entries are present. This function checks for the presence of such 1038 ** invalid entries and silently replaces them with NEdit's default values where 1039 ** necessary. Without this, NEdit will typically write several warnings to 1040 ** the terminal (Cannot convert string "FONTLIST" to type FontStruct etc) and 1041 ** fall back on some really ugly colors and fonts. 1042 */ 1043 static void patchResourcesForKDEbug(void) 1044 { 1045 /* 1046 * These are the resources found the Nedit.ad template shipped with KDE 3.0. 1047 */ 1048 static const char* buggyResources[][3] = { 1049 { "*fontList", "FONTLIST", NEDIT_DEFAULT_FONT }, 1050 { "*XmText.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG }, 1051 { "*XmText.foreground", "FOREGROUND", NEDIT_DEFAULT_FG }, 1052 { "*XmTextField.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG }, 1053 { "*XmTextField.foreground", "FOREGROUND", NEDIT_DEFAULT_FG }, 1054 { "*XmList.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG }, 1055 { "*XmList.foreground", "FOREGROUND", NEDIT_DEFAULT_FG }, 1056 { "*helpText.background", "BACKGROUND", NEDIT_DEFAULT_HELP_BG }, 1057 { "*helpText.foreground", "FOREGROUND", NEDIT_DEFAULT_HELP_FG }, 1058 { "*background", "BACKGROUND", NEDIT_DEFAULT_BG }, 1059 { "*foreground", "FOREGROUND", NEDIT_DEFAULT_FG, }, 1060 { "*selectColor", "BACKGROUND", NEDIT_DEFAULT_SEL_BG }, 1061 { "*highlightColor", "BACKGROUND", NEDIT_DEFAULT_HI_BG }, 1062 { "*text.background", "WINDOW_BACKGROUND", NEDIT_DEFAULT_TEXT_BG }, 1063 { "*text.foreground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_FG }, 1064 { "*text.selectBackground", "SELECT_BACKGROUND", NEDIT_DEFAULT_SEL_BG }, 1065 { "*text.selectForeground", "SELECT_FOREGROUND", NEDIT_DEFAULT_SEL_FG }, 1066 { "*text.cursorForeground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_CURSOR_FG}, 1067 /* { "*remapDeleteKey", "False", }, OK */ 1068 /* { "!*text.heavyCursor", "True" }, OK */ 1069 /* { "!*BlinkRate", "0" }, OK */ 1070 /* { "*shell", "/bin/sh" }, OK */ 1071 { "*statsLine.background", "BACKGROUND", NEDIT_DEFAULT_BG }, 1072 { "*statsLine.foreground", "FOREGROUND", NEDIT_DEFAULT_FG }, 1073 { NULL, NULL, NULL } }; 1074 XrmDatabase db; 1075 int i; 1076 1077 if (!TheDisplay) 1078 return; 1079 1080 db = XtDatabase(TheDisplay); 1081 1082 i = 0; 1083 while (buggyResources[i][0]) 1084 { 1085 const char* resource = buggyResources[i][0]; 1086 const char* buggyValue = buggyResources[i][1]; 1087 const char* defaultValue = buggyResources[i][2]; 1088 char name[128]; 1089 char class[128]; 1090 char* type; 1091 XrmValue resValue; 1092 1093 snprintf(name, 128, "%s%s", APP_NAME, resource); 1094 snprintf(class, 128, "%s%s", APP_CLASS, resource); /* Is this ok ? */ 1095 1096 if (XrmGetResource(db, name, class, &type, &resValue) && 1097 !strcmp(type, XmRString)) 1098 { 1099 /* Buggy value? Replace by the default. */ 1100 if (!strcmp(resValue.addr, buggyValue)) 1101 { 1102 XrmPutStringResource(&db, &name[0], (char*)defaultValue); 1103 } 1104 } 1105 ++i; 1106 } 1107 } 1108 1109 int XNEditDefaultCharsetIsUTF8(void) 1110 { 1111 static int defaultCharsetIsUtf8 = -1; 1112 if(defaultCharsetIsUtf8 == -1) { 1113 char *l = nl_langinfo(CODESET); 1114 defaultCharsetIsUtf8 = !strcmp(l, "UTF-8") ? 1 : 0; 1115 } 1116 return defaultCharsetIsUtf8; 1117 } 1118 1119 /* 1120 ** Same as the default X language procedure, except we check if Motif can 1121 ** handle the locale as well. 1122 */ 1123 1124 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure) 1125 { 1126 /* "xnl" will be set if the user passes in a new language via the 1127 "-xnllanguage" flag. If it's empty, then setlocale will get 1128 the default locale by some system-dependent means (usually, 1129 reading some environment variables). */ 1130 1131 if (! setlocale(LC_ALL, xnl)) { 1132 XtWarning("locale not supported by C library, locale unchanged"); 1133 } 1134 1135 /* patchLocaleForMotif(); */ 1136 1137 if (! XSupportsLocale()) { 1138 XtWarning("locale not supported by Xlib, locale set to C"); 1139 setlocale(LC_ALL, "C"); 1140 } 1141 if (! XSetLocaleModifiers("")) 1142 XtWarning("X locale modifiers not supported, using default"); 1143 1144 return setlocale(LC_ALL, NULL); /* re-query in case overwritten */ 1145 } 1146 1147 static int sortAlphabetical(const void* k1, const void* k2) 1148 { 1149 const char* key1 = *(const char**)k1; 1150 const char* key2 = *(const char**)k2; 1151 return strcmp(key1, key2); 1152 } 1153 1154 /* 1155 * Checks whether a given virtual key binding string is invalid. 1156 * A binding is considered invalid if there are duplicate key entries. 1157 */ 1158 static int virtKeyBindingsAreInvalid(const unsigned char* bindings) 1159 { 1160 int maxCount = 1, i, count; 1161 const char *pos = (const char*)bindings; 1162 char *copy; 1163 char *pos2, *pos3; 1164 char **keys; 1165 1166 /* First count the number of bindings; bindings are separated by \n 1167 strings. The number of bindings equals the number of \n + 1. 1168 Beware of leading and trailing \n; the number is actually an 1169 upper bound on the number of entries. */ 1170 while ((pos = strstr(pos, "\n"))) 1171 { 1172 ++pos; 1173 ++maxCount; 1174 } 1175 1176 if (maxCount == 1) 1177 return False; /* One binding is always ok */ 1178 1179 keys = (char**)NEditMalloc(maxCount*sizeof(char*)); 1180 copy = NEditStrdup((const char*)bindings); 1181 i = 0; 1182 pos2 = copy; 1183 1184 count = 0; 1185 while (i<maxCount && pos2 && *pos2) 1186 { 1187 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2; 1188 1189 if (*pos2 == '!') /* Ignore comment lines */ 1190 { 1191 pos2 = strstr(pos2, "\n"); 1192 continue; /* Go to the next line */ 1193 } 1194 1195 if (*pos2) 1196 { 1197 keys[i++] = pos2; 1198 ++count; 1199 pos3 = strstr(pos2, ":"); 1200 if (pos3) 1201 { 1202 *pos3++ = 0; /* Cut the string and jump to the next entry */ 1203 pos2 = pos3; 1204 } 1205 pos2 = strstr(pos2, "\n"); 1206 } 1207 } 1208 1209 if (count <= 1) 1210 { 1211 free(keys); 1212 NEditFree(copy); 1213 return False; /* No conflict */ 1214 } 1215 1216 /* Sort the keys and look for duplicates */ 1217 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical); 1218 for (i=1; i<count; ++i) 1219 { 1220 if (!strcmp(keys[i-1], keys[i])) 1221 { 1222 /* Duplicate detected */ 1223 free(keys); 1224 NEditFree(copy); 1225 return True; 1226 } 1227 } 1228 free(keys); 1229 NEditFree(copy); 1230 return False; 1231 } 1232 1233 /* 1234 * Optionally sanitizes the Motif default virtual key bindings. 1235 * Some applications install invalid bindings (attached to the root window), 1236 * which cause certain keys to malfunction in NEdit. 1237 * Through an X-resource, users can choose whether they want 1238 * - to always keep the existing bindings 1239 * - to override the bindings only if they are invalid 1240 * - to always override the existing bindings. 1241 */ 1242 1243 static Atom virtKeyAtom; 1244 1245 static unsigned char* sanitizeVirtualKeyBindings(void) 1246 { 1247 int overrideBindings = GetPrefOverrideVirtKeyBindings(); 1248 Window rootWindow; 1249 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS"; 1250 Atom dummyAtom; 1251 int getFmt; 1252 unsigned long dummyULong, nItems; 1253 unsigned char *insaneVirtKeyBindings = NULL; 1254 1255 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL; 1256 1257 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False); 1258 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay)); 1259 1260 /* Remove the property, if it exists; we'll restore it later again */ 1261 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX, 1262 True, XA_STRING, &dummyAtom, &getFmt, &nItems, 1263 &dummyULong, &insaneVirtKeyBindings) != Success 1264 || nItems == 0) 1265 { 1266 return NULL; /* No binding yet; nothing to do */ 1267 } 1268 1269 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO) 1270 { 1271 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings)) 1272 { 1273 /* Restore the property immediately; it seems valid */ 1274 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8, 1275 PropModeReplace, insaneVirtKeyBindings, 1276 strlen((const char*)insaneVirtKeyBindings)); 1277 XFree((char*)insaneVirtKeyBindings); 1278 return NULL; /* Prevent restoration */ 1279 } 1280 } 1281 return insaneVirtKeyBindings; 1282 } 1283 1284 /* 1285 * NEdit should not mess with the bindings installed by other apps, so we 1286 * just restore whatever was installed, if necessary 1287 */ 1288 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings) 1289 { 1290 if (insaneVirtKeyBindings) 1291 { 1292 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay)); 1293 /* Restore the root window atom, such that we don't affect 1294 other apps. */ 1295 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8, 1296 PropModeReplace, insaneVirtKeyBindings, 1297 strlen((const char*)insaneVirtKeyBindings)); 1298 XFree((char*)insaneVirtKeyBindings); 1299 } 1300 } 1301 1302 /* 1303 ** Warning handler that suppresses harmless but annoying warnings generated 1304 ** by non-production Lesstif versions. 1305 */ 1306 static void showWarningFilter(String message) 1307 { 1308 const char* bogusMessages[] = { 1309 #ifdef LESSTIF_VERSION 1310 "XmFontListCreate() is an obsolete function!", 1311 "No type converter registered for ''String'' to ''PathMode'' conversion.", 1312 "XtRemoveGrab asked to remove a widget not on the list", 1313 #endif 1314 NULL 1315 }; 1316 const char **bogusMessage = &bogusMessages[0]; 1317 1318 while (*bogusMessage) { 1319 size_t bogusLen = strlen(*bogusMessage); 1320 if (strncmp(message, *bogusMessage, bogusLen) == 0) { 1321 #ifdef DEBUG_LESSTIF_WARNINGS 1322 /* Developers may want to see which messages are suppressed. */ 1323 fprintf(stderr, "[SUPPRESSED] %s\n", message); 1324 #endif 1325 return; 1326 } 1327 ++bogusMessage; 1328 } 1329 1330 /* An unknown message. Keep it. */ 1331 fprintf(stderr, "%s\n", message); 1332 } 1333 1334 static void noWarningFilter(String message) 1335 { 1336 return; 1337 } 1338 1339 static void dndOpenFileCB(Widget w, XtPointer value, XtPointer data) { 1340 char *urilist = value; 1341 1342 size_t len = strlen(urilist); 1343 1344 size_t start = 0; 1345 if(len > 7 && !memcmp(urilist, "file://", 7)) { 1346 start = 7; 1347 } 1348 1349 int err = 0; 1350 1351 // urldecode 1352 char *path = NEditMalloc(len + 1); 1353 int k = 0; 1354 for(int i=start;i<len;i++) { 1355 char c = urilist[i]; 1356 if(c == '%') { 1357 if(i + 2 < len) { 1358 char code[3]; 1359 code[0] = urilist[i+1]; 1360 code[1] = urilist[i+2]; 1361 code[2] = '\0'; 1362 1363 errno = 0; 1364 char *end = NULL; 1365 int ascii = (int)strtol(code, &end, 16); 1366 if(errno == 0 && end == &code[2]) { 1367 path[k] = ascii; 1368 i += 2; 1369 } else { 1370 err = 1; 1371 break; 1372 } 1373 } else { 1374 err = 1; 1375 break; 1376 } 1377 } else if(c == '\n' || c == '\r') { 1378 break; 1379 } else { 1380 path[k] = c; 1381 } 1382 1383 k++; 1384 } 1385 path[k] = '\0'; 1386 1387 // open file 1388 if(err == 0) { 1389 char *params[2]; 1390 params[0] = path; 1391 params[1] = NULL; 1392 XtCallActionProc(w, "open", NULL, params, 1); 1393 } else { 1394 fprintf(stderr, "dnd open file: url decode error\n"); 1395 } 1396 1397 NEditFree(path); 1398 } 1399 1400 XrmDatabase GetDefaultResourceDB(void) 1401 { 1402 return defaultResourceDB; 1403 } 1404