Fix our use of $em_tab_dist after it was changed to 0 for 'turned of'.
[nedit.git] / source / nedit.c
blobe10b4655d67355a978dfb650dce9c22da5e4f9f4
1 static const char CVSID[] = "$Id: nedit.c,v 1.100 2007/11/29 22:18:09 tringali 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 <ctype.h>
58 #include <limits.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
64 #ifndef NO_XMIM
65 #include <X11/Xlocale.h>
66 #else
67 #include <locale.h>
68 #endif
70 #include <X11/Intrinsic.h>
71 #include <Xm/Xm.h>
72 #include <Xm/XmP.h>
74 #if XmVersion >= 1002
75 #include <Xm/RepType.h>
76 #endif
78 #ifdef VMS
79 #include <rmsdef.h>
80 #include "../util/VMSparam.h"
81 #include "../util/VMSUtils.h"
82 #else
84 #ifndef __MVS__
85 #include <sys/param.h>
86 #endif
87 #endif /*VMS*/
89 #ifdef HAVE_DEBUG_H
90 #include "../debug.h"
91 #endif
93 static void nextArg(int argc, char **argv, int *argIndex);
94 static int checkDoMacroArg(const char *macro);
95 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure);
96 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs);
97 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs);
98 static void fixupBrokenXKeysymDB(void);
99 static void patchResourcesForVisual(void);
100 static void patchResourcesForKDEbug(void);
101 static void patchLocaleForMotif(void);
102 static unsigned char* sanitizeVirtualKeyBindings(void);
103 static int sortAlphabetical(const void* k1, const void* k2);
104 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
105 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
106 static void noWarningFilter(String);
107 static void showWarningFilter(String);
109 WindowInfo *WindowList = NULL;
110 Display *TheDisplay = NULL;
111 char *ArgV0 = NULL;
112 Boolean IsServer = False;
113 Widget TheAppShell;
115 /* Reasons for choice of default font qualifications:
117 iso8859 appears to be necessary for newer versions of XFree86 that
118 default to Unicode encoding, which doesn't quite work with Motif.
119 Otherwise Motif puts up garbage (square blocks).
121 (This of course, is a stupid default because there are far more iso8859
122 apps than Unicode apps. But the X folks insist it's a client bug. Hah.)
124 RedHat 7.3 won't default to '-1' for an encoding, if left with a *,
125 and so reverts to "fixed". Yech. */
127 #define NEDIT_DEFAULT_FONT "-*-helvetica-medium-r-normal-*-*-120-*-*-*-*-iso8859-1," \
128 "-*-helvetica-bold-r-normal-*-*-120-*-*-*-*-iso8859-1=BOLD," \
129 "-*-helvetica-medium-o-normal-*-*-120-*-*-*-*-iso8859-1=ITALIC"
131 #define NEDIT_FIXED_FONT "-*-courier-medium-r-normal-*-*-120-*-*-*-*-iso8859-1," \
132 "-*-courier-bold-r-normal-*-*-120-*-*-*-*-iso8859-1=BOLD," \
133 "-*-courier-medium-o-normal-*-*-120-*-*-*-*-iso8859-1=ITALIC"
135 #define NEDIT_DEFAULT_BG "#b3b3b3"
137 #define NEDIT_TEXT_TRANSLATIONS "#override\\n" \
138 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \
139 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \
140 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \
141 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n"
143 static char *fallbackResources[] = {
144 /* Try to avoid Motif's horrificly ugly default colors and fonts,
145 if the user's environment provides no usable defaults. We try
146 to choose a Windows-y default color setting here. Editable text
147 fields are forced to a fixed-pitch font for usability.
149 By using the VendorShell fontList resources, Motif automatically
150 groups the fonts into the right classes. It's then easier for
151 the user or environment to override this sensibly:
153 nedit -xrm '*textFontList: myfont'
155 This is broken in recent versions of LessTif.
158 #ifdef LESSTIF_VERSION
159 "*FontList: " NEDIT_DEFAULT_FONT,
160 "*XmText.FontList: " NEDIT_FIXED_FONT,
161 "*XmTextField.FontList: " NEDIT_FIXED_FONT,
162 "*XmList.FontList: " NEDIT_FIXED_FONT,
163 "*XmFileSelectionBox*XmList.FontList: " NEDIT_FIXED_FONT,
164 #else
165 "*buttonFontList: " NEDIT_DEFAULT_FONT,
166 "*labelFontList: " NEDIT_DEFAULT_FONT,
167 "*textFontList: " NEDIT_FIXED_FONT,
168 #endif
170 "*background: " NEDIT_DEFAULT_BG,
171 "*foreground: " NEDIT_DEFAULT_FG,
172 "*XmText.foreground: " NEDIT_DEFAULT_FG,
173 "*XmText.background: " NEDIT_DEFAULT_TEXT_BG,
174 "*XmList.foreground: " NEDIT_DEFAULT_FG,
175 "*XmList.background: " NEDIT_DEFAULT_TEXT_BG,
176 "*XmTextField.foreground: " NEDIT_DEFAULT_FG,
177 "*XmTextField.background: " NEDIT_DEFAULT_TEXT_BG,
179 /* Use baseTranslations as per Xt Programmer's Manual, 10.2.12 */
180 "*XmText.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
181 "*XmTextField.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
183 "*XmLFolder.highlightThickness: 0",
184 "*XmLFolder.shadowThickness: 1",
185 "*XmLFolder.maxTabWidth: 150",
186 "*XmLFolder.traversalOn: False",
187 "*XmLFolder.inactiveForeground: #666" ,
188 "*tab.alignment: XmALIGNMENT_BEGINNING",
189 "*tab.marginWidth: 0",
190 "*tab.marginHeight: 1",
192 /* Prevent the file selection box from acting stupid. */
193 "*XmFileSelectionBox.resizePolicy: XmRESIZE_NONE",
194 "*XmFileSelectionBox.textAccelerators:",
195 "*XmFileSelectionBox.pathMode: XmPATH_MODE_RELATIVE",
196 "*XmFileSelectionBox.width: 500",
197 "*XmFileSelectionBox.height: 400",
199 /* NEdit-specific widgets. Theses things should probably be manually
200 jammed into the database, rather than fallbacks. We really want
201 the accelerators to be there even if someone creates an app-defaults
202 file against our wishes. */
204 "*text.lineNumForeground: " NEDIT_DEFAULT_LINENO_FG,
205 "*text.background: " NEDIT_DEFAULT_TEXT_BG,
206 "*text.foreground: " NEDIT_DEFAULT_FG,
207 "*text.highlightForeground: " NEDIT_DEFAULT_HI_FG,
208 "*text.highlightBackground: " NEDIT_DEFAULT_HI_BG,
209 "*textFrame.shadowThickness: 1",
210 "*menuBar.marginHeight: 0",
211 "*menuBar.shadowThickness: 1",
212 "*pane.sashHeight: 11",
213 "*pane.sashWidth: 11",
214 "*pane.marginWidth: 0",
215 "*pane.marginHeight: 0",
216 "*scrolledW*spacing: 0",
217 "*text.selectionArrayCount: 3",
218 "*helpText.background: " NEDIT_DEFAULT_HELP_BG,
219 "*helpText.foreground: " NEDIT_DEFAULT_HELP_FG,
220 "*helpText.selectBackground: " NEDIT_DEFAULT_BG,
221 "*statsLine.background: " NEDIT_DEFAULT_BG,
222 "*statsLine.FontList: " NEDIT_DEFAULT_FONT,
223 "*calltip.background: LemonChiffon1",
224 "*calltip.foreground: black",
225 "*iSearchForm*highlightThickness: 1",
226 "*fileMenu.tearOffModel: XmTEAR_OFF_ENABLED",
227 "*editMenu.tearOffModel: XmTEAR_OFF_ENABLED",
228 "*searchMenu.tearOffModel: XmTEAR_OFF_ENABLED",
229 "*preferencesMenu.tearOffModel: XmTEAR_OFF_ENABLED",
230 "*windowsMenu.tearOffModel: XmTEAR_OFF_ENABLED",
231 "*shellMenu.tearOffModel: XmTEAR_OFF_ENABLED",
232 "*macroMenu.tearOffModel: XmTEAR_OFF_ENABLED",
233 "*helpMenu.tearOffModel: XmTEAR_OFF_ENABLED",
234 "*fileMenu.mnemonic: F",
235 "*fileMenu.new.accelerator: Ctrl<Key>n",
236 "*fileMenu.new.acceleratorText: Ctrl+N",
237 "*fileMenu.newOpposite.accelerator: Shift Ctrl<Key>n",
238 "*fileMenu.newOpposite.acceleratorText: Shift+Ctrl+N",
239 "*fileMenu.open.accelerator: Ctrl<Key>o",
240 "*fileMenu.open.acceleratorText: Ctrl+O",
241 "*fileMenu.openSelected.accelerator: Ctrl<Key>y",
242 "*fileMenu.openSelected.acceleratorText: Ctrl+Y",
243 "*fileMenu.close.accelerator: Ctrl<Key>w",
244 "*fileMenu.close.acceleratorText: Ctrl+W",
245 "*fileMenu.save.accelerator: Ctrl<Key>s",
246 "*fileMenu.save.acceleratorText: Ctrl+S",
247 "*fileMenu.includeFile.accelerator: Alt<Key>i",
248 "*fileMenu.includeFile.acceleratorText: Alt+I",
249 "*fileMenu.print.accelerator: Ctrl<Key>p",
250 "*fileMenu.print.acceleratorText: Ctrl+P",
251 "*fileMenu.exit.accelerator: Ctrl<Key>q",
252 "*fileMenu.exit.acceleratorText: Ctrl+Q",
253 "*editMenu.mnemonic: E",
254 "*editMenu.undo.accelerator: Ctrl<Key>z",
255 "*editMenu.undo.acceleratorText: Ctrl+Z",
256 "*editMenu.redo.accelerator: Shift Ctrl<Key>z",
257 "*editMenu.redo.acceleratorText: Shift+Ctrl+Z",
258 /* Clipboard accelerators prevent the use of the clipboard in iSearch's
259 XmText, so they are left out. Their job is done by translations in
260 the main text widget, so the acceleratorText is still kept. */
261 "*editMenu.cut.acceleratorText: Ctrl+X",
262 "*editMenu.copy.acceleratorText: Ctrl+C",
263 "*editMenu.paste.acceleratorText: Ctrl+V",
264 "*editMenu.pasteColumn.accelerator: Shift Ctrl<Key>v",
265 "*editMenu.pasteColumn.acceleratorText: Ctrl+Shift+V",
266 "*editMenu.delete.acceleratorText: Del",
267 "*editMenu.selectAll.accelerator: Ctrl<Key>a",
268 "*editMenu.selectAll.acceleratorText: Ctrl+A",
269 "*editMenu.shiftLeft.accelerator: Ctrl<Key>9",
270 "*editMenu.shiftLeft.acceleratorText: [Shift]Ctrl+9",
271 "*editMenu.shiftLeftShift.accelerator: Shift Ctrl<Key>9",
272 "*editMenu.shiftRight.accelerator: Ctrl<Key>0",
273 "*editMenu.shiftRight.acceleratorText: [Shift]Ctrl+0",
274 "*editMenu.shiftRightShift.accelerator: Shift Ctrl<Key>0",
275 "*editMenu.upperCase.accelerator: Ctrl<Key>6",
276 "*editMenu.upperCase.acceleratorText: Ctrl+6",
277 "*editMenu.lowerCase.accelerator: Shift Ctrl<Key>6",
278 "*editMenu.lowerCase.acceleratorText: Shift+Ctrl+6",
279 "*editMenu.fillParagraph.accelerator: Ctrl<Key>j",
280 "*editMenu.fillParagraph.acceleratorText: Ctrl+J",
281 "*editMenu.insertFormFeed.accelerator: Alt Ctrl<Key>l",
282 "*editMenu.insertFormFeed.acceleratorText: Alt+Ctrl+L",
283 "*editMenu.insertCtrlCode.accelerator: Alt Ctrl<Key>i",
284 "*editMenu.insertCtrlCode.acceleratorText: Alt+Ctrl+I",
285 "*searchMenu.mnemonic: S",
286 "*searchMenu.find.accelerator: Ctrl<Key>f",
287 "*searchMenu.find.acceleratorText: [Shift]Ctrl+F",
288 "*searchMenu.findShift.accelerator: Shift Ctrl<Key>f",
289 "*searchMenu.findAgain.accelerator: Ctrl<Key>g",
290 "*searchMenu.findAgain.acceleratorText: [Shift]Ctrl+G",
291 "*searchMenu.findAgainShift.accelerator: Shift Ctrl<Key>g",
292 "*searchMenu.findSelection.accelerator: Ctrl<Key>h",
293 "*searchMenu.findSelection.acceleratorText: [Shift]Ctrl+H",
294 "*searchMenu.findSelectionShift.accelerator: Shift Ctrl<Key>h",
295 "*searchMenu.findIncremental.accelerator: Ctrl<Key>i",
296 "*searchMenu.findIncrementalShift.accelerator: Shift Ctrl<Key>i",
297 "*searchMenu.findIncremental.acceleratorText: [Shift]Ctrl+I",
298 "*searchMenu.replace.accelerator: Ctrl<Key>r",
299 "*searchMenu.replace.acceleratorText: [Shift]Ctrl+R",
300 "*searchMenu.replaceShift.accelerator: Shift Ctrl<Key>r",
301 "*searchMenu.findReplace.accelerator: Ctrl<Key>r",
302 "*searchMenu.findReplace.acceleratorText: [Shift]Ctrl+R",
303 "*searchMenu.findReplaceShift.accelerator: Shift Ctrl<Key>r",
304 "*searchMenu.replaceFindAgain.accelerator: Ctrl<Key>t",
305 "*searchMenu.replaceFindAgain.acceleratorText: [Shift]Ctrl+T",
306 "*searchMenu.replaceFindAgainShift.accelerator: Shift Ctrl<Key>t",
307 "*searchMenu.replaceAgain.accelerator: Alt<Key>t",
308 "*searchMenu.replaceAgain.acceleratorText: [Shift]Alt+T",
309 "*searchMenu.replaceAgainShift.accelerator: Shift Alt<Key>t",
310 "*searchMenu.gotoLineNumber.accelerator: Ctrl<Key>l",
311 "*searchMenu.gotoLineNumber.acceleratorText: Ctrl+L",
312 "*searchMenu.gotoSelected.accelerator: Ctrl<Key>e",
313 "*searchMenu.gotoSelected.acceleratorText: Ctrl+E",
314 "*searchMenu.mark.accelerator: Alt<Key>m",
315 "*searchMenu.mark.acceleratorText: Alt+M a-z",
316 "*searchMenu.gotoMark.accelerator: Alt<Key>g",
317 "*searchMenu.gotoMark.acceleratorText: [Shift]Alt+G a-z",
318 "*searchMenu.gotoMarkShift.accelerator: Shift Alt<Key>g",
319 "*searchMenu.gotoMatching.accelerator: Ctrl<Key>m",
320 "*searchMenu.gotoMatching.acceleratorText: [Shift]Ctrl+M",
321 "*searchMenu.gotoMatchingShift.accelerator: Shift Ctrl<Key>m",
322 "*searchMenu.findDefinition.accelerator: Ctrl<Key>d",
323 "*searchMenu.findDefinition.acceleratorText: Ctrl+D",
324 "*searchMenu.showCalltip.accelerator: Ctrl<Key>apostrophe",
325 "*searchMenu.showCalltip.acceleratorText: Ctrl+'",
326 "*preferencesMenu.mnemonic: P",
327 "*preferencesMenu.statisticsLine.accelerator: Alt<Key>a",
328 "*preferencesMenu.statisticsLine.acceleratorText: Alt+A",
329 "*preferencesMenu.overtype.acceleratorText: Insert",
330 "*shellMenu.mnemonic: l",
331 "*shellMenu.filterSelection.accelerator: Alt<Key>r",
332 "*shellMenu.filterSelection.acceleratorText: Alt+R",
333 "*shellMenu.executeCommand.accelerator: Alt<Key>x",
334 "*shellMenu.executeCommand.acceleratorText: Alt+X",
335 "*shellMenu.executeCommandLine.accelerator: Ctrl<Key>KP_Enter",
336 "*shellMenu.executeCommandLine.acceleratorText: Ctrl+KP Enter",
337 "*shellMenu.cancelShellCommand.accelerator: Ctrl<Key>period",
338 "*shellMenu.cancelShellCommand.acceleratorText: Ctrl+.",
339 "*macroMenu.mnemonic: c",
340 "*macroMenu.learnKeystrokes.accelerator: Alt<Key>k",
341 "*macroMenu.learnKeystrokes.acceleratorText: Alt+K",
342 "*macroMenu.finishLearn.accelerator: Alt<Key>k",
343 "*macroMenu.finishLearn.acceleratorText: Alt+K",
344 "*macroMenu.cancelLearn.accelerator: Ctrl<Key>period",
345 "*macroMenu.cancelLearn.acceleratorText: Ctrl+.",
346 "*macroMenu.replayKeystrokes.accelerator: Ctrl<Key>k",
347 "*macroMenu.replayKeystrokes.acceleratorText: Ctrl+K",
348 "*macroMenu.repeat.accelerator: Ctrl<Key>comma",
349 "*macroMenu.repeat.acceleratorText: Ctrl+,",
350 "*windowsMenu.mnemonic: W",
351 "*windowsMenu.splitPane.accelerator: Ctrl<Key>2",
352 "*windowsMenu.splitPane.acceleratorText: Ctrl+2",
353 "*windowsMenu.closePane.accelerator: Ctrl<Key>1",
354 "*windowsMenu.closePane.acceleratorText: Ctrl+1",
355 "*helpMenu.mnemonic: H",
356 "nedit.help.helpForm.sw.helpText*baseTranslations: #override\
357 <Key>Tab:help-focus-buttons()\\n\
358 <Key>Return:help-button-action(\"close\")\\n\
359 Ctrl<Key>F:help-button-action(\"find\")\\n\
360 Ctrl<Key>G:help-button-action(\"findAgain\")\\n\
361 <KeyPress>osfCancel:help-button-action(\"close\")\\n\
362 ~Meta~Ctrl~Shift<Btn1Down>:\
363 grab-focus() help-hyperlink()\\n\
364 ~Meta~Ctrl~Shift<Btn1Up>:\
365 help-hyperlink(\"current\", \"process-cancel\", \"extend-end\")\\n\
366 ~Meta~Ctrl~Shift<Btn2Down>:\
367 process-bdrag() help-hyperlink()\\n\
368 ~Meta~Ctrl~Shift<Btn2Up>:\
369 help-hyperlink(\"new\", \"process-cancel\", \"copy-to\")",
370 NULL
373 static const char cmdLineHelp[] =
374 #ifndef VMS
375 "Usage: nedit [-read] [-create] [-line n | +n] [-server] [-do command]\n\
376 [-tags file] [-tabs n] [-wrap] [-nowrap] [-autowrap]\n\
377 [-autoindent] [-noautoindent] [-autosave] [-noautosave]\n\
378 [-lm languagemode] [-rows n] [-columns n] [-font font]\n\
379 [-geometry geometry] [-iconic] [-noiconic] [-svrname name]\n\
380 [-display [host]:server[.screen] [-xrm resourcestring]\n\
381 [-import file] [-background color] [-foreground color]\n\
382 [-tabbed] [-untabbed] [-group] [-V|-version] [-h|-help]\n\
383 [--] [file...]\n";
384 #else
385 "[Sorry, no on-line help available.]\n"; /* Why is that ? */
386 #endif /*VMS*/
388 int main(int argc, char **argv)
390 int i, lineNum, nRead, fileSpecified = FALSE, editFlags = CREATE;
391 int gotoLine = False, macroFileRead = False, opts = True;
392 int iconic=False, tabbed = -1, group = 0, isTabbed;
393 char *toDoCommand = NULL, *geometry = NULL, *langMode = NULL;
394 char filename[MAXPATHLEN], pathname[MAXPATHLEN];
395 XtAppContext context;
396 XrmDatabase prefDB;
397 WindowInfo *window = NULL, *lastFile = NULL;
398 static const char *protectedKeywords[] = {"-iconic", "-icon", "-geometry",
399 "-g", "-rv", "-reverse", "-bd", "-bordercolor", "-borderwidth",
400 "-bw", "-title", NULL};
401 unsigned char* invalidBindings = NULL;
403 /* Warn user if this has been compiled wrong. */
404 enum MotifStability stability = GetMotifStability();
405 if (stability == MotifKnownBad) {
406 fputs("nedit: WARNING: This version of NEdit is built incorrectly, and will be unstable.\n"
407 "nedit: Please get a stable version of NEdit from http://www.nedit.org.\n",
408 stderr);
411 /* Save the command which was used to invoke nedit for restart command */
412 ArgV0 = argv[0];
414 /* Set locale for C library, X, and Motif input functions.
415 Reverts to "C" if requested locale not available. */
416 XtSetLanguageProc(NULL, neditLanguageProc, NULL);
418 /* Initialize X toolkit (does not open display yet) */
419 XtToolkitInitialize();
420 context = XtCreateApplicationContext();
422 /* Set up a warning handler to trap obnoxious Xt grab warnings */
423 SuppressPassiveGrabWarnings();
425 /* Set up a handler to suppress X warning messages by default */
426 XtAppSetWarningHandler(context, noWarningFilter);
428 /* Set up default resources if no app-defaults file is found */
429 XtAppSetFallbackResources(context, fallbackResources);
431 #if XmVersion >= 1002
432 /* Allow users to change tear off menus with X resources */
433 XmRepTypeInstallTearOffModelConverter();
434 #endif
436 #ifdef VMS
437 /* Convert the command line to Unix style (This is not an ideal solution) */
438 ConvertVMSCommandLine(&argc, &argv);
439 #endif /*VMS*/
440 #ifdef __EMX__
441 /* expand wildcards if necessary */
442 _wildcard(&argc, &argv);
443 #endif
445 /* Read the preferences file and command line into a database */
446 prefDB = CreateNEditPrefDB(&argc, argv);
448 /* Open the display and read X database and remaining command line args.
449 XtOpenDisplay must be allowed to process some of the resource arguments
450 with its inaccessible internal option table, but others, like -geometry
451 and -iconic are per-window and it should not be allowed to consume them,
452 so we temporarily masked them out. */
453 maskArgvKeywords(argc, argv, protectedKeywords);
454 /* X.Org 6.8 and above add support for ARGB visuals (with alpha-channel),
455 typically with a 32-bit color depth. By default, NEdit uses the visual
456 with the largest color depth. However, both OpenMotif and Lesstif
457 cannot handle ARGB visuals (crashes, weird display effects, ...), so
458 NEdit should avoid selecting such a visual.
459 Unfortunatly, there appears to be no reliable way to identify
460 ARGB visuals that doesn't require some of the recent X.Org
461 extensions. Luckily, the X.Org developers have provided a mechanism
462 that can hide these problematic visuals from the application. This can
463 be achieved by setting the XLIB_SKIP_ARGB_VISUALS environment variable.
464 Users can set this variable before starting NEdit, but it is much
465 more convenient that NEdit takes care of this. This must be done before
466 the display is opened (empirically verified). */
467 putenv("XLIB_SKIP_ARGB_VISUALS=1");
468 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS,
469 NULL, 0, &argc, argv);
470 unmaskArgvKeywords(argc, argv, protectedKeywords);
471 if (!TheDisplay) {
472 /* Respond to -V or -version even if there is no display */
473 for (i = 1; i < argc && strcmp(argv[i], "--"); i++)
475 if (0 == strcmp(argv[i], "-V") || 0 == strcmp(argv[i], "-version"))
477 PrintVersion();
478 exit(EXIT_SUCCESS);
481 fputs ("NEdit: Can't open display\n", stderr);
482 exit(EXIT_FAILURE);
485 /* Must be done before creating widgets */
486 fixupBrokenXKeysymDB();
487 patchResourcesForVisual();
488 patchResourcesForKDEbug();
490 /* Initialize global symbols and subroutines used in the macro language */
491 InitMacroGlobals();
492 RegisterMacroSubroutines();
494 /* Store preferences from the command line and .nedit file,
495 and set the appropriate preferences */
496 RestoreNEditPrefs(prefDB, XtDatabase(TheDisplay));
498 /* Intercept syntactically invalid virtual key bindings BEFORE we
499 create any shells. */
500 invalidBindings = sanitizeVirtualKeyBindings();
502 /* Create a hidden application shell that is the parent of all the
503 main editor windows. Realize it so it the window can act as
504 group leader. */
505 TheAppShell = CreateShellWithBestVis(APP_NAME,
506 APP_CLASS,
507 applicationShellWidgetClass,
508 TheDisplay,
509 NULL,
512 /* Restore the original bindings ASAP such that other apps are not affected. */
513 restoreInsaneVirtualKeyBindings(invalidBindings);
515 XtSetMappedWhenManaged(TheAppShell, False);
516 XtRealizeWidget(TheAppShell);
518 #ifndef NO_SESSION_RESTART
519 AttachSessionMgrHandler(TheAppShell);
520 #endif
522 /* More preference stuff */
523 LoadPrintPreferences(XtDatabase(TheDisplay), APP_NAME, APP_CLASS, True);
524 SetDeleteRemap(GetPrefMapDelete());
525 SetPointerCenteredDialogs(GetPrefRepositionDialogs());
526 SetGetEFTextFieldRemoval(!GetPrefStdOpenDialog());
528 /* Set up action procedures for menu item commands */
529 InstallMenuActions(context);
531 /* Add Actions for following hyperlinks in the help window */
532 InstallHelpLinkActions(context);
533 /* Add actions for mouse wheel support in scrolled windows (except text
534 area) */
535 InstallMouseWheelActions(context);
537 /* Install word delimiters for regular expression matching */
538 SetREDefaultWordDelimiters(GetPrefDelimiters());
540 /* Read the nedit dynamic database of files for the Open Previous
541 command (and eventually other information as well) */
542 ReadNEditDB();
544 /* Process -import command line argument before others which might
545 open windows (loading preferences doesn't update menu settings,
546 which would then be out of sync with the real preference settings) */
547 for (i=1; i<argc; i++) {
548 if(!strcmp(argv[i], "--")) {
549 break; /* treat all remaining arguments as filenames */
550 } else if (!strcmp(argv[i], "-import")) {
551 nextArg(argc, argv, &i);
552 ImportPrefFile(argv[i], False);
553 } else if (!strcmp(argv[i], "-importold")) {
554 nextArg(argc, argv, &i);
555 ImportPrefFile(argv[i], True);
559 /* Load the default tags file. Don't complain if it doesn't load, the tag
560 file resource is intended to be set and forgotten. Running nedit in a
561 directory without a tags should not cause it to spew out errors. */
562 if (*GetPrefTagFile() != '\0') {
563 AddTagsFile(GetPrefTagFile(), TAG);
566 if (strcmp(GetPrefServerName(), "") != 0) {
567 IsServer = True;
570 /* Process any command line arguments (-tags, -do, -read, -create,
571 +<line_number>, -line, -server, and files to edit) not already
572 processed by RestoreNEditPrefs. */
573 fileSpecified = FALSE;
574 for (i=1; i<argc; i++) {
575 if (opts && !strcmp(argv[i], "--")) {
576 opts = False; /* treat all remaining arguments as filenames */
577 continue;
578 } else if (opts && !strcmp(argv[i], "-tags")) {
579 nextArg(argc, argv, &i);
580 if (!AddTagsFile(argv[i], TAG))
581 fprintf(stderr, "NEdit: Unable to load tags file\n");
582 } else if (opts && !strcmp(argv[i], "-do")) {
583 nextArg(argc, argv, &i);
584 if (checkDoMacroArg(argv[i]))
585 toDoCommand = argv[i];
586 } else if (opts && !strcmp(argv[i], "-read")) {
587 editFlags |= PREF_READ_ONLY;
588 } else if (opts && !strcmp(argv[i], "-create")) {
589 editFlags |= SUPPRESS_CREATE_WARN;
590 } else if (opts && !strcmp(argv[i], "-tabbed")) {
591 tabbed = 1;
592 group = 0; /* override -group option */
593 } else if (opts && !strcmp(argv[i], "-untabbed")) {
594 tabbed = 0;
595 group = 0; /* override -group option */
596 } else if (opts && !strcmp(argv[i], "-group")) {
597 group = 2; /* 2: start new group, 1: in group */
598 } else if (opts && !strcmp(argv[i], "-line")) {
599 nextArg(argc, argv, &i);
600 nRead = sscanf(argv[i], "%d", &lineNum);
601 if (nRead != 1)
602 fprintf(stderr, "NEdit: argument to line should be a number\n");
603 else
604 gotoLine = True;
605 } else if (opts && (*argv[i] == '+')) {
606 nRead = sscanf((argv[i]+1), "%d", &lineNum);
607 if (nRead != 1)
608 fprintf(stderr, "NEdit: argument to + should be a number\n");
609 else
610 gotoLine = True;
611 } else if (opts && !strcmp(argv[i], "-server")) {
612 IsServer = True;
613 } else if (opts && !strcmp(argv[i], "-xwarn")) {
614 XtAppSetWarningHandler(context, showWarningFilter);
615 } else if (opts && (!strcmp(argv[i], "-iconic") ||
616 !strcmp(argv[i], "-icon"))) {
617 iconic = True;
618 } else if (opts && !strcmp(argv[i], "-noiconic")) {
619 iconic = False;
620 } else if (opts && (!strcmp(argv[i], "-geometry") ||
621 !strcmp(argv[i], "-g"))) {
622 nextArg(argc, argv, &i);
623 geometry = argv[i];
624 } else if (opts && !strcmp(argv[i], "-lm")) {
625 nextArg(argc, argv, &i);
626 langMode = argv[i];
627 } else if (opts && !strcmp(argv[i], "-import")) {
628 nextArg(argc, argv, &i); /* already processed, skip */
629 } else if (opts && (!strcmp(argv[i], "-V") ||
630 !strcmp(argv[i], "-version"))) {
631 PrintVersion();
632 exit(EXIT_SUCCESS);
633 } else if (opts && (!strcmp(argv[i], "-h") ||
634 !strcmp(argv[i], "-help"))) {
635 fprintf(stderr, "%s", cmdLineHelp);
636 exit(EXIT_SUCCESS);
637 } else if (opts && (*argv[i] == '-')) {
638 #ifdef VMS
639 *argv[i] = '/';
640 #endif /*VMS*/
641 fprintf(stderr, "nedit: Unrecognized option %s\n%s", argv[i],
642 cmdLineHelp);
643 exit(EXIT_FAILURE);
644 } else {
645 #ifdef VMS
646 int numFiles, j;
647 char **nameList = NULL;
648 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
649 /* wildcards and to obtain a full VMS file specification */
650 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
651 /* for each expanded file name do: */
652 for (j = 0; j < numFiles; ++j) {
653 if (ParseFilename(nameList[j], filename, pathname) == 0) {
654 /* determine if file is to be openned in new tab, by
655 factoring the options -group, -tabbed & -untabbed */
656 if (group == 2) {
657 isTabbed = 0; /* start a new window for new group */
658 group = 1; /* next file will be within group */
659 } else if (group == 1) {
660 isTabbed = 1; /* new tab for file in group */
661 } else { /* not in group */
662 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
665 /* Files are opened in background to improve opening speed
666 by defering certain time consuiming task such as syntax
667 highlighting. At the end of the file-opening loop, the
668 last file opened will be raised to restore those deferred
669 items. The current file may also be raised if there're
670 macros to execute on. */
671 window = EditExistingFile(WindowList, filename, pathname,
672 editFlags, geometry, iconic, langMode, isTabbed,
673 True);
674 fileSpecified = TRUE;
676 if (window) {
677 CleanUpTabBarExposeQueue(window);
679 /* raise the last file of previous window */
680 if (lastFile && window->shell != lastFile->shell) {
681 CleanUpTabBarExposeQueue(lastFile);
682 RaiseDocument(lastFile);
685 if (!macroFileRead) {
686 ReadMacroInitFile(WindowList);
687 macroFileRead = True;
689 if (gotoLine)
690 SelectNumberedLine(window, lineNum);
691 if (toDoCommand != NULL) {
692 DoMacro(window, toDoCommand, "-do macro");
693 toDoCommand = NULL;
694 if (!IsValidWindow(window))
695 window = NULL; /* window closed by macro */
696 if (lastFile && !IsValidWindow(lastFile))
697 lastFile = NULL; /* window closed by macro */
701 /* register last opened file for later use */
702 if (window)
703 lastFile = window;
704 } else {
705 fprintf(stderr, "nedit: file name too long: %s\n", nameList[j]);
707 free(nameList[j]);
709 if (nameList != NULL)
710 free(nameList);
711 #else
712 if (ParseFilename(argv[i], filename, pathname) == 0 ) {
713 /* determine if file is to be openned in new tab, by
714 factoring the options -group, -tabbed & -untabbed */
715 if (group == 2) {
716 isTabbed = 0; /* start a new window for new group */
717 group = 1; /* next file will be within group */
718 } else if (group == 1) {
719 isTabbed = 1; /* new tab for file in group */
720 } else { /* not in group */
721 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
724 /* Files are opened in background to improve opening speed
725 by defering certain time consuiming task such as syntax
726 highlighting. At the end of the file-opening loop, the
727 last file opened will be raised to restore those deferred
728 items. The current file may also be raised if there're
729 macros to execute on. */
730 window = EditExistingFile(WindowList, filename, pathname,
731 editFlags, geometry, iconic, langMode, isTabbed, True);
732 fileSpecified = TRUE;
733 if (window) {
734 CleanUpTabBarExposeQueue(window);
736 /* raise the last tab of previous window */
737 if (lastFile && window->shell != lastFile->shell) {
738 CleanUpTabBarExposeQueue(lastFile);
739 RaiseDocument(lastFile);
742 if (!macroFileRead) {
743 ReadMacroInitFile(WindowList);
744 macroFileRead = True;
746 if (gotoLine)
747 SelectNumberedLine(window, lineNum);
748 if (toDoCommand != NULL) {
749 DoMacro(window, toDoCommand, "-do macro");
750 toDoCommand = NULL;
751 if (!IsValidWindow(window))
752 window = NULL; /* window closed by macro */
753 if (lastFile && !IsValidWindow(lastFile))
754 lastFile = NULL; /* window closed by macro */
758 /* register last opened file for later use */
759 if (window)
760 lastFile = window;
761 } else {
762 fprintf(stderr, "nedit: file name too long: %s\n", argv[i]);
764 #endif /*VMS*/
767 #ifdef VMS
768 VMSFileScanDone();
769 #endif /*VMS*/
771 /* Raise the last file opened */
772 if (lastFile) {
773 CleanUpTabBarExposeQueue(lastFile);
774 RaiseDocument(lastFile);
776 CheckCloseDim();
778 /* If no file to edit was specified, open a window to edit "Untitled" */
779 if (!fileSpecified) {
780 EditNewFile(NULL, geometry, iconic, langMode, NULL);
781 ReadMacroInitFile(WindowList);
782 CheckCloseDim();
783 if (toDoCommand != NULL)
784 DoMacro(WindowList, toDoCommand, "-do macro");
787 /* Begin remembering last command invoked for "Repeat" menu item */
788 AddLastCommandActionHook(context);
790 /* Set up communication port and write ~/.nedit_server_process file */
791 if (IsServer)
792 InitServerCommunication();
794 /* Process events. */
795 if (IsServer)
796 ServerMainLoop(context);
797 else
798 XtAppMainLoop(context);
800 /* Not reached but this keeps some picky compilers happy */
801 return EXIT_SUCCESS;
804 static void nextArg(int argc, char **argv, int *argIndex)
806 if (*argIndex + 1 >= argc) {
807 #ifdef VMS
808 *argv[*argIndex] = '/';
809 #endif /*VMS*/
810 fprintf(stderr, "NEdit: %s requires an argument\n%s", argv[*argIndex],
811 cmdLineHelp);
812 exit(EXIT_FAILURE);
814 (*argIndex)++;
818 ** Return True if -do macro is valid, otherwise write an error on stderr
820 static int checkDoMacroArg(const char *macro)
822 Program *prog;
823 char *errMsg, *stoppedAt, *tMacro;
824 int macroLen;
826 /* Add a terminating newline (which command line users are likely to omit
827 since they are typically invoking a single routine) */
828 macroLen = strlen(macro);
829 tMacro = XtMalloc(strlen(macro)+2);
830 strncpy(tMacro, macro, macroLen);
831 tMacro[macroLen] = '\n';
832 tMacro[macroLen+1] = '\0';
834 /* Do a test parse */
835 prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
836 XtFree(tMacro);
837 if (prog == NULL) {
838 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
839 return False;
841 FreeProgram(prog);
842 return True;
846 ** maskArgvKeywords and unmaskArgvKeywords mangle selected keywords by
847 ** replacing the '-' with a space, for the purpose of hiding them from
848 ** XtOpenDisplay's option processing. Why this silly scheme? XtOpenDisplay
849 ** really needs to see command line arguments, particularly -display, but
850 ** there's no way to change the option processing table it uses, to keep
851 ** it from consuming arguments which are meant to apply per-window, like
852 ** -geometry and -iconic.
854 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs)
856 int i, k;
858 for (i=1; i<argc; i++)
859 for (k=0; maskArgs[k]!=NULL; k++)
860 if (!strcmp(argv[i], maskArgs[k]))
861 argv[i][0] = ' ';
865 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs)
867 int i, k;
869 for (i=1; i<argc; i++)
870 for (k=0; maskArgs[k]!=NULL; k++)
871 if (argv[i][0]==' ' && !strcmp(&argv[i][1], &maskArgs[k][1]))
872 argv[i][0] = '-';
877 ** Some Linux distros ship with XKEYSYMDB set to a bogus filename which
878 ** breaks all Motif applications. Ignore that, and let X fall back on the
879 ** default which is far more likely to work.
881 static void fixupBrokenXKeysymDB(void)
883 const char *keysym = getenv("XKEYSYMDB");
885 if (keysym != NULL && access(keysym, F_OK) != 0)
886 putenv("XKEYSYMDB");
890 ** If we're not using the default visual, then some default resources in
891 ** the database (colors) are not valid, because they are indexes into the
892 ** default colormap. If we used them blindly, then we'd get "random"
893 ** unreadable colors. So we inspect the resource list, and use the
894 ** fallback "grey" color instead if this is the case.
896 static void patchResourcesForVisual(void)
898 Cardinal i;
899 Visual *visual;
900 int depth;
901 Colormap map;
902 Boolean usingDefaultVisual;
903 XrmDatabase db;
905 if (!TheDisplay)
906 return;
908 db = XtDatabase(TheDisplay);
910 usingDefaultVisual = FindBestVisual(TheDisplay,
911 APP_NAME,
912 APP_CLASS,
913 &visual,
914 &depth,
915 &map);
917 if (!usingDefaultVisual)
919 #ifndef LESSTIF_VERSION
921 For non-Lesstif versions, we have to put non-default visuals etc.
922 in the resource data base to make sure that all (shell) widgets
923 inherit them, especially Motif's own shells (eg, drag icons).
925 For Lesstif, this doesn't work, but luckily, Lesstif handles
926 non-default visuals etc. properly for its own shells and
927 we can take care of things for our shells (eg, call tips) through
928 our shell creation wrappers in misc.c.
931 XrmValue value;
932 value.addr = (XPointer)&visual;
933 value.size = sizeof(visual);
934 XrmPutResource(&db, "*visual", "Visual", &value);
935 value.addr = (XPointer)&map;
936 value.size = sizeof(map);
937 XrmPutResource(&db, "*colormap", "Colormap", &value);
938 value.addr = (XPointer)&depth;
939 value.size = sizeof(depth);
940 XrmPutResource(&db, "*depth", "Int", &value);
942 /* Drag-and-drop visuals do not work well when using a different
943 visual. One some systems, you'll just get a funny-looking icon
944 (maybe all-black) but on other systems it crashes with a BadMatch
945 error. This appears to be an OSF Motif bug. It would be nicer
946 to just disable the visual itself, instead of the entire drag
947 operation.
949 Update: this is no longer necessary since all problems with
950 non-default visuals should now be solved.
952 XrmPutStringResource(&db, "*dragInitiatorProtocolStyle", "DRAG_NONE");
954 #endif
956 for (i = 1; i < XtNumber(fallbackResources); ++i)
958 Cardinal resIndex = i - 1;
960 if (strstr(fallbackResources[resIndex], "*background:") ||
961 strstr(fallbackResources[resIndex], "*foreground:"))
963 /* Qualify by application name to prevent them from being
964 converted against the wrong colormap. */
965 char buf[1024] = "*" APP_NAME;
966 strcat(buf, fallbackResources[resIndex]);
967 XrmPutLineResource(&db, buf);
974 ** Several KDE version (2.x and 3.x) ship with a template application-default
975 ** file for NEdit in which several strings have to be substituted in order to
976 ** make it a valid .ad file. However, for some reason (a KDE bug?), the
977 ** template sometimes ends up in the resource db unmodified, such that several
978 ** invalid entries are present. This function checks for the presence of such
979 ** invalid entries and silently replaces them with NEdit's default values where
980 ** necessary. Without this, NEdit will typically write several warnings to
981 ** the terminal (Cannot convert string "FONTLIST" to type FontStruct etc) and
982 ** fall back on some really ugly colors and fonts.
984 static void patchResourcesForKDEbug(void)
987 * These are the resources found the Nedit.ad template shipped with KDE 3.0.
989 static const char* buggyResources[][3] = {
990 { "*fontList", "FONTLIST", NEDIT_DEFAULT_FONT },
991 { "*XmText.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
992 { "*XmText.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
993 { "*XmTextField.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
994 { "*XmTextField.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
995 { "*XmList.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
996 { "*XmList.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
997 { "*helpText.background", "BACKGROUND", NEDIT_DEFAULT_HELP_BG },
998 { "*helpText.foreground", "FOREGROUND", NEDIT_DEFAULT_HELP_FG },
999 { "*background", "BACKGROUND", NEDIT_DEFAULT_BG },
1000 { "*foreground", "FOREGROUND", NEDIT_DEFAULT_FG, },
1001 { "*selectColor", "BACKGROUND", NEDIT_DEFAULT_SEL_BG },
1002 { "*highlightColor", "BACKGROUND", NEDIT_DEFAULT_HI_BG },
1003 { "*text.background", "WINDOW_BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
1004 { "*text.foreground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_FG },
1005 { "*text.selectBackground", "SELECT_BACKGROUND", NEDIT_DEFAULT_SEL_BG },
1006 { "*text.selectForeground", "SELECT_FOREGROUND", NEDIT_DEFAULT_SEL_FG },
1007 { "*text.cursorForeground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_CURSOR_FG},
1008 /* { "*remapDeleteKey", "False", }, OK */
1009 /* { "!*text.heavyCursor", "True" }, OK */
1010 /* { "!*BlinkRate", "0" }, OK */
1011 /* { "*shell", "/bin/sh" }, OK */
1012 { "*statsLine.background", "BACKGROUND", NEDIT_DEFAULT_BG },
1013 { "*statsLine.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
1014 { NULL, NULL, NULL } };
1015 XrmDatabase db;
1016 int i;
1018 if (!TheDisplay)
1019 return;
1021 db = XtDatabase(TheDisplay);
1023 i = 0;
1024 while (buggyResources[i][0])
1026 const char* resource = buggyResources[i][0];
1027 const char* buggyValue = buggyResources[i][1];
1028 const char* defaultValue = buggyResources[i][2];
1029 char name[128] = APP_NAME;
1030 char class[128] = APP_CLASS;
1031 char* type;
1032 XrmValue resValue;
1034 strcat(name, resource);
1035 strcat(class, resource); /* Is this ok ? */
1037 if (XrmGetResource(db, name, class, &type, &resValue) &&
1038 !strcmp(type, XmRString))
1040 /* Buggy value? Replace by the default. */
1041 if (!strcmp(resValue.addr, buggyValue))
1043 XrmPutStringResource(&db, &name[0], (char*)defaultValue);
1046 ++i;
1051 ** It seems OSF Motif cannot handle locales with UTF-8 at the end, crashing
1052 ** in various places. The easiest one to find is to open the File Open
1053 ** dialog box. So we lop off UTF-8 if it's there and continue. Newer
1054 ** versions of Linux distros (e.g., RedHat 8) set the default language to
1055 ** to have "UTF-8" at the end, so users were seeing these crashes.
1057 ** LessTif and OpenMotif 2.3 don't appear to have this problem.
1059 ** Some versions OpenMotif 2.2.3 don't have this problem, but there's
1060 ** no way to tell which.
1063 static void patchLocaleForMotif(void)
1065 #if !(defined(LESSTIF_VERSION) || XmVersion >= 2003)
1066 const char *ctype;
1067 char ctypebuf[1024];
1068 char *utf_start;
1070 /* We have to check LC_CTYPE specifically here, because the system
1071 might specify different locales for different categories (why
1072 anyone would want to do this is beyond me). As far as I can
1073 tell, only LC_CTYPE causes OSF Motif to crash. If it turns out
1074 others do, we'll have to iterate over a list of locale cateogries
1075 and patch every one of them. */
1077 ctype = setlocale(LC_CTYPE, NULL);
1079 if (!ctype)
1080 return;
1082 strncpy(ctypebuf, ctype, sizeof ctypebuf);
1084 if ((utf_start = strstr(ctypebuf, ".utf8")) ||
1085 (utf_start = strstr(ctypebuf, ".UTF-8")))
1087 *utf_start = '\0'; /* Samurai chop */
1088 setlocale(LC_CTYPE, ctypebuf);
1090 #endif
1095 ** Same as the default X language procedure, except we check if Motif can
1096 ** handle the locale as well.
1099 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure)
1101 /* "xnl" will be set if the user passes in a new language via the
1102 "-xnllanguage" flag. If it's empty, then setlocale will get
1103 the default locale by some system-dependent means (usually,
1104 reading some environment variables). */
1106 if (! setlocale(LC_ALL, xnl)) {
1107 XtWarning("locale not supported by C library, locale unchanged");
1110 patchLocaleForMotif();
1112 if (! XSupportsLocale()) {
1113 XtWarning("locale not supported by Xlib, locale set to C");
1114 setlocale(LC_ALL, "C");
1116 if (! XSetLocaleModifiers(""))
1117 XtWarning("X locale modifiers not supported, using default");
1119 return setlocale(LC_ALL, NULL); /* re-query in case overwritten */
1122 static int sortAlphabetical(const void* k1, const void* k2)
1124 const char* key1 = *(const char**)k1;
1125 const char* key2 = *(const char**)k2;
1126 return strcmp(key1, key2);
1130 * Checks whether a given virtual key binding string is invalid.
1131 * A binding is considered invalid if there are duplicate key entries.
1133 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
1135 int maxCount = 1, i, count;
1136 const char *pos = (const char*)bindings;
1137 char *copy;
1138 char *pos2, *pos3;
1139 char **keys;
1141 /* First count the number of bindings; bindings are separated by \n
1142 strings. The number of bindings equals the number of \n + 1.
1143 Beware of leading and trailing \n; the number is actually an
1144 upper bound on the number of entries. */
1145 while ((pos = strstr(pos, "\n")))
1147 ++pos;
1148 ++maxCount;
1151 if (maxCount == 1)
1152 return False; /* One binding is always ok */
1154 keys = (char**)malloc(maxCount*sizeof(char*));
1155 copy = XtNewString((const char*)bindings);
1156 i = 0;
1157 pos2 = copy;
1159 count = 0;
1160 while (i<maxCount && pos2 && *pos2)
1162 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
1164 if (*pos2 == '!') /* Ignore comment lines */
1166 pos2 = strstr(pos2, "\n");
1167 continue; /* Go to the next line */
1170 if (*pos2)
1172 keys[i++] = pos2;
1173 ++count;
1174 pos3 = strstr(pos2, ":");
1175 if (pos3)
1177 *pos3++ = 0; /* Cut the string and jump to the next entry */
1178 pos2 = pos3;
1180 pos2 = strstr(pos2, "\n");
1184 if (count <= 1)
1186 free(keys);
1187 XtFree(copy);
1188 return False; /* No conflict */
1191 /* Sort the keys and look for duplicates */
1192 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
1193 for (i=1; i<count; ++i)
1195 if (!strcmp(keys[i-1], keys[i]))
1197 /* Duplicate detected */
1198 free(keys);
1199 XtFree(copy);
1200 return True;
1203 free(keys);
1204 XtFree(copy);
1205 return False;
1209 * Optionally sanitizes the Motif default virtual key bindings.
1210 * Some applications install invalid bindings (attached to the root window),
1211 * which cause certain keys to malfunction in NEdit.
1212 * Through an X-resource, users can choose whether they want
1213 * - to always keep the existing bindings
1214 * - to override the bindings only if they are invalid
1215 * - to always override the existing bindings.
1218 static Atom virtKeyAtom;
1220 static unsigned char* sanitizeVirtualKeyBindings(void)
1222 int overrideBindings = GetPrefOverrideVirtKeyBindings();
1223 Window rootWindow;
1224 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
1225 Atom dummyAtom;
1226 int getFmt;
1227 unsigned long dummyULong, nItems;
1228 unsigned char *insaneVirtKeyBindings = NULL;
1230 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
1232 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
1233 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1235 /* Remove the property, if it exists; we'll restore it later again */
1236 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
1237 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
1238 &dummyULong, &insaneVirtKeyBindings) != Success
1239 || nItems == 0)
1241 return NULL; /* No binding yet; nothing to do */
1244 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
1246 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
1248 /* Restore the property immediately; it seems valid */
1249 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1250 PropModeReplace, insaneVirtKeyBindings,
1251 strlen((const char*)insaneVirtKeyBindings));
1252 XFree((char*)insaneVirtKeyBindings);
1253 return NULL; /* Prevent restoration */
1256 return insaneVirtKeyBindings;
1260 * NEdit should not mess with the bindings installed by other apps, so we
1261 * just restore whatever was installed, if necessary
1263 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
1265 if (insaneVirtKeyBindings)
1267 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1268 /* Restore the root window atom, such that we don't affect
1269 other apps. */
1270 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1271 PropModeReplace, insaneVirtKeyBindings,
1272 strlen((const char*)insaneVirtKeyBindings));
1273 XFree((char*)insaneVirtKeyBindings);
1278 ** Warning handler that suppresses harmless but annoying warnings generated
1279 ** by non-production Lesstif versions.
1281 static void showWarningFilter(String message)
1283 const char* bogusMessages[] = {
1284 #ifdef LESSTIF_VERSION
1285 "XmFontListCreate() is an obsolete function!",
1286 "No type converter registered for 'String' to 'PathMode' conversion.",
1287 "XtRemoveGrab asked to remove a widget not on the list",
1288 #endif
1289 NULL
1291 const char **bogusMessage = &bogusMessages[0];
1293 while (*bogusMessage) {
1294 size_t bogusLen = strlen(*bogusMessage);
1295 if (strncmp(message, *bogusMessage, bogusLen) == 0) {
1296 #ifdef DEBUG_LESSTIF_WARNINGS
1297 /* Developers may want to see which messages are suppressed. */
1298 fprintf(stderr, "[SUPPRESSED] %s\n", message);
1299 #endif
1300 return;
1302 ++bogusMessage;
1305 /* An unknown message. Keep it. */
1306 fprintf(stderr, "%s\n", message);
1309 static void noWarningFilter(String message)
1311 return;