1 static const char CVSID
[] = "$Id: macro.c,v 1.98 2005/02/11 02:42:18 ajbj Exp $";
2 /*******************************************************************************
4 * macro.c -- Macro file processing, learn/replay, and built-in macro *
7 * Copyright (C) 1999 Mark Edel *
9 * This is free software; you can redistribute it and/or modify it under the *
10 * terms of the GNU General Public License as published by the Free Software *
11 * Foundation; either version 2 of the License, or (at your option) any later *
12 * version. In addition, you may distribute versions of this program linked to *
13 * Motif or Open Motif. See README for details. *
15 * This software is distributed in the hope that it will be useful, but WITHOUT *
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
20 * You should have received a copy of the GNU General Public License along with *
21 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
22 * Place, Suite 330, Boston, MA 02111-1307 USA *
24 * Nirvana Text Editor *
27 * Written by Mark Edel *
29 *******************************************************************************/
32 #include "../config.h"
40 #include "preferences.h"
41 #include "interpret.h"
46 #include "smartIndent.h"
48 #include "selection.h"
52 #include "../util/DialogF.h"
53 #include "../util/misc.h"
54 #include "../util/fileUtils.h"
55 #include "../util/utils.h"
56 #include "highlight.h"
57 #include "highlightData.h"
66 #include "../util/VMSparam.h"
71 #include <sys/types.h>
74 #include <sys/param.h>
79 #include <X11/Intrinsic.h>
80 #include <X11/keysym.h>
82 #include <Xm/CutPaste.h>
84 #include <Xm/RowColumn.h>
85 #include <Xm/LabelG.h>
87 #include <Xm/ToggleB.h>
88 #include <Xm/DialogS.h>
89 #include <Xm/MessageB.h>
90 #include <Xm/SelectioB.h>
93 #include <Xm/Separator.h>
99 /* Maximum number of actions in a macro and args in
100 an action (to simplify the reader) */
101 #define MAX_MACRO_ACTIONS 1024
102 #define MAX_ACTION_ARGS 40
104 /* How long to wait (msec) before putting up Macro Command banner */
105 #define BANNER_WAIT_TIME 6000
107 /* The following definitions cause an exit from the macro with a message */
108 /* added if (1) to remove compiler warnings on solaris */
109 #define M_FAILURE(s) do { *errMsg = s; if (1) return False; } while (0)
110 #define M_STR_ALLOC_ASSERT(xDV) do { if (xDV.tag == STRING_TAG && !xDV.val.str.rep) { *errMsg = "Failed to allocate value: %s"; return(False); } } while (0)
111 #define M_ARRAY_INSERT_FAILURE() M_FAILURE("array element failed to insert: %s")
113 /* Data attached to window during shell command execution with
114 information for controling and communicating with the process */
116 XtIntervalId bannerTimeoutID
;
117 XtWorkProcId continueWorkProcID
;
119 char closeOnCompletion
;
121 RestartData
*context
;
125 /* Widgets and global data for Repeat dialog */
127 WindowInfo
*forWindow
;
129 Widget shell
, repeatText
, lastCmdToggle
;
130 Widget inSelToggle
, toEndToggle
;
133 static void cancelLearn(void);
134 static void runMacro(WindowInfo
*window
, Program
*prog
);
135 static void finishMacroCmdExecution(WindowInfo
*window
);
136 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
);
137 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
138 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
);
139 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
);
140 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
141 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
142 XEvent
*event
, String
*params
, Cardinal
*numParams
);
143 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
144 XEvent
*event
, String
*params
, Cardinal
*numParams
);
145 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
146 String
*params
, Cardinal numParams
);
147 static int isMouseAction(const char *action
);
148 static int isRedundantAction(const char *action
);
149 static int isIgnoredAction(const char *action
);
150 static int readCheckMacroString(Widget dialogParent
, char *string
,
151 WindowInfo
*runWindow
, const char *errIn
, char **errPos
);
152 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
);
153 static Boolean
continueWorkProc(XtPointer clientData
);
154 static int escapeStringChars(char *fromString
, char *toString
);
155 static int escapedStringLength(char *string
);
156 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
157 DataValue
*result
, char **errMsg
);
158 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
159 DataValue
*result
, char **errMsg
);
160 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
161 DataValue
*result
, char **errMsg
);
162 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
163 DataValue
*result
, char **errMsg
);
164 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
165 DataValue
*result
, char **errMsg
);
166 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
167 DataValue
*result
, char **errMsg
);
168 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
169 DataValue
*result
, char **errMsg
);
170 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
171 DataValue
*result
, char **errMsg
);
172 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
173 DataValue
*result
, char **errMsg
);
174 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
175 DataValue
*result
, char **errMsg
);
176 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
177 DataValue
*result
, char **errMsg
);
178 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
179 DataValue
*result
, char **errMsg
);
180 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
181 DataValue
*result
, char **errMsg
);
182 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
183 DataValue
*result
, char **errMsg
);
184 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
185 DataValue
*result
, char **errMsg
);
186 static int writeOrAppendFile(int append
, WindowInfo
*window
,
187 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
);
188 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
189 DataValue
*result
, char **errMsg
);
190 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
191 DataValue
*result
, char **errMsg
);
192 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
193 DataValue
*result
, char **errMsg
);
194 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
195 DataValue
*result
, char **errMsg
);
196 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
197 DataValue
*result
, char **errMsg
);
198 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
199 DataValue
*result
, char **errMsg
);
200 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
201 DataValue
*result
, char **errMsg
);
202 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
203 DataValue
*result
, char **errMsg
);
204 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
205 DataValue
*result
, char **errMsg
);
206 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
207 DataValue
*result
, char **errMsg
);
208 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
209 DataValue
*result
, char **errMsg
);
210 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
211 DataValue
*result
, char **errMsg
);
212 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
213 DataValue
*result
, char **errMsg
);
214 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
215 DataValue
*result
, char **errMsg
);
216 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
217 DataValue
*result
, char **errMsg
);
218 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
);
219 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
);
220 #ifdef LESSTIF_VERSION
221 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
223 #endif /* LESSTIF_VERSION */
224 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
225 DataValue
*result
, char **errMsg
);
226 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
228 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
230 #ifdef LESSTIF_VERSION
231 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
233 #endif /* LESSTIF_VERSION */
234 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
235 DataValue
*result
, char **errMsg
);
236 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
237 DataValue
*result
, char **errMsg
);
239 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
240 DataValue
*result
, char **errMsg
);
241 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
243 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
246 #ifdef LESSTIF_VERSION
247 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
249 #endif /* LESSTIF_VERSION */
250 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
251 DataValue
*result
, char **errMsg
);
252 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
253 DataValue
*result
, char **errMsg
);
255 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
256 int nArgs, DataValue *result, char **errMsg);
258 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
259 DataValue
*result
, char **errMsg
);
260 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
261 DataValue
*result
, char **errMsg
);
262 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
263 DataValue
*result
, char **errMsg
);
264 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
265 DataValue
*result
, char **errMsg
);
266 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
267 DataValue
*result
, char **errMsg
);
268 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
269 DataValue
*result
, char **errMsg
);
270 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
271 DataValue
*result
, char **errMsg
);
272 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
273 DataValue
*result
, char **errMsg
);
274 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
275 DataValue
*result
, char **errMsg
);
276 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
277 DataValue
*result
, char **errMsg
);
278 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
279 DataValue
*result
, char **errMsg
);
280 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
281 DataValue
*result
, char **errMsg
);
282 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
283 DataValue
*result
, char **errMsg
);
284 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
285 DataValue
*result
, char **errMsg
);
286 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
287 DataValue
*result
, char **errMsg
);
288 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
289 DataValue
*result
, char **errMsg
);
290 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
291 DataValue
*result
, char **errMsg
);
292 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
293 DataValue
*result
, char **errMsg
);
294 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
295 DataValue
*result
, char **errMsg
);
296 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
297 DataValue
*result
, char **errMsg
);
298 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
299 DataValue
*result
, char **errMsg
);
300 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
301 DataValue
*result
, char **errMsg
);
302 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
303 DataValue
*result
, char **errMsg
);
304 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
305 DataValue
*result
, char **errMsg
);
306 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
307 DataValue
*result
, char **errMsg
);
308 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
309 DataValue
*result
, char **errMsg
);
310 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
311 DataValue
*result
, char **errMsg
);
312 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
313 DataValue
*result
, char **errMsg
);
314 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
315 DataValue
*result
, char **errMsg
);
316 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
317 DataValue
*result
, char **errMsg
);
318 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
319 DataValue
*result
, char **errMsg
);
320 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
321 DataValue
*result
, char **errMsg
);
322 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
323 DataValue
*result
, char **errMsg
);
324 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
325 DataValue
*result
, char **errMsg
);
326 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
327 DataValue
*result
, char **errMsg
);
328 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
329 DataValue
*result
, char **errMsg
);
330 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
331 DataValue
*result
, char **errMsg
);
332 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
333 DataValue
*result
, char **errMsg
);
334 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
335 DataValue
*result
, char **errMsg
);
336 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
337 DataValue
*result
, char **errMsg
);
338 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
339 DataValue
*result
, char **errMsg
);
340 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
341 DataValue
*result
, char **errMsg
);
342 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
343 DataValue
*result
, char **errMsg
);
344 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
345 DataValue
*result
, char **errMsg
);
346 static int readSearchArgs(DataValue
*argList
, int nArgs
, int*searchDirection
,
347 int *searchType
, int *wrap
, char **errMsg
);
348 static int wrongNArgsErr(char **errMsg
);
349 static int tooFewArgsErr(char **errMsg
);
350 static int strCaseCmp(char *str1
, char *str2
);
351 static int readIntArg(DataValue dv
, int *result
, char **errMsg
);
352 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
355 static int backlightStringMV(WindowInfo *window, DataValue *argList,
356 int nArgs, DataValue *result, char **errMsg);
358 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
,
359 int nArgs
, DataValue
*result
, char **errMsg
);
360 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
361 DataValue
*result
, char **errMsg
);
362 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
363 DataValue
*result
, char **errMsg
);
364 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
365 DataValue
*result
, char **errMsg
);
366 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
367 DataValue
*result
, char **errMsg
);
368 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
369 DataValue
*result
, char **errMsg
);
370 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
371 DataValue
*result
, char **errMsg
);
372 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
373 DataValue
*result
, char **errMsg
);
374 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
375 DataValue
*result
, char **errMsg
);
376 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
377 int nArgs
, DataValue
*result
, char **errMsg
);
378 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
379 int nArgs
, DataValue
*result
, char **errMsg
);
380 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
381 int nArgs
, DataValue
*result
, char **errMsg
);
382 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
383 int nArgs
, DataValue
*result
, char **errMsg
);
385 static int fillPatternResult(DataValue
*result
, char **errMsg
, WindowInfo
*window
,
386 char *patternName
, Boolean preallocatedPatternName
, Boolean includeName
,
387 char *styleName
, int bufferPos
);
388 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
389 DataValue
*result
, char **errMsg
);
390 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
391 DataValue
*result
, char **errMsg
);
393 static int fillStyleResult(DataValue
*result
, char **errMsg
,
394 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
395 Boolean includeName
, int patCode
, int bufferPos
);
396 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
397 DataValue
*result
, char **errMsg
);
398 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
399 DataValue
*result
, char **errMsg
);
401 /* Built-in subroutines and variables for the macro language */
402 static BuiltInSubr MacroSubrs
[] = {lengthMS
, getRangeMS
, tPrintMS
,
403 dialogMS
, stringDialogMS
, replaceRangeMS
, replaceSelectionMS
,
404 setCursorPosMS
, getCharacterMS
, minMS
, maxMS
, searchMS
,
405 searchStringMS
, substringMS
, replaceSubstringMS
, readFileMS
,
406 writeFileMS
, appendFileMS
, beepMS
, getSelectionMS
, validNumberMS
,
407 replaceInStringMS
, selectMS
, selectRectangleMS
, focusWindowMS
,
408 shellCmdMS
, stringToClipboardMS
, clipboardToStringMS
, toupperMS
,
409 tolowerMS
, listDialogMS
, getenvMS
,
410 stringCompareMS
, splitMS
, calltipMS
, killCalltipMS
,
411 /* DISABLED for 5.4 setBacklightStringMS,*/
412 rangesetCreateMS
, rangesetDestroyMS
,
413 rangesetAddMS
, rangesetSubtractMS
, rangesetInvertMS
,
414 rangesetInfoMS
, rangesetRangeMS
, rangesetIncludesPosMS
,
415 rangesetSetColorMS
, rangesetSetNameMS
, rangesetSetModeMS
,
417 getPatternByNameMS
, getPatternAtPosMS
,
418 getStyleByNameMS
, getStyleAtPosMS
420 #define N_MACRO_SUBRS (sizeof MacroSubrs/sizeof *MacroSubrs)
421 static const char *MacroSubrNames
[N_MACRO_SUBRS
] = {"length", "get_range", "t_print",
422 "dialog", "string_dialog", "replace_range", "replace_selection",
423 "set_cursor_pos", "get_character", "min", "max", "search",
424 "search_string", "substring", "replace_substring", "read_file",
425 "write_file", "append_file", "beep", "get_selection", "valid_number",
426 "replace_in_string", "select", "select_rectangle", "focus_window",
427 "shell_command", "string_to_clipboard", "clipboard_to_string",
428 "toupper", "tolower", "list_dialog", "getenv",
429 "string_compare", "split", "calltip", "kill_calltip",
430 /* DISABLED for 5.4 "set_backlight_string", */
431 "rangeset_create", "rangeset_destroy",
432 "rangeset_add", "rangeset_subtract", "rangeset_invert",
433 "rangeset_info", "rangeset_range", "rangeset_includes",
434 "rangeset_set_color", "rangeset_set_name", "rangeset_set_mode",
435 "rangeset_get_by_name",
436 "get_pattern_by_name", "get_pattern_at_pos",
437 "get_style_by_name", "get_style_at_pos"
439 static BuiltInSubr SpecialVars
[] = {cursorMV
, lineMV
, columnMV
,
440 fileNameMV
, filePathMV
, lengthMV
, selectionStartMV
, selectionEndMV
,
441 selectionLeftMV
, selectionRightMV
, wrapMarginMV
, tabDistMV
,
442 emTabDistMV
, useTabsMV
, languageModeMV
, modifiedMV
,
443 statisticsLineMV
, incSearchLineMV
, showLineNumbersMV
,
444 autoIndentMV
, wrapTextMV
, highlightSyntaxMV
,
445 makeBackupCopyMV
, incBackupMV
, showMatchingMV
,
446 overTypeModeMV
, readOnlyMV
, lockedMV
, fileFormatMV
,
447 fontNameMV
, fontNameItalicMV
,
448 fontNameBoldMV
, fontNameBoldItalicMV
, subscriptSepMV
,
449 minFontWidthMV
, maxFontWidthMV
, topLineMV
, numDisplayLinesMV
,
450 displayWidthMV
, activePaneMV
, nPanesMV
, emptyArrayMV
,
451 serverNameMV
, calltipIDMV
,
452 /* DISABLED for 5.4 backlightStringMV, */
455 #define N_SPECIAL_VARS (sizeof SpecialVars/sizeof *SpecialVars)
456 static const char *SpecialVarNames
[N_SPECIAL_VARS
] = {"$cursor", "$line", "$column",
457 "$file_name", "$file_path", "$text_length", "$selection_start",
458 "$selection_end", "$selection_left", "$selection_right",
459 "$wrap_margin", "$tab_dist", "$em_tab_dist", "$use_tabs",
460 "$language_mode", "$modified",
461 "$statistics_line", "$incremental_search_line", "$show_line_numbers",
462 "$auto_indent", "$wrap_text", "$highlight_syntax",
463 "$make_backup_copy", "$incremental_backup", "$show_matching",
464 "$overtype_mode", "$read_only", "$locked", "$file_format",
465 "$font_name", "$font_name_italic",
466 "$font_name_bold", "$font_name_bold_italic", "$sub_sep",
467 "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
468 "$display_width", "$active_pane", "$n_panes", "$empty_array",
469 "$server_name", "$calltip_ID",
470 /* DISABLED for 5.4 "$backlight_string", */
474 /* Global symbols for returning values from built-in functions */
475 #define N_RETURN_GLOBALS 5
476 enum retGlobalSyms
{STRING_DIALOG_BUTTON
, SEARCH_END
, READ_STATUS
,
477 SHELL_CMD_STATUS
, LIST_DIALOG_BUTTON
};
478 static const char *ReturnGlobalNames
[N_RETURN_GLOBALS
] = {"$string_dialog_button",
479 "$search_end", "$read_status", "$shell_cmd_status",
480 "$list_dialog_button"};
481 static Symbol
*ReturnGlobals
[N_RETURN_GLOBALS
];
483 /* List of actions not useful when learning a macro sequence (also see below) */
484 static char* IgnoredActions
[] = {"focusIn", "focusOut"};
486 /* List of actions intended to be attached to mouse buttons, which the user
487 must be warned can't be recorded in a learn/replay sequence */
488 static const char* MouseActions
[] = {"grab_focus", "extend_adjust", "extend_start",
489 "extend_end", "secondary_or_drag_adjust", "secondary_adjust",
490 "secondary_or_drag_start", "secondary_start", "move_destination",
491 "move_to", "move_to_or_end_drag", "copy_to", "copy_to_or_end_drag",
492 "exchange", "process_bdrag", "mouse_pan"};
494 /* List of actions to not record because they
495 generate further actions, more suitable for recording */
496 static const char* RedundantActions
[] = {"open_dialog", "save_as_dialog",
497 "revert_to_saved_dialog", "include_file_dialog", "load_macro_file_dialog",
498 "load_tags_file_dialog", "find_dialog", "replace_dialog",
499 "goto_line_number_dialog", "mark_dialog", "goto_mark_dialog",
500 "control_code_dialog", "filter_selection_dialog", "execute_command_dialog",
501 "repeat_dialog", "start_incremental_find"};
503 /* The last command executed (used by the Repeat command) */
504 static char *LastCommand
= NULL
;
506 /* The current macro to execute on Replay command */
507 static char *ReplayMacro
= NULL
;
509 /* Buffer where macro commands are recorded in Learn mode */
510 static textBuffer
*MacroRecordBuf
= NULL
;
512 /* Action Hook id for recording actions for Learn mode */
513 static XtActionHookId MacroRecordActionHook
= 0;
515 /* Window where macro recording is taking place */
516 static WindowInfo
*MacroRecordWindow
= NULL
;
518 /* Arrays for translating escape characters in escapeStringChars */
519 static char ReplaceChars
[] = "\\\"ntbrfav";
520 static char EscapeChars
[] = "\\\"\n\t\b\r\f\a\v";
523 ** Install built-in macro subroutines and special variables for accessing
524 ** editor information
526 void RegisterMacroSubroutines(void)
528 static DataValue subrPtr
= {NO_TAG
, {0}}, noValue
= {NO_TAG
, {0}};
531 /* Install symbols for built-in routines and variables, with pointers
532 to the appropriate c routines to do the work */
533 for (i
=0; i
<N_MACRO_SUBRS
; i
++) {
534 subrPtr
.val
.subr
= MacroSubrs
[i
];
535 InstallSymbol(MacroSubrNames
[i
], C_FUNCTION_SYM
, subrPtr
);
537 for (i
=0; i
<N_SPECIAL_VARS
; i
++) {
538 subrPtr
.val
.subr
= SpecialVars
[i
];
539 InstallSymbol(SpecialVarNames
[i
], PROC_VALUE_SYM
, subrPtr
);
542 /* Define global variables used for return values, remember their
543 locations so they can be set without a LookupSymbol call */
544 for (i
=0; i
<N_RETURN_GLOBALS
; i
++)
545 ReturnGlobals
[i
] = InstallSymbol(ReturnGlobalNames
[i
], GLOBAL_SYM
,
549 #define MAX_LEARN_MSG_LEN ((2 * MAX_ACCEL_LEN) + 60)
550 void BeginLearn(WindowInfo
*window
)
558 char message
[MAX_LEARN_MSG_LEN
];
560 /* If we're already in learn mode, return */
561 if (MacroRecordActionHook
!= 0)
564 /* dim the inappropriate menus and items, and undim finish and cancel */
565 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
566 if (!IsTopDocument(win
))
568 XtSetSensitive(win
->learnItem
, False
);
570 SetSensitive(window
, window
->finishLearnItem
, True
);
571 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
572 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
574 SetSensitive(window
, window
->cancelMacroItem
, True
);
576 /* Mark the window where learn mode is happening */
577 MacroRecordWindow
= window
;
579 /* Allocate a text buffer for accumulating the macro strings */
580 MacroRecordBuf
= BufCreate();
582 /* Add the action hook for recording the actions */
583 MacroRecordActionHook
=
584 XtAppAddActionHook(XtWidgetToApplicationContext(window
->shell
),
585 learnActionHook
, window
);
587 /* Extract accelerator texts from menu PushButtons */
588 XtVaGetValues(window
->finishLearnItem
, XmNacceleratorText
, &xmFinish
, NULL
);
589 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
591 /* Translate Motif strings to char* */
592 cFinish
= GetXmStringText(xmFinish
);
593 cCancel
= GetXmStringText(xmCancel
);
595 /* Free Motif Strings */
596 XmStringFree(xmFinish
);
597 XmStringFree(xmCancel
);
600 if (cFinish
[0] == '\0') {
601 if (cCancel
[0] == '\0') {
602 strncpy(message
, "Learn Mode -- Use menu to finish or cancel",
604 message
[MAX_LEARN_MSG_LEN
- 1] = '\0';
608 "Learn Mode -- Use menu to finish, press %s to cancel",
613 if (cCancel
[0] == '\0') {
615 "Learn Mode -- Press %s to finish, use menu to cancel",
621 "Learn Mode -- Press %s to finish, %s to cancel",
631 /* Put up the learn-mode banner */
632 SetModeMessage(window
, message
);
635 void AddLastCommandActionHook(XtAppContext context
)
637 XtAppAddActionHook(context
, lastActionHook
, NULL
);
640 void FinishLearn(void)
644 /* If we're not in learn mode, return */
645 if (MacroRecordActionHook
== 0)
648 /* Remove the action hook */
649 XtRemoveActionHook(MacroRecordActionHook
);
650 MacroRecordActionHook
= 0;
652 /* Free the old learn/replay sequence */
653 if (ReplayMacro
!= NULL
)
656 /* Store the finished action for the replay menu item */
657 ReplayMacro
= BufGetAll(MacroRecordBuf
);
659 /* Free the buffer used to accumulate the macro sequence */
660 BufFree(MacroRecordBuf
);
662 /* Undim the menu items dimmed during learn */
663 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
664 if (!IsTopDocument(win
))
666 XtSetSensitive(win
->learnItem
, True
);
668 if (IsTopDocument(MacroRecordWindow
)) {
669 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
670 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
673 /* Undim the replay and paste-macro buttons */
674 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
675 if (!IsTopDocument(win
))
677 XtSetSensitive(win
->replayItem
, True
);
679 DimPasteReplayBtns(True
);
681 /* Clear learn-mode banner */
682 ClearModeMessage(MacroRecordWindow
);
686 ** Cancel Learn mode, or macro execution (they're bound to the same menu item)
688 void CancelMacroOrLearn(WindowInfo
*window
)
690 if (MacroRecordActionHook
!= 0)
692 else if (window
->macroCmdData
!= NULL
)
693 AbortMacroCommand(window
);
696 static void cancelLearn(void)
700 /* If we're not in learn mode, return */
701 if (MacroRecordActionHook
== 0)
704 /* Remove the action hook */
705 XtRemoveActionHook(MacroRecordActionHook
);
706 MacroRecordActionHook
= 0;
708 /* Free the macro under construction */
709 BufFree(MacroRecordBuf
);
711 /* Undim the menu items dimmed during learn */
712 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
713 if (!IsTopDocument(win
))
715 XtSetSensitive(win
->learnItem
, True
);
717 if (IsTopDocument(MacroRecordWindow
)) {
718 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
719 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
722 /* Clear learn-mode banner */
723 ClearModeMessage(MacroRecordWindow
);
727 ** Execute the learn/replay sequence stored in "window"
729 void Replay(WindowInfo
*window
)
732 char *errMsg
, *stoppedAt
;
734 /* Verify that a replay macro exists and it's not empty and that */
735 /* we're not already running a macro */
736 if (ReplayMacro
!= NULL
&&
737 ReplayMacro
[0] != 0 &&
738 window
->macroCmdData
== NULL
) {
739 /* Parse the replay macro (it's stored in text form) and compile it into
740 an executable program "prog" */
741 prog
= ParseMacro(ReplayMacro
, &errMsg
, &stoppedAt
);
744 "NEdit internal error, learn/replay macro syntax error: %s\n",
749 /* run the executable program */
750 runMacro(window
, prog
);
755 ** Read the initial NEdit macro file if one exists.
757 void ReadMacroInitFile(WindowInfo
*window
)
759 const char* autoloadName
= GetRCFileName(AUTOLOAD_NM
);
760 static int initFileLoaded
= False
;
762 /* GetRCFileName() might return NULL if an error occurs during
763 creation of the preference file directory. */
764 if (autoloadName
!= NULL
&& !initFileLoaded
)
766 ReadMacroFile(window
, autoloadName
, False
);
767 initFileLoaded
= True
;
772 ** Read an NEdit macro file. Extends the syntax of the macro parser with
773 ** define keyword, and allows intermixing of defines with immediate actions.
775 int ReadMacroFile(WindowInfo
*window
, const char *fileName
, int warnNotExist
)
780 fileString
= ReadAnyTextFile(fileName
);
781 if (fileString
== NULL
){
782 if (errno
!= ENOENT
|| warnNotExist
)
784 DialogF(DF_ERR
, window
->shell
, 1, "Read Macro",
785 "Error reading macro file %s: %s", "OK", fileName
,
787 strerror(errno
, vaxc$errno
));
795 /* Parse fileString */
796 result
= readCheckMacroString(window
->shell
, fileString
, window
, fileName
,
803 ** Parse and execute a macro string including macro definitions. Report
804 ** parsing errors in a dialog posted over window->shell.
806 int ReadMacroString(WindowInfo
*window
, char *string
, const char *errIn
)
808 return readCheckMacroString(window
->shell
, string
, window
, errIn
, NULL
);
812 ** Check a macro string containing definitions for errors. Returns True
813 ** if macro compiled successfully. Returns False and puts up
814 ** a dialog explaining if macro did not compile successfully.
816 int CheckMacroString(Widget dialogParent
, char *string
, const char *errIn
,
819 return readCheckMacroString(dialogParent
, string
, NULL
, errIn
, errPos
);
823 ** Parse and optionally execute a macro string including macro definitions.
824 ** Report parsing errors in a dialog posted over dialogParent, using the
825 ** string errIn to identify the entity being parsed (filename, macro string,
826 ** etc.). If runWindow is specified, runs the macro against the window. If
827 ** runWindow is passed as NULL, does parse only. If errPos is non-null,
828 ** returns a pointer to the error location in the string.
830 static int readCheckMacroString(Widget dialogParent
, char *string
,
831 WindowInfo
*runWindow
, const char *errIn
, char **errPos
)
833 char *stoppedAt
, *inPtr
, *namePtr
, *errMsg
;
834 char subrName
[MAX_SYM_LEN
];
840 while (*inPtr
!= '\0') {
842 /* skip over white space and comments */
843 while (*inPtr
==' ' || *inPtr
=='\t' || *inPtr
=='\n'|| *inPtr
=='#') {
845 while (*inPtr
!= '\n' && *inPtr
!= '\0') inPtr
++;
852 /* look for define keyword, and compile and store defined routines */
853 if (!strncmp(inPtr
, "define", 6) && (inPtr
[6]==' ' || inPtr
[6]=='\t')) {
855 inPtr
+= strspn(inPtr
, " \t\n");
857 while (isalnum((unsigned char)*inPtr
) || *inPtr
== '_')
858 *namePtr
++ = *inPtr
++;
860 inPtr
+= strspn(inPtr
, " \t\n");
862 if (errPos
!= NULL
) *errPos
= stoppedAt
;
863 return ParseError(dialogParent
, string
, inPtr
,
864 errIn
, "expected '{'");
866 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
868 if (errPos
!= NULL
) *errPos
= stoppedAt
;
869 return ParseError(dialogParent
, string
, stoppedAt
,
872 if (runWindow
!= NULL
) {
873 sym
= LookupSymbol(subrName
);
875 subrPtr
.val
.prog
= prog
;
876 subrPtr
.tag
= NO_TAG
;
877 sym
= InstallSymbol(subrName
, MACRO_FUNCTION_SYM
, subrPtr
);
879 if (sym
->type
== MACRO_FUNCTION_SYM
)
880 FreeProgram(sym
->value
.val
.prog
);
882 sym
->type
= MACRO_FUNCTION_SYM
;
883 sym
->value
.val
.prog
= prog
;
888 /* Parse and execute immediate (outside of any define) macro commands
889 and WAIT for them to finish executing before proceeding. Note that
890 the code below is not perfect. If you interleave code blocks with
891 definitions in a file which is loaded from another macro file, it
892 will probably run the code blocks in reverse order! */
894 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
896 if (errPos
!= NULL
) *errPos
= stoppedAt
;
897 return ParseError(dialogParent
, string
, stoppedAt
,
900 if (runWindow
!= NULL
) {
902 if (runWindow
->macroCmdData
== NULL
) {
903 runMacro(runWindow
, prog
);
904 while (runWindow
->macroCmdData
!= NULL
) {
905 XtAppNextEvent(XtWidgetToApplicationContext(
906 runWindow
->shell
), &nextEvent
);
907 ServerDispatchEvent(&nextEvent
);
910 RunMacroAsSubrCall(prog
);
919 ** Run a pre-compiled macro, changing the interface state to reflect that
920 ** a macro is running, and handling preemption, resumption, and cancellation.
921 ** frees prog when macro execution is complete;
923 static void runMacro(WindowInfo
*window
, Program
*prog
)
928 macroCmdInfo
*cmdData
;
931 /* If a macro is already running, just call the program as a subroutine,
932 instead of starting a new one, so we don't have to keep a separate
933 context, and the macros will serialize themselves automatically */
934 if (window
->macroCmdData
!= NULL
) {
935 RunMacroAsSubrCall(prog
);
939 /* put up a watch cursor over the waiting window */
940 BeginWait(window
->shell
);
942 /* enable the cancel menu item */
943 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
944 s
=XmStringCreateSimple("Cancel Macro"), NULL
);
946 SetSensitive(window
, window
->cancelMacroItem
, True
);
948 /* Create a data structure for passing macro execution information around
949 amongst the callback routines which will process i/o and completion */
950 cmdData
= (macroCmdInfo
*)XtMalloc(sizeof(macroCmdInfo
));
951 window
->macroCmdData
= cmdData
;
952 cmdData
->bannerIsUp
= False
;
953 cmdData
->closeOnCompletion
= False
;
954 cmdData
->program
= prog
;
955 cmdData
->context
= NULL
;
956 cmdData
->continueWorkProcID
= 0;
957 cmdData
->dialog
= NULL
;
959 /* Set up timer proc for putting up banner when macro takes too long */
960 cmdData
->bannerTimeoutID
= XtAppAddTimeOut(
961 XtWidgetToApplicationContext(window
->shell
), BANNER_WAIT_TIME
,
962 bannerTimeoutProc
, window
);
964 /* Begin macro execution */
965 stat
= ExecuteMacro(window
, prog
, 0, NULL
, &result
, &cmdData
->context
,
968 if (stat
== MACRO_ERROR
)
970 finishMacroCmdExecution(window
);
971 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
972 "Error executing macro: %s", "OK", errMsg
);
976 if (stat
== MACRO_DONE
) {
977 finishMacroCmdExecution(window
);
980 if (stat
== MACRO_TIME_LIMIT
) {
981 ResumeMacroExecution(window
);
984 /* (stat == MACRO_PREEMPT) Macro was preempted */
988 ** Continue with macro execution after preemption. Called by the routines
989 ** whose actions cause preemption when they have completed their lengthy tasks.
990 ** Re-establishes macro execution work proc. Window must be the window in
991 ** which the macro is executing (the window to which macroCmdData is attached),
992 ** and not the window to which operations are focused.
994 void ResumeMacroExecution(WindowInfo
*window
)
996 macroCmdInfo
*cmdData
= (macroCmdInfo
*)window
->macroCmdData
;
999 cmdData
->continueWorkProcID
= XtAppAddWorkProc(
1000 XtWidgetToApplicationContext(window
->shell
),
1001 continueWorkProc
, window
);
1005 ** Cancel the macro command in progress (user cancellation via GUI)
1007 void AbortMacroCommand(WindowInfo
*window
)
1009 if (window
->macroCmdData
== NULL
)
1012 /* If there's both a macro and a shell command executing, the shell command
1013 must have been called from the macro. When called from a macro, shell
1014 commands don't put up cancellation controls of their own, but rely
1015 instead on the macro cancellation mechanism (here) */
1017 if (window
->shellCmdData
!= NULL
)
1018 AbortShellCommand(window
);
1021 /* Free the continuation */
1022 FreeRestartData(((macroCmdInfo
*)window
->macroCmdData
)->context
);
1024 /* Kill the macro command */
1025 finishMacroCmdExecution(window
);
1029 ** Call this before closing a window, to clean up macro references to the
1030 ** window, stop any macro which might be running from it, free associated
1031 ** memory, and check that a macro is not attempting to close the window from
1032 ** which it is run. If this is being called from a macro, and the window
1033 ** this routine is examining is the window from which the macro was run, this
1034 ** routine will return False, and the caller must NOT CLOSE THE WINDOW.
1035 ** Instead, empty it and make it Untitled, and let the macro completion
1036 ** process close the window when the macro is finished executing.
1038 int MacroWindowCloseActions(WindowInfo
*window
)
1040 macroCmdInfo
*mcd
, *cmdData
= window
->macroCmdData
;
1043 if (MacroRecordActionHook
!= 0 && MacroRecordWindow
== window
) {
1047 /* If no macro is executing in the window, allow the close, but check
1048 if macros executing in other windows have it as focus. If so, set
1049 their focus back to the window from which they were originally run */
1050 if (cmdData
== NULL
) {
1051 for (w
=WindowList
; w
!=NULL
; w
=w
->next
) {
1052 mcd
= (macroCmdInfo
*)w
->macroCmdData
;
1053 if (w
== MacroRunWindow() && MacroFocusWindow() == window
)
1054 SetMacroFocusWindow(MacroRunWindow());
1055 else if (mcd
!= NULL
&& mcd
->context
->focusWindow
== window
)
1056 mcd
->context
->focusWindow
= mcd
->context
->runWindow
;
1061 /* If the macro currently running (and therefore calling us, because
1062 execution must otherwise return to the main loop to execute any
1063 commands), is running in this window, tell the caller not to close,
1064 and schedule window close on completion of macro */
1065 if (window
== MacroRunWindow()) {
1066 cmdData
->closeOnCompletion
= True
;
1070 /* Free the continuation */
1071 FreeRestartData(cmdData
->context
);
1073 /* Kill the macro command */
1074 finishMacroCmdExecution(window
);
1079 ** Clean up after the execution of a macro command: free memory, and restore
1080 ** the user interface state.
1082 static void finishMacroCmdExecution(WindowInfo
*window
)
1084 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1085 int closeOnCompletion
= cmdData
->closeOnCompletion
;
1087 XClientMessageEvent event
;
1089 /* Cancel pending timeout and work proc */
1090 if (cmdData
->bannerTimeoutID
!= 0)
1091 XtRemoveTimeOut(cmdData
->bannerTimeoutID
);
1092 if (cmdData
->continueWorkProcID
!= 0)
1093 XtRemoveWorkProc(cmdData
->continueWorkProcID
);
1095 /* Clean up waiting-for-macro-command-to-complete mode */
1096 EndWait(window
->shell
);
1097 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
1098 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
1100 SetSensitive(window
, window
->cancelMacroItem
, False
);
1101 if (cmdData
->bannerIsUp
)
1102 ClearModeMessage(window
);
1104 /* If a dialog was up, get rid of it */
1105 if (cmdData
->dialog
!= NULL
)
1106 XtDestroyWidget(XtParent(cmdData
->dialog
));
1108 /* Free execution information */
1109 FreeProgram(cmdData
->program
);
1110 XtFree((char *)cmdData
);
1111 window
->macroCmdData
= NULL
;
1113 /* If macro closed its own window, window was made empty and untitled,
1114 but close was deferred until completion. This is completion, so if
1115 the window is still empty, do the close */
1116 if (closeOnCompletion
&& !window
->filenameSet
&& !window
->fileChanged
) {
1117 CloseWindow(window
);
1121 /* If no other macros are executing, do garbage collection */
1124 /* In processing the .neditmacro file (and possibly elsewhere), there
1125 is an event loop which waits for macro completion. Send an event
1126 to wake up that loop, otherwise execution will stall until the user
1127 does something to the window. */
1128 if (!closeOnCompletion
) {
1130 event
.type
= ClientMessage
;
1131 XSendEvent(XtDisplay(window
->shell
), XtWindow(window
->shell
), False
,
1132 NoEventMask
, (XEvent
*)&event
);
1137 ** Do garbage collection of strings if there are no macros currently
1138 ** executing. NEdit's macro language GC strategy is to call this routine
1139 ** whenever a macro completes. If other macros are still running (preempted
1140 ** or waiting for a shell command or dialog), this does nothing and therefore
1141 ** defers GC to the completion of the last macro out.
1147 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
1148 if (win
->macroCmdData
!= NULL
|| InSmartIndentMacros(win
))
1150 GarbageCollectStrings();
1154 ** Executes macro string "macro" using the lastFocus pane in "window".
1155 ** Reports errors via a dialog posted over "window", integrating the name
1156 ** "errInName" into the message to help identify the source of the error.
1158 void DoMacro(WindowInfo
*window
, const char *macro
, const char *errInName
)
1161 char *errMsg
, *stoppedAt
, *tMacro
;
1164 /* Add a terminating newline (which command line users are likely to omit
1165 since they are typically invoking a single routine) */
1166 macroLen
= strlen(macro
);
1167 tMacro
= XtMalloc(strlen(macro
)+2);
1168 strncpy(tMacro
, macro
, macroLen
);
1169 tMacro
[macroLen
] = '\n';
1170 tMacro
[macroLen
+1] = '\0';
1172 /* Parse the macro and report errors if it fails */
1173 prog
= ParseMacro(tMacro
, &errMsg
, &stoppedAt
);
1175 ParseError(window
->shell
, tMacro
, stoppedAt
, errInName
, errMsg
);
1181 /* run the executable program (prog is freed upon completion) */
1182 runMacro(window
, prog
);
1186 ** Get the current Learn/Replay macro in text form. Returned string is a
1187 ** pointer to the stored macro and should not be freed by the caller (and
1188 ** will cease to exist when the next replay macro is installed)
1190 char *GetReplayMacro(void)
1196 ** Present the user a dialog for "Repeat" command
1198 void RepeatDialog(WindowInfo
*window
)
1200 Widget form
, selBox
, radioBox
, timesForm
;
1203 char *lastCmdLabel
, *parenChar
;
1207 if (LastCommand
== NULL
)
1209 DialogF(DF_WARN
, window
->shell
, 1, "Repeat Macro",
1210 "No previous commands or learn/\nreplay sequences to repeat",
1215 /* Remeber the last command, since the user is allowed to work in the
1216 window while the dialog is up */
1217 rd
= (repeatDialog
*)XtMalloc(sizeof(repeatDialog
));
1218 rd
->lastCommand
= XtNewString(LastCommand
);
1220 /* make a label for the Last command item of the dialog, which includes
1221 the last executed action name */
1222 parenChar
= strchr(LastCommand
, '(');
1223 if (parenChar
== NULL
)
1225 cmdNameLen
= parenChar
-LastCommand
;
1226 lastCmdLabel
= XtMalloc(16 + cmdNameLen
);
1227 strcpy(lastCmdLabel
, "Last Command (");
1228 strncpy(&lastCmdLabel
[14], LastCommand
, cmdNameLen
);
1229 strcpy(&lastCmdLabel
[14 + cmdNameLen
], ")");
1231 XtSetArg(selBoxArgs
[0], XmNautoUnmanage
, False
);
1232 selBox
= CreatePromptDialog(window
->shell
, "repeat", selBoxArgs
, 1);
1233 rd
->shell
= XtParent(selBox
);
1234 XtAddCallback(rd
->shell
, XmNdestroyCallback
, repeatDestroyCB
, rd
);
1235 XtAddCallback(selBox
, XmNokCallback
, repeatOKCB
, rd
);
1236 XtAddCallback(selBox
, XmNapplyCallback
, repeatApplyCB
, rd
);
1237 XtAddCallback(selBox
, XmNcancelCallback
, repeatCancelCB
, rd
);
1238 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_TEXT
));
1239 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_SELECTION_LABEL
));
1240 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_HELP_BUTTON
));
1241 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_APPLY_BUTTON
));
1242 XtVaSetValues(XtParent(selBox
), XmNtitle
, "Repeat Macro", NULL
);
1243 AddMotifCloseCallback(XtParent(selBox
), repeatCancelCB
, rd
);
1245 form
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, selBox
, NULL
);
1247 radioBox
= XtVaCreateManagedWidget("cmdSrc", xmRowColumnWidgetClass
, form
,
1248 XmNradioBehavior
, True
,
1249 XmNorientation
, XmHORIZONTAL
,
1250 XmNpacking
, XmPACK_TIGHT
,
1251 XmNtopAttachment
, XmATTACH_FORM
,
1252 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1253 rd
->lastCmdToggle
= XtVaCreateManagedWidget("lastCmdToggle",
1254 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1255 XmNlabelString
, s1
=XmStringCreateSimple(lastCmdLabel
),
1256 XmNmnemonic
, 'C', NULL
);
1258 XtFree(lastCmdLabel
);
1259 XtVaCreateManagedWidget("learnReplayToggle",
1260 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1262 s1
=XmStringCreateSimple("Learn/Replay"),
1264 XmNsensitive
, ReplayMacro
!= NULL
, NULL
);
1267 timesForm
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, form
,
1268 XmNtopAttachment
, XmATTACH_WIDGET
,
1269 XmNtopWidget
, radioBox
,
1271 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1272 radioBox
= XtVaCreateManagedWidget("method", xmRowColumnWidgetClass
,
1274 XmNradioBehavior
, True
,
1275 XmNorientation
, XmHORIZONTAL
,
1276 XmNpacking
, XmPACK_TIGHT
,
1277 XmNtopAttachment
, XmATTACH_FORM
,
1278 XmNbottomAttachment
, XmATTACH_FORM
,
1279 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1280 rd
->inSelToggle
= XtVaCreateManagedWidget("inSelToggle",
1281 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1282 XmNlabelString
, s1
=XmStringCreateSimple("In Selection"),
1283 XmNmnemonic
, 'I', NULL
);
1285 rd
->toEndToggle
= XtVaCreateManagedWidget("toEndToggle",
1286 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1287 XmNlabelString
, s1
=XmStringCreateSimple("To End"),
1288 XmNmnemonic
, 'T', NULL
);
1290 XtVaCreateManagedWidget("nTimesToggle",
1291 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1292 XmNlabelString
, s1
=XmStringCreateSimple("N Times"),
1294 XmNset
, True
, NULL
);
1296 rd
->repeatText
= XtVaCreateManagedWidget("repeatText", xmTextWidgetClass
,
1299 XmNtopAttachment
, XmATTACH_FORM
,
1300 XmNbottomAttachment
, XmATTACH_FORM
,
1301 XmNleftAttachment
, XmATTACH_WIDGET
,
1302 XmNleftWidget
, radioBox
, NULL
);
1303 RemapDeleteKey(rd
->repeatText
);
1305 /* Handle mnemonic selection of buttons and focus to dialog */
1306 AddDialogMnemonicHandler(form
, FALSE
);
1308 /* Set initial focus */
1309 #if XmVersion >= 1002
1310 XtVaSetValues(form
, XmNinitialFocus
, timesForm
, NULL
);
1311 XtVaSetValues(timesForm
, XmNinitialFocus
, rd
->repeatText
, NULL
);
1315 rd
->forWindow
= window
;
1316 ManageDialogCenteredOnPointer(selBox
);
1319 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1321 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1323 if (doRepeatDialogAction(rd
, ((XmAnyCallbackStruct
*)callData
)->event
))
1324 XtDestroyWidget(rd
->shell
);
1327 /* Note that the apply button is not managed in the repeat dialog. The dialog
1328 itself is capable of non-modal operation, but to be complete, it needs
1329 to dynamically update last command, dimming of learn/replay, possibly a
1330 stop button for the macro, and possibly in-selection with selection */
1331 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1333 doRepeatDialogAction((repeatDialog
*)clientData
,
1334 ((XmAnyCallbackStruct
*)callData
)->event
);
1337 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
)
1340 char nTimesStr
[TYPE_INT_STR_SIZE(int)];
1343 /* Find out from the dialog how to repeat the command */
1344 if (XmToggleButtonGetState(rd
->inSelToggle
))
1346 if (!rd
->forWindow
->buffer
->primary
.selected
)
1348 DialogF(DF_WARN
, rd
->shell
, 1, "Repeat Macro",
1349 "No selection in window to repeat within", "OK");
1350 XmProcessTraversal(rd
->inSelToggle
, XmTRAVERSE_CURRENT
);
1353 params
[0] = "in_selection";
1354 } else if (XmToggleButtonGetState(rd
->toEndToggle
))
1356 params
[0] = "to_end";
1359 if (GetIntTextWarn(rd
->repeatText
, &nTimes
, "number of times", True
)
1362 XmProcessTraversal(rd
->repeatText
, XmTRAVERSE_CURRENT
);
1365 sprintf(nTimesStr
, "%d", nTimes
);
1366 params
[0] = nTimesStr
;
1369 /* Figure out which command user wants to repeat */
1370 if (XmToggleButtonGetState(rd
->lastCmdToggle
))
1371 params
[1] = XtNewString(rd
->lastCommand
);
1373 if (ReplayMacro
== NULL
)
1375 params
[1] = XtNewString(ReplayMacro
);
1378 /* call the action routine repeat_macro to do the work */
1379 XtCallActionProc(rd
->forWindow
->lastFocus
, "repeat_macro", event
, params
,2);
1384 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1386 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1388 XtDestroyWidget(rd
->shell
);
1391 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1393 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1395 XtFree(rd
->lastCommand
);
1400 ** Dispatches a macro to which repeats macro command in "command", either
1401 ** an integer number of times ("how" == positive integer), or within a
1402 ** selected range ("how" == REPEAT_IN_SEL), or to the end of the window
1403 ** ("how == REPEAT_TO_END).
1405 ** Note that as with most macro routines, this returns BEFORE the macro is
1406 ** finished executing
1408 void RepeatMacro(WindowInfo
*window
, const char *command
, int how
)
1411 char *errMsg
, *stoppedAt
, *loopMacro
, *loopedCmd
;
1413 if (command
== NULL
)
1416 /* Wrap a for loop and counter/tests around the command */
1417 if (how
== REPEAT_TO_END
)
1418 loopMacro
= "lastCursor=-1\nstartPos=$cursor\n\
1419 while($cursor>=startPos&&$cursor!=lastCursor){\nlastCursor=$cursor\n%s\n}\n";
1420 else if (how
== REPEAT_IN_SEL
)
1421 loopMacro
= "selStart = $selection_start\nif (selStart == -1)\nreturn\n\
1422 selEnd = $selection_end\nset_cursor_pos(selStart)\nselect(0,0)\n\
1423 boundText = get_range(selEnd, selEnd+10)\n\
1424 while($cursor >= selStart && $cursor < selEnd && \\\n\
1425 get_range(selEnd, selEnd+10) == boundText) {\n\
1426 startLength = $text_length\n%s\n\
1427 selEnd += $text_length - startLength\n}\n";
1429 loopMacro
= "for(i=0;i<%d;i++){\n%s\n}\n";
1430 loopedCmd
= XtMalloc(strlen(command
) + strlen(loopMacro
) + 25);
1431 if (how
== REPEAT_TO_END
|| how
== REPEAT_IN_SEL
)
1432 sprintf(loopedCmd
, loopMacro
, command
);
1434 sprintf(loopedCmd
, loopMacro
, how
, command
);
1436 /* Parse the resulting macro into an executable program "prog" */
1437 prog
= ParseMacro(loopedCmd
, &errMsg
, &stoppedAt
);
1439 fprintf(stderr
, "NEdit internal error, repeat macro syntax wrong: %s\n",
1445 /* run the executable program */
1446 runMacro(window
, prog
);
1450 ** Macro recording action hook for Learn/Replay, added temporarily during
1453 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
1454 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1460 /* Select only actions in text panes in the window for which this
1461 action hook is recording macros (from clientData). */
1462 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1463 if (window
->textArea
== w
)
1465 for (i
=0; i
<window
->nPanes
; i
++) {
1466 if (window
->textPanes
[i
] == w
)
1469 if (i
< window
->nPanes
)
1472 if (window
== NULL
|| window
!= (WindowInfo
*)clientData
)
1475 /* beep on un-recordable operations which require a mouse position, to
1476 remind the user that the action was not recorded */
1477 if (isMouseAction(actionName
)) {
1478 XBell(XtDisplay(w
), 0);
1482 /* Record the action and its parameters */
1483 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1484 if (actionString
!= NULL
) {
1485 BufInsert(MacroRecordBuf
, MacroRecordBuf
->length
, actionString
);
1486 XtFree(actionString
);
1491 ** Permanent action hook for remembering last action for possible replay
1493 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
1494 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1500 /* Find the window to which this action belongs */
1501 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1502 if (window
->textArea
== w
)
1504 for (i
=0; i
<window
->nPanes
; i
++) {
1505 if (window
->textPanes
[i
] == w
)
1508 if (i
< window
->nPanes
)
1514 /* The last action is recorded for the benefit of repeating the last
1515 action. Don't record repeat_macro and wipe out the real action */
1516 if (!strcmp(actionName
, "repeat_macro"))
1519 /* Record the action and its parameters */
1520 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1521 if (actionString
!= NULL
) {
1522 if (LastCommand
!= NULL
)
1523 XtFree(LastCommand
);
1524 LastCommand
= actionString
;
1529 ** Create a macro string to represent an invocation of an action routine.
1530 ** Returns NULL for non-operational or un-recordable actions.
1532 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
1533 String
*params
, Cardinal numParams
)
1535 char chars
[20], *charList
[1], *outStr
, *outPtr
;
1537 int i
, nChars
, nParams
, length
, nameLength
;
1542 if (isIgnoredAction(actionName
) || isRedundantAction(actionName
) ||
1543 isMouseAction(actionName
))
1546 /* Convert self_insert actions, to insert_string */
1547 if (!strcmp(actionName
, "self_insert") ||
1548 !strcmp(actionName
, "self-insert")) {
1549 actionName
= "insert_string";
1551 nChars
= XLookupString((XKeyEvent
*)event
, chars
, 19, &keysym
, NULL
);
1556 nChars
= XmImMbLookupString(w
, (XKeyEvent
*)event
,
1557 chars
, 19, &keysym
, &status
);
1558 if (nChars
== 0 || status
== XLookupNone
||
1559 status
== XLookupKeySym
|| status
== XBufferOverflow
)
1562 chars
[nChars
] = '\0';
1563 charList
[0] = chars
;
1567 nParams
= numParams
;
1569 /* Figure out the length of string required */
1570 nameLength
= strlen(actionName
);
1571 length
= nameLength
+ 3;
1572 for (i
=0; i
<nParams
; i
++)
1573 length
+= escapedStringLength(params
[i
]) + 4;
1575 /* Allocate the string and copy the information to it */
1576 outPtr
= outStr
= XtMalloc(length
+ 1);
1577 strcpy(outPtr
, actionName
);
1578 outPtr
+= nameLength
;
1580 for (i
=0; i
<nParams
; i
++) {
1582 outPtr
+= escapeStringChars(params
[i
], outPtr
);
1583 *outPtr
++ = '\"'; *outPtr
++ = ','; *outPtr
++ = ' ';
1587 *outPtr
++ = ')'; *outPtr
++ = '\n'; *outPtr
++ = '\0';
1591 static int isMouseAction(const char *action
)
1595 for (i
=0; i
<(int)XtNumber(MouseActions
); i
++)
1596 if (!strcmp(action
, MouseActions
[i
]))
1601 static int isRedundantAction(const char *action
)
1605 for (i
=0; i
<(int)XtNumber(RedundantActions
); i
++)
1606 if (!strcmp(action
, RedundantActions
[i
]))
1611 static int isIgnoredAction(const char *action
)
1615 for (i
=0; i
<(int)XtNumber(IgnoredActions
); i
++)
1616 if (!strcmp(action
, IgnoredActions
[i
]))
1622 ** Timer proc for putting up the "Macro Command in Progress" banner if
1623 ** the process is taking too long.
1625 #define MAX_TIMEOUT_MSG_LEN (MAX_ACCEL_LEN + 60)
1626 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
)
1628 WindowInfo
*window
= (WindowInfo
*)clientData
;
1629 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1632 char message
[MAX_TIMEOUT_MSG_LEN
];
1634 cmdData
->bannerIsUp
= True
;
1636 /* Extract accelerator text from menu PushButtons */
1637 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
1639 /* Translate Motif string to char* */
1640 cCancel
= GetXmStringText(xmCancel
);
1642 /* Free Motif String */
1643 XmStringFree(xmCancel
);
1645 /* Create message */
1646 if (cCancel
[0] == '\0') {
1647 strncpy(message
, "Macro Command in Progress", MAX_TIMEOUT_MSG_LEN
);
1648 message
[MAX_TIMEOUT_MSG_LEN
- 1] = '\0';
1652 "Macro Command in Progress -- Press %s to Cancel",
1659 SetModeMessage(window
, message
);
1660 cmdData
->bannerTimeoutID
= 0;
1664 ** Work proc for continuing execution of a preempted macro.
1666 ** Xt WorkProcs are designed to run first-in first-out, which makes them
1667 ** very bad at sharing time between competing tasks. For this reason, it's
1668 ** usually bad to use work procs anywhere where their execution is likely to
1669 ** overlap. Using a work proc instead of a timer proc (which I usually
1670 ** prefer) here means macros will probably share time badly, but we're more
1671 ** interested in making the macros cancelable, and in continuing other work
1672 ** than having users run a bunch of them at once together.
1674 static Boolean
continueWorkProc(XtPointer clientData
)
1676 WindowInfo
*window
= (WindowInfo
*)clientData
;
1677 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1682 stat
= ContinueMacro(cmdData
->context
, &result
, &errMsg
);
1683 if (stat
== MACRO_ERROR
)
1685 finishMacroCmdExecution(window
);
1686 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
1687 "Error executing macro: %s", "OK", errMsg
);
1689 } else if (stat
== MACRO_DONE
)
1691 finishMacroCmdExecution(window
);
1693 } else if (stat
== MACRO_PREEMPT
)
1695 cmdData
->continueWorkProcID
= 0;
1699 /* Macro exceeded time slice, re-schedule it */
1700 if (stat
!= MACRO_TIME_LIMIT
)
1701 return True
; /* shouldn't happen */
1706 ** Copy fromString to toString replacing special characters in strings, such
1707 ** that they can be read back by the macro parser's string reader. i.e. double
1708 ** quotes are replaced by \", backslashes are replaced with \\, C-std control
1709 ** characters like \n are replaced with their backslash counterparts. This
1710 ** routine should be kept reasonably in sync with yylex in parse.y. Companion
1711 ** routine escapedStringLength predicts the length needed to write the string
1712 ** when it is expanded with the additional characters. Returns the number
1713 ** of characters to which the string expanded.
1715 static int escapeStringChars(char *fromString
, char *toString
)
1717 char *e
, *c
, *outPtr
= toString
;
1719 /* substitute escape sequences */
1720 for (c
=fromString
; *c
!='\0'; c
++) {
1721 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1724 *outPtr
++ = ReplaceChars
[e
-EscapeChars
];
1732 return outPtr
- toString
;
1736 ** Predict the length of a string needed to hold a copy of "string" with
1737 ** special characters replaced with escape sequences by escapeStringChars.
1739 static int escapedStringLength(char *string
)
1744 /* calculate length and allocate returned string */
1745 for (c
=string
; *c
!='\0'; c
++) {
1746 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1758 ** Built-in macro subroutine for getting the length of a string
1760 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1761 DataValue
*result
, char **errMsg
)
1763 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
1766 return wrongNArgsErr(errMsg
);
1767 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1769 result
->tag
= INT_TAG
;
1770 result
->val
.n
= strlen(string
);
1775 ** Built-in macro subroutines for min and max
1777 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1778 DataValue
*result
, char **errMsg
)
1780 int minVal
, value
, i
;
1783 return tooFewArgsErr(errMsg
);
1784 if (!readIntArg(argList
[0], &minVal
, errMsg
))
1786 for (i
=0; i
<nArgs
; i
++) {
1787 if (!readIntArg(argList
[i
], &value
, errMsg
))
1789 minVal
= value
< minVal
? value
: minVal
;
1791 result
->tag
= INT_TAG
;
1792 result
->val
.n
= minVal
;
1795 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1796 DataValue
*result
, char **errMsg
)
1798 int maxVal
, value
, i
;
1801 return tooFewArgsErr(errMsg
);
1802 if (!readIntArg(argList
[0], &maxVal
, errMsg
))
1804 for (i
=0; i
<nArgs
; i
++) {
1805 if (!readIntArg(argList
[i
], &value
, errMsg
))
1807 maxVal
= value
> maxVal
? value
: maxVal
;
1809 result
->tag
= INT_TAG
;
1810 result
->val
.n
= maxVal
;
1814 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1815 DataValue
*result
, char **errMsg
)
1817 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1819 char fullname
[MAXPATHLEN
];
1820 char normalizedString
[MAXPATHLEN
];
1822 /* Read the argument representing the window to focus to, and translate
1823 it into a pointer to a real WindowInfo */
1825 return wrongNArgsErr(errMsg
);
1827 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
1829 } else if (!strcmp(string
, "last")) {
1831 } else if (!strcmp(string
, "next")) {
1833 } else if (strlen(string
) >= MAXPATHLEN
) {
1834 *errMsg
= "Pathname too long in focus_window()";
1837 /* just use the plain name as supplied */
1838 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1839 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1840 if (!strcmp(string
, fullname
)) {
1844 /* didn't work? try normalizing the string passed in */
1846 strncpy(normalizedString
, string
, MAXPATHLEN
);
1847 normalizedString
[MAXPATHLEN
-1] = '\0';
1848 if (1 == NormalizePathname(normalizedString
)) {
1849 /* Something is broken with the input pathname. */
1850 *errMsg
= "Pathname too long in focus_window()";
1853 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1854 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1855 if (!strcmp(normalizedString
, fullname
))
1861 /* If no matching window was found, return empty string and do nothing */
1863 result
->tag
= STRING_TAG
;
1864 result
->val
.str
.rep
= PERM_ALLOC_STR("");
1865 result
->val
.str
.len
= 0;
1869 /* Change the focused window to the requested one */
1870 SetMacroFocusWindow(w
);
1872 /* turn on syntax highlight that might have been deferred */
1873 if (w
->highlightSyntax
&& w
->highlightData
==NULL
)
1874 StartHighlighting(w
, False
);
1876 /* Return the name of the window */
1877 result
->tag
= STRING_TAG
;
1878 AllocNString(&result
->val
.str
, strlen(w
->path
)+strlen(w
->filename
)+1);
1879 sprintf(result
->val
.str
.rep
, "%s%s", w
->path
, w
->filename
);
1884 ** Built-in macro subroutine for getting text from the current window's text
1887 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1888 DataValue
*result
, char **errMsg
)
1891 textBuffer
*buf
= window
->buffer
;
1894 /* Validate arguments and convert to int */
1896 return wrongNArgsErr(errMsg
);
1897 if (!readIntArg(argList
[0], &from
, errMsg
))
1899 if (!readIntArg(argList
[1], &to
, errMsg
))
1901 if (from
< 0) from
= 0;
1902 if (from
> buf
->length
) from
= buf
->length
;
1904 if (to
> buf
->length
) to
= buf
->length
;
1905 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1907 /* Copy text from buffer (this extra copy could be avoided if textBuf.c
1908 provided a routine for writing into a pre-allocated string) */
1909 result
->tag
= STRING_TAG
;
1910 AllocNString(&result
->val
.str
, to
- from
+ 1);
1911 rangeText
= BufGetRange(buf
, from
, to
);
1912 BufUnsubstituteNullChars(rangeText
, buf
);
1913 strcpy(result
->val
.str
.rep
, rangeText
);
1914 /* Note: after the un-substitution, it is possible that strlen() != len,
1915 but that's because strlen() can't deal with 0-characters. */
1921 ** Built-in macro subroutine for getting a single character at the position
1922 ** given, from the current window
1924 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1925 DataValue
*result
, char **errMsg
)
1928 textBuffer
*buf
= window
->buffer
;
1930 /* Validate argument and convert it to int */
1932 return wrongNArgsErr(errMsg
);
1933 if (!readIntArg(argList
[0], &pos
, errMsg
))
1935 if (pos
< 0) pos
= 0;
1936 if (pos
> buf
->length
) pos
= buf
->length
;
1938 /* Return the character in a pre-allocated string) */
1939 result
->tag
= STRING_TAG
;
1940 AllocNString(&result
->val
.str
, 2);
1941 result
->val
.str
.rep
[0] = BufGetCharacter(buf
, pos
);
1942 BufUnsubstituteNullChars(result
->val
.str
.rep
, buf
);
1943 /* Note: after the un-substitution, it is possible that strlen() != len,
1944 but that's because strlen() can't deal with 0-characters. */
1949 ** Built-in macro subroutine for replacing text in the current window's text
1952 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1953 DataValue
*result
, char **errMsg
)
1956 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1957 textBuffer
*buf
= window
->buffer
;
1959 /* Validate arguments and convert to int */
1961 return wrongNArgsErr(errMsg
);
1962 if (!readIntArg(argList
[0], &from
, errMsg
))
1964 if (!readIntArg(argList
[1], &to
, errMsg
))
1966 if (!readStringArg(argList
[2], &string
, stringStorage
, errMsg
))
1968 if (from
< 0) from
= 0;
1969 if (from
> buf
->length
) from
= buf
->length
;
1971 if (to
> buf
->length
) to
= buf
->length
;
1972 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1974 /* Don't allow modifications if the window is read-only */
1975 if (IS_ANY_LOCKED(window
->lockReasons
)) {
1976 XBell(XtDisplay(window
->shell
), 0);
1977 result
->tag
= NO_TAG
;
1981 /* There are no null characters in the string (because macro strings
1982 still have null termination), but if the string contains the
1983 character used by the buffer for null substitution, it could
1984 theoretically become a null. In the highly unlikely event that
1985 all of the possible substitution characters in the buffer are used
1986 up, stop the macro and tell the user of the failure */
1987 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
1988 *errMsg
= "Too much binary data in file";
1992 /* Do the replace */
1993 BufReplace(buf
, from
, to
, string
);
1994 result
->tag
= NO_TAG
;
1999 ** Built-in macro subroutine for replacing the primary-selection selected
2000 ** text in the current window's text buffer
2002 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2003 DataValue
*result
, char **errMsg
)
2005 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2007 /* Validate argument and convert to string */
2009 return wrongNArgsErr(errMsg
);
2010 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2013 /* Don't allow modifications if the window is read-only */
2014 if (IS_ANY_LOCKED(window
->lockReasons
)) {
2015 XBell(XtDisplay(window
->shell
), 0);
2016 result
->tag
= NO_TAG
;
2020 /* There are no null characters in the string (because macro strings
2021 still have null termination), but if the string contains the
2022 character used by the buffer for null substitution, it could
2023 theoretically become a null. In the highly unlikely event that
2024 all of the possible substitution characters in the buffer are used
2025 up, stop the macro and tell the user of the failure */
2026 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
2027 *errMsg
= "Too much binary data in file";
2031 /* Do the replace */
2032 BufReplaceSelected(window
->buffer
, string
);
2033 result
->tag
= NO_TAG
;
2038 ** Built-in macro subroutine for getting the text currently selected by
2039 ** the primary selection in the current window's text buffer, or in any
2040 ** part of screen if "any" argument is given
2042 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2043 DataValue
*result
, char **errMsg
)
2047 /* Read argument list to check for "any" keyword, and get the appropriate
2049 if (nArgs
!= 0 && nArgs
!= 1)
2050 return wrongNArgsErr(errMsg
);
2052 if (argList
[0].tag
!= STRING_TAG
|| strcmp(argList
[0].val
.str
.rep
, "any")) {
2053 *errMsg
= "Unrecognized argument to %s";
2056 selText
= GetAnySelection(window
);
2057 if (selText
== NULL
)
2058 selText
= XtNewString("");
2060 selText
= BufGetSelectionText(window
->buffer
);
2061 BufUnsubstituteNullChars(selText
, window
->buffer
);
2064 /* Return the text as an allocated string */
2065 result
->tag
= STRING_TAG
;
2066 AllocNStringCpy(&result
->val
.str
, selText
);
2072 ** Built-in macro subroutine for determining if implicit conversion of
2073 ** a string to number will succeed or fail
2075 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2076 DataValue
*result
, char **errMsg
)
2078 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2081 return wrongNArgsErr(errMsg
);
2083 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
2087 result
->tag
= INT_TAG
;
2088 result
->val
.n
= StringToNum(string
, NULL
);
2094 ** Built-in macro subroutine for replacing a substring within another string
2096 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2097 DataValue
*result
, char **errMsg
)
2099 int from
, to
, length
, replaceLen
, outLen
;
2100 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *replStr
;
2102 /* Validate arguments and convert to int */
2104 return wrongNArgsErr(errMsg
);
2105 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2107 if (!readIntArg(argList
[1], &from
, errMsg
))
2109 if (!readIntArg(argList
[2], &to
, errMsg
))
2111 if (!readStringArg(argList
[3], &replStr
, stringStorage
[1], errMsg
))
2113 length
= strlen(string
);
2114 if (from
< 0) from
= 0;
2115 if (from
> length
) from
= length
;
2117 if (to
> length
) to
= length
;
2118 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2120 /* Allocate a new string and do the replacement */
2121 replaceLen
= strlen(replStr
);
2122 outLen
= length
- (to
- from
) + replaceLen
;
2123 result
->tag
= STRING_TAG
;
2124 AllocNString(&result
->val
.str
, outLen
+1);
2125 strncpy(result
->val
.str
.rep
, string
, from
);
2126 strncpy(&result
->val
.str
.rep
[from
], replStr
, replaceLen
);
2127 strncpy(&result
->val
.str
.rep
[from
+ replaceLen
], &string
[to
], length
- to
);
2132 ** Built-in macro subroutine for getting a substring of a string.
2133 ** Called as substring(string, from [, to])
2135 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2136 DataValue
*result
, char **errMsg
)
2138 int from
, to
, length
;
2139 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2141 /* Validate arguments and convert to int */
2142 if (nArgs
!= 2 && nArgs
!= 3)
2143 return wrongNArgsErr(errMsg
);
2144 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2146 if (!readIntArg(argList
[1], &from
, errMsg
))
2148 length
= to
= strlen(string
);
2150 if (!readIntArg(argList
[2], &to
, errMsg
))
2152 if (from
< 0) from
+= length
;
2153 if (from
< 0) from
= 0;
2154 if (from
> length
) from
= length
;
2155 if (to
< 0) to
+= length
;
2157 if (to
> length
) to
= length
;
2158 if (from
> to
) to
= from
;
2160 /* Allocate a new string and copy the sub-string into it */
2161 result
->tag
= STRING_TAG
;
2162 AllocNStringNCpy(&result
->val
.str
, &string
[from
], to
- from
);
2166 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2167 DataValue
*result
, char **errMsg
)
2170 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2172 /* Validate arguments and convert to int */
2174 return wrongNArgsErr(errMsg
);
2175 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2177 length
= strlen(string
);
2179 /* Allocate a new string and copy an uppercased version of the string it */
2180 result
->tag
= STRING_TAG
;
2181 AllocNString(&result
->val
.str
, length
+ 1);
2182 for (i
=0; i
<length
; i
++)
2183 result
->val
.str
.rep
[i
] = toupper((unsigned char)string
[i
]);
2187 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2188 DataValue
*result
, char **errMsg
)
2191 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2193 /* Validate arguments and convert to int */
2195 return wrongNArgsErr(errMsg
);
2196 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2198 length
= strlen(string
);
2200 /* Allocate a new string and copy an lowercased version of the string it */
2201 result
->tag
= STRING_TAG
;
2202 AllocNString(&result
->val
.str
, length
+ 1);
2203 for (i
=0; i
<length
; i
++)
2204 result
->val
.str
.rep
[i
] = tolower((unsigned char)string
[i
]);
2208 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2209 DataValue
*result
, char **errMsg
)
2214 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2216 /* Get the string argument */
2218 return wrongNArgsErr(errMsg
);
2219 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2222 /* Use the XmClipboard routines to copy the text to the clipboard.
2223 If errors occur, just give up. */
2224 result
->tag
= NO_TAG
;
2225 stat
= XmClipboardStartCopy(TheDisplay
, XtWindow(window
->textArea
),
2226 s
=XmStringCreateSimple("NEdit"), XtLastTimestampProcessed(TheDisplay
),
2227 window
->textArea
, NULL
, &itemID
);
2229 if (stat
!= ClipboardSuccess
)
2231 if (XmClipboardCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
, "STRING",
2232 string
, strlen(string
), 0, NULL
) != ClipboardSuccess
)
2234 XmClipboardEndCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
);
2238 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2239 DataValue
*result
, char **errMsg
)
2241 unsigned long length
, retLength
;
2244 /* Should have no arguments */
2246 return wrongNArgsErr(errMsg
);
2248 /* Ask if there's a string in the clipboard, and get its length */
2249 if (XmClipboardInquireLength(TheDisplay
, XtWindow(window
->shell
), "STRING",
2250 &length
) != ClipboardSuccess
) {
2251 result
->tag
= STRING_TAG
;
2252 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2253 result
->val
.str
.len
= 0;
2257 /* Allocate a new string to hold the data */
2258 result
->tag
= STRING_TAG
;
2259 AllocNString(&result
->val
.str
, (int)length
+ 1);
2261 /* Copy the clipboard contents to the string */
2262 if (XmClipboardRetrieve(TheDisplay
, XtWindow(window
->shell
), "STRING",
2263 result
->val
.str
.rep
, length
, &retLength
, &id
) != ClipboardSuccess
)
2265 result
->val
.str
.rep
[retLength
] = '\0';
2266 result
->val
.str
.len
= retLength
;
2273 ** Built-in macro subroutine for reading the contents of a text file into
2274 ** a string. On success, returns 1 in $readStatus, and the contents of the
2275 ** file as a string in the subroutine return value. On failure, returns
2276 ** the empty string "" and an 0 $readStatus.
2278 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2279 DataValue
*result
, char **errMsg
)
2281 char stringStorage
[TYPE_INT_STR_SIZE(int)], *name
;
2282 struct stat statbuf
;
2286 /* Validate arguments and convert to int */
2288 return wrongNArgsErr(errMsg
);
2289 if (!readStringArg(argList
[0], &name
, stringStorage
, errMsg
))
2292 /* Read the whole file into an allocated string */
2293 if ((fp
= fopen(name
, "r")) == NULL
)
2295 if (fstat(fileno(fp
), &statbuf
) != 0)
2297 result
->tag
= STRING_TAG
;
2298 AllocNString(&result
->val
.str
, statbuf
.st_size
+1);
2299 readLen
= fread(result
->val
.str
.rep
, sizeof(char), statbuf
.st_size
+1, fp
);
2303 /* Couldn't trust file size. Use slower but more general method */
2304 int chunkSize
= 1024;
2307 buffer
= XtMalloc(readLen
* sizeof(char));
2308 memcpy(buffer
, result
->val
.str
.rep
, readLen
* sizeof(char));
2310 buffer
= XtRealloc(buffer
, (readLen
+chunkSize
)*sizeof(char));
2311 readLen
+= fread(&buffer
[readLen
], sizeof(char), chunkSize
, fp
);
2317 AllocNString(&result
->val
.str
, readLen
+ 1);
2318 memcpy(result
->val
.str
.rep
, buffer
, readLen
* sizeof(char));
2323 /* Return the results */
2324 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2325 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= True
;
2332 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2333 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= False
;
2334 result
->tag
= STRING_TAG
;
2335 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2336 result
->val
.str
.len
= 0;
2341 ** Built-in macro subroutines for writing or appending a string (parameter $1)
2342 ** to a file named in parameter $2. Returns 1 on successful write, or 0 if
2345 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2346 DataValue
*result
, char **errMsg
)
2348 return writeOrAppendFile(False
, window
, argList
, nArgs
, result
, errMsg
);
2351 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2352 DataValue
*result
, char **errMsg
)
2354 return writeOrAppendFile(True
, window
, argList
, nArgs
, result
, errMsg
);
2357 static int writeOrAppendFile(int append
, WindowInfo
*window
,
2358 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
)
2360 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *name
, *string
;
2363 /* Validate argument */
2365 return wrongNArgsErr(errMsg
);
2366 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2368 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
))
2372 if ((fp
= fopen(name
, append
? "a" : "w")) == NULL
) {
2373 result
->tag
= INT_TAG
;
2374 result
->val
.n
= False
;
2378 /* write the string to the file */
2379 fwrite(string
, sizeof(char), strlen(string
), fp
);
2382 result
->tag
= INT_TAG
;
2383 result
->val
.n
= False
;
2388 /* return the status */
2389 result
->tag
= INT_TAG
;
2390 result
->val
.n
= True
;
2395 ** Built-in macro subroutine for searching silently in a window without
2396 ** dialogs, beeps, or changes to the selection. Arguments are: $1: string to
2397 ** search for, $2: starting position. Optional arguments may include the
2398 ** strings: "wrap" to make the search wrap around the beginning or end of the
2399 ** string, "backward" or "forward" to change the search direction ("forward" is
2400 ** the default), "literal", "case" or "regex" to change the search type
2401 ** (default is "literal").
2403 ** Returns the starting position of the match, or -1 if nothing matched.
2404 ** also returns the ending position of the match in $searchEndPos
2406 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2407 DataValue
*result
, char **errMsg
)
2409 DataValue newArgList
[9];
2412 /* Use the search string routine, by adding the buffer contents as
2413 the string argument */
2415 return wrongNArgsErr(errMsg
);
2416 newArgList
[0].tag
= STRING_TAG
;
2417 newArgList
[0].val
.str
.rep
= BufGetAll(window
->buffer
);
2418 newArgList
[0].val
.str
.len
= window
->buffer
->length
;
2419 memcpy(&newArgList
[1], argList
, nArgs
* sizeof(DataValue
));
2420 retVal
= searchStringMS(window
, newArgList
, nArgs
+1, result
, errMsg
);
2421 XtFree(newArgList
[0].val
.str
.rep
);
2426 ** Built-in macro subroutine for searching a string. Arguments are $1:
2427 ** string to search in, $2: string to search for, $3: starting position.
2428 ** Optional arguments may include the strings: "wrap" to make the search
2429 ** wrap around the beginning or end of the string, "backward" or "forward"
2430 ** to change the search direction ("forward" is the default), "literal",
2431 ** "case" or "regex" to change the search type (default is "literal").
2433 ** Returns the starting position of the match, or -1 if nothing matched.
2434 ** also returns the ending position of the match in $searchEndPos
2436 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2437 DataValue
*result
, char **errMsg
)
2439 int beginPos
, wrap
, direction
, found
= False
, foundStart
, foundEnd
, type
;
2440 int skipSearch
= False
, len
;
2441 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
;
2443 /* Validate arguments and convert to proper types */
2445 return tooFewArgsErr(errMsg
);
2446 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2448 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2450 if (!readIntArg(argList
[2], &beginPos
, errMsg
))
2452 if (!readSearchArgs(&argList
[3], nArgs
-3, &direction
, &type
, &wrap
, errMsg
))
2455 len
= argList
[0].val
.str
.len
;
2456 if (beginPos
> len
) {
2457 if (direction
== SEARCH_FORWARD
) {
2459 beginPos
= 0; /* Wrap immediately */
2467 } else if (beginPos
< 0) {
2468 if (direction
== SEARCH_BACKWARD
) {
2470 beginPos
= len
; /* Wrap immediately */
2481 found
= SearchString(string
, searchStr
, direction
, type
, wrap
, beginPos
,
2482 &foundStart
, &foundEnd
, NULL
, NULL
, GetWindowDelimiters(window
));
2484 /* Return the results */
2485 ReturnGlobals
[SEARCH_END
]->value
.tag
= INT_TAG
;
2486 ReturnGlobals
[SEARCH_END
]->value
.val
.n
= found
? foundEnd
: 0;
2487 result
->tag
= INT_TAG
;
2488 result
->val
.n
= found
? foundStart
: -1;
2493 ** Built-in macro subroutine for replacing all occurences of a search string in
2494 ** a string with a replacement string. Arguments are $1: string to search in,
2495 ** $2: string to search for, $3: replacement string. Also takes an optional
2496 ** search type: one of "literal", "case" or "regex" (default is "literal"), and
2497 ** an optional "copy" argument.
2499 ** Returns a new string with all of the replacements done. If no replacements
2500 ** were performed and "copy" was specified, returns a copy of the original
2501 ** string. Otherwise returns an empty string ("").
2503 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2504 DataValue
*result
, char **errMsg
)
2506 char stringStorage
[3][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
, *replaceStr
;
2507 char *argStr
, *replacedStr
;
2508 int searchType
= SEARCH_LITERAL
, copyStart
, copyEnd
;
2509 int replacedLen
, replaceEnd
, force
=False
, i
;
2511 /* Validate arguments and convert to proper types */
2512 if (nArgs
< 3 || nArgs
> 5)
2513 return wrongNArgsErr(errMsg
);
2514 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2516 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2518 if (!readStringArg(argList
[2], &replaceStr
, stringStorage
[2], errMsg
))
2520 for (i
= 3; i
< nArgs
; i
++) {
2521 /* Read the optional search type and force arguments */
2522 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
2524 if (!StringToSearchType(argStr
, &searchType
)) {
2525 /* It's not a search type. is it "copy"? */
2526 if (!strcmp(argStr
, "copy")) {
2529 *errMsg
= "unrecognized argument to %s";
2535 /* Do the replace */
2536 replacedStr
= ReplaceAllInString(string
, searchStr
, replaceStr
, searchType
,
2537 ©Start
, ©End
, &replacedLen
, GetWindowDelimiters(window
));
2539 /* Return the results */
2540 result
->tag
= STRING_TAG
;
2541 if (replacedStr
== NULL
) {
2543 /* Just copy the original DataValue */
2544 if (argList
[0].tag
== STRING_TAG
) {
2545 result
->val
.str
.rep
= argList
[0].val
.str
.rep
;
2546 result
->val
.str
.len
= argList
[0].val
.str
.len
;
2549 AllocNStringCpy(&result
->val
.str
, string
);
2553 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2554 result
->val
.str
.len
= 0;
2558 size_t remainder
= strlen(&string
[copyEnd
]);
2559 replaceEnd
= copyStart
+ replacedLen
;
2560 AllocNString(&result
->val
.str
, replaceEnd
+ remainder
+ 1);
2561 strncpy(result
->val
.str
.rep
, string
, copyStart
);
2562 strcpy(&result
->val
.str
.rep
[copyStart
], replacedStr
);
2563 strcpy(&result
->val
.str
.rep
[replaceEnd
], &string
[copyEnd
]);
2564 XtFree(replacedStr
);
2569 static int readSearchArgs(DataValue
*argList
, int nArgs
, int *searchDirection
,
2570 int *searchType
, int *wrap
, char **errMsg
)
2573 char *argStr
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2576 *searchDirection
= SEARCH_FORWARD
;
2577 *searchType
= SEARCH_LITERAL
;
2578 for (i
=0; i
<nArgs
; i
++) {
2579 if (!readStringArg(argList
[i
], &argStr
, stringStorage
, errMsg
))
2581 else if (!strcmp(argStr
, "wrap"))
2583 else if (!strcmp(argStr
, "nowrap"))
2585 else if (!strcmp(argStr
, "backward"))
2586 *searchDirection
= SEARCH_BACKWARD
;
2587 else if (!strcmp(argStr
, "forward"))
2588 *searchDirection
= SEARCH_FORWARD
;
2589 else if (!StringToSearchType(argStr
, searchType
)) {
2590 *errMsg
= "Unrecognized argument to %s";
2597 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2598 DataValue
*result
, char **errMsg
)
2602 /* Get argument and convert to int */
2604 return wrongNArgsErr(errMsg
);
2605 if (!readIntArg(argList
[0], &pos
, errMsg
))
2608 /* Set the position */
2609 TextSetCursorPos(window
->lastFocus
, pos
);
2610 result
->tag
= NO_TAG
;
2614 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2615 DataValue
*result
, char **errMsg
)
2617 int start
, end
, startTmp
;
2619 /* Get arguments and convert to int */
2621 return wrongNArgsErr(errMsg
);
2622 if (!readIntArg(argList
[0], &start
, errMsg
))
2624 if (!readIntArg(argList
[1], &end
, errMsg
))
2627 /* Verify integrity of arguments */
2633 if (start
< 0) start
= 0;
2634 if (start
> window
->buffer
->length
) start
= window
->buffer
->length
;
2635 if (end
< 0) end
= 0;
2636 if (end
> window
->buffer
->length
) end
= window
->buffer
->length
;
2638 /* Make the selection */
2639 BufSelect(window
->buffer
, start
, end
);
2640 result
->tag
= NO_TAG
;
2644 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2645 DataValue
*result
, char **errMsg
)
2647 int start
, end
, left
, right
;
2649 /* Get arguments and convert to int */
2651 return wrongNArgsErr(errMsg
);
2652 if (!readIntArg(argList
[0], &start
, errMsg
))
2654 if (!readIntArg(argList
[1], &end
, errMsg
))
2656 if (!readIntArg(argList
[2], &left
, errMsg
))
2658 if (!readIntArg(argList
[3], &right
, errMsg
))
2661 /* Make the selection */
2662 BufRectSelect(window
->buffer
, start
, end
, left
, right
);
2663 result
->tag
= NO_TAG
;
2668 ** Macro subroutine to ring the bell
2670 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2671 DataValue
*result
, char **errMsg
)
2674 return wrongNArgsErr(errMsg
);
2675 XBell(XtDisplay(window
->shell
), 0);
2676 result
->tag
= NO_TAG
;
2680 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2681 DataValue
*result
, char **errMsg
)
2683 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2687 return tooFewArgsErr(errMsg
);
2688 for (i
=0; i
<nArgs
; i
++) {
2689 if (!readStringArg(argList
[i
], &string
, stringStorage
, errMsg
))
2691 printf("%s%s", string
, i
==nArgs
-1 ? "" : " ");
2694 result
->tag
= NO_TAG
;
2699 ** Built-in macro subroutine for getting the value of an environment variable
2701 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2702 DataValue
*result
, char **errMsg
)
2704 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
2708 /* Get name of variable to get */
2710 return wrongNArgsErr(errMsg
);
2711 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
2712 *errMsg
= "argument to %s must be a string";
2715 value
= getenv(name
);
2719 /* Return the text as an allocated string */
2720 result
->tag
= STRING_TAG
;
2721 AllocNStringCpy(&result
->val
.str
, value
);
2725 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2726 DataValue
*result
, char **errMsg
)
2728 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *cmdString
, *inputString
;
2731 return wrongNArgsErr(errMsg
);
2732 if (!readStringArg(argList
[0], &cmdString
, stringStorage
[0], errMsg
))
2734 if (!readStringArg(argList
[1], &inputString
, stringStorage
[1], errMsg
))
2737 /* Shell command execution requires that the macro be suspended, so
2738 this subroutine can't be run if macro execution can't be interrupted */
2739 if (MacroRunWindow()->macroCmdData
== NULL
) {
2740 *errMsg
= "%s can't be called from non-suspendable context";
2745 *errMsg
= "Shell commands not supported under VMS";
2748 ShellCmdToMacroString(window
, cmdString
, inputString
);
2749 result
->tag
= INT_TAG
;
2756 ** Method used by ShellCmdToMacroString (called by shellCmdMS), for returning
2757 ** macro string and exit status after the execution of a shell command is
2758 ** complete. (Sorry about the poor modularity here, it's just not worth
2759 ** teaching other modules about macro return globals, since other than this,
2760 ** they're not used outside of macro.c)
2762 void ReturnShellCommandOutput(WindowInfo
*window
, const char *outText
, int status
)
2765 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2767 if (cmdData
== NULL
)
2769 retVal
.tag
= STRING_TAG
;
2770 AllocNStringCpy(&retVal
.val
.str
, outText
);
2771 ModifyReturnedValue(cmdData
->context
, retVal
);
2772 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.tag
= INT_TAG
;
2773 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.val
.n
= status
;
2776 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2777 DataValue
*result
, char **errMsg
)
2779 macroCmdInfo
*cmdData
;
2780 char stringStorage
[TYPE_INT_STR_SIZE(int)];
2781 char btnStorage
[TYPE_INT_STR_SIZE(int)];
2790 /* Ignore the focused window passed as the function argument and put
2791 the dialog up over the window which is executing the macro */
2792 window
= MacroRunWindow();
2793 cmdData
= window
->macroCmdData
;
2795 /* Dialogs require macro to be suspended and interleaved with other macros.
2796 This subroutine can't be run if macro execution can't be interrupted */
2798 *errMsg
= "%s can't be called from non-suspendable context";
2802 /* Read and check the arguments. The first being the dialog message,
2803 and the rest being the button labels */
2805 *errMsg
= "%s subroutine called with no arguments";
2808 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
)) {
2812 /* check that all button labels can be read */
2813 for (i
=1; i
<nArgs
; i
++) {
2814 if (!readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
)) {
2819 /* pick up the first button */
2827 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
2830 /* Create the message box dialog widget and its dialog shell parent */
2832 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2833 XtSetArg(al
[ac
], XmNmessageString
, s1
=MKSTRING(message
)); ac
++;
2834 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
2835 dialog
= CreateMessageDialog(window
->shell
, "macroDialog", al
, ac
);
2838 /* Only set margin width for the default OK button */
2839 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2840 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
2846 AddMotifCloseCallback(XtParent(dialog
), dialogCloseCB
, window
);
2847 XtAddCallback(dialog
, XmNokCallback
, dialogBtnCB
, window
);
2848 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2849 XmNuserData
, (XtPointer
)1, NULL
);
2850 cmdData
->dialog
= dialog
;
2852 /* Unmanage default buttons, except for "OK" */
2853 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2854 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2856 /* Make callback for the unmanaged cancel button (which can
2857 still get executed via the esc key) activate close box action */
2858 XtAddCallback(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2859 XmNactivateCallback
, dialogCloseCB
, window
);
2861 /* Add user specified buttons (1st is already done) */
2862 for (i
=1; i
<nBtns
; i
++) {
2863 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
2864 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2865 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
2866 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2867 XtAddCallback(btn
, XmNactivateCallback
, dialogBtnCB
, window
);
2871 #ifdef LESSTIF_VERSION
2872 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2873 the escape key for closing the dialog (probably because the
2874 cancel button is not managed). */
2875 XtAddEventHandler(dialog
, KeyPressMask
, False
, dialogEscCB
,
2877 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
2878 True
, GrabModeAsync
, GrabModeAsync
);
2879 #endif /* LESSTIF_VERSION */
2881 /* Put up the dialog */
2882 ManageDialogCenteredOnPointer(dialog
);
2884 /* Stop macro execution until the dialog is complete */
2887 /* Return placeholder result. Value will be changed by button callback */
2888 result
->tag
= INT_TAG
;
2893 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2895 WindowInfo
*window
= (WindowInfo
*)clientData
;
2896 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2900 /* Return the index of the button which was pressed (stored in the userData
2901 field of the button widget). The 1st button, being a gadget, is not
2903 if (cmdData
== NULL
)
2904 return; /* shouldn't happen */
2905 if (XtClass(w
) == xmPushButtonWidgetClass
) {
2906 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
2907 retVal
.val
.n
= (int)userData
;
2910 retVal
.tag
= INT_TAG
;
2911 ModifyReturnedValue(cmdData
->context
, retVal
);
2913 /* Pop down the dialog */
2914 XtDestroyWidget(XtParent(cmdData
->dialog
));
2915 cmdData
->dialog
= NULL
;
2917 /* Continue preempted macro execution */
2918 ResumeMacroExecution(window
);
2921 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2923 WindowInfo
*window
= (WindowInfo
*)clientData
;
2924 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2927 /* Return 0 to show that the dialog was closed via the window close box */
2929 retVal
.tag
= INT_TAG
;
2930 ModifyReturnedValue(cmdData
->context
, retVal
);
2932 /* Pop down the dialog */
2933 XtDestroyWidget(XtParent(cmdData
->dialog
));
2934 cmdData
->dialog
= NULL
;
2936 /* Continue preempted macro execution */
2937 ResumeMacroExecution(window
);
2940 #ifdef LESSTIF_VERSION
2941 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
2944 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
2946 if (clientData
!= NULL
) {
2947 dialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
2951 #endif /* LESSTIF_VERSION */
2953 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2954 DataValue
*result
, char **errMsg
)
2956 macroCmdInfo
*cmdData
;
2957 char stringStorage
[TYPE_INT_STR_SIZE(int)];
2958 char btnStorage
[TYPE_INT_STR_SIZE(int)];
2967 /* Ignore the focused window passed as the function argument and put
2968 the dialog up over the window which is executing the macro */
2969 window
= MacroRunWindow();
2970 cmdData
= window
->macroCmdData
;
2972 /* Dialogs require macro to be suspended and interleaved with other macros.
2973 This subroutine can't be run if macro execution can't be interrupted */
2975 *errMsg
= "%s can't be called from non-suspendable context";
2979 /* Read and check the arguments. The first being the dialog message,
2980 and the rest being the button labels */
2982 *errMsg
= "%s subroutine called with no arguments";
2985 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
)) {
2988 /* check that all button labels can be read */
2989 for (i
=1; i
<nArgs
; i
++) {
2990 if (!readStringArg(argList
[i
], &btnLabel
, stringStorage
, errMsg
)) {
3001 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
3004 /* Create the selection box dialog widget and its dialog shell parent */
3006 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3007 XtSetArg(al
[ac
], XmNselectionLabelString
, s1
=MKSTRING(message
)); ac
++;
3008 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
3009 dialog
= CreatePromptDialog(window
->shell
, "macroStringDialog", al
, ac
);
3012 /* Only set margin width for the default OK button */
3013 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3014 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
3020 AddMotifCloseCallback(XtParent(dialog
), stringDialogCloseCB
, window
);
3021 XtAddCallback(dialog
, XmNokCallback
, stringDialogBtnCB
, window
);
3022 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3023 XmNuserData
, (XtPointer
)1, NULL
);
3024 cmdData
->dialog
= dialog
;
3026 /* Unmanage unneded widgets */
3027 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3028 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3030 /* Make callback for the unmanaged cancel button (which can
3031 still get executed via the esc key) activate close box action */
3032 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3033 XmNactivateCallback
, stringDialogCloseCB
, window
);
3035 /* Add user specified buttons (1st is already done). Selection box
3036 requires a place-holder widget to be added before buttons can be
3037 added, that's what the separator below is for */
3038 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3039 for (i
=1; i
<nBtns
; i
++) {
3040 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
3041 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3042 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
3043 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3044 XtAddCallback(btn
, XmNactivateCallback
, stringDialogBtnCB
, window
);
3048 #ifdef LESSTIF_VERSION
3049 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3050 the escape key for closing the dialog (probably because the
3051 cancel button is not managed). */
3052 XtAddEventHandler(dialog
, KeyPressMask
, False
, stringDialogEscCB
,
3054 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3055 True
, GrabModeAsync
, GrabModeAsync
);
3056 #endif /* LESSTIF_VERSION */
3058 /* Put up the dialog */
3059 ManageDialogCenteredOnPointer(dialog
);
3061 /* Stop macro execution until the dialog is complete */
3064 /* Return placeholder result. Value will be changed by button callback */
3065 result
->tag
= INT_TAG
;
3070 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
3073 WindowInfo
*window
= (WindowInfo
*)clientData
;
3074 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3080 /* shouldn't happen, but would crash if it did */
3081 if (cmdData
== NULL
)
3084 /* Return the string entered in the selection text area */
3085 text
= XmTextGetString(XmSelectionBoxGetChild(cmdData
->dialog
,
3087 retVal
.tag
= STRING_TAG
;
3088 AllocNStringCpy(&retVal
.val
.str
, text
);
3090 ModifyReturnedValue(cmdData
->context
, retVal
);
3092 /* Find the index of the button which was pressed (stored in the userData
3093 field of the button widget). The 1st button, being a gadget, is not
3095 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3096 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3097 btnNum
= (int)userData
;
3101 /* Return the button number in the global variable $string_dialog_button */
3102 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3103 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3105 /* Pop down the dialog */
3106 XtDestroyWidget(XtParent(cmdData
->dialog
));
3107 cmdData
->dialog
= NULL
;
3109 /* Continue preempted macro execution */
3110 ResumeMacroExecution(window
);
3113 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
3116 WindowInfo
*window
= (WindowInfo
*)clientData
;
3117 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3120 /* shouldn't happen, but would crash if it did */
3121 if (cmdData
== NULL
)
3124 /* Return an empty string */
3125 retVal
.tag
= STRING_TAG
;
3126 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3127 retVal
.val
.str
.len
= 0;
3128 ModifyReturnedValue(cmdData
->context
, retVal
);
3130 /* Return button number 0 in the global variable $string_dialog_button */
3131 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3132 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= 0;
3134 /* Pop down the dialog */
3135 XtDestroyWidget(XtParent(cmdData
->dialog
));
3136 cmdData
->dialog
= NULL
;
3138 /* Continue preempted macro execution */
3139 ResumeMacroExecution(window
);
3142 #ifdef LESSTIF_VERSION
3143 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3146 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3148 if (clientData
!= NULL
) {
3149 stringDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3153 #endif /* LESSTIF_VERSION */
3156 ** A subroutine to put up a calltip
3157 ** First arg is either text to be displayed or a key for tip/tag lookup.
3158 ** Optional second arg is the buffer position beneath which to display the
3159 ** upper-left corner of the tip. Default (or -1) puts it under the cursor.
3160 ** Additional optional arguments:
3161 ** "tipText": (default) Indicates first arg is text to be displayed in tip.
3162 ** "tipKey": Indicates first arg is key in calltips database. If key
3163 ** is not found in tip database then the tags database is also
3165 ** "tagKey": Indicates first arg is key in tags database. (Skips
3166 ** search in calltips database.)
3167 ** "center": Horizontally center the calltip at the position
3168 ** "right": Put the right edge of the calltip at the position
3169 ** "center" and "right" cannot both be specified.
3170 ** "above": Place the calltip above the position
3171 ** "strict": Don't move the calltip to keep it on-screen and away
3172 ** from the cursor's line.
3174 ** Returns the new calltip's ID on success, 0 on failure.
3176 ** Does this need to go on IgnoredActions? I don't think so, since
3177 ** showing a calltip may be part of the action you want to learn.
3179 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3180 DataValue
*result
, char **errMsg
)
3182 char stringStorage
[TYPE_INT_STR_SIZE(int)], *tipText
, *txtArg
;
3183 Boolean anchored
= False
, lookup
= True
;
3185 int anchorPos
, hAlign
= TIP_LEFT
, vAlign
= TIP_BELOW
,
3186 alignMode
= TIP_SLOPPY
;
3188 /* Read and check the string */
3190 *errMsg
= "%s subroutine called with too few arguments";
3194 *errMsg
= "%s subroutine called with too many arguments";
3198 /* Read the tip text or key */
3199 if (!readStringArg(argList
[0], &tipText
, stringStorage
, errMsg
))
3202 /* Read the anchor position (-1 for unanchored) */
3204 if (!readIntArg(argList
[1], &anchorPos
, errMsg
))
3209 if (anchorPos
>= 0) anchored
= True
;
3211 /* Any further args are directives for relative positioning */
3212 for (i
= 2; i
< nArgs
; ++i
) {
3213 if (!readStringArg(argList
[i
], &txtArg
, stringStorage
, errMsg
)){
3216 switch( txtArg
[0] ) {
3218 if (strcmp(txtArg
, "center"))
3220 hAlign
= TIP_CENTER
;
3223 if (strcmp(txtArg
, "right"))
3228 if (strcmp(txtArg
, "above"))
3233 if (strcmp(txtArg
, "strict"))
3235 alignMode
= TIP_STRICT
;
3238 if (!strcmp(txtArg
, "tipText"))
3240 else if (!strcmp(txtArg
, "tipKey"))
3242 else if (!strcmp(txtArg
, "tagKey"))
3243 mode
= TIP_FROM_TAG
;
3252 result
->tag
= INT_TAG
;
3253 if (mode
< 0) lookup
= False
;
3254 /* Look up (maybe) a calltip and display it */
3255 result
->val
.n
= ShowTipString( window
, tipText
, anchored
, anchorPos
, lookup
,
3256 mode
, hAlign
, vAlign
, alignMode
);
3261 /* This is how the (more informative) global var. version would work,
3262 assuming there was a global buffer called msg. */
3263 /* sprintf(msg, "unrecognized argument to %%s: \"%s\"", txtArg);
3265 *errMsg
= "unrecognized argument to %s";
3270 ** A subroutine to kill the current calltip
3272 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3273 DataValue
*result
, char **errMsg
)
3278 *errMsg
= "%s subroutine called with too many arguments";
3282 if (!readIntArg(argList
[0], &calltipID
, errMsg
))
3286 KillCalltip( window
, calltipID
);
3288 result
->tag
= NO_TAG
;
3293 * A subroutine to get the ID of the current calltip, or 0 if there is none.
3295 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
,
3296 int nArgs
, DataValue
*result
, char **errMsg
)
3298 result
->tag
= INT_TAG
;
3299 result
->val
.n
= GetCalltipID(window
, 0);
3304 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3305 DataValue
*result
, char **errMsg
)
3307 macroCmdInfo
*cmdData
;
3308 char stringStorage
[TYPE_INT_STR_SIZE(int)];
3309 char textStorage
[TYPE_INT_STR_SIZE(int)];
3310 char btnStorage
[TYPE_INT_STR_SIZE(int)];
3312 char *message
, *text
;
3317 char *p
, *old_p
, **text_lines
, *tmp
;
3320 XmString
*test_strings
;
3326 /* Ignore the focused window passed as the function argument and put
3327 the dialog up over the window which is executing the macro */
3328 window
= MacroRunWindow();
3329 cmdData
= window
->macroCmdData
;
3331 /* Dialogs require macro to be suspended and interleaved with other macros.
3332 This subroutine can't be run if macro execution can't be interrupted */
3334 *errMsg
= "%s can't be called from non-suspendable context";
3338 /* Read and check the arguments. The first being the dialog message,
3339 and the rest being the button labels */
3341 *errMsg
= "%s subroutine called with no message, string or arguments";
3345 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
))
3348 if (!readStringArg(argList
[1], &text
, textStorage
, errMsg
))
3351 if (!text
|| text
[0] == '\0') {
3352 *errMsg
= "%s subroutine called with empty list data";
3356 /* check that all button labels can be read */
3357 for (i
=2; i
<nArgs
; i
++)
3358 if (!readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
))
3361 /* pick up the first button */
3369 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
3372 /* count the lines in the text - add one for unterminated last line */
3374 for (p
= text
; *p
; p
++)
3378 /* now set up arrays of pointers to lines */
3379 /* test_strings to hold the display strings (tab expanded) */
3380 /* text_lines to hold the original text lines (without the '\n's) */
3381 test_strings
= (XmString
*) XtMalloc(sizeof(XmString
) * nlines
);
3382 text_lines
= (char **)XtMalloc(sizeof(char *) * (nlines
+ 1));
3383 for (n
= 0; n
< nlines
; n
++) {
3384 test_strings
[n
] = (XmString
)0;
3385 text_lines
[n
] = (char *)0;
3387 text_lines
[n
] = (char *)0; /* make sure this is a null-terminated table */
3389 /* pick up the tabDist value */
3390 tabDist
= window
->buffer
->tabDist
;
3392 /* load the table */
3396 tmp_len
= 0; /* current allocated size of temporary buffer tmp */
3397 tmp
= malloc(1); /* temporary buffer into which to expand tabs */
3399 is_last
= (*p
== '\0');
3400 if (*p
== '\n' || is_last
) {
3402 if (strlen(old_p
) > 0) { /* only include non-empty lines */
3406 /* save the actual text line in text_lines[n] */
3407 text_lines
[n
] = (char *)XtMalloc(strlen(old_p
) + 1);
3408 strcpy(text_lines
[n
], old_p
);
3410 /* work out the tabs expanded length */
3411 for (s
= old_p
, l
= 0; *s
; s
++)
3412 l
+= (*s
== '\t') ? tabDist
- (l
% tabDist
) : 1;
3414 /* verify tmp is big enough then tab-expand old_p into tmp */
3416 tmp
= realloc(tmp
, (tmp_len
= l
) + 1);
3417 for (s
= old_p
, t
= tmp
, l
= 0; *s
; s
++) {
3419 for (i
= tabDist
- (l
% tabDist
); i
--; l
++)
3428 /* that's it: tmp is the tab-expanded version of old_p */
3429 test_strings
[n
] = MKSTRING(tmp
);
3434 *p
= '\n'; /* put back our newline */
3439 free(tmp
); /* don't need this anymore */
3442 test_strings
[0] = MKSTRING("");
3446 /* Create the selection box dialog widget and its dialog shell parent */
3448 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3449 XtSetArg(al
[ac
], XmNlistLabelString
, s1
=MKSTRING(message
)); ac
++;
3450 XtSetArg(al
[ac
], XmNlistItems
, test_strings
); ac
++;
3451 XtSetArg(al
[ac
], XmNlistItemCount
, nlines
); ac
++;
3452 XtSetArg(al
[ac
], XmNlistVisibleItemCount
, (nlines
> 10) ? 10 : nlines
); ac
++;
3453 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
3454 dialog
= CreateSelectionDialog(window
->shell
, "macroListDialog", al
, ac
);
3457 /* Only set margin width for the default OK button */
3458 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3459 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
3463 AddMotifCloseCallback(XtParent(dialog
), listDialogCloseCB
, window
);
3464 XtAddCallback(dialog
, XmNokCallback
, listDialogBtnCB
, window
);
3465 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3466 XmNuserData
, (XtPointer
)1, NULL
);
3469 cmdData
->dialog
= dialog
;
3471 /* forget lines stored in list */
3473 XmStringFree(test_strings
[n
]);
3474 XtFree((char *)test_strings
);
3476 /* modify the list */
3477 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_LIST
),
3478 XmNselectionPolicy
, XmSINGLE_SELECT
,
3479 XmNuserData
, (XtPointer
)text_lines
, NULL
);
3481 /* Unmanage unneeded widgets */
3482 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_APPLY_BUTTON
));
3483 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3484 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3485 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_TEXT
));
3486 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_SELECTION_LABEL
));
3488 /* Make callback for the unmanaged cancel button (which can
3489 still get executed via the esc key) activate close box action */
3490 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3491 XmNactivateCallback
, listDialogCloseCB
, window
);
3493 /* Add user specified buttons (1st is already done). Selection box
3494 requires a place-holder widget to be added before buttons can be
3495 added, that's what the separator below is for */
3496 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3497 for (i
=1; i
<nBtns
; i
++) {
3498 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
3499 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3500 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
3501 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3502 XtAddCallback(btn
, XmNactivateCallback
, listDialogBtnCB
, window
);
3506 #ifdef LESSTIF_VERSION
3507 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3508 the escape key for closing the dialog. */
3509 XtAddEventHandler(dialog
, KeyPressMask
, False
, listDialogEscCB
,
3511 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3512 True
, GrabModeAsync
, GrabModeAsync
);
3513 #endif /* LESSTIF_VERSION */
3515 /* Put up the dialog */
3516 ManageDialogCenteredOnPointer(dialog
);
3518 /* Stop macro execution until the dialog is complete */
3521 /* Return placeholder result. Value will be changed by button callback */
3522 result
->tag
= INT_TAG
;
3527 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
3530 WindowInfo
*window
= (WindowInfo
*)clientData
;
3531 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3537 int n_sel
, *seltable
, sel_index
= 0;
3541 /* shouldn't happen, but would crash if it did */
3542 if (cmdData
== NULL
)
3545 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3546 /* Return the string selected in the selection list area */
3547 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3548 if (!XmListGetSelectedPos(theList
, &seltable
, &n_sel
)) {
3552 sel_index
= seltable
[0] - 1;
3553 XtFree((XtPointer
)seltable
);
3557 text
= PERM_ALLOC_STR("");
3561 length
= strlen((char *)text_lines
[sel_index
]);
3562 text
= AllocString(length
+ 1);
3563 strcpy(text
, text_lines
[sel_index
]);
3566 /* don't need text_lines anymore: free it */
3567 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3568 XtFree((XtPointer
)text_lines
[sel_index
]);
3569 XtFree((XtPointer
)text_lines
);
3571 retVal
.tag
= STRING_TAG
;
3572 retVal
.val
.str
.rep
= text
;
3573 retVal
.val
.str
.len
= length
;
3574 ModifyReturnedValue(cmdData
->context
, retVal
);
3576 /* Find the index of the button which was pressed (stored in the userData
3577 field of the button widget). The 1st button, being a gadget, is not
3579 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3580 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3581 btnNum
= (int)userData
;
3585 /* Return the button number in the global variable $list_dialog_button */
3586 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3587 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3589 /* Pop down the dialog */
3590 XtDestroyWidget(XtParent(cmdData
->dialog
));
3591 cmdData
->dialog
= NULL
;
3593 /* Continue preempted macro execution */
3594 ResumeMacroExecution(window
);
3597 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
3600 WindowInfo
*window
= (WindowInfo
*)clientData
;
3601 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3607 /* shouldn't happen, but would crash if it did */
3608 if (cmdData
== NULL
)
3611 /* don't need text_lines anymore: retrieve it then free it */
3612 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3613 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3614 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3615 XtFree((XtPointer
)text_lines
[sel_index
]);
3616 XtFree((XtPointer
)text_lines
);
3618 /* Return an empty string */
3619 retVal
.tag
= STRING_TAG
;
3620 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3621 retVal
.val
.str
.len
= 0;
3622 ModifyReturnedValue(cmdData
->context
, retVal
);
3624 /* Return button number 0 in the global variable $list_dialog_button */
3625 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3626 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= 0;
3628 /* Pop down the dialog */
3629 XtDestroyWidget(XtParent(cmdData
->dialog
));
3630 cmdData
->dialog
= NULL
;
3632 /* Continue preempted macro execution */
3633 ResumeMacroExecution(window
);
3635 /* T Balinski End */
3637 #ifdef LESSTIF_VERSION
3638 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3641 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3643 if (clientData
!= NULL
) {
3644 listDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3648 #endif /* LESSTIF_VERSION */
3651 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3652 DataValue
*result
, char **errMsg
)
3654 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3655 char *leftStr
, *rightStr
, *argStr
;
3656 int considerCase
= True
;
3661 return(wrongNArgsErr(errMsg
));
3663 if (!readStringArg(argList
[0], &leftStr
, stringStorage
[0], errMsg
))
3665 if (!readStringArg(argList
[1], &rightStr
, stringStorage
[1], errMsg
))
3667 for (i
= 2; i
< nArgs
; ++i
) {
3668 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
3670 else if (!strcmp(argStr
, "case"))
3671 considerCase
= True
;
3672 else if (!strcmp(argStr
, "nocase"))
3673 considerCase
= False
;
3675 *errMsg
= "Unrecognized argument to %s";
3680 compareResult
= strcmp(leftStr
, rightStr
);
3681 compareResult
= (compareResult
> 0) ? 1 : ((compareResult
< 0) ? -1 : 0);
3684 compareResult
= strCaseCmp(leftStr
, rightStr
);
3686 result
->tag
= INT_TAG
;
3687 result
->val
.n
= compareResult
;
3692 ** This function is intended to split strings into an array of substrings
3693 ** Importatnt note: It should always return at least one entry with key 0
3694 ** split("", ",") result[0] = ""
3695 ** split("1,2", ",") result[0] = "1" result[1] = "2"
3696 ** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
3698 ** This behavior is specifically important when used to break up
3699 ** array sub-scripts
3702 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3703 DataValue
*result
, char **errMsg
)
3705 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3706 char *sourceStr
, *splitStr
, *typeSplitStr
;
3707 int searchType
, beginPos
, foundStart
, foundEnd
, strLength
, lastEnd
;
3708 int found
, elementEnd
, indexNum
;
3709 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
3714 return(wrongNArgsErr(errMsg
));
3716 if (!readStringArg(argList
[0], &sourceStr
, stringStorage
[0], errMsg
)) {
3717 *errMsg
= "first argument must be a string: %s";
3720 if (!readStringArg(argList
[1], &splitStr
, stringStorage
[1], errMsg
)) {
3724 if (splitStr
[0] == 0) {
3728 if (splitStr
== NULL
) {
3729 *errMsg
= "second argument must be a non-empty string: %s";
3732 if (nArgs
> 2 && readStringArg(argList
[2], &typeSplitStr
, stringStorage
[2], errMsg
)) {
3733 if (!StringToSearchType(typeSplitStr
, &searchType
)) {
3734 *errMsg
= "unrecognized argument to %s";
3739 searchType
= SEARCH_LITERAL
;
3742 result
->tag
= ARRAY_TAG
;
3743 result
->val
.arrayPtr
= ArrayNew();
3748 strLength
= strlen(sourceStr
);
3750 while (found
&& beginPos
< strLength
) {
3751 sprintf(indexStr
, "%d", indexNum
);
3752 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3753 if (!allocIndexStr
) {
3754 *errMsg
= "array element failed to allocate key: %s";
3757 strcpy(allocIndexStr
, indexStr
);
3758 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
, searchType
,
3759 False
, beginPos
, &foundStart
, &foundEnd
,
3760 NULL
, NULL
, GetWindowDelimiters(window
));
3761 elementEnd
= found
? foundStart
: strLength
;
3762 elementLen
= elementEnd
- lastEnd
;
3763 element
.tag
= STRING_TAG
;
3764 if (!AllocNStringNCpy(&element
.val
.str
, &sourceStr
[lastEnd
], elementLen
)) {
3765 *errMsg
= "failed to allocate element value: %s";
3769 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3770 M_ARRAY_INSERT_FAILURE();
3774 if (foundStart
== foundEnd
) {
3775 beginPos
= foundEnd
+ 1; /* Avoid endless loop for 0-width match */
3777 beginPos
= foundEnd
;
3780 beginPos
= strLength
; /* Break the loop */
3786 sprintf(indexStr
, "%d", indexNum
);
3787 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3788 if (!allocIndexStr
) {
3789 *errMsg
= "array element failed to allocate key: %s";
3792 strcpy(allocIndexStr
, indexStr
);
3793 element
.tag
= STRING_TAG
;
3794 if (lastEnd
== strLength
) {
3795 /* The pattern mathed the end of the string. Add an empty chunk. */
3796 element
.val
.str
.rep
= PERM_ALLOC_STR("");
3797 element
.val
.str
.len
= 0;
3799 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3800 M_ARRAY_INSERT_FAILURE();
3803 /* We skipped the last character to prevent an endless loop.
3804 Add it to the list. */
3805 elementLen
= strLength
- lastEnd
;
3806 if (!AllocNStringNCpy(&element
.val
.str
, &sourceStr
[lastEnd
], elementLen
)) {
3807 *errMsg
= "failed to allocate element value: %s";
3811 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3812 M_ARRAY_INSERT_FAILURE();
3815 /* If the pattern can match zero-length strings, we may have to
3816 add a final empty chunk.
3817 For instance: split("abc\n", "$", "regex")
3818 -> matches before \n and at end of string
3819 -> expected output: "abc", "\n", ""
3820 The '\n' gets added in the lines above, but we still have to
3821 verify whether the pattern also matches the end of the string,
3822 and add an empty chunk in case it does. */
3823 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
,
3824 searchType
, False
, strLength
, &foundStart
, &foundEnd
,
3825 NULL
, NULL
, GetWindowDelimiters(window
));
3828 sprintf(indexStr
, "%d", indexNum
);
3829 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3830 if (!allocIndexStr
) {
3831 *errMsg
= "array element failed to allocate key: %s";
3834 strcpy(allocIndexStr
, indexStr
);
3835 element
.tag
= STRING_TAG
;
3836 element
.val
.str
.rep
= PERM_ALLOC_STR("");
3837 element
.val
.str
.len
= 0;
3839 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3840 M_ARRAY_INSERT_FAILURE();
3849 ** Set the backlighting string resource for the current window. If no parameter
3850 ** is passed or the value "default" is passed, it attempts to set the preference
3851 ** value of the resource. If the empty string is passed, the backlighting string
3852 ** will be cleared, turning off backlighting.
3855 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
3856 int nArgs, DataValue *result, char **errMsg)
3858 char *backlightString;
3861 backlightString = GetPrefBacklightCharTypes();
3863 else if (nArgs == 1) {
3864 if (argList[0].tag != STRING_TAG) {
3865 *errMsg = "%s not called with a string parameter";
3868 backlightString = argList[0].val.str.rep;
3871 return wrongNArgsErr(errMsg);
3873 if (strcmp(backlightString, "default") == 0)
3874 backlightString = GetPrefBacklightCharTypes();
3875 if (backlightString && *backlightString == '\0') / * empty string param * /
3876 backlightString = NULL; / * turns of backlighting * /
3878 SetBacklightChars(window, backlightString);
3882 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3883 DataValue
*result
, char **errMsg
)
3885 result
->tag
= INT_TAG
;
3886 result
->val
.n
= TextGetCursorPos(window
->lastFocus
);
3890 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3891 DataValue
*result
, char **errMsg
)
3893 int line
, cursorPos
, colNum
;
3895 result
->tag
= INT_TAG
;
3896 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3897 if (!TextPosToLineAndCol(window
->lastFocus
, cursorPos
, &line
, &colNum
))
3898 line
= BufCountLines(window
->buffer
, 0, cursorPos
) + 1;
3899 result
->val
.n
= line
;
3903 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3904 DataValue
*result
, char **errMsg
)
3906 textBuffer
*buf
= window
->buffer
;
3909 result
->tag
= INT_TAG
;
3910 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3911 result
->val
.n
= BufCountDispChars(buf
, BufStartOfLine(buf
, cursorPos
),
3916 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3917 DataValue
*result
, char **errMsg
)
3919 result
->tag
= STRING_TAG
;
3920 AllocNStringCpy(&result
->val
.str
, window
->filename
);
3924 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3925 DataValue
*result
, char **errMsg
)
3927 result
->tag
= STRING_TAG
;
3928 AllocNStringCpy(&result
->val
.str
, window
->path
);
3932 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3933 DataValue
*result
, char **errMsg
)
3935 result
->tag
= INT_TAG
;
3936 result
->val
.n
= window
->buffer
->length
;
3940 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3941 DataValue
*result
, char **errMsg
)
3943 result
->tag
= INT_TAG
;
3944 result
->val
.n
= window
->buffer
->primary
.selected
?
3945 window
->buffer
->primary
.start
: -1;
3949 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3950 DataValue
*result
, char **errMsg
)
3952 result
->tag
= INT_TAG
;
3953 result
->val
.n
= window
->buffer
->primary
.selected
?
3954 window
->buffer
->primary
.end
: -1;
3958 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3959 DataValue
*result
, char **errMsg
)
3961 selection
*sel
= &window
->buffer
->primary
;
3963 result
->tag
= INT_TAG
;
3964 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectStart
: -1;
3968 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3969 DataValue
*result
, char **errMsg
)
3971 selection
*sel
= &window
->buffer
->primary
;
3973 result
->tag
= INT_TAG
;
3974 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectEnd
: -1;
3978 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3979 DataValue
*result
, char **errMsg
)
3983 XtVaGetValues(window
->textArea
, textNcolumns
, &nCols
,
3984 textNwrapMargin
, &margin
, NULL
);
3985 result
->tag
= INT_TAG
;
3986 result
->val
.n
= margin
== 0 ? nCols
: margin
;
3990 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3991 DataValue
*result
, char **errMsg
)
3993 result
->tag
= INT_TAG
;
3994 result
->val
.n
= window
->showStats
? 1 : 0;
3998 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3999 DataValue
*result
, char **errMsg
)
4001 result
->tag
= INT_TAG
;
4002 result
->val
.n
= window
->showISearchLine
? 1 : 0;
4006 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4007 DataValue
*result
, char **errMsg
)
4009 result
->tag
= INT_TAG
;
4010 result
->val
.n
= window
->showLineNumbers
? 1 : 0;
4014 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4015 DataValue
*result
, char **errMsg
)
4019 switch (window
->indentStyle
) {
4020 case NO_AUTO_INDENT
:
4021 res
= PERM_ALLOC_STR("off");
4024 res
= PERM_ALLOC_STR("on");
4027 res
= PERM_ALLOC_STR("smart");
4030 *errMsg
= "Invalid indent style value encountered in %s";
4034 result
->tag
= STRING_TAG
;
4035 result
->val
.str
.rep
= res
;
4036 result
->val
.str
.len
= strlen(res
);
4040 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4041 DataValue
*result
, char **errMsg
)
4045 switch (window
->wrapMode
) {
4047 res
= PERM_ALLOC_STR("none");
4050 res
= PERM_ALLOC_STR("auto");
4052 case CONTINUOUS_WRAP
:
4053 res
= PERM_ALLOC_STR("continuous");
4056 *errMsg
= "Invalid wrap style value encountered in %s";
4060 result
->tag
= STRING_TAG
;
4061 result
->val
.str
.rep
= res
;
4062 result
->val
.str
.len
= strlen(res
);
4066 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4067 DataValue
*result
, char **errMsg
)
4069 result
->tag
= INT_TAG
;
4070 result
->val
.n
= window
->highlightSyntax
? 1 : 0;
4074 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4075 DataValue
*result
, char **errMsg
)
4077 result
->tag
= INT_TAG
;
4078 result
->val
.n
= window
->saveOldVersion
? 1 : 0;
4082 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4083 DataValue
*result
, char **errMsg
)
4085 result
->tag
= INT_TAG
;
4086 result
->val
.n
= window
->autoSave
? 1 : 0;
4090 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4091 DataValue
*result
, char **errMsg
)
4095 switch (window
->showMatchingStyle
) {
4097 res
= PERM_ALLOC_STR(NO_FLASH_STRING
);
4100 res
= PERM_ALLOC_STR(FLASH_DELIMIT_STRING
);
4103 res
= PERM_ALLOC_STR(FLASH_RANGE_STRING
);
4106 *errMsg
= "Invalid match flashing style value encountered in %s";
4110 result
->tag
= STRING_TAG
;
4111 result
->val
.str
.rep
= res
;
4112 result
->val
.str
.len
= strlen(res
);
4116 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4117 DataValue
*result
, char **errMsg
)
4119 result
->tag
= INT_TAG
;
4120 result
->val
.n
= window
->overstrike
? 1 : 0;
4124 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4125 DataValue
*result
, char **errMsg
)
4127 result
->tag
= INT_TAG
;
4128 result
->val
.n
= (IS_ANY_LOCKED(window
->lockReasons
)) ? 1 : 0;
4132 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4133 DataValue
*result
, char **errMsg
)
4135 result
->tag
= INT_TAG
;
4136 result
->val
.n
= (IS_USER_LOCKED(window
->lockReasons
)) ? 1 : 0;
4140 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4141 DataValue
*result
, char **errMsg
)
4145 switch (window
->fileFormat
) {
4146 case UNIX_FILE_FORMAT
:
4147 res
= PERM_ALLOC_STR("unix");
4149 case DOS_FILE_FORMAT
:
4150 res
= PERM_ALLOC_STR("dos");
4152 case MAC_FILE_FORMAT
:
4153 res
= PERM_ALLOC_STR("macintosh");
4156 *errMsg
= "Invalid linefeed style value encountered in %s";
4159 result
->tag
= STRING_TAG
;
4160 result
->val
.str
.rep
= res
;
4161 result
->val
.str
.len
= strlen(res
);
4165 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4166 DataValue
*result
, char **errMsg
)
4168 result
->tag
= STRING_TAG
;
4169 AllocNStringCpy(&result
->val
.str
, window
->fontName
);
4173 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4174 DataValue
*result
, char **errMsg
)
4176 result
->tag
= STRING_TAG
;
4177 AllocNStringCpy(&result
->val
.str
, window
->italicFontName
);
4181 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4182 DataValue
*result
, char **errMsg
)
4184 result
->tag
= STRING_TAG
;
4185 AllocNStringCpy(&result
->val
.str
, window
->boldFontName
);
4189 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4190 DataValue
*result
, char **errMsg
)
4192 result
->tag
= STRING_TAG
;
4193 AllocNStringCpy(&result
->val
.str
, window
->boldItalicFontName
);
4197 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4198 DataValue
*result
, char **errMsg
)
4200 result
->tag
= STRING_TAG
;
4201 result
->val
.str
.rep
= PERM_ALLOC_STR(ARRAY_DIM_SEP
);
4202 result
->val
.str
.len
= strlen(result
->val
.str
.rep
);
4206 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4207 DataValue
*result
, char **errMsg
)
4209 result
->tag
= INT_TAG
;
4210 result
->val
.n
= TextGetMinFontWidth(window
->textArea
, window
->highlightSyntax
);
4214 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4215 DataValue
*result
, char **errMsg
)
4217 result
->tag
= INT_TAG
;
4218 result
->val
.n
= TextGetMaxFontWidth(window
->textArea
, window
->highlightSyntax
);
4222 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4223 DataValue
*result
, char **errMsg
)
4225 result
->tag
= INT_TAG
;
4226 result
->val
.n
= TextFirstVisibleLine(window
->lastFocus
);
4230 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4231 DataValue
*result
, char **errMsg
)
4233 result
->tag
= INT_TAG
;
4234 result
->val
.n
= TextNumVisibleLines(window
->lastFocus
);
4238 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4239 DataValue
*result
, char **errMsg
)
4241 result
->tag
= INT_TAG
;
4242 result
->val
.n
= TextVisibleWidth(window
->lastFocus
);
4246 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4247 DataValue
*result
, char **errMsg
)
4249 result
->tag
= INT_TAG
;
4250 result
->val
.n
= WidgetToPaneIndex(window
, window
->lastFocus
) + 1;
4254 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4255 DataValue
*result
, char **errMsg
)
4257 result
->tag
= INT_TAG
;
4258 result
->val
.n
= window
->nPanes
+ 1;
4262 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4263 DataValue
*result
, char **errMsg
)
4265 result
->tag
= ARRAY_TAG
;
4266 result
->val
.arrayPtr
= NULL
;
4270 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4271 DataValue
*result
, char **errMsg
)
4273 result
->tag
= STRING_TAG
;
4274 AllocNStringCpy(&result
->val
.str
, GetPrefServerName());
4278 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4279 DataValue
*result
, char **errMsg
)
4281 result
->tag
= INT_TAG
;
4282 result
->val
.n
= window
->buffer
->tabDist
;
4286 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4287 DataValue
*result
, char **errMsg
)
4291 XtVaGetValues(window
->textArea
, textNemulateTabs
, &dist
, NULL
);
4292 result
->tag
= INT_TAG
;
4293 result
->val
.n
= dist
== 0 ? -1 : dist
;
4297 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4298 DataValue
*result
, char **errMsg
)
4300 result
->tag
= INT_TAG
;
4301 result
->val
.n
= window
->buffer
->useTabs
;
4305 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4306 DataValue
*result
, char **errMsg
)
4308 result
->tag
= INT_TAG
;
4309 result
->val
.n
= window
->fileChanged
;
4313 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4314 DataValue
*result
, char **errMsg
)
4316 char *lmName
= LanguageModeName(window
->languageMode
);
4320 result
->tag
= STRING_TAG
;
4321 AllocNStringCpy(&result
->val
.str
, lmName
);
4326 static int backlightStringMV(WindowInfo *window, DataValue *argList,
4327 int nArgs, DataValue *result, char **errMsg)
4329 char *backlightString = window->backlightCharTypes;
4331 result->tag = STRING_TAG;
4332 if (!backlightString || !window->backlightChars)
4333 backlightString = "";
4334 AllocNStringCpy(&result->val.str, backlightString);
4338 /* -------------------------------------------------------------------------- */
4341 ** Range set macro variables and functions
4344 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4345 DataValue
*result
, char **errMsg
)
4347 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4348 unsigned char *rangesetList
;
4349 char *allocIndexStr
;
4350 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4354 result
->tag
= ARRAY_TAG
;
4355 result
->val
.arrayPtr
= ArrayNew();
4357 if (rangesetTable
== NULL
) {
4361 rangesetList
= RangesetGetList(rangesetTable
);
4362 nRangesets
= strlen((char*)rangesetList
);
4363 for(i
= 0; i
< nRangesets
; i
++) {
4364 element
.tag
= INT_TAG
;
4365 element
.val
.n
= rangesetList
[i
];
4367 sprintf(indexStr
, "%d", nRangesets
- i
- 1);
4368 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4369 if (allocIndexStr
== NULL
)
4370 M_FAILURE("Failed to allocate array key in %s");
4371 strcpy(allocIndexStr
, indexStr
);
4373 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4374 M_FAILURE("Failed to insert array element in %s");
4382 ** Built-in macro subroutine to create a new rangeset or rangesets.
4383 ** If called with one argument: $1 is the number of rangesets required and
4384 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4385 ** (or an empty array if the requested number of rangesets are not available).
4386 ** If called with no arguments, returns a single rangeset label (not an array),
4387 ** or an empty string if there are no rangesets available.
4389 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4390 DataValue
*result
, char **errMsg
)
4393 int i
, nRangesetsRequired
;
4395 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
4397 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4400 return wrongNArgsErr(errMsg
);
4402 if (rangesetTable
== NULL
) {
4403 window
->buffer
->rangesetTable
= rangesetTable
=
4404 RangesetTableAlloc(window
->buffer
);
4408 label
= RangesetCreate(rangesetTable
);
4410 result
->tag
= INT_TAG
;
4411 result
->val
.n
= label
;
4415 if (!readIntArg(argList
[0], &nRangesetsRequired
, errMsg
))
4418 result
->tag
= ARRAY_TAG
;
4419 result
->val
.arrayPtr
= ArrayNew();
4421 if (nRangesetsRequired
> nRangesetsAvailable(rangesetTable
))
4424 for (i
= 0; i
< nRangesetsRequired
; i
++) {
4425 element
.tag
= INT_TAG
;
4426 element
.val
.n
= RangesetCreate(rangesetTable
);
4428 sprintf(indexStr
, "%d", i
);
4429 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4430 if (!allocIndexStr
) {
4431 *errMsg
= "Array element failed to allocate key: %s";
4434 strcpy(allocIndexStr
, indexStr
);
4435 ArrayInsert(result
, allocIndexStr
, &element
);
4444 ** Built-in macro subroutine for forgetting a range set.
4447 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4448 DataValue
*result
, char **errMsg
)
4450 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4453 char keyString
[TYPE_INT_STR_SIZE(int)];
4454 int deleteLabels
[N_RANGESETS
];
4459 return wrongNArgsErr(errMsg
);
4462 if (argList
[0].tag
== ARRAY_TAG
) {
4463 array
= &argList
[0];
4464 arraySize
= ArraySize(array
);
4466 if (arraySize
> N_RANGESETS
) {
4467 M_FAILURE("Too many elements in array in %s");
4470 for (i
= 0; i
< arraySize
; i
++) {
4471 sprintf(keyString
, "%d", i
);
4473 if (!ArrayGet(array
, keyString
, &element
)) {
4474 M_FAILURE("Invalid key in array in %s");
4477 if (!readIntArg(element
, &label
, errMsg
)
4478 || !RangesetLabelOK(label
)) {
4479 M_FAILURE("Invalid rangeset label in array in %s");
4482 deleteLabels
[i
] = label
;
4485 for (i
= 0; i
< arraySize
; i
++) {
4486 RangesetForget(rangesetTable
, deleteLabels
[i
]);
4491 if (!readIntArg(argList
[0], &label
, errMsg
)
4492 || !RangesetLabelOK(label
)) {
4493 M_FAILURE("Invalid rangeset label in %s");
4496 if(rangesetTable
!= NULL
) {
4497 RangesetForget(rangesetTable
, label
);
4502 result
->tag
= NO_TAG
;
4508 ** Built-in macro subroutine for getting all range sets with a specfic name.
4509 ** Arguments are $1: range set name.
4510 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4513 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4514 DataValue
*result
, char **errMsg
)
4516 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4519 char *name
, *rangeset_name
;
4520 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4521 unsigned char *rangesetList
;
4522 char *allocIndexStr
;
4523 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4524 int nRangesets
, i
, insertIndex
= 0;
4528 return wrongNArgsErr(errMsg
);
4531 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
4532 M_FAILURE("First parameter is not a name string in %s");
4535 result
->tag
= ARRAY_TAG
;
4536 result
->val
.arrayPtr
= ArrayNew();
4538 if (rangesetTable
== NULL
) {
4542 rangesetList
= RangesetGetList(rangesetTable
);
4543 nRangesets
= strlen((char *)rangesetList
);
4544 for (i
= 0; i
< nRangesets
; ++i
) {
4545 label
= rangesetList
[i
];
4546 rangeset
= RangesetFetch(rangesetTable
, label
);
4548 rangeset_name
= RangesetGetName(rangeset
);
4549 if (strcmp(name
, rangeset_name
? rangeset_name
: "") == 0) {
4550 element
.tag
= INT_TAG
;
4551 element
.val
.n
= label
;
4553 sprintf(indexStr
, "%d", insertIndex
);
4554 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4555 if (allocIndexStr
== NULL
)
4556 M_FAILURE("Failed to allocate array key in %s");
4558 strcpy(allocIndexStr
, indexStr
);
4560 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4561 M_FAILURE("Failed to insert array element in %s");
4573 ** Built-in macro subroutine for adding to a range set. Arguments are $1: range
4574 ** set label (one integer), then either (a) $2: source range set label,
4575 ** (b) $2: int start-range, $3: int end-range, (c) nothing (use selection
4576 ** if any to specify range to add - must not be rectangular). Returns the
4577 ** index of the newly added range (cases b and c), or 0 (case a).
4580 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4581 DataValue
*result
, char **errMsg
)
4583 textBuffer
*buffer
= window
->buffer
;
4584 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4585 Rangeset
*targetRangeset
, *sourceRangeset
;
4586 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
, index
;
4589 if (nArgs
< 1 || nArgs
> 3)
4590 return wrongNArgsErr(errMsg
);
4592 if (!readIntArg(argList
[0], &label
, errMsg
)
4593 || !RangesetLabelOK(label
)) {
4594 M_FAILURE("First parameter is an invalid rangeset label in %s");
4597 if (rangesetTable
== NULL
) {
4598 M_FAILURE("Rangeset does not exist in %s");
4601 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4603 if (targetRangeset
== NULL
) {
4604 M_FAILURE("Rangeset does not exist in %s");
4610 /* pick up current selection in this window */
4611 if (!BufGetSelectionPos(buffer
, &start
, &end
,
4612 &isRect
, &rectStart
, &rectEnd
) || isRect
) {
4613 M_FAILURE("Selection missing or rectangular in call to %s");
4615 if (!RangesetAddBetween(targetRangeset
, start
, end
)) {
4616 M_FAILURE("Failure to add selection in %s");
4621 /* add ranges taken from a second set */
4622 if (!readIntArg(argList
[1], &label
, errMsg
)
4623 || !RangesetLabelOK(label
)) {
4624 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4627 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4628 if (sourceRangeset
== NULL
) {
4629 M_FAILURE("Second rangeset does not exist in %s");
4632 RangesetAdd(targetRangeset
, sourceRangeset
);
4636 /* add a range bounded by the start and end positions in $2, $3 */
4637 if (!readIntArg(argList
[1], &start
, errMsg
)) {
4640 if (!readIntArg(argList
[2], &end
, errMsg
)) {
4644 /* make sure range is in order and fits buffer size */
4645 maxpos
= buffer
->length
;
4646 if (start
< 0) start
= 0;
4647 if (start
> maxpos
) start
= maxpos
;
4648 if (end
< 0) end
= 0;
4649 if (end
> maxpos
) end
= maxpos
;
4650 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4652 if ((start
!= end
) && !RangesetAddBetween(targetRangeset
, start
, end
)) {
4653 M_FAILURE("Failed to add range in %s");
4657 /* (to) which range did we just add? */
4658 if (nArgs
!= 2 && start
>= 0) {
4659 start
= (start
+ end
) / 2; /* "middle" of added range */
4660 index
= 1 + RangesetFindRangeOfPos(targetRangeset
, start
, False
);
4667 result
->tag
= INT_TAG
;
4668 result
->val
.n
= index
;
4674 ** Built-in macro subroutine for removing from a range set. Almost identical to
4675 ** rangesetAddMS() - only changes are from RangesetAdd()/RangesetAddBetween()
4676 ** to RangesetSubtract()/RangesetSubtractBetween(), the handling of an
4677 ** undefined destination range, and that it returns no value.
4680 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4681 DataValue
*result
, char **errMsg
)
4683 textBuffer
*buffer
= window
->buffer
;
4684 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4685 Rangeset
*targetRangeset
, *sourceRangeset
;
4686 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
;
4689 if (nArgs
< 1 || nArgs
> 3) {
4690 return wrongNArgsErr(errMsg
);
4693 if (!readIntArg(argList
[0], &label
, errMsg
)
4694 || !RangesetLabelOK(label
)) {
4695 M_FAILURE("First parameter is an invalid rangeset label in %s");
4698 if (rangesetTable
== NULL
) {
4699 M_FAILURE("Rangeset does not exist in %s");
4702 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4703 if (targetRangeset
== NULL
) {
4704 M_FAILURE("Rangeset does not exist in %s");
4708 /* remove current selection in this window */
4709 if (!BufGetSelectionPos(buffer
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)
4711 M_FAILURE("Selection missing or rectangular in call to %s");
4713 RangesetRemoveBetween(targetRangeset
, start
, end
);
4717 /* remove ranges taken from a second set */
4718 if (!readIntArg(argList
[1], &label
, errMsg
)
4719 || !RangesetLabelOK(label
)) {
4720 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4723 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4724 if (sourceRangeset
== NULL
) {
4725 M_FAILURE("Second rangeset does not exist in %s");
4727 RangesetRemove(targetRangeset
, sourceRangeset
);
4731 /* remove a range bounded by the start and end positions in $2, $3 */
4732 if (!readIntArg(argList
[1], &start
, errMsg
))
4734 if (!readIntArg(argList
[2], &end
, errMsg
))
4737 /* make sure range is in order and fits buffer size */
4738 maxpos
= buffer
->length
;
4739 if (start
< 0) start
= 0;
4740 if (start
> maxpos
) start
= maxpos
;
4741 if (end
< 0) end
= 0;
4742 if (end
> maxpos
) end
= maxpos
;
4743 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4745 RangesetRemoveBetween(targetRangeset
, start
, end
);
4749 result
->tag
= NO_TAG
;
4755 ** Built-in macro subroutine to invert a range set. Argument is $1: range set
4756 ** label (one alphabetic character). Returns nothing. Fails if range set
4760 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4761 DataValue
*result
, char **errMsg
)
4764 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4769 return wrongNArgsErr(errMsg
);
4771 if (!readIntArg(argList
[0], &label
, errMsg
)
4772 || !RangesetLabelOK(label
)) {
4773 M_FAILURE("First parameter is an invalid rangeset label in %s");
4776 if (rangesetTable
== NULL
) {
4777 M_FAILURE("Rangeset does not exist in %s");
4780 rangeset
= RangesetFetch(rangesetTable
, label
);
4781 if (rangeset
== NULL
) {
4782 M_FAILURE("Rangeset does not exist in %s");
4785 if (RangesetInverse(rangeset
) < 0) {
4786 M_FAILURE("Problem inverting rangeset in %s");
4790 result
->tag
= NO_TAG
;
4796 ** Built-in macro subroutine for finding out info about a rangeset. Takes one
4797 ** argument of a rangeset label. Returns an array with the following keys:
4798 ** defined, count, color, mode.
4801 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4802 DataValue
*result
, char **errMsg
)
4804 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4805 Rangeset
*rangeset
= NULL
;
4807 char *color
, *name
, *mode
;
4812 return wrongNArgsErr(errMsg
);
4814 if (!readIntArg(argList
[0], &label
, errMsg
)
4815 || !RangesetLabelOK(label
)) {
4816 M_FAILURE("First parameter is an invalid rangeset label in %s");
4819 if (rangesetTable
!= NULL
) {
4820 rangeset
= RangesetFetch(rangesetTable
, label
);
4823 RangesetGetInfo(rangeset
, &defined
, &label
, &count
, &color
, &name
, &mode
);
4826 result
->tag
= ARRAY_TAG
;
4827 result
->val
.arrayPtr
= ArrayNew();
4829 element
.tag
= INT_TAG
;
4830 element
.val
.n
= defined
;
4831 if (!ArrayInsert(result
, PERM_ALLOC_STR("defined"), &element
))
4832 M_FAILURE("Failed to insert array element \"defined\" in %s");
4834 element
.tag
= INT_TAG
;
4835 element
.val
.n
= count
;
4836 if (!ArrayInsert(result
, PERM_ALLOC_STR("count"), &element
))
4837 M_FAILURE("Failed to insert array element \"count\" in %s");
4839 element
.tag
= STRING_TAG
;
4840 if (!AllocNStringCpy(&element
.val
.str
, color
))
4841 M_FAILURE("Failed to allocate array value \"color\" in %s");
4842 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &element
))
4843 M_FAILURE("Failed to insert array element \"color\" in %s");
4845 element
.tag
= STRING_TAG
;
4846 if (!AllocNStringCpy(&element
.val
.str
, name
))
4847 M_FAILURE("Failed to allocate array value \"name\" in %s");
4848 if (!ArrayInsert(result
, PERM_ALLOC_STR("name"), &element
)) {
4849 M_FAILURE("Failed to insert array element \"name\" in %s");
4852 element
.tag
= STRING_TAG
;
4853 if (!AllocNStringCpy(&element
.val
.str
, mode
))
4854 M_FAILURE("Failed to allocate array value \"mode\" in %s");
4855 if (!ArrayInsert(result
, PERM_ALLOC_STR("mode"), &element
))
4856 M_FAILURE("Failed to insert array element \"mode\" in %s");
4862 ** Built-in macro subroutine for finding the extent of a range in a set.
4863 ** If only one parameter is supplied, use the spanning range of all
4864 ** ranges, otherwise select the individual range specified. Returns
4865 ** an array with the keys "start" and "end" and values
4868 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4869 DataValue
*result
, char **errMsg
)
4871 textBuffer
*buffer
= window
->buffer
;
4872 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4874 int start
, end
, dummy
, rangeIndex
, ok
;
4878 if (nArgs
< 1 || nArgs
> 2) {
4879 return wrongNArgsErr(errMsg
);
4882 if (!readIntArg(argList
[0], &label
, errMsg
)
4883 || !RangesetLabelOK(label
)) {
4884 M_FAILURE("First parameter is an invalid rangeset label in %s");
4887 if (rangesetTable
== NULL
) {
4888 M_FAILURE("Rangeset does not exist in %s");
4892 rangeset
= RangesetFetch(rangesetTable
, label
);
4893 if (rangeset
!= NULL
) {
4895 rangeIndex
= RangesetGetNRanges(rangeset
) - 1;
4896 ok
= RangesetFindRangeNo(rangeset
, 0, &start
, &dummy
);
4897 ok
&= RangesetFindRangeNo(rangeset
, rangeIndex
, &dummy
, &end
);
4900 else if (nArgs
== 2) {
4901 if (!readIntArg(argList
[1], &rangeIndex
, errMsg
)) {
4904 ok
= RangesetFindRangeNo(rangeset
, rangeIndex
-1, &start
, &end
);
4909 result
->tag
= ARRAY_TAG
;
4910 result
->val
.arrayPtr
= ArrayNew();
4915 element
.tag
= INT_TAG
;
4916 element
.val
.n
= start
;
4917 if (!ArrayInsert(result
, PERM_ALLOC_STR("start"), &element
))
4918 M_FAILURE("Failed to insert array element \"start\" in %s");
4920 element
.tag
= INT_TAG
;
4921 element
.val
.n
= end
;
4922 if (!ArrayInsert(result
, PERM_ALLOC_STR("end"), &element
))
4923 M_FAILURE("Failed to insert array element \"end\" in %s");
4929 ** Built-in macro subroutine for checking a position against a range. If only
4930 ** one parameter is supplied, the current cursor position is used. Returns
4931 ** false (zero) if not in a range, range index (1-based) if in a range;
4932 ** fails if parameters were bad.
4935 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
4936 int nArgs
, DataValue
*result
, char **errMsg
)
4938 textBuffer
*buffer
= window
->buffer
;
4939 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4941 int pos
, rangeIndex
, maxpos
;
4944 if (nArgs
< 1 || nArgs
> 2) {
4945 return wrongNArgsErr(errMsg
);
4948 if (!readIntArg(argList
[0], &label
, errMsg
)
4949 || !RangesetLabelOK(label
)) {
4950 M_FAILURE("First parameter is an invalid rangeset label in %s");
4953 if (rangesetTable
== NULL
) {
4954 M_FAILURE("Rangeset does not exist in %s");
4957 rangeset
= RangesetFetch(rangesetTable
, label
);
4958 if (rangeset
== NULL
) {
4959 M_FAILURE("Rangeset does not exist in %s");
4963 pos
= TextGetCursorPos(window
->lastFocus
);
4965 else if (nArgs
== 2) {
4966 if (!readIntArg(argList
[1], &pos
, errMsg
))
4970 maxpos
= buffer
->length
;
4971 if (pos
< 0 || pos
> maxpos
) {
4975 rangeIndex
= RangesetFindRangeOfPos(rangeset
, pos
, False
) + 1;
4979 result
->tag
= INT_TAG
;
4980 result
->val
.n
= rangeIndex
;
4985 ** Set the color of a range set's ranges. it is ignored if the color cannot be
4986 ** found/applied. If no color is applied, any current color is removed. Returns
4987 ** true if the rangeset is valid.
4990 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
4991 int nArgs
, DataValue
*result
, char **errMsg
)
4993 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4994 textBuffer
*buffer
= window
->buffer
;
4995 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5001 return wrongNArgsErr(errMsg
);
5004 if (!readIntArg(argList
[0], &label
, errMsg
)
5005 || !RangesetLabelOK(label
)) {
5006 M_FAILURE("First parameter is an invalid rangeset label in %s");
5009 if (rangesetTable
== NULL
) {
5010 M_FAILURE("Rangeset does not exist in %s");
5013 rangeset
= RangesetFetch(rangesetTable
, label
);
5014 if (rangeset
== NULL
) {
5015 M_FAILURE("Rangeset does not exist in %s");
5019 if (rangeset
!= NULL
) {
5020 if (!readStringArg(argList
[1], &color_name
, stringStorage
[0], errMsg
)) {
5021 M_FAILURE("Second parameter is not a color name string in %s");
5025 RangesetAssignColorName(rangeset
, color_name
);
5028 result
->tag
= NO_TAG
;
5033 ** Set the name of a range set's ranges. Returns
5034 ** true if the rangeset is valid.
5037 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
5038 int nArgs
, DataValue
*result
, char **errMsg
)
5040 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5041 textBuffer
*buffer
= window
->buffer
;
5042 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5048 return wrongNArgsErr(errMsg
);
5051 if (!readIntArg(argList
[0], &label
, errMsg
)
5052 || !RangesetLabelOK(label
)) {
5053 M_FAILURE("First parameter is an invalid rangeset label in %s");
5056 if (rangesetTable
== NULL
) {
5057 M_FAILURE("Rangeset does not exist in %s");
5060 rangeset
= RangesetFetch(rangesetTable
, label
);
5061 if (rangeset
== NULL
) {
5062 M_FAILURE("Rangeset does not exist in %s");
5066 if (rangeset
!= NULL
) {
5067 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
)) {
5068 M_FAILURE("Second parameter is not a valid name string in %s");
5072 RangesetAssignName(rangeset
, name
);
5075 result
->tag
= NO_TAG
;
5080 ** Change a range's modification response. Returns true if the rangeset is
5081 ** valid and the response type name is valid.
5084 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
5085 int nArgs
, DataValue
*result
, char **errMsg
)
5087 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5088 textBuffer
*buffer
= window
->buffer
;
5089 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5091 char *update_fn_name
;
5095 if (nArgs
< 1 || nArgs
> 2) {
5096 return wrongNArgsErr(errMsg
);
5099 if (!readIntArg(argList
[0], &label
, errMsg
)
5100 || !RangesetLabelOK(label
)) {
5101 M_FAILURE("First parameter is an invalid rangeset label in %s");
5104 if (rangesetTable
== NULL
) {
5105 M_FAILURE("Rangeset does not exist in %s");
5108 rangeset
= RangesetFetch(rangesetTable
, label
);
5109 if (rangeset
== NULL
) {
5110 M_FAILURE("Rangeset does not exist in %s");
5113 update_fn_name
= "";
5114 if (rangeset
!= NULL
) {
5116 if (!readStringArg(argList
[1], &update_fn_name
, stringStorage
[0], errMsg
)) {
5117 M_FAILURE("Second parameter is not a string in %s");
5122 ok
= RangesetChangeModifyResponse(rangeset
, update_fn_name
);
5125 M_FAILURE("Second parameter is not a valid mode in %s");
5129 result
->tag
= NO_TAG
;
5133 /* -------------------------------------------------------------------------- */
5137 ** Routines to get details directly from the window.
5141 ** Sets up an array containing information about a style given its name or
5142 ** a buffer position (bufferPos >= 0) and its highlighting pattern code
5144 ** From the name we obtain:
5145 ** ["color"] Foreground color name of style
5146 ** ["background"] Background color name of style if specified
5147 ** ["bold"] '1' if style is bold, '0' otherwise
5148 ** ["italic"] '1' if style is italic, '0' otherwise
5149 ** Given position and pattern code we obtain:
5150 ** ["rgb"] RGB representation of foreground color of style
5151 ** ["back_rgb"] RGB representation of background color of style
5152 ** ["extent"] Forward distance from position over which style applies
5153 ** We only supply the style name if the includeName parameter is set:
5154 ** ["style"] Name of style
5157 static int fillStyleResult(DataValue
*result
, char **errMsg
,
5158 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
5159 Boolean includeName
, int patCode
, int bufferPos
)
5162 char colorValue
[20];
5165 /* initialize array */
5166 result
->tag
= ARRAY_TAG
;
5167 result
->val
.arrayPtr
= ArrayNew();
5169 /* the following array entries will be strings */
5170 DV
.tag
= STRING_TAG
;
5173 /* insert style name */
5174 if (preallocatedStyleName
) {
5175 DV
.val
.str
.rep
= styleName
;
5176 DV
.val
.str
.len
= strlen(styleName
);
5179 AllocNStringCpy(&DV
.val
.str
, styleName
);
5181 M_STR_ALLOC_ASSERT(DV
);
5182 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5183 M_ARRAY_INSERT_FAILURE();
5187 /* insert color name */
5188 AllocNStringCpy(&DV
.val
.str
, ColorOfNamedStyle(styleName
));
5189 M_STR_ALLOC_ASSERT(DV
);
5190 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &DV
)) {
5191 M_ARRAY_INSERT_FAILURE();
5194 /* Prepare array element for color value
5195 (only possible if we pass through the dynamic highlight pattern tables
5196 in other words, only if we have a pattern code) */
5198 HighlightColorValueOfCode(window
, patCode
, &r
, &g
, &b
);
5199 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5200 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5201 M_STR_ALLOC_ASSERT(DV
);
5202 if (!ArrayInsert(result
, PERM_ALLOC_STR("rgb"), &DV
)) {
5203 M_ARRAY_INSERT_FAILURE();
5207 /* Prepare array element for background color name */
5208 AllocNStringCpy(&DV
.val
.str
, BgColorOfNamedStyle(styleName
));
5209 M_STR_ALLOC_ASSERT(DV
);
5210 if (!ArrayInsert(result
, PERM_ALLOC_STR("background"), &DV
)) {
5211 M_ARRAY_INSERT_FAILURE();
5214 /* Prepare array element for background color value
5215 (only possible if we pass through the dynamic highlight pattern tables
5216 in other words, only if we have a pattern code) */
5218 GetHighlightBGColorOfCode(window
, patCode
, &r
, &g
, &b
);
5219 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5220 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5221 M_STR_ALLOC_ASSERT(DV
);
5222 if (!ArrayInsert(result
, PERM_ALLOC_STR("back_rgb"), &DV
)) {
5223 M_ARRAY_INSERT_FAILURE();
5227 /* the following array entries will be integers */
5230 /* Put boldness value in array */
5231 DV
.val
.n
= FontOfNamedStyleIsBold(styleName
);
5232 if (!ArrayInsert(result
, PERM_ALLOC_STR("bold"), &DV
)) {
5233 M_ARRAY_INSERT_FAILURE();
5236 /* Put italicity value in array */
5237 DV
.val
.n
= FontOfNamedStyleIsItalic(styleName
);
5238 if (!ArrayInsert(result
, PERM_ALLOC_STR("italic"), &DV
)) {
5239 M_ARRAY_INSERT_FAILURE();
5242 if (bufferPos
>= 0) {
5244 const char *styleNameNotUsed
= NULL
;
5245 DV
.val
.n
= StyleLengthOfCodeFromPos(window
, bufferPos
, &styleNameNotUsed
);
5246 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5247 M_ARRAY_INSERT_FAILURE();
5254 ** Returns an array containing information about the style of name $1
5255 ** ["color"] Foreground color name of style
5256 ** ["background"] Background color name of style if specified
5257 ** ["bold"] '1' if style is bold, '0' otherwise
5258 ** ["italic"] '1' if style is italic, '0' otherwise
5261 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5262 DataValue
*result
, char **errMsg
)
5264 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5267 /* Validate number of arguments */
5269 return wrongNArgsErr(errMsg
);
5272 /* Prepare result */
5273 result
->tag
= ARRAY_TAG
;
5274 result
->val
.arrayPtr
= NULL
;
5276 if (!readStringArg(argList
[0], &styleName
, stringStorage
[0], errMsg
)) {
5277 M_FAILURE("First parameter is not a string in %s");
5280 if (!NamedStyleExists(styleName
)) {
5281 /* if the given name is invalid we just return an empty array. */
5285 return fillStyleResult(result
, errMsg
, window
,
5286 styleName
, (argList
[0].tag
== STRING_TAG
), False
, 0, -1);
5290 ** Returns an array containing information about the style of position $1
5291 ** ["style"] Name of style
5292 ** ["color"] Foreground color name of style
5293 ** ["background"] Background color name of style if specified
5294 ** ["bold"] '1' if style is bold, '0' otherwise
5295 ** ["italic"] '1' if style is italic, '0' otherwise
5296 ** ["rgb"] RGB representation of foreground color of style
5297 ** ["back_rgb"] RGB representation of background color of style
5298 ** ["extent"] Forward distance from position over which style applies
5301 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5302 DataValue
*result
, char **errMsg
)
5306 textBuffer
*buf
= window
->buffer
;
5308 /* Validate number of arguments */
5310 return wrongNArgsErr(errMsg
);
5313 /* Prepare result */
5314 result
->tag
= ARRAY_TAG
;
5315 result
->val
.arrayPtr
= NULL
;
5317 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5321 /* Verify sane buffer position */
5322 if ((bufferPos
< 0) || (bufferPos
>= buf
->length
)) {
5323 /* If the position is not legal, we cannot guess anything about
5324 the style, so we return an empty array. */
5328 /* Determine pattern code */
5329 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5331 /* if there is no pattern we just return an empty array. */
5335 return fillStyleResult(result
, errMsg
, window
,
5336 HighlightStyleOfCode(window
, patCode
), False
, True
, patCode
, bufferPos
);
5340 ** Sets up an array containing information about a pattern given its name or
5341 ** a buffer position (bufferPos >= 0).
5342 ** From the name we obtain:
5343 ** ["style"] Name of style
5344 ** ["extent"] Forward distance from position over which style applies
5345 ** We only supply the pattern name if the includeName parameter is set:
5346 ** ["pattern"] Name of pattern
5349 static int fillPatternResult(DataValue
*result
, char **errMsg
,
5350 WindowInfo
*window
, char *patternName
, Boolean preallocatedPatternName
,
5351 Boolean includeName
, char* styleName
, int bufferPos
)
5355 /* initialize array */
5356 result
->tag
= ARRAY_TAG
;
5357 result
->val
.arrayPtr
= ArrayNew();
5359 /* the following array entries will be strings */
5360 DV
.tag
= STRING_TAG
;
5363 /* insert pattern name */
5364 if (preallocatedPatternName
) {
5365 DV
.val
.str
.rep
= patternName
;
5366 DV
.val
.str
.len
= strlen(patternName
);
5369 AllocNStringCpy(&DV
.val
.str
, patternName
);
5371 M_STR_ALLOC_ASSERT(DV
);
5372 if (!ArrayInsert(result
, PERM_ALLOC_STR("pattern"), &DV
)) {
5373 M_ARRAY_INSERT_FAILURE();
5377 /* insert style name */
5378 AllocNStringCpy(&DV
.val
.str
, styleName
);
5379 M_STR_ALLOC_ASSERT(DV
);
5380 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5381 M_ARRAY_INSERT_FAILURE();
5384 /* the following array entries will be integers */
5387 if (bufferPos
>= 0) {
5390 DV
.val
.n
= HighlightLengthOfCodeFromPos(window
, bufferPos
, &checkCode
);
5391 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5392 M_ARRAY_INSERT_FAILURE();
5400 ** Returns an array containing information about a highlighting pattern. The
5401 ** single parameter contains the pattern name for which this information is
5403 ** The returned array looks like this:
5404 ** ["style"] Name of style
5406 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5407 DataValue
*result
, char **errMsg
)
5409 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5410 char *patternName
= NULL
;
5411 highlightPattern
*pattern
;
5413 /* Begin of building the result. */
5414 result
->tag
= ARRAY_TAG
;
5415 result
->val
.arrayPtr
= NULL
;
5417 /* Validate number of arguments */
5419 return wrongNArgsErr(errMsg
);
5422 if (!readStringArg(argList
[0], &patternName
, stringStorage
[0], errMsg
)) {
5423 M_FAILURE("First parameter is not a string in %s");
5426 pattern
= FindPatternOfWindow(window
, patternName
);
5427 if (pattern
== NULL
) {
5428 /* The pattern's name is unknown. */
5432 return fillPatternResult(result
, errMsg
, window
, patternName
,
5433 (argList
[0].tag
== STRING_TAG
), False
, pattern
->style
, -1);
5437 ** Returns an array containing information about the highlighting pattern
5438 ** applied at a given position, passed as the only parameter.
5439 ** The returned array looks like this:
5440 ** ["pattern"] Name of pattern
5441 ** ["style"] Name of style
5442 ** ["extent"] Distance from position over which this pattern applies
5444 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5445 DataValue
*result
, char **errMsg
)
5448 textBuffer
*buffer
= window
->buffer
;
5451 /* Begin of building the result. */
5452 result
->tag
= ARRAY_TAG
;
5453 result
->val
.arrayPtr
= NULL
;
5455 /* Validate number of arguments */
5457 return wrongNArgsErr(errMsg
);
5460 /* The most straightforward case: Get a pattern, style and extent
5461 for a buffer position. */
5462 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5466 /* Verify sane buffer position
5467 * You would expect that buffer->length would be among the sane
5468 * positions, but we have n characters and n+1 buffer positions. */
5469 if ((bufferPos
< 0) || (bufferPos
>= buffer
->length
)) {
5470 /* If the position is not legal, we cannot guess anything about
5471 the highlighting pattern, so we return an empty array. */
5475 /* Determine the highlighting pattern used */
5476 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5478 /* if there is no highlighting pattern we just return an empty array. */
5482 return fillPatternResult(result
, errMsg
, window
,
5483 HighlightNameOfCode(window
, patCode
), False
, True
,
5484 HighlightStyleOfCode(window
, patCode
), bufferPos
);
5487 static int wrongNArgsErr(char **errMsg
)
5489 *errMsg
= "Wrong number of arguments to function %s";
5493 static int tooFewArgsErr(char **errMsg
)
5495 *errMsg
= "Too few arguments to function %s";
5500 ** strCaseCmp compares its arguments and returns 0 if the two strings
5501 ** are equal IGNORING case differences. Otherwise returns 1 or -1
5502 ** depending on relative comparison.
5504 static int strCaseCmp(char *str1
, char *str2
)
5508 for (c1
= str1
, c2
= str2
;
5509 (*c1
!= '\0' && *c2
!= '\0')
5510 && toupper((unsigned char)*c1
) == toupper((unsigned char)*c2
);
5515 if (((unsigned char)toupper((unsigned char)*c1
))
5516 > ((unsigned char)toupper((unsigned char)*c2
)))
5519 } else if (((unsigned char)toupper((unsigned char)*c1
))
5520 < ((unsigned char)toupper((unsigned char)*c2
)))
5530 ** Get an integer value from a tagged DataValue structure. Return True
5531 ** if conversion succeeded, and store result in *result, otherwise
5532 ** return False with an error message in *errMsg.
5534 static int readIntArg(DataValue dv
, int *result
, char **errMsg
)
5538 if (dv
.tag
== INT_TAG
) {
5541 } else if (dv
.tag
== STRING_TAG
) {
5542 for (c
=dv
.val
.str
.rep
; *c
!= '\0'; c
++) {
5543 if (!(isdigit((unsigned char)*c
) || *c
== ' ' || *c
== '\t')) {
5547 sscanf(dv
.val
.str
.rep
, "%d", result
);
5552 *errMsg
= "%s called with non-integer argument";
5557 ** Get an string value from a tagged DataValue structure. Return True
5558 ** if conversion succeeded, and store result in *result, otherwise
5559 ** return False with an error message in *errMsg. If an integer value
5560 ** is converted, write the string in the space provided by "stringStorage",
5561 ** which must be large enough to handle ints of the maximum size.
5563 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
5566 if (dv
.tag
== STRING_TAG
) {
5567 *result
= dv
.val
.str
.rep
;
5569 } else if (dv
.tag
== INT_TAG
) {
5570 sprintf(stringStorage
, "%d", dv
.val
.n
);
5571 *result
= stringStorage
;
5574 *errMsg
= "%s called with unknown object";