1 static const char CVSID
[] = "$Id: macro.c,v 1.116 2008/08/20 14:57:35 lebert Exp $";
2 /*******************************************************************************
4 * macro.c -- Macro file processing, learn/replay, and built-in macro *
7 * Copyright (C) 1999 Mark Edel *
9 * This is free software; you can redistribute it and/or modify it under the *
10 * terms of the GNU General Public License as published by the Free Software *
11 * Foundation; either version 2 of the License, or (at your option) any later *
12 * version. In addition, you may distribute versions of this program linked to *
13 * Motif or Open Motif. See README for details. *
15 * This software is distributed in the hope that it will be useful, but WITHOUT *
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
20 * You should have received a copy of the GNU General Public License along with *
21 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
22 * Place, Suite 330, Boston, MA 02111-1307 USA *
24 * Nirvana Text Editor *
27 * Written by Mark Edel *
29 *******************************************************************************/
32 #include "../config.h"
40 #include "preferences.h"
41 #include "interpret.h"
46 #include "smartIndent.h"
48 #include "selection.h"
52 #include "../util/DialogF.h"
53 #include "../util/misc.h"
54 #include "../util/fileUtils.h"
55 #include "../util/utils.h"
56 #include "../util/getfiles.h"
57 #include "highlight.h"
58 #include "highlightData.h"
68 #include "../util/VMSparam.h"
73 #include <sys/types.h>
76 #include <sys/param.h>
81 #include <X11/Intrinsic.h>
82 #include <X11/keysym.h>
84 #include <Xm/CutPaste.h>
86 #include <Xm/RowColumn.h>
87 #include <Xm/LabelG.h>
89 #include <Xm/ToggleB.h>
90 #include <Xm/DialogS.h>
91 #include <Xm/MessageB.h>
92 #include <Xm/SelectioB.h>
95 #include <Xm/Separator.h>
101 /* Maximum number of actions in a macro and args in
102 an action (to simplify the reader) */
103 #define MAX_MACRO_ACTIONS 1024
104 #define MAX_ACTION_ARGS 40
106 /* How long to wait (msec) before putting up Macro Command banner */
107 #define BANNER_WAIT_TIME 6000
109 /* The following definitions cause an exit from the macro with a message */
110 /* added if (1) to remove compiler warnings on solaris */
111 #define M_FAILURE(s) do { *errMsg = s; if (1) return False; } while (0)
112 #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)
113 #define M_ARRAY_INSERT_FAILURE() M_FAILURE("array element failed to insert: %s")
115 /* Data attached to window during shell command execution with
116 information for controling and communicating with the process */
118 XtIntervalId bannerTimeoutID
;
119 XtWorkProcId continueWorkProcID
;
121 char closeOnCompletion
;
123 RestartData
*context
;
127 /* Widgets and global data for Repeat dialog */
129 WindowInfo
*forWindow
;
131 Widget shell
, repeatText
, lastCmdToggle
;
132 Widget inSelToggle
, toEndToggle
;
135 static void cancelLearn(void);
136 static void runMacro(WindowInfo
*window
, Program
*prog
);
137 static void finishMacroCmdExecution(WindowInfo
*window
);
138 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
);
139 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
140 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
);
141 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
);
142 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
143 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
144 XEvent
*event
, String
*params
, Cardinal
*numParams
);
145 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
146 XEvent
*event
, String
*params
, Cardinal
*numParams
);
147 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
148 String
*params
, Cardinal numParams
);
149 static int isMouseAction(const char *action
);
150 static int isRedundantAction(const char *action
);
151 static int isIgnoredAction(const char *action
);
152 static int readCheckMacroString(Widget dialogParent
, char *string
,
153 WindowInfo
*runWindow
, const char *errIn
, char **errPos
);
154 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
);
155 static Boolean
continueWorkProc(XtPointer clientData
);
156 static int escapeStringChars(char *fromString
, char *toString
);
157 static int escapedStringLength(char *string
);
158 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
159 DataValue
*result
, char **errMsg
);
160 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
161 DataValue
*result
, char **errMsg
);
162 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
163 DataValue
*result
, char **errMsg
);
164 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
165 DataValue
*result
, char **errMsg
);
166 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
167 DataValue
*result
, char **errMsg
);
168 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
169 DataValue
*result
, char **errMsg
);
170 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
171 DataValue
*result
, char **errMsg
);
172 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
173 DataValue
*result
, char **errMsg
);
174 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
175 DataValue
*result
, char **errMsg
);
176 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
177 DataValue
*result
, char **errMsg
);
178 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
179 DataValue
*result
, char **errMsg
);
180 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
181 DataValue
*result
, char **errMsg
);
182 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
183 DataValue
*result
, char **errMsg
);
184 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
185 DataValue
*result
, char **errMsg
);
186 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
187 DataValue
*result
, char **errMsg
);
188 static int writeOrAppendFile(int append
, WindowInfo
*window
,
189 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
);
190 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
191 DataValue
*result
, char **errMsg
);
192 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
193 DataValue
*result
, char **errMsg
);
194 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
195 DataValue
*result
, char **errMsg
);
196 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
197 DataValue
*result
, char **errMsg
);
198 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
199 DataValue
*result
, char **errMsg
);
200 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
201 DataValue
*result
, char **errMsg
);
202 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
203 DataValue
*result
, char **errMsg
);
204 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
205 DataValue
*result
, char **errMsg
);
206 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
207 DataValue
*result
, char **errMsg
);
208 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
209 DataValue
*result
, char **errMsg
);
210 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
211 DataValue
*result
, char **errMsg
);
212 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
213 DataValue
*result
, char **errMsg
);
214 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
215 DataValue
*result
, char **errMsg
);
216 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
217 DataValue
*result
, char **errMsg
);
218 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
219 DataValue
*result
, char **errMsg
);
220 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
);
221 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
);
222 #ifdef LESSTIF_VERSION
223 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
225 #endif /* LESSTIF_VERSION */
226 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
227 DataValue
*result
, char **errMsg
);
228 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
230 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
232 #ifdef LESSTIF_VERSION
233 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
235 #endif /* LESSTIF_VERSION */
236 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
237 DataValue
*result
, char **errMsg
);
238 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
239 DataValue
*result
, char **errMsg
);
241 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
242 DataValue
*result
, char **errMsg
);
243 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
245 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
248 #ifdef LESSTIF_VERSION
249 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
251 #endif /* LESSTIF_VERSION */
252 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
253 DataValue
*result
, char **errMsg
);
254 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
255 DataValue
*result
, char **errMsg
);
257 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
258 int nArgs, DataValue *result, char **errMsg);
260 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
261 DataValue
*result
, char **errMsg
);
262 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
263 DataValue
*result
, char **errMsg
);
264 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
265 DataValue
*result
, char **errMsg
);
266 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
267 DataValue
*result
, char **errMsg
);
268 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
269 DataValue
*result
, char **errMsg
);
270 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
271 DataValue
*result
, char **errMsg
);
272 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
273 DataValue
*result
, char **errMsg
);
274 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
275 DataValue
*result
, char **errMsg
);
276 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
277 DataValue
*result
, char **errMsg
);
278 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
279 DataValue
*result
, char **errMsg
);
280 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
281 DataValue
*result
, char **errMsg
);
282 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
283 DataValue
*result
, char **errMsg
);
284 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
285 DataValue
*result
, char **errMsg
);
286 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
287 DataValue
*result
, char **errMsg
);
288 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
289 DataValue
*result
, char **errMsg
);
290 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
291 DataValue
*result
, char **errMsg
);
292 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
293 DataValue
*result
, char **errMsg
);
294 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
295 DataValue
*result
, char **errMsg
);
296 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
297 DataValue
*result
, char **errMsg
);
298 static int matchSyntaxBasedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
299 DataValue
*result
, char **errMsg
);
300 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
301 DataValue
*result
, char **errMsg
);
302 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
303 DataValue
*result
, char **errMsg
);
304 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
305 DataValue
*result
, char **errMsg
);
306 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
307 DataValue
*result
, char **errMsg
);
308 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
309 DataValue
*result
, char **errMsg
);
310 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
311 DataValue
*result
, char **errMsg
);
312 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
313 DataValue
*result
, char **errMsg
);
314 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
315 DataValue
*result
, char **errMsg
);
316 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
317 DataValue
*result
, char **errMsg
);
318 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
319 DataValue
*result
, char **errMsg
);
320 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
321 DataValue
*result
, char **errMsg
);
322 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
323 DataValue
*result
, char **errMsg
);
324 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
325 DataValue
*result
, char **errMsg
);
326 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
327 DataValue
*result
, char **errMsg
);
328 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
329 DataValue
*result
, char **errMsg
);
330 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
331 DataValue
*result
, char **errMsg
);
332 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
333 DataValue
*result
, char **errMsg
);
334 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
335 DataValue
*result
, char **errMsg
);
336 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
337 DataValue
*result
, char **errMsg
);
338 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
339 DataValue
*result
, char **errMsg
);
340 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
341 DataValue
*result
, char **errMsg
);
342 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
343 DataValue
*result
, char **errMsg
);
344 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
345 DataValue
*result
, char **errMsg
);
346 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
347 DataValue
*result
, char **errMsg
);
348 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
349 DataValue
*result
, char **errMsg
);
350 static int readSearchArgs(DataValue
*argList
, int nArgs
, int*searchDirection
,
351 int *searchType
, int *wrap
, char **errMsg
);
352 static int wrongNArgsErr(char **errMsg
);
353 static int tooFewArgsErr(char **errMsg
);
354 static int strCaseCmp(char *str1
, char *str2
);
355 static int readIntArg(DataValue dv
, int *result
, char **errMsg
);
356 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
359 static int backlightStringMV(WindowInfo *window, DataValue *argList,
360 int nArgs, DataValue *result, char **errMsg);
362 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
,
363 int nArgs
, DataValue
*result
, char **errMsg
);
364 static int versionMV(WindowInfo
* window
, DataValue
* argList
, int nArgs
,
365 DataValue
* result
, char** errMsg
);
366 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
367 DataValue
*result
, char **errMsg
);
368 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
369 DataValue
*result
, char **errMsg
);
370 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
371 DataValue
*result
, char **errMsg
);
372 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
373 DataValue
*result
, char **errMsg
);
374 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
375 DataValue
*result
, char **errMsg
);
376 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
377 DataValue
*result
, char **errMsg
);
378 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
379 DataValue
*result
, char **errMsg
);
380 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
381 DataValue
*result
, char **errMsg
);
382 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
383 int nArgs
, DataValue
*result
, char **errMsg
);
384 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
385 int nArgs
, DataValue
*result
, char **errMsg
);
386 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
387 int nArgs
, DataValue
*result
, char **errMsg
);
388 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
389 int nArgs
, DataValue
*result
, char **errMsg
);
391 static int fillPatternResult(DataValue
*result
, char **errMsg
, WindowInfo
*window
,
392 char *patternName
, Boolean preallocatedPatternName
, Boolean includeName
,
393 char *styleName
, int bufferPos
);
394 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
395 DataValue
*result
, char **errMsg
);
396 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
397 DataValue
*result
, char **errMsg
);
399 static int fillStyleResult(DataValue
*result
, char **errMsg
,
400 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
401 Boolean includeName
, int patCode
, int bufferPos
);
402 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
403 DataValue
*result
, char **errMsg
);
404 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
405 DataValue
*result
, char **errMsg
);
406 static int filenameDialogMS(WindowInfo
* window
, DataValue
* argList
, int nArgs
,
407 DataValue
* result
, char** errMsg
);
409 /* Built-in subroutines and variables for the macro language */
410 static BuiltInSubr MacroSubrs
[] = {lengthMS
, getRangeMS
, tPrintMS
,
411 dialogMS
, stringDialogMS
, replaceRangeMS
, replaceSelectionMS
,
412 setCursorPosMS
, getCharacterMS
, minMS
, maxMS
, searchMS
,
413 searchStringMS
, substringMS
, replaceSubstringMS
, readFileMS
,
414 writeFileMS
, appendFileMS
, beepMS
, getSelectionMS
, validNumberMS
,
415 replaceInStringMS
, selectMS
, selectRectangleMS
, focusWindowMS
,
416 shellCmdMS
, stringToClipboardMS
, clipboardToStringMS
, toupperMS
,
417 tolowerMS
, listDialogMS
, getenvMS
,
418 stringCompareMS
, splitMS
, calltipMS
, killCalltipMS
,
419 /* DISABLED for 5.4 setBacklightStringMS,*/
420 rangesetCreateMS
, rangesetDestroyMS
,
421 rangesetAddMS
, rangesetSubtractMS
, rangesetInvertMS
,
422 rangesetInfoMS
, rangesetRangeMS
, rangesetIncludesPosMS
,
423 rangesetSetColorMS
, rangesetSetNameMS
, rangesetSetModeMS
,
425 getPatternByNameMS
, getPatternAtPosMS
,
426 getStyleByNameMS
, getStyleAtPosMS
, filenameDialogMS
428 #define N_MACRO_SUBRS (sizeof MacroSubrs/sizeof *MacroSubrs)
429 static const char *MacroSubrNames
[N_MACRO_SUBRS
] = {"length", "get_range", "t_print",
430 "dialog", "string_dialog", "replace_range", "replace_selection",
431 "set_cursor_pos", "get_character", "min", "max", "search",
432 "search_string", "substring", "replace_substring", "read_file",
433 "write_file", "append_file", "beep", "get_selection", "valid_number",
434 "replace_in_string", "select", "select_rectangle", "focus_window",
435 "shell_command", "string_to_clipboard", "clipboard_to_string",
436 "toupper", "tolower", "list_dialog", "getenv",
437 "string_compare", "split", "calltip", "kill_calltip",
438 /* DISABLED for 5.4 "set_backlight_string", */
439 "rangeset_create", "rangeset_destroy",
440 "rangeset_add", "rangeset_subtract", "rangeset_invert",
441 "rangeset_info", "rangeset_range", "rangeset_includes",
442 "rangeset_set_color", "rangeset_set_name", "rangeset_set_mode",
443 "rangeset_get_by_name",
444 "get_pattern_by_name", "get_pattern_at_pos",
445 "get_style_by_name", "get_style_at_pos", "filename_dialog"
447 static BuiltInSubr SpecialVars
[] = {cursorMV
, lineMV
, columnMV
,
448 fileNameMV
, filePathMV
, lengthMV
, selectionStartMV
, selectionEndMV
,
449 selectionLeftMV
, selectionRightMV
, wrapMarginMV
, tabDistMV
,
450 emTabDistMV
, useTabsMV
, languageModeMV
, modifiedMV
,
451 statisticsLineMV
, incSearchLineMV
, showLineNumbersMV
,
452 autoIndentMV
, wrapTextMV
, highlightSyntaxMV
,
453 makeBackupCopyMV
, incBackupMV
, showMatchingMV
, matchSyntaxBasedMV
,
454 overTypeModeMV
, readOnlyMV
, lockedMV
, fileFormatMV
,
455 fontNameMV
, fontNameItalicMV
,
456 fontNameBoldMV
, fontNameBoldItalicMV
, subscriptSepMV
,
457 minFontWidthMV
, maxFontWidthMV
, topLineMV
, numDisplayLinesMV
,
458 displayWidthMV
, activePaneMV
, nPanesMV
, emptyArrayMV
,
459 serverNameMV
, calltipIDMV
,
460 /* DISABLED for 5.4 backlightStringMV, */
461 rangesetListMV
, versionMV
463 #define N_SPECIAL_VARS (sizeof SpecialVars/sizeof *SpecialVars)
464 static const char *SpecialVarNames
[N_SPECIAL_VARS
] = {"$cursor", "$line", "$column",
465 "$file_name", "$file_path", "$text_length", "$selection_start",
466 "$selection_end", "$selection_left", "$selection_right",
467 "$wrap_margin", "$tab_dist", "$em_tab_dist", "$use_tabs",
468 "$language_mode", "$modified",
469 "$statistics_line", "$incremental_search_line", "$show_line_numbers",
470 "$auto_indent", "$wrap_text", "$highlight_syntax",
471 "$make_backup_copy", "$incremental_backup", "$show_matching", "$match_syntax_based",
472 "$overtype_mode", "$read_only", "$locked", "$file_format",
473 "$font_name", "$font_name_italic",
474 "$font_name_bold", "$font_name_bold_italic", "$sub_sep",
475 "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
476 "$display_width", "$active_pane", "$n_panes", "$empty_array",
477 "$server_name", "$calltip_ID",
478 /* DISABLED for 5.4 "$backlight_string", */
479 "$rangeset_list", "$VERSION"
482 /* Global symbols for returning values from built-in functions */
483 #define N_RETURN_GLOBALS 5
484 enum retGlobalSyms
{STRING_DIALOG_BUTTON
, SEARCH_END
, READ_STATUS
,
485 SHELL_CMD_STATUS
, LIST_DIALOG_BUTTON
};
486 static const char *ReturnGlobalNames
[N_RETURN_GLOBALS
] = {"$string_dialog_button",
487 "$search_end", "$read_status", "$shell_cmd_status",
488 "$list_dialog_button"};
489 static Symbol
*ReturnGlobals
[N_RETURN_GLOBALS
];
491 /* List of actions not useful when learning a macro sequence (also see below) */
492 static char* IgnoredActions
[] = {"focusIn", "focusOut"};
494 /* List of actions intended to be attached to mouse buttons, which the user
495 must be warned can't be recorded in a learn/replay sequence */
496 static const char* MouseActions
[] = {"grab_focus", "extend_adjust", "extend_start",
497 "extend_end", "secondary_or_drag_adjust", "secondary_adjust",
498 "secondary_or_drag_start", "secondary_start", "move_destination",
499 "move_to", "move_to_or_end_drag", "copy_to", "copy_to_or_end_drag",
500 "exchange", "process_bdrag", "mouse_pan"};
502 /* List of actions to not record because they
503 generate further actions, more suitable for recording */
504 static const char* RedundantActions
[] = {"open_dialog", "save_as_dialog",
505 "revert_to_saved_dialog", "include_file_dialog", "load_macro_file_dialog",
506 "load_tags_file_dialog", "find_dialog", "replace_dialog",
507 "goto_line_number_dialog", "mark_dialog", "goto_mark_dialog",
508 "control_code_dialog", "filter_selection_dialog", "execute_command_dialog",
509 "repeat_dialog", "start_incremental_find"};
511 /* The last command executed (used by the Repeat command) */
512 static char *LastCommand
= NULL
;
514 /* The current macro to execute on Replay command */
515 static char *ReplayMacro
= NULL
;
517 /* Buffer where macro commands are recorded in Learn mode */
518 static textBuffer
*MacroRecordBuf
= NULL
;
520 /* Action Hook id for recording actions for Learn mode */
521 static XtActionHookId MacroRecordActionHook
= 0;
523 /* Window where macro recording is taking place */
524 static WindowInfo
*MacroRecordWindow
= NULL
;
526 /* Arrays for translating escape characters in escapeStringChars */
527 static char ReplaceChars
[] = "\\\"ntbrfav";
528 static char EscapeChars
[] = "\\\"\n\t\b\r\f\a\v";
531 ** Install built-in macro subroutines and special variables for accessing
532 ** editor information
534 void RegisterMacroSubroutines(void)
536 static DataValue subrPtr
= {NO_TAG
, {0}}, noValue
= {NO_TAG
, {0}};
539 /* Install symbols for built-in routines and variables, with pointers
540 to the appropriate c routines to do the work */
541 for (i
=0; i
<N_MACRO_SUBRS
; i
++) {
542 subrPtr
.val
.subr
= MacroSubrs
[i
];
543 InstallSymbol(MacroSubrNames
[i
], C_FUNCTION_SYM
, subrPtr
);
545 for (i
=0; i
<N_SPECIAL_VARS
; i
++) {
546 subrPtr
.val
.subr
= SpecialVars
[i
];
547 InstallSymbol(SpecialVarNames
[i
], PROC_VALUE_SYM
, subrPtr
);
550 /* Define global variables used for return values, remember their
551 locations so they can be set without a LookupSymbol call */
552 for (i
=0; i
<N_RETURN_GLOBALS
; i
++)
553 ReturnGlobals
[i
] = InstallSymbol(ReturnGlobalNames
[i
], GLOBAL_SYM
,
557 #define MAX_LEARN_MSG_LEN ((2 * MAX_ACCEL_LEN) + 60)
558 void BeginLearn(WindowInfo
*window
)
566 char message
[MAX_LEARN_MSG_LEN
];
568 /* If we're already in learn mode, return */
569 if (MacroRecordActionHook
!= 0)
572 /* dim the inappropriate menus and items, and undim finish and cancel */
573 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
574 if (!IsTopDocument(win
))
576 XtSetSensitive(win
->learnItem
, False
);
578 SetSensitive(window
, window
->finishLearnItem
, True
);
579 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
580 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
582 SetSensitive(window
, window
->cancelMacroItem
, True
);
584 /* Mark the window where learn mode is happening */
585 MacroRecordWindow
= window
;
587 /* Allocate a text buffer for accumulating the macro strings */
588 MacroRecordBuf
= BufCreate();
590 /* Add the action hook for recording the actions */
591 MacroRecordActionHook
=
592 XtAppAddActionHook(XtWidgetToApplicationContext(window
->shell
),
593 learnActionHook
, window
);
595 /* Extract accelerator texts from menu PushButtons */
596 XtVaGetValues(window
->finishLearnItem
, XmNacceleratorText
, &xmFinish
, NULL
);
597 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
599 /* Translate Motif strings to char* */
600 cFinish
= GetXmStringText(xmFinish
);
601 cCancel
= GetXmStringText(xmCancel
);
603 /* Free Motif Strings */
604 XmStringFree(xmFinish
);
605 XmStringFree(xmCancel
);
608 if (cFinish
[0] == '\0') {
609 if (cCancel
[0] == '\0') {
610 strncpy(message
, "Learn Mode -- Use menu to finish or cancel",
612 message
[MAX_LEARN_MSG_LEN
- 1] = '\0';
616 "Learn Mode -- Use menu to finish, press %s to cancel",
621 if (cCancel
[0] == '\0') {
623 "Learn Mode -- Press %s to finish, use menu to cancel",
629 "Learn Mode -- Press %s to finish, %s to cancel",
639 /* Put up the learn-mode banner */
640 SetModeMessage(window
, message
);
643 void AddLastCommandActionHook(XtAppContext context
)
645 XtAppAddActionHook(context
, lastActionHook
, NULL
);
648 void FinishLearn(void)
652 /* If we're not in learn mode, return */
653 if (MacroRecordActionHook
== 0)
656 /* Remove the action hook */
657 XtRemoveActionHook(MacroRecordActionHook
);
658 MacroRecordActionHook
= 0;
660 /* Free the old learn/replay sequence */
663 /* Store the finished action for the replay menu item */
664 ReplayMacro
= BufGetAll(MacroRecordBuf
);
666 /* Free the buffer used to accumulate the macro sequence */
667 BufFree(MacroRecordBuf
);
669 /* Undim the menu items dimmed during learn */
670 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
671 if (!IsTopDocument(win
))
673 XtSetSensitive(win
->learnItem
, True
);
675 if (IsTopDocument(MacroRecordWindow
)) {
676 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
677 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
680 /* Undim the replay and paste-macro buttons */
681 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
682 if (!IsTopDocument(win
))
684 XtSetSensitive(win
->replayItem
, True
);
686 DimPasteReplayBtns(True
);
688 /* Clear learn-mode banner */
689 ClearModeMessage(MacroRecordWindow
);
693 ** Cancel Learn mode, or macro execution (they're bound to the same menu item)
695 void CancelMacroOrLearn(WindowInfo
*window
)
697 if (MacroRecordActionHook
!= 0)
699 else if (window
->macroCmdData
!= NULL
)
700 AbortMacroCommand(window
);
703 static void cancelLearn(void)
707 /* If we're not in learn mode, return */
708 if (MacroRecordActionHook
== 0)
711 /* Remove the action hook */
712 XtRemoveActionHook(MacroRecordActionHook
);
713 MacroRecordActionHook
= 0;
715 /* Free the macro under construction */
716 BufFree(MacroRecordBuf
);
718 /* Undim the menu items dimmed during learn */
719 for (win
=WindowList
; win
!=NULL
; win
=win
->next
) {
720 if (!IsTopDocument(win
))
722 XtSetSensitive(win
->learnItem
, True
);
724 if (IsTopDocument(MacroRecordWindow
)) {
725 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
726 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
729 /* Clear learn-mode banner */
730 ClearModeMessage(MacroRecordWindow
);
734 ** Execute the learn/replay sequence stored in "window"
736 void Replay(WindowInfo
*window
)
739 char *errMsg
, *stoppedAt
;
741 /* Verify that a replay macro exists and it's not empty and that */
742 /* we're not already running a macro */
743 if (ReplayMacro
!= NULL
&&
744 ReplayMacro
[0] != 0 &&
745 window
->macroCmdData
== NULL
) {
746 /* Parse the replay macro (it's stored in text form) and compile it into
747 an executable program "prog" */
748 prog
= ParseMacro(ReplayMacro
, &errMsg
, &stoppedAt
);
751 "NEdit internal error, learn/replay macro syntax error: %s\n",
756 /* run the executable program */
757 runMacro(window
, prog
);
762 ** Read the initial NEdit macro file if one exists.
764 void ReadMacroInitFile(WindowInfo
*window
)
766 const char* autoloadName
= GetRCFileName(AUTOLOAD_NM
);
767 static int initFileLoaded
= False
;
769 /* GetRCFileName() might return NULL if an error occurs during
770 creation of the preference file directory. */
771 if (autoloadName
!= NULL
&& !initFileLoaded
)
773 ReadMacroFile(window
, autoloadName
, False
);
774 initFileLoaded
= True
;
779 ** Read an NEdit macro file. Extends the syntax of the macro parser with
780 ** define keyword, and allows intermixing of defines with immediate actions.
782 int ReadMacroFile(WindowInfo
*window
, const char *fileName
, int warnNotExist
)
787 /* read-in macro file and force a terminating \n, to prevent syntax
788 ** errors with statements on the last line
790 fileString
= ReadAnyTextFile(fileName
, True
);
791 if (fileString
== NULL
){
792 if (errno
!= ENOENT
|| warnNotExist
)
794 DialogF(DF_ERR
, window
->shell
, 1, "Read Macro",
795 "Error reading macro file %s: %s", "OK", fileName
,
797 strerror(errno
, vaxc$errno
));
805 /* Parse fileString */
806 result
= readCheckMacroString(window
->shell
, fileString
, window
, fileName
,
813 ** Parse and execute a macro string including macro definitions. Report
814 ** parsing errors in a dialog posted over window->shell.
816 int ReadMacroString(WindowInfo
*window
, char *string
, const char *errIn
)
818 return readCheckMacroString(window
->shell
, string
, window
, errIn
, NULL
);
822 ** Check a macro string containing definitions for errors. Returns True
823 ** if macro compiled successfully. Returns False and puts up
824 ** a dialog explaining if macro did not compile successfully.
826 int CheckMacroString(Widget dialogParent
, char *string
, const char *errIn
,
829 return readCheckMacroString(dialogParent
, string
, NULL
, errIn
, errPos
);
833 ** Parse and optionally execute a macro string including macro definitions.
834 ** Report parsing errors in a dialog posted over dialogParent, using the
835 ** string errIn to identify the entity being parsed (filename, macro string,
836 ** etc.). If runWindow is specified, runs the macro against the window. If
837 ** runWindow is passed as NULL, does parse only. If errPos is non-null,
838 ** returns a pointer to the error location in the string.
840 static int readCheckMacroString(Widget dialogParent
, char *string
,
841 WindowInfo
*runWindow
, const char *errIn
, char **errPos
)
843 char *stoppedAt
, *inPtr
, *namePtr
, *errMsg
;
844 char subrName
[MAX_SYM_LEN
];
848 Stack
* progStack
= (Stack
*) XtMalloc(sizeof(Stack
));
849 progStack
->top
= NULL
;
853 while (*inPtr
!= '\0') {
855 /* skip over white space and comments */
856 while (*inPtr
==' ' || *inPtr
=='\t' || *inPtr
=='\n'|| *inPtr
=='#') {
858 while (*inPtr
!= '\n' && *inPtr
!= '\0') inPtr
++;
865 /* look for define keyword, and compile and store defined routines */
866 if (!strncmp(inPtr
, "define", 6) && (inPtr
[6]==' ' || inPtr
[6]=='\t')) {
868 inPtr
+= strspn(inPtr
, " \t\n");
870 while ((namePtr
< &subrName
[MAX_SYM_LEN
- 1])
871 && (isalnum((unsigned char)*inPtr
) || *inPtr
== '_')) {
872 *namePtr
++ = *inPtr
++;
875 if (isalnum((unsigned char)*inPtr
) || *inPtr
== '_') {
876 return ParseError(dialogParent
, string
, inPtr
, errIn
,
877 "subroutine name too long");
879 inPtr
+= strspn(inPtr
, " \t\n");
881 if (errPos
!= NULL
) *errPos
= stoppedAt
;
882 return ParseError(dialogParent
, string
, inPtr
,
883 errIn
, "expected '{'");
885 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
887 if (errPos
!= NULL
) *errPos
= stoppedAt
;
888 return ParseError(dialogParent
, string
, stoppedAt
,
891 if (runWindow
!= NULL
) {
892 sym
= LookupSymbol(subrName
);
894 subrPtr
.val
.prog
= prog
;
895 subrPtr
.tag
= NO_TAG
;
896 sym
= InstallSymbol(subrName
, MACRO_FUNCTION_SYM
, subrPtr
);
898 if (sym
->type
== MACRO_FUNCTION_SYM
)
899 FreeProgram(sym
->value
.val
.prog
);
901 sym
->type
= MACRO_FUNCTION_SYM
;
902 sym
->value
.val
.prog
= prog
;
907 /* Parse and execute immediate (outside of any define) macro commands
908 and WAIT for them to finish executing before proceeding. Note that
909 the code below is not perfect. If you interleave code blocks with
910 definitions in a file which is loaded from another macro file, it
911 will probably run the code blocks in reverse order! */
913 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
915 if (errPos
!= NULL
) {
919 return ParseError(dialogParent
, string
, stoppedAt
,
923 if (runWindow
!= NULL
) {
925 if (runWindow
->macroCmdData
== NULL
) {
926 runMacro(runWindow
, prog
);
927 while (runWindow
->macroCmdData
!= NULL
) {
928 XtAppNextEvent(XtWidgetToApplicationContext(
929 runWindow
->shell
), &nextEvent
);
930 ServerDispatchEvent(&nextEvent
);
933 /* If we come here this means that the string was parsed
934 from within another macro via load_macro_file(). In
935 this case, plain code segments outside of define
936 blocks are rolled into one Program each and put on
937 the stack. At the end, the stack is unrolled, so the
938 plain Programs would be executed in the wrong order.
940 So we don't hand the Programs over to the interpreter
941 just yet (via RunMacroAsSubrCall()), but put it on a
942 stack of our own, reversing order once again. */
943 Push(progStack
, (void*) prog
);
950 /* Unroll reversal stack for macros loaded from macros. */
951 while (NULL
!= (prog
= (Program
*) Pop(progStack
))) {
952 RunMacroAsSubrCall(prog
);
955 /* This stack is empty, so just free it without checking the members. */
956 XtFree((char*) progStack
);
962 ** Run a pre-compiled macro, changing the interface state to reflect that
963 ** a macro is running, and handling preemption, resumption, and cancellation.
964 ** frees prog when macro execution is complete;
966 static void runMacro(WindowInfo
*window
, Program
*prog
)
971 macroCmdInfo
*cmdData
;
974 /* If a macro is already running, just call the program as a subroutine,
975 instead of starting a new one, so we don't have to keep a separate
976 context, and the macros will serialize themselves automatically */
977 if (window
->macroCmdData
!= NULL
) {
978 RunMacroAsSubrCall(prog
);
982 /* put up a watch cursor over the waiting window */
983 BeginWait(window
->shell
);
985 /* enable the cancel menu item */
986 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
987 s
=XmStringCreateSimple("Cancel Macro"), NULL
);
989 SetSensitive(window
, window
->cancelMacroItem
, True
);
991 /* Create a data structure for passing macro execution information around
992 amongst the callback routines which will process i/o and completion */
993 cmdData
= (macroCmdInfo
*)XtMalloc(sizeof(macroCmdInfo
));
994 window
->macroCmdData
= cmdData
;
995 cmdData
->bannerIsUp
= False
;
996 cmdData
->closeOnCompletion
= False
;
997 cmdData
->program
= prog
;
998 cmdData
->context
= NULL
;
999 cmdData
->continueWorkProcID
= 0;
1000 cmdData
->dialog
= NULL
;
1002 /* Set up timer proc for putting up banner when macro takes too long */
1003 cmdData
->bannerTimeoutID
= XtAppAddTimeOut(
1004 XtWidgetToApplicationContext(window
->shell
), BANNER_WAIT_TIME
,
1005 bannerTimeoutProc
, window
);
1007 /* Begin macro execution */
1008 stat
= ExecuteMacro(window
, prog
, 0, NULL
, &result
, &cmdData
->context
,
1011 if (stat
== MACRO_ERROR
)
1013 finishMacroCmdExecution(window
);
1014 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
1015 "Error executing macro: %s", "OK", errMsg
);
1019 if (stat
== MACRO_DONE
) {
1020 finishMacroCmdExecution(window
);
1023 if (stat
== MACRO_TIME_LIMIT
) {
1024 ResumeMacroExecution(window
);
1027 /* (stat == MACRO_PREEMPT) Macro was preempted */
1031 ** Continue with macro execution after preemption. Called by the routines
1032 ** whose actions cause preemption when they have completed their lengthy tasks.
1033 ** Re-establishes macro execution work proc. Window must be the window in
1034 ** which the macro is executing (the window to which macroCmdData is attached),
1035 ** and not the window to which operations are focused.
1037 void ResumeMacroExecution(WindowInfo
*window
)
1039 macroCmdInfo
*cmdData
= (macroCmdInfo
*)window
->macroCmdData
;
1041 if (cmdData
!= NULL
)
1042 cmdData
->continueWorkProcID
= XtAppAddWorkProc(
1043 XtWidgetToApplicationContext(window
->shell
),
1044 continueWorkProc
, window
);
1048 ** Cancel the macro command in progress (user cancellation via GUI)
1050 void AbortMacroCommand(WindowInfo
*window
)
1052 if (window
->macroCmdData
== NULL
)
1055 /* If there's both a macro and a shell command executing, the shell command
1056 must have been called from the macro. When called from a macro, shell
1057 commands don't put up cancellation controls of their own, but rely
1058 instead on the macro cancellation mechanism (here) */
1060 if (window
->shellCmdData
!= NULL
)
1061 AbortShellCommand(window
);
1064 /* Free the continuation */
1065 FreeRestartData(((macroCmdInfo
*)window
->macroCmdData
)->context
);
1067 /* Kill the macro command */
1068 finishMacroCmdExecution(window
);
1072 ** Call this before closing a window, to clean up macro references to the
1073 ** window, stop any macro which might be running from it, free associated
1074 ** memory, and check that a macro is not attempting to close the window from
1075 ** which it is run. If this is being called from a macro, and the window
1076 ** this routine is examining is the window from which the macro was run, this
1077 ** routine will return False, and the caller must NOT CLOSE THE WINDOW.
1078 ** Instead, empty it and make it Untitled, and let the macro completion
1079 ** process close the window when the macro is finished executing.
1081 int MacroWindowCloseActions(WindowInfo
*window
)
1083 macroCmdInfo
*mcd
, *cmdData
= window
->macroCmdData
;
1086 if (MacroRecordActionHook
!= 0 && MacroRecordWindow
== window
) {
1090 /* If no macro is executing in the window, allow the close, but check
1091 if macros executing in other windows have it as focus. If so, set
1092 their focus back to the window from which they were originally run */
1093 if (cmdData
== NULL
) {
1094 for (w
=WindowList
; w
!=NULL
; w
=w
->next
) {
1095 mcd
= (macroCmdInfo
*)w
->macroCmdData
;
1096 if (w
== MacroRunWindow() && MacroFocusWindow() == window
)
1097 SetMacroFocusWindow(MacroRunWindow());
1098 else if (mcd
!= NULL
&& mcd
->context
->focusWindow
== window
)
1099 mcd
->context
->focusWindow
= mcd
->context
->runWindow
;
1104 /* If the macro currently running (and therefore calling us, because
1105 execution must otherwise return to the main loop to execute any
1106 commands), is running in this window, tell the caller not to close,
1107 and schedule window close on completion of macro */
1108 if (window
== MacroRunWindow()) {
1109 cmdData
->closeOnCompletion
= True
;
1113 /* Free the continuation */
1114 FreeRestartData(cmdData
->context
);
1116 /* Kill the macro command */
1117 finishMacroCmdExecution(window
);
1122 ** Clean up after the execution of a macro command: free memory, and restore
1123 ** the user interface state.
1125 static void finishMacroCmdExecution(WindowInfo
*window
)
1127 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1128 int closeOnCompletion
= cmdData
->closeOnCompletion
;
1130 XClientMessageEvent event
;
1132 /* Cancel pending timeout and work proc */
1133 if (cmdData
->bannerTimeoutID
!= 0)
1134 XtRemoveTimeOut(cmdData
->bannerTimeoutID
);
1135 if (cmdData
->continueWorkProcID
!= 0)
1136 XtRemoveWorkProc(cmdData
->continueWorkProcID
);
1138 /* Clean up waiting-for-macro-command-to-complete mode */
1139 EndWait(window
->shell
);
1140 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
1141 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
1143 SetSensitive(window
, window
->cancelMacroItem
, False
);
1144 if (cmdData
->bannerIsUp
)
1145 ClearModeMessage(window
);
1147 /* If a dialog was up, get rid of it */
1148 if (cmdData
->dialog
!= NULL
)
1149 XtDestroyWidget(XtParent(cmdData
->dialog
));
1151 /* Free execution information */
1152 FreeProgram(cmdData
->program
);
1153 XtFree((char *)cmdData
);
1154 window
->macroCmdData
= NULL
;
1156 /* If macro closed its own window, window was made empty and untitled,
1157 but close was deferred until completion. This is completion, so if
1158 the window is still empty, do the close */
1159 if (closeOnCompletion
&& !window
->filenameSet
&& !window
->fileChanged
) {
1160 CloseWindow(window
);
1164 /* If no other macros are executing, do garbage collection */
1167 /* In processing the .neditmacro file (and possibly elsewhere), there
1168 is an event loop which waits for macro completion. Send an event
1169 to wake up that loop, otherwise execution will stall until the user
1170 does something to the window. */
1171 if (!closeOnCompletion
) {
1173 event
.type
= ClientMessage
;
1174 XSendEvent(XtDisplay(window
->shell
), XtWindow(window
->shell
), False
,
1175 NoEventMask
, (XEvent
*)&event
);
1180 ** Do garbage collection of strings if there are no macros currently
1181 ** executing. NEdit's macro language GC strategy is to call this routine
1182 ** whenever a macro completes. If other macros are still running (preempted
1183 ** or waiting for a shell command or dialog), this does nothing and therefore
1184 ** defers GC to the completion of the last macro out.
1190 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
1191 if (win
->macroCmdData
!= NULL
|| InSmartIndentMacros(win
))
1193 GarbageCollectStrings();
1197 ** Executes macro string "macro" using the lastFocus pane in "window".
1198 ** Reports errors via a dialog posted over "window", integrating the name
1199 ** "errInName" into the message to help identify the source of the error.
1201 void DoMacro(WindowInfo
*window
, const char *macro
, const char *errInName
)
1204 char *errMsg
, *stoppedAt
, *tMacro
;
1207 /* Add a terminating newline (which command line users are likely to omit
1208 since they are typically invoking a single routine) */
1209 macroLen
= strlen(macro
);
1210 tMacro
= XtMalloc(strlen(macro
)+2);
1211 strncpy(tMacro
, macro
, macroLen
);
1212 tMacro
[macroLen
] = '\n';
1213 tMacro
[macroLen
+1] = '\0';
1215 /* Parse the macro and report errors if it fails */
1216 prog
= ParseMacro(tMacro
, &errMsg
, &stoppedAt
);
1218 ParseError(window
->shell
, tMacro
, stoppedAt
, errInName
, errMsg
);
1224 /* run the executable program (prog is freed upon completion) */
1225 runMacro(window
, prog
);
1229 ** Get the current Learn/Replay macro in text form. Returned string is a
1230 ** pointer to the stored macro and should not be freed by the caller (and
1231 ** will cease to exist when the next replay macro is installed)
1233 char *GetReplayMacro(void)
1239 ** Present the user a dialog for "Repeat" command
1241 void RepeatDialog(WindowInfo
*window
)
1243 Widget form
, selBox
, radioBox
, timesForm
;
1246 char *lastCmdLabel
, *parenChar
;
1250 if (LastCommand
== NULL
)
1252 DialogF(DF_WARN
, window
->shell
, 1, "Repeat Macro",
1253 "No previous commands or learn/\nreplay sequences to repeat",
1258 /* Remeber the last command, since the user is allowed to work in the
1259 window while the dialog is up */
1260 rd
= (repeatDialog
*)XtMalloc(sizeof(repeatDialog
));
1261 rd
->lastCommand
= XtNewString(LastCommand
);
1263 /* make a label for the Last command item of the dialog, which includes
1264 the last executed action name */
1265 parenChar
= strchr(LastCommand
, '(');
1266 if (parenChar
== NULL
)
1268 cmdNameLen
= parenChar
-LastCommand
;
1269 lastCmdLabel
= XtMalloc(16 + cmdNameLen
);
1270 strcpy(lastCmdLabel
, "Last Command (");
1271 strncpy(&lastCmdLabel
[14], LastCommand
, cmdNameLen
);
1272 strcpy(&lastCmdLabel
[14 + cmdNameLen
], ")");
1274 XtSetArg(selBoxArgs
[0], XmNautoUnmanage
, False
);
1275 selBox
= CreatePromptDialog(window
->shell
, "repeat", selBoxArgs
, 1);
1276 rd
->shell
= XtParent(selBox
);
1277 XtAddCallback(rd
->shell
, XmNdestroyCallback
, repeatDestroyCB
, rd
);
1278 XtAddCallback(selBox
, XmNokCallback
, repeatOKCB
, rd
);
1279 XtAddCallback(selBox
, XmNapplyCallback
, repeatApplyCB
, rd
);
1280 XtAddCallback(selBox
, XmNcancelCallback
, repeatCancelCB
, rd
);
1281 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_TEXT
));
1282 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_SELECTION_LABEL
));
1283 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_HELP_BUTTON
));
1284 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_APPLY_BUTTON
));
1285 XtVaSetValues(XtParent(selBox
), XmNtitle
, "Repeat Macro", NULL
);
1286 AddMotifCloseCallback(XtParent(selBox
), repeatCancelCB
, rd
);
1288 form
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, selBox
, NULL
);
1290 radioBox
= XtVaCreateManagedWidget("cmdSrc", xmRowColumnWidgetClass
, form
,
1291 XmNradioBehavior
, True
,
1292 XmNorientation
, XmHORIZONTAL
,
1293 XmNpacking
, XmPACK_TIGHT
,
1294 XmNtopAttachment
, XmATTACH_FORM
,
1295 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1296 rd
->lastCmdToggle
= XtVaCreateManagedWidget("lastCmdToggle",
1297 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1298 XmNlabelString
, s1
=XmStringCreateSimple(lastCmdLabel
),
1299 XmNmnemonic
, 'C', NULL
);
1301 XtFree(lastCmdLabel
);
1302 XtVaCreateManagedWidget("learnReplayToggle",
1303 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1305 s1
=XmStringCreateSimple("Learn/Replay"),
1307 XmNsensitive
, ReplayMacro
!= NULL
, NULL
);
1310 timesForm
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, form
,
1311 XmNtopAttachment
, XmATTACH_WIDGET
,
1312 XmNtopWidget
, radioBox
,
1314 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1315 radioBox
= XtVaCreateManagedWidget("method", xmRowColumnWidgetClass
,
1317 XmNradioBehavior
, True
,
1318 XmNorientation
, XmHORIZONTAL
,
1319 XmNpacking
, XmPACK_TIGHT
,
1320 XmNtopAttachment
, XmATTACH_FORM
,
1321 XmNbottomAttachment
, XmATTACH_FORM
,
1322 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1323 rd
->inSelToggle
= XtVaCreateManagedWidget("inSelToggle",
1324 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1325 XmNlabelString
, s1
=XmStringCreateSimple("In Selection"),
1326 XmNmnemonic
, 'I', NULL
);
1328 rd
->toEndToggle
= XtVaCreateManagedWidget("toEndToggle",
1329 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1330 XmNlabelString
, s1
=XmStringCreateSimple("To End"),
1331 XmNmnemonic
, 'T', NULL
);
1333 XtVaCreateManagedWidget("nTimesToggle",
1334 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1335 XmNlabelString
, s1
=XmStringCreateSimple("N Times"),
1337 XmNset
, True
, NULL
);
1339 rd
->repeatText
= XtVaCreateManagedWidget("repeatText", xmTextWidgetClass
,
1342 XmNtopAttachment
, XmATTACH_FORM
,
1343 XmNbottomAttachment
, XmATTACH_FORM
,
1344 XmNleftAttachment
, XmATTACH_WIDGET
,
1345 XmNleftWidget
, radioBox
, NULL
);
1346 RemapDeleteKey(rd
->repeatText
);
1348 /* Handle mnemonic selection of buttons and focus to dialog */
1349 AddDialogMnemonicHandler(form
, FALSE
);
1351 /* Set initial focus */
1352 #if XmVersion >= 1002
1353 XtVaSetValues(form
, XmNinitialFocus
, timesForm
, NULL
);
1354 XtVaSetValues(timesForm
, XmNinitialFocus
, rd
->repeatText
, NULL
);
1358 rd
->forWindow
= window
;
1359 ManageDialogCenteredOnPointer(selBox
);
1362 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1364 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1366 if (doRepeatDialogAction(rd
, ((XmAnyCallbackStruct
*)callData
)->event
))
1367 XtDestroyWidget(rd
->shell
);
1370 /* Note that the apply button is not managed in the repeat dialog. The dialog
1371 itself is capable of non-modal operation, but to be complete, it needs
1372 to dynamically update last command, dimming of learn/replay, possibly a
1373 stop button for the macro, and possibly in-selection with selection */
1374 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1376 doRepeatDialogAction((repeatDialog
*)clientData
,
1377 ((XmAnyCallbackStruct
*)callData
)->event
);
1380 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
)
1383 char nTimesStr
[TYPE_INT_STR_SIZE(int)];
1386 /* Find out from the dialog how to repeat the command */
1387 if (XmToggleButtonGetState(rd
->inSelToggle
))
1389 if (!rd
->forWindow
->buffer
->primary
.selected
)
1391 DialogF(DF_WARN
, rd
->shell
, 1, "Repeat Macro",
1392 "No selection in window to repeat within", "OK");
1393 XmProcessTraversal(rd
->inSelToggle
, XmTRAVERSE_CURRENT
);
1396 params
[0] = "in_selection";
1397 } else if (XmToggleButtonGetState(rd
->toEndToggle
))
1399 params
[0] = "to_end";
1402 if (GetIntTextWarn(rd
->repeatText
, &nTimes
, "number of times", True
)
1405 XmProcessTraversal(rd
->repeatText
, XmTRAVERSE_CURRENT
);
1408 sprintf(nTimesStr
, "%d", nTimes
);
1409 params
[0] = nTimesStr
;
1412 /* Figure out which command user wants to repeat */
1413 if (XmToggleButtonGetState(rd
->lastCmdToggle
))
1414 params
[1] = XtNewString(rd
->lastCommand
);
1416 if (ReplayMacro
== NULL
)
1418 params
[1] = XtNewString(ReplayMacro
);
1421 /* call the action routine repeat_macro to do the work */
1422 XtCallActionProc(rd
->forWindow
->lastFocus
, "repeat_macro", event
, params
,2);
1427 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1429 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1431 XtDestroyWidget(rd
->shell
);
1434 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1436 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1438 XtFree(rd
->lastCommand
);
1443 ** Dispatches a macro to which repeats macro command in "command", either
1444 ** an integer number of times ("how" == positive integer), or within a
1445 ** selected range ("how" == REPEAT_IN_SEL), or to the end of the window
1446 ** ("how == REPEAT_TO_END).
1448 ** Note that as with most macro routines, this returns BEFORE the macro is
1449 ** finished executing
1451 void RepeatMacro(WindowInfo
*window
, const char *command
, int how
)
1454 char *errMsg
, *stoppedAt
, *loopMacro
, *loopedCmd
;
1456 if (command
== NULL
)
1459 /* Wrap a for loop and counter/tests around the command */
1460 if (how
== REPEAT_TO_END
)
1461 loopMacro
= "lastCursor=-1\nstartPos=$cursor\n\
1462 while($cursor>=startPos&&$cursor!=lastCursor){\nlastCursor=$cursor\n%s\n}\n";
1463 else if (how
== REPEAT_IN_SEL
)
1464 loopMacro
= "selStart = $selection_start\nif (selStart == -1)\nreturn\n\
1465 selEnd = $selection_end\nset_cursor_pos(selStart)\nselect(0,0)\n\
1466 boundText = get_range(selEnd, selEnd+10)\n\
1467 while($cursor >= selStart && $cursor < selEnd && \\\n\
1468 get_range(selEnd, selEnd+10) == boundText) {\n\
1469 startLength = $text_length\n%s\n\
1470 selEnd += $text_length - startLength\n}\n";
1472 loopMacro
= "for(i=0;i<%d;i++){\n%s\n}\n";
1473 loopedCmd
= XtMalloc(strlen(command
) + strlen(loopMacro
) + 25);
1474 if (how
== REPEAT_TO_END
|| how
== REPEAT_IN_SEL
)
1475 sprintf(loopedCmd
, loopMacro
, command
);
1477 sprintf(loopedCmd
, loopMacro
, how
, command
);
1479 /* Parse the resulting macro into an executable program "prog" */
1480 prog
= ParseMacro(loopedCmd
, &errMsg
, &stoppedAt
);
1482 fprintf(stderr
, "NEdit internal error, repeat macro syntax wrong: %s\n",
1488 /* run the executable program */
1489 runMacro(window
, prog
);
1493 ** Macro recording action hook for Learn/Replay, added temporarily during
1496 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
1497 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1503 /* Select only actions in text panes in the window for which this
1504 action hook is recording macros (from clientData). */
1505 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1506 if (window
->textArea
== w
)
1508 for (i
=0; i
<window
->nPanes
; i
++) {
1509 if (window
->textPanes
[i
] == w
)
1512 if (i
< window
->nPanes
)
1515 if (window
== NULL
|| window
!= (WindowInfo
*)clientData
)
1518 /* beep on un-recordable operations which require a mouse position, to
1519 remind the user that the action was not recorded */
1520 if (isMouseAction(actionName
)) {
1521 XBell(XtDisplay(w
), 0);
1525 /* Record the action and its parameters */
1526 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1527 if (actionString
!= NULL
) {
1528 BufInsert(MacroRecordBuf
, MacroRecordBuf
->length
, actionString
);
1529 XtFree(actionString
);
1534 ** Permanent action hook for remembering last action for possible replay
1536 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
1537 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1543 /* Find the window to which this action belongs */
1544 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1545 if (window
->textArea
== w
)
1547 for (i
=0; i
<window
->nPanes
; i
++) {
1548 if (window
->textPanes
[i
] == w
)
1551 if (i
< window
->nPanes
)
1557 /* The last action is recorded for the benefit of repeating the last
1558 action. Don't record repeat_macro and wipe out the real action */
1559 if (!strcmp(actionName
, "repeat_macro"))
1562 /* Record the action and its parameters */
1563 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1564 if (actionString
!= NULL
) {
1565 XtFree(LastCommand
);
1566 LastCommand
= actionString
;
1571 ** Create a macro string to represent an invocation of an action routine.
1572 ** Returns NULL for non-operational or un-recordable actions.
1574 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
1575 String
*params
, Cardinal numParams
)
1577 char chars
[20], *charList
[1], *outStr
, *outPtr
;
1579 int i
, nChars
, nParams
, length
, nameLength
;
1584 if (isIgnoredAction(actionName
) || isRedundantAction(actionName
) ||
1585 isMouseAction(actionName
))
1588 /* Convert self_insert actions, to insert_string */
1589 if (!strcmp(actionName
, "self_insert") ||
1590 !strcmp(actionName
, "self-insert")) {
1591 actionName
= "insert_string";
1593 nChars
= XLookupString((XKeyEvent
*)event
, chars
, 19, &keysym
, NULL
);
1598 nChars
= XmImMbLookupString(w
, (XKeyEvent
*)event
,
1599 chars
, 19, &keysym
, &status
);
1600 if (nChars
== 0 || status
== XLookupNone
||
1601 status
== XLookupKeySym
|| status
== XBufferOverflow
)
1604 chars
[nChars
] = '\0';
1605 charList
[0] = chars
;
1609 nParams
= numParams
;
1611 /* Figure out the length of string required */
1612 nameLength
= strlen(actionName
);
1613 length
= nameLength
+ 3;
1614 for (i
=0; i
<nParams
; i
++)
1615 length
+= escapedStringLength(params
[i
]) + 4;
1617 /* Allocate the string and copy the information to it */
1618 outPtr
= outStr
= XtMalloc(length
+ 1);
1619 strcpy(outPtr
, actionName
);
1620 outPtr
+= nameLength
;
1622 for (i
=0; i
<nParams
; i
++) {
1624 outPtr
+= escapeStringChars(params
[i
], outPtr
);
1625 *outPtr
++ = '\"'; *outPtr
++ = ','; *outPtr
++ = ' ';
1629 *outPtr
++ = ')'; *outPtr
++ = '\n'; *outPtr
++ = '\0';
1633 static int isMouseAction(const char *action
)
1637 for (i
=0; i
<(int)XtNumber(MouseActions
); i
++)
1638 if (!strcmp(action
, MouseActions
[i
]))
1643 static int isRedundantAction(const char *action
)
1647 for (i
=0; i
<(int)XtNumber(RedundantActions
); i
++)
1648 if (!strcmp(action
, RedundantActions
[i
]))
1653 static int isIgnoredAction(const char *action
)
1657 for (i
=0; i
<(int)XtNumber(IgnoredActions
); i
++)
1658 if (!strcmp(action
, IgnoredActions
[i
]))
1664 ** Timer proc for putting up the "Macro Command in Progress" banner if
1665 ** the process is taking too long.
1667 #define MAX_TIMEOUT_MSG_LEN (MAX_ACCEL_LEN + 60)
1668 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
)
1670 WindowInfo
*window
= (WindowInfo
*)clientData
;
1671 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1673 char *cCancel
= "\0";
1674 char message
[MAX_TIMEOUT_MSG_LEN
];
1676 cmdData
->bannerIsUp
= True
;
1678 /* Extract accelerator text from menu PushButtons */
1679 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
1681 if (!XmStringEmpty(xmCancel
))
1683 /* Translate Motif string to char* */
1684 cCancel
= GetXmStringText(xmCancel
);
1686 /* Free Motif String */
1687 XmStringFree(xmCancel
);
1690 /* Create message */
1691 if (cCancel
[0] == '\0') {
1692 strncpy(message
, "Macro Command in Progress", MAX_TIMEOUT_MSG_LEN
);
1693 message
[MAX_TIMEOUT_MSG_LEN
- 1] = '\0';
1697 "Macro Command in Progress -- Press %s to Cancel",
1704 SetModeMessage(window
, message
);
1705 cmdData
->bannerTimeoutID
= 0;
1709 ** Work proc for continuing execution of a preempted macro.
1711 ** Xt WorkProcs are designed to run first-in first-out, which makes them
1712 ** very bad at sharing time between competing tasks. For this reason, it's
1713 ** usually bad to use work procs anywhere where their execution is likely to
1714 ** overlap. Using a work proc instead of a timer proc (which I usually
1715 ** prefer) here means macros will probably share time badly, but we're more
1716 ** interested in making the macros cancelable, and in continuing other work
1717 ** than having users run a bunch of them at once together.
1719 static Boolean
continueWorkProc(XtPointer clientData
)
1721 WindowInfo
*window
= (WindowInfo
*)clientData
;
1722 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1727 stat
= ContinueMacro(cmdData
->context
, &result
, &errMsg
);
1728 if (stat
== MACRO_ERROR
)
1730 finishMacroCmdExecution(window
);
1731 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
1732 "Error executing macro: %s", "OK", errMsg
);
1734 } else if (stat
== MACRO_DONE
)
1736 finishMacroCmdExecution(window
);
1738 } else if (stat
== MACRO_PREEMPT
)
1740 cmdData
->continueWorkProcID
= 0;
1744 /* Macro exceeded time slice, re-schedule it */
1745 if (stat
!= MACRO_TIME_LIMIT
)
1746 return True
; /* shouldn't happen */
1751 ** Copy fromString to toString replacing special characters in strings, such
1752 ** that they can be read back by the macro parser's string reader. i.e. double
1753 ** quotes are replaced by \", backslashes are replaced with \\, C-std control
1754 ** characters like \n are replaced with their backslash counterparts. This
1755 ** routine should be kept reasonably in sync with yylex in parse.y. Companion
1756 ** routine escapedStringLength predicts the length needed to write the string
1757 ** when it is expanded with the additional characters. Returns the number
1758 ** of characters to which the string expanded.
1760 static int escapeStringChars(char *fromString
, char *toString
)
1762 char *e
, *c
, *outPtr
= toString
;
1764 /* substitute escape sequences */
1765 for (c
=fromString
; *c
!='\0'; c
++) {
1766 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1769 *outPtr
++ = ReplaceChars
[e
-EscapeChars
];
1777 return outPtr
- toString
;
1781 ** Predict the length of a string needed to hold a copy of "string" with
1782 ** special characters replaced with escape sequences by escapeStringChars.
1784 static int escapedStringLength(char *string
)
1789 /* calculate length and allocate returned string */
1790 for (c
=string
; *c
!='\0'; c
++) {
1791 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1803 ** Built-in macro subroutine for getting the length of a string
1805 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1806 DataValue
*result
, char **errMsg
)
1808 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
1811 return wrongNArgsErr(errMsg
);
1812 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1814 result
->tag
= INT_TAG
;
1815 result
->val
.n
= strlen(string
);
1820 ** Built-in macro subroutines for min and max
1822 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1823 DataValue
*result
, char **errMsg
)
1825 int minVal
, value
, i
;
1828 return tooFewArgsErr(errMsg
);
1829 if (!readIntArg(argList
[0], &minVal
, errMsg
))
1831 for (i
=0; i
<nArgs
; i
++) {
1832 if (!readIntArg(argList
[i
], &value
, errMsg
))
1834 minVal
= value
< minVal
? value
: minVal
;
1836 result
->tag
= INT_TAG
;
1837 result
->val
.n
= minVal
;
1840 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1841 DataValue
*result
, char **errMsg
)
1843 int maxVal
, value
, i
;
1846 return tooFewArgsErr(errMsg
);
1847 if (!readIntArg(argList
[0], &maxVal
, errMsg
))
1849 for (i
=0; i
<nArgs
; i
++) {
1850 if (!readIntArg(argList
[i
], &value
, errMsg
))
1852 maxVal
= value
> maxVal
? value
: maxVal
;
1854 result
->tag
= INT_TAG
;
1855 result
->val
.n
= maxVal
;
1859 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1860 DataValue
*result
, char **errMsg
)
1862 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1864 char fullname
[MAXPATHLEN
];
1865 char normalizedString
[MAXPATHLEN
];
1867 /* Read the argument representing the window to focus to, and translate
1868 it into a pointer to a real WindowInfo */
1870 return wrongNArgsErr(errMsg
);
1872 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
1874 } else if (!strcmp(string
, "last")) {
1876 } else if (!strcmp(string
, "next")) {
1878 } else if (strlen(string
) >= MAXPATHLEN
) {
1879 *errMsg
= "Pathname too long in focus_window()";
1882 /* just use the plain name as supplied */
1883 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1884 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1885 if (!strcmp(string
, fullname
)) {
1889 /* didn't work? try normalizing the string passed in */
1891 strncpy(normalizedString
, string
, MAXPATHLEN
);
1892 normalizedString
[MAXPATHLEN
-1] = '\0';
1893 if (1 == NormalizePathname(normalizedString
)) {
1894 /* Something is broken with the input pathname. */
1895 *errMsg
= "Pathname too long in focus_window()";
1898 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1899 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1900 if (!strcmp(normalizedString
, fullname
))
1906 /* If no matching window was found, return empty string and do nothing */
1908 result
->tag
= STRING_TAG
;
1909 result
->val
.str
.rep
= PERM_ALLOC_STR("");
1910 result
->val
.str
.len
= 0;
1914 /* Change the focused window to the requested one */
1915 SetMacroFocusWindow(w
);
1917 /* turn on syntax highlight that might have been deferred */
1918 if (w
->highlightSyntax
&& w
->highlightData
==NULL
)
1919 StartHighlighting(w
, False
);
1921 /* Return the name of the window */
1922 result
->tag
= STRING_TAG
;
1923 AllocNString(&result
->val
.str
, strlen(w
->path
)+strlen(w
->filename
)+1);
1924 sprintf(result
->val
.str
.rep
, "%s%s", w
->path
, w
->filename
);
1929 ** Built-in macro subroutine for getting text from the current window's text
1932 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1933 DataValue
*result
, char **errMsg
)
1936 textBuffer
*buf
= window
->buffer
;
1939 /* Validate arguments and convert to int */
1941 return wrongNArgsErr(errMsg
);
1942 if (!readIntArg(argList
[0], &from
, errMsg
))
1944 if (!readIntArg(argList
[1], &to
, errMsg
))
1946 if (from
< 0) from
= 0;
1947 if (from
> buf
->length
) from
= buf
->length
;
1949 if (to
> buf
->length
) to
= buf
->length
;
1950 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1952 /* Copy text from buffer (this extra copy could be avoided if textBuf.c
1953 provided a routine for writing into a pre-allocated string) */
1954 result
->tag
= STRING_TAG
;
1955 AllocNString(&result
->val
.str
, to
- from
+ 1);
1956 rangeText
= BufGetRange(buf
, from
, to
);
1957 BufUnsubstituteNullChars(rangeText
, buf
);
1958 strcpy(result
->val
.str
.rep
, rangeText
);
1959 /* Note: after the un-substitution, it is possible that strlen() != len,
1960 but that's because strlen() can't deal with 0-characters. */
1966 ** Built-in macro subroutine for getting a single character at the position
1967 ** given, from the current window
1969 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1970 DataValue
*result
, char **errMsg
)
1973 textBuffer
*buf
= window
->buffer
;
1975 /* Validate argument and convert it to int */
1977 return wrongNArgsErr(errMsg
);
1978 if (!readIntArg(argList
[0], &pos
, errMsg
))
1980 if (pos
< 0) pos
= 0;
1981 if (pos
> buf
->length
) pos
= buf
->length
;
1983 /* Return the character in a pre-allocated string) */
1984 result
->tag
= STRING_TAG
;
1985 AllocNString(&result
->val
.str
, 2);
1986 result
->val
.str
.rep
[0] = BufGetCharacter(buf
, pos
);
1987 BufUnsubstituteNullChars(result
->val
.str
.rep
, buf
);
1988 /* Note: after the un-substitution, it is possible that strlen() != len,
1989 but that's because strlen() can't deal with 0-characters. */
1994 ** Built-in macro subroutine for replacing text in the current window's text
1997 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1998 DataValue
*result
, char **errMsg
)
2001 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2002 textBuffer
*buf
= window
->buffer
;
2004 /* Validate arguments and convert to int */
2006 return wrongNArgsErr(errMsg
);
2007 if (!readIntArg(argList
[0], &from
, errMsg
))
2009 if (!readIntArg(argList
[1], &to
, errMsg
))
2011 if (!readStringArg(argList
[2], &string
, stringStorage
, errMsg
))
2013 if (from
< 0) from
= 0;
2014 if (from
> buf
->length
) from
= buf
->length
;
2016 if (to
> buf
->length
) to
= buf
->length
;
2017 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2019 /* Don't allow modifications if the window is read-only */
2020 if (IS_ANY_LOCKED(window
->lockReasons
)) {
2021 XBell(XtDisplay(window
->shell
), 0);
2022 result
->tag
= NO_TAG
;
2026 /* There are no null characters in the string (because macro strings
2027 still have null termination), but if the string contains the
2028 character used by the buffer for null substitution, it could
2029 theoretically become a null. In the highly unlikely event that
2030 all of the possible substitution characters in the buffer are used
2031 up, stop the macro and tell the user of the failure */
2032 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
2033 *errMsg
= "Too much binary data in file";
2037 /* Do the replace */
2038 BufReplace(buf
, from
, to
, string
);
2039 result
->tag
= NO_TAG
;
2044 ** Built-in macro subroutine for replacing the primary-selection selected
2045 ** text in the current window's text buffer
2047 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2048 DataValue
*result
, char **errMsg
)
2050 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2052 /* Validate argument and convert to string */
2054 return wrongNArgsErr(errMsg
);
2055 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2058 /* Don't allow modifications if the window is read-only */
2059 if (IS_ANY_LOCKED(window
->lockReasons
)) {
2060 XBell(XtDisplay(window
->shell
), 0);
2061 result
->tag
= NO_TAG
;
2065 /* There are no null characters in the string (because macro strings
2066 still have null termination), but if the string contains the
2067 character used by the buffer for null substitution, it could
2068 theoretically become a null. In the highly unlikely event that
2069 all of the possible substitution characters in the buffer are used
2070 up, stop the macro and tell the user of the failure */
2071 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
2072 *errMsg
= "Too much binary data in file";
2076 /* Do the replace */
2077 BufReplaceSelected(window
->buffer
, string
);
2078 result
->tag
= NO_TAG
;
2083 ** Built-in macro subroutine for getting the text currently selected by
2084 ** the primary selection in the current window's text buffer, or in any
2085 ** part of screen if "any" argument is given
2087 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2088 DataValue
*result
, char **errMsg
)
2092 /* Read argument list to check for "any" keyword, and get the appropriate
2094 if (nArgs
!= 0 && nArgs
!= 1)
2095 return wrongNArgsErr(errMsg
);
2097 if (argList
[0].tag
!= STRING_TAG
|| strcmp(argList
[0].val
.str
.rep
, "any")) {
2098 *errMsg
= "Unrecognized argument to %s";
2101 selText
= GetAnySelection(window
);
2102 if (selText
== NULL
)
2103 selText
= XtNewString("");
2105 selText
= BufGetSelectionText(window
->buffer
);
2106 BufUnsubstituteNullChars(selText
, window
->buffer
);
2109 /* Return the text as an allocated string */
2110 result
->tag
= STRING_TAG
;
2111 AllocNStringCpy(&result
->val
.str
, selText
);
2117 ** Built-in macro subroutine for determining if implicit conversion of
2118 ** a string to number will succeed or fail
2120 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2121 DataValue
*result
, char **errMsg
)
2123 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2126 return wrongNArgsErr(errMsg
);
2128 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
2132 result
->tag
= INT_TAG
;
2133 result
->val
.n
= StringToNum(string
, NULL
);
2139 ** Built-in macro subroutine for replacing a substring within another string
2141 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2142 DataValue
*result
, char **errMsg
)
2144 int from
, to
, length
, replaceLen
, outLen
;
2145 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *replStr
;
2147 /* Validate arguments and convert to int */
2149 return wrongNArgsErr(errMsg
);
2150 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2152 if (!readIntArg(argList
[1], &from
, errMsg
))
2154 if (!readIntArg(argList
[2], &to
, errMsg
))
2156 if (!readStringArg(argList
[3], &replStr
, stringStorage
[1], errMsg
))
2158 length
= strlen(string
);
2159 if (from
< 0) from
= 0;
2160 if (from
> length
) from
= length
;
2162 if (to
> length
) to
= length
;
2163 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2165 /* Allocate a new string and do the replacement */
2166 replaceLen
= strlen(replStr
);
2167 outLen
= length
- (to
- from
) + replaceLen
;
2168 result
->tag
= STRING_TAG
;
2169 AllocNString(&result
->val
.str
, outLen
+1);
2170 strncpy(result
->val
.str
.rep
, string
, from
);
2171 strncpy(&result
->val
.str
.rep
[from
], replStr
, replaceLen
);
2172 strncpy(&result
->val
.str
.rep
[from
+ replaceLen
], &string
[to
], length
- to
);
2177 ** Built-in macro subroutine for getting a substring of a string.
2178 ** Called as substring(string, from [, to])
2180 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2181 DataValue
*result
, char **errMsg
)
2183 int from
, to
, length
;
2184 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2186 /* Validate arguments and convert to int */
2187 if (nArgs
!= 2 && nArgs
!= 3)
2188 return wrongNArgsErr(errMsg
);
2189 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2191 if (!readIntArg(argList
[1], &from
, errMsg
))
2193 length
= to
= strlen(string
);
2195 if (!readIntArg(argList
[2], &to
, errMsg
))
2197 if (from
< 0) from
+= length
;
2198 if (from
< 0) from
= 0;
2199 if (from
> length
) from
= length
;
2200 if (to
< 0) to
+= length
;
2202 if (to
> length
) to
= length
;
2203 if (from
> to
) to
= from
;
2205 /* Allocate a new string and copy the sub-string into it */
2206 result
->tag
= STRING_TAG
;
2207 AllocNStringNCpy(&result
->val
.str
, &string
[from
], to
- from
);
2211 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2212 DataValue
*result
, char **errMsg
)
2215 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2217 /* Validate arguments and convert to int */
2219 return wrongNArgsErr(errMsg
);
2220 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2222 length
= strlen(string
);
2224 /* Allocate a new string and copy an uppercased version of the string it */
2225 result
->tag
= STRING_TAG
;
2226 AllocNString(&result
->val
.str
, length
+ 1);
2227 for (i
=0; i
<length
; i
++)
2228 result
->val
.str
.rep
[i
] = toupper((unsigned char)string
[i
]);
2232 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2233 DataValue
*result
, char **errMsg
)
2236 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2238 /* Validate arguments and convert to int */
2240 return wrongNArgsErr(errMsg
);
2241 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2243 length
= strlen(string
);
2245 /* Allocate a new string and copy an lowercased version of the string it */
2246 result
->tag
= STRING_TAG
;
2247 AllocNString(&result
->val
.str
, length
+ 1);
2248 for (i
=0; i
<length
; i
++)
2249 result
->val
.str
.rep
[i
] = tolower((unsigned char)string
[i
]);
2253 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2254 DataValue
*result
, char **errMsg
)
2259 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2261 /* Get the string argument */
2263 return wrongNArgsErr(errMsg
);
2264 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2267 /* Use the XmClipboard routines to copy the text to the clipboard.
2268 If errors occur, just give up. */
2269 result
->tag
= NO_TAG
;
2270 stat
= SpinClipboardStartCopy(TheDisplay
, XtWindow(window
->textArea
),
2271 s
=XmStringCreateSimple("NEdit"), XtLastTimestampProcessed(TheDisplay
),
2272 window
->textArea
, NULL
, &itemID
);
2274 if (stat
!= ClipboardSuccess
)
2276 if (SpinClipboardCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
, "STRING",
2277 string
, strlen(string
), 0, NULL
) != ClipboardSuccess
) {
2278 SpinClipboardEndCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
);
2281 SpinClipboardEndCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
);
2285 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2286 DataValue
*result
, char **errMsg
)
2288 unsigned long length
, retLength
;
2291 /* Should have no arguments */
2293 return wrongNArgsErr(errMsg
);
2295 /* Ask if there's a string in the clipboard, and get its length */
2296 if (SpinClipboardInquireLength(TheDisplay
, XtWindow(window
->shell
), "STRING",
2297 &length
) != ClipboardSuccess
) {
2298 result
->tag
= STRING_TAG
;
2299 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2300 result
->val
.str
.len
= 0;
2302 * Possibly, the clipboard can remain in a locked state after
2303 * a failure, so we try to remove the lock, just to be sure.
2305 SpinClipboardUnlock(TheDisplay
, XtWindow(window
->shell
));
2309 /* Allocate a new string to hold the data */
2310 result
->tag
= STRING_TAG
;
2311 AllocNString(&result
->val
.str
, (int)length
+ 1);
2313 /* Copy the clipboard contents to the string */
2314 if (SpinClipboardRetrieve(TheDisplay
, XtWindow(window
->shell
), "STRING",
2315 result
->val
.str
.rep
, length
, &retLength
, &id
) != ClipboardSuccess
) {
2318 * Possibly, the clipboard can remain in a locked state after
2319 * a failure, so we try to remove the lock, just to be sure.
2321 SpinClipboardUnlock(TheDisplay
, XtWindow(window
->shell
));
2323 result
->val
.str
.rep
[retLength
] = '\0';
2324 result
->val
.str
.len
= retLength
;
2331 ** Built-in macro subroutine for reading the contents of a text file into
2332 ** a string. On success, returns 1 in $readStatus, and the contents of the
2333 ** file as a string in the subroutine return value. On failure, returns
2334 ** the empty string "" and an 0 $readStatus.
2336 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2337 DataValue
*result
, char **errMsg
)
2339 char stringStorage
[TYPE_INT_STR_SIZE(int)], *name
;
2340 struct stat statbuf
;
2344 /* Validate arguments and convert to int */
2346 return wrongNArgsErr(errMsg
);
2347 if (!readStringArg(argList
[0], &name
, stringStorage
, errMsg
))
2350 /* Read the whole file into an allocated string */
2351 if ((fp
= fopen(name
, "r")) == NULL
)
2353 if (fstat(fileno(fp
), &statbuf
) != 0)
2355 result
->tag
= STRING_TAG
;
2356 AllocNString(&result
->val
.str
, statbuf
.st_size
+1);
2357 readLen
= fread(result
->val
.str
.rep
, sizeof(char), statbuf
.st_size
+1, fp
);
2361 /* Couldn't trust file size. Use slower but more general method */
2362 int chunkSize
= 1024;
2365 buffer
= XtMalloc(readLen
* sizeof(char));
2366 memcpy(buffer
, result
->val
.str
.rep
, readLen
* sizeof(char));
2368 buffer
= XtRealloc(buffer
, (readLen
+chunkSize
)*sizeof(char));
2369 readLen
+= fread(&buffer
[readLen
], sizeof(char), chunkSize
, fp
);
2375 AllocNString(&result
->val
.str
, readLen
+ 1);
2376 memcpy(result
->val
.str
.rep
, buffer
, readLen
* sizeof(char));
2381 /* Return the results */
2382 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2383 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= True
;
2390 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2391 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= False
;
2392 result
->tag
= STRING_TAG
;
2393 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2394 result
->val
.str
.len
= 0;
2399 ** Built-in macro subroutines for writing or appending a string (parameter $1)
2400 ** to a file named in parameter $2. Returns 1 on successful write, or 0 if
2403 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2404 DataValue
*result
, char **errMsg
)
2406 return writeOrAppendFile(False
, window
, argList
, nArgs
, result
, errMsg
);
2409 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2410 DataValue
*result
, char **errMsg
)
2412 return writeOrAppendFile(True
, window
, argList
, nArgs
, result
, errMsg
);
2415 static int writeOrAppendFile(int append
, WindowInfo
*window
,
2416 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
)
2418 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *name
, *string
;
2421 /* Validate argument */
2423 return wrongNArgsErr(errMsg
);
2424 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2426 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
))
2430 if ((fp
= fopen(name
, append
? "a" : "w")) == NULL
) {
2431 result
->tag
= INT_TAG
;
2432 result
->val
.n
= False
;
2436 /* write the string to the file */
2437 fwrite(string
, sizeof(char), strlen(string
), fp
);
2440 result
->tag
= INT_TAG
;
2441 result
->val
.n
= False
;
2446 /* return the status */
2447 result
->tag
= INT_TAG
;
2448 result
->val
.n
= True
;
2453 ** Built-in macro subroutine for searching silently in a window without
2454 ** dialogs, beeps, or changes to the selection. Arguments are: $1: string to
2455 ** search for, $2: starting position. Optional arguments may include the
2456 ** strings: "wrap" to make the search wrap around the beginning or end of the
2457 ** string, "backward" or "forward" to change the search direction ("forward" is
2458 ** the default), "literal", "case" or "regex" to change the search type
2459 ** (default is "literal").
2461 ** Returns the starting position of the match, or -1 if nothing matched.
2462 ** also returns the ending position of the match in $searchEndPos
2464 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2465 DataValue
*result
, char **errMsg
)
2467 DataValue newArgList
[9];
2469 /* Use the search string routine, by adding the buffer contents as
2470 the string argument */
2472 return wrongNArgsErr(errMsg
);
2474 /* we remove constness from BufAsString() result since we know
2475 searchStringMS will not modify the result */
2476 newArgList
[0].tag
= STRING_TAG
;
2477 newArgList
[0].val
.str
.rep
= (char *)BufAsString(window
->buffer
);
2478 newArgList
[0].val
.str
.len
= window
->buffer
->length
;
2480 /* copy other arguments to the new argument list */
2481 memcpy(&newArgList
[1], argList
, nArgs
* sizeof(DataValue
));
2483 return searchStringMS(window
, newArgList
, nArgs
+1, result
, errMsg
);
2487 ** Built-in macro subroutine for searching a string. Arguments are $1:
2488 ** string to search in, $2: string to search for, $3: starting position.
2489 ** Optional arguments may include the strings: "wrap" to make the search
2490 ** wrap around the beginning or end of the string, "backward" or "forward"
2491 ** to change the search direction ("forward" is the default), "literal",
2492 ** "case" or "regex" to change the search type (default is "literal").
2494 ** Returns the starting position of the match, or -1 if nothing matched.
2495 ** also returns the ending position of the match in $searchEndPos
2497 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2498 DataValue
*result
, char **errMsg
)
2500 int beginPos
, wrap
, direction
, found
= False
, foundStart
, foundEnd
, type
;
2501 int skipSearch
= False
, len
;
2502 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
;
2504 /* Validate arguments and convert to proper types */
2506 return tooFewArgsErr(errMsg
);
2507 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2509 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2511 if (!readIntArg(argList
[2], &beginPos
, errMsg
))
2513 if (!readSearchArgs(&argList
[3], nArgs
-3, &direction
, &type
, &wrap
, errMsg
))
2516 len
= argList
[0].val
.str
.len
;
2517 if (beginPos
> len
) {
2518 if (direction
== SEARCH_FORWARD
) {
2520 beginPos
= 0; /* Wrap immediately */
2528 } else if (beginPos
< 0) {
2529 if (direction
== SEARCH_BACKWARD
) {
2531 beginPos
= len
; /* Wrap immediately */
2542 found
= SearchString(string
, searchStr
, direction
, type
, wrap
, beginPos
,
2543 &foundStart
, &foundEnd
, NULL
, NULL
, GetWindowDelimiters(window
));
2545 /* Return the results */
2546 ReturnGlobals
[SEARCH_END
]->value
.tag
= INT_TAG
;
2547 ReturnGlobals
[SEARCH_END
]->value
.val
.n
= found
? foundEnd
: 0;
2548 result
->tag
= INT_TAG
;
2549 result
->val
.n
= found
? foundStart
: -1;
2554 ** Built-in macro subroutine for replacing all occurences of a search string in
2555 ** a string with a replacement string. Arguments are $1: string to search in,
2556 ** $2: string to search for, $3: replacement string. Also takes an optional
2557 ** search type: one of "literal", "case" or "regex" (default is "literal"), and
2558 ** an optional "copy" argument.
2560 ** Returns a new string with all of the replacements done. If no replacements
2561 ** were performed and "copy" was specified, returns a copy of the original
2562 ** string. Otherwise returns an empty string ("").
2564 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2565 DataValue
*result
, char **errMsg
)
2567 char stringStorage
[3][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
, *replaceStr
;
2568 char *argStr
, *replacedStr
;
2569 int searchType
= SEARCH_LITERAL
, copyStart
, copyEnd
;
2570 int replacedLen
, replaceEnd
, force
=False
, i
;
2572 /* Validate arguments and convert to proper types */
2573 if (nArgs
< 3 || nArgs
> 5)
2574 return wrongNArgsErr(errMsg
);
2575 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2577 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2579 if (!readStringArg(argList
[2], &replaceStr
, stringStorage
[2], errMsg
))
2581 for (i
= 3; i
< nArgs
; i
++) {
2582 /* Read the optional search type and force arguments */
2583 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
2585 if (!StringToSearchType(argStr
, &searchType
)) {
2586 /* It's not a search type. is it "copy"? */
2587 if (!strcmp(argStr
, "copy")) {
2590 *errMsg
= "unrecognized argument to %s";
2596 /* Do the replace */
2597 replacedStr
= ReplaceAllInString(string
, searchStr
, replaceStr
, searchType
,
2598 ©Start
, ©End
, &replacedLen
, GetWindowDelimiters(window
));
2600 /* Return the results */
2601 result
->tag
= STRING_TAG
;
2602 if (replacedStr
== NULL
) {
2604 /* Just copy the original DataValue */
2605 if (argList
[0].tag
== STRING_TAG
) {
2606 result
->val
.str
.rep
= argList
[0].val
.str
.rep
;
2607 result
->val
.str
.len
= argList
[0].val
.str
.len
;
2610 AllocNStringCpy(&result
->val
.str
, string
);
2614 result
->val
.str
.rep
= PERM_ALLOC_STR("");
2615 result
->val
.str
.len
= 0;
2619 size_t remainder
= strlen(&string
[copyEnd
]);
2620 replaceEnd
= copyStart
+ replacedLen
;
2621 AllocNString(&result
->val
.str
, replaceEnd
+ remainder
+ 1);
2622 strncpy(result
->val
.str
.rep
, string
, copyStart
);
2623 strcpy(&result
->val
.str
.rep
[copyStart
], replacedStr
);
2624 strcpy(&result
->val
.str
.rep
[replaceEnd
], &string
[copyEnd
]);
2625 XtFree(replacedStr
);
2630 static int readSearchArgs(DataValue
*argList
, int nArgs
, int *searchDirection
,
2631 int *searchType
, int *wrap
, char **errMsg
)
2634 char *argStr
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2637 *searchDirection
= SEARCH_FORWARD
;
2638 *searchType
= SEARCH_LITERAL
;
2639 for (i
=0; i
<nArgs
; i
++) {
2640 if (!readStringArg(argList
[i
], &argStr
, stringStorage
, errMsg
))
2642 else if (!strcmp(argStr
, "wrap"))
2644 else if (!strcmp(argStr
, "nowrap"))
2646 else if (!strcmp(argStr
, "backward"))
2647 *searchDirection
= SEARCH_BACKWARD
;
2648 else if (!strcmp(argStr
, "forward"))
2649 *searchDirection
= SEARCH_FORWARD
;
2650 else if (!StringToSearchType(argStr
, searchType
)) {
2651 *errMsg
= "Unrecognized argument to %s";
2658 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2659 DataValue
*result
, char **errMsg
)
2663 /* Get argument and convert to int */
2665 return wrongNArgsErr(errMsg
);
2666 if (!readIntArg(argList
[0], &pos
, errMsg
))
2669 /* Set the position */
2670 TextSetCursorPos(window
->lastFocus
, pos
);
2671 result
->tag
= NO_TAG
;
2675 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2676 DataValue
*result
, char **errMsg
)
2678 int start
, end
, startTmp
;
2680 /* Get arguments and convert to int */
2682 return wrongNArgsErr(errMsg
);
2683 if (!readIntArg(argList
[0], &start
, errMsg
))
2685 if (!readIntArg(argList
[1], &end
, errMsg
))
2688 /* Verify integrity of arguments */
2694 if (start
< 0) start
= 0;
2695 if (start
> window
->buffer
->length
) start
= window
->buffer
->length
;
2696 if (end
< 0) end
= 0;
2697 if (end
> window
->buffer
->length
) end
= window
->buffer
->length
;
2699 /* Make the selection */
2700 BufSelect(window
->buffer
, start
, end
);
2701 result
->tag
= NO_TAG
;
2705 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2706 DataValue
*result
, char **errMsg
)
2708 int start
, end
, left
, right
;
2710 /* Get arguments and convert to int */
2712 return wrongNArgsErr(errMsg
);
2713 if (!readIntArg(argList
[0], &start
, errMsg
))
2715 if (!readIntArg(argList
[1], &end
, errMsg
))
2717 if (!readIntArg(argList
[2], &left
, errMsg
))
2719 if (!readIntArg(argList
[3], &right
, errMsg
))
2722 /* Make the selection */
2723 BufRectSelect(window
->buffer
, start
, end
, left
, right
);
2724 result
->tag
= NO_TAG
;
2729 ** Macro subroutine to ring the bell
2731 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2732 DataValue
*result
, char **errMsg
)
2735 return wrongNArgsErr(errMsg
);
2736 XBell(XtDisplay(window
->shell
), 0);
2737 result
->tag
= NO_TAG
;
2741 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2742 DataValue
*result
, char **errMsg
)
2744 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2748 return tooFewArgsErr(errMsg
);
2749 for (i
=0; i
<nArgs
; i
++) {
2750 if (!readStringArg(argList
[i
], &string
, stringStorage
, errMsg
))
2752 printf("%s%s", string
, i
==nArgs
-1 ? "" : " ");
2755 result
->tag
= NO_TAG
;
2760 ** Built-in macro subroutine for getting the value of an environment variable
2762 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2763 DataValue
*result
, char **errMsg
)
2765 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
2769 /* Get name of variable to get */
2771 return wrongNArgsErr(errMsg
);
2772 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
2773 *errMsg
= "argument to %s must be a string";
2776 value
= getenv(name
);
2780 /* Return the text as an allocated string */
2781 result
->tag
= STRING_TAG
;
2782 AllocNStringCpy(&result
->val
.str
, value
);
2786 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2787 DataValue
*result
, char **errMsg
)
2789 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *cmdString
, *inputString
;
2792 return wrongNArgsErr(errMsg
);
2793 if (!readStringArg(argList
[0], &cmdString
, stringStorage
[0], errMsg
))
2795 if (!readStringArg(argList
[1], &inputString
, stringStorage
[1], errMsg
))
2798 /* Shell command execution requires that the macro be suspended, so
2799 this subroutine can't be run if macro execution can't be interrupted */
2800 if (MacroRunWindow()->macroCmdData
== NULL
) {
2801 *errMsg
= "%s can't be called from non-suspendable context";
2806 *errMsg
= "Shell commands not supported under VMS";
2809 ShellCmdToMacroString(window
, cmdString
, inputString
);
2810 result
->tag
= INT_TAG
;
2817 ** Method used by ShellCmdToMacroString (called by shellCmdMS), for returning
2818 ** macro string and exit status after the execution of a shell command is
2819 ** complete. (Sorry about the poor modularity here, it's just not worth
2820 ** teaching other modules about macro return globals, since other than this,
2821 ** they're not used outside of macro.c)
2823 void ReturnShellCommandOutput(WindowInfo
*window
, const char *outText
, int status
)
2826 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2828 if (cmdData
== NULL
)
2830 retVal
.tag
= STRING_TAG
;
2831 AllocNStringCpy(&retVal
.val
.str
, outText
);
2832 ModifyReturnedValue(cmdData
->context
, retVal
);
2833 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.tag
= INT_TAG
;
2834 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.val
.n
= status
;
2837 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2838 DataValue
*result
, char **errMsg
)
2840 macroCmdInfo
*cmdData
;
2841 char stringStorage
[TYPE_INT_STR_SIZE(int)];
2842 char btnStorage
[TYPE_INT_STR_SIZE(int)];
2851 /* Ignore the focused window passed as the function argument and put
2852 the dialog up over the window which is executing the macro */
2853 window
= MacroRunWindow();
2854 cmdData
= window
->macroCmdData
;
2856 /* Dialogs require macro to be suspended and interleaved with other macros.
2857 This subroutine can't be run if macro execution can't be interrupted */
2859 *errMsg
= "%s can't be called from non-suspendable context";
2863 /* Read and check the arguments. The first being the dialog message,
2864 and the rest being the button labels */
2866 *errMsg
= "%s subroutine called with no arguments";
2869 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
)) {
2873 /* check that all button labels can be read */
2874 for (i
=1; i
<nArgs
; i
++) {
2875 if (!readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
)) {
2880 /* pick up the first button */
2888 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
2891 /* Create the message box dialog widget and its dialog shell parent */
2893 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2894 XtSetArg(al
[ac
], XmNmessageString
, s1
=MKSTRING(message
)); ac
++;
2895 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
2896 dialog
= CreateMessageDialog(window
->shell
, "macroDialog", al
, ac
);
2899 /* Only set margin width for the default OK button */
2900 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2901 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
2907 AddMotifCloseCallback(XtParent(dialog
), dialogCloseCB
, window
);
2908 XtAddCallback(dialog
, XmNokCallback
, dialogBtnCB
, window
);
2909 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2910 XmNuserData
, (XtPointer
)1, NULL
);
2911 cmdData
->dialog
= dialog
;
2913 /* Unmanage default buttons, except for "OK" */
2914 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2915 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2917 /* Make callback for the unmanaged cancel button (which can
2918 still get executed via the esc key) activate close box action */
2919 XtAddCallback(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2920 XmNactivateCallback
, dialogCloseCB
, window
);
2922 /* Add user specified buttons (1st is already done) */
2923 for (i
=1; i
<nBtns
; i
++) {
2924 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
2925 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2926 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
2927 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2928 XtAddCallback(btn
, XmNactivateCallback
, dialogBtnCB
, window
);
2932 #ifdef LESSTIF_VERSION
2933 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2934 the escape key for closing the dialog (probably because the
2935 cancel button is not managed). */
2936 XtAddEventHandler(dialog
, KeyPressMask
, False
, dialogEscCB
,
2938 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
2939 True
, GrabModeAsync
, GrabModeAsync
);
2940 #endif /* LESSTIF_VERSION */
2942 /* Put up the dialog */
2943 ManageDialogCenteredOnPointer(dialog
);
2945 /* Stop macro execution until the dialog is complete */
2948 /* Return placeholder result. Value will be changed by button callback */
2949 result
->tag
= INT_TAG
;
2954 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2956 WindowInfo
*window
= (WindowInfo
*)clientData
;
2957 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2961 /* Return the index of the button which was pressed (stored in the userData
2962 field of the button widget). The 1st button, being a gadget, is not
2964 if (cmdData
== NULL
)
2965 return; /* shouldn't happen */
2966 if (XtClass(w
) == xmPushButtonWidgetClass
) {
2967 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
2968 retVal
.val
.n
= (int)userData
;
2971 retVal
.tag
= INT_TAG
;
2972 ModifyReturnedValue(cmdData
->context
, retVal
);
2974 /* Pop down the dialog */
2975 XtDestroyWidget(XtParent(cmdData
->dialog
));
2976 cmdData
->dialog
= NULL
;
2978 /* Continue preempted macro execution */
2979 ResumeMacroExecution(window
);
2982 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2984 WindowInfo
*window
= (WindowInfo
*)clientData
;
2985 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2988 /* Return 0 to show that the dialog was closed via the window close box */
2990 retVal
.tag
= INT_TAG
;
2991 ModifyReturnedValue(cmdData
->context
, retVal
);
2993 /* Pop down the dialog */
2994 XtDestroyWidget(XtParent(cmdData
->dialog
));
2995 cmdData
->dialog
= NULL
;
2997 /* Continue preempted macro execution */
2998 ResumeMacroExecution(window
);
3001 #ifdef LESSTIF_VERSION
3002 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3005 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3007 if (clientData
!= NULL
) {
3008 dialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3012 #endif /* LESSTIF_VERSION */
3014 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3015 DataValue
*result
, char **errMsg
)
3017 macroCmdInfo
*cmdData
;
3018 char stringStorage
[TYPE_INT_STR_SIZE(int)];
3019 char btnStorage
[TYPE_INT_STR_SIZE(int)];
3028 /* Ignore the focused window passed as the function argument and put
3029 the dialog up over the window which is executing the macro */
3030 window
= MacroRunWindow();
3031 cmdData
= window
->macroCmdData
;
3033 /* Dialogs require macro to be suspended and interleaved with other macros.
3034 This subroutine can't be run if macro execution can't be interrupted */
3036 *errMsg
= "%s can't be called from non-suspendable context";
3040 /* Read and check the arguments. The first being the dialog message,
3041 and the rest being the button labels */
3043 *errMsg
= "%s subroutine called with no arguments";
3046 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
)) {
3049 /* check that all button labels can be read */
3050 for (i
=1; i
<nArgs
; i
++) {
3051 if (!readStringArg(argList
[i
], &btnLabel
, stringStorage
, errMsg
)) {
3062 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
3065 /* Create the selection box dialog widget and its dialog shell parent */
3067 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3068 XtSetArg(al
[ac
], XmNselectionLabelString
, s1
=MKSTRING(message
)); ac
++;
3069 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
3070 dialog
= CreatePromptDialog(window
->shell
, "macroStringDialog", al
, ac
);
3073 /* Only set margin width for the default OK button */
3074 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3075 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
3081 AddMotifCloseCallback(XtParent(dialog
), stringDialogCloseCB
, window
);
3082 XtAddCallback(dialog
, XmNokCallback
, stringDialogBtnCB
, window
);
3083 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3084 XmNuserData
, (XtPointer
)1, NULL
);
3085 cmdData
->dialog
= dialog
;
3087 /* Unmanage unneded widgets */
3088 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3089 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3091 /* Make callback for the unmanaged cancel button (which can
3092 still get executed via the esc key) activate close box action */
3093 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3094 XmNactivateCallback
, stringDialogCloseCB
, window
);
3096 /* Add user specified buttons (1st is already done). Selection box
3097 requires a place-holder widget to be added before buttons can be
3098 added, that's what the separator below is for */
3099 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3100 for (i
=1; i
<nBtns
; i
++) {
3101 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
3102 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3103 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
3104 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3105 XtAddCallback(btn
, XmNactivateCallback
, stringDialogBtnCB
, window
);
3109 #ifdef LESSTIF_VERSION
3110 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3111 the escape key for closing the dialog (probably because the
3112 cancel button is not managed). */
3113 XtAddEventHandler(dialog
, KeyPressMask
, False
, stringDialogEscCB
,
3115 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3116 True
, GrabModeAsync
, GrabModeAsync
);
3117 #endif /* LESSTIF_VERSION */
3119 /* Put up the dialog */
3120 ManageDialogCenteredOnPointer(dialog
);
3122 /* Stop macro execution until the dialog is complete */
3125 /* Return placeholder result. Value will be changed by button callback */
3126 result
->tag
= INT_TAG
;
3131 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
3134 WindowInfo
*window
= (WindowInfo
*)clientData
;
3135 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3141 /* shouldn't happen, but would crash if it did */
3142 if (cmdData
== NULL
)
3145 /* Return the string entered in the selection text area */
3146 text
= XmTextGetString(XmSelectionBoxGetChild(cmdData
->dialog
,
3148 retVal
.tag
= STRING_TAG
;
3149 AllocNStringCpy(&retVal
.val
.str
, text
);
3151 ModifyReturnedValue(cmdData
->context
, retVal
);
3153 /* Find the index of the button which was pressed (stored in the userData
3154 field of the button widget). The 1st button, being a gadget, is not
3156 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3157 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3158 btnNum
= (int)userData
;
3162 /* Return the button number in the global variable $string_dialog_button */
3163 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3164 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3166 /* Pop down the dialog */
3167 XtDestroyWidget(XtParent(cmdData
->dialog
));
3168 cmdData
->dialog
= NULL
;
3170 /* Continue preempted macro execution */
3171 ResumeMacroExecution(window
);
3174 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
3177 WindowInfo
*window
= (WindowInfo
*)clientData
;
3178 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3181 /* shouldn't happen, but would crash if it did */
3182 if (cmdData
== NULL
)
3185 /* Return an empty string */
3186 retVal
.tag
= STRING_TAG
;
3187 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3188 retVal
.val
.str
.len
= 0;
3189 ModifyReturnedValue(cmdData
->context
, retVal
);
3191 /* Return button number 0 in the global variable $string_dialog_button */
3192 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3193 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= 0;
3195 /* Pop down the dialog */
3196 XtDestroyWidget(XtParent(cmdData
->dialog
));
3197 cmdData
->dialog
= NULL
;
3199 /* Continue preempted macro execution */
3200 ResumeMacroExecution(window
);
3203 #ifdef LESSTIF_VERSION
3204 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3207 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3209 if (clientData
!= NULL
) {
3210 stringDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3214 #endif /* LESSTIF_VERSION */
3217 ** A subroutine to put up a calltip
3218 ** First arg is either text to be displayed or a key for tip/tag lookup.
3219 ** Optional second arg is the buffer position beneath which to display the
3220 ** upper-left corner of the tip. Default (or -1) puts it under the cursor.
3221 ** Additional optional arguments:
3222 ** "tipText": (default) Indicates first arg is text to be displayed in tip.
3223 ** "tipKey": Indicates first arg is key in calltips database. If key
3224 ** is not found in tip database then the tags database is also
3226 ** "tagKey": Indicates first arg is key in tags database. (Skips
3227 ** search in calltips database.)
3228 ** "center": Horizontally center the calltip at the position
3229 ** "right": Put the right edge of the calltip at the position
3230 ** "center" and "right" cannot both be specified.
3231 ** "above": Place the calltip above the position
3232 ** "strict": Don't move the calltip to keep it on-screen and away
3233 ** from the cursor's line.
3235 ** Returns the new calltip's ID on success, 0 on failure.
3237 ** Does this need to go on IgnoredActions? I don't think so, since
3238 ** showing a calltip may be part of the action you want to learn.
3240 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3241 DataValue
*result
, char **errMsg
)
3243 char stringStorage
[TYPE_INT_STR_SIZE(int)], *tipText
, *txtArg
;
3244 Boolean anchored
= False
, lookup
= True
;
3246 int anchorPos
, hAlign
= TIP_LEFT
, vAlign
= TIP_BELOW
,
3247 alignMode
= TIP_SLOPPY
;
3249 /* Read and check the string */
3251 *errMsg
= "%s subroutine called with too few arguments";
3255 *errMsg
= "%s subroutine called with too many arguments";
3259 /* Read the tip text or key */
3260 if (!readStringArg(argList
[0], &tipText
, stringStorage
, errMsg
))
3263 /* Read the anchor position (-1 for unanchored) */
3265 if (!readIntArg(argList
[1], &anchorPos
, errMsg
))
3270 if (anchorPos
>= 0) anchored
= True
;
3272 /* Any further args are directives for relative positioning */
3273 for (i
= 2; i
< nArgs
; ++i
) {
3274 if (!readStringArg(argList
[i
], &txtArg
, stringStorage
, errMsg
)){
3277 switch( txtArg
[0] ) {
3279 if (strcmp(txtArg
, "center"))
3281 hAlign
= TIP_CENTER
;
3284 if (strcmp(txtArg
, "right"))
3289 if (strcmp(txtArg
, "above"))
3294 if (strcmp(txtArg
, "strict"))
3296 alignMode
= TIP_STRICT
;
3299 if (!strcmp(txtArg
, "tipText"))
3301 else if (!strcmp(txtArg
, "tipKey"))
3303 else if (!strcmp(txtArg
, "tagKey"))
3304 mode
= TIP_FROM_TAG
;
3313 result
->tag
= INT_TAG
;
3314 if (mode
< 0) lookup
= False
;
3315 /* Look up (maybe) a calltip and display it */
3316 result
->val
.n
= ShowTipString( window
, tipText
, anchored
, anchorPos
, lookup
,
3317 mode
, hAlign
, vAlign
, alignMode
);
3322 /* This is how the (more informative) global var. version would work,
3323 assuming there was a global buffer called msg. */
3324 /* sprintf(msg, "unrecognized argument to %%s: \"%s\"", txtArg);
3326 *errMsg
= "unrecognized argument to %s";
3331 ** A subroutine to kill the current calltip
3333 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3334 DataValue
*result
, char **errMsg
)
3339 *errMsg
= "%s subroutine called with too many arguments";
3343 if (!readIntArg(argList
[0], &calltipID
, errMsg
))
3347 KillCalltip( window
, calltipID
);
3349 result
->tag
= NO_TAG
;
3354 * A subroutine to get the ID of the current calltip, or 0 if there is none.
3356 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
,
3357 int nArgs
, DataValue
*result
, char **errMsg
)
3359 result
->tag
= INT_TAG
;
3360 result
->val
.n
= GetCalltipID(window
, 0);
3365 ** filename_dialog([title[, mode[, defaultPath[, filter[, defaultName]]]]])
3367 ** Presents a FileSelectionDialog to the user prompting for a new file.
3370 ** title - will be the title of the dialog, defaults to "Choose file".
3371 ** mode - if set to "exist" (default), the "New File Name" TextField
3372 ** of the FSB will be unmanaged. If "new", the TextField will
3374 ** defaultPath - is the default path to use. Default (or "") will use the
3375 ** active document's directory.
3376 ** filter - the file glob which determines which files to display.
3377 ** Is set to "*" if filter is "" and by default.
3378 ** defaultName - is the default filename that is filled in automatically.
3380 ** Returns "" if the user cancelled the dialog, otherwise returns the path to
3381 ** the file that was selected
3383 ** Note that defaultName doesn't work on all *tifs. :-(
3385 static int filenameDialogMS(WindowInfo
* window
, DataValue
* argList
, int nArgs
,
3386 DataValue
* result
, char** errMsg
)
3388 char stringStorage
[5][TYPE_INT_STR_SIZE(int)];
3389 char filename
[MAXPATHLEN
+ 1];
3390 char* title
= "Choose Filename";
3391 char* mode
= "exist";
3392 char* defaultPath
= "";
3394 char* defaultName
= "";
3395 char* orgDefaultPath
;
3399 /* Ignore the focused window passed as the function argument and put
3400 the dialog up over the window which is executing the macro */
3401 window
= MacroRunWindow();
3403 /* Dialogs require macro to be suspended and interleaved with other macros.
3404 This subroutine can't be run if macro execution can't be interrupted */
3405 if (NULL
== window
->macroCmdData
) {
3406 M_FAILURE("%s can't be called from non-suspendable context");
3409 /* Get the argument list. */
3410 if (nArgs
> 0 && !readStringArg(argList
[0], &title
, stringStorage
[0],
3415 if (nArgs
> 1 && !readStringArg(argList
[1], &mode
, stringStorage
[1],
3419 if (0 != strcmp(mode
, "exist") && 0 != strcmp(mode
, "new")) {
3420 M_FAILURE("Invalid value for mode in %s");
3423 if (nArgs
> 2 && !readStringArg(argList
[2], &defaultPath
, stringStorage
[2],
3428 if (nArgs
> 3 && !readStringArg(argList
[3], &filter
, stringStorage
[3],
3433 if (nArgs
> 4 && !readStringArg(argList
[4], &defaultName
, stringStorage
[4],
3439 M_FAILURE("%s called with too many arguments. Expects at most 5 arguments.");
3442 /* Set default directory (saving original for later) */
3443 orgDefaultPath
= GetFileDialogDefaultDirectory();
3444 if ('\0' != defaultPath
[0]) {
3445 SetFileDialogDefaultDirectory(defaultPath
);
3447 SetFileDialogDefaultDirectory(window
->path
);
3450 /* Set filter (saving original for later) */
3451 orgFilter
= GetFileDialogDefaultPattern();
3452 if ('\0' != filter
[0]) {
3453 SetFileDialogDefaultPattern(filter
);
3456 /* Fork to one of the worker methods from util/getfiles.c.
3457 (This should obviously be refactored.) */
3458 if (0 == strcmp(mode
, "exist")) {
3459 gfnResult
= GetExistingFilename(window
->shell
, title
, filename
);
3461 gfnResult
= GetNewFilename(window
->shell
, title
, filename
, defaultName
);
3462 } /* Invalid values are weeded out above. */
3464 /* Reset original values and free temps */
3465 SetFileDialogDefaultDirectory(orgDefaultPath
);
3466 SetFileDialogDefaultPattern(orgFilter
);
3467 XtFree(orgDefaultPath
);
3470 result
->tag
= STRING_TAG
;
3471 if (GFN_OK
== gfnResult
) {
3472 /* Got a string, copy it to the result */
3473 if (!AllocNStringNCpy(&result
->val
.str
, filename
, MAXPATHLEN
)) {
3474 M_FAILURE("failed to allocate return value: %s");
3477 /* User cancelled. Return "" */
3478 result
->val
.str
.rep
= PERM_ALLOC_STR("");
3479 result
->val
.str
.len
= 0;
3486 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3487 DataValue
*result
, char **errMsg
)
3489 macroCmdInfo
*cmdData
;
3490 char stringStorage
[TYPE_INT_STR_SIZE(int)];
3491 char textStorage
[TYPE_INT_STR_SIZE(int)];
3492 char btnStorage
[TYPE_INT_STR_SIZE(int)];
3494 char *message
, *text
;
3499 char *p
, *old_p
, **text_lines
, *tmp
;
3502 XmString
*test_strings
;
3508 /* Ignore the focused window passed as the function argument and put
3509 the dialog up over the window which is executing the macro */
3510 window
= MacroRunWindow();
3511 cmdData
= window
->macroCmdData
;
3513 /* Dialogs require macro to be suspended and interleaved with other macros.
3514 This subroutine can't be run if macro execution can't be interrupted */
3516 *errMsg
= "%s can't be called from non-suspendable context";
3520 /* Read and check the arguments. The first being the dialog message,
3521 and the rest being the button labels */
3523 *errMsg
= "%s subroutine called with no message, string or arguments";
3527 if (!readStringArg(argList
[0], &message
, stringStorage
, errMsg
))
3530 if (!readStringArg(argList
[1], &text
, textStorage
, errMsg
))
3533 if (!text
|| text
[0] == '\0') {
3534 *errMsg
= "%s subroutine called with empty list data";
3538 /* check that all button labels can be read */
3539 for (i
=2; i
<nArgs
; i
++)
3540 if (!readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
))
3543 /* pick up the first button */
3551 readStringArg(argList
[0], &btnLabel
, btnStorage
, errMsg
);
3554 /* count the lines in the text - add one for unterminated last line */
3556 for (p
= text
; *p
; p
++)
3560 /* now set up arrays of pointers to lines */
3561 /* test_strings to hold the display strings (tab expanded) */
3562 /* text_lines to hold the original text lines (without the '\n's) */
3563 test_strings
= (XmString
*) XtMalloc(sizeof(XmString
) * nlines
);
3564 text_lines
= (char **)XtMalloc(sizeof(char *) * (nlines
+ 1));
3565 for (n
= 0; n
< nlines
; n
++) {
3566 test_strings
[n
] = (XmString
)0;
3567 text_lines
[n
] = (char *)0;
3569 text_lines
[n
] = (char *)0; /* make sure this is a null-terminated table */
3571 /* pick up the tabDist value */
3572 tabDist
= window
->buffer
->tabDist
;
3574 /* load the table */
3578 tmp_len
= 0; /* current allocated size of temporary buffer tmp */
3579 tmp
= malloc(1); /* temporary buffer into which to expand tabs */
3581 is_last
= (*p
== '\0');
3582 if (*p
== '\n' || is_last
) {
3584 if (strlen(old_p
) > 0) { /* only include non-empty lines */
3588 /* save the actual text line in text_lines[n] */
3589 text_lines
[n
] = (char *)XtMalloc(strlen(old_p
) + 1);
3590 strcpy(text_lines
[n
], old_p
);
3592 /* work out the tabs expanded length */
3593 for (s
= old_p
, l
= 0; *s
; s
++)
3594 l
+= (*s
== '\t') ? tabDist
- (l
% tabDist
) : 1;
3596 /* verify tmp is big enough then tab-expand old_p into tmp */
3598 tmp
= realloc(tmp
, (tmp_len
= l
) + 1);
3599 for (s
= old_p
, t
= tmp
, l
= 0; *s
; s
++) {
3601 for (i
= tabDist
- (l
% tabDist
); i
--; l
++)
3610 /* that's it: tmp is the tab-expanded version of old_p */
3611 test_strings
[n
] = MKSTRING(tmp
);
3616 *p
= '\n'; /* put back our newline */
3621 free(tmp
); /* don't need this anymore */
3624 test_strings
[0] = MKSTRING("");
3628 /* Create the selection box dialog widget and its dialog shell parent */
3630 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3631 XtSetArg(al
[ac
], XmNlistLabelString
, s1
=MKSTRING(message
)); ac
++;
3632 XtSetArg(al
[ac
], XmNlistItems
, test_strings
); ac
++;
3633 XtSetArg(al
[ac
], XmNlistItemCount
, nlines
); ac
++;
3634 XtSetArg(al
[ac
], XmNlistVisibleItemCount
, (nlines
> 10) ? 10 : nlines
); ac
++;
3635 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabel
)); ac
++;
3636 dialog
= CreateSelectionDialog(window
->shell
, "macroListDialog", al
, ac
);
3639 /* Only set margin width for the default OK button */
3640 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3641 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
3645 AddMotifCloseCallback(XtParent(dialog
), listDialogCloseCB
, window
);
3646 XtAddCallback(dialog
, XmNokCallback
, listDialogBtnCB
, window
);
3647 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3648 XmNuserData
, (XtPointer
)1, NULL
);
3651 cmdData
->dialog
= dialog
;
3653 /* forget lines stored in list */
3655 XmStringFree(test_strings
[n
]);
3656 XtFree((char *)test_strings
);
3658 /* modify the list */
3659 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_LIST
),
3660 XmNselectionPolicy
, XmSINGLE_SELECT
,
3661 XmNuserData
, (XtPointer
)text_lines
, NULL
);
3663 /* Unmanage unneeded widgets */
3664 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_APPLY_BUTTON
));
3665 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3666 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3667 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_TEXT
));
3668 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_SELECTION_LABEL
));
3670 /* Make callback for the unmanaged cancel button (which can
3671 still get executed via the esc key) activate close box action */
3672 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3673 XmNactivateCallback
, listDialogCloseCB
, window
);
3675 /* Add user specified buttons (1st is already done). Selection box
3676 requires a place-holder widget to be added before buttons can be
3677 added, that's what the separator below is for */
3678 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3679 for (i
=1; i
<nBtns
; i
++) {
3680 readStringArg(argList
[i
], &btnLabel
, btnStorage
, errMsg
);
3681 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3682 XmNlabelString
, s1
=XmStringCreateSimple(btnLabel
),
3683 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3684 XtAddCallback(btn
, XmNactivateCallback
, listDialogBtnCB
, window
);
3688 #ifdef LESSTIF_VERSION
3689 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3690 the escape key for closing the dialog. */
3691 XtAddEventHandler(dialog
, KeyPressMask
, False
, listDialogEscCB
,
3693 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3694 True
, GrabModeAsync
, GrabModeAsync
);
3695 #endif /* LESSTIF_VERSION */
3697 /* Put up the dialog */
3698 ManageDialogCenteredOnPointer(dialog
);
3700 /* Stop macro execution until the dialog is complete */
3703 /* Return placeholder result. Value will be changed by button callback */
3704 result
->tag
= INT_TAG
;
3709 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
3712 WindowInfo
*window
= (WindowInfo
*)clientData
;
3713 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3719 int n_sel
, *seltable
, sel_index
= 0;
3723 /* shouldn't happen, but would crash if it did */
3724 if (cmdData
== NULL
)
3727 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3728 /* Return the string selected in the selection list area */
3729 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3730 if (!XmListGetSelectedPos(theList
, &seltable
, &n_sel
)) {
3734 sel_index
= seltable
[0] - 1;
3735 XtFree((XtPointer
)seltable
);
3739 text
= PERM_ALLOC_STR("");
3743 length
= strlen((char *)text_lines
[sel_index
]);
3744 text
= AllocString(length
+ 1);
3745 strcpy(text
, text_lines
[sel_index
]);
3748 /* don't need text_lines anymore: free it */
3749 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3750 XtFree((XtPointer
)text_lines
[sel_index
]);
3751 XtFree((XtPointer
)text_lines
);
3753 retVal
.tag
= STRING_TAG
;
3754 retVal
.val
.str
.rep
= text
;
3755 retVal
.val
.str
.len
= length
;
3756 ModifyReturnedValue(cmdData
->context
, retVal
);
3758 /* Find the index of the button which was pressed (stored in the userData
3759 field of the button widget). The 1st button, being a gadget, is not
3761 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3762 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3763 btnNum
= (int)userData
;
3767 /* Return the button number in the global variable $list_dialog_button */
3768 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3769 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3771 /* Pop down the dialog */
3772 XtDestroyWidget(XtParent(cmdData
->dialog
));
3773 cmdData
->dialog
= NULL
;
3775 /* Continue preempted macro execution */
3776 ResumeMacroExecution(window
);
3779 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
3782 WindowInfo
*window
= (WindowInfo
*)clientData
;
3783 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3789 /* shouldn't happen, but would crash if it did */
3790 if (cmdData
== NULL
)
3793 /* don't need text_lines anymore: retrieve it then free it */
3794 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3795 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3796 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3797 XtFree((XtPointer
)text_lines
[sel_index
]);
3798 XtFree((XtPointer
)text_lines
);
3800 /* Return an empty string */
3801 retVal
.tag
= STRING_TAG
;
3802 retVal
.val
.str
.rep
= PERM_ALLOC_STR("");
3803 retVal
.val
.str
.len
= 0;
3804 ModifyReturnedValue(cmdData
->context
, retVal
);
3806 /* Return button number 0 in the global variable $list_dialog_button */
3807 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3808 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= 0;
3810 /* Pop down the dialog */
3811 XtDestroyWidget(XtParent(cmdData
->dialog
));
3812 cmdData
->dialog
= NULL
;
3814 /* Continue preempted macro execution */
3815 ResumeMacroExecution(window
);
3817 /* T Balinski End */
3819 #ifdef LESSTIF_VERSION
3820 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3823 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3825 if (clientData
!= NULL
) {
3826 listDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3830 #endif /* LESSTIF_VERSION */
3833 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3834 DataValue
*result
, char **errMsg
)
3836 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3837 char *leftStr
, *rightStr
, *argStr
;
3838 int considerCase
= True
;
3843 return(wrongNArgsErr(errMsg
));
3845 if (!readStringArg(argList
[0], &leftStr
, stringStorage
[0], errMsg
))
3847 if (!readStringArg(argList
[1], &rightStr
, stringStorage
[1], errMsg
))
3849 for (i
= 2; i
< nArgs
; ++i
) {
3850 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
3852 else if (!strcmp(argStr
, "case"))
3853 considerCase
= True
;
3854 else if (!strcmp(argStr
, "nocase"))
3855 considerCase
= False
;
3857 *errMsg
= "Unrecognized argument to %s";
3862 compareResult
= strcmp(leftStr
, rightStr
);
3863 compareResult
= (compareResult
> 0) ? 1 : ((compareResult
< 0) ? -1 : 0);
3866 compareResult
= strCaseCmp(leftStr
, rightStr
);
3868 result
->tag
= INT_TAG
;
3869 result
->val
.n
= compareResult
;
3874 ** This function is intended to split strings into an array of substrings
3875 ** Importatnt note: It should always return at least one entry with key 0
3876 ** split("", ",") result[0] = ""
3877 ** split("1,2", ",") result[0] = "1" result[1] = "2"
3878 ** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
3880 ** This behavior is specifically important when used to break up
3881 ** array sub-scripts
3884 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3885 DataValue
*result
, char **errMsg
)
3887 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3888 char *sourceStr
, *splitStr
, *typeSplitStr
;
3889 int searchType
, beginPos
, foundStart
, foundEnd
, strLength
, lastEnd
;
3890 int found
, elementEnd
, indexNum
;
3891 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
3896 return(wrongNArgsErr(errMsg
));
3898 if (!readStringArg(argList
[0], &sourceStr
, stringStorage
[0], errMsg
)) {
3899 *errMsg
= "first argument must be a string: %s";
3902 if (!readStringArg(argList
[1], &splitStr
, stringStorage
[1], errMsg
)) {
3906 if (splitStr
[0] == 0) {
3910 if (splitStr
== NULL
) {
3911 *errMsg
= "second argument must be a non-empty string: %s";
3914 if (nArgs
> 2 && readStringArg(argList
[2], &typeSplitStr
, stringStorage
[2], errMsg
)) {
3915 if (!StringToSearchType(typeSplitStr
, &searchType
)) {
3916 *errMsg
= "unrecognized argument to %s";
3921 searchType
= SEARCH_LITERAL
;
3924 result
->tag
= ARRAY_TAG
;
3925 result
->val
.arrayPtr
= ArrayNew();
3930 strLength
= strlen(sourceStr
);
3932 while (found
&& beginPos
< strLength
) {
3933 sprintf(indexStr
, "%d", indexNum
);
3934 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3935 if (!allocIndexStr
) {
3936 *errMsg
= "array element failed to allocate key: %s";
3939 strcpy(allocIndexStr
, indexStr
);
3940 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
, searchType
,
3941 False
, beginPos
, &foundStart
, &foundEnd
,
3942 NULL
, NULL
, GetWindowDelimiters(window
));
3943 elementEnd
= found
? foundStart
: strLength
;
3944 elementLen
= elementEnd
- lastEnd
;
3945 element
.tag
= STRING_TAG
;
3946 if (!AllocNStringNCpy(&element
.val
.str
, &sourceStr
[lastEnd
], elementLen
)) {
3947 *errMsg
= "failed to allocate element value: %s";
3951 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3952 M_ARRAY_INSERT_FAILURE();
3956 if (foundStart
== foundEnd
) {
3957 beginPos
= foundEnd
+ 1; /* Avoid endless loop for 0-width match */
3959 beginPos
= foundEnd
;
3962 beginPos
= strLength
; /* Break the loop */
3968 sprintf(indexStr
, "%d", indexNum
);
3969 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3970 if (!allocIndexStr
) {
3971 *errMsg
= "array element failed to allocate key: %s";
3974 strcpy(allocIndexStr
, indexStr
);
3975 element
.tag
= STRING_TAG
;
3976 if (lastEnd
== strLength
) {
3977 /* The pattern mathed the end of the string. Add an empty chunk. */
3978 element
.val
.str
.rep
= PERM_ALLOC_STR("");
3979 element
.val
.str
.len
= 0;
3981 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3982 M_ARRAY_INSERT_FAILURE();
3985 /* We skipped the last character to prevent an endless loop.
3986 Add it to the list. */
3987 elementLen
= strLength
- lastEnd
;
3988 if (!AllocNStringNCpy(&element
.val
.str
, &sourceStr
[lastEnd
], elementLen
)) {
3989 *errMsg
= "failed to allocate element value: %s";
3993 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3994 M_ARRAY_INSERT_FAILURE();
3997 /* If the pattern can match zero-length strings, we may have to
3998 add a final empty chunk.
3999 For instance: split("abc\n", "$", "regex")
4000 -> matches before \n and at end of string
4001 -> expected output: "abc", "\n", ""
4002 The '\n' gets added in the lines above, but we still have to
4003 verify whether the pattern also matches the end of the string,
4004 and add an empty chunk in case it does. */
4005 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
,
4006 searchType
, False
, strLength
, &foundStart
, &foundEnd
,
4007 NULL
, NULL
, GetWindowDelimiters(window
));
4010 sprintf(indexStr
, "%d", indexNum
);
4011 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4012 if (!allocIndexStr
) {
4013 *errMsg
= "array element failed to allocate key: %s";
4016 strcpy(allocIndexStr
, indexStr
);
4017 element
.tag
= STRING_TAG
;
4018 element
.val
.str
.rep
= PERM_ALLOC_STR("");
4019 element
.val
.str
.len
= 0;
4021 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
4022 M_ARRAY_INSERT_FAILURE();
4031 ** Set the backlighting string resource for the current window. If no parameter
4032 ** is passed or the value "default" is passed, it attempts to set the preference
4033 ** value of the resource. If the empty string is passed, the backlighting string
4034 ** will be cleared, turning off backlighting.
4037 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
4038 int nArgs, DataValue *result, char **errMsg)
4040 char *backlightString;
4043 backlightString = GetPrefBacklightCharTypes();
4045 else if (nArgs == 1) {
4046 if (argList[0].tag != STRING_TAG) {
4047 *errMsg = "%s not called with a string parameter";
4050 backlightString = argList[0].val.str.rep;
4053 return wrongNArgsErr(errMsg);
4055 if (strcmp(backlightString, "default") == 0)
4056 backlightString = GetPrefBacklightCharTypes();
4057 if (backlightString && *backlightString == '\0') / * empty string param * /
4058 backlightString = NULL; / * turns of backlighting * /
4060 SetBacklightChars(window, backlightString);
4064 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4065 DataValue
*result
, char **errMsg
)
4067 result
->tag
= INT_TAG
;
4068 result
->val
.n
= TextGetCursorPos(window
->lastFocus
);
4072 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4073 DataValue
*result
, char **errMsg
)
4075 int line
, cursorPos
, colNum
;
4077 result
->tag
= INT_TAG
;
4078 cursorPos
= TextGetCursorPos(window
->lastFocus
);
4079 if (!TextPosToLineAndCol(window
->lastFocus
, cursorPos
, &line
, &colNum
))
4080 line
= BufCountLines(window
->buffer
, 0, cursorPos
) + 1;
4081 result
->val
.n
= line
;
4085 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4086 DataValue
*result
, char **errMsg
)
4088 textBuffer
*buf
= window
->buffer
;
4091 result
->tag
= INT_TAG
;
4092 cursorPos
= TextGetCursorPos(window
->lastFocus
);
4093 result
->val
.n
= BufCountDispChars(buf
, BufStartOfLine(buf
, cursorPos
),
4098 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4099 DataValue
*result
, char **errMsg
)
4101 result
->tag
= STRING_TAG
;
4102 AllocNStringCpy(&result
->val
.str
, window
->filename
);
4106 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4107 DataValue
*result
, char **errMsg
)
4109 result
->tag
= STRING_TAG
;
4110 AllocNStringCpy(&result
->val
.str
, window
->path
);
4114 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4115 DataValue
*result
, char **errMsg
)
4117 result
->tag
= INT_TAG
;
4118 result
->val
.n
= window
->buffer
->length
;
4122 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4123 DataValue
*result
, char **errMsg
)
4125 result
->tag
= INT_TAG
;
4126 result
->val
.n
= window
->buffer
->primary
.selected
?
4127 window
->buffer
->primary
.start
: -1;
4131 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4132 DataValue
*result
, char **errMsg
)
4134 result
->tag
= INT_TAG
;
4135 result
->val
.n
= window
->buffer
->primary
.selected
?
4136 window
->buffer
->primary
.end
: -1;
4140 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4141 DataValue
*result
, char **errMsg
)
4143 selection
*sel
= &window
->buffer
->primary
;
4145 result
->tag
= INT_TAG
;
4146 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectStart
: -1;
4150 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4151 DataValue
*result
, char **errMsg
)
4153 selection
*sel
= &window
->buffer
->primary
;
4155 result
->tag
= INT_TAG
;
4156 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectEnd
: -1;
4160 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4161 DataValue
*result
, char **errMsg
)
4165 XtVaGetValues(window
->textArea
, textNcolumns
, &nCols
,
4166 textNwrapMargin
, &margin
, NULL
);
4167 result
->tag
= INT_TAG
;
4168 result
->val
.n
= margin
== 0 ? nCols
: margin
;
4172 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4173 DataValue
*result
, char **errMsg
)
4175 result
->tag
= INT_TAG
;
4176 result
->val
.n
= window
->showStats
? 1 : 0;
4180 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4181 DataValue
*result
, char **errMsg
)
4183 result
->tag
= INT_TAG
;
4184 result
->val
.n
= window
->showISearchLine
? 1 : 0;
4188 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4189 DataValue
*result
, char **errMsg
)
4191 result
->tag
= INT_TAG
;
4192 result
->val
.n
= window
->showLineNumbers
? 1 : 0;
4196 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4197 DataValue
*result
, char **errMsg
)
4201 switch (window
->indentStyle
) {
4202 case NO_AUTO_INDENT
:
4203 res
= PERM_ALLOC_STR("off");
4206 res
= PERM_ALLOC_STR("on");
4209 res
= PERM_ALLOC_STR("smart");
4212 *errMsg
= "Invalid indent style value encountered in %s";
4216 result
->tag
= STRING_TAG
;
4217 result
->val
.str
.rep
= res
;
4218 result
->val
.str
.len
= strlen(res
);
4222 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4223 DataValue
*result
, char **errMsg
)
4227 switch (window
->wrapMode
) {
4229 res
= PERM_ALLOC_STR("none");
4232 res
= PERM_ALLOC_STR("auto");
4234 case CONTINUOUS_WRAP
:
4235 res
= PERM_ALLOC_STR("continuous");
4238 *errMsg
= "Invalid wrap style value encountered in %s";
4242 result
->tag
= STRING_TAG
;
4243 result
->val
.str
.rep
= res
;
4244 result
->val
.str
.len
= strlen(res
);
4248 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4249 DataValue
*result
, char **errMsg
)
4251 result
->tag
= INT_TAG
;
4252 result
->val
.n
= window
->highlightSyntax
? 1 : 0;
4256 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4257 DataValue
*result
, char **errMsg
)
4259 result
->tag
= INT_TAG
;
4260 result
->val
.n
= window
->saveOldVersion
? 1 : 0;
4264 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4265 DataValue
*result
, char **errMsg
)
4267 result
->tag
= INT_TAG
;
4268 result
->val
.n
= window
->autoSave
? 1 : 0;
4272 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4273 DataValue
*result
, char **errMsg
)
4277 switch (window
->showMatchingStyle
) {
4279 res
= PERM_ALLOC_STR(NO_FLASH_STRING
);
4282 res
= PERM_ALLOC_STR(FLASH_DELIMIT_STRING
);
4285 res
= PERM_ALLOC_STR(FLASH_RANGE_STRING
);
4288 *errMsg
= "Invalid match flashing style value encountered in %s";
4292 result
->tag
= STRING_TAG
;
4293 result
->val
.str
.rep
= res
;
4294 result
->val
.str
.len
= strlen(res
);
4298 static int matchSyntaxBasedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4299 DataValue
*result
, char **errMsg
)
4301 result
->tag
= INT_TAG
;
4302 result
->val
.n
= window
->matchSyntaxBased
? 1 : 0;
4308 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4309 DataValue
*result
, char **errMsg
)
4311 result
->tag
= INT_TAG
;
4312 result
->val
.n
= window
->overstrike
? 1 : 0;
4316 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4317 DataValue
*result
, char **errMsg
)
4319 result
->tag
= INT_TAG
;
4320 result
->val
.n
= (IS_ANY_LOCKED(window
->lockReasons
)) ? 1 : 0;
4324 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4325 DataValue
*result
, char **errMsg
)
4327 result
->tag
= INT_TAG
;
4328 result
->val
.n
= (IS_USER_LOCKED(window
->lockReasons
)) ? 1 : 0;
4332 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4333 DataValue
*result
, char **errMsg
)
4337 switch (window
->fileFormat
) {
4338 case UNIX_FILE_FORMAT
:
4339 res
= PERM_ALLOC_STR("unix");
4341 case DOS_FILE_FORMAT
:
4342 res
= PERM_ALLOC_STR("dos");
4344 case MAC_FILE_FORMAT
:
4345 res
= PERM_ALLOC_STR("macintosh");
4348 *errMsg
= "Invalid linefeed style value encountered in %s";
4351 result
->tag
= STRING_TAG
;
4352 result
->val
.str
.rep
= res
;
4353 result
->val
.str
.len
= strlen(res
);
4357 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4358 DataValue
*result
, char **errMsg
)
4360 result
->tag
= STRING_TAG
;
4361 AllocNStringCpy(&result
->val
.str
, window
->fontName
);
4365 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4366 DataValue
*result
, char **errMsg
)
4368 result
->tag
= STRING_TAG
;
4369 AllocNStringCpy(&result
->val
.str
, window
->italicFontName
);
4373 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4374 DataValue
*result
, char **errMsg
)
4376 result
->tag
= STRING_TAG
;
4377 AllocNStringCpy(&result
->val
.str
, window
->boldFontName
);
4381 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4382 DataValue
*result
, char **errMsg
)
4384 result
->tag
= STRING_TAG
;
4385 AllocNStringCpy(&result
->val
.str
, window
->boldItalicFontName
);
4389 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4390 DataValue
*result
, char **errMsg
)
4392 result
->tag
= STRING_TAG
;
4393 result
->val
.str
.rep
= PERM_ALLOC_STR(ARRAY_DIM_SEP
);
4394 result
->val
.str
.len
= strlen(result
->val
.str
.rep
);
4398 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4399 DataValue
*result
, char **errMsg
)
4401 result
->tag
= INT_TAG
;
4402 result
->val
.n
= TextGetMinFontWidth(window
->textArea
, window
->highlightSyntax
);
4406 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4407 DataValue
*result
, char **errMsg
)
4409 result
->tag
= INT_TAG
;
4410 result
->val
.n
= TextGetMaxFontWidth(window
->textArea
, window
->highlightSyntax
);
4414 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4415 DataValue
*result
, char **errMsg
)
4417 result
->tag
= INT_TAG
;
4418 result
->val
.n
= TextFirstVisibleLine(window
->lastFocus
);
4422 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4423 DataValue
*result
, char **errMsg
)
4425 result
->tag
= INT_TAG
;
4426 result
->val
.n
= TextNumVisibleLines(window
->lastFocus
);
4430 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4431 DataValue
*result
, char **errMsg
)
4433 result
->tag
= INT_TAG
;
4434 result
->val
.n
= TextVisibleWidth(window
->lastFocus
);
4438 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4439 DataValue
*result
, char **errMsg
)
4441 result
->tag
= INT_TAG
;
4442 result
->val
.n
= WidgetToPaneIndex(window
, window
->lastFocus
) + 1;
4446 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4447 DataValue
*result
, char **errMsg
)
4449 result
->tag
= INT_TAG
;
4450 result
->val
.n
= window
->nPanes
+ 1;
4454 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4455 DataValue
*result
, char **errMsg
)
4457 result
->tag
= ARRAY_TAG
;
4458 result
->val
.arrayPtr
= NULL
;
4462 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4463 DataValue
*result
, char **errMsg
)
4465 result
->tag
= STRING_TAG
;
4466 AllocNStringCpy(&result
->val
.str
, GetPrefServerName());
4470 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4471 DataValue
*result
, char **errMsg
)
4473 result
->tag
= INT_TAG
;
4474 result
->val
.n
= window
->buffer
->tabDist
;
4478 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4479 DataValue
*result
, char **errMsg
)
4483 XtVaGetValues(window
->textArea
, textNemulateTabs
, &dist
, NULL
);
4484 result
->tag
= INT_TAG
;
4485 result
->val
.n
= dist
;
4489 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4490 DataValue
*result
, char **errMsg
)
4492 result
->tag
= INT_TAG
;
4493 result
->val
.n
= window
->buffer
->useTabs
;
4497 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4498 DataValue
*result
, char **errMsg
)
4500 result
->tag
= INT_TAG
;
4501 result
->val
.n
= window
->fileChanged
;
4505 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4506 DataValue
*result
, char **errMsg
)
4508 char *lmName
= LanguageModeName(window
->languageMode
);
4512 result
->tag
= STRING_TAG
;
4513 AllocNStringCpy(&result
->val
.str
, lmName
);
4518 static int backlightStringMV(WindowInfo *window, DataValue *argList,
4519 int nArgs, DataValue *result, char **errMsg)
4521 char *backlightString = window->backlightCharTypes;
4523 result->tag = STRING_TAG;
4524 if (!backlightString || !window->backlightChars)
4525 backlightString = "";
4526 AllocNStringCpy(&result->val.str, backlightString);
4530 /* -------------------------------------------------------------------------- */
4533 ** Range set macro variables and functions
4535 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4536 DataValue
*result
, char **errMsg
)
4538 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4539 unsigned char *rangesetList
;
4540 char *allocIndexStr
;
4541 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4545 result
->tag
= ARRAY_TAG
;
4546 result
->val
.arrayPtr
= ArrayNew();
4548 if (rangesetTable
== NULL
) {
4552 rangesetList
= RangesetGetList(rangesetTable
);
4553 nRangesets
= strlen((char*)rangesetList
);
4554 for(i
= 0; i
< nRangesets
; i
++) {
4555 element
.tag
= INT_TAG
;
4556 element
.val
.n
= rangesetList
[i
];
4558 sprintf(indexStr
, "%d", nRangesets
- i
- 1);
4559 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4560 if (allocIndexStr
== NULL
)
4561 M_FAILURE("Failed to allocate array key in %s");
4562 strcpy(allocIndexStr
, indexStr
);
4564 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4565 M_FAILURE("Failed to insert array element in %s");
4572 ** Returns the version number of the current macro language implementation.
4573 ** For releases, this is the same number as NEdit's major.minor version
4574 ** number to keep things simple. For developer versions this could really
4577 ** Note that the current way to build $VERSION builds the same value for
4578 ** different point revisions. This is done because the macro interface
4579 ** does not change for the same version.
4581 static int versionMV(WindowInfo
* window
, DataValue
* argList
, int nArgs
,
4582 DataValue
* result
, char** errMsg
)
4584 static unsigned version
= NEDIT_VERSION
* 1000 + NEDIT_REVISION
;
4586 result
->tag
= INT_TAG
;
4587 result
->val
.n
= version
;
4592 ** Built-in macro subroutine to create a new rangeset or rangesets.
4593 ** If called with one argument: $1 is the number of rangesets required and
4594 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4595 ** (or an empty array if the requested number of rangesets are not available).
4596 ** If called with no arguments, returns a single rangeset label (not an array),
4597 ** or an empty string if there are no rangesets available.
4599 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4600 DataValue
*result
, char **errMsg
)
4603 int i
, nRangesetsRequired
;
4605 char indexStr
[TYPE_INT_STR_SIZE(int)], *allocIndexStr
;
4607 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4610 return wrongNArgsErr(errMsg
);
4612 if (rangesetTable
== NULL
) {
4613 window
->buffer
->rangesetTable
= rangesetTable
=
4614 RangesetTableAlloc(window
->buffer
);
4618 label
= RangesetCreate(rangesetTable
);
4620 result
->tag
= INT_TAG
;
4621 result
->val
.n
= label
;
4625 if (!readIntArg(argList
[0], &nRangesetsRequired
, errMsg
))
4628 result
->tag
= ARRAY_TAG
;
4629 result
->val
.arrayPtr
= ArrayNew();
4631 if (nRangesetsRequired
> nRangesetsAvailable(rangesetTable
))
4634 for (i
= 0; i
< nRangesetsRequired
; i
++) {
4635 element
.tag
= INT_TAG
;
4636 element
.val
.n
= RangesetCreate(rangesetTable
);
4638 sprintf(indexStr
, "%d", i
);
4639 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4640 if (!allocIndexStr
) {
4641 *errMsg
= "Array element failed to allocate key: %s";
4644 strcpy(allocIndexStr
, indexStr
);
4645 ArrayInsert(result
, allocIndexStr
, &element
);
4653 ** Built-in macro subroutine for forgetting a range set.
4655 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4656 DataValue
*result
, char **errMsg
)
4658 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4661 char keyString
[TYPE_INT_STR_SIZE(int)];
4662 int deleteLabels
[N_RANGESETS
];
4667 return wrongNArgsErr(errMsg
);
4670 if (argList
[0].tag
== ARRAY_TAG
) {
4671 array
= &argList
[0];
4672 arraySize
= ArraySize(array
);
4674 if (arraySize
> N_RANGESETS
) {
4675 M_FAILURE("Too many elements in array in %s");
4678 for (i
= 0; i
< arraySize
; i
++) {
4679 sprintf(keyString
, "%d", i
);
4681 if (!ArrayGet(array
, keyString
, &element
)) {
4682 M_FAILURE("Invalid key in array in %s");
4685 if (!readIntArg(element
, &label
, errMsg
)
4686 || !RangesetLabelOK(label
)) {
4687 M_FAILURE("Invalid rangeset label in array in %s");
4690 deleteLabels
[i
] = label
;
4693 for (i
= 0; i
< arraySize
; i
++) {
4694 RangesetForget(rangesetTable
, deleteLabels
[i
]);
4697 if (!readIntArg(argList
[0], &label
, errMsg
)
4698 || !RangesetLabelOK(label
)) {
4699 M_FAILURE("Invalid rangeset label in %s");
4702 if(rangesetTable
!= NULL
) {
4703 RangesetForget(rangesetTable
, label
);
4708 result
->tag
= NO_TAG
;
4714 ** Built-in macro subroutine for getting all range sets with a specfic name.
4715 ** Arguments are $1: range set name.
4716 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4718 static int rangesetGetByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4719 DataValue
*result
, char **errMsg
)
4721 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
4724 char *name
, *rangeset_name
;
4725 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4726 unsigned char *rangesetList
;
4727 char *allocIndexStr
;
4728 char indexStr
[TYPE_INT_STR_SIZE(int)] ;
4729 int nRangesets
, i
, insertIndex
= 0;
4733 return wrongNArgsErr(errMsg
);
4736 if (!readStringArg(argList
[0], &name
, stringStorage
[0], errMsg
)) {
4737 M_FAILURE("First parameter is not a name string in %s");
4740 result
->tag
= ARRAY_TAG
;
4741 result
->val
.arrayPtr
= ArrayNew();
4743 if (rangesetTable
== NULL
) {
4747 rangesetList
= RangesetGetList(rangesetTable
);
4748 nRangesets
= strlen((char *)rangesetList
);
4749 for (i
= 0; i
< nRangesets
; ++i
) {
4750 label
= rangesetList
[i
];
4751 rangeset
= RangesetFetch(rangesetTable
, label
);
4753 rangeset_name
= RangesetGetName(rangeset
);
4754 if (strcmp(name
, rangeset_name
? rangeset_name
: "") == 0) {
4755 element
.tag
= INT_TAG
;
4756 element
.val
.n
= label
;
4758 sprintf(indexStr
, "%d", insertIndex
);
4759 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4760 if (allocIndexStr
== NULL
)
4761 M_FAILURE("Failed to allocate array key in %s");
4763 strcpy(allocIndexStr
, indexStr
);
4765 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4766 M_FAILURE("Failed to insert array element in %s");
4777 ** Built-in macro subroutine for adding to a range set. Arguments are $1: range
4778 ** set label (one integer), then either (a) $2: source range set label,
4779 ** (b) $2: int start-range, $3: int end-range, (c) nothing (use selection
4780 ** if any to specify range to add - must not be rectangular). Returns the
4781 ** index of the newly added range (cases b and c), or 0 (case a).
4783 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4784 DataValue
*result
, char **errMsg
)
4786 textBuffer
*buffer
= window
->buffer
;
4787 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4788 Rangeset
*targetRangeset
, *sourceRangeset
;
4789 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
, index
;
4792 if (nArgs
< 1 || nArgs
> 3)
4793 return wrongNArgsErr(errMsg
);
4795 if (!readIntArg(argList
[0], &label
, errMsg
)
4796 || !RangesetLabelOK(label
)) {
4797 M_FAILURE("First parameter is an invalid rangeset label in %s");
4800 if (rangesetTable
== NULL
) {
4801 M_FAILURE("Rangeset does not exist in %s");
4804 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4806 if (targetRangeset
== NULL
) {
4807 M_FAILURE("Rangeset does not exist in %s");
4813 /* pick up current selection in this window */
4814 if (!BufGetSelectionPos(buffer
, &start
, &end
,
4815 &isRect
, &rectStart
, &rectEnd
) || isRect
) {
4816 M_FAILURE("Selection missing or rectangular in call to %s");
4818 if (!RangesetAddBetween(targetRangeset
, start
, end
)) {
4819 M_FAILURE("Failure to add selection in %s");
4824 /* add ranges taken from a second set */
4825 if (!readIntArg(argList
[1], &label
, errMsg
)
4826 || !RangesetLabelOK(label
)) {
4827 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4830 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4831 if (sourceRangeset
== NULL
) {
4832 M_FAILURE("Second rangeset does not exist in %s");
4835 RangesetAdd(targetRangeset
, sourceRangeset
);
4839 /* add a range bounded by the start and end positions in $2, $3 */
4840 if (!readIntArg(argList
[1], &start
, errMsg
)) {
4843 if (!readIntArg(argList
[2], &end
, errMsg
)) {
4847 /* make sure range is in order and fits buffer size */
4848 maxpos
= buffer
->length
;
4849 if (start
< 0) start
= 0;
4850 if (start
> maxpos
) start
= maxpos
;
4851 if (end
< 0) end
= 0;
4852 if (end
> maxpos
) end
= maxpos
;
4853 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4855 if ((start
!= end
) && !RangesetAddBetween(targetRangeset
, start
, end
)) {
4856 M_FAILURE("Failed to add range in %s");
4860 /* (to) which range did we just add? */
4861 if (nArgs
!= 2 && start
>= 0) {
4862 start
= (start
+ end
) / 2; /* "middle" of added range */
4863 index
= 1 + RangesetFindRangeOfPos(targetRangeset
, start
, False
);
4870 result
->tag
= INT_TAG
;
4871 result
->val
.n
= index
;
4877 ** Built-in macro subroutine for removing from a range set. Almost identical to
4878 ** rangesetAddMS() - only changes are from RangesetAdd()/RangesetAddBetween()
4879 ** to RangesetSubtract()/RangesetSubtractBetween(), the handling of an
4880 ** undefined destination range, and that it returns no value.
4882 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4883 DataValue
*result
, char **errMsg
)
4885 textBuffer
*buffer
= window
->buffer
;
4886 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4887 Rangeset
*targetRangeset
, *sourceRangeset
;
4888 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
;
4891 if (nArgs
< 1 || nArgs
> 3) {
4892 return wrongNArgsErr(errMsg
);
4895 if (!readIntArg(argList
[0], &label
, errMsg
)
4896 || !RangesetLabelOK(label
)) {
4897 M_FAILURE("First parameter is an invalid rangeset label in %s");
4900 if (rangesetTable
== NULL
) {
4901 M_FAILURE("Rangeset does not exist in %s");
4904 targetRangeset
= RangesetFetch(rangesetTable
, label
);
4905 if (targetRangeset
== NULL
) {
4906 M_FAILURE("Rangeset does not exist in %s");
4910 /* remove current selection in this window */
4911 if (!BufGetSelectionPos(buffer
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)
4913 M_FAILURE("Selection missing or rectangular in call to %s");
4915 RangesetRemoveBetween(targetRangeset
, start
, end
);
4919 /* remove ranges taken from a second set */
4920 if (!readIntArg(argList
[1], &label
, errMsg
)
4921 || !RangesetLabelOK(label
)) {
4922 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4925 sourceRangeset
= RangesetFetch(rangesetTable
, label
);
4926 if (sourceRangeset
== NULL
) {
4927 M_FAILURE("Second rangeset does not exist in %s");
4929 RangesetRemove(targetRangeset
, sourceRangeset
);
4933 /* remove a range bounded by the start and end positions in $2, $3 */
4934 if (!readIntArg(argList
[1], &start
, errMsg
))
4936 if (!readIntArg(argList
[2], &end
, errMsg
))
4939 /* make sure range is in order and fits buffer size */
4940 maxpos
= buffer
->length
;
4941 if (start
< 0) start
= 0;
4942 if (start
> maxpos
) start
= maxpos
;
4943 if (end
< 0) end
= 0;
4944 if (end
> maxpos
) end
= maxpos
;
4945 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4947 RangesetRemoveBetween(targetRangeset
, start
, end
);
4951 result
->tag
= NO_TAG
;
4957 ** Built-in macro subroutine to invert a range set. Argument is $1: range set
4958 ** label (one alphabetic character). Returns nothing. Fails if range set
4961 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4962 DataValue
*result
, char **errMsg
)
4965 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4970 return wrongNArgsErr(errMsg
);
4972 if (!readIntArg(argList
[0], &label
, errMsg
)
4973 || !RangesetLabelOK(label
)) {
4974 M_FAILURE("First parameter is an invalid rangeset label in %s");
4977 if (rangesetTable
== NULL
) {
4978 M_FAILURE("Rangeset does not exist in %s");
4981 rangeset
= RangesetFetch(rangesetTable
, label
);
4982 if (rangeset
== NULL
) {
4983 M_FAILURE("Rangeset does not exist in %s");
4986 if (RangesetInverse(rangeset
) < 0) {
4987 M_FAILURE("Problem inverting rangeset in %s");
4991 result
->tag
= NO_TAG
;
4997 ** Built-in macro subroutine for finding out info about a rangeset. Takes one
4998 ** argument of a rangeset label. Returns an array with the following keys:
4999 ** defined, count, color, mode.
5001 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5002 DataValue
*result
, char **errMsg
)
5004 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
5005 Rangeset
*rangeset
= NULL
;
5007 char *color
, *name
, *mode
;
5012 return wrongNArgsErr(errMsg
);
5014 if (!readIntArg(argList
[0], &label
, errMsg
)
5015 || !RangesetLabelOK(label
)) {
5016 M_FAILURE("First parameter is an invalid rangeset label in %s");
5019 if (rangesetTable
!= NULL
) {
5020 rangeset
= RangesetFetch(rangesetTable
, label
);
5023 RangesetGetInfo(rangeset
, &defined
, &label
, &count
, &color
, &name
, &mode
);
5026 result
->tag
= ARRAY_TAG
;
5027 result
->val
.arrayPtr
= ArrayNew();
5029 element
.tag
= INT_TAG
;
5030 element
.val
.n
= defined
;
5031 if (!ArrayInsert(result
, PERM_ALLOC_STR("defined"), &element
))
5032 M_FAILURE("Failed to insert array element \"defined\" in %s");
5034 element
.tag
= INT_TAG
;
5035 element
.val
.n
= count
;
5036 if (!ArrayInsert(result
, PERM_ALLOC_STR("count"), &element
))
5037 M_FAILURE("Failed to insert array element \"count\" in %s");
5039 element
.tag
= STRING_TAG
;
5040 if (!AllocNStringCpy(&element
.val
.str
, color
))
5041 M_FAILURE("Failed to allocate array value \"color\" in %s");
5042 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &element
))
5043 M_FAILURE("Failed to insert array element \"color\" in %s");
5045 element
.tag
= STRING_TAG
;
5046 if (!AllocNStringCpy(&element
.val
.str
, name
))
5047 M_FAILURE("Failed to allocate array value \"name\" in %s");
5048 if (!ArrayInsert(result
, PERM_ALLOC_STR("name"), &element
)) {
5049 M_FAILURE("Failed to insert array element \"name\" in %s");
5052 element
.tag
= STRING_TAG
;
5053 if (!AllocNStringCpy(&element
.val
.str
, mode
))
5054 M_FAILURE("Failed to allocate array value \"mode\" in %s");
5055 if (!ArrayInsert(result
, PERM_ALLOC_STR("mode"), &element
))
5056 M_FAILURE("Failed to insert array element \"mode\" in %s");
5062 ** Built-in macro subroutine for finding the extent of a range in a set.
5063 ** If only one parameter is supplied, use the spanning range of all
5064 ** ranges, otherwise select the individual range specified. Returns
5065 ** an array with the keys "start" and "end" and values
5067 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5068 DataValue
*result
, char **errMsg
)
5070 textBuffer
*buffer
= window
->buffer
;
5071 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5073 int start
, end
, dummy
, rangeIndex
, ok
;
5077 if (nArgs
< 1 || nArgs
> 2) {
5078 return wrongNArgsErr(errMsg
);
5081 if (!readIntArg(argList
[0], &label
, errMsg
)
5082 || !RangesetLabelOK(label
)) {
5083 M_FAILURE("First parameter is an invalid rangeset label in %s");
5086 if (rangesetTable
== NULL
) {
5087 M_FAILURE("Rangeset does not exist in %s");
5091 rangeset
= RangesetFetch(rangesetTable
, label
);
5092 if (rangeset
!= NULL
) {
5094 rangeIndex
= RangesetGetNRanges(rangeset
) - 1;
5095 ok
= RangesetFindRangeNo(rangeset
, 0, &start
, &dummy
);
5096 ok
&= RangesetFindRangeNo(rangeset
, rangeIndex
, &dummy
, &end
);
5099 else if (nArgs
== 2) {
5100 if (!readIntArg(argList
[1], &rangeIndex
, errMsg
)) {
5103 ok
= RangesetFindRangeNo(rangeset
, rangeIndex
-1, &start
, &end
);
5108 result
->tag
= ARRAY_TAG
;
5109 result
->val
.arrayPtr
= ArrayNew();
5114 element
.tag
= INT_TAG
;
5115 element
.val
.n
= start
;
5116 if (!ArrayInsert(result
, PERM_ALLOC_STR("start"), &element
))
5117 M_FAILURE("Failed to insert array element \"start\" in %s");
5119 element
.tag
= INT_TAG
;
5120 element
.val
.n
= end
;
5121 if (!ArrayInsert(result
, PERM_ALLOC_STR("end"), &element
))
5122 M_FAILURE("Failed to insert array element \"end\" in %s");
5128 ** Built-in macro subroutine for checking a position against a range. If only
5129 ** one parameter is supplied, the current cursor position is used. Returns
5130 ** false (zero) if not in a range, range index (1-based) if in a range;
5131 ** fails if parameters were bad.
5133 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
5134 int nArgs
, DataValue
*result
, char **errMsg
)
5136 textBuffer
*buffer
= window
->buffer
;
5137 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5139 int pos
, rangeIndex
, maxpos
;
5142 if (nArgs
< 1 || nArgs
> 2) {
5143 return wrongNArgsErr(errMsg
);
5146 if (!readIntArg(argList
[0], &label
, errMsg
)
5147 || !RangesetLabelOK(label
)) {
5148 M_FAILURE("First parameter is an invalid rangeset label in %s");
5151 if (rangesetTable
== NULL
) {
5152 M_FAILURE("Rangeset does not exist in %s");
5155 rangeset
= RangesetFetch(rangesetTable
, label
);
5156 if (rangeset
== NULL
) {
5157 M_FAILURE("Rangeset does not exist in %s");
5161 pos
= TextGetCursorPos(window
->lastFocus
);
5163 else if (nArgs
== 2) {
5164 if (!readIntArg(argList
[1], &pos
, errMsg
))
5168 maxpos
= buffer
->length
;
5169 if (pos
< 0 || pos
> maxpos
) {
5173 rangeIndex
= RangesetFindRangeOfPos(rangeset
, pos
, False
) + 1;
5177 result
->tag
= INT_TAG
;
5178 result
->val
.n
= rangeIndex
;
5183 ** Set the color of a range set's ranges. it is ignored if the color cannot be
5184 ** found/applied. If no color is applied, any current color is removed. Returns
5185 ** true if the rangeset is valid.
5187 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
5188 int nArgs
, DataValue
*result
, char **errMsg
)
5190 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5191 textBuffer
*buffer
= window
->buffer
;
5192 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5198 return wrongNArgsErr(errMsg
);
5201 if (!readIntArg(argList
[0], &label
, errMsg
)
5202 || !RangesetLabelOK(label
)) {
5203 M_FAILURE("First parameter is an invalid rangeset label in %s");
5206 if (rangesetTable
== NULL
) {
5207 M_FAILURE("Rangeset does not exist in %s");
5210 rangeset
= RangesetFetch(rangesetTable
, label
);
5211 if (rangeset
== NULL
) {
5212 M_FAILURE("Rangeset does not exist in %s");
5216 if (rangeset
!= NULL
) {
5217 if (!readStringArg(argList
[1], &color_name
, stringStorage
[0], errMsg
)) {
5218 M_FAILURE("Second parameter is not a color name string in %s");
5222 RangesetAssignColorName(rangeset
, color_name
);
5225 result
->tag
= NO_TAG
;
5230 ** Set the name of a range set's ranges. Returns
5231 ** true if the rangeset is valid.
5233 static int rangesetSetNameMS(WindowInfo
*window
, DataValue
*argList
,
5234 int nArgs
, DataValue
*result
, char **errMsg
)
5236 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5237 textBuffer
*buffer
= window
->buffer
;
5238 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5244 return wrongNArgsErr(errMsg
);
5247 if (!readIntArg(argList
[0], &label
, errMsg
)
5248 || !RangesetLabelOK(label
)) {
5249 M_FAILURE("First parameter is an invalid rangeset label in %s");
5252 if (rangesetTable
== NULL
) {
5253 M_FAILURE("Rangeset does not exist in %s");
5256 rangeset
= RangesetFetch(rangesetTable
, label
);
5257 if (rangeset
== NULL
) {
5258 M_FAILURE("Rangeset does not exist in %s");
5262 if (rangeset
!= NULL
) {
5263 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
)) {
5264 M_FAILURE("Second parameter is not a valid name string in %s");
5268 RangesetAssignName(rangeset
, name
);
5271 result
->tag
= NO_TAG
;
5276 ** Change a range's modification response. Returns true if the rangeset is
5277 ** valid and the response type name is valid.
5279 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
5280 int nArgs
, DataValue
*result
, char **errMsg
)
5282 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5283 textBuffer
*buffer
= window
->buffer
;
5284 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
5286 char *update_fn_name
;
5290 if (nArgs
< 1 || nArgs
> 2) {
5291 return wrongNArgsErr(errMsg
);
5294 if (!readIntArg(argList
[0], &label
, errMsg
)
5295 || !RangesetLabelOK(label
)) {
5296 M_FAILURE("First parameter is an invalid rangeset label in %s");
5299 if (rangesetTable
== NULL
) {
5300 M_FAILURE("Rangeset does not exist in %s");
5303 rangeset
= RangesetFetch(rangesetTable
, label
);
5304 if (rangeset
== NULL
) {
5305 M_FAILURE("Rangeset does not exist in %s");
5308 update_fn_name
= "";
5309 if (rangeset
!= NULL
) {
5311 if (!readStringArg(argList
[1], &update_fn_name
, stringStorage
[0], errMsg
)) {
5312 M_FAILURE("Second parameter is not a string in %s");
5317 ok
= RangesetChangeModifyResponse(rangeset
, update_fn_name
);
5320 M_FAILURE("Second parameter is not a valid mode in %s");
5324 result
->tag
= NO_TAG
;
5328 /* -------------------------------------------------------------------------- */
5332 ** Routines to get details directly from the window.
5336 ** Sets up an array containing information about a style given its name or
5337 ** a buffer position (bufferPos >= 0) and its highlighting pattern code
5339 ** From the name we obtain:
5340 ** ["color"] Foreground color name of style
5341 ** ["background"] Background color name of style if specified
5342 ** ["bold"] '1' if style is bold, '0' otherwise
5343 ** ["italic"] '1' if style is italic, '0' otherwise
5344 ** Given position and pattern code we obtain:
5345 ** ["rgb"] RGB representation of foreground color of style
5346 ** ["back_rgb"] RGB representation of background color of style
5347 ** ["extent"] Forward distance from position over which style applies
5348 ** We only supply the style name if the includeName parameter is set:
5349 ** ["style"] Name of style
5352 static int fillStyleResult(DataValue
*result
, char **errMsg
,
5353 WindowInfo
*window
, char *styleName
, Boolean preallocatedStyleName
,
5354 Boolean includeName
, int patCode
, int bufferPos
)
5357 char colorValue
[20];
5360 /* initialize array */
5361 result
->tag
= ARRAY_TAG
;
5362 result
->val
.arrayPtr
= ArrayNew();
5364 /* the following array entries will be strings */
5365 DV
.tag
= STRING_TAG
;
5368 /* insert style name */
5369 if (preallocatedStyleName
) {
5370 DV
.val
.str
.rep
= styleName
;
5371 DV
.val
.str
.len
= strlen(styleName
);
5374 AllocNStringCpy(&DV
.val
.str
, styleName
);
5376 M_STR_ALLOC_ASSERT(DV
);
5377 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5378 M_ARRAY_INSERT_FAILURE();
5382 /* insert color name */
5383 AllocNStringCpy(&DV
.val
.str
, ColorOfNamedStyle(styleName
));
5384 M_STR_ALLOC_ASSERT(DV
);
5385 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &DV
)) {
5386 M_ARRAY_INSERT_FAILURE();
5389 /* Prepare array element for color value
5390 (only possible if we pass through the dynamic highlight pattern tables
5391 in other words, only if we have a pattern code) */
5393 HighlightColorValueOfCode(window
, patCode
, &r
, &g
, &b
);
5394 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5395 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5396 M_STR_ALLOC_ASSERT(DV
);
5397 if (!ArrayInsert(result
, PERM_ALLOC_STR("rgb"), &DV
)) {
5398 M_ARRAY_INSERT_FAILURE();
5402 /* Prepare array element for background color name */
5403 AllocNStringCpy(&DV
.val
.str
, BgColorOfNamedStyle(styleName
));
5404 M_STR_ALLOC_ASSERT(DV
);
5405 if (!ArrayInsert(result
, PERM_ALLOC_STR("background"), &DV
)) {
5406 M_ARRAY_INSERT_FAILURE();
5409 /* Prepare array element for background color value
5410 (only possible if we pass through the dynamic highlight pattern tables
5411 in other words, only if we have a pattern code) */
5413 GetHighlightBGColorOfCode(window
, patCode
, &r
, &g
, &b
);
5414 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
5415 AllocNStringCpy(&DV
.val
.str
, colorValue
);
5416 M_STR_ALLOC_ASSERT(DV
);
5417 if (!ArrayInsert(result
, PERM_ALLOC_STR("back_rgb"), &DV
)) {
5418 M_ARRAY_INSERT_FAILURE();
5422 /* the following array entries will be integers */
5425 /* Put boldness value in array */
5426 DV
.val
.n
= FontOfNamedStyleIsBold(styleName
);
5427 if (!ArrayInsert(result
, PERM_ALLOC_STR("bold"), &DV
)) {
5428 M_ARRAY_INSERT_FAILURE();
5431 /* Put italicity value in array */
5432 DV
.val
.n
= FontOfNamedStyleIsItalic(styleName
);
5433 if (!ArrayInsert(result
, PERM_ALLOC_STR("italic"), &DV
)) {
5434 M_ARRAY_INSERT_FAILURE();
5437 if (bufferPos
>= 0) {
5439 const char *styleNameNotUsed
= NULL
;
5440 DV
.val
.n
= StyleLengthOfCodeFromPos(window
, bufferPos
, &styleNameNotUsed
);
5441 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5442 M_ARRAY_INSERT_FAILURE();
5449 ** Returns an array containing information about the style of name $1
5450 ** ["color"] Foreground color name of style
5451 ** ["background"] Background color name of style if specified
5452 ** ["bold"] '1' if style is bold, '0' otherwise
5453 ** ["italic"] '1' if style is italic, '0' otherwise
5456 static int getStyleByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5457 DataValue
*result
, char **errMsg
)
5459 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5462 /* Validate number of arguments */
5464 return wrongNArgsErr(errMsg
);
5467 /* Prepare result */
5468 result
->tag
= ARRAY_TAG
;
5469 result
->val
.arrayPtr
= NULL
;
5471 if (!readStringArg(argList
[0], &styleName
, stringStorage
[0], errMsg
)) {
5472 M_FAILURE("First parameter is not a string in %s");
5475 if (!NamedStyleExists(styleName
)) {
5476 /* if the given name is invalid we just return an empty array. */
5480 return fillStyleResult(result
, errMsg
, window
,
5481 styleName
, (argList
[0].tag
== STRING_TAG
), False
, 0, -1);
5485 ** Returns an array containing information about the style of position $1
5486 ** ["style"] Name of style
5487 ** ["color"] Foreground color name of style
5488 ** ["background"] Background color name of style if specified
5489 ** ["bold"] '1' if style is bold, '0' otherwise
5490 ** ["italic"] '1' if style is italic, '0' otherwise
5491 ** ["rgb"] RGB representation of foreground color of style
5492 ** ["back_rgb"] RGB representation of background color of style
5493 ** ["extent"] Forward distance from position over which style applies
5496 static int getStyleAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5497 DataValue
*result
, char **errMsg
)
5501 textBuffer
*buf
= window
->buffer
;
5503 /* Validate number of arguments */
5505 return wrongNArgsErr(errMsg
);
5508 /* Prepare result */
5509 result
->tag
= ARRAY_TAG
;
5510 result
->val
.arrayPtr
= NULL
;
5512 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5516 /* Verify sane buffer position */
5517 if ((bufferPos
< 0) || (bufferPos
>= buf
->length
)) {
5518 /* If the position is not legal, we cannot guess anything about
5519 the style, so we return an empty array. */
5523 /* Determine pattern code */
5524 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5526 /* if there is no pattern we just return an empty array. */
5530 return fillStyleResult(result
, errMsg
, window
,
5531 HighlightStyleOfCode(window
, patCode
), False
, True
, patCode
, bufferPos
);
5535 ** Sets up an array containing information about a pattern given its name or
5536 ** a buffer position (bufferPos >= 0).
5537 ** From the name we obtain:
5538 ** ["style"] Name of style
5539 ** ["extent"] Forward distance from position over which style applies
5540 ** We only supply the pattern name if the includeName parameter is set:
5541 ** ["pattern"] Name of pattern
5544 static int fillPatternResult(DataValue
*result
, char **errMsg
,
5545 WindowInfo
*window
, char *patternName
, Boolean preallocatedPatternName
,
5546 Boolean includeName
, char* styleName
, int bufferPos
)
5550 /* initialize array */
5551 result
->tag
= ARRAY_TAG
;
5552 result
->val
.arrayPtr
= ArrayNew();
5554 /* the following array entries will be strings */
5555 DV
.tag
= STRING_TAG
;
5558 /* insert pattern name */
5559 if (preallocatedPatternName
) {
5560 DV
.val
.str
.rep
= patternName
;
5561 DV
.val
.str
.len
= strlen(patternName
);
5564 AllocNStringCpy(&DV
.val
.str
, patternName
);
5566 M_STR_ALLOC_ASSERT(DV
);
5567 if (!ArrayInsert(result
, PERM_ALLOC_STR("pattern"), &DV
)) {
5568 M_ARRAY_INSERT_FAILURE();
5572 /* insert style name */
5573 AllocNStringCpy(&DV
.val
.str
, styleName
);
5574 M_STR_ALLOC_ASSERT(DV
);
5575 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
5576 M_ARRAY_INSERT_FAILURE();
5579 /* the following array entries will be integers */
5582 if (bufferPos
>= 0) {
5585 DV
.val
.n
= HighlightLengthOfCodeFromPos(window
, bufferPos
, &checkCode
);
5586 if (!ArrayInsert(result
, PERM_ALLOC_STR("extent"), &DV
)) {
5587 M_ARRAY_INSERT_FAILURE();
5595 ** Returns an array containing information about a highlighting pattern. The
5596 ** single parameter contains the pattern name for which this information is
5598 ** The returned array looks like this:
5599 ** ["style"] Name of style
5601 static int getPatternByNameMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5602 DataValue
*result
, char **errMsg
)
5604 char stringStorage
[1][TYPE_INT_STR_SIZE(int)];
5605 char *patternName
= NULL
;
5606 highlightPattern
*pattern
;
5608 /* Begin of building the result. */
5609 result
->tag
= ARRAY_TAG
;
5610 result
->val
.arrayPtr
= NULL
;
5612 /* Validate number of arguments */
5614 return wrongNArgsErr(errMsg
);
5617 if (!readStringArg(argList
[0], &patternName
, stringStorage
[0], errMsg
)) {
5618 M_FAILURE("First parameter is not a string in %s");
5621 pattern
= FindPatternOfWindow(window
, patternName
);
5622 if (pattern
== NULL
) {
5623 /* The pattern's name is unknown. */
5627 return fillPatternResult(result
, errMsg
, window
, patternName
,
5628 (argList
[0].tag
== STRING_TAG
), False
, pattern
->style
, -1);
5632 ** Returns an array containing information about the highlighting pattern
5633 ** applied at a given position, passed as the only parameter.
5634 ** The returned array looks like this:
5635 ** ["pattern"] Name of pattern
5636 ** ["style"] Name of style
5637 ** ["extent"] Distance from position over which this pattern applies
5639 static int getPatternAtPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
5640 DataValue
*result
, char **errMsg
)
5643 textBuffer
*buffer
= window
->buffer
;
5646 /* Begin of building the result. */
5647 result
->tag
= ARRAY_TAG
;
5648 result
->val
.arrayPtr
= NULL
;
5650 /* Validate number of arguments */
5652 return wrongNArgsErr(errMsg
);
5655 /* The most straightforward case: Get a pattern, style and extent
5656 for a buffer position. */
5657 if (!readIntArg(argList
[0], &bufferPos
, errMsg
)) {
5661 /* Verify sane buffer position
5662 * You would expect that buffer->length would be among the sane
5663 * positions, but we have n characters and n+1 buffer positions. */
5664 if ((bufferPos
< 0) || (bufferPos
>= buffer
->length
)) {
5665 /* If the position is not legal, we cannot guess anything about
5666 the highlighting pattern, so we return an empty array. */
5670 /* Determine the highlighting pattern used */
5671 patCode
= HighlightCodeOfPos(window
, bufferPos
);
5673 /* if there is no highlighting pattern we just return an empty array. */
5677 return fillPatternResult(result
, errMsg
, window
,
5678 HighlightNameOfCode(window
, patCode
), False
, True
,
5679 HighlightStyleOfCode(window
, patCode
), bufferPos
);
5682 static int wrongNArgsErr(char **errMsg
)
5684 *errMsg
= "Wrong number of arguments to function %s";
5688 static int tooFewArgsErr(char **errMsg
)
5690 *errMsg
= "Too few arguments to function %s";
5695 ** strCaseCmp compares its arguments and returns 0 if the two strings
5696 ** are equal IGNORING case differences. Otherwise returns 1 or -1
5697 ** depending on relative comparison.
5699 static int strCaseCmp(char *str1
, char *str2
)
5703 for (c1
= str1
, c2
= str2
;
5704 (*c1
!= '\0' && *c2
!= '\0')
5705 && toupper((unsigned char)*c1
) == toupper((unsigned char)*c2
);
5710 if (((unsigned char)toupper((unsigned char)*c1
))
5711 > ((unsigned char)toupper((unsigned char)*c2
)))
5714 } else if (((unsigned char)toupper((unsigned char)*c1
))
5715 < ((unsigned char)toupper((unsigned char)*c2
)))
5725 ** Get an integer value from a tagged DataValue structure. Return True
5726 ** if conversion succeeded, and store result in *result, otherwise
5727 ** return False with an error message in *errMsg.
5729 static int readIntArg(DataValue dv
, int *result
, char **errMsg
)
5733 if (dv
.tag
== INT_TAG
) {
5736 } else if (dv
.tag
== STRING_TAG
) {
5737 for (c
=dv
.val
.str
.rep
; *c
!= '\0'; c
++) {
5738 if (!(isdigit((unsigned char)*c
) || *c
== ' ' || *c
== '\t')) {
5742 sscanf(dv
.val
.str
.rep
, "%d", result
);
5747 *errMsg
= "%s called with non-integer argument";
5752 ** Get an string value from a tagged DataValue structure. Return True
5753 ** if conversion succeeded, and store result in *result, otherwise
5754 ** return False with an error message in *errMsg. If an integer value
5755 ** is converted, write the string in the space provided by "stringStorage",
5756 ** which must be large enough to handle ints of the maximum size.
5758 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
5761 if (dv
.tag
== STRING_TAG
) {
5762 *result
= dv
.val
.str
.rep
;
5764 } else if (dv
.tag
== INT_TAG
) {
5765 sprintf(stringStorage
, "%d", dv
.val
.n
);
5766 *result
= stringStorage
;
5769 *errMsg
= "%s called with unknown object";