Minor fix.
[nedit.git] / source / nedit.c
blobcef75c2948d9d9496fd9c52d995db6e3de465e3c
1 static const char CVSID[] = "$Id: nedit.c,v 1.91 2005/03/03 14:49:37 edg Exp $";
2 /*******************************************************************************
3 * *
4 * nedit.c -- Nirvana Editor main program *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute versions of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * May 10, 1991 *
25 * *
26 * Written by Mark Edel *
27 * *
28 * Modifications: *
29 * *
30 * 8/18/93 - Mark Edel & Joy Kyriakopulos - Ported to VMS *
31 * *
32 *******************************************************************************/
34 #ifdef HAVE_CONFIG_H
35 #include "../config.h"
36 #endif
38 #include "nedit.h"
39 /* #include "textBuf.h" */
40 #include "file.h"
41 #include "preferences.h"
42 #include "regularExp.h"
43 #include "selection.h"
44 #include "tags.h"
45 #include "menu.h"
46 #include "macro.h"
47 #include "server.h"
48 #include "interpret.h"
49 #include "parse.h"
50 #include "help.h"
51 #include "../util/misc.h"
52 #include "../util/printUtils.h"
53 #include "../util/fileUtils.h"
54 #include "../util/getfiles.h"
55 #include "../util/motif.h"
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
61 #include <limits.h>
63 #ifndef NO_XMIM
64 #include <X11/Xlocale.h>
65 #else
66 #include <locale.h>
67 #endif
68 #include <X11/Intrinsic.h>
69 #include <Xm/Xm.h>
70 #include <Xm/XmP.h>
71 #if XmVersion >= 1002
72 #include <Xm/RepType.h>
73 #endif
74 #ifdef VMS
75 #include <rmsdef.h>
76 #include "../util/VMSparam.h"
77 #include "../util/VMSUtils.h"
78 #else
79 #ifndef __MVS__
80 #include <sys/param.h>
81 #endif
82 #endif /*VMS*/
85 #ifdef HAVE_DEBUG_H
86 #include "../debug.h"
87 #endif
89 static void nextArg(int argc, char **argv, int *argIndex);
90 static int checkDoMacroArg(const char *macro);
91 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure);
92 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs);
93 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs);
94 static void patchResourcesForVisual(void);
95 static void patchResourcesForKDEbug(void);
96 static void patchLocaleForMotif(void);
97 static unsigned char* sanitizeVirtualKeyBindings(void);
98 static int sortAlphabetical(const void* k1, const void* k2);
99 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
100 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
101 #ifdef LESSTIF_VERSION
102 static void bogusWarningFilter(String);
103 #endif
105 WindowInfo *WindowList = NULL;
106 Display *TheDisplay = NULL;
107 char *ArgV0 = NULL;
108 Boolean IsServer = False;
109 Widget TheAppShell;
111 /* Reasons for choice of default font qualifications:
113 iso8859 appears to be necessary for newer versions of XFree86 that
114 default to Unicode encoding, which doesn't quite work with Motif.
115 Otherwise Motif puts up garbage (square blocks).
117 (This of course, is a stupid default because there are far more iso8859
118 apps than Unicode apps. But the X folks insist it's a client bug. Hah.)
120 RedHat 7.3 won't default to '-1' for an encoding, if left with a *,
121 and so reverts to "fixed". Yech. */
123 #define NEDIT_DEFAULT_FONT "-*-helvetica-medium-r-normal-*-*-120-*-*-*-iso8859-1," \
124 "-*-helvetica-bold-r-normal-*-*-120-*-*-*-iso8859-1=BOLD," \
125 "-*-helvetica-medium-o-normal-*-*-120-*-*-*-iso8859-1=ITALIC"
127 #define NEDIT_FIXED_FONT "-*-courier-medium-r-normal-*-*-120-*-*-*-iso8859-1," \
128 "-*-courier-bold-r-normal-*-*-120-*-*-*-iso8859-1=BOLD," \
129 "-*-courier-medium-o-normal-*-*-120-*-*-*-iso8859-1=ITALIC"
131 #define NEDIT_DEFAULT_BG "#b3b3b3"
133 #define NEDIT_TEXT_TRANSLATIONS "#override\\n" \
134 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \
135 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \
136 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \
137 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n"
139 static char *fallbackResources[] = {
140 /* Try to avoid Motif's horrificly ugly default colors and fonts,
141 if the user's environment provides no usable defaults. We try
142 to choose a Windows-y default color setting here. Editable text
143 fields are forced to a fixed-pitch font for usability.
145 By using the VendorShell fontList resources, Motif automatically
146 groups the fonts into the right classes. It's then easier for
147 the user or environment to override this sensibly:
149 nedit -xrm '*textFontList: myfont'
151 This is broken in recent versions of LessTif.
154 #ifdef LESSTIF_VERSION
155 "*FontList: " NEDIT_DEFAULT_FONT,
156 "*XmText.FontList: " NEDIT_FIXED_FONT,
157 "*XmTextField.FontList: " NEDIT_FIXED_FONT,
158 "*XmList.FontList: " NEDIT_FIXED_FONT,
159 "*XmFileSelectionBox*XmList.FontList: " NEDIT_FIXED_FONT,
160 #else
161 "*buttonFontList: " NEDIT_DEFAULT_FONT,
162 "*labelFontList: " NEDIT_DEFAULT_FONT,
163 "*textFontList: " NEDIT_FIXED_FONT,
164 #endif
166 "*background: " NEDIT_DEFAULT_BG,
167 "*foreground: " NEDIT_DEFAULT_FG,
168 "*XmText.foreground: " NEDIT_DEFAULT_FG,
169 "*XmText.background: " NEDIT_DEFAULT_TEXT_BG,
170 "*XmList.foreground: " NEDIT_DEFAULT_FG,
171 "*XmList.background: " NEDIT_DEFAULT_TEXT_BG,
172 "*XmTextField.foreground: " NEDIT_DEFAULT_FG,
173 "*XmTextField.background: " NEDIT_DEFAULT_TEXT_BG,
175 /* Use baseTranslations as per Xt Programmer's Manual, 10.2.12 */
176 "*XmText.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
177 "*XmTextField.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
179 "*XmLFolder.highlightThickness: 0",
180 "*XmLFolder.shadowThickness: 1",
181 "*XmLFolder.maxTabWidth: 150",
182 "*XmLFolder.traversalOn: False",
183 "*XmLFolder.inactiveForeground: #666" ,
184 "*tab.alignment: XmALIGNMENT_BEGINNING",
185 "*tab.marginWidth: 0",
186 "*tab.marginHeight: 1",
188 /* Prevent the file selection box from acting stupid. */
189 "*XmFileSelectionBox.resizePolicy: XmRESIZE_NONE",
190 "*XmFileSelectionBox.textAccelerators:",
192 /* NEdit-specific widgets. Theses things should probably be manually
193 jammed into the database, rather than fallbacks. We really want
194 the accelerators to be there even if someone creates an app-defaults
195 file against our wishes. */
197 "*text.lineNumForeground: " NEDIT_DEFAULT_LINENO_FG,
198 "*text.background: " NEDIT_DEFAULT_TEXT_BG,
199 "*text.foreground: " NEDIT_DEFAULT_FG,
200 "*text.highlightForeground: " NEDIT_DEFAULT_HI_FG,
201 "*text.highlightBackground: " NEDIT_DEFAULT_HI_BG,
202 "*textFrame.shadowThickness: 1",
203 "*menuBar.marginHeight: 0",
204 "*menuBar.shadowThickness: 1",
205 "*pane.sashHeight: 11",
206 "*pane.sashWidth: 11",
207 "*pane.marginWidth: 0",
208 "*pane.marginHeight: 0",
209 "*scrolledW*spacing: 0",
210 "*text.selectionArrayCount: 3",
211 "*helpText.background: " NEDIT_DEFAULT_HELP_BG,
212 "*helpText.foreground: " NEDIT_DEFAULT_HELP_FG,
213 "*helpText.selectBackground: " NEDIT_DEFAULT_BG,
214 "*statsLine.background: " NEDIT_DEFAULT_BG,
215 "*statsLine.FontList: " NEDIT_DEFAULT_FONT,
216 "*calltip.background: LemonChiffon1",
217 "*calltip.foreground: black",
218 "*iSearchForm*highlightThickness: 1",
219 "*fileMenu.tearOffModel: XmTEAR_OFF_ENABLED",
220 "*editMenu.tearOffModel: XmTEAR_OFF_ENABLED",
221 "*searchMenu.tearOffModel: XmTEAR_OFF_ENABLED",
222 "*preferencesMenu.tearOffModel: XmTEAR_OFF_ENABLED",
223 "*windowsMenu.tearOffModel: XmTEAR_OFF_ENABLED",
224 "*shellMenu.tearOffModel: XmTEAR_OFF_ENABLED",
225 "*macroMenu.tearOffModel: XmTEAR_OFF_ENABLED",
226 "*helpMenu.tearOffModel: XmTEAR_OFF_ENABLED",
227 "*fileMenu.mnemonic: F",
228 "*fileMenu.new.accelerator: Ctrl<Key>n",
229 "*fileMenu.new.acceleratorText: Ctrl+N",
230 "*fileMenu.newOpposite.accelerator: Shift Ctrl<Key>n",
231 "*fileMenu.newOpposite.acceleratorText: Shift+Ctrl+N",
232 "*fileMenu.open.accelerator: Ctrl<Key>o",
233 "*fileMenu.open.acceleratorText: Ctrl+O",
234 "*fileMenu.openSelected.accelerator: Ctrl<Key>y",
235 "*fileMenu.openSelected.acceleratorText: Ctrl+Y",
236 "*fileMenu.close.accelerator: Ctrl<Key>w",
237 "*fileMenu.close.acceleratorText: Ctrl+W",
238 "*fileMenu.save.accelerator: Ctrl<Key>s",
239 "*fileMenu.save.acceleratorText: Ctrl+S",
240 "*fileMenu.includeFile.accelerator: Alt<Key>i",
241 "*fileMenu.includeFile.acceleratorText: Alt+I",
242 "*fileMenu.print.accelerator: Ctrl<Key>p",
243 "*fileMenu.print.acceleratorText: Ctrl+P",
244 "*fileMenu.exit.accelerator: Ctrl<Key>q",
245 "*fileMenu.exit.acceleratorText: Ctrl+Q",
246 "*editMenu.mnemonic: E",
247 "*editMenu.undo.accelerator: Ctrl<Key>z",
248 "*editMenu.undo.acceleratorText: Ctrl+Z",
249 "*editMenu.redo.accelerator: Shift Ctrl<Key>z",
250 "*editMenu.redo.acceleratorText: Shift+Ctrl+Z",
251 "*editMenu.cut.accelerator: Ctrl<Key>x",
252 "*editMenu.cut.acceleratorText: Ctrl+X",
253 "*editMenu.copy.accelerator: Ctrl<Key>c",
254 "*editMenu.copy.acceleratorText: Ctrl+C",
255 "*editMenu.paste.accelerator: Ctrl<Key>v",
256 "*editMenu.paste.acceleratorText: Ctrl+V",
257 "*editMenu.pasteColumn.accelerator: Shift Ctrl<Key>v",
258 "*editMenu.pasteColumn.acceleratorText: Ctrl+Shift+V",
259 "*editMenu.delete.acceleratorText: Del",
260 "*editMenu.selectAll.accelerator: Ctrl<Key>a",
261 "*editMenu.selectAll.acceleratorText: Ctrl+A",
262 "*editMenu.shiftLeft.accelerator: Ctrl<Key>9",
263 "*editMenu.shiftLeft.acceleratorText: [Shift]Ctrl+9",
264 "*editMenu.shiftLeftShift.accelerator: Shift Ctrl<Key>9",
265 "*editMenu.shiftRight.accelerator: Ctrl<Key>0",
266 "*editMenu.shiftRight.acceleratorText: [Shift]Ctrl+0",
267 "*editMenu.shiftRightShift.accelerator: Shift Ctrl<Key>0",
268 "*editMenu.upperCase.accelerator: Ctrl<Key>6",
269 "*editMenu.upperCase.acceleratorText: Ctrl+6",
270 "*editMenu.lowerCase.accelerator: Shift Ctrl<Key>6",
271 "*editMenu.lowerCase.acceleratorText: Shift+Ctrl+6",
272 "*editMenu.fillParagraph.accelerator: Ctrl<Key>j",
273 "*editMenu.fillParagraph.acceleratorText: Ctrl+J",
274 "*editMenu.insertFormFeed.accelerator: Alt Ctrl<Key>l",
275 "*editMenu.insertFormFeed.acceleratorText: Alt+Ctrl+L",
276 "*editMenu.insertCtrlCode.accelerator: Alt Ctrl<Key>i",
277 "*editMenu.insertCtrlCode.acceleratorText: Alt+Ctrl+I",
278 "*searchMenu.mnemonic: S",
279 "*searchMenu.find.accelerator: Ctrl<Key>f",
280 "*searchMenu.find.acceleratorText: [Shift]Ctrl+F",
281 "*searchMenu.findShift.accelerator: Shift Ctrl<Key>f",
282 "*searchMenu.findAgain.accelerator: Ctrl<Key>g",
283 "*searchMenu.findAgain.acceleratorText: [Shift]Ctrl+G",
284 "*searchMenu.findAgainShift.accelerator: Shift Ctrl<Key>g",
285 "*searchMenu.findSelection.accelerator: Ctrl<Key>h",
286 "*searchMenu.findSelection.acceleratorText: [Shift]Ctrl+H",
287 "*searchMenu.findSelectionShift.accelerator: Shift Ctrl<Key>h",
288 "*searchMenu.findIncremental.accelerator: Ctrl<Key>i",
289 "*searchMenu.findIncrementalShift.accelerator: Shift Ctrl<Key>i",
290 "*searchMenu.findIncremental.acceleratorText: [Shift]Ctrl+I",
291 "*searchMenu.replace.accelerator: Ctrl<Key>r",
292 "*searchMenu.replace.acceleratorText: [Shift]Ctrl+R",
293 "*searchMenu.replaceShift.accelerator: Shift Ctrl<Key>r",
294 "*searchMenu.findReplace.accelerator: Ctrl<Key>r",
295 "*searchMenu.findReplace.acceleratorText: [Shift]Ctrl+R",
296 "*searchMenu.findReplaceShift.accelerator: Shift Ctrl<Key>r",
297 "*searchMenu.replaceFindAgain.accelerator: Ctrl<Key>t",
298 "*searchMenu.replaceFindAgain.acceleratorText: [Shift]Ctrl+T",
299 "*searchMenu.replaceFindAgainShift.accelerator: Shift Ctrl<Key>t",
300 "*searchMenu.replaceAgain.accelerator: Alt<Key>t",
301 "*searchMenu.replaceAgain.acceleratorText: [Shift]Alt+T",
302 "*searchMenu.replaceAgainShift.accelerator: Shift Alt<Key>t",
303 "*searchMenu.gotoLineNumber.accelerator: Ctrl<Key>l",
304 "*searchMenu.gotoLineNumber.acceleratorText: Ctrl+L",
305 "*searchMenu.gotoSelected.accelerator: Ctrl<Key>e",
306 "*searchMenu.gotoSelected.acceleratorText: Ctrl+E",
307 "*searchMenu.mark.accelerator: Alt<Key>m",
308 "*searchMenu.mark.acceleratorText: Alt+M a-z",
309 "*searchMenu.gotoMark.accelerator: Alt<Key>g",
310 "*searchMenu.gotoMark.acceleratorText: [Shift]Alt+G a-z",
311 "*searchMenu.gotoMarkShift.accelerator: Shift Alt<Key>g",
312 "*searchMenu.gotoMatching.accelerator: Ctrl<Key>m",
313 "*searchMenu.gotoMatching.acceleratorText: [Shift]Ctrl+M",
314 "*searchMenu.gotoMatchingShift.accelerator: Shift Ctrl<Key>m",
315 "*searchMenu.findDefinition.accelerator: Ctrl<Key>d",
316 "*searchMenu.findDefinition.acceleratorText: Ctrl+D",
317 "*searchMenu.showCalltip.accelerator: Ctrl<Key>apostrophe",
318 "*searchMenu.showCalltip.acceleratorText: Ctrl+'",
319 "*preferencesMenu.mnemonic: P",
320 "*preferencesMenu.statisticsLine.accelerator: Alt<Key>a",
321 "*preferencesMenu.statisticsLine.acceleratorText: Alt+A",
322 "*preferencesMenu.overtype.acceleratorText: Insert",
323 "*shellMenu.mnemonic: l",
324 "*shellMenu.filterSelection.accelerator: Alt<Key>r",
325 "*shellMenu.filterSelection.acceleratorText: Alt+R",
326 "*shellMenu.executeCommand.accelerator: Alt<Key>x",
327 "*shellMenu.executeCommand.acceleratorText: Alt+X",
328 "*shellMenu.executeCommandLine.accelerator: Ctrl<Key>KP_Enter",
329 "*shellMenu.executeCommandLine.acceleratorText: Ctrl+KP Enter",
330 "*shellMenu.cancelShellCommand.accelerator: Ctrl<Key>period",
331 "*shellMenu.cancelShellCommand.acceleratorText: Ctrl+.",
332 "*macroMenu.mnemonic: c",
333 "*macroMenu.learnKeystrokes.accelerator: Alt<Key>k",
334 "*macroMenu.learnKeystrokes.acceleratorText: Alt+K",
335 "*macroMenu.finishLearn.accelerator: Alt<Key>k",
336 "*macroMenu.finishLearn.acceleratorText: Alt+K",
337 "*macroMenu.cancelLearn.accelerator: Ctrl<Key>period",
338 "*macroMenu.cancelLearn.acceleratorText: Ctrl+.",
339 "*macroMenu.replayKeystrokes.accelerator: Ctrl<Key>k",
340 "*macroMenu.replayKeystrokes.acceleratorText: Ctrl+K",
341 "*macroMenu.repeat.accelerator: Ctrl<Key>comma",
342 "*macroMenu.repeat.acceleratorText: Ctrl+,",
343 "*windowsMenu.mnemonic: W",
344 "*windowsMenu.splitPane.accelerator: Ctrl<Key>2",
345 "*windowsMenu.splitPane.acceleratorText: Ctrl+2",
346 "*windowsMenu.closePane.accelerator: Ctrl<Key>1",
347 "*windowsMenu.closePane.acceleratorText: Ctrl+1",
348 "*helpMenu.mnemonic: H",
349 "nedit.help.helpForm.sw.helpText*baseTranslations: #override\
350 <Key>Tab:help-focus-buttons()\\n\
351 <Key>Return:help-button-action(\"close\")\\n\
352 Ctrl<Key>F:help-button-action(\"find\")\\n\
353 Ctrl<Key>G:help-button-action(\"findAgain\")\\n\
354 <KeyPress>osfCancel:help-button-action(\"close\")\\n\
355 ~Meta~Ctrl~Shift<Btn1Down>:\
356 grab-focus() help-hyperlink()\\n\
357 ~Meta~Ctrl~Shift<Btn1Up>:\
358 help-hyperlink(\"current\", \"process-cancel\", \"extend-end\")\\n\
359 ~Meta~Ctrl~Shift<Btn2Down>:\
360 process-bdrag() help-hyperlink()\\n\
361 ~Meta~Ctrl~Shift<Btn2Up>:\
362 help-hyperlink(\"new\", \"process-cancel\", \"copy-to\")",
363 NULL
366 static const char cmdLineHelp[] =
367 #ifndef VMS
368 "Usage: nedit [-read] [-create] [-line n | +n] [-server] [-do command]\n\
369 [-tags file] [-tabs n] [-wrap] [-nowrap] [-autowrap]\n\
370 [-autoindent] [-noautoindent] [-autosave] [-noautosave]\n\
371 [-lm languagemode] [-rows n] [-columns n] [-font font]\n\
372 [-geometry geometry] [-iconic] [-noiconic] [-svrname name]\n\
373 [-display [host]:server[.screen] [-xrm resourcestring]\n\
374 [-import file] [-background color] [-foreground color]\n\
375 [-tabbed] [-untabbed] [-group] [-V|-version] [-h|-help]\n\
376 [--] [file...]\n";
377 #else
378 "[Sorry, no on-line help available.]\n"; /* Why is that ? */
379 #endif /*VMS*/
381 int main(int argc, char **argv)
383 int i, lineNum, nRead, fileSpecified = FALSE, editFlags = CREATE;
384 int gotoLine = False, macroFileRead = False, opts = True;
385 int iconic=False, tabbed = -1, group = 0, isTabbed;
386 char *toDoCommand = NULL, *geometry = NULL, *langMode = NULL;
387 char filename[MAXPATHLEN], pathname[MAXPATHLEN];
388 XtAppContext context;
389 XrmDatabase prefDB;
390 WindowInfo *window = NULL, *lastFile = NULL;
391 static const char *protectedKeywords[] = {"-iconic", "-icon", "-geometry",
392 "-g", "-rv", "-reverse", "-bd", "-bordercolor", "-borderwidth",
393 "-bw", "-title", NULL};
394 unsigned char* invalidBindings = NULL;
396 /* Warn user if this has been compiled wrong. */
397 enum MotifStability stability = GetMotifStability();
398 if (stability == MotifKnownBad) {
399 fprintf(stderr, "nedit: WARNING: This version of NEdit is built incorrectly, and will be unstable.\n"
400 "nedit: Please get a stable version of NEdit from http://www.nedit.org.\n");
403 /* Save the command which was used to invoke nedit for restart command */
404 ArgV0 = argv[0];
406 /* Set locale for C library, X, and Motif input functions.
407 Reverts to "C" if requested locale not available. */
408 XtSetLanguageProc(NULL, neditLanguageProc, NULL);
410 /* Initialize X toolkit (does not open display yet) */
411 XtToolkitInitialize();
412 context = XtCreateApplicationContext();
414 /* Set up a warning handler to trap obnoxious Xt grab warnings */
415 SuppressPassiveGrabWarnings();
417 #ifdef LESSTIF_VERSION
418 /* Set up a handler to suppress bogus Lesstif warning messages */
419 XtAppSetWarningHandler(context, bogusWarningFilter);
420 #endif
422 /* Set up default resources if no app-defaults file is found */
423 XtAppSetFallbackResources(context, fallbackResources);
425 #if XmVersion >= 1002
426 /* Allow users to change tear off menus with X resources */
427 XmRepTypeInstallTearOffModelConverter();
428 #endif
430 #ifdef VMS
431 /* Convert the command line to Unix style (This is not an ideal solution) */
432 ConvertVMSCommandLine(&argc, &argv);
433 #endif /*VMS*/
434 #ifdef __EMX__
435 /* expand wildcards if necessary */
436 _wildcard(&argc, &argv);
437 #endif
439 /* Read the preferences file and command line into a database */
440 prefDB = CreateNEditPrefDB(&argc, argv);
442 /* Open the display and read X database and remaining command line args.
443 XtOpenDisplay must be allowed to process some of the resource arguments
444 with its inaccessible internal option table, but others, like -geometry
445 and -iconic are per-window and it should not be allowed to consume them,
446 so we temporarily masked them out. */
447 maskArgvKeywords(argc, argv, protectedKeywords);
448 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS,
449 NULL, 0, &argc, argv);
450 unmaskArgvKeywords(argc, argv, protectedKeywords);
451 if (!TheDisplay) {
452 /* Respond to -V or -version even if there is no display */
453 for (i = 1; i < argc && strcmp(argv[i], "--"); i++)
455 if (0 == strcmp(argv[i], "-V") || 0 == strcmp(argv[i], "-version"))
457 PrintVersion();
458 exit(EXIT_SUCCESS);
461 XtWarning ("NEdit: Can't open display\n");
462 exit(EXIT_FAILURE);
465 /* Must be done before creating widgets */
466 patchResourcesForVisual();
467 patchResourcesForKDEbug();
469 /* Initialize global symbols and subroutines used in the macro language */
470 InitMacroGlobals();
471 RegisterMacroSubroutines();
473 /* Store preferences from the command line and .nedit file,
474 and set the appropriate preferences */
475 RestoreNEditPrefs(prefDB, XtDatabase(TheDisplay));
477 /* Intercept syntactically invalid virtual key bindings BEFORE we
478 create any shells. */
479 invalidBindings = sanitizeVirtualKeyBindings();
481 /* Create a hidden application shell that is the parent of all the
482 main editor windows. Realize it so it the window can act as
483 group leader. */
484 TheAppShell = CreateShellWithBestVis(APP_NAME,
485 APP_CLASS,
486 applicationShellWidgetClass,
487 TheDisplay,
488 NULL,
491 /* Restore the original bindings ASAP such that other apps are not affected. */
492 restoreInsaneVirtualKeyBindings(invalidBindings);
494 XtSetMappedWhenManaged(TheAppShell, False);
495 XtRealizeWidget(TheAppShell);
497 #ifndef NO_SESSION_RESTART
498 AttachSessionMgrHandler(TheAppShell);
499 #endif
501 /* More preference stuff */
502 LoadPrintPreferences(XtDatabase(TheDisplay), APP_NAME, APP_CLASS, True);
503 SetDeleteRemap(GetPrefMapDelete());
504 SetPointerCenteredDialogs(GetPrefRepositionDialogs());
505 SetGetEFTextFieldRemoval(!GetPrefStdOpenDialog());
507 /* Set up action procedures for menu item commands */
508 InstallMenuActions(context);
510 /* Add Actions for following hyperlinks in the help window */
511 InstallHelpLinkActions(context);
512 /* Add actions for mouse wheel support in scrolled windows (except text
513 area) */
514 InstallMouseWheelActions(context);
516 /* Install word delimiters for regular expression matching */
517 SetREDefaultWordDelimiters(GetPrefDelimiters());
519 /* Read the nedit dynamic database of files for the Open Previous
520 command (and eventually other information as well) */
521 ReadNEditDB();
523 /* Process -import command line argument before others which might
524 open windows (loading preferences doesn't update menu settings,
525 which would then be out of sync with the real preference settings) */
526 for (i=1; i<argc; i++) {
527 if(!strcmp(argv[i], "--")) {
528 break; /* treat all remaining arguments as filenames */
529 } else if (!strcmp(argv[i], "-import")) {
530 nextArg(argc, argv, &i);
531 ImportPrefFile(argv[i], False);
532 } else if (!strcmp(argv[i], "-importold")) {
533 nextArg(argc, argv, &i);
534 ImportPrefFile(argv[i], True);
538 /* Load the default tags file. Don't complain if it doesn't load, the tag
539 file resource is intended to be set and forgotten. Running nedit in a
540 directory without a tags should not cause it to spew out errors. */
541 if (*GetPrefTagFile() != '\0') {
542 AddTagsFile(GetPrefTagFile(), TAG);
545 if (strcmp(GetPrefServerName(), "") != 0) {
546 IsServer = True;
549 /* Process any command line arguments (-tags, -do, -read, -create,
550 +<line_number>, -line, -server, and files to edit) not already
551 processed by RestoreNEditPrefs. */
552 fileSpecified = FALSE;
553 for (i=1; i<argc; i++) {
554 if (opts && !strcmp(argv[i], "--")) {
555 opts = False; /* treat all remaining arguments as filenames */
556 continue;
557 } else if (opts && !strcmp(argv[i], "-tags")) {
558 nextArg(argc, argv, &i);
559 if (!AddTagsFile(argv[i], TAG))
560 fprintf(stderr, "NEdit: Unable to load tags file\n");
561 } else if (opts && !strcmp(argv[i], "-do")) {
562 nextArg(argc, argv, &i);
563 if (checkDoMacroArg(argv[i]))
564 toDoCommand = argv[i];
565 } else if (opts && !strcmp(argv[i], "-read")) {
566 editFlags |= PREF_READ_ONLY;
567 } else if (opts && !strcmp(argv[i], "-create")) {
568 editFlags |= SUPPRESS_CREATE_WARN;
569 } else if (opts && !strcmp(argv[i], "-tabbed")) {
570 tabbed = 1;
571 group = 0; /* override -group option */
572 } else if (opts && !strcmp(argv[i], "-untabbed")) {
573 tabbed = 0;
574 group = 0; /* override -group option */
575 } else if (opts && !strcmp(argv[i], "-group")) {
576 group = 2; /* 2: start new group, 1: in group */
577 } else if (opts && !strcmp(argv[i], "-line")) {
578 nextArg(argc, argv, &i);
579 nRead = sscanf(argv[i], "%d", &lineNum);
580 if (nRead != 1)
581 fprintf(stderr, "NEdit: argument to line should be a number\n");
582 else
583 gotoLine = True;
584 } else if (opts && (*argv[i] == '+')) {
585 nRead = sscanf((argv[i]+1), "%d", &lineNum);
586 if (nRead != 1)
587 fprintf(stderr, "NEdit: argument to + should be a number\n");
588 else
589 gotoLine = True;
590 } else if (opts && !strcmp(argv[i], "-server")) {
591 IsServer = True;
592 } else if (opts && (!strcmp(argv[i], "-iconic") ||
593 !strcmp(argv[i], "-icon"))) {
594 iconic = True;
595 } else if (opts && !strcmp(argv[i], "-noiconic")) {
596 iconic = False;
597 } else if (opts && (!strcmp(argv[i], "-geometry") ||
598 !strcmp(argv[i], "-g"))) {
599 nextArg(argc, argv, &i);
600 geometry = argv[i];
601 } else if (opts && !strcmp(argv[i], "-lm")) {
602 nextArg(argc, argv, &i);
603 langMode = argv[i];
604 } else if (opts && !strcmp(argv[i], "-import")) {
605 nextArg(argc, argv, &i); /* already processed, skip */
606 } else if (opts && (!strcmp(argv[i], "-V") ||
607 !strcmp(argv[i], "-version"))) {
608 PrintVersion();
609 exit(EXIT_SUCCESS);
610 } else if (opts && (!strcmp(argv[i], "-h") ||
611 !strcmp(argv[i], "-help"))) {
612 fprintf(stderr, "%s", cmdLineHelp);
613 exit(EXIT_SUCCESS);
614 } else if (opts && (*argv[i] == '-')) {
615 #ifdef VMS
616 *argv[i] = '/';
617 #endif /*VMS*/
618 fprintf(stderr, "nedit: Unrecognized option %s\n%s", argv[i],
619 cmdLineHelp);
620 exit(EXIT_FAILURE);
621 } else {
622 #ifdef VMS
623 int numFiles, j;
624 char **nameList = NULL;
625 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
626 /* wildcards and to obtain a full VMS file specification */
627 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
628 /* for each expanded file name do: */
629 for (j = 0; j < numFiles; ++j) {
630 if (ParseFilename(nameList[j], filename, pathname) == 0) {
631 /* determine if file is to be openned in new tab, by
632 factoring the options -group, -tabbed & -untabbed */
633 if (group == 2) {
634 isTabbed = 0; /* start a new window for new group */
635 group = 1; /* next file will be within group */
636 } else if (group == 1) {
637 isTabbed = 1; /* new tab for file in group */
638 } else { /* not in group */
639 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
642 /* Files are opened in background to improve opening speed
643 by defering certain time consuiming task such as syntax
644 highlighting. At the end of the file-opening loop, the
645 last file opened will be raised to restore those deferred
646 items. The current file may also be raised if there're
647 macros to execute on. */
648 window = EditExistingFile(WindowList, filename, pathname,
649 editFlags, geometry, iconic, langMode, isTabbed,
650 True);
651 fileSpecified = TRUE;
653 if (window) {
654 CleanUpTabBarExposeQueue(window);
656 /* raise the last file of previous window */
657 if (lastFile && window->shell != lastFile->shell) {
658 CleanUpTabBarExposeQueue(lastFile);
659 RaiseDocument(lastFile);
662 if (!macroFileRead) {
663 ReadMacroInitFile(WindowList);
664 macroFileRead = True;
666 if (gotoLine)
667 SelectNumberedLine(window, lineNum);
668 if (toDoCommand != NULL) {
669 DoMacro(window, toDoCommand, "-do macro");
670 toDoCommand = NULL;
671 if (!IsValidWindow(window))
672 window = NULL; /* window closed by macro */
673 if (lastFile && !IsValidWindow(lastFile))
674 lastFile = NULL; /* window closed by macro */
678 /* register last opened file for later use */
679 if (window)
680 lastFile = window;
681 } else {
682 fprintf(stderr, "nedit: file name too long: %s\n", nameList[j]);
684 free(nameList[j]);
686 if (nameList != NULL)
687 free(nameList);
688 #else
689 if (ParseFilename(argv[i], filename, pathname) == 0 ) {
690 /* determine if file is to be openned in new tab, by
691 factoring the options -group, -tabbed & -untabbed */
692 if (group == 2) {
693 isTabbed = 0; /* start a new window for new group */
694 group = 1; /* next file will be within group */
695 } else if (group == 1) {
696 isTabbed = 1; /* new tab for file in group */
697 } else { /* not in group */
698 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
701 /* Files are opened in background to improve opening speed
702 by defering certain time consuiming task such as syntax
703 highlighting. At the end of the file-opening loop, the
704 last file opened will be raised to restore those deferred
705 items. The current file may also be raised if there're
706 macros to execute on. */
707 window = EditExistingFile(WindowList, filename, pathname,
708 editFlags, geometry, iconic, langMode, isTabbed, True);
709 fileSpecified = TRUE;
710 if (window) {
711 CleanUpTabBarExposeQueue(window);
713 /* raise the last tab of previous window */
714 if (lastFile && window->shell != lastFile->shell) {
715 CleanUpTabBarExposeQueue(lastFile);
716 RaiseDocument(lastFile);
719 if (!macroFileRead) {
720 ReadMacroInitFile(WindowList);
721 macroFileRead = True;
723 if (gotoLine)
724 SelectNumberedLine(window, lineNum);
725 if (toDoCommand != NULL) {
726 DoMacro(window, toDoCommand, "-do macro");
727 toDoCommand = NULL;
728 if (!IsValidWindow(window))
729 window = NULL; /* window closed by macro */
730 if (lastFile && !IsValidWindow(lastFile))
731 lastFile = NULL; /* window closed by macro */
735 /* register last opened file for later use */
736 if (window)
737 lastFile = window;
738 } else {
739 fprintf(stderr, "nedit: file name too long: %s\n", argv[i]);
741 #endif /*VMS*/
744 #ifdef VMS
745 VMSFileScanDone();
746 #endif /*VMS*/
748 /* Raise the last file opened */
749 if (lastFile) {
750 CleanUpTabBarExposeQueue(lastFile);
751 RaiseDocument(lastFile);
753 CheckCloseDim();
755 /* If no file to edit was specified, open a window to edit "Untitled" */
756 if (!fileSpecified) {
757 EditNewFile(NULL, geometry, iconic, langMode, NULL);
758 ReadMacroInitFile(WindowList);
759 CheckCloseDim();
760 if (toDoCommand != NULL)
761 DoMacro(WindowList, toDoCommand, "-do macro");
764 /* Begin remembering last command invoked for "Repeat" menu item */
765 AddLastCommandActionHook(context);
767 /* Set up communication port and write ~/.nedit_server_process file */
768 if (IsServer)
769 InitServerCommunication();
771 /* Process events. */
772 if (IsServer)
773 ServerMainLoop(context);
774 else
775 XtAppMainLoop(context);
777 /* Not reached but this keeps some picky compilers happy */
778 return EXIT_SUCCESS;
781 static void nextArg(int argc, char **argv, int *argIndex)
783 if (*argIndex + 1 >= argc) {
784 #ifdef VMS
785 *argv[*argIndex] = '/';
786 #endif /*VMS*/
787 fprintf(stderr, "NEdit: %s requires an argument\n%s", argv[*argIndex],
788 cmdLineHelp);
789 exit(EXIT_FAILURE);
791 (*argIndex)++;
795 ** Return True if -do macro is valid, otherwise write an error on stderr
797 static int checkDoMacroArg(const char *macro)
799 Program *prog;
800 char *errMsg, *stoppedAt, *tMacro;
801 int macroLen;
803 /* Add a terminating newline (which command line users are likely to omit
804 since they are typically invoking a single routine) */
805 macroLen = strlen(macro);
806 tMacro = XtMalloc(strlen(macro)+2);
807 strncpy(tMacro, macro, macroLen);
808 tMacro[macroLen] = '\n';
809 tMacro[macroLen+1] = '\0';
811 /* Do a test parse */
812 prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
813 XtFree(tMacro);
814 if (prog == NULL) {
815 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
816 return False;
818 FreeProgram(prog);
819 return True;
823 ** maskArgvKeywords and unmaskArgvKeywords mangle selected keywords by
824 ** replacing the '-' with a space, for the purpose of hiding them from
825 ** XtOpenDisplay's option processing. Why this silly scheme? XtOpenDisplay
826 ** really needs to see command line arguments, particularly -display, but
827 ** there's no way to change the option processing table it uses, to keep
828 ** it from consuming arguments which are meant to apply per-window, like
829 ** -geometry and -iconic.
831 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs)
833 int i, k;
835 for (i=1; i<argc; i++)
836 for (k=0; maskArgs[k]!=NULL; k++)
837 if (!strcmp(argv[i], maskArgs[k]))
838 argv[i][0] = ' ';
842 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs)
844 int i, k;
846 for (i=1; i<argc; i++)
847 for (k=0; maskArgs[k]!=NULL; k++)
848 if (argv[i][0]==' ' && !strcmp(&argv[i][1], &maskArgs[k][1]))
849 argv[i][0] = '-';
853 ** If we're not using the default visual, then some default resources in
854 ** the database (colors) are not valid, because they are indexes into the
855 ** default colormap. If we used them blindly, then we'd get "random"
856 ** unreadable colors. So we inspect the resource list, and use the
857 ** fallback "grey" color instead if this is the case.
859 static void patchResourcesForVisual(void)
861 Cardinal i;
862 Visual *visual;
863 int depth;
864 Colormap map;
865 Boolean usingDefaultVisual;
866 XrmDatabase db;
868 if (!TheDisplay)
869 return;
871 db = XtDatabase(TheDisplay);
873 usingDefaultVisual = FindBestVisual(TheDisplay,
874 APP_NAME,
875 APP_CLASS,
876 &visual,
877 &depth,
878 &map);
880 if (!usingDefaultVisual)
882 #ifndef LESSTIF_VERSION
884 For non-Lesstif versions, we have to put non-default visuals etc.
885 in the resource data base to make sure that all (shell) widgets
886 inherit them, especially Motif's own shells (eg, drag icons).
888 For Lesstif, this doesn't work, but luckily, Lesstif handles
889 non-default visuals etc. properly for its own shells and
890 we can take care of things for our shells (eg, call tips) through
891 our shell creation wrappers in misc.c.
894 XrmValue value;
895 value.addr = (XPointer)&visual;
896 value.size = sizeof(visual);
897 XrmPutResource(&db, "*visual", "Visual", &value);
898 value.addr = (XPointer)&map;
899 value.size = sizeof(map);
900 XrmPutResource(&db, "*colormap", "Colormap", &value);
901 value.addr = (XPointer)&depth;
902 value.size = sizeof(depth);
903 XrmPutResource(&db, "*depth", "Int", &value);
905 /* Drag-and-drop visuals do not work well when using a different
906 visual. One some systems, you'll just get a funny-looking icon
907 (maybe all-black) but on other systems it crashes with a BadMatch
908 error. This appears to be an OSF Motif bug. It would be nicer
909 to just disable the visual itself, instead of the entire drag
910 operation.
912 Update: this is no longer necessary since all problems with
913 non-default visuals should now be solved.
915 XrmPutStringResource(&db, "*dragInitiatorProtocolStyle", "DRAG_NONE");
917 #endif
919 for (i = 1; i < XtNumber(fallbackResources); ++i)
921 Cardinal resIndex = i - 1;
923 if (strstr(fallbackResources[resIndex], "*background:") ||
924 strstr(fallbackResources[resIndex], "*foreground:"))
926 /* Qualify by application name to prevent them from being
927 converted against the wrong colormap. */
928 char buf[1024] = "*" APP_NAME;
929 strcat(buf, fallbackResources[resIndex]);
930 XrmPutLineResource(&db, buf);
937 ** Several KDE version (2.x and 3.x) ship with a template application-default
938 ** file for NEdit in which several strings have to be substituted in order to
939 ** make it a valid .ad file. However, for some reason (a KDE bug?), the
940 ** template sometimes ends up in the resource db unmodified, such that several
941 ** invalid entries are present. This function checks for the presence of such
942 ** invalid entries and silently replaces them with NEdit's default values where
943 ** necessary. Without this, NEdit will typically write several warnings to
944 ** the terminal (Cannot convert string "FONTLIST" to type FontStruct etc) and
945 ** fall back on some really ugly colors and fonts.
947 static void patchResourcesForKDEbug(void)
950 * These are the resources found the Nedit.ad template shipped with KDE 3.0.
952 static const char* buggyResources[][3] = {
953 { "*fontList", "FONTLIST", NEDIT_DEFAULT_FONT },
954 { "*XmText.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
955 { "*XmText.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
956 { "*XmTextField.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
957 { "*XmTextField.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
958 { "*XmList.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
959 { "*XmList.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
960 { "*helpText.background", "BACKGROUND", NEDIT_DEFAULT_HELP_BG },
961 { "*helpText.foreground", "FOREGROUND", NEDIT_DEFAULT_HELP_FG },
962 { "*background", "BACKGROUND", NEDIT_DEFAULT_BG },
963 { "*foreground", "FOREGROUND", NEDIT_DEFAULT_FG, },
964 { "*selectColor", "BACKGROUND", NEDIT_DEFAULT_SEL_BG },
965 { "*highlightColor", "BACKGROUND", NEDIT_DEFAULT_HI_BG },
966 { "*text.background", "WINDOW_BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
967 { "*text.foreground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_FG },
968 { "*text.selectBackground", "SELECT_BACKGROUND", NEDIT_DEFAULT_SEL_BG },
969 { "*text.selectForeground", "SELECT_FOREGROUND", NEDIT_DEFAULT_SEL_FG },
970 { "*text.cursorForeground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_CURSOR_FG},
971 /* { "*remapDeleteKey", "False", }, OK */
972 /* { "!*text.heavyCursor", "True" }, OK */
973 /* { "!*BlinkRate", "0" }, OK */
974 /* { "*shell", "/bin/sh" }, OK */
975 { "*statsLine.background", "BACKGROUND", NEDIT_DEFAULT_BG },
976 { "*statsLine.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
977 { NULL, NULL, NULL } };
978 XrmDatabase db;
979 int i;
981 if (!TheDisplay)
982 return;
984 db = XtDatabase(TheDisplay);
986 i = 0;
987 while (buggyResources[i][0])
989 const char* resource = buggyResources[i][0];
990 const char* buggyValue = buggyResources[i][1];
991 const char* defaultValue = buggyResources[i][2];
992 char name[128] = APP_NAME;
993 char class[128] = APP_CLASS;
994 char* type;
995 XrmValue resValue;
997 strcat(name, resource);
998 strcat(class, resource); /* Is this ok ? */
1000 if (XrmGetResource(db, name, class, &type, &resValue) &&
1001 !strcmp(type, XmRString))
1003 /* Buggy value? Replace by the default. */
1004 if (!strcmp(resValue.addr, buggyValue))
1006 XrmPutStringResource(&db, &name[0], (char*)defaultValue);
1009 ++i;
1014 ** It seems OSF Motif cannot handle locales with UTF-8 at the end, crashing
1015 ** in various places. The easiest one to find is to open the File Open
1016 ** dialog box. So we lop off UTF-8 if it's there and continue. Newer
1017 ** versions of Linux distros (e.g., RedHat 8) set the default language to
1018 ** to have "UTF-8" at the end, so users were seeing these crashes.
1020 static void patchLocaleForMotif()
1022 #ifndef LESSTIF_VERSION
1023 const char *ctype;
1024 char ctypebuf[1024];
1025 char *utf_start;
1027 /* We have to check LC_CTYPE specifically here, because the system
1028 might specify different locales for different categories (why
1029 anyone would want to do this is beyond me). As far as I can
1030 tell, only LC_CTYPE causes OSF Motif to crash. If it turns out
1031 others do, we'll have to iterate over a list of locale cateogries
1032 and patch every one of them. */
1034 ctype = setlocale(LC_CTYPE, NULL);
1036 if (!ctype)
1037 return;
1039 strncpy(ctypebuf, ctype, sizeof ctypebuf);
1041 if ((utf_start = strstr(ctypebuf, ".utf8")) ||
1042 (utf_start = strstr(ctypebuf, ".UTF-8")))
1044 *utf_start = '\0'; /* Samurai chop */
1045 XtWarning("UTF8 locale not supported.");
1046 setlocale(LC_CTYPE, ctypebuf);
1048 #endif
1053 ** Same as the default X language procedure, except we check if Motif can
1054 ** handle the locale as well.
1057 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure)
1059 /* "xnl" will be set if the user passes in a new language via the
1060 "-xnllanguage" flag. If it's empty, then setlocale will get
1061 the default locale by some system-dependent means (usually,
1062 reading some environment variables). */
1064 if (! setlocale(LC_ALL, xnl)) {
1065 XtWarning("locale not supported by C library, locale unchanged");
1068 patchLocaleForMotif();
1070 if (! XSupportsLocale()) {
1071 XtWarning("locale not supported by Xlib, locale set to C");
1072 setlocale(LC_ALL, "C");
1074 if (! XSetLocaleModifiers(""))
1075 XtWarning("X locale modifiers not supported, using default");
1077 return setlocale(LC_ALL, NULL); /* re-query in case overwritten */
1080 static int sortAlphabetical(const void* k1, const void* k2)
1082 const char* key1 = *(const char**)k1;
1083 const char* key2 = *(const char**)k2;
1084 return strcmp(key1, key2);
1088 * Checks whether a given virtual key binding string is invalid.
1089 * A binding is considered invalid if there are duplicate key entries.
1091 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
1093 int maxCount = 1, i, count;
1094 const char *pos = (const char*)bindings;
1095 char *copy;
1096 char *pos2, *pos3;
1097 char **keys;
1099 /* First count the number of bindings; bindings are separated by \n
1100 strings. The number of bindings equals the number of \n + 1.
1101 Beware of leading and trailing \n; the number is actually an
1102 upper bound on the number of entries. */
1103 while ((pos = strstr(pos, "\n")))
1105 ++pos;
1106 ++maxCount;
1109 if (maxCount == 1)
1110 return False; /* One binding is always ok */
1112 keys = (char**)malloc(maxCount*sizeof(char*));
1113 copy = XtNewString((const char*)bindings);
1114 i = 0;
1115 pos2 = copy;
1117 count = 0;
1118 while (i<maxCount && pos2 && *pos2)
1120 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
1122 if (*pos2 == '!') /* Ignore comment lines */
1124 pos2 = strstr(pos2, "\n");
1125 continue; /* Go to the next line */
1128 if (*pos2)
1130 keys[i++] = pos2;
1131 ++count;
1132 pos3 = strstr(pos2, ":");
1133 if (pos3)
1135 *pos3++ = 0; /* Cut the string and jump to the next entry */
1136 pos2 = pos3;
1138 pos2 = strstr(pos2, "\n");
1142 if (count <= 1)
1144 free(keys);
1145 XtFree(copy);
1146 return False; /* No conflict */
1149 /* Sort the keys and look for duplicates */
1150 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
1151 for (i=1; i<count; ++i)
1153 if (!strcmp(keys[i-1], keys[i]))
1155 /* Duplicate detected */
1156 free(keys);
1157 XtFree(copy);
1158 return True;
1161 free(keys);
1162 XtFree(copy);
1163 return False;
1167 * Optionally sanitizes the Motif default virtual key bindings.
1168 * Some applications install invalid bindings (attached to the root window),
1169 * which cause certain keys to malfunction in NEdit.
1170 * Through an X-resource, users can choose whether they want
1171 * - to always keep the existing bindings
1172 * - to override the bindings only if they are invalid
1173 * - to always override the existing bindings.
1176 static Atom virtKeyAtom;
1178 static unsigned char* sanitizeVirtualKeyBindings(void)
1180 int overrideBindings = GetPrefOverrideVirtKeyBindings();
1181 Window rootWindow;
1182 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
1183 Atom dummyAtom;
1184 int getFmt;
1185 unsigned long dummyULong, nItems;
1186 unsigned char *insaneVirtKeyBindings = NULL;
1188 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
1190 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
1191 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1193 /* Remove the property, if it exists; we'll restore it later again */
1194 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
1195 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
1196 &dummyULong, &insaneVirtKeyBindings) != Success
1197 || nItems == 0)
1199 return NULL; /* No binding yet; nothing to do */
1202 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
1204 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
1206 /* Restore the property immediately; it seems valid */
1207 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1208 PropModeReplace, insaneVirtKeyBindings,
1209 strlen((const char*)insaneVirtKeyBindings));
1210 XFree((char*)insaneVirtKeyBindings);
1211 return NULL; /* Prevent restoration */
1214 return insaneVirtKeyBindings;
1218 * NEdit should not mess with the bindings installed by other apps, so we
1219 * just restore whatever was installed, if necessary
1221 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
1223 if (insaneVirtKeyBindings)
1225 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1226 /* Restore the root window atom, such that we don't affect
1227 other apps. */
1228 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1229 PropModeReplace, insaneVirtKeyBindings,
1230 strlen((const char*)insaneVirtKeyBindings));
1231 XFree((char*)insaneVirtKeyBindings);
1235 #ifdef LESSTIF_VERSION
1237 ** Warning handler that suppresses harmless but annoying warnings generated
1238 ** by non-production Lesstif versions.
1240 static void bogusWarningFilter(String message)
1242 const char* bogusMessages[] = {
1243 "XmFontListCreate() is an obsolete function!",
1244 "No type converter registered for 'String' to 'PathMode' conversion.",
1245 "XtRemoveGrab asked to remove a widget not on the list",
1246 NULL };
1247 const char **bogusMessage = &bogusMessages[0];
1249 while (*bogusMessage) {
1250 size_t bogusLen = strlen(*bogusMessage);
1251 if (strncmp(message, *bogusMessage, bogusLen) == 0) {
1252 #ifdef DEBUG_LESSTIF_WARNINGS
1253 /* Developers may want to see which messages are suppressed. */
1254 fprintf(stderr, "[SUPPRESSED] %s\n", message);
1255 #endif
1256 return;
1258 ++bogusMessage;
1261 /* An unknown message. Keep it. */
1262 fprintf(stderr, "%s\n", message);
1264 #endif /* LESSTIF_VERSION */