1 static const char CVSID
[] = "$Id: macro.c,v 1.86 2004/06/10 17:01:26 edg 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 *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
39 #include "preferences.h"
40 #include "interpret.h"
45 #include "smartIndent.h"
47 #include "selection.h"
51 #include "../util/DialogF.h"
52 #include "../util/misc.h"
53 #include "../util/fileUtils.h"
54 #include "../util/utils.h"
55 #include "highlight.h"
56 #include "highlightData.h"
65 #include "../util/VMSparam.h"
70 #include <sys/types.h>
73 #include <sys/param.h>
78 #include <X11/Intrinsic.h>
79 #include <X11/keysym.h>
81 #include <Xm/CutPaste.h>
83 #include <Xm/RowColumn.h>
84 #include <Xm/LabelG.h>
86 #include <Xm/ToggleB.h>
87 #include <Xm/DialogS.h>
88 #include <Xm/MessageB.h>
89 #include <Xm/SelectioB.h>
92 #include <Xm/Separator.h>
98 /* Maximum number of actions in a macro and args in
99 an action (to simplify the reader) */
100 #define MAX_MACRO_ACTIONS 1024
101 #define MAX_ACTION_ARGS 40
103 /* How long to wait (msec) before putting up Macro Command banner */
104 #define BANNER_WAIT_TIME 6000
106 /* The following definitions cause an exit from the macro with a message */
107 /* added if (1) to remove compiler warnings on solaris */
108 #define M_FAILURE(s) do { *errMsg = s; if (1) return False; } while (0)
109 #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)
110 #define M_ARRAY_INSERT_FAILURE() M_FAILURE("array element failed to insert: %s")
112 /* Data attached to window during shell command execution with
113 information for controling and communicating with the process */
115 XtIntervalId bannerTimeoutID
;
116 XtWorkProcId continueWorkProcID
;
118 char closeOnCompletion
;
120 RestartData
*context
;
124 /* Widgets and global data for Repeat dialog */
126 WindowInfo
*forWindow
;
128 Widget shell
, repeatText
, lastCmdToggle
;
129 Widget inSelToggle
, toEndToggle
;
132 static void cancelLearn(void);
133 static void runMacro(WindowInfo
*window
, Program
*prog
);
134 static void finishMacroCmdExecution(WindowInfo
*window
);
135 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
);
136 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
137 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
);
138 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
);
139 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
140 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
141 XEvent
*event
, String
*params
, Cardinal
*numParams
);
142 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
143 XEvent
*event
, String
*params
, Cardinal
*numParams
);
144 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
145 String
*params
, Cardinal numParams
);
146 static int isMouseAction(const char *action
);
147 static int isRedundantAction(const char *action
);
148 static int isIgnoredAction(const char *action
);
149 static int readCheckMacroString(Widget dialogParent
, char *string
,
150 WindowInfo
*runWindow
, const char *errIn
, char **errPos
);
151 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
);
152 static Boolean
continueWorkProc(XtPointer clientData
);
153 static int escapeStringChars(char *fromString
, char *toString
);
154 static int escapedStringLength(char *string
);
155 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
156 DataValue
*result
, char **errMsg
);
157 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
158 DataValue
*result
, char **errMsg
);
159 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
160 DataValue
*result
, char **errMsg
);
161 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
162 DataValue
*result
, char **errMsg
);
163 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
164 DataValue
*result
, char **errMsg
);
165 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
166 DataValue
*result
, char **errMsg
);
167 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
168 DataValue
*result
, char **errMsg
);
169 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
170 DataValue
*result
, char **errMsg
);
171 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
172 DataValue
*result
, char **errMsg
);
173 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
174 DataValue
*result
, char **errMsg
);
175 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
176 DataValue
*result
, char **errMsg
);
177 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
178 DataValue
*result
, char **errMsg
);
179 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
180 DataValue
*result
, char **errMsg
);
181 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
182 DataValue
*result
, char **errMsg
);
183 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
184 DataValue
*result
, char **errMsg
);
185 static int writeOrAppendFile(int append
, WindowInfo
*window
,
186 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
);
187 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
188 DataValue
*result
, char **errMsg
);
189 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
190 DataValue
*result
, char **errMsg
);
191 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
192 DataValue
*result
, char **errMsg
);
193 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
194 DataValue
*result
, char **errMsg
);
195 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
196 DataValue
*result
, char **errMsg
);
197 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
198 DataValue
*result
, char **errMsg
);
199 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
200 DataValue
*result
, char **errMsg
);
201 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
202 DataValue
*result
, char **errMsg
);
203 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
204 DataValue
*result
, char **errMsg
);
205 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
206 DataValue
*result
, char **errMsg
);
207 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
208 DataValue
*result
, char **errMsg
);
209 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
210 DataValue
*result
, char **errMsg
);
211 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
212 DataValue
*result
, char **errMsg
);
213 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
214 DataValue
*result
, char **errMsg
);
215 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
216 DataValue
*result
, char **errMsg
);
217 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
);
218 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
);
219 #ifdef LESSTIF_VERSION
220 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
222 #endif /* LESSTIF_VERSION */
223 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
224 DataValue
*result
, char **errMsg
);
225 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
227 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
229 #ifdef LESSTIF_VERSION
230 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
232 #endif /* LESSTIF_VERSION */
233 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
234 DataValue
*result
, char **errMsg
);
235 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
236 DataValue
*result
, char **errMsg
);
238 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
239 DataValue
*result
, char **errMsg
);
240 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
242 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
245 #ifdef LESSTIF_VERSION
246 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
248 #endif /* LESSTIF_VERSION */
249 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
250 DataValue
*result
, char **errMsg
);
251 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
252 DataValue
*result
, char **errMsg
);
254 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
255 int nArgs, DataValue *result, char **errMsg);
257 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
258 DataValue
*result
, char **errMsg
);
259 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
260 DataValue
*result
, char **errMsg
);
261 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
262 DataValue
*result
, char **errMsg
);
263 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
264 DataValue
*result
, char **errMsg
);
265 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
266 DataValue
*result
, char **errMsg
);
267 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
268 DataValue
*result
, char **errMsg
);
269 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
270 DataValue
*result
, char **errMsg
);
271 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
272 DataValue
*result
, char **errMsg
);
273 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
274 DataValue
*result
, char **errMsg
);
275 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
276 DataValue
*result
, char **errMsg
);
277 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
278 DataValue
*result
, char **errMsg
);
279 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
280 DataValue
*result
, char **errMsg
);
281 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
282 DataValue
*result
, char **errMsg
);
283 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
284 DataValue
*result
, char **errMsg
);
285 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
286 DataValue
*result
, char **errMsg
);
287 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
288 DataValue
*result
, char **errMsg
);
289 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
290 DataValue
*result
, char **errMsg
);
291 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
292 DataValue
*result
, char **errMsg
);
293 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
294 DataValue
*result
, char **errMsg
);
295 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
296 DataValue
*result
, char **errMsg
);
297 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
298 DataValue
*result
, char **errMsg
);
299 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
300 DataValue
*result
, char **errMsg
);
301 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
302 DataValue
*result
, char **errMsg
);
303 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
304 DataValue
*result
, char **errMsg
);
305 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
306 DataValue
*result
, char **errMsg
);
307 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
308 DataValue
*result
, char **errMsg
);
309 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
310 DataValue
*result
, char **errMsg
);
311 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
312 DataValue
*result
, char **errMsg
);
313 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
314 DataValue
*result
, char **errMsg
);
315 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
316 DataValue
*result
, char **errMsg
);
317 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
318 DataValue
*result
, char **errMsg
);
319 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
320 DataValue
*result
, char **errMsg
);
321 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
322 DataValue
*result
, char **errMsg
);
323 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
324 DataValue
*result
, char **errMsg
);
325 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
326 DataValue
*result
, char **errMsg
);
327 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
328 DataValue
*result
, char **errMsg
);
329 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
330 DataValue
*result
, char **errMsg
);
331 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
332 DataValue
*result
, char **errMsg
);
333 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
334 DataValue
*result
, char **errMsg
);
335 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
336 DataValue
*result
, char **errMsg
);
337 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
338 DataValue
*result
, char **errMsg
);
339 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
340 DataValue
*result
, char **errMsg
);
341 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
342 DataValue
*result
, char **errMsg
);
343 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
344 DataValue
*result
, char **errMsg
);
345 static int readSearchArgs(DataValue
*argList
, int nArgs
, int*searchDirection
,
346 int *searchType
, int *wrap
, char **errMsg
);
347 static int wrongNArgsErr(char **errMsg
);
348 static int tooFewArgsErr(char **errMsg
);
349 static int strCaseCmp(char *str1
, char *str2
);
350 static int readIntArg(DataValue dv
, int *result
, char **errMsg
);
351 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
354 static int backlightStringMV(WindowInfo *window, DataValue *argList,
355 int nArgs, DataValue *result, char **errMsg);
357 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
,
358 int nArgs
, DataValue
*result
, char **errMsg
);
359 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
360 DataValue
*result
, char **errMsg
);
361 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
362 DataValue
*result
, char **errMsg
);
363 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
364 DataValue
*result
, char **errMsg
);
365 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
366 DataValue
*result
, char **errMsg
);
367 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
368 DataValue
*result
, char **errMsg
);
369 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
370 DataValue
*result
, char **errMsg
);
371 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
372 DataValue
*result
, char **errMsg
);
373 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
374 DataValue
*result
, char **errMsg
);
375 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
376 int nArgs
, DataValue
*result
, char **errMsg
);
377 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
378 int nArgs
, DataValue
*result
, char **errMsg
);
379 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
380 int nArgs
, DataValue
*result
, char **errMsg
);
381 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
382 int nArgs
, DataValue
*result
, char **errMsg
);
384 static int fillPatternResult(DataValue
*result
, char **errMsg
, WindowInfo
*window
,
385 char *patternName
, Boolean preallocatedPatternName
, Boolean includeName
,
386 char *styleName
, int bufferPos
);
387 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
388 DataValue
*result
, char **errMsg
);
389 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
390 DataValue
*result
, char **errMsg
);
392 static int fillStyleResult(DataValue
*result
, char **errMsg
,
393 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
394 Boolean includeName
, int patCode
, int bufferPos
);
395 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
396 DataValue
*result
, char **errMsg
);
397 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
398 DataValue
*result
, char **errMsg
);
400 /* Built-in subroutines and variables for the macro language */
401 static BuiltInSubr MacroSubrs
[] = {lengthMS
, getRangeMS
, tPrintMS
,
402 dialogMS
, stringDialogMS
, replaceRangeMS
, replaceSelectionMS
,
403 setCursorPosMS
, getCharacterMS
, minMS
, maxMS
, searchMS
,
404 searchStringMS
, substringMS
, replaceSubstringMS
, readFileMS
,
405 writeFileMS
, appendFileMS
, beepMS
, getSelectionMS
, validNumberMS
,
406 replaceInStringMS
, selectMS
, selectRectangleMS
, focusWindowMS
,
407 shellCmdMS
, stringToClipboardMS
, clipboardToStringMS
, toupperMS
,
408 tolowerMS
, listDialogMS
, getenvMS
,
409 stringCompareMS
, splitMS
, calltipMS
, killCalltipMS
,
410 /* DISABLED for 5.4 setBacklightStringMS,*/
411 rangesetCreateMS
, rangesetDestroyMS
,
412 rangesetAddMS
, rangesetSubtractMS
, rangesetInvertMS
,
413 rangesetInfoMS
, rangesetRangeMS
, rangesetIncludesPosMS
,
414 rangesetSetColorMS
, rangesetSetNameMS
, rangesetSetModeMS
,
416 getPatternByNameMS
, getPatternAtPosMS
,
417 getStyleByNameMS
, getStyleAtPosMS
419 #define N_MACRO_SUBRS (sizeof MacroSubrs/sizeof *MacroSubrs)
420 static const char *MacroSubrNames
[N_MACRO_SUBRS
] = {"length", "get_range", "t_print",
421 "dialog", "string_dialog", "replace_range", "replace_selection",
422 "set_cursor_pos", "get_character", "min", "max", "search",
423 "search_string", "substring", "replace_substring", "read_file",
424 "write_file", "append_file", "beep", "get_selection", "valid_number",
425 "replace_in_string", "select", "select_rectangle", "focus_window",
426 "shell_command", "string_to_clipboard", "clipboard_to_string",
427 "toupper", "tolower", "list_dialog", "getenv",
428 "string_compare", "split", "calltip", "kill_calltip",
429 /* DISABLED for 5.4 "set_backlight_string", */
430 "rangeset_create", "rangeset_destroy",
431 "rangeset_add", "rangeset_subtract", "rangeset_invert",
432 "rangeset_info", "rangeset_range", "rangeset_includes",
433 "rangeset_set_color", "rangeset_set_name", "rangeset_set_mode",
434 "rangeset_get_by_name",
435 "get_pattern_by_name", "get_pattern_at_pos",
436 "get_style_by_name", "get_style_at_pos"
438 static BuiltInSubr SpecialVars
[] = {cursorMV
, lineMV
, columnMV
,
439 fileNameMV
, filePathMV
, lengthMV
, selectionStartMV
, selectionEndMV
,
440 selectionLeftMV
, selectionRightMV
, wrapMarginMV
, tabDistMV
,
441 emTabDistMV
, useTabsMV
, languageModeMV
, modifiedMV
,
442 statisticsLineMV
, incSearchLineMV
, showLineNumbersMV
,
443 autoIndentMV
, wrapTextMV
, highlightSyntaxMV
,
444 makeBackupCopyMV
, incBackupMV
, showMatchingMV
,
445 overTypeModeMV
, readOnlyMV
, lockedMV
, fileFormatMV
,
446 fontNameMV
, fontNameItalicMV
,
447 fontNameBoldMV
, fontNameBoldItalicMV
, subscriptSepMV
,
448 minFontWidthMV
, maxFontWidthMV
, topLineMV
, numDisplayLinesMV
,
449 displayWidthMV
, activePaneMV
, nPanesMV
, emptyArrayMV
,
450 serverNameMV
, calltipIDMV
,
451 /* DISABLED for 5.4 backlightStringMV, */
454 #define N_SPECIAL_VARS (sizeof SpecialVars/sizeof *SpecialVars)
455 static const char *SpecialVarNames
[N_SPECIAL_VARS
] = {"$cursor", "$line", "$column",
456 "$file_name", "$file_path", "$text_length", "$selection_start",
457 "$selection_end", "$selection_left", "$selection_right",
458 "$wrap_margin", "$tab_dist", "$em_tab_dist", "$use_tabs",
459 "$language_mode", "$modified",
460 "$statistics_line", "$incremental_search_line", "$show_line_numbers",
461 "$auto_indent", "$wrap_text", "$highlight_syntax",
462 "$make_backup_copy", "$incremental_backup", "$show_matching",
463 "$overtype_mode", "$read_only", "$locked", "$file_format",
464 "$font_name", "$font_name_italic",
465 "$font_name_bold", "$font_name_bold_italic", "$sub_sep",
466 "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
467 "$display_width", "$active_pane", "$n_panes", "$empty_array",
468 "$server_name", "$calltip_ID",
469 /* DISABLED for 5.4 "$backlight_string", */
473 /* Global symbols for returning values from built-in functions */
474 #define N_RETURN_GLOBALS 5
475 enum retGlobalSyms
{STRING_DIALOG_BUTTON
, SEARCH_END
, READ_STATUS
,
476 SHELL_CMD_STATUS
, LIST_DIALOG_BUTTON
};
477 static const char *ReturnGlobalNames
[N_RETURN_GLOBALS
] = {"$string_dialog_button",
478 "$search_end", "$read_status", "$shell_cmd_status",
479 "$list_dialog_button"};
480 static Symbol
*ReturnGlobals
[N_RETURN_GLOBALS
];
482 /* List of actions not useful when learning a macro sequence (also see below) */
483 static char* IgnoredActions
[] = {"focusIn", "focusOut"};
485 /* List of actions intended to be attached to mouse buttons, which the user
486 must be warned can't be recorded in a learn/replay sequence */
487 static const char* MouseActions
[] = {"grab_focus", "extend_adjust", "extend_start",
488 "extend_end", "secondary_or_drag_adjust", "secondary_adjust",
489 "secondary_or_drag_start", "secondary_start", "move_destination",
490 "move_to", "move_to_or_end_drag", "copy_to", "copy_to_or_end_drag",
491 "exchange", "process_bdrag", "mouse_pan"};
493 /* List of actions to not record because they
494 generate further actions, more suitable for recording */
495 static const char* RedundantActions
[] = {"open_dialog", "save_as_dialog",
496 "revert_to_saved_dialog", "include_file_dialog", "load_macro_file_dialog",
497 "load_tags_file_dialog", "find_dialog", "replace_dialog",
498 "goto_line_number_dialog", "mark_dialog", "goto_mark_dialog",
499 "control_code_dialog", "filter_selection_dialog", "execute_command_dialog",
500 "repeat_dialog", "start_incremental_find"};
502 /* The last command executed (used by the Repeat command) */
503 static char *LastCommand
= NULL
;
505 /* The current macro to execute on Replay command */
506 static char *ReplayMacro
= NULL
;
508 /* Buffer where macro commands are recorded in Learn mode */
509 static textBuffer
*MacroRecordBuf
= NULL
;
511 /* Action Hook id for recording actions for Learn mode */
512 static XtActionHookId MacroRecordActionHook
= 0;
514 /* Window where macro recording is taking place */
515 static WindowInfo
*MacroRecordWindow
= NULL
;
517 /* Arrays for translating escape characters in escapeStringChars */
518 static char ReplaceChars
[] = "\\\"ntbrfav";
519 static char EscapeChars
[] = "\\\"\n\t\b\r\f\a\v";
522 ** Install built-in macro subroutines and special variables for accessing
523 ** editor information
525 void RegisterMacroSubroutines(void)
527 static DataValue subrPtr
= {NO_TAG
, {0}}, noValue
= {NO_TAG
, {0}};
530 /* Install symbols for built-in routines and variables, with pointers
531 to the appropriate c routines to do the work */
532 for (i
=0; i
<N_MACRO_SUBRS
; i
++) {
533 subrPtr
.val
.subr
= MacroSubrs
[i
];
534 InstallSymbol(MacroSubrNames
[i
], C_FUNCTION_SYM
, subrPtr
);
536 for (i
=0; i
<N_SPECIAL_VARS
; i
++) {
537 subrPtr
.val
.subr
= SpecialVars
[i
];
538 InstallSymbol(SpecialVarNames
[i
], PROC_VALUE_SYM
, subrPtr
);
541 /* Define global variables used for return values, remember their
542 locations so they can be set without a LookupSymbol call */
543 for (i
=0; i
<N_RETURN_GLOBALS
; i
++)
544 ReturnGlobals
[i
] = InstallSymbol(ReturnGlobalNames
[i
], GLOBAL_SYM
,
548 #define MAX_LEARN_MSG_LEN ((2 * MAX_ACCEL_LEN) + 60)
549 void BeginLearn(WindowInfo
*window
)
557 char message
[MAX_LEARN_MSG_LEN
];
559 /* If we're already in learn mode, return */
560 if (MacroRecordActionHook
!= 0)
563 /* dim the inappropriate menus and items, and undim finish and cancel */
564 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
565 if (!IsTopDocument(win
))
567 XtSetSensitive(win
->learnItem
, False
);
569 SetSensitive(window
, window
->finishLearnItem
, True
);
570 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
571 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
573 SetSensitive(window
, window
->cancelMacroItem
, True
);
575 /* Mark the window where learn mode is happening */
576 MacroRecordWindow
= window
;
578 /* Allocate a text buffer for accumulating the macro strings */
579 MacroRecordBuf
= BufCreate();
581 /* Add the action hook for recording the actions */
582 MacroRecordActionHook
=
583 XtAppAddActionHook(XtWidgetToApplicationContext(window
->shell
),
584 learnActionHook
, window
);
586 /* Extract accelerator texts from menu PushButtons */
587 XtVaGetValues(window
->finishLearnItem
, XmNacceleratorText
, &xmFinish
, NULL
);
588 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
590 /* Translate Motif strings to char* */
591 cFinish
= GetXmStringText(xmFinish
);
592 cCancel
= GetXmStringText(xmCancel
);
594 /* Free Motif Strings */
595 XmStringFree(xmFinish
);
596 XmStringFree(xmCancel
);
599 if (cFinish
[0] == '\0') {
600 if (cCancel
[0] == '\0') {
601 strncpy(message
, "Learn Mode -- Use menu to finish or cancel",
603 message
[MAX_LEARN_MSG_LEN
- 1] = '\0';
607 "Learn Mode -- Use menu to finish, press %s to cancel",
612 if (cCancel
[0] == '\0') {
614 "Learn Mode -- Press %s to finish, use menu to cancel",
620 "Learn Mode -- Press %s to finish, %s to cancel",
630 /* Put up the learn-mode banner */
631 SetModeMessage(window
, message
);
634 void AddLastCommandActionHook(XtAppContext context
)
636 XtAppAddActionHook(context
, lastActionHook
, NULL
);
639 void FinishLearn(void)
643 /* If we're not in learn mode, return */
644 if (MacroRecordActionHook
== 0)
647 /* Remove the action hook */
648 XtRemoveActionHook(MacroRecordActionHook
);
649 MacroRecordActionHook
= 0;
651 /* Free the old learn/replay sequence */
652 if (ReplayMacro
!= NULL
)
655 /* Store the finished action for the replay menu item */
656 ReplayMacro
= BufGetAll(MacroRecordBuf
);
658 /* Free the buffer used to accumulate the macro sequence */
659 BufFree(MacroRecordBuf
);
661 /* Undim the menu items dimmed during learn */
662 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
663 if (!IsTopDocument(win
))
665 XtSetSensitive(win
->learnItem
, True
);
667 if (IsTopDocument(MacroRecordWindow
)) {
668 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
669 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
672 /* Undim the replay and paste-macro buttons */
673 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
674 if (!IsTopDocument(win
))
676 XtSetSensitive(win
->replayItem
, True
);
678 DimPasteReplayBtns(True
);
680 /* Clear learn-mode banner */
681 ClearModeMessage(MacroRecordWindow
);
685 ** Cancel Learn mode, or macro execution (they're bound to the same menu item)
687 void CancelMacroOrLearn(WindowInfo
*window
)
689 if (MacroRecordActionHook
!= 0)
691 else if (window
->macroCmdData
!= NULL
)
692 AbortMacroCommand(window
);
695 static void cancelLearn(void)
699 /* If we're not in learn mode, return */
700 if (MacroRecordActionHook
== 0)
703 /* Remove the action hook */
704 XtRemoveActionHook(MacroRecordActionHook
);
705 MacroRecordActionHook
= 0;
707 /* Free the macro under construction */
708 BufFree(MacroRecordBuf
);
710 /* Undim the menu items dimmed during learn */
711 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
712 if (!IsTopDocument(win
))
714 XtSetSensitive(win
->learnItem
, True
);
716 if (IsTopDocument(MacroRecordWindow
)) {
717 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
718 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
721 /* Clear learn-mode banner */
722 ClearModeMessage(MacroRecordWindow
);
726 ** Execute the learn/replay sequence stored in "window"
728 void Replay(WindowInfo
*window
)
731 char *errMsg
, *stoppedAt
;
733 /* Verify that a replay macro exists and it's not empty and that */
734 /* we're not already running a macro */
735 if (ReplayMacro
!= NULL
&&
736 ReplayMacro
[0] != 0 &&
737 window
->macroCmdData
== NULL
) {
738 /* Parse the replay macro (it's stored in text form) and compile it into
739 an executable program "prog" */
740 prog
= ParseMacro(ReplayMacro
, &errMsg
, &stoppedAt
);
743 "NEdit internal error, learn/replay macro syntax error: %s\n",
748 /* run the executable program */
749 runMacro(window
, prog
);
754 ** Read the initial NEdit macro file if one exists.
756 void ReadMacroInitFile(WindowInfo
*window
)
758 const char* autoloadName
= GetRCFileName(AUTOLOAD_NM
);
759 static int initFileLoaded
= False
;
761 /* GetRCFileName() might return NULL if an error occurs during
762 creation of the preference file directory. */
763 if (autoloadName
!= NULL
&& !initFileLoaded
)
765 ReadMacroFile(window
, autoloadName
, False
);
766 initFileLoaded
= True
;
771 ** Read an NEdit macro file. Extends the syntax of the macro parser with
772 ** define keyword, and allows intermixing of defines with immediate actions.
774 int ReadMacroFile(WindowInfo
*window
, const char *fileName
, int warnNotExist
)
779 fileString
= ReadAnyTextFile(fileName
);
780 if (fileString
== NULL
){
781 if (errno
!= ENOENT
|| warnNotExist
)
783 DialogF(DF_ERR
, window
->shell
, 1, "Read Macro",
784 "Error reading macro file %s: %s", "dismiss", fileName
,
786 strerror(errno
, vaxc$errno
));
794 /* Parse fileString */
795 result
= readCheckMacroString(window
->shell
, fileString
, window
, fileName
,
802 ** Parse and execute a macro string including macro definitions. Report
803 ** parsing errors in a dialog posted over window->shell.
805 int ReadMacroString(WindowInfo
*window
, char *string
, const char *errIn
)
807 return readCheckMacroString(window
->shell
, string
, window
, errIn
, NULL
);
811 ** Check a macro string containing definitions for errors. Returns True
812 ** if macro compiled successfully. Returns False and puts up
813 ** a dialog explaining if macro did not compile successfully.
815 int CheckMacroString(Widget dialogParent
, char *string
, const char *errIn
,
818 return readCheckMacroString(dialogParent
, string
, NULL
, errIn
, errPos
);
822 ** Parse and optionally execute a macro string including macro definitions.
823 ** Report parsing errors in a dialog posted over dialogParent, using the
824 ** string errIn to identify the entity being parsed (filename, macro string,
825 ** etc.). If runWindow is specified, runs the macro against the window. If
826 ** runWindow is passed as NULL, does parse only. If errPos is non-null,
827 ** returns a pointer to the error location in the string.
829 static int readCheckMacroString(Widget dialogParent
, char *string
,
830 WindowInfo
*runWindow
, const char *errIn
, char **errPos
)
832 char *stoppedAt
, *inPtr
, *namePtr
, *errMsg
;
833 char subrName
[MAX_SYM_LEN
];
839 while (*inPtr
!= '\0') {
841 /* skip over white space and comments */
842 while (*inPtr
==' ' || *inPtr
=='\t' || *inPtr
=='\n'|| *inPtr
=='#') {
844 while (*inPtr
!= '\n' && *inPtr
!= '\0') inPtr
++;
851 /* look for define keyword, and compile and store defined routines */
852 if (!strncmp(inPtr
, "define", 6) && (inPtr
[6]==' ' || inPtr
[6]=='\t')) {
854 inPtr
+= strspn(inPtr
, " \t\n");
856 while (isalnum((unsigned char)*inPtr
) || *inPtr
== '_')
857 *namePtr
++ = *inPtr
++;
859 inPtr
+= strspn(inPtr
, " \t\n");
861 if (errPos
!= NULL
) *errPos
= stoppedAt
;
862 return ParseError(dialogParent
, string
, inPtr
,
863 errIn
, "expected '{'");
865 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
867 if (errPos
!= NULL
) *errPos
= stoppedAt
;
868 return ParseError(dialogParent
, string
, stoppedAt
,
871 if (runWindow
!= NULL
) {
872 sym
= LookupSymbol(subrName
);
874 subrPtr
.val
.prog
= prog
;
875 subrPtr
.tag
= NO_TAG
;
876 sym
= InstallSymbol(subrName
, MACRO_FUNCTION_SYM
, subrPtr
);
878 if (sym
->type
== MACRO_FUNCTION_SYM
)
879 FreeProgram(sym
->value
.val
.prog
);
881 sym
->type
= MACRO_FUNCTION_SYM
;
882 sym
->value
.val
.prog
= prog
;
887 /* Parse and execute immediate (outside of any define) macro commands
888 and WAIT for them to finish executing before proceeding. Note that
889 the code below is not perfect. If you interleave code blocks with
890 definitions in a file which is loaded from another macro file, it
891 will probably run the code blocks in reverse order! */
893 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
895 if (errPos
!= NULL
) *errPos
= stoppedAt
;
896 return ParseError(dialogParent
, string
, stoppedAt
,
899 if (runWindow
!= NULL
) {
901 if (runWindow
->macroCmdData
== NULL
) {
902 runMacro(runWindow
, prog
);
903 while (runWindow
->macroCmdData
!= NULL
) {
904 XtAppNextEvent(XtWidgetToApplicationContext(
905 runWindow
->shell
), &nextEvent
);
906 ServerDispatchEvent(&nextEvent
);
909 RunMacroAsSubrCall(prog
);
918 ** Run a pre-compiled macro, changing the interface state to reflect that
919 ** a macro is running, and handling preemption, resumption, and cancellation.
920 ** frees prog when macro execution is complete;
922 static void runMacro(WindowInfo
*window
, Program
*prog
)
927 macroCmdInfo
*cmdData
;
930 /* If a macro is already running, just call the program as a subroutine,
931 instead of starting a new one, so we don't have to keep a separate
932 context, and the macros will serialize themselves automatically */
933 if (window
->macroCmdData
!= NULL
) {
934 RunMacroAsSubrCall(prog
);
938 /* put up a watch cursor over the waiting window */
939 BeginWait(window
->shell
);
941 /* enable the cancel menu item */
942 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
943 s
=XmStringCreateSimple("Cancel Macro"), NULL
);
945 SetSensitive(window
, window
->cancelMacroItem
, True
);
947 /* Create a data structure for passing macro execution information around
948 amongst the callback routines which will process i/o and completion */
949 cmdData
= (macroCmdInfo
*)XtMalloc(sizeof(macroCmdInfo
));
950 window
->macroCmdData
= cmdData
;
951 cmdData
->bannerIsUp
= False
;
952 cmdData
->closeOnCompletion
= False
;
953 cmdData
->program
= prog
;
954 cmdData
->context
= NULL
;
955 cmdData
->continueWorkProcID
= 0;
956 cmdData
->dialog
= NULL
;
958 /* Set up timer proc for putting up banner when macro takes too long */
959 cmdData
->bannerTimeoutID
= XtAppAddTimeOut(
960 XtWidgetToApplicationContext(window
->shell
), BANNER_WAIT_TIME
,
961 bannerTimeoutProc
, window
);
963 /* Begin macro execution */
964 stat
= ExecuteMacro(window
, prog
, 0, NULL
, &result
, &cmdData
->context
,
967 if (stat
== MACRO_ERROR
)
969 finishMacroCmdExecution(window
);
970 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
971 "Error executing macro: %s", "Dismiss", errMsg
);
975 if (stat
== MACRO_DONE
) {
976 finishMacroCmdExecution(window
);
979 if (stat
== MACRO_TIME_LIMIT
) {
980 ResumeMacroExecution(window
);
983 /* (stat == MACRO_PREEMPT) Macro was preempted */
987 ** Continue with macro execution after preemption. Called by the routines
988 ** whose actions cause preemption when they have completed their lengthy tasks.
989 ** Re-establishes macro execution work proc. Window must be the window in
990 ** which the macro is executing (the window to which macroCmdData is attached),
991 ** and not the window to which operations are focused.
993 void ResumeMacroExecution(WindowInfo
*window
)
995 macroCmdInfo
*cmdData
= (macroCmdInfo
*)window
->macroCmdData
;
998 cmdData
->continueWorkProcID
= XtAppAddWorkProc(
999 XtWidgetToApplicationContext(window
->shell
),
1000 continueWorkProc
, window
);
1004 ** Cancel the macro command in progress (user cancellation via GUI)
1006 void AbortMacroCommand(WindowInfo
*window
)
1008 if (window
->macroCmdData
== NULL
)
1011 /* If there's both a macro and a shell command executing, the shell command
1012 must have been called from the macro. When called from a macro, shell
1013 commands don't put up cancellation controls of their own, but rely
1014 instead on the macro cancellation mechanism (here) */
1016 if (window
->shellCmdData
!= NULL
)
1017 AbortShellCommand(window
);
1020 /* Free the continuation */
1021 FreeRestartData(((macroCmdInfo
*)window
->macroCmdData
)->context
);
1023 /* Kill the macro command */
1024 finishMacroCmdExecution(window
);
1028 ** Call this before closing a window, to clean up macro references to the
1029 ** window, stop any macro which might be running from it, free associated
1030 ** memory, and check that a macro is not attempting to close the window from
1031 ** which it is run. If this is being called from a macro, and the window
1032 ** this routine is examining is the window from which the macro was run, this
1033 ** routine will return False, and the caller must NOT CLOSE THE WINDOW.
1034 ** Instead, empty it and make it Untitled, and let the macro completion
1035 ** process close the window when the macro is finished executing.
1037 int MacroWindowCloseActions(WindowInfo
*window
)
1039 macroCmdInfo
*mcd
, *cmdData
= window
->macroCmdData
;
1042 if (MacroRecordActionHook
!= 0 && MacroRecordWindow
== window
) {
1046 /* If no macro is executing in the window, allow the close, but check
1047 if macros executing in other windows have it as focus. If so, set
1048 their focus back to the window from which they were originally run */
1049 if (cmdData
== NULL
) {
1050 for (w
=WindowList
; w
!=NULL
; w
=w
->next
) {
1051 mcd
= (macroCmdInfo
*)w
->macroCmdData
;
1052 if (w
== MacroRunWindow() && MacroFocusWindow() == window
)
1053 SetMacroFocusWindow(MacroRunWindow());
1054 else if (mcd
!= NULL
&& mcd
->context
->focusWindow
== window
)
1055 mcd
->context
->focusWindow
= mcd
->context
->runWindow
;
1060 /* If the macro currently running (and therefore calling us, because
1061 execution must otherwise return to the main loop to execute any
1062 commands), is running in this window, tell the caller not to close,
1063 and schedule window close on completion of macro */
1064 if (window
== MacroRunWindow()) {
1065 cmdData
->closeOnCompletion
= True
;
1069 /* Free the continuation */
1070 FreeRestartData(cmdData
->context
);
1072 /* Kill the macro command */
1073 finishMacroCmdExecution(window
);
1078 ** Clean up after the execution of a macro command: free memory, and restore
1079 ** the user interface state.
1081 static void finishMacroCmdExecution(WindowInfo
*window
)
1083 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1084 int closeOnCompletion
= cmdData
->closeOnCompletion
;
1086 XClientMessageEvent event
;
1088 /* Cancel pending timeout and work proc */
1089 if (cmdData
->bannerTimeoutID
!= 0)
1090 XtRemoveTimeOut(cmdData
->bannerTimeoutID
);
1091 if (cmdData
->continueWorkProcID
!= 0)
1092 XtRemoveWorkProc(cmdData
->continueWorkProcID
);
1094 /* Clean up waiting-for-macro-command-to-complete mode */
1095 EndWait(window
->shell
);
1096 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
1097 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
1099 SetSensitive(window
, window
->cancelMacroItem
, False
);
1100 if (cmdData
->bannerIsUp
)
1101 ClearModeMessage(window
);
1103 /* If a dialog was up, get rid of it */
1104 if (cmdData
->dialog
!= NULL
)
1105 XtDestroyWidget(XtParent(cmdData
->dialog
));
1107 /* Free execution information */
1108 FreeProgram(cmdData
->program
);
1109 XtFree((char *)cmdData
);
1110 window
->macroCmdData
= NULL
;
1112 /* If macro closed its own window, window was made empty and untitled,
1113 but close was deferred until completion. This is completion, so if
1114 the window is still empty, do the close */
1115 if (closeOnCompletion
&& !window
->filenameSet
&& !window
->fileChanged
) {
1116 CloseWindow(window
);
1120 /* If no other macros are executing, do garbage collection */
1123 /* In processing the .neditmacro file (and possibly elsewhere), there
1124 is an event loop which waits for macro completion. Send an event
1125 to wake up that loop, otherwise execution will stall until the user
1126 does something to the window. */
1127 if (!closeOnCompletion
) {
1129 event
.type
= ClientMessage
;
1130 XSendEvent(XtDisplay(window
->shell
), XtWindow(window
->shell
), False
,
1131 NoEventMask
, (XEvent
*)&event
);
1136 ** Do garbage collection of strings if there are no macros currently
1137 ** executing. NEdit's macro language GC strategy is to call this routine
1138 ** whenever a macro completes. If other macros are still running (preempted
1139 ** or waiting for a shell command or dialog), this does nothing and therefore
1140 ** defers GC to the completion of the last macro out.
1146 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
1147 if (win
->macroCmdData
!= NULL
|| InSmartIndentMacros(win
))
1149 GarbageCollectStrings();
1153 ** Executes macro string "macro" using the lastFocus pane in "window".
1154 ** Reports errors via a dialog posted over "window", integrating the name
1155 ** "errInName" into the message to help identify the source of the error.
1157 void DoMacro(WindowInfo
*window
, const char *macro
, const char *errInName
)
1160 char *errMsg
, *stoppedAt
, *tMacro
;
1163 /* Add a terminating newline (which command line users are likely to omit
1164 since they are typically invoking a single routine) */
1165 macroLen
= strlen(macro
);
1166 tMacro
= XtMalloc(strlen(macro
)+2);
1167 strncpy(tMacro
, macro
, macroLen
);
1168 tMacro
[macroLen
] = '\n';
1169 tMacro
[macroLen
+1] = '\0';
1171 /* Parse the macro and report errors if it fails */
1172 prog
= ParseMacro(tMacro
, &errMsg
, &stoppedAt
);
1174 ParseError(window
->shell
, tMacro
, stoppedAt
, errInName
, errMsg
);
1180 /* run the executable program (prog is freed upon completion) */
1181 runMacro(window
, prog
);
1185 ** Get the current Learn/Replay macro in text form. Returned string is a
1186 ** pointer to the stored macro and should not be freed by the caller (and
1187 ** will cease to exist when the next replay macro is installed)
1189 char *GetReplayMacro(void)
1195 ** Present the user a dialog for "Repeat" command
1197 void RepeatDialog(WindowInfo
*window
)
1199 Widget form
, selBox
, radioBox
, timesForm
;
1202 char *lastCmdLabel
, *parenChar
;
1206 if (LastCommand
== NULL
)
1208 DialogF(DF_WARN
, window
->shell
, 1, "Repeat Macro",
1209 "No previous commands or learn/\nreplay sequences to repeat",
1214 /* Remeber the last command, since the user is allowed to work in the
1215 window while the dialog is up */
1216 rd
= (repeatDialog
*)XtMalloc(sizeof(repeatDialog
));
1217 rd
->lastCommand
= XtNewString(LastCommand
);
1219 /* make a label for the Last command item of the dialog, which includes
1220 the last executed action name */
1221 parenChar
= strchr(LastCommand
, '(');
1222 if (parenChar
== NULL
)
1224 cmdNameLen
= parenChar
-LastCommand
;
1225 lastCmdLabel
= XtMalloc(16 + cmdNameLen
);
1226 strcpy(lastCmdLabel
, "Last Command (");
1227 strncpy(&lastCmdLabel
[14], LastCommand
, cmdNameLen
);
1228 strcpy(&lastCmdLabel
[14 + cmdNameLen
], ")");
1230 XtSetArg(selBoxArgs
[0], XmNautoUnmanage
, False
);
1231 selBox
= CreatePromptDialog(window
->shell
, "repeat", selBoxArgs
, 1);
1232 rd
->shell
= XtParent(selBox
);
1233 XtAddCallback(rd
->shell
, XmNdestroyCallback
, repeatDestroyCB
, rd
);
1234 XtAddCallback(selBox
, XmNokCallback
, repeatOKCB
, rd
);
1235 XtAddCallback(selBox
, XmNapplyCallback
, repeatApplyCB
, rd
);
1236 XtAddCallback(selBox
, XmNcancelCallback
, repeatCancelCB
, rd
);
1237 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_TEXT
));
1238 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_SELECTION_LABEL
));
1239 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_HELP_BUTTON
));
1240 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_APPLY_BUTTON
));
1241 XtVaSetValues(XtParent(selBox
), XmNtitle
, "Repeat Macro", NULL
);
1242 AddMotifCloseCallback(XtParent(selBox
), repeatCancelCB
, rd
);
1244 form
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, selBox
, NULL
);
1246 radioBox
= XtVaCreateManagedWidget("cmdSrc", xmRowColumnWidgetClass
, form
,
1247 XmNradioBehavior
, True
,
1248 XmNorientation
, XmHORIZONTAL
,
1249 XmNpacking
, XmPACK_TIGHT
,
1250 XmNtopAttachment
, XmATTACH_FORM
,
1251 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1252 rd
->lastCmdToggle
= XtVaCreateManagedWidget("lastCmdToggle",
1253 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1254 XmNlabelString
, s1
=XmStringCreateSimple(lastCmdLabel
),
1255 XmNmnemonic
, 'C', NULL
);
1257 XtFree(lastCmdLabel
);
1258 XtVaCreateManagedWidget("learnReplayToggle",
1259 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1261 s1
=XmStringCreateSimple("Learn/Replay"),
1263 XmNsensitive
, ReplayMacro
!= NULL
, NULL
);
1266 timesForm
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, form
,
1267 XmNtopAttachment
, XmATTACH_WIDGET
,
1268 XmNtopWidget
, radioBox
,
1270 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1271 radioBox
= XtVaCreateManagedWidget("method", xmRowColumnWidgetClass
,
1273 XmNradioBehavior
, True
,
1274 XmNorientation
, XmHORIZONTAL
,
1275 XmNpacking
, XmPACK_TIGHT
,
1276 XmNtopAttachment
, XmATTACH_FORM
,
1277 XmNbottomAttachment
, XmATTACH_FORM
,
1278 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1279 rd
->inSelToggle
= XtVaCreateManagedWidget("inSelToggle",
1280 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1281 XmNlabelString
, s1
=XmStringCreateSimple("In Selection"),
1282 XmNmnemonic
, 'I', NULL
);
1284 rd
->toEndToggle
= XtVaCreateManagedWidget("toEndToggle",
1285 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1286 XmNlabelString
, s1
=XmStringCreateSimple("To End"),
1287 XmNmnemonic
, 'T', NULL
);
1289 XtVaCreateManagedWidget("nTimesToggle",
1290 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1291 XmNlabelString
, s1
=XmStringCreateSimple("N Times"),
1293 XmNset
, True
, NULL
);
1295 rd
->repeatText
= XtVaCreateManagedWidget("repeatText", xmTextWidgetClass
,
1298 XmNtopAttachment
, XmATTACH_FORM
,
1299 XmNbottomAttachment
, XmATTACH_FORM
,
1300 XmNleftAttachment
, XmATTACH_WIDGET
,
1301 XmNleftWidget
, radioBox
, NULL
);
1302 RemapDeleteKey(rd
->repeatText
);
1304 /* Handle mnemonic selection of buttons and focus to dialog */
1305 AddDialogMnemonicHandler(form
, FALSE
);
1307 /* Set initial focus */
1308 #if XmVersion >= 1002
1309 XtVaSetValues(form
, XmNinitialFocus
, timesForm
, NULL
);
1310 XtVaSetValues(timesForm
, XmNinitialFocus
, rd
->repeatText
, NULL
);
1314 rd
->forWindow
= window
;
1315 ManageDialogCenteredOnPointer(selBox
);
1318 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1320 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1322 if (doRepeatDialogAction(rd
, ((XmAnyCallbackStruct
*)callData
)->event
))
1323 XtDestroyWidget(rd
->shell
);
1326 /* Note that the apply button is not managed in the repeat dialog. The dialog
1327 itself is capable of non-modal operation, but to be complete, it needs
1328 to dynamically update last command, dimming of learn/replay, possibly a
1329 stop button for the macro, and possibly in-selection with selection */
1330 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1332 doRepeatDialogAction((repeatDialog
*)clientData
,
1333 ((XmAnyCallbackStruct
*)callData
)->event
);
1336 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
)
1339 char nTimesStr
[TYPE_INT_STR_SIZE(int)];
1342 /* Find out from the dialog how to repeat the command */
1343 if (XmToggleButtonGetState(rd
->inSelToggle
))
1345 if (!rd
->forWindow
->buffer
->primary
.selected
)
1347 DialogF(DF_WARN
, rd
->shell
, 1, "Repeat Macro",
1348 "No selection in window to repeat within", "Dismiss");
1349 XmProcessTraversal(rd
->inSelToggle
, XmTRAVERSE_CURRENT
);
1352 params
[0] = "in_selection";
1353 } else if (XmToggleButtonGetState(rd
->toEndToggle
))
1355 params
[0] = "to_end";
1358 if (GetIntTextWarn(rd
->repeatText
, &nTimes
, "number of times", True
)
1361 XmProcessTraversal(rd
->repeatText
, XmTRAVERSE_CURRENT
);
1364 sprintf(nTimesStr
, "%d", nTimes
);
1365 params
[0] = nTimesStr
;
1368 /* Figure out which command user wants to repeat */
1369 if (XmToggleButtonGetState(rd
->lastCmdToggle
))
1370 params
[1] = XtNewString(rd
->lastCommand
);
1372 if (ReplayMacro
== NULL
)
1374 params
[1] = XtNewString(ReplayMacro
);
1377 /* call the action routine repeat_macro to do the work */
1378 XtCallActionProc(rd
->forWindow
->lastFocus
, "repeat_macro", event
, params
,2);
1383 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1385 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1387 XtDestroyWidget(rd
->shell
);
1390 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1392 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1394 XtFree(rd
->lastCommand
);
1399 ** Dispatches a macro to which repeats macro command in "command", either
1400 ** an integer number of times ("how" == positive integer), or within a
1401 ** selected range ("how" == REPEAT_IN_SEL), or to the end of the window
1402 ** ("how == REPEAT_TO_END).
1404 ** Note that as with most macro routines, this returns BEFORE the macro is
1405 ** finished executing
1407 void RepeatMacro(WindowInfo
*window
, const char *command
, int how
)
1410 char *errMsg
, *stoppedAt
, *loopMacro
, *loopedCmd
;
1412 if (command
== NULL
)
1415 /* Wrap a for loop and counter/tests around the command */
1416 if (how
== REPEAT_TO_END
)
1417 loopMacro
= "lastCursor=-1\nstartPos=$cursor\n\
1418 while($cursor>=startPos&&$cursor!=lastCursor){\nlastCursor=$cursor\n%s\n}\n";
1419 else if (how
== REPEAT_IN_SEL
)
1420 loopMacro
= "selStart = $selection_start\nif (selStart == -1)\nreturn\n\
1421 selEnd = $selection_end\nset_cursor_pos(selStart)\nselect(0,0)\n\
1422 boundText = get_range(selEnd, selEnd+10)\n\
1423 while($cursor >= selStart && $cursor < selEnd && \\\n\
1424 get_range(selEnd, selEnd+10) == boundText) {\n\
1425 startLength = $text_length\n%s\n\
1426 selEnd += $text_length - startLength\n}\n";
1428 loopMacro
= "for(i=0;i<%d;i++){\n%s\n}\n";
1429 loopedCmd
= XtMalloc(strlen(command
) + strlen(loopMacro
) + 25);
1430 if (how
== REPEAT_TO_END
|| how
== REPEAT_IN_SEL
)
1431 sprintf(loopedCmd
, loopMacro
, command
);
1433 sprintf(loopedCmd
, loopMacro
, how
, command
);
1435 /* Parse the resulting macro into an executable program "prog" */
1436 prog
= ParseMacro(loopedCmd
, &errMsg
, &stoppedAt
);
1438 fprintf(stderr
, "NEdit internal error, repeat macro syntax wrong: %s\n",
1444 /* run the executable program */
1445 runMacro(window
, prog
);
1449 ** Macro recording action hook for Learn/Replay, added temporarily during
1452 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
1453 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1459 /* Select only actions in text panes in the window for which this
1460 action hook is recording macros (from clientData). */
1461 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1462 if (window
->textArea
== w
)
1464 for (i
=0; i
<window
->nPanes
; i
++) {
1465 if (window
->textPanes
[i
] == w
)
1468 if (i
< window
->nPanes
)
1471 if (window
== NULL
|| window
!= (WindowInfo
*)clientData
)
1474 /* beep on un-recordable operations which require a mouse position, to
1475 remind the user that the action was not recorded */
1476 if (isMouseAction(actionName
)) {
1477 XBell(XtDisplay(w
), 0);
1481 /* Record the action and its parameters */
1482 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1483 if (actionString
!= NULL
) {
1484 BufInsert(MacroRecordBuf
, MacroRecordBuf
->length
, actionString
);
1485 XtFree(actionString
);
1490 ** Permanent action hook for remembering last action for possible replay
1492 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
1493 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1499 /* Find the window to which this action belongs */
1500 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1501 if (window
->textArea
== w
)
1503 for (i
=0; i
<window
->nPanes
; i
++) {
1504 if (window
->textPanes
[i
] == w
)
1507 if (i
< window
->nPanes
)
1513 /* The last action is recorded for the benefit of repeating the last
1514 action. Don't record repeat_macro and wipe out the real action */
1515 if (!strcmp(actionName
, "repeat_macro"))
1518 /* Record the action and its parameters */
1519 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1520 if (actionString
!= NULL
) {
1521 if (LastCommand
!= NULL
)
1522 XtFree(LastCommand
);
1523 LastCommand
= actionString
;
1528 ** Create a macro string to represent an invocation of an action routine.
1529 ** Returns NULL for non-operational or un-recordable actions.
1531 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
1532 String
*params
, Cardinal numParams
)
1534 char chars
[20], *charList
[1], *outStr
, *outPtr
;
1536 int i
, nChars
, nParams
, length
, nameLength
;
1541 if (isIgnoredAction(actionName
) || isRedundantAction(actionName
) ||
1542 isMouseAction(actionName
))
1545 /* Convert self_insert actions, to insert_string */
1546 if (!strcmp(actionName
, "self_insert") ||
1547 !strcmp(actionName
, "self-insert")) {
1548 actionName
= "insert_string";
1550 nChars
= XLookupString((XKeyEvent
*)event
, chars
, 19, &keysym
, NULL
);
1555 nChars
= XmImMbLookupString(w
, (XKeyEvent
*)event
,
1556 chars
, 19, &keysym
, &status
);
1557 if (nChars
== 0 || status
== XLookupNone
||
1558 status
== XLookupKeySym
|| status
== XBufferOverflow
)
1561 chars
[nChars
] = '\0';
1562 charList
[0] = chars
;
1566 nParams
= numParams
;
1568 /* Figure out the length of string required */
1569 nameLength
= strlen(actionName
);
1570 length
= nameLength
+ 3;
1571 for (i
=0; i
<nParams
; i
++)
1572 length
+= escapedStringLength(params
[i
]) + 4;
1574 /* Allocate the string and copy the information to it */
1575 outPtr
= outStr
= XtMalloc(length
+ 1);
1576 strcpy(outPtr
, actionName
);
1577 outPtr
+= nameLength
;
1579 for (i
=0; i
<nParams
; i
++) {
1581 outPtr
+= escapeStringChars(params
[i
], outPtr
);
1582 *outPtr
++ = '\"'; *outPtr
++ = ','; *outPtr
++ = ' ';
1586 *outPtr
++ = ')'; *outPtr
++ = '\n'; *outPtr
++ = '\0';
1590 static int isMouseAction(const char *action
)
1594 for (i
=0; i
<(int)XtNumber(MouseActions
); i
++)
1595 if (!strcmp(action
, MouseActions
[i
]))
1600 static int isRedundantAction(const char *action
)
1604 for (i
=0; i
<(int)XtNumber(RedundantActions
); i
++)
1605 if (!strcmp(action
, RedundantActions
[i
]))
1610 static int isIgnoredAction(const char *action
)
1614 for (i
=0; i
<(int)XtNumber(IgnoredActions
); i
++)
1615 if (!strcmp(action
, IgnoredActions
[i
]))
1621 ** Timer proc for putting up the "Macro Command in Progress" banner if
1622 ** the process is taking too long.
1624 #define MAX_TIMEOUT_MSG_LEN (MAX_ACCEL_LEN + 60)
1625 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
)
1627 WindowInfo
*window
= (WindowInfo
*)clientData
;
1628 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1631 char message
[MAX_TIMEOUT_MSG_LEN
];
1633 cmdData
->bannerIsUp
= True
;
1635 /* Extract accelerator text from menu PushButtons */
1636 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
1638 /* Translate Motif string to char* */
1639 cCancel
= GetXmStringText(xmCancel
);
1641 /* Free Motif String */
1642 XmStringFree(xmCancel
);
1644 /* Create message */
1645 if (cCancel
[0] == '\0') {
1646 strncpy(message
, "Macro Command in Progress", MAX_TIMEOUT_MSG_LEN
);
1647 message
[MAX_TIMEOUT_MSG_LEN
- 1] = '\0';
1651 "Macro Command in Progress -- Press %s to Cancel",
1658 SetModeMessage(window
, message
);
1659 cmdData
->bannerTimeoutID
= 0;
1663 ** Work proc for continuing execution of a preempted macro.
1665 ** Xt WorkProcs are designed to run first-in first-out, which makes them
1666 ** very bad at sharing time between competing tasks. For this reason, it's
1667 ** usually bad to use work procs anywhere where their execution is likely to
1668 ** overlap. Using a work proc instead of a timer proc (which I usually
1669 ** prefer) here means macros will probably share time badly, but we're more
1670 ** interested in making the macros cancelable, and in continuing other work
1671 ** than having users run a bunch of them at once together.
1673 static Boolean
continueWorkProc(XtPointer clientData
)
1675 WindowInfo
*window
= (WindowInfo
*)clientData
;
1676 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1681 stat
= ContinueMacro(cmdData
->context
, &result
, &errMsg
);
1682 if (stat
== MACRO_ERROR
)
1684 finishMacroCmdExecution(window
);
1685 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
1686 "Error executing macro: %s", "Dismiss", errMsg
);
1688 } else if (stat
== MACRO_DONE
)
1690 finishMacroCmdExecution(window
);
1692 } else if (stat
== MACRO_PREEMPT
)
1694 cmdData
->continueWorkProcID
= 0;
1698 /* Macro exceeded time slice, re-schedule it */
1699 if (stat
!= MACRO_TIME_LIMIT
)
1700 return True
; /* shouldn't happen */
1705 ** Copy fromString to toString replacing special characters in strings, such
1706 ** that they can be read back by the macro parser's string reader. i.e. double
1707 ** quotes are replaced by \", backslashes are replaced with \\, C-std control
1708 ** characters like \n are replaced with their backslash counterparts. This
1709 ** routine should be kept reasonably in sync with yylex in parse.y. Companion
1710 ** routine escapedStringLength predicts the length needed to write the string
1711 ** when it is expanded with the additional characters. Returns the number
1712 ** of characters to which the string expanded.
1714 static int escapeStringChars(char *fromString
, char *toString
)
1716 char *e
, *c
, *outPtr
= toString
;
1718 /* substitute escape sequences */
1719 for (c
=fromString
; *c
!='\0'; c
++) {
1720 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1723 *outPtr
++ = ReplaceChars
[e
-EscapeChars
];
1731 return outPtr
- toString
;
1735 ** Predict the length of a string needed to hold a copy of "string" with
1736 ** special characters replaced with escape sequences by escapeStringChars.
1738 static int escapedStringLength(char *string
)
1743 /* calculate length and allocate returned string */
1744 for (c
=string
; *c
!='\0'; c
++) {
1745 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1757 ** Built-in macro subroutine for getting the length of a string
1759 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1760 DataValue
*result
, char **errMsg
)
1762 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
1765 return wrongNArgsErr(errMsg
);
1766 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1768 result
->tag
= INT_TAG
;
1769 result
->val
.n
= strlen(string
);
1774 ** Built-in macro subroutines for min and max
1776 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1777 DataValue
*result
, char **errMsg
)
1779 int minVal
, value
, i
;
1782 return tooFewArgsErr(errMsg
);
1783 if (!readIntArg(argList
[0], &minVal
, errMsg
))
1785 for (i
=0; i
<nArgs
; i
++) {
1786 if (!readIntArg(argList
[i
], &value
, errMsg
))
1788 minVal
= value
< minVal
? value
: minVal
;
1790 result
->tag
= INT_TAG
;
1791 result
->val
.n
= minVal
;
1794 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1795 DataValue
*result
, char **errMsg
)
1797 int maxVal
, value
, i
;
1800 return tooFewArgsErr(errMsg
);
1801 if (!readIntArg(argList
[0], &maxVal
, errMsg
))
1803 for (i
=0; i
<nArgs
; i
++) {
1804 if (!readIntArg(argList
[i
], &value
, errMsg
))
1806 maxVal
= value
> maxVal
? value
: maxVal
;
1808 result
->tag
= INT_TAG
;
1809 result
->val
.n
= maxVal
;
1813 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1814 DataValue
*result
, char **errMsg
)
1816 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1818 char fullname
[MAXPATHLEN
];
1820 /* Read the argument representing the window to focus to, and translate
1821 it into a pointer to a real WindowInfo */
1823 return wrongNArgsErr(errMsg
);
1824 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1826 else if (!strcmp(string
, "last"))
1828 else if (!strcmp(string
, "next"))
1831 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1832 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1833 if (!strcmp(string
, fullname
))
1838 /* If no matching window was found, return empty string and do nothing */
1840 result
->tag
= STRING_TAG
;
1841 result
->val
.str
.rep
= PERM_ALLOC_STR("");
1842 result
->val
.str
.len
= 0;
1846 /* Change the focused window to the requested one */
1847 SetMacroFocusWindow(w
);
1849 /* turn on syntax highlight that might have been deferred */
1850 if (w
->highlightSyntax
&& w
->highlightData
==NULL
)
1851 StartHighlighting(w
, False
);
1853 /* Return the name of the window */
1854 result
->tag
= STRING_TAG
;
1855 AllocNString(&result
->val
.str
, strlen(w
->path
)+strlen(w
->filename
)+1);
1856 sprintf(result
->val
.str
.rep
, "%s%s", w
->path
, w
->filename
);
1861 ** Built-in macro subroutine for getting text from the current window's text
1864 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1865 DataValue
*result
, char **errMsg
)
1868 textBuffer
*buf
= window
->buffer
;
1871 /* Validate arguments and convert to int */
1873 return wrongNArgsErr(errMsg
);
1874 if (!readIntArg(argList
[0], &from
, errMsg
))
1876 if (!readIntArg(argList
[1], &to
, errMsg
))
1878 if (from
< 0) from
= 0;
1879 if (from
> buf
->length
) from
= buf
->length
;
1881 if (to
> buf
->length
) to
= buf
->length
;
1882 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1884 /* Copy text from buffer (this extra copy could be avoided if textBuf.c
1885 provided a routine for writing into a pre-allocated string) */
1886 result
->tag
= STRING_TAG
;
1887 AllocNString(&result
->val
.str
, to
- from
+ 1);
1888 rangeText
= BufGetRange(buf
, from
, to
);
1889 BufUnsubstituteNullChars(rangeText
, buf
);
1890 strcpy(result
->val
.str
.rep
, rangeText
);
1891 /* Note: after the un-substitution, it is possible that strlen() != len,
1892 but that's because strlen() can't deal with 0-characters. */
1898 ** Built-in macro subroutine for getting a single character at the position
1899 ** given, from the current window
1901 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1902 DataValue
*result
, char **errMsg
)
1905 textBuffer
*buf
= window
->buffer
;
1907 /* Validate argument and convert it to int */
1909 return wrongNArgsErr(errMsg
);
1910 if (!readIntArg(argList
[0], &pos
, errMsg
))
1912 if (pos
< 0) pos
= 0;
1913 if (pos
> buf
->length
) pos
= buf
->length
;
1915 /* Return the character in a pre-allocated string) */
1916 result
->tag
= STRING_TAG
;
1917 AllocNString(&result
->val
.str
, 2);
1918 result
->val
.str
.rep
[0] = BufGetCharacter(buf
, pos
);
1919 BufUnsubstituteNullChars(result
->val
.str
.rep
, buf
);
1920 /* Note: after the un-substitution, it is possible that strlen() != len,
1921 but that's because strlen() can't deal with 0-characters. */
1926 ** Built-in macro subroutine for replacing text in the current window's text
1929 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1930 DataValue
*result
, char **errMsg
)
1933 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1934 textBuffer
*buf
= window
->buffer
;
1936 /* Validate arguments and convert to int */
1938 return wrongNArgsErr(errMsg
);
1939 if (!readIntArg(argList
[0], &from
, errMsg
))
1941 if (!readIntArg(argList
[1], &to
, errMsg
))
1943 if (!readStringArg(argList
[2], &string
, stringStorage
, errMsg
))
1945 if (from
< 0) from
= 0;
1946 if (from
> buf
->length
) from
= buf
->length
;
1948 if (to
> buf
->length
) to
= buf
->length
;
1949 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1951 /* Don't allow modifications if the window is read-only */
1952 if (IS_ANY_LOCKED(window
->lockReasons
)) {
1953 XBell(XtDisplay(window
->shell
), 0);
1954 result
->tag
= NO_TAG
;
1958 /* There are no null characters in the string (because macro strings
1959 still have null termination), but if the string contains the
1960 character used by the buffer for null substitution, it could
1961 theoretically become a null. In the highly unlikely event that
1962 all of the possible substitution characters in the buffer are used
1963 up, stop the macro and tell the user of the failure */
1964 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
1965 *errMsg
= "Too much binary data in file";
1969 /* Do the replace */
1970 BufReplace(buf
, from
, to
, string
);
1971 result
->tag
= NO_TAG
;
1976 ** Built-in macro subroutine for replacing the primary-selection selected
1977 ** text in the current window's text buffer
1979 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1980 DataValue
*result
, char **errMsg
)
1982 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1984 /* Validate argument and convert to string */
1986 return wrongNArgsErr(errMsg
);
1987 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1990 /* Don't allow modifications if the window is read-only */
1991 if (IS_ANY_LOCKED(window
->lockReasons
)) {
1992 XBell(XtDisplay(window
->shell
), 0);
1993 result
->tag
= NO_TAG
;
1997 /* There are no null characters in the string (because macro strings
1998 still have null termination), but if the string contains the
1999 character used by the buffer for null substitution, it could
2000 theoretically become a null. In the highly unlikely event that
2001 all of the possible substitution characters in the buffer are used
2002 up, stop the macro and tell the user of the failure */
2003 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
2004 *errMsg
= "Too much binary data in file";
2008 /* Do the replace */
2009 BufReplaceSelected(window
->buffer
, string
);
2010 result
->tag
= NO_TAG
;
2015 ** Built-in macro subroutine for getting the text currently selected by
2016 ** the primary selection in the current window's text buffer, or in any
2017 ** part of screen if "any" argument is given
2019 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2020 DataValue
*result
, char **errMsg
)
2024 /* Read argument list to check for "any" keyword, and get the appropriate
2026 if (nArgs
!= 0 && nArgs
!= 1)
2027 return wrongNArgsErr(errMsg
);
2029 if (argList
[0].tag
!= STRING_TAG
|| strcmp(argList
[0].val
.str
.rep
, "any")) {
2030 *errMsg
= "Unrecognized argument to %s";
2033 selText
= GetAnySelection(window
);
2034 if (selText
== NULL
)
2035 selText
= XtNewString("");
2037 selText
= BufGetSelectionText(window
->buffer
);
2038 BufUnsubstituteNullChars(selText
, window
->buffer
);
2041 /* Return the text as an allocated string */
2042 result
->tag
= STRING_TAG
;
2043 AllocNStringCpy(&result
->val
.str
, selText
);
2049 ** Built-in macro subroutine for determining if implicit conversion of
2050 ** a string to number will succeed or fail
2052 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2053 DataValue
*result
, char **errMsg
)
2055 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2058 return wrongNArgsErr(errMsg
);
2060 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
2064 result
->tag
= INT_TAG
;
2065 result
->val
.n
= StringToNum(string
, NULL
);
2071 ** Built-in macro subroutine for replacing a substring within another string
2073 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2074 DataValue
*result
, char **errMsg
)
2076 int from
, to
, length
, replaceLen
, outLen
;
2077 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *replStr
;
2079 /* Validate arguments and convert to int */
2081 return wrongNArgsErr(errMsg
);
2082 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2084 if (!readIntArg(argList
[1], &from
, errMsg
))
2086 if (!readIntArg(argList
[2], &to
, errMsg
))
2088 if (!readStringArg(argList
[3], &replStr
, stringStorage
[1], errMsg
))
2090 length
= strlen(string
);
2091 if (from
< 0) from
= 0;
2092 if (from
> length
) from
= length
;
2094 if (to
> length
) to
= length
;
2095 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2097 /* Allocate a new string and do the replacement */
2098 replaceLen
= strlen(replStr
);
2099 outLen
= length
- (to
- from
) + replaceLen
;
2100 result
->tag
= STRING_TAG
;
2101 AllocNString(&result
->val
.str
, outLen
+1);
2102 strncpy(result
->val
.str
.rep
, string
, from
);
2103 strncpy(&result
->val
.str
.rep
[from
], replStr
, replaceLen
);
2104 strncpy(&result
->val
.str
.rep
[from
+ replaceLen
], &string
[to
], length
- to
);
2109 ** Built-in macro subroutine for getting a substring of a string
2111 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2112 DataValue
*result
, char **errMsg
)
2114 int from
, to
, length
;
2115 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2117 /* Validate arguments and convert to int */
2119 return wrongNArgsErr(errMsg
);
2120 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2122 if (!readIntArg(argList
[1], &from
, errMsg
))
2124 if (!readIntArg(argList
[2], &to
, errMsg
))
2126 length
= strlen(string
);
2127 if (from
< 0) from
= 0;
2128 if (from
> length
) from
= length
;
2130 if (to
> length
) to
= length
;
2131 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2133 /* Allocate a new string and copy the sub-string into it */
2134 result
->tag
= STRING_TAG
;
2135 AllocNStringNCpy(&result
->val
.str
, &string
[from
], to
- from
);
2139 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2140 DataValue
*result
, char **errMsg
)
2143 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2145 /* Validate arguments and convert to int */
2147 return wrongNArgsErr(errMsg
);
2148 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2150 length
= strlen(string
);
2152 /* Allocate a new string and copy an uppercased version of the string it */
2153 result
->tag
= STRING_TAG
;
2154 AllocNString(&result
->val
.str
, length
+ 1);
2155 for (i
=0; i
<length
; i
++)
2156 result
->val
.str
.rep
[i
] = toupper((unsigned char)string
[i
]);
2160 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2161 DataValue
*result
, char **errMsg
)
2164 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2166 /* Validate arguments and convert to int */
2168 return wrongNArgsErr(errMsg
);
2169 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2171 length
= strlen(string
);
2173 /* Allocate a new string and copy an lowercased version of the string it */
2174 result
->tag
= STRING_TAG
;
2175 AllocNString(&result
->val
.str
, length
+ 1);
2176 for (i
=0; i
<length
; i
++)
2177 result
->val
.str
.rep
[i
] = tolower((unsigned char)string
[i
]);
2181 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2182 DataValue
*result
, char **errMsg
)
2187 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2189 /* Get the string argument */
2191 return wrongNArgsErr(errMsg
);
2192 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2195 /* Use the XmClipboard routines to copy the text to the clipboard.
2196 If errors occur, just give up. */
2197 result
->tag
= NO_TAG
;
2198 stat
= XmClipboardStartCopy(TheDisplay
, XtWindow(window
->textArea
),
2199 s
=XmStringCreateSimple("NEdit"), XtLastTimestampProcessed(TheDisplay
),
2200 window
->textArea
, NULL
, &itemID
);
2202 if (stat
!= ClipboardSuccess
)
2204 if (XmClipboardCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
, "STRING",
2205 string
, strlen(string
), 0, NULL
) != ClipboardSuccess
)
2207 XmClipboardEndCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
);
2211 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2212 DataValue
*result
, char **errMsg
)
2214 unsigned long length
, retLength
;
2217 /* Should have no arguments */
2219 return wrongNArgsErr(errMsg
);
2221 /* Ask if there's a string in the clipboard, and get its length */
2222 if (XmClipboardInquireLength(TheDisplay
, XtWindow(window
->shell
), "STRING",
2223 &length
) != ClipboardSuccess
) {
2224 result
->tag
= STRING_TAG
;
2225 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2226 result
->val
.str
.len
= 0;
2230 /* Allocate a new string to hold the data */
2231 result
->tag
= STRING_TAG
;
2232 AllocNString(&result
->val
.str
, (int)length
+ 1);
2234 /* Copy the clipboard contents to the string */
2235 if (XmClipboardRetrieve(TheDisplay
, XtWindow(window
->shell
), "STRING",
2236 result
->val
.str
.rep
, length
, &retLength
, &id
) != ClipboardSuccess
)
2238 result
->val
.str
.rep
[retLength
] = '\0';
2239 result
->val
.str
.len
= retLength
;
2246 ** Built-in macro subroutine for reading the contents of a text file into
2247 ** a string. On success, returns 1 in $readStatus, and the contents of the
2248 ** file as a string in the subroutine return value. On failure, returns
2249 ** the empty string "" and an 0 $readStatus.
2251 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2252 DataValue
*result
, char **errMsg
)
2254 char stringStorage
[TYPE_INT_STR_SIZE(int)], *name
;
2255 struct stat statbuf
;
2259 /* Validate arguments and convert to int */
2261 return wrongNArgsErr(errMsg
);
2262 if (!readStringArg(argList
[0], &name
, stringStorage
, errMsg
))
2265 /* Read the whole file into an allocated string */
2266 if ((fp
= fopen(name
, "r")) == NULL
)
2268 if (fstat(fileno(fp
), &statbuf
) != 0)
2270 result
->tag
= STRING_TAG
;
2271 AllocNString(&result
->val
.str
, statbuf
.st_size
+1);
2272 readLen
= fread(result
->val
.str
.rep
, sizeof(char), statbuf
.st_size
+1, fp
);
2276 /* Couldn't trust file size. Use slower but more general method */
2277 int chunkSize
= 1024;
2280 buffer
= XtMalloc(readLen
* sizeof(char));
2281 memcpy(buffer
, result
->val
.str
.rep
, readLen
* sizeof(char));
2283 buffer
= XtRealloc(buffer
, (readLen
+chunkSize
)*sizeof(char));
2284 readLen
+= fread(&buffer
[readLen
], sizeof(char), chunkSize
, fp
);
2290 AllocNString(&result
->val
.str
, readLen
+ 1);
2291 memcpy(result
->val
.str
.rep
, buffer
, readLen
* sizeof(char));
2296 /* Return the results */
2297 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2298 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= True
;
2305 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2306 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= False
;
2307 result
->tag
= STRING_TAG
;
2308 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2309 result
->val
.str
.len
= 0;
2314 ** Built-in macro subroutines for writing or appending a string (parameter $1)
2315 ** to a file named in parameter $2. Returns 1 on successful write, or 0 if
2318 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2319 DataValue
*result
, char **errMsg
)
2321 return writeOrAppendFile(False
, window
, argList
, nArgs
, result
, errMsg
);
2324 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2325 DataValue
*result
, char **errMsg
)
2327 return writeOrAppendFile(True
, window
, argList
, nArgs
, result
, errMsg
);
2330 static int writeOrAppendFile(int append
, WindowInfo
*window
,
2331 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
)
2333 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *name
, *string
;
2336 /* Validate argument */
2338 return wrongNArgsErr(errMsg
);
2339 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2341 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
))
2345 if ((fp
= fopen(name
, append
? "a" : "w")) == NULL
) {
2346 result
->tag
= INT_TAG
;
2347 result
->val
.n
= False
;
2351 /* write the string to the file */
2352 fwrite(string
, sizeof(char), strlen(string
), fp
);
2355 result
->tag
= INT_TAG
;
2356 result
->val
.n
= False
;
2361 /* return the status */
2362 result
->tag
= INT_TAG
;
2363 result
->val
.n
= True
;
2368 ** Built-in macro subroutine for searching silently in a window without
2369 ** dialogs, beeps, or changes to the selection. Arguments are: $1: string to
2370 ** search for, $2: starting position. Optional arguments may include the
2371 ** strings: "wrap" to make the search wrap around the beginning or end of the
2372 ** string, "backward" or "forward" to change the search direction ("forward" is
2373 ** the default), "literal", "case" or "regex" to change the search type
2374 ** (default is "literal").
2376 ** Returns the starting position of the match, or -1 if nothing matched.
2377 ** also returns the ending position of the match in $searchEndPos
2379 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2380 DataValue
*result
, char **errMsg
)
2382 DataValue newArgList
[9];
2385 /* Use the search string routine, by adding the buffer contents as
2386 the string argument */
2388 return wrongNArgsErr(errMsg
);
2389 newArgList
[0].tag
= STRING_TAG
;
2390 newArgList
[0].val
.str
.rep
= BufGetAll(window
->buffer
);
2391 newArgList
[0].val
.str
.len
= window
->buffer
->length
;
2392 memcpy(&newArgList
[1], argList
, nArgs
* sizeof(DataValue
));
2393 retVal
= searchStringMS(window
, newArgList
, nArgs
+1, result
, errMsg
);
2394 XtFree(newArgList
[0].val
.str
.rep
);
2399 ** Built-in macro subroutine for searching a string. Arguments are $1:
2400 ** string to search in, $2: string to search for, $3: starting position.
2401 ** Optional arguments may include the strings: "wrap" to make the search
2402 ** wrap around the beginning or end of the string, "backward" or "forward"
2403 ** to change the search direction ("forward" is the default), "literal",
2404 ** "case" or "regex" to change the search type (default is "literal").
2406 ** Returns the starting position of the match, or -1 if nothing matched.
2407 ** also returns the ending position of the match in $searchEndPos
2409 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2410 DataValue
*result
, char **errMsg
)
2412 int beginPos
, wrap
, direction
, found
= False
, foundStart
, foundEnd
, type
;
2413 int skipSearch
= False
, len
;
2414 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
;
2416 /* Validate arguments and convert to proper types */
2418 return tooFewArgsErr(errMsg
);
2419 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2421 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2423 if (!readIntArg(argList
[2], &beginPos
, errMsg
))
2425 if (!readSearchArgs(&argList
[3], nArgs
-3, &direction
, &type
, &wrap
, errMsg
))
2428 len
= argList
[0].val
.str
.len
;
2429 if (beginPos
> len
) {
2430 if (direction
== SEARCH_FORWARD
) {
2432 beginPos
= 0; /* Wrap immediately */
2440 } else if (beginPos
< 0) {
2441 if (direction
== SEARCH_BACKWARD
) {
2443 beginPos
= len
; /* Wrap immediately */
2454 found
= SearchString(string
, searchStr
, direction
, type
, wrap
, beginPos
,
2455 &foundStart
, &foundEnd
, NULL
, NULL
, GetWindowDelimiters(window
));
2457 /* Return the results */
2458 ReturnGlobals
[SEARCH_END
]->value
.tag
= INT_TAG
;
2459 ReturnGlobals
[SEARCH_END
]->value
.val
.n
= found
? foundEnd
: 0;
2460 result
->tag
= INT_TAG
;
2461 result
->val
.n
= found
? foundStart
: -1;
2466 ** Built-in macro subroutine for replacing all occurences of a search string in
2467 ** a string with a replacement string. Arguments are $1: string to search in,
2468 ** $2: string to search for, $3: replacement string. Also takes an optional
2469 ** search type: one of "literal", "case" or "regex" (default is "literal"), and
2470 ** an optional "copy" argument.
2472 ** Returns a new string with all of the replacements done. If no replacements
2473 ** were performed and "copy" was specified, returns a copy of the original
2474 ** string. Otherwise returns an empty string ("").
2476 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2477 DataValue
*result
, char **errMsg
)
2479 char stringStorage
[3][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
, *replaceStr
;
2480 char *argStr
, *replacedStr
;
2481 int searchType
= SEARCH_LITERAL
, copyStart
, copyEnd
;
2482 int replacedLen
, replaceEnd
, force
=False
, i
;
2484 /* Validate arguments and convert to proper types */
2485 if (nArgs
< 3 || nArgs
> 5)
2486 return wrongNArgsErr(errMsg
);
2487 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2489 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2491 if (!readStringArg(argList
[2], &replaceStr
, stringStorage
[2], errMsg
))
2493 for (i
= 3; i
< nArgs
; i
++) {
2494 /* Read the optional search type and force arguments */
2495 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
2497 if (!StringToSearchType(argStr
, &searchType
)) {
2498 /* It's not a search type. is it "copy"? */
2499 if (!strcmp(argStr
, "copy")) {
2502 *errMsg
= "unrecognized argument to %s";
2508 /* Do the replace */
2509 replacedStr
= ReplaceAllInString(string
, searchStr
, replaceStr
, searchType
,
2510 ©Start
, ©End
, &replacedLen
, GetWindowDelimiters(window
));
2512 /* Return the results */
2513 result
->tag
= STRING_TAG
;
2514 if (replacedStr
== NULL
) {
2516 /* Just copy the original DataValue */
2517 if (argList
[0].tag
== STRING_TAG
) {
2518 result
->val
.str
.rep
= argList
[0].val
.str
.rep
;
2519 result
->val
.str
.len
= argList
[0].val
.str
.len
;
2522 AllocNStringCpy(&result
->val
.str
, string
);
2526 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2527 result
->val
.str
.len
= 0;
2531 size_t remainder
= strlen(&string
[copyEnd
]);
2532 replaceEnd
= copyStart
+ replacedLen
;
2533 AllocNString(&result
->val
.str
, replaceEnd
+ remainder
+ 1);
2534 strncpy(result
->val
.str
.rep
, string
, copyStart
);
2535 strcpy(&result
->val
.str
.rep
[copyStart
], replacedStr
);
2536 strcpy(&result
->val
.str
.rep
[replaceEnd
], &string
[copyEnd
]);
2537 XtFree(replacedStr
);
2542 static int readSearchArgs(DataValue
*argList
, int nArgs
, int *searchDirection
,
2543 int *searchType
, int *wrap
, char **errMsg
)
2546 char *argStr
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2549 *searchDirection
= SEARCH_FORWARD
;
2550 *searchType
= SEARCH_LITERAL
;
2551 for (i
=0; i
<nArgs
; i
++) {
2552 if (!readStringArg(argList
[i
], &argStr
, stringStorage
, errMsg
))
2554 else if (!strcmp(argStr
, "wrap"))
2556 else if (!strcmp(argStr
, "nowrap"))
2558 else if (!strcmp(argStr
, "backward"))
2559 *searchDirection
= SEARCH_BACKWARD
;
2560 else if (!strcmp(argStr
, "forward"))
2561 *searchDirection
= SEARCH_FORWARD
;
2562 else if (!StringToSearchType(argStr
, searchType
)) {
2563 *errMsg
= "Unrecognized argument to %s";
2570 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2571 DataValue
*result
, char **errMsg
)
2575 /* Get argument and convert to int */
2577 return wrongNArgsErr(errMsg
);
2578 if (!readIntArg(argList
[0], &pos
, errMsg
))
2581 /* Set the position */
2582 TextSetCursorPos(window
->lastFocus
, pos
);
2583 result
->tag
= NO_TAG
;
2587 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2588 DataValue
*result
, char **errMsg
)
2590 int start
, end
, startTmp
;
2592 /* Get arguments and convert to int */
2594 return wrongNArgsErr(errMsg
);
2595 if (!readIntArg(argList
[0], &start
, errMsg
))
2597 if (!readIntArg(argList
[1], &end
, errMsg
))
2600 /* Verify integrity of arguments */
2606 if (start
< 0) start
= 0;
2607 if (start
> window
->buffer
->length
) start
= window
->buffer
->length
;
2608 if (end
< 0) end
= 0;
2609 if (end
> window
->buffer
->length
) end
= window
->buffer
->length
;
2611 /* Make the selection */
2612 BufSelect(window
->buffer
, start
, end
);
2613 result
->tag
= NO_TAG
;
2617 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2618 DataValue
*result
, char **errMsg
)
2620 int start
, end
, left
, right
;
2622 /* Get arguments and convert to int */
2624 return wrongNArgsErr(errMsg
);
2625 if (!readIntArg(argList
[0], &start
, errMsg
))
2627 if (!readIntArg(argList
[1], &end
, errMsg
))
2629 if (!readIntArg(argList
[2], &left
, errMsg
))
2631 if (!readIntArg(argList
[3], &right
, errMsg
))
2634 /* Make the selection */
2635 BufRectSelect(window
->buffer
, start
, end
, left
, right
);
2636 result
->tag
= NO_TAG
;
2641 ** Macro subroutine to ring the bell
2643 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2644 DataValue
*result
, char **errMsg
)
2647 return wrongNArgsErr(errMsg
);
2648 XBell(XtDisplay(window
->shell
), 0);
2649 result
->tag
= NO_TAG
;
2653 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2654 DataValue
*result
, char **errMsg
)
2656 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2660 return tooFewArgsErr(errMsg
);
2661 for (i
=0; i
<nArgs
; i
++) {
2662 if (!readStringArg(argList
[i
], &string
, stringStorage
, errMsg
))
2664 printf("%s%s", string
, i
==nArgs
-1 ? "" : " ");
2667 result
->tag
= NO_TAG
;
2672 ** Built-in macro subroutine for getting the value of an environment variable
2674 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2675 DataValue
*result
, char **errMsg
)
2677 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
2681 /* Get name of variable to get */
2683 return wrongNArgsErr(errMsg
);
2684 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
2685 *errMsg
= "argument to %s must be a string";
2688 value
= getenv(name
);
2692 /* Return the text as an allocated string */
2693 result
->tag
= STRING_TAG
;
2694 AllocNStringCpy(&result
->val
.str
, value
);
2698 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2699 DataValue
*result
, char **errMsg
)
2701 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *cmdString
, *inputString
;
2704 return wrongNArgsErr(errMsg
);
2705 if (!readStringArg(argList
[0], &cmdString
, stringStorage
[0], errMsg
))
2707 if (!readStringArg(argList
[1], &inputString
, stringStorage
[1], errMsg
))
2710 /* Shell command execution requires that the macro be suspended, so
2711 this subroutine can't be run if macro execution can't be interrupted */
2712 if (MacroRunWindow()->macroCmdData
== NULL
) {
2713 *errMsg
= "%s can't be called from non-suspendable context";
2718 *errMsg
= "Shell commands not supported under VMS";
2721 ShellCmdToMacroString(window
, cmdString
, inputString
);
2722 result
->tag
= INT_TAG
;
2729 ** Method used by ShellCmdToMacroString (called by shellCmdMS), for returning
2730 ** macro string and exit status after the execution of a shell command is
2731 ** complete. (Sorry about the poor modularity here, it's just not worth
2732 ** teaching other modules about macro return globals, since other than this,
2733 ** they're not used outside of macro.c)
2735 void ReturnShellCommandOutput(WindowInfo
*window
, const char *outText
, int status
)
2738 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2740 if (cmdData
== NULL
)
2742 retVal
.tag
= STRING_TAG
;
2743 AllocNStringCpy(&retVal
.val
.str
, outText
);
2744 ModifyReturnedValue(cmdData
->context
, retVal
);
2745 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.tag
= INT_TAG
;
2746 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.val
.n
= status
;
2749 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2750 DataValue
*result
, char **errMsg
)
2752 macroCmdInfo
*cmdData
;
2753 char stringStorage
[9][TYPE_INT_STR_SIZE(int)];
2754 char *btnLabels
[sizeof(stringStorage
)/sizeof(*stringStorage
)];
2762 /* Ignore the focused window passed as the function argument and put
2763 the dialog up over the window which is executing the macro */
2764 window
= MacroRunWindow();
2765 cmdData
= window
->macroCmdData
;
2767 /* Dialogs require macro to be suspended and interleaved with other macros.
2768 This subroutine can't be run if macro execution can't be interrupted */
2770 *errMsg
= "%s can't be called from non-suspendable context";
2774 /* Read and check the arguments. The first being the dialog message,
2775 and the rest being the button labels */
2777 *errMsg
= "%s subroutine called with no arguments";
2780 if (nArgs
> sizeof(stringStorage
)/sizeof(*stringStorage
)) {
2781 *errMsg
= "%s subroutine called with too many arguments";
2784 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
2786 for (i
=1; i
<nArgs
; i
++)
2787 if (!readStringArg(argList
[i
], &btnLabels
[i
-1], stringStorage
[i
],
2791 btnLabels
[0] = "Dismiss";
2796 /* Create the message box dialog widget and its dialog shell parent */
2798 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2799 XtSetArg(al
[ac
], XmNmessageString
, s1
=MKSTRING(message
)); ac
++;
2800 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0]));
2802 dialog
= CreateMessageDialog(window
->shell
, "macroDialog", al
, ac
);
2805 AddMotifCloseCallback(XtParent(dialog
), dialogCloseCB
, window
);
2806 XtAddCallback(dialog
, XmNokCallback
, dialogBtnCB
, window
);
2807 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2808 XmNuserData
, (XtPointer
)1, NULL
);
2809 cmdData
->dialog
= dialog
;
2811 /* Unmanage default buttons, except for "OK" */
2812 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2813 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2815 /* Make callback for the unmanaged cancel button (which can
2816 still get executed via the esc key) activate close box action */
2817 XtAddCallback(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2818 XmNactivateCallback
, dialogCloseCB
, window
);
2820 /* Add user specified buttons (1st is already done) */
2821 for (i
=1; i
<nBtns
; i
++) {
2822 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2823 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
2824 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2825 XtAddCallback(btn
, XmNactivateCallback
, dialogBtnCB
, window
);
2829 #ifdef LESSTIF_VERSION
2830 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2831 the escape key for closing the dialog (probably because the
2832 cancel button is not managed). */
2833 XtAddEventHandler(dialog
, KeyPressMask
, False
, dialogEscCB
,
2835 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
2836 True
, GrabModeAsync
, GrabModeAsync
);
2837 #endif /* LESSTIF_VERSION */
2839 /* Put up the dialog */
2840 ManageDialogCenteredOnPointer(dialog
);
2842 /* Stop macro execution until the dialog is complete */
2845 /* Return placeholder result. Value will be changed by button callback */
2846 result
->tag
= INT_TAG
;
2851 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2853 WindowInfo
*window
= (WindowInfo
*)clientData
;
2854 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2858 /* Return the index of the button which was pressed (stored in the userData
2859 field of the button widget). The 1st button, being a gadget, is not
2861 if (cmdData
== NULL
)
2862 return; /* shouldn't happen */
2863 if (XtClass(w
) == xmPushButtonWidgetClass
) {
2864 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
2865 retVal
.val
.n
= (int)userData
;
2868 retVal
.tag
= INT_TAG
;
2869 ModifyReturnedValue(cmdData
->context
, retVal
);
2871 /* Pop down the dialog */
2872 XtDestroyWidget(XtParent(cmdData
->dialog
));
2873 cmdData
->dialog
= NULL
;
2875 /* Continue preempted macro execution */
2876 ResumeMacroExecution(window
);
2879 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2881 WindowInfo
*window
= (WindowInfo
*)clientData
;
2882 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2885 /* Return 0 to show that the dialog was closed via the window close box */
2887 retVal
.tag
= INT_TAG
;
2888 ModifyReturnedValue(cmdData
->context
, retVal
);
2890 /* Pop down the dialog */
2891 XtDestroyWidget(XtParent(cmdData
->dialog
));
2892 cmdData
->dialog
= NULL
;
2894 /* Continue preempted macro execution */
2895 ResumeMacroExecution(window
);
2898 #ifdef LESSTIF_VERSION
2899 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
2902 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
2904 if (clientData
!= NULL
) {
2905 dialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
2909 #endif /* LESSTIF_VERSION */
2911 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2912 DataValue
*result
, char **errMsg
)
2914 macroCmdInfo
*cmdData
;
2915 char stringStorage
[9][TYPE_INT_STR_SIZE(int)];
2916 char *btnLabels
[sizeof(stringStorage
)/sizeof(*stringStorage
)];
2924 /* Ignore the focused window passed as the function argument and put
2925 the dialog up over the window which is executing the macro */
2926 window
= MacroRunWindow();
2927 cmdData
= window
->macroCmdData
;
2929 /* Dialogs require macro to be suspended and interleaved with other macros.
2930 This subroutine can't be run if macro execution can't be interrupted */
2932 *errMsg
= "%s can't be called from non-suspendable context";
2936 /* Read and check the arguments. The first being the dialog message,
2937 and the rest being the button labels */
2939 *errMsg
= "%s subroutine called with no arguments";
2942 if (nArgs
> sizeof(stringStorage
)/sizeof(*stringStorage
)) {
2943 *errMsg
= "%s subroutine called with too many arguments";
2946 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
2948 for (i
=1; i
<nArgs
; i
++)
2949 if (!readStringArg(argList
[i
], &btnLabels
[i
-1], stringStorage
[i
],
2953 btnLabels
[0] = "Dismiss";
2958 /* Create the selection box dialog widget and its dialog shell parent */
2960 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2961 XtSetArg(al
[ac
], XmNselectionLabelString
, s1
=MKSTRING(message
)); ac
++;
2962 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0]));
2964 dialog
= CreatePromptDialog(window
->shell
, "macroStringDialog", al
, ac
);
2967 AddMotifCloseCallback(XtParent(dialog
), stringDialogCloseCB
, window
);
2968 XtAddCallback(dialog
, XmNokCallback
, stringDialogBtnCB
, window
);
2969 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2970 XmNuserData
, (XtPointer
)1, NULL
);
2971 cmdData
->dialog
= dialog
;
2973 /* Unmanage unneded widgets */
2974 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2975 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2977 /* Make callback for the unmanaged cancel button (which can
2978 still get executed via the esc key) activate close box action */
2979 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2980 XmNactivateCallback
, stringDialogCloseCB
, window
);
2982 /* Add user specified buttons (1st is already done). Selection box
2983 requires a place-holder widget to be added before buttons can be
2984 added, that's what the separator below is for */
2985 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
2986 for (i
=1; i
<nBtns
; i
++) {
2987 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2988 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
2989 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2990 XtAddCallback(btn
, XmNactivateCallback
, stringDialogBtnCB
, window
);
2994 #ifdef LESSTIF_VERSION
2995 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2996 the escape key for closing the dialog (probably because the
2997 cancel button is not managed). */
2998 XtAddEventHandler(dialog
, KeyPressMask
, False
, stringDialogEscCB
,
3000 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3001 True
, GrabModeAsync
, GrabModeAsync
);
3002 #endif /* LESSTIF_VERSION */
3004 /* Put up the dialog */
3005 ManageDialogCenteredOnPointer(dialog
);
3007 /* Stop macro execution until the dialog is complete */
3010 /* Return placeholder result. Value will be changed by button callback */
3011 result
->tag
= INT_TAG
;
3016 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
3019 WindowInfo
*window
= (WindowInfo
*)clientData
;
3020 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3026 /* shouldn't happen, but would crash if it did */
3027 if (cmdData
== NULL
)
3030 /* Return the string entered in the selection text area */
3031 text
= XmTextGetString(XmSelectionBoxGetChild(cmdData
->dialog
,
3033 retVal
.tag
= STRING_TAG
;
3034 AllocNStringCpy(&retVal
.val
.str
, text
);
3036 ModifyReturnedValue(cmdData
->context
, retVal
);
3038 /* Find the index of the button which was pressed (stored in the userData
3039 field of the button widget). The 1st button, being a gadget, is not
3041 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3042 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3043 btnNum
= (int)userData
;
3047 /* Return the button number in the global variable $string_dialog_button */
3048 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3049 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3051 /* Pop down the dialog */
3052 XtDestroyWidget(XtParent(cmdData
->dialog
));
3053 cmdData
->dialog
= NULL
;
3055 /* Continue preempted macro execution */
3056 ResumeMacroExecution(window
);
3059 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
3062 WindowInfo
*window
= (WindowInfo
*)clientData
;
3063 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3066 /* shouldn't happen, but would crash if it did */
3067 if (cmdData
== NULL
)
3070 /* Return an empty string */
3071 retVal
.tag
= STRING_TAG
;
3072 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3073 retVal
.val
.str
.len
= 0;
3074 ModifyReturnedValue(cmdData
->context
, retVal
);
3076 /* Return button number 0 in the global variable $string_dialog_button */
3077 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3078 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= 0;
3080 /* Pop down the dialog */
3081 XtDestroyWidget(XtParent(cmdData
->dialog
));
3082 cmdData
->dialog
= NULL
;
3084 /* Continue preempted macro execution */
3085 ResumeMacroExecution(window
);
3088 #ifdef LESSTIF_VERSION
3089 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3092 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3094 if (clientData
!= NULL
) {
3095 stringDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3099 #endif /* LESSTIF_VERSION */
3102 ** A subroutine to put up a calltip
3103 ** First arg is either text to be displayed or a key for tip/tag lookup.
3104 ** Optional second arg is the buffer position beneath which to display the
3105 ** upper-left corner of the tip. Default (or -1) puts it under the cursor.
3106 ** Additional optional arguments:
3107 ** "tipText": (default) Indicates first arg is text to be displayed in tip.
3108 ** "tipKey": Indicates first arg is key in calltips database. If key
3109 ** is not found in tip database then the tags database is also
3111 ** "tagKey": Indicates first arg is key in tags database. (Skips
3112 ** search in calltips database.)
3113 ** "center": Horizontally center the calltip at the position
3114 ** "right": Put the right edge of the calltip at the position
3115 ** "center" and "right" cannot both be specified.
3116 ** "above": Place the calltip above the position
3117 ** "strict": Don't move the calltip to keep it on-screen and away
3118 ** from the cursor's line.
3120 ** Returns the new calltip's ID on success, 0 on failure.
3122 ** Does this need to go on IgnoredActions? I don't think so, since
3123 ** showing a calltip may be part of the action you want to learn.
3125 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3126 DataValue
*result
, char **errMsg
)
3128 char stringStorage
[TYPE_INT_STR_SIZE(int)], *tipText
, *txtArg
;
3129 Boolean anchored
= False
, lookup
= True
;
3131 int anchorPos
, hAlign
= TIP_LEFT
, vAlign
= TIP_BELOW
,
3132 alignMode
= TIP_SLOPPY
;
3134 /* Read and check the string */
3136 *errMsg
= "%s subroutine called with too few arguments";
3140 *errMsg
= "%s subroutine called with too many arguments";
3144 /* Read the tip text or key */
3145 if (!readStringArg(argList
[0], &tipText
, stringStorage
, errMsg
))
3148 /* Read the anchor position (-1 for unanchored) */
3150 if (!readIntArg(argList
[1], &anchorPos
, errMsg
))
3155 if (anchorPos
>= 0) anchored
= True
;
3157 /* Any further args are directives for relative positioning */
3158 for (i
= 2; i
< nArgs
; ++i
) {
3159 if (!readStringArg(argList
[i
], &txtArg
, stringStorage
, errMsg
)){
3162 switch( txtArg
[0] ) {
3164 if (strcmp(txtArg
, "center"))
3166 hAlign
= TIP_CENTER
;
3169 if (strcmp(txtArg
, "right"))
3174 if (strcmp(txtArg
, "above"))
3179 if (strcmp(txtArg
, "strict"))
3181 alignMode
= TIP_STRICT
;
3184 if (!strcmp(txtArg
, "tipText"))
3186 else if (!strcmp(txtArg
, "tipKey"))
3188 else if (!strcmp(txtArg
, "tagKey"))
3189 mode
= TIP_FROM_TAG
;
3198 result
->tag
= INT_TAG
;
3199 if (mode
< 0) lookup
= False
;
3200 /* Look up (maybe) a calltip and display it */
3201 result
->val
.n
= ShowTipString( window
, tipText
, anchored
, anchorPos
, lookup
,
3202 mode
, hAlign
, vAlign
, alignMode
);
3207 /* This is how the (more informative) global var. version would work,
3208 assuming there was a global buffer called msg. */
3209 /* sprintf(msg, "unrecognized argument to %%s: \"%s\"", txtArg);
3211 *errMsg
= "unrecognized argument to %s";
3216 ** A subroutine to kill the current calltip
3218 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3219 DataValue
*result
, char **errMsg
)
3224 *errMsg
= "%s subroutine called with too many arguments";
3228 if (!readIntArg(argList
[0], &calltipID
, errMsg
))
3232 KillCalltip( window
, calltipID
);
3234 result
->tag
= NO_TAG
;
3239 * A subroutine to get the ID of the current calltip, or 0 if there is none.
3241 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
,
3242 int nArgs
, DataValue
*result
, char **errMsg
)
3244 result
->tag
= INT_TAG
;
3245 result
->val
.n
= GetCalltipID(window
, 0);
3250 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3251 DataValue
*result
, char **errMsg
)
3253 macroCmdInfo
*cmdData
;
3254 char stringStorage
[9][TYPE_INT_STR_SIZE(int)];
3255 char *btnLabels
[sizeof(stringStorage
)/sizeof(*stringStorage
)];
3256 char *message
, *text
;
3261 char *p
, *old_p
, **text_lines
, *tmp
;
3264 XmString
*test_strings
;
3270 /* Ignore the focused window passed as the function argument and put
3271 the dialog up over the window which is executing the macro */
3272 window
= MacroRunWindow();
3273 cmdData
= window
->macroCmdData
;
3275 /* Dialogs require macro to be suspended and interleaved with other macros.
3276 This subroutine can't be run if macro execution can't be interrupted */
3278 *errMsg
= "%s can't be called from non-suspendable context";
3282 /* Read and check the arguments. The first being the dialog message,
3283 and the rest being the button labels */
3285 *errMsg
= "%s subroutine called with no message, string or arguments";
3288 if (nArgs
> sizeof(stringStorage
)/sizeof(*stringStorage
)) {
3289 *errMsg
= "%s subroutine called with too many arguments";
3293 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
3296 if (!readStringArg(argList
[1], &text
, stringStorage
[0], errMsg
))
3299 if (!text
|| text
[0] == '\0') {
3300 *errMsg
= "%s subroutine called with empty list data";
3304 for (i
=2; i
<nArgs
; i
++)
3305 if (!readStringArg(argList
[i
], &btnLabels
[i
-2], stringStorage
[i
],
3309 btnLabels
[0] = "Dismiss";
3314 /* count the lines in the text - add one for unterminated last line */
3316 for (p
= text
; *p
; p
++)
3320 /* now set up arrays of pointers to lines */
3321 /* test_strings to hold the display strings (tab expanded) */
3322 /* text_lines to hold the original text lines (without the '\n's) */
3323 test_strings
= (XmString
*) XtMalloc(sizeof(XmString
) * nlines
);
3324 text_lines
= (char **)XtMalloc(sizeof(char *) * (nlines
+ 1));
3325 for (n
= 0; n
< nlines
; n
++) {
3326 test_strings
[n
] = (XmString
)0;
3327 text_lines
[n
] = (char *)0;
3329 text_lines
[n
] = (char *)0; /* make sure this is a null-terminated table */
3331 /* pick up the tabDist value */
3332 tabDist
= window
->buffer
->tabDist
;
3334 /* load the table */
3338 tmp_len
= 0; /* current allocated size of temporary buffer tmp */
3339 tmp
= malloc(1); /* temporary buffer into which to expand tabs */
3341 is_last
= (*p
== '\0');
3342 if (*p
== '\n' || is_last
) {
3344 if (strlen(old_p
) > 0) { /* only include non-empty lines */
3348 /* save the actual text line in text_lines[n] */
3349 text_lines
[n
] = (char *)XtMalloc(strlen(old_p
) + 1);
3350 strcpy(text_lines
[n
], old_p
);
3352 /* work out the tabs expanded length */
3353 for (s
= old_p
, l
= 0; *s
; s
++)
3354 l
+= (*s
== '\t') ? tabDist
- (l
% tabDist
) : 1;
3356 /* verify tmp is big enough then tab-expand old_p into tmp */
3358 tmp
= realloc(tmp
, (tmp_len
= l
) + 1);
3359 for (s
= old_p
, t
= tmp
, l
= 0; *s
; s
++) {
3361 for (i
= tabDist
- (l
% tabDist
); i
--; l
++)
3370 /* that's it: tmp is the tab-expanded version of old_p */
3371 test_strings
[n
] = MKSTRING(tmp
);
3376 *p
= '\n'; /* put back our newline */
3381 free(tmp
); /* don't need this anymore */
3384 test_strings
[0] = MKSTRING("");
3388 /* Create the selection box dialog widget and its dialog shell parent */
3390 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3391 XtSetArg(al
[ac
], XmNlistLabelString
, s1
=MKSTRING(message
)); ac
++;
3392 XtSetArg(al
[ac
], XmNlistItems
, test_strings
); ac
++;
3393 XtSetArg(al
[ac
], XmNlistItemCount
, nlines
); ac
++;
3394 XtSetArg(al
[ac
], XmNlistVisibleItemCount
, (nlines
> 10) ? 10 : nlines
); ac
++;
3395 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0])); ac
++;
3396 dialog
= CreateSelectionDialog(window
->shell
, "macroListDialog", al
, ac
);
3397 AddMotifCloseCallback(XtParent(dialog
), listDialogCloseCB
, window
);
3398 XtAddCallback(dialog
, XmNokCallback
, listDialogBtnCB
, window
);
3399 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3400 XmNuserData
, (XtPointer
)1, NULL
);
3403 cmdData
->dialog
= dialog
;
3405 /* forget lines stored in list */
3407 XmStringFree(test_strings
[n
]);
3408 XtFree((char *)test_strings
);
3410 /* modify the list */
3411 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_LIST
),
3412 XmNselectionPolicy
, XmSINGLE_SELECT
,
3413 XmNuserData
, (XtPointer
)text_lines
, NULL
);
3415 /* Unmanage unneeded widgets */
3416 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_APPLY_BUTTON
));
3417 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3418 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3419 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_TEXT
));
3420 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_SELECTION_LABEL
));
3422 /* Make callback for the unmanaged cancel button (which can
3423 still get executed via the esc key) activate close box action */
3424 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3425 XmNactivateCallback
, listDialogCloseCB
, window
);
3427 /* Add user specified buttons (1st is already done). Selection box
3428 requires a place-holder widget to be added before buttons can be
3429 added, that's what the separator below is for */
3430 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3431 for (i
=1; i
<nBtns
; i
++) {
3432 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3433 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
3434 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3435 XtAddCallback(btn
, XmNactivateCallback
, listDialogBtnCB
, window
);
3439 #ifdef LESSTIF_VERSION
3440 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3441 the escape key for closing the dialog. */
3442 XtAddEventHandler(dialog
, KeyPressMask
, False
, listDialogEscCB
,
3444 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3445 True
, GrabModeAsync
, GrabModeAsync
);
3446 #endif /* LESSTIF_VERSION */
3448 /* Put up the dialog */
3449 ManageDialogCenteredOnPointer(dialog
);
3451 /* Stop macro execution until the dialog is complete */
3454 /* Return placeholder result. Value will be changed by button callback */
3455 result
->tag
= INT_TAG
;
3460 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
3463 WindowInfo
*window
= (WindowInfo
*)clientData
;
3464 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3470 int n_sel
, *seltable
, sel_index
= 0;
3474 /* shouldn't happen, but would crash if it did */
3475 if (cmdData
== NULL
)
3478 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3479 /* Return the string selected in the selection list area */
3480 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3481 if (!XmListGetSelectedPos(theList
, &seltable
, &n_sel
)) {
3485 sel_index
= seltable
[0] - 1;
3486 XtFree((XtPointer
)seltable
);
3490 text
= PERM_ALLOC_STR("");
3494 length
= strlen((char *)text_lines
[sel_index
]);
3495 text
= AllocString(length
+ 1);
3496 strcpy(text
, text_lines
[sel_index
]);
3499 /* don't need text_lines anymore: free it */
3500 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3501 XtFree((XtPointer
)text_lines
[sel_index
]);
3502 XtFree((XtPointer
)text_lines
);
3504 retVal
.tag
= STRING_TAG
;
3505 retVal
.val
.str
.rep
= text
;
3506 retVal
.val
.str
.len
= length
;
3507 ModifyReturnedValue(cmdData
->context
, retVal
);
3509 /* Find the index of the button which was pressed (stored in the userData
3510 field of the button widget). The 1st button, being a gadget, is not
3512 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3513 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3514 btnNum
= (int)userData
;
3518 /* Return the button number in the global variable $list_dialog_button */
3519 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3520 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3522 /* Pop down the dialog */
3523 XtDestroyWidget(XtParent(cmdData
->dialog
));
3524 cmdData
->dialog
= NULL
;
3526 /* Continue preempted macro execution */
3527 ResumeMacroExecution(window
);
3530 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
3533 WindowInfo
*window
= (WindowInfo
*)clientData
;
3534 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3540 /* shouldn't happen, but would crash if it did */
3541 if (cmdData
== NULL
)
3544 /* don't need text_lines anymore: retrieve it then free it */
3545 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3546 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3547 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3548 XtFree((XtPointer
)text_lines
[sel_index
]);
3549 XtFree((XtPointer
)text_lines
);
3551 /* Return an empty string */
3552 retVal
.tag
= STRING_TAG
;
3553 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3554 retVal
.val
.str
.len
= 0;
3555 ModifyReturnedValue(cmdData
->context
, retVal
);
3557 /* Return button number 0 in the global variable $list_dialog_button */
3558 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3559 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= 0;
3561 /* Pop down the dialog */
3562 XtDestroyWidget(XtParent(cmdData
->dialog
));
3563 cmdData
->dialog
= NULL
;
3565 /* Continue preempted macro execution */
3566 ResumeMacroExecution(window
);
3568 /* T Balinski End */
3570 #ifdef LESSTIF_VERSION
3571 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3574 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3576 if (clientData
!= NULL
) {
3577 listDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3581 #endif /* LESSTIF_VERSION */
3584 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3585 DataValue
*result
, char **errMsg
)
3587 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3588 char *leftStr
, *rightStr
, *argStr
;
3589 int considerCase
= True
;
3594 return(wrongNArgsErr(errMsg
));
3596 if (!readStringArg(argList
[0], &leftStr
, stringStorage
[0], errMsg
))
3598 if (!readStringArg(argList
[1], &rightStr
, stringStorage
[1], errMsg
))
3600 for (i
= 2; i
< nArgs
; ++i
) {
3601 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
3603 else if (!strcmp(argStr
, "case"))
3604 considerCase
= True
;
3605 else if (!strcmp(argStr
, "nocase"))
3606 considerCase
= False
;
3608 *errMsg
= "Unrecognized argument to %s";
3613 compareResult
= strcmp(leftStr
, rightStr
);
3614 compareResult
= (compareResult
> 0) ? 1 : ((compareResult
< 0) ? -1 : 0);
3617 compareResult
= strCaseCmp(leftStr
, rightStr
);
3619 result
->tag
= INT_TAG
;
3620 result
->val
.n
= compareResult
;
3625 ** This function is intended to split strings into an array of substrings
3626 ** Importatnt note: It should always return at least one entry with key 0
3627 ** split("", ",") result[0] = ""
3628 ** split("1,2", ",") result[0] = "1" result[1] = "2"
3629 ** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
3631 ** This behavior is specifically important when used to break up
3632 ** array sub-scripts
3635 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3636 DataValue
*result
, char **errMsg
)
3638 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3639 char *sourceStr
, *splitStr
, *typeSplitStr
;
3640 int searchType
, beginPos
, foundStart
, foundEnd
, strLength
;
3641 int found
, elementEnd
, indexNum
;
3642 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
3647 return(wrongNArgsErr(errMsg
));
3649 if (!readStringArg(argList
[0], &sourceStr
, stringStorage
[0], errMsg
)) {
3650 *errMsg
= "first argument must be a string: %s";
3653 if (!readStringArg(argList
[1], &splitStr
, stringStorage
[1], errMsg
)) {
3657 if (splitStr
[0] == 0) {
3661 if (splitStr
== NULL
) {
3662 *errMsg
= "second argument must be a non-empty string: %s";
3665 if (nArgs
> 2 && readStringArg(argList
[2], &typeSplitStr
, stringStorage
[2], errMsg
)) {
3666 if (!StringToSearchType(typeSplitStr
, &searchType
)) {
3667 *errMsg
= "unrecognized argument to %s";
3672 searchType
= SEARCH_LITERAL
;
3675 result
->tag
= ARRAY_TAG
;
3676 result
->val
.arrayPtr
= ArrayNew();
3680 strLength
= strlen(sourceStr
);
3682 while (found
&& beginPos
< strLength
) {
3683 sprintf(indexStr
, "%d", indexNum
);
3684 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3685 if (!allocIndexStr
) {
3686 *errMsg
= "array element failed to allocate key: %s";
3689 strcpy(allocIndexStr
, indexStr
);
3690 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
, searchType
,
3691 False
, beginPos
, &foundStart
, &foundEnd
,
3692 NULL
, NULL
, GetWindowDelimiters(window
));
3693 elementEnd
= found
? foundStart
: strLength
;
3694 elementLen
= elementEnd
- beginPos
;
3695 element
.tag
= STRING_TAG
;
3696 if (!AllocNStringNCpy(&element
.val
.str
, &sourceStr
[beginPos
], elementLen
)) {
3697 *errMsg
= "failed to allocate element value: %s";
3701 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3702 M_ARRAY_INSERT_FAILURE();
3705 beginPos
= found
? foundEnd
: strLength
;
3709 sprintf(indexStr
, "%d", indexNum
);
3710 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3711 if (!allocIndexStr
) {
3712 *errMsg
= "array element failed to allocate key: %s";
3715 strcpy(allocIndexStr
, indexStr
);
3716 element
.tag
= STRING_TAG
;
3717 element
.val
.str
.rep
= PERM_ALLOC_STR("");
3718 element
.val
.str
.len
= 0;
3720 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3721 M_ARRAY_INSERT_FAILURE();
3728 ** Set the backlighting string resource for the current window. If no parameter
3729 ** is passed or the value "default" is passed, it attempts to set the preference
3730 ** value of the resource. If the empty string is passed, the backlighting string
3731 ** will be cleared, turning off backlighting.
3734 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
3735 int nArgs, DataValue *result, char **errMsg)
3737 char *backlightString;
3740 backlightString = GetPrefBacklightCharTypes();
3742 else if (nArgs == 1) {
3743 if (argList[0].tag != STRING_TAG) {
3744 *errMsg = "%s not called with a string parameter";
3747 backlightString = argList[0].val.str.rep;
3750 return wrongNArgsErr(errMsg);
3752 if (strcmp(backlightString, "default") == 0)
3753 backlightString = GetPrefBacklightCharTypes();
3754 if (backlightString && *backlightString == '\0') / * empty string param * /
3755 backlightString = NULL; / * turns of backlighting * /
3757 SetBacklightChars(window, backlightString);
3761 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3762 DataValue
*result
, char **errMsg
)
3764 result
->tag
= INT_TAG
;
3765 result
->val
.n
= TextGetCursorPos(window
->lastFocus
);
3769 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3770 DataValue
*result
, char **errMsg
)
3772 int line
, cursorPos
, colNum
;
3774 result
->tag
= INT_TAG
;
3775 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3776 if (!TextPosToLineAndCol(window
->lastFocus
, cursorPos
, &line
, &colNum
))
3777 line
= BufCountLines(window
->buffer
, 0, cursorPos
) + 1;
3778 result
->val
.n
= line
;
3782 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3783 DataValue
*result
, char **errMsg
)
3785 textBuffer
*buf
= window
->buffer
;
3788 result
->tag
= INT_TAG
;
3789 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3790 result
->val
.n
= BufCountDispChars(buf
, BufStartOfLine(buf
, cursorPos
),
3795 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3796 DataValue
*result
, char **errMsg
)
3798 result
->tag
= STRING_TAG
;
3799 AllocNStringCpy(&result
->val
.str
, window
->filename
);
3803 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3804 DataValue
*result
, char **errMsg
)
3806 result
->tag
= STRING_TAG
;
3807 AllocNStringCpy(&result
->val
.str
, window
->path
);
3811 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3812 DataValue
*result
, char **errMsg
)
3814 result
->tag
= INT_TAG
;
3815 result
->val
.n
= window
->buffer
->length
;
3819 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3820 DataValue
*result
, char **errMsg
)
3822 result
->tag
= INT_TAG
;
3823 result
->val
.n
= window
->buffer
->primary
.selected
?
3824 window
->buffer
->primary
.start
: -1;
3828 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3829 DataValue
*result
, char **errMsg
)
3831 result
->tag
= INT_TAG
;
3832 result
->val
.n
= window
->buffer
->primary
.selected
?
3833 window
->buffer
->primary
.end
: -1;
3837 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3838 DataValue
*result
, char **errMsg
)
3840 selection
*sel
= &window
->buffer
->primary
;
3842 result
->tag
= INT_TAG
;
3843 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectStart
: -1;
3847 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3848 DataValue
*result
, char **errMsg
)
3850 selection
*sel
= &window
->buffer
->primary
;
3852 result
->tag
= INT_TAG
;
3853 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectEnd
: -1;
3857 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3858 DataValue
*result
, char **errMsg
)
3862 XtVaGetValues(window
->textArea
, textNcolumns
, &nCols
,
3863 textNwrapMargin
, &margin
, NULL
);
3864 result
->tag
= INT_TAG
;
3865 result
->val
.n
= margin
== 0 ? nCols
: margin
;
3869 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3870 DataValue
*result
, char **errMsg
)
3872 result
->tag
= INT_TAG
;
3873 result
->val
.n
= window
->showStats
? 1 : 0;
3877 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3878 DataValue
*result
, char **errMsg
)
3880 result
->tag
= INT_TAG
;
3881 result
->val
.n
= window
->showISearchLine
? 1 : 0;
3885 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3886 DataValue
*result
, char **errMsg
)
3888 result
->tag
= INT_TAG
;
3889 result
->val
.n
= window
->showLineNumbers
? 1 : 0;
3893 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3894 DataValue
*result
, char **errMsg
)
3898 switch (window
->indentStyle
) {
3899 case NO_AUTO_INDENT
:
3900 res
= PERM_ALLOC_STR("off");
3903 res
= PERM_ALLOC_STR("on");
3906 res
= PERM_ALLOC_STR("smart");
3909 *errMsg
= "Invalid indent style value encountered in %s";
3913 result
->tag
= STRING_TAG
;
3914 result
->val
.str
.rep
= res
;
3915 result
->val
.str
.len
= strlen(res
);
3919 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3920 DataValue
*result
, char **errMsg
)
3924 switch (window
->wrapMode
) {
3926 res
= PERM_ALLOC_STR("none");
3929 res
= PERM_ALLOC_STR("auto");
3931 case CONTINUOUS_WRAP
:
3932 res
= PERM_ALLOC_STR("continuous");
3935 *errMsg
= "Invalid wrap style value encountered in %s";
3939 result
->tag
= STRING_TAG
;
3940 result
->val
.str
.rep
= res
;
3941 result
->val
.str
.len
= strlen(res
);
3945 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3946 DataValue
*result
, char **errMsg
)
3948 result
->tag
= INT_TAG
;
3949 result
->val
.n
= window
->highlightSyntax
? 1 : 0;
3953 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3954 DataValue
*result
, char **errMsg
)
3956 result
->tag
= INT_TAG
;
3957 result
->val
.n
= window
->saveOldVersion
? 1 : 0;
3961 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3962 DataValue
*result
, char **errMsg
)
3964 result
->tag
= INT_TAG
;
3965 result
->val
.n
= window
->autoSave
? 1 : 0;
3969 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3970 DataValue
*result
, char **errMsg
)
3974 switch (window
->showMatchingStyle
) {
3976 res
= PERM_ALLOC_STR(NO_FLASH_STRING
);
3979 res
= PERM_ALLOC_STR(FLASH_DELIMIT_STRING
);
3982 res
= PERM_ALLOC_STR(FLASH_RANGE_STRING
);
3985 *errMsg
= "Invalid match flashing style value encountered in %s";
3989 result
->tag
= STRING_TAG
;
3990 result
->val
.str
.rep
= res
;
3991 result
->val
.str
.len
= strlen(res
);
3995 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3996 DataValue
*result
, char **errMsg
)
3998 result
->tag
= INT_TAG
;
3999 result
->val
.n
= window
->overstrike
? 1 : 0;
4003 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4004 DataValue
*result
, char **errMsg
)
4006 result
->tag
= INT_TAG
;
4007 result
->val
.n
= (IS_ANY_LOCKED(window
->lockReasons
)) ? 1 : 0;
4011 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4012 DataValue
*result
, char **errMsg
)
4014 result
->tag
= INT_TAG
;
4015 result
->val
.n
= (IS_USER_LOCKED(window
->lockReasons
)) ? 1 : 0;
4019 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4020 DataValue
*result
, char **errMsg
)
4024 switch (window
->fileFormat
) {
4025 case UNIX_FILE_FORMAT
:
4026 res
= PERM_ALLOC_STR("unix");
4028 case DOS_FILE_FORMAT
:
4029 res
= PERM_ALLOC_STR("dos");
4031 case MAC_FILE_FORMAT
:
4032 res
= PERM_ALLOC_STR("macintosh");
4035 *errMsg
= "Invalid linefeed style value encountered in %s";
4038 result
->tag
= STRING_TAG
;
4039 result
->val
.str
.rep
= res
;
4040 result
->val
.str
.len
= strlen(res
);
4044 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4045 DataValue
*result
, char **errMsg
)
4047 result
->tag
= STRING_TAG
;
4048 AllocNStringCpy(&result
->val
.str
, window
->fontName
);
4052 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4053 DataValue
*result
, char **errMsg
)
4055 result
->tag
= STRING_TAG
;
4056 AllocNStringCpy(&result
->val
.str
, window
->italicFontName
);
4060 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4061 DataValue
*result
, char **errMsg
)
4063 result
->tag
= STRING_TAG
;
4064 AllocNStringCpy(&result
->val
.str
, window
->boldFontName
);
4068 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4069 DataValue
*result
, char **errMsg
)
4071 result
->tag
= STRING_TAG
;
4072 AllocNStringCpy(&result
->val
.str
, window
->boldItalicFontName
);
4076 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4077 DataValue
*result
, char **errMsg
)
4079 result
->tag
= STRING_TAG
;
4080 result
->val
.str
.rep
= PERM_ALLOC_STR(ARRAY_DIM_SEP
);
4081 result
->val
.str
.len
= strlen(result
->val
.str
.rep
);
4085 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4086 DataValue
*result
, char **errMsg
)
4088 result
->tag
= INT_TAG
;
4089 result
->val
.n
= TextGetMinFontWidth(window
->textArea
, window
->highlightSyntax
);
4093 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4094 DataValue
*result
, char **errMsg
)
4096 result
->tag
= INT_TAG
;
4097 result
->val
.n
= TextGetMaxFontWidth(window
->textArea
, window
->highlightSyntax
);
4101 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4102 DataValue
*result
, char **errMsg
)
4104 result
->tag
= INT_TAG
;
4105 result
->val
.n
= TextFirstVisibleLine(window
->lastFocus
);
4109 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4110 DataValue
*result
, char **errMsg
)
4112 result
->tag
= INT_TAG
;
4113 result
->val
.n
= TextNumVisibleLines(window
->lastFocus
);
4117 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4118 DataValue
*result
, char **errMsg
)
4120 result
->tag
= INT_TAG
;
4121 result
->val
.n
= TextVisibleWidth(window
->lastFocus
);
4125 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4126 DataValue
*result
, char **errMsg
)
4128 result
->tag
= INT_TAG
;
4129 result
->val
.n
= WidgetToPaneIndex(window
, window
->lastFocus
) + 1;
4133 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4134 DataValue
*result
, char **errMsg
)
4136 result
->tag
= INT_TAG
;
4137 result
->val
.n
= window
->nPanes
+ 1;
4141 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4142 DataValue
*result
, char **errMsg
)
4144 result
->tag
= ARRAY_TAG
;
4145 result
->val
.arrayPtr
= NULL
;
4149 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4150 DataValue
*result
, char **errMsg
)
4152 result
->tag
= STRING_TAG
;
4153 AllocNStringCpy(&result
->val
.str
, GetPrefServerName());
4157 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4158 DataValue
*result
, char **errMsg
)
4160 result
->tag
= INT_TAG
;
4161 result
->val
.n
= window
->buffer
->tabDist
;
4165 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4166 DataValue
*result
, char **errMsg
)
4170 XtVaGetValues(window
->textArea
, textNemulateTabs
, &dist
, NULL
);
4171 result
->tag
= INT_TAG
;
4172 result
->val
.n
= dist
== 0 ? -1 : dist
;
4176 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4177 DataValue
*result
, char **errMsg
)
4179 result
->tag
= INT_TAG
;
4180 result
->val
.n
= window
->buffer
->useTabs
;
4184 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4185 DataValue
*result
, char **errMsg
)
4187 result
->tag
= INT_TAG
;
4188 result
->val
.n
= window
->fileChanged
;
4192 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4193 DataValue
*result
, char **errMsg
)
4195 char *lmName
= LanguageModeName(window
->languageMode
);
4199 result
->tag
= STRING_TAG
;
4200 AllocNStringCpy(&result
->val
.str
, lmName
);
4205 static int backlightStringMV(WindowInfo *window, DataValue *argList,
4206 int nArgs, DataValue *result, char **errMsg)
4208 char *backlightString = window->backlightCharTypes;
4210 result->tag = STRING_TAG;
4211 if (!backlightString || !window->backlightChars)
4212 backlightString = "";
4213 AllocNStringCpy(&result->val.str, backlightString);
4217 /* -------------------------------------------------------------------------- */
4220 ** Range set macro variables and functions
4223 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4224 DataValue
*result
, char **errMsg
)
4226 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4227 unsigned char *rangesetList
;
4228 char *allocIndexStr
;
4229 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4233 result
->tag
= ARRAY_TAG
;
4234 result
->val
.arrayPtr
= ArrayNew();
4236 if (rangesetTable
== NULL
) {
4240 rangesetList
= RangesetGetList(rangesetTable
);
4241 nRangesets
= strlen((char*)rangesetList
);
4242 for(i
= 0; i
< nRangesets
; i
++) {
4243 element
.tag
= INT_TAG
;
4244 element
.val
.n
= rangesetList
[i
];
4246 sprintf(indexStr
, "%d", nRangesets
- i
- 1);
4247 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4248 if (allocIndexStr
== NULL
)
4249 M_FAILURE("Failed to allocate array key in %s");
4250 strcpy(allocIndexStr
, indexStr
);
4252 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4253 M_FAILURE("Failed to insert array element in %s");
4261 ** Built-in macro subroutine to create a new rangeset or rangesets.
4262 ** If called with one argument: $1 is the number of rangesets required and
4263 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4264 ** (or an empty array if the requested number of rangesets are not available).
4265 ** If called with no arguments, returns a single rangeset label (not an array),
4266 ** or an empty string if there are no rangesets available.
4268 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4269 DataValue
*result
, char **errMsg
)
4272 int i
, nRangesetsRequired
;
4274 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
4276 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4279 return wrongNArgsErr(errMsg
);
4281 if (rangesetTable
== NULL
) {
4282 window
->buffer
->rangesetTable
= rangesetTable
=
4283 RangesetTableAlloc(window
->buffer
);
4287 label
= RangesetCreate(rangesetTable
);
4289 result
->tag
= INT_TAG
;
4290 result
->val
.n
= label
;
4294 if (!readIntArg(argList
[0], &nRangesetsRequired
, errMsg
))
4297 result
->tag
= ARRAY_TAG
;
4298 result
->val
.arrayPtr
= ArrayNew();
4300 if (nRangesetsRequired
> nRangesetsAvailable(rangesetTable
))
4303 for (i
= 0; i
< nRangesetsRequired
; i
++) {
4304 element
.tag
= INT_TAG
;
4305 element
.val
.n
= RangesetCreate(rangesetTable
);
4307 sprintf(indexStr
, "%d", i
);
4308 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4309 if (!allocIndexStr
) {
4310 *errMsg
= "Array element failed to allocate key: %s";
4313 strcpy(allocIndexStr
, indexStr
);
4314 ArrayInsert(result
, allocIndexStr
, &element
);
4323 ** Built-in macro subroutine for forgetting a range set.
4326 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4327 DataValue
*result
, char **errMsg
)
4329 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4332 char keyString
[TYPE_INT_STR_SIZE(int)];
4333 int deleteLabels
[N_RANGESETS
];
4338 return wrongNArgsErr(errMsg
);
4341 if (argList
[0].tag
== ARRAY_TAG
) {
4342 array
= &argList
[0];
4343 arraySize
= ArraySize(array
);
4345 if (arraySize
> N_RANGESETS
) {
4346 M_FAILURE("Too many elements in array in %s");
4349 for (i
= 0; i
< arraySize
; i
++) {
4350 sprintf(keyString
, "%d", i
);
4352 if (!ArrayGet(array
, keyString
, &element
)) {
4353 M_FAILURE("Invalid key in array in %s");
4356 if (!readIntArg(element
, &label
, errMsg
)
4357 || !RangesetLabelOK(label
)) {
4358 M_FAILURE("Invalid rangeset label in array in %s");
4361 deleteLabels
[i
] = label
;
4364 for (i
= 0; i
< arraySize
; i
++) {
4365 RangesetForget(rangesetTable
, deleteLabels
[i
]);
4370 if (!readIntArg(argList
[0], &label
, errMsg
)
4371 || !RangesetLabelOK(label
)) {
4372 M_FAILURE("Invalid rangeset label in %s");
4375 if(rangesetTable
!= NULL
) {
4376 RangesetForget(rangesetTable
, label
);
4381 result
->tag
= NO_TAG
;
4387 ** Built-in macro subroutine for getting all range sets with a specfic name.
4388 ** Arguments are $1: range set name.
4389 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4392 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4393 DataValue
*result
, char **errMsg
)
4395 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4398 char *name
, *rangeset_name
;
4399 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4400 unsigned char *rangesetList
;
4401 char *allocIndexStr
;
4402 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4403 int nRangesets
, i
, insertIndex
= 0;
4407 return wrongNArgsErr(errMsg
);
4410 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
4411 M_FAILURE("First parameter is not a name string in %s");
4414 result
->tag
= ARRAY_TAG
;
4415 result
->val
.arrayPtr
= ArrayNew();
4417 if (rangesetTable
== NULL
) {
4421 rangesetList
= RangesetGetList(rangesetTable
);
4422 nRangesets
= strlen((char *)rangesetList
);
4423 for (i
= 0; i
< nRangesets
; ++i
) {
4424 label
= rangesetList
[i
];
4425 rangeset
= RangesetFetch(rangesetTable
, label
);
4427 rangeset_name
= RangesetGetName(rangeset
);
4428 if (strcmp(name
, rangeset_name
? rangeset_name
: "") == 0) {
4429 element
.tag
= INT_TAG
;
4430 element
.val
.n
= label
;
4432 sprintf(indexStr
, "%d", insertIndex
);
4433 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4434 if (allocIndexStr
== NULL
)
4435 M_FAILURE("Failed to allocate array key in %s");
4437 strcpy(allocIndexStr
, indexStr
);
4439 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4440 M_FAILURE("Failed to insert array element in %s");
4452 ** Built-in macro subroutine for adding to a range set. Arguments are $1: range
4453 ** set label (one integer), then either (a) $2: source range set label,
4454 ** (b) $2: int start-range, $3: int end-range, (c) nothing (use selection
4455 ** if any to specify range to add - must not be rectangular). Returns the
4456 ** index of the newly added range (cases b and c), or 0 (case a).
4459 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4460 DataValue
*result
, char **errMsg
)
4462 textBuffer
*buffer
= window
->buffer
;
4463 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4464 Rangeset
*targetRangeset
, *sourceRangeset
;
4465 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
, index
;
4468 if (nArgs
< 1 || nArgs
> 3)
4469 return wrongNArgsErr(errMsg
);
4471 if (!readIntArg(argList
[0], &label
, errMsg
)
4472 || !RangesetLabelOK(label
)) {
4473 M_FAILURE("First parameter is an invalid rangeset label in %s");
4476 if (rangesetTable
== NULL
) {
4477 M_FAILURE("Rangeset does not exist in %s");
4480 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4482 if (targetRangeset
== NULL
) {
4483 M_FAILURE("Rangeset does not exist in %s");
4489 /* pick up current selection in this window */
4490 if (!BufGetSelectionPos(buffer
, &start
, &end
,
4491 &isRect
, &rectStart
, &rectEnd
) || isRect
) {
4492 M_FAILURE("Selection missing or rectangular in call to %s");
4494 if (!RangesetAddBetween(targetRangeset
, start
, end
)) {
4495 M_FAILURE("Failure to add selection in %s");
4500 /* add ranges taken from a second set */
4501 if (!readIntArg(argList
[1], &label
, errMsg
)
4502 || !RangesetLabelOK(label
)) {
4503 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4506 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4507 if (sourceRangeset
== NULL
) {
4508 M_FAILURE("Second rangeset does not exist in %s");
4511 RangesetAdd(targetRangeset
, sourceRangeset
);
4515 /* add a range bounded by the start and end positions in $2, $3 */
4516 if (!readIntArg(argList
[1], &start
, errMsg
)) {
4519 if (!readIntArg(argList
[2], &end
, errMsg
)) {
4523 /* make sure range is in order and fits buffer size */
4524 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4525 if (start
< 0) start
= 0;
4526 if (start
> maxpos
) start
= maxpos
;
4527 if (end
< 0) end
= 0;
4528 if (end
> maxpos
) end
= maxpos
;
4529 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4531 if ((start
!= end
) && !RangesetAddBetween(targetRangeset
, start
, end
)) {
4532 M_FAILURE("Failed to add range in %s");
4536 /* (to) which range did we just add? */
4537 if (nArgs
!= 2 && start
>= 0) {
4538 start
= (start
+ end
) / 2; /* "middle" of added range */
4539 index
= 1 + RangesetFindRangeOfPos(targetRangeset
, start
, False
);
4546 result
->tag
= INT_TAG
;
4547 result
->val
.n
= index
;
4553 ** Built-in macro subroutine for removing from a range set. Almost identical to
4554 ** rangesetAddMS() - only changes are from RangesetAdd()/RangesetAddBetween()
4555 ** to RangesetSubtract()/RangesetSubtractBetween(), the handling of an
4556 ** undefined destination range, and that it returns no value.
4559 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4560 DataValue
*result
, char **errMsg
)
4562 textBuffer
*buffer
= window
->buffer
;
4563 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4564 Rangeset
*targetRangeset
, *sourceRangeset
;
4565 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
;
4568 if (nArgs
< 1 || nArgs
> 3) {
4569 return wrongNArgsErr(errMsg
);
4572 if (!readIntArg(argList
[0], &label
, errMsg
)
4573 || !RangesetLabelOK(label
)) {
4574 M_FAILURE("First parameter is an invalid rangeset label in %s");
4577 if (rangesetTable
== NULL
) {
4578 M_FAILURE("Rangeset does not exist in %s");
4581 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4582 if (targetRangeset
== NULL
) {
4583 M_FAILURE("Rangeset does not exist in %s");
4587 /* remove current selection in this window */
4588 if (!BufGetSelectionPos(buffer
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)
4590 M_FAILURE("Selection missing or rectangular in call to %s");
4592 RangesetRemoveBetween(targetRangeset
, start
, end
);
4596 /* remove ranges taken from a second set */
4597 if (!readIntArg(argList
[1], &label
, errMsg
)
4598 || !RangesetLabelOK(label
)) {
4599 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4602 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4603 if (sourceRangeset
== NULL
) {
4604 M_FAILURE("Second rangeset does not exist in %s");
4606 RangesetRemove(targetRangeset
, sourceRangeset
);
4610 /* remove a range bounded by the start and end positions in $2, $3 */
4611 if (!readIntArg(argList
[1], &start
, errMsg
))
4613 if (!readIntArg(argList
[2], &end
, errMsg
))
4616 /* make sure range is in order and fits buffer size */
4617 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4618 if (start
< 0) start
= 0;
4619 if (start
> maxpos
) start
= maxpos
;
4620 if (end
< 0) end
= 0;
4621 if (end
> maxpos
) end
= maxpos
;
4622 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4624 RangesetRemoveBetween(targetRangeset
, start
, end
);
4628 result
->tag
= NO_TAG
;
4634 ** Built-in macro subroutine to invert a range set. Argument is $1: range set
4635 ** label (one alphabetic character). Returns nothing. Fails if range set
4639 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4640 DataValue
*result
, char **errMsg
)
4643 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4648 return wrongNArgsErr(errMsg
);
4650 if (!readIntArg(argList
[0], &label
, errMsg
)
4651 || !RangesetLabelOK(label
)) {
4652 M_FAILURE("First parameter is an invalid rangeset label in %s");
4655 if (rangesetTable
== NULL
) {
4656 M_FAILURE("Rangeset does not exist in %s");
4659 rangeset
= RangesetFetch(rangesetTable
, label
);
4660 if (rangeset
== NULL
) {
4661 M_FAILURE("Rangeset does not exist in %s");
4664 if (RangesetInverse(rangeset
) < 0) {
4665 M_FAILURE("Problem inverting rangeset in %s");
4669 result
->tag
= NO_TAG
;
4675 ** Built-in macro subroutine for finding out info about a rangeset. Takes one
4676 ** argument of a rangeset label. Returns an array with the following keys:
4677 ** defined, count, color, mode.
4680 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4681 DataValue
*result
, char **errMsg
)
4683 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4684 Rangeset
*rangeset
= NULL
;
4686 char *color
, *name
, *mode
;
4691 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 rangeset
= RangesetFetch(rangesetTable
, label
);
4702 RangesetGetInfo(rangeset
, &defined
, &label
, &count
, &color
, &name
, &mode
);
4705 result
->tag
= ARRAY_TAG
;
4706 result
->val
.arrayPtr
= ArrayNew();
4708 element
.tag
= INT_TAG
;
4709 element
.val
.n
= defined
;
4710 if (!ArrayInsert(result
, PERM_ALLOC_STR("defined"), &element
))
4711 M_FAILURE("Failed to insert array element \"defined\" in %s");
4713 element
.tag
= INT_TAG
;
4714 element
.val
.n
= count
;
4715 if (!ArrayInsert(result
, PERM_ALLOC_STR("count"), &element
))
4716 M_FAILURE("Failed to insert array element \"count\" in %s");
4718 element
.tag
= STRING_TAG
;
4719 if (!AllocNStringCpy(&element
.val
.str
, color
))
4720 M_FAILURE("Failed to allocate array value \"color\" in %s");
4721 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &element
))
4722 M_FAILURE("Failed to insert array element \"color\" in %s");
4724 element
.tag
= STRING_TAG
;
4725 if (!AllocNStringCpy(&element
.val
.str
, name
))
4726 M_FAILURE("Failed to allocate array value \"name\" in %s");
4727 if (!ArrayInsert(result
, PERM_ALLOC_STR("name"), &element
)) {
4728 M_FAILURE("Failed to insert array element \"name\" in %s");
4731 element
.tag
= STRING_TAG
;
4732 if (!AllocNStringCpy(&element
.val
.str
, mode
))
4733 M_FAILURE("Failed to allocate array value \"mode\" in %s");
4734 if (!ArrayInsert(result
, PERM_ALLOC_STR("mode"), &element
))
4735 M_FAILURE("Failed to insert array element \"mode\" in %s");
4741 ** Built-in macro subroutine for finding the extent of a range in a set.
4742 ** If only one parameter is supplied, use the spanning range of all
4743 ** ranges, otherwise select the individual range specified. Returns
4744 ** an array with the keys "start" and "end" and values
4747 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4748 DataValue
*result
, char **errMsg
)
4750 textBuffer
*buffer
= window
->buffer
;
4751 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4753 int start
, end
, dummy
, rangeIndex
, ok
;
4757 if (nArgs
< 1 || nArgs
> 2) {
4758 return wrongNArgsErr(errMsg
);
4761 if (!readIntArg(argList
[0], &label
, errMsg
)
4762 || !RangesetLabelOK(label
)) {
4763 M_FAILURE("First parameter is an invalid rangeset label in %s");
4766 if (rangesetTable
== NULL
) {
4767 M_FAILURE("Rangeset does not exist in %s");
4771 rangeset
= RangesetFetch(rangesetTable
, label
);
4772 if (rangeset
!= NULL
) {
4774 rangeIndex
= RangesetGetNRanges(rangeset
) - 1;
4775 ok
= RangesetFindRangeNo(rangeset
, 0, &start
, &dummy
);
4776 ok
&= RangesetFindRangeNo(rangeset
, rangeIndex
, &dummy
, &end
);
4779 else if (nArgs
== 2) {
4780 if (!readIntArg(argList
[1], &rangeIndex
, errMsg
)) {
4783 ok
= RangesetFindRangeNo(rangeset
, rangeIndex
-1, &start
, &end
);
4788 result
->tag
= ARRAY_TAG
;
4789 result
->val
.arrayPtr
= ArrayNew();
4794 element
.tag
= INT_TAG
;
4795 element
.val
.n
= start
;
4796 if (!ArrayInsert(result
, PERM_ALLOC_STR("start"), &element
))
4797 M_FAILURE("Failed to insert array element \"start\" in %s");
4799 element
.tag
= INT_TAG
;
4800 element
.val
.n
= end
;
4801 if (!ArrayInsert(result
, PERM_ALLOC_STR("end"), &element
))
4802 M_FAILURE("Failed to insert array element \"end\" in %s");
4808 ** Built-in macro subroutine for checking a position against a range. If only
4809 ** one parameter is supplied, the current cursor position is used. Returns
4810 ** false (zero) if not in a range, range index (1-based) if in a range;
4811 ** fails if parameters were bad.
4814 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
4815 int nArgs
, DataValue
*result
, char **errMsg
)
4817 textBuffer
*buffer
= window
->buffer
;
4818 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4820 int pos
, rangeIndex
, maxpos
;
4823 if (nArgs
< 1 || nArgs
> 2) {
4824 return wrongNArgsErr(errMsg
);
4827 if (!readIntArg(argList
[0], &label
, errMsg
)
4828 || !RangesetLabelOK(label
)) {
4829 M_FAILURE("First parameter is an invalid rangeset label in %s");
4832 if (rangesetTable
== NULL
) {
4833 M_FAILURE("Rangeset does not exist in %s");
4836 rangeset
= RangesetFetch(rangesetTable
, label
);
4837 if (rangeset
== NULL
) {
4838 M_FAILURE("Rangeset does not exist in %s");
4842 pos
= TextGetCursorPos(window
->lastFocus
);
4844 else if (nArgs
== 2) {
4845 if (!readIntArg(argList
[1], &pos
, errMsg
))
4849 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4850 if (pos
< 0 || pos
> maxpos
) {
4854 rangeIndex
= RangesetFindRangeOfPos(rangeset
, pos
, False
) + 1;
4858 result
->tag
= INT_TAG
;
4859 result
->val
.n
= rangeIndex
;
4864 ** Set the color of a range set's ranges. it is ignored if the color cannot be
4865 ** found/applied. If no color is applied, any current color is removed. Returns
4866 ** true if the rangeset is valid.
4869 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
4870 int nArgs
, DataValue
*result
, char **errMsg
)
4872 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4873 textBuffer
*buffer
= window
->buffer
;
4874 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4880 return wrongNArgsErr(errMsg
);
4883 if (!readIntArg(argList
[0], &label
, errMsg
)
4884 || !RangesetLabelOK(label
)) {
4885 M_FAILURE("First parameter is an invalid rangeset label in %s");
4888 if (rangesetTable
== NULL
) {
4889 M_FAILURE("Rangeset does not exist in %s");
4892 rangeset
= RangesetFetch(rangesetTable
, label
);
4893 if (rangeset
== NULL
) {
4894 M_FAILURE("Rangeset does not exist in %s");
4898 if (rangeset
!= NULL
) {
4899 if (!readStringArg(argList
[1], &color_name
, stringStorage
[0], errMsg
)) {
4900 M_FAILURE("Second parameter is not a color name string in %s");
4904 RangesetAssignColorName(rangeset
, color_name
);
4907 result
->tag
= NO_TAG
;
4912 ** Set the name of a range set's ranges. Returns
4913 ** true if the rangeset is valid.
4916 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
4917 int nArgs
, DataValue
*result
, char **errMsg
)
4919 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4920 textBuffer
*buffer
= window
->buffer
;
4921 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4927 return wrongNArgsErr(errMsg
);
4930 if (!readIntArg(argList
[0], &label
, errMsg
)
4931 || !RangesetLabelOK(label
)) {
4932 M_FAILURE("First parameter is an invalid rangeset label in %s");
4935 if (rangesetTable
== NULL
) {
4936 M_FAILURE("Rangeset does not exist in %s");
4939 rangeset
= RangesetFetch(rangesetTable
, label
);
4940 if (rangeset
== NULL
) {
4941 M_FAILURE("Rangeset does not exist in %s");
4945 if (rangeset
!= NULL
) {
4946 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
)) {
4947 M_FAILURE("Second parameter is not a valid name string in %s");
4951 RangesetAssignName(rangeset
, name
);
4954 result
->tag
= NO_TAG
;
4959 ** Change a range's modification response. Returns true if the rangeset is
4960 ** valid and the response type name is valid.
4963 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
4964 int nArgs
, DataValue
*result
, char **errMsg
)
4966 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4967 textBuffer
*buffer
= window
->buffer
;
4968 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4970 char *update_fn_name
;
4974 if (nArgs
< 1 || nArgs
> 2) {
4975 return wrongNArgsErr(errMsg
);
4978 if (!readIntArg(argList
[0], &label
, errMsg
)
4979 || !RangesetLabelOK(label
)) {
4980 M_FAILURE("First parameter is an invalid rangeset label in %s");
4983 if (rangesetTable
== NULL
) {
4984 M_FAILURE("Rangeset does not exist in %s");
4987 rangeset
= RangesetFetch(rangesetTable
, label
);
4988 if (rangeset
== NULL
) {
4989 M_FAILURE("Rangeset does not exist in %s");
4992 update_fn_name
= "";
4993 if (rangeset
!= NULL
) {
4995 if (!readStringArg(argList
[1], &update_fn_name
, stringStorage
[0], errMsg
)) {
4996 M_FAILURE("Second parameter is not a string in %s");
5001 ok
= RangesetChangeModifyResponse(rangeset
, update_fn_name
);
5004 M_FAILURE("Second parameter is not a valid mode in %s");
5008 result
->tag
= NO_TAG
;
5012 /* -------------------------------------------------------------------------- */
5016 ** Routines to get details directly from the window.
5020 ** Sets up an array containing information about a style given its name or
5021 ** a buffer position (bufferPos >= 0) and its highlighting pattern code
5023 ** From the name we obtain:
5024 ** ["color"] Foreground color name of style
5025 ** ["background"] Background color name of style if specified
5026 ** ["bold"] '1' if style is bold, '0' otherwise
5027 ** ["italic"] '1' if style is italic, '0' otherwise
5028 ** Given position and pattern code we obtain:
5029 ** ["rgb"] RGB representation of foreground color of style
5030 ** ["back_rgb"] RGB representation of background color of style
5031 ** ["extent"] Forward distance from position over which style applies
5032 ** We only supply the style name if the includeName parameter is set:
5033 ** ["style"] Name of style
5036 static int fillStyleResult(DataValue
*result
, char **errMsg
,
5037 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
5038 Boolean includeName
, int patCode
, int bufferPos
)
5041 char colorValue
[20];
5044 /* initialize array */
5045 result
->tag
= ARRAY_TAG
;
5046 result
->val
.arrayPtr
= ArrayNew();
5048 /* the following array entries will be strings */
5049 DV
.tag
= STRING_TAG
;
5052 /* insert style name */
5053 if (preallocatedStyleName
) {
5054 DV
.val
.str
.rep
= styleName
;
5055 DV
.val
.str
.len
= strlen(styleName
);
5058 AllocNStringCpy(&DV
.val
.str
, styleName
);
5060 M_STR_ALLOC_ASSERT(DV
);
5061 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5062 M_ARRAY_INSERT_FAILURE();
5066 /* insert color name */
5067 AllocNStringCpy(&DV
.val
.str
, ColorOfNamedStyle(styleName
));
5068 M_STR_ALLOC_ASSERT(DV
);
5069 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &DV
)) {
5070 M_ARRAY_INSERT_FAILURE();
5073 /* Prepare array element for color value
5074 (only possible if we pass through the dynamic highlight pattern tables
5075 in other words, only if we have a pattern code) */
5077 HighlightColorValueOfCode(window
, patCode
, &r
, &g
, &b
);
5078 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5079 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5080 M_STR_ALLOC_ASSERT(DV
);
5081 if (!ArrayInsert(result
, PERM_ALLOC_STR("rgb"), &DV
)) {
5082 M_ARRAY_INSERT_FAILURE();
5086 /* Prepare array element for background color name */
5087 AllocNStringCpy(&DV
.val
.str
, BgColorOfNamedStyle(styleName
));
5088 M_STR_ALLOC_ASSERT(DV
);
5089 if (!ArrayInsert(result
, PERM_ALLOC_STR("background"), &DV
)) {
5090 M_ARRAY_INSERT_FAILURE();
5093 /* Prepare array element for background color value
5094 (only possible if we pass through the dynamic highlight pattern tables
5095 in other words, only if we have a pattern code) */
5097 GetHighlightBGColorOfCode(window
, patCode
, &r
, &g
, &b
);
5098 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5099 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5100 M_STR_ALLOC_ASSERT(DV
);
5101 if (!ArrayInsert(result
, PERM_ALLOC_STR("back_rgb"), &DV
)) {
5102 M_ARRAY_INSERT_FAILURE();
5106 /* the following array entries will be integers */
5109 /* Put boldness value in array */
5110 DV
.val
.n
= FontOfNamedStyleIsBold(styleName
);
5111 if (!ArrayInsert(result
, PERM_ALLOC_STR("bold"), &DV
)) {
5112 M_ARRAY_INSERT_FAILURE();
5115 /* Put italicity value in array */
5116 DV
.val
.n
= FontOfNamedStyleIsItalic(styleName
);
5117 if (!ArrayInsert(result
, PERM_ALLOC_STR("italic"), &DV
)) {
5118 M_ARRAY_INSERT_FAILURE();
5121 if (bufferPos
>= 0) {
5123 const char *styleNameNotUsed
= NULL
;
5124 DV
.val
.n
= StyleLengthOfCodeFromPos(window
, bufferPos
, &styleNameNotUsed
);
5125 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5126 M_ARRAY_INSERT_FAILURE();
5133 ** Returns an array containing information about the style of name $1
5134 ** ["color"] Foreground color name of style
5135 ** ["background"] Background color name of style if specified
5136 ** ["bold"] '1' if style is bold, '0' otherwise
5137 ** ["italic"] '1' if style is italic, '0' otherwise
5140 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5141 DataValue
*result
, char **errMsg
)
5143 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5146 /* Validate number of arguments */
5148 return wrongNArgsErr(errMsg
);
5151 /* Prepare result */
5152 result
->tag
= ARRAY_TAG
;
5153 result
->val
.arrayPtr
= NULL
;
5155 if (!readStringArg(argList
[0], &styleName
, stringStorage
[0], errMsg
)) {
5156 M_FAILURE("First parameter is not a string in %s");
5159 if (!NamedStyleExists(styleName
)) {
5160 /* if the given name is invalid we just return an empty array. */
5164 return fillStyleResult(result
, errMsg
, window
,
5165 styleName
, (argList
[0].tag
== STRING_TAG
), False
, 0, -1);
5169 ** Returns an array containing information about the style of position $1
5170 ** ["style"] Name of style
5171 ** ["color"] Foreground color name of style
5172 ** ["background"] Background color name of style if specified
5173 ** ["bold"] '1' if style is bold, '0' otherwise
5174 ** ["italic"] '1' if style is italic, '0' otherwise
5175 ** ["rgb"] RGB representation of foreground color of style
5176 ** ["back_rgb"] RGB representation of background color of style
5177 ** ["extent"] Forward distance from position over which style applies
5180 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5181 DataValue
*result
, char **errMsg
)
5185 textBuffer
*buf
= window
->buffer
;
5187 /* Validate number of arguments */
5189 return wrongNArgsErr(errMsg
);
5192 /* Prepare result */
5193 result
->tag
= ARRAY_TAG
;
5194 result
->val
.arrayPtr
= NULL
;
5196 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5200 /* Verify sane buffer position */
5201 if ((bufferPos
< 0) || (bufferPos
>= buf
->length
)) {
5202 /* If the position is not legal, we cannot guess anything about
5203 the style, so we return an empty array. */
5207 /* Determine pattern code */
5208 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5210 /* if there is no pattern we just return an empty array. */
5214 return fillStyleResult(result
, errMsg
, window
,
5215 HighlightStyleOfCode(window
, patCode
), False
, True
, patCode
, bufferPos
);
5219 ** Sets up an array containing information about a pattern given its name or
5220 ** a buffer position (bufferPos >= 0).
5221 ** From the name we obtain:
5222 ** ["style"] Name of style
5223 ** ["extent"] Forward distance from position over which style applies
5224 ** We only supply the pattern name if the includeName parameter is set:
5225 ** ["pattern"] Name of pattern
5228 static int fillPatternResult(DataValue
*result
, char **errMsg
,
5229 WindowInfo
*window
, char *patternName
, Boolean preallocatedPatternName
,
5230 Boolean includeName
, char* styleName
, int bufferPos
)
5234 /* initialize array */
5235 result
->tag
= ARRAY_TAG
;
5236 result
->val
.arrayPtr
= ArrayNew();
5238 /* the following array entries will be strings */
5239 DV
.tag
= STRING_TAG
;
5242 /* insert pattern name */
5243 if (preallocatedPatternName
) {
5244 DV
.val
.str
.rep
= patternName
;
5245 DV
.val
.str
.len
= strlen(patternName
);
5248 AllocNStringCpy(&DV
.val
.str
, patternName
);
5250 M_STR_ALLOC_ASSERT(DV
);
5251 if (!ArrayInsert(result
, PERM_ALLOC_STR("pattern"), &DV
)) {
5252 M_ARRAY_INSERT_FAILURE();
5256 /* insert style name */
5257 AllocNStringCpy(&DV
.val
.str
, styleName
);
5258 M_STR_ALLOC_ASSERT(DV
);
5259 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5260 M_ARRAY_INSERT_FAILURE();
5263 /* the following array entries will be integers */
5266 if (bufferPos
>= 0) {
5269 DV
.val
.n
= HighlightLengthOfCodeFromPos(window
, bufferPos
, &checkCode
);
5270 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5271 M_ARRAY_INSERT_FAILURE();
5279 ** Returns an array containing information about a highlighting pattern. The
5280 ** single parameter contains the pattern name for which this information is
5282 ** The returned array looks like this:
5283 ** ["style"] Name of style
5285 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5286 DataValue
*result
, char **errMsg
)
5288 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5289 char *patternName
= NULL
;
5290 highlightPattern
*pattern
;
5292 /* Begin of building the result. */
5293 result
->tag
= ARRAY_TAG
;
5294 result
->val
.arrayPtr
= NULL
;
5296 /* Validate number of arguments */
5298 return wrongNArgsErr(errMsg
);
5301 if (!readStringArg(argList
[0], &patternName
, stringStorage
[0], errMsg
)) {
5302 M_FAILURE("First parameter is not a string in %s");
5305 pattern
= FindPatternOfWindow(window
, patternName
);
5306 if (pattern
== NULL
) {
5307 /* The pattern's name is unknown. */
5311 return fillPatternResult(result
, errMsg
, window
, patternName
,
5312 (argList
[0].tag
== STRING_TAG
), False
, pattern
->style
, -1);
5316 ** Returns an array containing information about the highlighting pattern
5317 ** applied at a given position, passed as the only parameter.
5318 ** The returned array looks like this:
5319 ** ["pattern"] Name of pattern
5320 ** ["style"] Name of style
5321 ** ["extent"] Distance from position over which this pattern applies
5323 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5324 DataValue
*result
, char **errMsg
)
5327 textBuffer
*buffer
= window
->buffer
;
5330 /* Begin of building the result. */
5331 result
->tag
= ARRAY_TAG
;
5332 result
->val
.arrayPtr
= NULL
;
5334 /* Validate number of arguments */
5336 return wrongNArgsErr(errMsg
);
5339 /* The most straightforward case: Get a pattern, style and extent
5340 for a buffer position. */
5341 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5345 /* Verify sane buffer position
5346 * You would expect that buffer->length would be among the sane
5347 * positions, but we have n characters and n+1 buffer positions. */
5348 if ((bufferPos
< 0) || (bufferPos
>= buffer
->length
)) {
5349 /* If the position is not legal, we cannot guess anything about
5350 the highlighting pattern, so we return an empty array. */
5354 /* Determine the highlighting pattern used */
5355 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5357 /* if there is no highlighting pattern we just return an empty array. */
5361 return fillPatternResult(result
, errMsg
, window
,
5362 HighlightNameOfCode(window
, patCode
), False
, True
,
5363 HighlightStyleOfCode(window
, patCode
), bufferPos
);
5366 static int wrongNArgsErr(char **errMsg
)
5368 *errMsg
= "Wrong number of arguments to function %s";
5372 static int tooFewArgsErr(char **errMsg
)
5374 *errMsg
= "Too few arguments to function %s";
5379 ** strCaseCmp compares its arguments and returns 0 if the two strings
5380 ** are equal IGNORING case differences. Otherwise returns 1 or -1
5381 ** depending on relative comparison.
5383 static int strCaseCmp(char *str1
, char *str2
)
5387 for (c1
= str1
, c2
= str2
;
5388 (*c1
!= '\0' && *c2
!= '\0')
5389 && toupper((unsigned char)*c1
) == toupper((unsigned char)*c2
);
5394 if (((unsigned char)toupper((unsigned char)*c1
))
5395 > ((unsigned char)toupper((unsigned char)*c2
)))
5398 } else if (((unsigned char)toupper((unsigned char)*c1
))
5399 < ((unsigned char)toupper((unsigned char)*c2
)))
5409 ** Get an integer value from a tagged DataValue structure. Return True
5410 ** if conversion succeeded, and store result in *result, otherwise
5411 ** return False with an error message in *errMsg.
5413 static int readIntArg(DataValue dv
, int *result
, char **errMsg
)
5417 if (dv
.tag
== INT_TAG
) {
5420 } else if (dv
.tag
== STRING_TAG
) {
5421 for (c
=dv
.val
.str
.rep
; *c
!= '\0'; c
++) {
5422 if (!(isdigit((unsigned char)*c
) || *c
== ' ' || *c
== '\t')) {
5426 sscanf(dv
.val
.str
.rep
, "%d", result
);
5431 *errMsg
= "%s called with non-integer argument";
5436 ** Get an string value from a tagged DataValue structure. Return True
5437 ** if conversion succeeded, and store result in *result, otherwise
5438 ** return False with an error message in *errMsg. If an integer value
5439 ** is converted, write the string in the space provided by "stringStorage",
5440 ** which must be large enough to handle ints of the maximum size.
5442 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
5445 if (dv
.tag
== STRING_TAG
) {
5446 *result
= dv
.val
.str
.rep
;
5448 } else if (dv
.tag
== INT_TAG
) {
5449 sprintf(stringStorage
, "%d", dv
.val
.n
);
5450 *result
= stringStorage
;
5453 *errMsg
= "%s called with unknown object";