1 static const char CVSID
[] = "$Id: macro.c,v 1.73 2003/07/27 10:03:22 edg Exp $";
2 /*******************************************************************************
4 * macro.c -- Macro file processing, learn/replay, and built-in macro *
7 * Copyright (C) 1999 Mark Edel *
9 * This is free software; you can redistribute it and/or modify it under the *
10 * terms of the GNU General Public License as published by the Free Software *
11 * Foundation; either version 2 of the License, or (at your option) any later *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
39 #include "preferences.h"
40 #include "interpret.h"
45 #include "smartIndent.h"
47 #include "selection.h"
51 #include "../util/DialogF.h"
52 #include "../util/misc.h"
53 #include "../util/fileUtils.h"
54 #include "../util/utils.h"
55 #include "highlight.h"
56 #include "highlightData.h"
65 #include "../util/VMSparam.h"
70 #include <sys/types.h>
73 #include <sys/param.h>
78 #include <X11/Intrinsic.h>
79 #include <X11/keysym.h>
81 #include <Xm/CutPaste.h>
83 #include <Xm/RowColumn.h>
84 #include <Xm/LabelG.h>
86 #include <Xm/ToggleB.h>
87 #include <Xm/DialogS.h>
88 #include <Xm/MessageB.h>
89 #include <Xm/SelectioB.h>
92 #include <Xm/Separator.h>
98 /* Maximum number of actions in a macro and args in
99 an action (to simplify the reader) */
100 #define MAX_MACRO_ACTIONS 1024
101 #define MAX_ACTION_ARGS 40
103 /* How long to wait (msec) before putting up Macro Command banner */
104 #define BANNER_WAIT_TIME 6000
106 /* The following definitions cause an exit from the macro with a message */
107 /* added if (1) to remove compiler warnings on solaris */
108 #define M_FAILURE(s) do { *errMsg = s; if (1) return False; } while (0)
109 #define M_STR_ALLOC_ASSERT(xDV) do { if (xDV.tag == STRING_TAG && !xDV.val.str) { *errMsg = "Failed to allocate value: %s"; return(False); } } while (0)
110 #define M_ARRAY_INSERT_FAILURE() M_FAILURE("array element failed to insert: %s")
112 /* Data attached to window during shell command execution with
113 information for controling and communicating with the process */
115 XtIntervalId bannerTimeoutID
;
116 XtWorkProcId continueWorkProcID
;
118 char closeOnCompletion
;
120 RestartData
*context
;
124 /* Widgets and global data for Repeat dialog */
126 WindowInfo
*forWindow
;
128 Widget shell
, repeatText
, lastCmdToggle
;
129 Widget inSelToggle
, toEndToggle
;
132 static void cancelLearn(void);
133 static void runMacro(WindowInfo
*window
, Program
*prog
);
134 static void finishMacroCmdExecution(WindowInfo
*window
);
135 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
);
136 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
137 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
);
138 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
);
139 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
140 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
141 XEvent
*event
, String
*params
, Cardinal
*numParams
);
142 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
143 XEvent
*event
, String
*params
, Cardinal
*numParams
);
144 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
145 String
*params
, Cardinal numParams
);
146 static int isMouseAction(const char *action
);
147 static int isRedundantAction(const char *action
);
148 static int isIgnoredAction(const char *action
);
149 static int readCheckMacroString(Widget dialogParent
, char *string
,
150 WindowInfo
*runWindow
, const char *errIn
, char **errPos
);
151 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
);
152 static Boolean
continueWorkProc(XtPointer clientData
);
153 static int escapeStringChars(char *fromString
, char *toString
);
154 static int escapedStringLength(char *string
);
155 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
156 DataValue
*result
, char **errMsg
);
157 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
158 DataValue
*result
, char **errMsg
);
159 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
160 DataValue
*result
, char **errMsg
);
161 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
162 DataValue
*result
, char **errMsg
);
163 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
164 DataValue
*result
, char **errMsg
);
165 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
166 DataValue
*result
, char **errMsg
);
167 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
168 DataValue
*result
, char **errMsg
);
169 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
170 DataValue
*result
, char **errMsg
);
171 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
172 DataValue
*result
, char **errMsg
);
173 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
174 DataValue
*result
, char **errMsg
);
175 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
176 DataValue
*result
, char **errMsg
);
177 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
178 DataValue
*result
, char **errMsg
);
179 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
180 DataValue
*result
, char **errMsg
);
181 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
182 DataValue
*result
, char **errMsg
);
183 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
184 DataValue
*result
, char **errMsg
);
185 static int writeOrAppendFile(int append
, WindowInfo
*window
,
186 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
);
187 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
188 DataValue
*result
, char **errMsg
);
189 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
190 DataValue
*result
, char **errMsg
);
191 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
192 DataValue
*result
, char **errMsg
);
193 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
194 DataValue
*result
, char **errMsg
);
195 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
196 DataValue
*result
, char **errMsg
);
197 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
198 DataValue
*result
, char **errMsg
);
199 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
200 DataValue
*result
, char **errMsg
);
201 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
202 DataValue
*result
, char **errMsg
);
203 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
204 DataValue
*result
, char **errMsg
);
205 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
206 DataValue
*result
, char **errMsg
);
207 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
208 DataValue
*result
, char **errMsg
);
209 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
210 DataValue
*result
, char **errMsg
);
211 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
212 DataValue
*result
, char **errMsg
);
213 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
214 DataValue
*result
, char **errMsg
);
215 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
216 DataValue
*result
, char **errMsg
);
217 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
);
218 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
);
219 #ifdef LESSTIF_VERSION
220 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
222 #endif /* LESSTIF_VERSION */
223 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
224 DataValue
*result
, char **errMsg
);
225 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
227 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
229 #ifdef LESSTIF_VERSION
230 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
232 #endif /* LESSTIF_VERSION */
233 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
234 DataValue
*result
, char **errMsg
);
235 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
236 DataValue
*result
, char **errMsg
);
238 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
239 DataValue
*result
, char **errMsg
);
240 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
242 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
245 #ifdef LESSTIF_VERSION
246 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
248 #endif /* LESSTIF_VERSION */
249 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
250 DataValue
*result
, char **errMsg
);
251 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
252 DataValue
*result
, char **errMsg
);
254 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
255 int nArgs, DataValue *result, char **errMsg);
257 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
258 DataValue
*result
, char **errMsg
);
259 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
260 DataValue
*result
, char **errMsg
);
261 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
262 DataValue
*result
, char **errMsg
);
263 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
264 DataValue
*result
, char **errMsg
);
265 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
266 DataValue
*result
, char **errMsg
);
267 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
268 DataValue
*result
, char **errMsg
);
269 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
270 DataValue
*result
, char **errMsg
);
271 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
272 DataValue
*result
, char **errMsg
);
273 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
274 DataValue
*result
, char **errMsg
);
275 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
276 DataValue
*result
, char **errMsg
);
277 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
278 DataValue
*result
, char **errMsg
);
279 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
280 DataValue
*result
, char **errMsg
);
281 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
282 DataValue
*result
, char **errMsg
);
283 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
284 DataValue
*result
, char **errMsg
);
285 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
286 DataValue
*result
, char **errMsg
);
287 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
288 DataValue
*result
, char **errMsg
);
289 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
290 DataValue
*result
, char **errMsg
);
291 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
292 DataValue
*result
, char **errMsg
);
293 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
294 DataValue
*result
, char **errMsg
);
295 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
296 DataValue
*result
, char **errMsg
);
297 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
298 DataValue
*result
, char **errMsg
);
299 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
300 DataValue
*result
, char **errMsg
);
301 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
302 DataValue
*result
, char **errMsg
);
303 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
304 DataValue
*result
, char **errMsg
);
305 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
306 DataValue
*result
, char **errMsg
);
307 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
308 DataValue
*result
, char **errMsg
);
309 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
310 DataValue
*result
, char **errMsg
);
311 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
312 DataValue
*result
, char **errMsg
);
313 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
314 DataValue
*result
, char **errMsg
);
315 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
316 DataValue
*result
, char **errMsg
);
317 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
318 DataValue
*result
, char **errMsg
);
319 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
320 DataValue
*result
, char **errMsg
);
321 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
322 DataValue
*result
, char **errMsg
);
323 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
324 DataValue
*result
, char **errMsg
);
325 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
326 DataValue
*result
, char **errMsg
);
327 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
328 DataValue
*result
, char **errMsg
);
329 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
330 DataValue
*result
, char **errMsg
);
331 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
332 DataValue
*result
, char **errMsg
);
333 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
334 DataValue
*result
, char **errMsg
);
335 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
336 DataValue
*result
, char **errMsg
);
337 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
338 DataValue
*result
, char **errMsg
);
339 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
340 DataValue
*result
, char **errMsg
);
341 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
342 DataValue
*result
, char **errMsg
);
343 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
344 DataValue
*result
, char **errMsg
);
345 static int readSearchArgs(DataValue
*argList
, int nArgs
, int*searchDirection
,
346 int *searchType
, int *wrap
, char **errMsg
);
347 static int wrongNArgsErr(char **errMsg
);
348 static int tooFewArgsErr(char **errMsg
);
349 static int strCaseCmp(char *str1
, char *str2
);
350 static int readIntArg(DataValue dv
, int *result
, char **errMsg
);
351 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
354 static int backlightStringMV(WindowInfo *window, DataValue *argList,
355 int nArgs, DataValue *result, char **errMsg);
357 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
,
358 int nArgs
, DataValue
*result
, char **errMsg
);
359 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
360 DataValue
*result
, char **errMsg
);
361 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
362 DataValue
*result
, char **errMsg
);
363 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
364 DataValue
*result
, char **errMsg
);
365 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
366 DataValue
*result
, char **errMsg
);
367 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
368 DataValue
*result
, char **errMsg
);
369 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
370 DataValue
*result
, char **errMsg
);
371 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
372 DataValue
*result
, char **errMsg
);
373 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
374 int nArgs
, DataValue
*result
, char **errMsg
);
375 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
376 int nArgs
, DataValue
*result
, char **errMsg
);
377 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
378 int nArgs
, DataValue
*result
, char **errMsg
);
380 static int getPatternMS(WindowInfo
*window
, DataValue
*argList
,
381 int nArgs
, DataValue
*result
, char **errMsg
);
382 static int getStyleMS(WindowInfo
*window
, DataValue
*argList
,
383 int nArgs
, DataValue
*result
, char **errMsg
);
385 /* Built-in subroutines and variables for the macro language */
386 static BuiltInSubr MacroSubrs
[] = {lengthMS
, getRangeMS
, tPrintMS
,
387 dialogMS
, stringDialogMS
, replaceRangeMS
, replaceSelectionMS
,
388 setCursorPosMS
, getCharacterMS
, minMS
, maxMS
, searchMS
,
389 searchStringMS
, substringMS
, replaceSubstringMS
, readFileMS
,
390 writeFileMS
, appendFileMS
, beepMS
, getSelectionMS
, validNumberMS
,
391 replaceInStringMS
, selectMS
, selectRectangleMS
, focusWindowMS
,
392 shellCmdMS
, stringToClipboardMS
, clipboardToStringMS
, toupperMS
,
393 tolowerMS
, listDialogMS
, getenvMS
,
394 stringCompareMS
, splitMS
, calltipMS
, killCalltipMS
,
395 /* DISABLED for 5.4 setBacklightStringMS,*/
396 rangesetCreateMS
, rangesetDestroyMS
,
397 rangesetAddMS
, rangesetSubtractMS
, rangesetInvertMS
,
398 rangesetInfoMS
, rangesetRangeMS
, rangesetIncludesPosMS
,
399 rangesetSetColorMS
, rangesetSetModeMS
,
400 getPatternMS
, getStyleMS
402 #define N_MACRO_SUBRS (sizeof MacroSubrs/sizeof *MacroSubrs)
403 static const char *MacroSubrNames
[N_MACRO_SUBRS
] = {"length", "get_range", "t_print",
404 "dialog", "string_dialog", "replace_range", "replace_selection",
405 "set_cursor_pos", "get_character", "min", "max", "search",
406 "search_string", "substring", "replace_substring", "read_file",
407 "write_file", "append_file", "beep", "get_selection", "valid_number",
408 "replace_in_string", "select", "select_rectangle", "focus_window",
409 "shell_command", "string_to_clipboard", "clipboard_to_string",
410 "toupper", "tolower", "list_dialog", "getenv",
411 "string_compare", "split", "calltip", "kill_calltip",
412 /* DISABLED for 5.4 "set_backlight_string", */
413 "rangeset_create", "rangeset_destroy",
414 "rangeset_add", "rangeset_subtract", "rangeset_invert",
415 "rangeset_info", "rangeset_range", "rangeset_includes",
416 "rangeset_set_color", "rangeset_set_mode",
417 "get_pattern", "get_style"
419 static BuiltInSubr SpecialVars
[] = {cursorMV
, lineMV
, columnMV
,
420 fileNameMV
, filePathMV
, lengthMV
, selectionStartMV
, selectionEndMV
,
421 selectionLeftMV
, selectionRightMV
, wrapMarginMV
, tabDistMV
,
422 emTabDistMV
, useTabsMV
, languageModeMV
, modifiedMV
,
423 statisticsLineMV
, incSearchLineMV
, showLineNumbersMV
,
424 autoIndentMV
, wrapTextMV
, highlightSyntaxMV
,
425 makeBackupCopyMV
, incBackupMV
, showMatchingMV
,
426 overTypeModeMV
, readOnlyMV
, lockedMV
, fileFormatMV
,
427 fontNameMV
, fontNameItalicMV
,
428 fontNameBoldMV
, fontNameBoldItalicMV
, subscriptSepMV
,
429 minFontWidthMV
, maxFontWidthMV
, topLineMV
, numDisplayLinesMV
,
430 displayWidthMV
, activePaneMV
, nPanesMV
, emptyArrayMV
,
431 serverNameMV
, calltipIDMV
,
432 /* DISABLED for 5.4 backlightStringMV, */
435 #define N_SPECIAL_VARS (sizeof SpecialVars/sizeof *SpecialVars)
436 static const char *SpecialVarNames
[N_SPECIAL_VARS
] = {"$cursor", "$line", "$column",
437 "$file_name", "$file_path", "$text_length", "$selection_start",
438 "$selection_end", "$selection_left", "$selection_right",
439 "$wrap_margin", "$tab_dist", "$em_tab_dist", "$use_tabs",
440 "$language_mode", "$modified",
441 "$statistics_line", "$incremental_search_line", "$show_line_numbers",
442 "$auto_indent", "$wrap_text", "$highlight_syntax",
443 "$make_backup_copy", "$incremental_backup", "$show_matching",
444 "$overtype_mode", "$read_only", "$locked", "$file_format",
445 "$font_name", "$font_name_italic",
446 "$font_name_bold", "$font_name_bold_italic", "$sub_sep",
447 "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
448 "$display_width", "$active_pane", "$n_panes", "$empty_array",
449 "$server_name", "$calltip_ID",
450 /* DISABLED for 5.4 "$backlight_string", */
454 /* Global symbols for returning values from built-in functions */
455 #define N_RETURN_GLOBALS 5
456 enum retGlobalSyms
{STRING_DIALOG_BUTTON
, SEARCH_END
, READ_STATUS
,
457 SHELL_CMD_STATUS
, LIST_DIALOG_BUTTON
};
458 static const char *ReturnGlobalNames
[N_RETURN_GLOBALS
] = {"$string_dialog_button",
459 "$search_end", "$read_status", "$shell_cmd_status",
460 "$list_dialog_button"};
461 static Symbol
*ReturnGlobals
[N_RETURN_GLOBALS
];
463 /* List of actions not useful when learning a macro sequence (also see below) */
464 static char* IgnoredActions
[] = {"focusIn", "focusOut"};
466 /* List of actions intended to be attached to mouse buttons, which the user
467 must be warned can't be recorded in a learn/replay sequence */
468 static const char* MouseActions
[] = {"grab_focus", "extend_adjust", "extend_start",
469 "extend_end", "secondary_or_drag_adjust", "secondary_adjust",
470 "secondary_or_drag_start", "secondary_start", "move_destination",
471 "move_to", "move_to_or_end_drag", "copy_to", "copy_to_or_end_drag",
472 "exchange", "process_bdrag", "mouse_pan"};
474 /* List of actions to not record because they
475 generate further actions, more suitable for recording */
476 static const char* RedundantActions
[] = {"open_dialog", "save_as_dialog",
477 "revert_to_saved_dialog", "include_file_dialog", "load_macro_file_dialog",
478 "load_tags_file_dialog", "find_dialog", "replace_dialog",
479 "goto_line_number_dialog", "mark_dialog", "goto_mark_dialog",
480 "control_code_dialog", "filter_selection_dialog", "execute_command_dialog",
481 "repeat_dialog", "start_incremental_find"};
483 /* The last command executed (used by the Repeat command) */
484 static char *LastCommand
= NULL
;
486 /* The current macro to execute on Replay command */
487 static char *ReplayMacro
= NULL
;
489 /* Buffer where macro commands are recorded in Learn mode */
490 static textBuffer
*MacroRecordBuf
= NULL
;
492 /* Action Hook id for recording actions for Learn mode */
493 static XtActionHookId MacroRecordActionHook
= 0;
495 /* Window where macro recording is taking place */
496 static WindowInfo
*MacroRecordWindow
= NULL
;
498 /* Arrays for translating escape characters in escapeStringChars */
499 static char ReplaceChars
[] = "\\\"ntbrfav";
500 static char EscapeChars
[] = "\\\"\n\t\b\r\f\a\v";
503 ** Install built-in macro subroutines and special variables for accessing
504 ** editor information
506 void RegisterMacroSubroutines(void)
508 static DataValue subrPtr
= {NO_TAG
, {0}}, noValue
= {NO_TAG
, {0}};
511 /* Install symbols for built-in routines and variables, with pointers
512 to the appropriate c routines to do the work */
513 for (i
=0; i
<N_MACRO_SUBRS
; i
++) {
514 subrPtr
.val
.subr
= MacroSubrs
[i
];
515 InstallSymbol(MacroSubrNames
[i
], C_FUNCTION_SYM
, subrPtr
);
517 for (i
=0; i
<N_SPECIAL_VARS
; i
++) {
518 subrPtr
.val
.subr
= SpecialVars
[i
];
519 InstallSymbol(SpecialVarNames
[i
], PROC_VALUE_SYM
, subrPtr
);
522 /* Define global variables used for return values, remember their
523 locations so they can be set without a LookupSymbol call */
524 for (i
=0; i
<N_RETURN_GLOBALS
; i
++)
525 ReturnGlobals
[i
] = InstallSymbol(ReturnGlobalNames
[i
], GLOBAL_SYM
,
529 #define MAX_LEARN_MSG_LEN ((2 * MAX_ACCEL_LEN) + 60)
530 void BeginLearn(WindowInfo
*window
)
538 char message
[MAX_LEARN_MSG_LEN
];
540 /* If we're already in learn mode, return */
541 if (MacroRecordActionHook
!= 0)
544 /* dim the inappropriate menus and items, and undim finish and cancel */
545 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
546 XtSetSensitive(win
->learnItem
, False
);
547 XtSetSensitive(window
->finishLearnItem
, True
);
548 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
549 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
551 XtSetSensitive(window
->cancelMacroItem
, True
);
553 /* Mark the window where learn mode is happening */
554 MacroRecordWindow
= window
;
556 /* Allocate a text buffer for accumulating the macro strings */
557 MacroRecordBuf
= BufCreate();
559 /* Add the action hook for recording the actions */
560 MacroRecordActionHook
=
561 XtAppAddActionHook(XtWidgetToApplicationContext(window
->shell
),
562 learnActionHook
, window
);
564 /* Extract accelerator texts from menu PushButtons */
565 XtVaGetValues(window
->finishLearnItem
, XmNacceleratorText
, &xmFinish
, NULL
);
566 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
568 /* Translate Motif strings to char* */
569 cFinish
= GetXmStringText(xmFinish
);
570 cCancel
= GetXmStringText(xmCancel
);
572 /* Free Motif Strings */
573 XmStringFree(xmFinish
);
574 XmStringFree(xmCancel
);
577 if (cFinish
[0] == '\0') {
578 if (cCancel
[0] == '\0') {
579 strncpy(message
, "Learn Mode -- Use menu to finish or cancel",
581 message
[MAX_LEARN_MSG_LEN
- 1] = '\0';
585 "Learn Mode -- Use menu to finish, press %s to cancel",
590 if (cCancel
[0] == '\0') {
592 "Learn Mode -- Press %s to finish, use menu to cancel",
598 "Learn Mode -- Press %s to finish, %s to cancel",
608 /* Put up the learn-mode banner */
609 SetModeMessage(window
, message
);
612 void AddLastCommandActionHook(XtAppContext context
)
614 XtAppAddActionHook(context
, lastActionHook
, NULL
);
617 void FinishLearn(void)
621 /* If we're not in learn mode, return */
622 if (MacroRecordActionHook
== 0)
625 /* Remove the action hook */
626 XtRemoveActionHook(MacroRecordActionHook
);
627 MacroRecordActionHook
= 0;
629 /* Free the old learn/replay sequence */
630 if (ReplayMacro
!= NULL
)
633 /* Store the finished action for the replay menu item */
634 ReplayMacro
= BufGetAll(MacroRecordBuf
);
636 /* Free the buffer used to accumulate the macro sequence */
637 BufFree(MacroRecordBuf
);
639 /* Undim the menu items dimmed during learn */
640 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
641 XtSetSensitive(win
->learnItem
, True
);
642 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
643 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
645 /* Undim the replay and paste-macro buttons */
646 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
647 XtSetSensitive(win
->replayItem
, True
);
648 DimPasteReplayBtns(True
);
650 /* Clear learn-mode banner */
651 ClearModeMessage(MacroRecordWindow
);
655 ** Cancel Learn mode, or macro execution (they're bound to the same menu item)
657 void CancelMacroOrLearn(WindowInfo
*window
)
659 if (MacroRecordActionHook
!= 0)
661 else if (window
->macroCmdData
!= NULL
)
662 AbortMacroCommand(window
);
665 static void cancelLearn(void)
669 /* If we're not in learn mode, return */
670 if (MacroRecordActionHook
== 0)
673 /* Remove the action hook */
674 XtRemoveActionHook(MacroRecordActionHook
);
675 MacroRecordActionHook
= 0;
677 /* Free the macro under construction */
678 BufFree(MacroRecordBuf
);
680 /* Undim the menu items dimmed during learn */
681 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
682 XtSetSensitive(win
->learnItem
, True
);
683 XtSetSensitive(MacroRecordWindow
->finishLearnItem
, False
);
684 XtSetSensitive(MacroRecordWindow
->cancelMacroItem
, False
);
686 /* Clear learn-mode banner */
687 ClearModeMessage(MacroRecordWindow
);
691 ** Execute the learn/replay sequence stored in "window"
693 void Replay(WindowInfo
*window
)
696 char *errMsg
, *stoppedAt
;
698 /* Verify that a replay macro exists and it's not empty and that */
699 /* we're not already running a macro */
700 if (ReplayMacro
!= NULL
&&
701 ReplayMacro
[0] != 0 &&
702 window
->macroCmdData
== NULL
) {
703 /* Parse the replay macro (it's stored in text form) and compile it into
704 an executable program "prog" */
705 prog
= ParseMacro(ReplayMacro
, &errMsg
, &stoppedAt
);
708 "NEdit internal error, learn/replay macro syntax error: %s\n",
713 /* run the executable program */
714 runMacro(window
, prog
);
719 ** Read the initial NEdit macro file if one exists.
721 void ReadMacroInitFile(WindowInfo
*window
)
723 const char* autoloadName
= GetRCFileName(AUTOLOAD_NM
);
724 static int initFileLoaded
= False
;
726 /* GetRCFileName() might return NULL if an error occurs during
727 creation of the preference file directory. */
728 if (autoloadName
!= NULL
&& !initFileLoaded
)
730 ReadMacroFile(window
, autoloadName
, False
);
731 initFileLoaded
= True
;
736 ** Read an NEdit macro file. Extends the syntax of the macro parser with
737 ** define keyword, and allows intermixing of defines with immediate actions.
739 int ReadMacroFile(WindowInfo
*window
, const char *fileName
, int warnNotExist
)
744 fileString
= ReadAnyTextFile(fileName
);
745 if (fileString
== NULL
){
746 if (errno
!= ENOENT
|| warnNotExist
)
748 DialogF(DF_ERR
, window
->shell
, 1, "Read Macro",
749 "Error reading macro file %s: %s", "dismiss", fileName
,
751 strerror(errno
, vaxc$errno
));
759 /* Parse fileString */
760 result
= readCheckMacroString(window
->shell
, fileString
, window
, fileName
,
767 ** Parse and execute a macro string including macro definitions. Report
768 ** parsing errors in a dialog posted over window->shell.
770 int ReadMacroString(WindowInfo
*window
, char *string
, const char *errIn
)
772 return readCheckMacroString(window
->shell
, string
, window
, errIn
, NULL
);
776 ** Check a macro string containing definitions for errors. Returns True
777 ** if macro compiled successfully. Returns False and puts up
778 ** a dialog explaining if macro did not compile successfully.
780 int CheckMacroString(Widget dialogParent
, char *string
, const char *errIn
,
783 return readCheckMacroString(dialogParent
, string
, NULL
, errIn
, errPos
);
787 ** Parse and optionally execute a macro string including macro definitions.
788 ** Report parsing errors in a dialog posted over dialogParent, using the
789 ** string errIn to identify the entity being parsed (filename, macro string,
790 ** etc.). If runWindow is specified, runs the macro against the window. If
791 ** runWindow is passed as NULL, does parse only. If errPos is non-null,
792 ** returns a pointer to the error location in the string.
794 static int readCheckMacroString(Widget dialogParent
, char *string
,
795 WindowInfo
*runWindow
, const char *errIn
, char **errPos
)
797 char *stoppedAt
, *inPtr
, *namePtr
, *errMsg
;
798 char subrName
[MAX_SYM_LEN
];
804 while (*inPtr
!= '\0') {
806 /* skip over white space and comments */
807 while (*inPtr
==' ' || *inPtr
=='\t' || *inPtr
=='\n'|| *inPtr
=='#') {
809 while (*inPtr
!= '\n' && *inPtr
!= '\0') inPtr
++;
816 /* look for define keyword, and compile and store defined routines */
817 if (!strncmp(inPtr
, "define", 6) && (inPtr
[6]==' ' || inPtr
[6]=='\t')) {
819 inPtr
+= strspn(inPtr
, " \t\n");
821 while (isalnum((unsigned char)*inPtr
) || *inPtr
== '_')
822 *namePtr
++ = *inPtr
++;
824 inPtr
+= strspn(inPtr
, " \t\n");
826 if (errPos
!= NULL
) *errPos
= stoppedAt
;
827 return ParseError(dialogParent
, string
, inPtr
,
828 errIn
, "expected '{'");
830 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
832 if (errPos
!= NULL
) *errPos
= stoppedAt
;
833 return ParseError(dialogParent
, string
, stoppedAt
,
836 if (runWindow
!= NULL
) {
837 sym
= LookupSymbol(subrName
);
839 subrPtr
.val
.prog
= prog
;
840 subrPtr
.tag
= NO_TAG
;
841 sym
= InstallSymbol(subrName
, MACRO_FUNCTION_SYM
, subrPtr
);
843 if (sym
->type
== MACRO_FUNCTION_SYM
)
844 FreeProgram(sym
->value
.val
.prog
);
846 sym
->type
= MACRO_FUNCTION_SYM
;
847 sym
->value
.val
.prog
= prog
;
852 /* Parse and execute immediate (outside of any define) macro commands
853 and WAIT for them to finish executing before proceeding. Note that
854 the code below is not perfect. If you interleave code blocks with
855 definitions in a file which is loaded from another macro file, it
856 will probably run the code blocks in reverse order! */
858 prog
= ParseMacro(inPtr
, &errMsg
, &stoppedAt
);
860 if (errPos
!= NULL
) *errPos
= stoppedAt
;
861 return ParseError(dialogParent
, string
, stoppedAt
,
864 if (runWindow
!= NULL
) {
866 if (runWindow
->macroCmdData
== NULL
) {
867 runMacro(runWindow
, prog
);
868 while (runWindow
->macroCmdData
!= NULL
) {
869 XtAppNextEvent(XtWidgetToApplicationContext(
870 runWindow
->shell
), &nextEvent
);
871 ServerDispatchEvent(&nextEvent
);
874 RunMacroAsSubrCall(prog
);
883 ** Run a pre-compiled macro, changing the interface state to reflect that
884 ** a macro is running, and handling preemption, resumption, and cancellation.
885 ** frees prog when macro execution is complete;
887 static void runMacro(WindowInfo
*window
, Program
*prog
)
892 macroCmdInfo
*cmdData
;
895 /* If a macro is already running, just call the program as a subroutine,
896 instead of starting a new one, so we don't have to keep a separate
897 context, and the macros will serialize themselves automatically */
898 if (window
->macroCmdData
!= NULL
) {
899 RunMacroAsSubrCall(prog
);
903 /* put up a watch cursor over the waiting window */
904 BeginWait(window
->shell
);
906 /* enable the cancel menu item */
907 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
908 s
=XmStringCreateSimple("Cancel Macro"), NULL
);
910 XtSetSensitive(window
->cancelMacroItem
, True
);
912 /* Create a data structure for passing macro execution information around
913 amongst the callback routines which will process i/o and completion */
914 cmdData
= (macroCmdInfo
*)XtMalloc(sizeof(macroCmdInfo
));
915 window
->macroCmdData
= cmdData
;
916 cmdData
->bannerIsUp
= False
;
917 cmdData
->closeOnCompletion
= False
;
918 cmdData
->program
= prog
;
919 cmdData
->context
= NULL
;
920 cmdData
->continueWorkProcID
= 0;
921 cmdData
->dialog
= NULL
;
923 /* Set up timer proc for putting up banner when macro takes too long */
924 cmdData
->bannerTimeoutID
= XtAppAddTimeOut(
925 XtWidgetToApplicationContext(window
->shell
), BANNER_WAIT_TIME
,
926 bannerTimeoutProc
, window
);
928 /* Begin macro execution */
929 stat
= ExecuteMacro(window
, prog
, 0, NULL
, &result
, &cmdData
->context
,
932 if (stat
== MACRO_ERROR
)
934 finishMacroCmdExecution(window
);
935 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
936 "Error executing macro: %s", "Dismiss", errMsg
);
940 if (stat
== MACRO_DONE
) {
941 finishMacroCmdExecution(window
);
944 if (stat
== MACRO_TIME_LIMIT
) {
945 ResumeMacroExecution(window
);
948 /* (stat == MACRO_PREEMPT) Macro was preempted */
952 ** Continue with macro execution after preemption. Called by the routines
953 ** whose actions cause preemption when they have completed their lengthy tasks.
954 ** Re-establishes macro execution work proc. Window must be the window in
955 ** which the macro is executing (the window to which macroCmdData is attached),
956 ** and not the window to which operations are focused.
958 void ResumeMacroExecution(WindowInfo
*window
)
960 macroCmdInfo
*cmdData
= (macroCmdInfo
*)window
->macroCmdData
;
963 cmdData
->continueWorkProcID
= XtAppAddWorkProc(
964 XtWidgetToApplicationContext(window
->shell
),
965 continueWorkProc
, window
);
969 ** Cancel the macro command in progress (user cancellation via GUI)
971 void AbortMacroCommand(WindowInfo
*window
)
973 if (window
->macroCmdData
== NULL
)
976 /* If there's both a macro and a shell command executing, the shell command
977 must have been called from the macro. When called from a macro, shell
978 commands don't put up cancellation controls of their own, but rely
979 instead on the macro cancellation mechanism (here) */
981 if (window
->shellCmdData
!= NULL
)
982 AbortShellCommand(window
);
985 /* Free the continuation */
986 FreeRestartData(((macroCmdInfo
*)window
->macroCmdData
)->context
);
988 /* Kill the macro command */
989 finishMacroCmdExecution(window
);
993 ** Call this before closing a window, to clean up macro references to the
994 ** window, stop any macro which might be running from it, free associated
995 ** memory, and check that a macro is not attempting to close the window from
996 ** which it is run. If this is being called from a macro, and the window
997 ** this routine is examining is the window from which the macro was run, this
998 ** routine will return False, and the caller must NOT CLOSE THE WINDOW.
999 ** Instead, empty it and make it Untitled, and let the macro completion
1000 ** process close the window when the macro is finished executing.
1002 int MacroWindowCloseActions(WindowInfo
*window
)
1004 macroCmdInfo
*mcd
, *cmdData
= window
->macroCmdData
;
1007 if (MacroRecordActionHook
!= 0 && MacroRecordWindow
== window
) {
1011 /* If no macro is executing in the window, allow the close, but check
1012 if macros executing in other windows have it as focus. If so, set
1013 their focus back to the window from which they were originally run */
1014 if (cmdData
== NULL
) {
1015 for (w
=WindowList
; w
!=NULL
; w
=w
->next
) {
1016 mcd
= (macroCmdInfo
*)w
->macroCmdData
;
1017 if (w
== MacroRunWindow() && MacroFocusWindow() == window
)
1018 SetMacroFocusWindow(MacroRunWindow());
1019 else if (mcd
!= NULL
&& mcd
->context
->focusWindow
== window
)
1020 mcd
->context
->focusWindow
= mcd
->context
->runWindow
;
1025 /* If the macro currently running (and therefore calling us, because
1026 execution must otherwise return to the main loop to execute any
1027 commands), is running in this window, tell the caller not to close,
1028 and schedule window close on completion of macro */
1029 if (window
== MacroRunWindow()) {
1030 cmdData
->closeOnCompletion
= True
;
1034 /* Free the continuation */
1035 FreeRestartData(cmdData
->context
);
1037 /* Kill the macro command */
1038 finishMacroCmdExecution(window
);
1043 ** Clean up after the execution of a macro command: free memory, and restore
1044 ** the user interface state.
1046 static void finishMacroCmdExecution(WindowInfo
*window
)
1048 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1049 int closeOnCompletion
= cmdData
->closeOnCompletion
;
1051 XClientMessageEvent event
;
1053 /* Cancel pending timeout and work proc */
1054 if (cmdData
->bannerTimeoutID
!= 0)
1055 XtRemoveTimeOut(cmdData
->bannerTimeoutID
);
1056 if (cmdData
->continueWorkProcID
!= 0)
1057 XtRemoveWorkProc(cmdData
->continueWorkProcID
);
1059 /* Clean up waiting-for-macro-command-to-complete mode */
1060 EndWait(window
->shell
);
1061 XtVaSetValues(window
->cancelMacroItem
, XmNlabelString
,
1062 s
=XmStringCreateSimple("Cancel Learn"), NULL
);
1064 XtSetSensitive(window
->cancelMacroItem
, False
);
1065 if (cmdData
->bannerIsUp
)
1066 ClearModeMessage(window
);
1068 /* If a dialog was up, get rid of it */
1069 if (cmdData
->dialog
!= NULL
)
1070 XtDestroyWidget(XtParent(cmdData
->dialog
));
1072 /* Free execution information */
1073 FreeProgram(cmdData
->program
);
1074 XtFree((char *)cmdData
);
1075 window
->macroCmdData
= NULL
;
1077 /* If macro closed its own window, window was made empty and untitled,
1078 but close was deferred until completion. This is completion, so if
1079 the window is still empty, do the close */
1080 if (closeOnCompletion
&& !window
->filenameSet
&& !window
->fileChanged
) {
1081 CloseWindow(window
);
1085 /* If no other macros are executing, do garbage collection */
1088 /* In processing the .neditmacro file (and possibly elsewhere), there
1089 is an event loop which waits for macro completion. Send an event
1090 to wake up that loop, otherwise execution will stall until the user
1091 does something to the window. */
1092 if (!closeOnCompletion
) {
1094 event
.type
= ClientMessage
;
1095 XSendEvent(XtDisplay(window
->shell
), XtWindow(window
->shell
), False
,
1096 NoEventMask
, (XEvent
*)&event
);
1101 ** Do garbage collection of strings if there are no macros currently
1102 ** executing. NEdit's macro language GC strategy is to call this routine
1103 ** whenever a macro completes. If other macros are still running (preempted
1104 ** or waiting for a shell command or dialog), this does nothing and therefore
1105 ** defers GC to the completion of the last macro out.
1111 for (win
=WindowList
; win
!=NULL
; win
=win
->next
)
1112 if (win
->macroCmdData
!= NULL
|| InSmartIndentMacros(win
))
1114 GarbageCollectStrings();
1118 ** Executes macro string "macro" using the lastFocus pane in "window".
1119 ** Reports errors via a dialog posted over "window", integrating the name
1120 ** "errInName" into the message to help identify the source of the error.
1122 void DoMacro(WindowInfo
*window
, const char *macro
, const char *errInName
)
1125 char *errMsg
, *stoppedAt
, *tMacro
;
1128 /* Add a terminating newline (which command line users are likely to omit
1129 since they are typically invoking a single routine) */
1130 macroLen
= strlen(macro
);
1131 tMacro
= XtMalloc(strlen(macro
)+2);
1132 strncpy(tMacro
, macro
, macroLen
);
1133 tMacro
[macroLen
] = '\n';
1134 tMacro
[macroLen
+1] = '\0';
1136 /* Parse the macro and report errors if it fails */
1137 prog
= ParseMacro(tMacro
, &errMsg
, &stoppedAt
);
1139 ParseError(window
->shell
, tMacro
, stoppedAt
, errInName
, errMsg
);
1145 /* run the executable program (prog is freed upon completion) */
1146 runMacro(window
, prog
);
1150 ** Get the current Learn/Replay macro in text form. Returned string is a
1151 ** pointer to the stored macro and should not be freed by the caller (and
1152 ** will cease to exist when the next replay macro is installed)
1154 char *GetReplayMacro(void)
1160 ** Present the user a dialog for "Repeat" command
1162 void RepeatDialog(WindowInfo
*window
)
1164 Widget form
, selBox
, radioBox
, timesForm
;
1167 char *lastCmdLabel
, *parenChar
;
1171 if (LastCommand
== NULL
)
1173 DialogF(DF_WARN
, window
->shell
, 1, "Repeat Macro",
1174 "No previous commands or learn/\nreplay sequences to repeat",
1179 /* Remeber the last command, since the user is allowed to work in the
1180 window while the dialog is up */
1181 rd
= (repeatDialog
*)XtMalloc(sizeof(repeatDialog
));
1182 rd
->lastCommand
= XtNewString(LastCommand
);
1184 /* make a label for the Last command item of the dialog, which includes
1185 the last executed action name */
1186 parenChar
= strchr(LastCommand
, '(');
1187 if (parenChar
== NULL
)
1189 cmdNameLen
= parenChar
-LastCommand
;
1190 lastCmdLabel
= XtMalloc(16 + cmdNameLen
);
1191 strcpy(lastCmdLabel
, "Last Command (");
1192 strncpy(&lastCmdLabel
[14], LastCommand
, cmdNameLen
);
1193 strcpy(&lastCmdLabel
[14 + cmdNameLen
], ")");
1195 XtSetArg(selBoxArgs
[0], XmNautoUnmanage
, False
);
1196 selBox
= CreatePromptDialog(window
->shell
, "repeat", selBoxArgs
, 1);
1197 rd
->shell
= XtParent(selBox
);
1198 XtAddCallback(rd
->shell
, XmNdestroyCallback
, repeatDestroyCB
, rd
);
1199 XtAddCallback(selBox
, XmNokCallback
, repeatOKCB
, rd
);
1200 XtAddCallback(selBox
, XmNapplyCallback
, repeatApplyCB
, rd
);
1201 XtAddCallback(selBox
, XmNcancelCallback
, repeatCancelCB
, rd
);
1202 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_TEXT
));
1203 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_SELECTION_LABEL
));
1204 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_HELP_BUTTON
));
1205 XtUnmanageChild(XmSelectionBoxGetChild(selBox
, XmDIALOG_APPLY_BUTTON
));
1206 XtVaSetValues(XtParent(selBox
), XmNtitle
, "Repeat Macro", NULL
);
1207 AddMotifCloseCallback(XtParent(selBox
), repeatCancelCB
, rd
);
1209 form
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, selBox
, NULL
);
1211 radioBox
= XtVaCreateManagedWidget("cmdSrc", xmRowColumnWidgetClass
, form
,
1212 XmNradioBehavior
, True
,
1213 XmNorientation
, XmHORIZONTAL
,
1214 XmNpacking
, XmPACK_TIGHT
,
1215 XmNtopAttachment
, XmATTACH_FORM
,
1216 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1217 rd
->lastCmdToggle
= XtVaCreateManagedWidget("lastCmdToggle",
1218 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1219 XmNlabelString
, s1
=XmStringCreateSimple(lastCmdLabel
),
1220 XmNmnemonic
, 'C', NULL
);
1222 XtFree(lastCmdLabel
);
1223 XtVaCreateManagedWidget("learnReplayToggle",
1224 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1226 s1
=XmStringCreateSimple("Learn/Replay"),
1228 XmNsensitive
, ReplayMacro
!= NULL
, NULL
);
1231 timesForm
= XtVaCreateManagedWidget("form", xmFormWidgetClass
, form
,
1232 XmNtopAttachment
, XmATTACH_WIDGET
,
1233 XmNtopWidget
, radioBox
,
1235 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1236 radioBox
= XtVaCreateManagedWidget("method", xmRowColumnWidgetClass
,
1238 XmNradioBehavior
, True
,
1239 XmNorientation
, XmHORIZONTAL
,
1240 XmNpacking
, XmPACK_TIGHT
,
1241 XmNtopAttachment
, XmATTACH_FORM
,
1242 XmNbottomAttachment
, XmATTACH_FORM
,
1243 XmNleftAttachment
, XmATTACH_FORM
, NULL
);
1244 rd
->inSelToggle
= XtVaCreateManagedWidget("inSelToggle",
1245 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1246 XmNlabelString
, s1
=XmStringCreateSimple("In Selection"),
1247 XmNmnemonic
, 'I', NULL
);
1249 rd
->toEndToggle
= XtVaCreateManagedWidget("toEndToggle",
1250 xmToggleButtonWidgetClass
, radioBox
, XmNset
, False
,
1251 XmNlabelString
, s1
=XmStringCreateSimple("To End"),
1252 XmNmnemonic
, 'T', NULL
);
1254 XtVaCreateManagedWidget("nTimesToggle",
1255 xmToggleButtonWidgetClass
, radioBox
, XmNset
, True
,
1256 XmNlabelString
, s1
=XmStringCreateSimple("N Times"),
1258 XmNset
, True
, NULL
);
1260 rd
->repeatText
= XtVaCreateManagedWidget("repeatText", xmTextWidgetClass
,
1263 XmNtopAttachment
, XmATTACH_FORM
,
1264 XmNbottomAttachment
, XmATTACH_FORM
,
1265 XmNleftAttachment
, XmATTACH_WIDGET
,
1266 XmNleftWidget
, radioBox
, NULL
);
1267 RemapDeleteKey(rd
->repeatText
);
1269 /* Handle mnemonic selection of buttons and focus to dialog */
1270 AddDialogMnemonicHandler(form
, FALSE
);
1272 /* Set initial focus */
1273 #if XmVersion >= 1002
1274 XtVaSetValues(form
, XmNinitialFocus
, timesForm
, NULL
);
1275 XtVaSetValues(timesForm
, XmNinitialFocus
, rd
->repeatText
, NULL
);
1279 rd
->forWindow
= window
;
1280 ManageDialogCenteredOnPointer(selBox
);
1283 static void repeatOKCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1285 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1287 if (doRepeatDialogAction(rd
, ((XmAnyCallbackStruct
*)callData
)->event
))
1288 XtDestroyWidget(rd
->shell
);
1291 /* Note that the apply button is not managed in the repeat dialog. The dialog
1292 itself is capable of non-modal operation, but to be complete, it needs
1293 to dynamically update last command, dimming of learn/replay, possibly a
1294 stop button for the macro, and possibly in-selection with selection */
1295 static void repeatApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1297 doRepeatDialogAction((repeatDialog
*)clientData
,
1298 ((XmAnyCallbackStruct
*)callData
)->event
);
1301 static int doRepeatDialogAction(repeatDialog
*rd
, XEvent
*event
)
1304 char nTimesStr
[TYPE_INT_STR_SIZE(int)];
1307 /* Find out from the dialog how to repeat the command */
1308 if (XmToggleButtonGetState(rd
->inSelToggle
))
1310 if (!rd
->forWindow
->buffer
->primary
.selected
)
1312 DialogF(DF_WARN
, rd
->shell
, 1, "Repeat Macro",
1313 "No selection in window to repeat within", "Dismiss");
1314 XmProcessTraversal(rd
->inSelToggle
, XmTRAVERSE_CURRENT
);
1317 params
[0] = "in_selection";
1318 } else if (XmToggleButtonGetState(rd
->toEndToggle
))
1320 params
[0] = "to_end";
1323 if (GetIntTextWarn(rd
->repeatText
, &nTimes
, "number of times", True
)
1326 XmProcessTraversal(rd
->repeatText
, XmTRAVERSE_CURRENT
);
1329 sprintf(nTimesStr
, "%d", nTimes
);
1330 params
[0] = nTimesStr
;
1333 /* Figure out which command user wants to repeat */
1334 if (XmToggleButtonGetState(rd
->lastCmdToggle
))
1335 params
[1] = XtNewString(rd
->lastCommand
);
1337 if (ReplayMacro
== NULL
)
1339 params
[1] = XtNewString(ReplayMacro
);
1342 /* call the action routine repeat_macro to do the work */
1343 XtCallActionProc(rd
->forWindow
->lastFocus
, "repeat_macro", event
, params
,2);
1348 static void repeatCancelCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1350 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1352 XtDestroyWidget(rd
->shell
);
1355 static void repeatDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1357 repeatDialog
*rd
= (repeatDialog
*)clientData
;
1359 XtFree(rd
->lastCommand
);
1364 ** Dispatches a macro to which repeats macro command in "command", either
1365 ** an integer number of times ("how" == positive integer), or within a
1366 ** selected range ("how" == REPEAT_IN_SEL), or to the end of the window
1367 ** ("how == REPEAT_TO_END).
1369 ** Note that as with most macro routines, this returns BEFORE the macro is
1370 ** finished executing
1372 void RepeatMacro(WindowInfo
*window
, const char *command
, int how
)
1375 char *errMsg
, *stoppedAt
, *loopMacro
, *loopedCmd
;
1377 if (command
== NULL
)
1380 /* Wrap a for loop and counter/tests around the command */
1381 if (how
== REPEAT_TO_END
)
1382 loopMacro
= "lastCursor=-1\nstartPos=$cursor\n\
1383 while($cursor>=startPos&&$cursor!=lastCursor){\nlastCursor=$cursor\n%s\n}\n";
1384 else if (how
== REPEAT_IN_SEL
)
1385 loopMacro
= "selStart = $selection_start\nif (selStart == -1)\nreturn\n\
1386 selEnd = $selection_end\nset_cursor_pos(selStart)\nselect(0,0)\n\
1387 boundText = get_range(selEnd, selEnd+10)\n\
1388 while($cursor >= selStart && $cursor < selEnd && \\\n\
1389 get_range(selEnd, selEnd+10) == boundText) {\n\
1390 startLength = $text_length\n%s\n\
1391 selEnd += $text_length - startLength\n}\n";
1393 loopMacro
= "for(i=0;i<%d;i++){\n%s\n}\n";
1394 loopedCmd
= XtMalloc(strlen(command
) + strlen(loopMacro
) + 25);
1395 if (how
== REPEAT_TO_END
|| how
== REPEAT_IN_SEL
)
1396 sprintf(loopedCmd
, loopMacro
, command
);
1398 sprintf(loopedCmd
, loopMacro
, how
, command
);
1400 /* Parse the resulting macro into an executable program "prog" */
1401 prog
= ParseMacro(loopedCmd
, &errMsg
, &stoppedAt
);
1403 fprintf(stderr
, "NEdit internal error, repeat macro syntax wrong: %s\n",
1409 /* run the executable program */
1410 runMacro(window
, prog
);
1414 ** Macro recording action hook for Learn/Replay, added temporarily during
1417 static void learnActionHook(Widget w
, XtPointer clientData
, String actionName
,
1418 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1424 /* Select only actions in text panes in the window for which this
1425 action hook is recording macros (from clientData). */
1426 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1427 if (window
->textArea
== w
)
1429 for (i
=0; i
<window
->nPanes
; i
++) {
1430 if (window
->textPanes
[i
] == w
)
1433 if (i
< window
->nPanes
)
1436 if (window
== NULL
|| window
!= (WindowInfo
*)clientData
)
1439 /* beep on un-recordable operations which require a mouse position, to
1440 remind the user that the action was not recorded */
1441 if (isMouseAction(actionName
)) {
1442 XBell(XtDisplay(w
), 0);
1446 /* Record the action and its parameters */
1447 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1448 if (actionString
!= NULL
) {
1449 BufInsert(MacroRecordBuf
, MacroRecordBuf
->length
, actionString
);
1450 XtFree(actionString
);
1455 ** Permanent action hook for remembering last action for possible replay
1457 static void lastActionHook(Widget w
, XtPointer clientData
, String actionName
,
1458 XEvent
*event
, String
*params
, Cardinal
*numParams
)
1464 /* Find the window to which this action belongs */
1465 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1466 if (window
->textArea
== w
)
1468 for (i
=0; i
<window
->nPanes
; i
++) {
1469 if (window
->textPanes
[i
] == w
)
1472 if (i
< window
->nPanes
)
1478 /* The last action is recorded for the benefit of repeating the last
1479 action. Don't record repeat_macro and wipe out the real action */
1480 if (!strcmp(actionName
, "repeat_macro"))
1483 /* Record the action and its parameters */
1484 actionString
= actionToString(w
, actionName
, event
, params
, *numParams
);
1485 if (actionString
!= NULL
) {
1486 if (LastCommand
!= NULL
)
1487 XtFree(LastCommand
);
1488 LastCommand
= actionString
;
1493 ** Create a macro string to represent an invocation of an action routine.
1494 ** Returns NULL for non-operational or un-recordable actions.
1496 static char *actionToString(Widget w
, char *actionName
, XEvent
*event
,
1497 String
*params
, Cardinal numParams
)
1499 char chars
[20], *charList
[1], *outStr
, *outPtr
;
1501 int i
, nChars
, nParams
, length
, nameLength
;
1506 if (isIgnoredAction(actionName
) || isRedundantAction(actionName
) ||
1507 isMouseAction(actionName
))
1510 /* Convert self_insert actions, to insert_string */
1511 if (!strcmp(actionName
, "self_insert") ||
1512 !strcmp(actionName
, "self-insert")) {
1513 actionName
= "insert_string";
1515 nChars
= XLookupString((XKeyEvent
*)event
, chars
, 19, &keysym
, NULL
);
1520 nChars
= XmImMbLookupString(w
, (XKeyEvent
*)event
,
1521 chars
, 19, &keysym
, &status
);
1522 if (nChars
== 0 || status
== XLookupNone
||
1523 status
== XLookupKeySym
|| status
== XBufferOverflow
)
1526 chars
[nChars
] = '\0';
1527 charList
[0] = chars
;
1531 nParams
= numParams
;
1533 /* Figure out the length of string required */
1534 nameLength
= strlen(actionName
);
1535 length
= nameLength
+ 3;
1536 for (i
=0; i
<nParams
; i
++)
1537 length
+= escapedStringLength(params
[i
]) + 4;
1539 /* Allocate the string and copy the information to it */
1540 outPtr
= outStr
= XtMalloc(length
+ 1);
1541 strcpy(outPtr
, actionName
);
1542 outPtr
+= nameLength
;
1544 for (i
=0; i
<nParams
; i
++) {
1546 outPtr
+= escapeStringChars(params
[i
], outPtr
);
1547 *outPtr
++ = '\"'; *outPtr
++ = ','; *outPtr
++ = ' ';
1551 *outPtr
++ = ')'; *outPtr
++ = '\n'; *outPtr
++ = '\0';
1555 static int isMouseAction(const char *action
)
1559 for (i
=0; i
<(int)XtNumber(MouseActions
); i
++)
1560 if (!strcmp(action
, MouseActions
[i
]))
1565 static int isRedundantAction(const char *action
)
1569 for (i
=0; i
<(int)XtNumber(RedundantActions
); i
++)
1570 if (!strcmp(action
, RedundantActions
[i
]))
1575 static int isIgnoredAction(const char *action
)
1579 for (i
=0; i
<(int)XtNumber(IgnoredActions
); i
++)
1580 if (!strcmp(action
, IgnoredActions
[i
]))
1586 ** Timer proc for putting up the "Macro Command in Progress" banner if
1587 ** the process is taking too long.
1589 #define MAX_TIMEOUT_MSG_LEN (MAX_ACCEL_LEN + 60)
1590 static void bannerTimeoutProc(XtPointer clientData
, XtIntervalId
*id
)
1592 WindowInfo
*window
= (WindowInfo
*)clientData
;
1593 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1596 char message
[MAX_TIMEOUT_MSG_LEN
];
1598 cmdData
->bannerIsUp
= True
;
1600 /* Extract accelerator text from menu PushButtons */
1601 XtVaGetValues(window
->cancelMacroItem
, XmNacceleratorText
, &xmCancel
, NULL
);
1603 /* Translate Motif string to char* */
1604 cCancel
= GetXmStringText(xmCancel
);
1606 /* Free Motif String */
1607 XmStringFree(xmCancel
);
1609 /* Create message */
1610 if (cCancel
[0] == '\0') {
1611 strncpy(message
, "Macro Command in Progress", MAX_TIMEOUT_MSG_LEN
);
1612 message
[MAX_TIMEOUT_MSG_LEN
- 1] = '\0';
1616 "Macro Command in Progress -- Press %s to Cancel",
1623 SetModeMessage(window
, message
);
1624 cmdData
->bannerTimeoutID
= 0;
1628 ** Work proc for continuing execution of a preempted macro.
1630 ** Xt WorkProcs are designed to run first-in first-out, which makes them
1631 ** very bad at sharing time between competing tasks. For this reason, it's
1632 ** usually bad to use work procs anywhere where their execution is likely to
1633 ** overlap. Using a work proc instead of a timer proc (which I usually
1634 ** prefer) here means macros will probably share time badly, but we're more
1635 ** interested in making the macros cancelable, and in continuing other work
1636 ** than having users run a bunch of them at once together.
1638 static Boolean
continueWorkProc(XtPointer clientData
)
1640 WindowInfo
*window
= (WindowInfo
*)clientData
;
1641 macroCmdInfo
*cmdData
= window
->macroCmdData
;
1646 stat
= ContinueMacro(cmdData
->context
, &result
, &errMsg
);
1647 if (stat
== MACRO_ERROR
)
1649 finishMacroCmdExecution(window
);
1650 DialogF(DF_ERR
, window
->shell
, 1, "Macro Error",
1651 "Error executing macro: %s", "Dismiss", errMsg
);
1653 } else if (stat
== MACRO_DONE
)
1655 finishMacroCmdExecution(window
);
1657 } else if (stat
== MACRO_PREEMPT
)
1659 cmdData
->continueWorkProcID
= 0;
1663 /* Macro exceeded time slice, re-schedule it */
1664 if (stat
!= MACRO_TIME_LIMIT
)
1665 return True
; /* shouldn't happen */
1670 ** Copy fromString to toString replacing special characters in strings, such
1671 ** that they can be read back by the macro parser's string reader. i.e. double
1672 ** quotes are replaced by \", backslashes are replaced with \\, C-std control
1673 ** characters like \n are replaced with their backslash counterparts. This
1674 ** routine should be kept reasonably in sync with yylex in parse.y. Companion
1675 ** routine escapedStringLength predicts the length needed to write the string
1676 ** when it is expanded with the additional characters. Returns the number
1677 ** of characters to which the string expanded.
1679 static int escapeStringChars(char *fromString
, char *toString
)
1681 char *e
, *c
, *outPtr
= toString
;
1683 /* substitute escape sequences */
1684 for (c
=fromString
; *c
!='\0'; c
++) {
1685 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1688 *outPtr
++ = ReplaceChars
[e
-EscapeChars
];
1696 return outPtr
- toString
;
1700 ** Predict the length of a string needed to hold a copy of "string" with
1701 ** special characters replaced with escape sequences by escapeStringChars.
1703 static int escapedStringLength(char *string
)
1708 /* calculate length and allocate returned string */
1709 for (c
=string
; *c
!='\0'; c
++) {
1710 for (e
=EscapeChars
; *e
!='\0'; e
++) {
1722 ** Built-in macro subroutine for getting the length of a string
1724 static int lengthMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1725 DataValue
*result
, char **errMsg
)
1727 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
1730 return wrongNArgsErr(errMsg
);
1731 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1733 result
->tag
= INT_TAG
;
1734 result
->val
.n
= strlen(string
);
1739 ** Built-in macro subroutines for min and max
1741 static int minMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1742 DataValue
*result
, char **errMsg
)
1744 int minVal
, value
, i
;
1747 return tooFewArgsErr(errMsg
);
1748 if (!readIntArg(argList
[0], &minVal
, errMsg
))
1750 for (i
=0; i
<nArgs
; i
++) {
1751 if (!readIntArg(argList
[i
], &value
, errMsg
))
1753 minVal
= value
< minVal
? value
: minVal
;
1755 result
->tag
= INT_TAG
;
1756 result
->val
.n
= minVal
;
1759 static int maxMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1760 DataValue
*result
, char **errMsg
)
1762 int maxVal
, value
, i
;
1765 return tooFewArgsErr(errMsg
);
1766 if (!readIntArg(argList
[0], &maxVal
, errMsg
))
1768 for (i
=0; i
<nArgs
; i
++) {
1769 if (!readIntArg(argList
[i
], &value
, errMsg
))
1771 maxVal
= value
> maxVal
? value
: maxVal
;
1773 result
->tag
= INT_TAG
;
1774 result
->val
.n
= maxVal
;
1778 static int focusWindowMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1779 DataValue
*result
, char **errMsg
)
1781 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1783 char fullname
[MAXPATHLEN
];
1785 /* Read the argument representing the window to focus to, and translate
1786 it into a pointer to a real WindowInfo */
1788 return wrongNArgsErr(errMsg
);
1789 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1791 else if (!strcmp(string
, "last"))
1793 else if (!strcmp(string
, "next"))
1796 for (w
=WindowList
; w
!= NULL
; w
= w
->next
) {
1797 sprintf(fullname
, "%s%s", w
->path
, w
->filename
);
1798 if (!strcmp(string
, fullname
))
1803 /* If no matching window was found, return empty string and do nothing */
1805 result
->tag
= STRING_TAG
;
1806 result
->val
.str
= PERM_ALLOC_STR("");
1810 /* Change the focused window to the requested one */
1811 SetMacroFocusWindow(w
);
1813 /* Return the name of the window */
1814 result
->tag
= STRING_TAG
;
1815 result
->val
.str
= AllocString(strlen(w
->path
)+strlen(w
->filename
)+1);
1816 sprintf(result
->val
.str
, "%s%s", w
->path
, w
->filename
);
1821 ** Built-in macro subroutine for getting text from the current window's text
1824 static int getRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1825 DataValue
*result
, char **errMsg
)
1828 textBuffer
*buf
= window
->buffer
;
1831 /* Validate arguments and convert to int */
1833 return wrongNArgsErr(errMsg
);
1834 if (!readIntArg(argList
[0], &from
, errMsg
))
1836 if (!readIntArg(argList
[1], &to
, errMsg
))
1838 if (from
< 0) from
= 0;
1839 if (from
> buf
->length
) from
= buf
->length
;
1841 if (to
> buf
->length
) to
= buf
->length
;
1842 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1844 /* Copy text from buffer (this extra copy could be avoided if textBuf.c
1845 provided a routine for writing into a pre-allocated string) */
1846 result
->tag
= STRING_TAG
;
1847 result
->val
.str
= AllocString(to
- from
+ 1);
1848 rangeText
= BufGetRange(buf
, from
, to
);
1849 BufUnsubstituteNullChars(rangeText
, buf
);
1850 strcpy(result
->val
.str
, rangeText
);
1856 ** Built-in macro subroutine for getting a single character at the position
1857 ** given, from the current window
1859 static int getCharacterMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1860 DataValue
*result
, char **errMsg
)
1863 textBuffer
*buf
= window
->buffer
;
1865 /* Validate argument and convert it to int */
1867 return wrongNArgsErr(errMsg
);
1868 if (!readIntArg(argList
[0], &pos
, errMsg
))
1870 if (pos
< 0) pos
= 0;
1871 if (pos
> buf
->length
) pos
= buf
->length
;
1873 /* Return the character in a pre-allocated string) */
1874 result
->tag
= STRING_TAG
;
1875 result
->val
.str
= AllocString(2);
1876 result
->val
.str
[0] = BufGetCharacter(buf
, pos
);
1877 result
->val
.str
[1] = '\0';
1878 BufUnsubstituteNullChars(result
->val
.str
, buf
);
1883 ** Built-in macro subroutine for replacing text in the current window's text
1886 static int replaceRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1887 DataValue
*result
, char **errMsg
)
1890 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1891 textBuffer
*buf
= window
->buffer
;
1893 /* Validate arguments and convert to int */
1895 return wrongNArgsErr(errMsg
);
1896 if (!readIntArg(argList
[0], &from
, errMsg
))
1898 if (!readIntArg(argList
[1], &to
, errMsg
))
1900 if (!readStringArg(argList
[2], &string
, stringStorage
, errMsg
))
1902 if (from
< 0) from
= 0;
1903 if (from
> buf
->length
) from
= buf
->length
;
1905 if (to
> buf
->length
) to
= buf
->length
;
1906 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
1908 /* Don't allow modifications if the window is read-only */
1909 if (IS_ANY_LOCKED(window
->lockReasons
)) {
1910 XBell(XtDisplay(window
->shell
), 0);
1911 result
->tag
= NO_TAG
;
1915 /* There are no null characters in the string (because macro strings
1916 still have null termination), but if the string contains the
1917 character used by the buffer for null substitution, it could
1918 theoretically become a null. In the highly unlikely event that
1919 all of the possible substitution characters in the buffer are used
1920 up, stop the macro and tell the user of the failure */
1921 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
1922 *errMsg
= "Too much binary data in file";
1926 /* Do the replace */
1927 BufReplace(buf
, from
, to
, string
);
1928 result
->tag
= NO_TAG
;
1933 ** Built-in macro subroutine for replacing the primary-selection selected
1934 ** text in the current window's text buffer
1936 static int replaceSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1937 DataValue
*result
, char **errMsg
)
1939 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
1941 /* Validate argument and convert to string */
1943 return wrongNArgsErr(errMsg
);
1944 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
1947 /* Don't allow modifications if the window is read-only */
1948 if (IS_ANY_LOCKED(window
->lockReasons
)) {
1949 XBell(XtDisplay(window
->shell
), 0);
1950 result
->tag
= NO_TAG
;
1954 /* There are no null characters in the string (because macro strings
1955 still have null termination), but if the string contains the
1956 character used by the buffer for null substitution, it could
1957 theoretically become a null. In the highly unlikely event that
1958 all of the possible substitution characters in the buffer are used
1959 up, stop the macro and tell the user of the failure */
1960 if (!BufSubstituteNullChars(string
, strlen(string
), window
->buffer
)) {
1961 *errMsg
= "Too much binary data in file";
1965 /* Do the replace */
1966 BufReplaceSelected(window
->buffer
, string
);
1967 result
->tag
= NO_TAG
;
1972 ** Built-in macro subroutine for getting the text currently selected by
1973 ** the primary selection in the current window's text buffer, or in any
1974 ** part of screen if "any" argument is given
1976 static int getSelectionMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
1977 DataValue
*result
, char **errMsg
)
1981 /* Read argument list to check for "any" keyword, and get the appropriate
1983 if (nArgs
!= 0 && nArgs
!= 1)
1984 return wrongNArgsErr(errMsg
);
1986 if (argList
[0].tag
!= STRING_TAG
|| strcmp(argList
[0].val
.str
, "any")) {
1987 *errMsg
= "Unrecognized argument to %s";
1990 selText
= GetAnySelection(window
);
1991 if (selText
== NULL
)
1992 selText
= XtNewString("");
1994 selText
= BufGetSelectionText(window
->buffer
);
1995 BufUnsubstituteNullChars(selText
, window
->buffer
);
1998 /* Return the text as an allocated string */
1999 result
->tag
= STRING_TAG
;
2000 result
->val
.str
= AllocString(strlen(selText
) + 1);
2001 strcpy(result
->val
.str
, selText
);
2007 ** Built-in macro subroutine for determining if implicit conversion of
2008 ** a string to number will succeed or fail
2010 static int validNumberMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2011 DataValue
*result
, char **errMsg
)
2013 char *string
, stringStorage
[TYPE_INT_STR_SIZE(int)];
2016 return wrongNArgsErr(errMsg
);
2018 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
)) {
2022 result
->tag
= INT_TAG
;
2023 result
->val
.n
= StringToNum(string
, NULL
);
2029 ** Built-in macro subroutine for replacing a substring within another string
2031 static int replaceSubstringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2032 DataValue
*result
, char **errMsg
)
2034 int from
, to
, length
, replaceLen
, outLen
;
2035 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *replStr
;
2037 /* Validate arguments and convert to int */
2039 return wrongNArgsErr(errMsg
);
2040 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2042 if (!readIntArg(argList
[1], &from
, errMsg
))
2044 if (!readIntArg(argList
[2], &to
, errMsg
))
2046 if (!readStringArg(argList
[3], &replStr
, stringStorage
[1], errMsg
))
2048 length
= strlen(string
);
2049 if (from
< 0) from
= 0;
2050 if (from
> length
) from
= length
;
2052 if (to
> length
) to
= length
;
2053 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2055 /* Allocate a new string and do the replacement */
2056 replaceLen
= strlen(replStr
);
2057 outLen
= length
- (to
- from
) + replaceLen
;
2058 result
->tag
= STRING_TAG
;
2059 result
->val
.str
= AllocString(outLen
+1);
2060 strncpy(result
->val
.str
, string
, from
);
2061 strncpy(&result
->val
.str
[from
], replStr
, replaceLen
);
2062 strncpy(&result
->val
.str
[from
+ replaceLen
], &string
[to
], length
- to
);
2063 result
->val
.str
[outLen
] = '\0';
2068 ** Built-in macro subroutine for getting a substring of a string
2070 static int substringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2071 DataValue
*result
, char **errMsg
)
2073 int from
, to
, length
;
2074 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2076 /* Validate arguments and convert to int */
2078 return wrongNArgsErr(errMsg
);
2079 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2081 if (!readIntArg(argList
[1], &from
, errMsg
))
2083 if (!readIntArg(argList
[2], &to
, errMsg
))
2085 length
= strlen(string
);
2086 if (from
< 0) from
= 0;
2087 if (from
> length
) from
= length
;
2089 if (to
> length
) to
= length
;
2090 if (from
> to
) {int temp
= from
; from
= to
; to
= temp
;}
2092 /* Allocate a new string and copy the sub-string into it */
2093 result
->tag
= STRING_TAG
;
2094 result
->val
.str
= AllocString(to
- from
+ 1);
2095 strncpy(result
->val
.str
, &string
[from
], to
- from
);
2096 result
->val
.str
[to
- from
] = '\0';
2100 static int toupperMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2101 DataValue
*result
, char **errMsg
)
2104 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2106 /* Validate arguments and convert to int */
2108 return wrongNArgsErr(errMsg
);
2109 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2111 length
= strlen(string
);
2113 /* Allocate a new string and copy an uppercased version of the string it */
2114 result
->tag
= STRING_TAG
;
2115 result
->val
.str
= AllocString(length
+ 1);
2116 for (i
=0; i
<length
; i
++)
2117 result
->val
.str
[i
] = toupper((unsigned char)string
[i
]);
2118 result
->val
.str
[length
] = '\0';
2122 static int tolowerMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2123 DataValue
*result
, char **errMsg
)
2126 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2128 /* Validate arguments and convert to int */
2130 return wrongNArgsErr(errMsg
);
2131 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2133 length
= strlen(string
);
2135 /* Allocate a new string and copy an lowercased version of the string it */
2136 result
->tag
= STRING_TAG
;
2137 result
->val
.str
= AllocString(length
+ 1);
2138 for (i
=0; i
<length
; i
++)
2139 result
->val
.str
[i
] = tolower((unsigned char)string
[i
]);
2140 result
->val
.str
[length
] = '\0';
2144 static int stringToClipboardMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2145 DataValue
*result
, char **errMsg
)
2150 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2152 /* Get the string argument */
2154 return wrongNArgsErr(errMsg
);
2155 if (!readStringArg(argList
[0], &string
, stringStorage
, errMsg
))
2158 /* Use the XmClipboard routines to copy the text to the clipboard.
2159 If errors occur, just give up. */
2160 result
->tag
= NO_TAG
;
2161 stat
= XmClipboardStartCopy(TheDisplay
, XtWindow(window
->textArea
),
2162 s
=XmStringCreateSimple("NEdit"), XtLastTimestampProcessed(TheDisplay
),
2163 window
->textArea
, NULL
, &itemID
);
2165 if (stat
!= ClipboardSuccess
)
2167 if (XmClipboardCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
, "STRING",
2168 string
, strlen(string
), 0, NULL
) != ClipboardSuccess
)
2170 XmClipboardEndCopy(TheDisplay
, XtWindow(window
->textArea
), itemID
);
2174 static int clipboardToStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2175 DataValue
*result
, char **errMsg
)
2177 unsigned long length
, retLength
;
2180 /* Should have no arguments */
2182 return wrongNArgsErr(errMsg
);
2184 /* Ask if there's a string in the clipboard, and get its length */
2185 if (XmClipboardInquireLength(TheDisplay
, XtWindow(window
->shell
), "STRING",
2186 &length
) != ClipboardSuccess
) {
2187 result
->tag
= STRING_TAG
;
2188 result
->val
.str
= PERM_ALLOC_STR("");
2192 /* Allocate a new string to hold the data */
2193 result
->tag
= STRING_TAG
;
2194 result
->val
.str
= AllocString((int)length
+ 1);
2196 /* Copy the clipboard contents to the string */
2197 if (XmClipboardRetrieve(TheDisplay
, XtWindow(window
->shell
), "STRING",
2198 result
->val
.str
, length
, &retLength
, &id
) != ClipboardSuccess
)
2200 result
->val
.str
[retLength
] = '\0';
2207 ** Built-in macro subroutine for reading the contents of a text file into
2208 ** a string. On success, returns 1 in $readStatus, and the contents of the
2209 ** file as a string in the subroutine return value. On failure, returns
2210 ** the empty string "" and an 0 $readStatus.
2212 static int readFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2213 DataValue
*result
, char **errMsg
)
2215 char stringStorage
[TYPE_INT_STR_SIZE(int)], *name
;
2216 struct stat statbuf
;
2220 /* Validate arguments and convert to int */
2222 return wrongNArgsErr(errMsg
);
2223 if (!readStringArg(argList
[0], &name
, stringStorage
, errMsg
))
2226 /* Read the whole file into an allocated string */
2227 if ((fp
= fopen(name
, "r")) == NULL
)
2229 if (fstat(fileno(fp
), &statbuf
) != 0)
2231 result
->tag
= STRING_TAG
;
2232 result
->val
.str
= AllocString(statbuf
.st_size
+1);
2233 readLen
= fread(result
->val
.str
, sizeof(char), statbuf
.st_size
+1, fp
);
2237 /* Couldn't trust file size. Use slower but more general method */
2238 int chunkSize
= 1024;
2241 buffer
= XtMalloc(readLen
* sizeof(char));
2242 memcpy(buffer
, result
->val
.str
, readLen
* sizeof(char));
2244 buffer
= XtRealloc(buffer
, (readLen
+chunkSize
)*sizeof(char));
2245 readLen
+= fread(&buffer
[readLen
], sizeof(char), chunkSize
, fp
);
2251 result
->val
.str
= AllocString(readLen
+ 1);
2252 memcpy(result
->val
.str
, buffer
, readLen
* sizeof(char));
2255 result
->val
.str
[readLen
] = '\0';
2258 /* Return the results */
2259 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2260 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= True
;
2267 ReturnGlobals
[READ_STATUS
]->value
.tag
= INT_TAG
;
2268 ReturnGlobals
[READ_STATUS
]->value
.val
.n
= False
;
2269 result
->tag
= STRING_TAG
;
2270 result
->val
.str
= PERM_ALLOC_STR("");
2275 ** Built-in macro subroutines for writing or appending a string (parameter $1)
2276 ** to a file named in parameter $2. Returns 1 on successful write, or 0 if
2279 static int writeFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2280 DataValue
*result
, char **errMsg
)
2282 return writeOrAppendFile(False
, window
, argList
, nArgs
, result
, errMsg
);
2285 static int appendFileMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2286 DataValue
*result
, char **errMsg
)
2288 return writeOrAppendFile(True
, window
, argList
, nArgs
, result
, errMsg
);
2291 static int writeOrAppendFile(int append
, WindowInfo
*window
,
2292 DataValue
*argList
, int nArgs
, DataValue
*result
, char **errMsg
)
2294 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *name
, *string
;
2297 /* Validate argument */
2299 return wrongNArgsErr(errMsg
);
2300 if (!readStringArg(argList
[0], &string
, stringStorage
[1], errMsg
))
2302 if (!readStringArg(argList
[1], &name
, stringStorage
[0], errMsg
))
2306 if ((fp
= fopen(name
, append
? "a" : "w")) == NULL
) {
2307 result
->tag
= INT_TAG
;
2308 result
->val
.n
= False
;
2312 /* write the string to the file */
2313 fwrite(string
, sizeof(char), strlen(string
), fp
);
2316 result
->tag
= INT_TAG
;
2317 result
->val
.n
= False
;
2322 /* return the status */
2323 result
->tag
= INT_TAG
;
2324 result
->val
.n
= True
;
2329 ** Built-in macro subroutine for searching silently in a window without
2330 ** dialogs, beeps, or changes to the selection. Arguments are: $1: string to
2331 ** search for, $2: starting position. Optional arguments may include the
2332 ** strings: "wrap" to make the search wrap around the beginning or end of the
2333 ** string, "backward" or "forward" to change the search direction ("forward" is
2334 ** the default), "literal", "case" or "regex" to change the search type
2335 ** (default is "literal").
2337 ** Returns the starting position of the match, or -1 if nothing matched.
2338 ** also returns the ending position of the match in $searchEndPos
2340 static int searchMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2341 DataValue
*result
, char **errMsg
)
2343 DataValue newArgList
[9];
2346 /* Use the search string routine, by adding the buffer contents as
2347 the string argument */
2349 return wrongNArgsErr(errMsg
);
2350 newArgList
[0].tag
= STRING_TAG
;
2351 newArgList
[0].val
.str
= BufGetAll(window
->buffer
);
2352 memcpy(&newArgList
[1], argList
, nArgs
* sizeof(DataValue
));
2353 retVal
= searchStringMS(window
, newArgList
, nArgs
+1, result
, errMsg
);
2354 XtFree(newArgList
[0].val
.str
);
2359 ** Built-in macro subroutine for searching a string. Arguments are $1:
2360 ** string to search in, $2: string to search for, $3: starting position.
2361 ** Optional arguments may include the strings: "wrap" to make the search
2362 ** wrap around the beginning or end of the string, "backward" or "forward"
2363 ** to change the search direction ("forward" is the default), "literal",
2364 ** "case" or "regex" to change the search type (default is "literal").
2366 ** Returns the starting position of the match, or -1 if nothing matched.
2367 ** also returns the ending position of the match in $searchEndPos
2369 static int searchStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2370 DataValue
*result
, char **errMsg
)
2372 int beginPos
, wrap
, direction
, found
= False
, foundStart
, foundEnd
, type
;
2373 int skipSearch
= False
, len
;
2374 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
;
2376 /* Validate arguments and convert to proper types */
2378 return tooFewArgsErr(errMsg
);
2379 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2381 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2383 if (!readIntArg(argList
[2], &beginPos
, errMsg
))
2385 if (!readSearchArgs(&argList
[3], nArgs
-3, &direction
, &type
, &wrap
, errMsg
))
2388 /* This is potentially costly, but it is necessary to protect us from
2389 illegal memory accesses if beginPos is too large (or negative). Note:
2390 matching at position "len" is allowed: a $ matches end of string. */
2391 len
= beginPos
? strlen(string
) : 0; /* avoid strlen if beginPos == 0 */
2392 if (beginPos
> len
) {
2393 if (direction
== SEARCH_FORWARD
) {
2395 beginPos
= 0; /* Wrap immediately */
2403 } else if (beginPos
< 0) {
2404 if (direction
== SEARCH_BACKWARD
) {
2406 beginPos
= len
; /* Wrap immediately */
2417 found
= SearchString(string
, searchStr
, direction
, type
, wrap
, beginPos
,
2418 &foundStart
, &foundEnd
, NULL
, NULL
, GetWindowDelimiters(window
));
2420 /* Return the results */
2421 ReturnGlobals
[SEARCH_END
]->value
.tag
= INT_TAG
;
2422 ReturnGlobals
[SEARCH_END
]->value
.val
.n
= found
? foundEnd
: 0;
2423 result
->tag
= INT_TAG
;
2424 result
->val
.n
= found
? foundStart
: -1;
2429 ** Built-in macro subroutine for replacing all occurences of a search string in
2430 ** a string with a replacement string. Arguments are $1: string to search in,
2431 ** $2: string to search for, $3: replacement string. Also takes an optional
2432 ** search type: one of "literal", "case" or "regex" (default is "literal"), and
2433 ** an optional "copy" argument.
2435 ** Returns a new string with all of the replacements done. If no replacements
2436 ** were performed and "copy" was specified, returns a copy of the original
2437 ** string. Otherwise returns an empty string ("").
2439 static int replaceInStringMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2440 DataValue
*result
, char **errMsg
)
2442 char stringStorage
[3][TYPE_INT_STR_SIZE(int)], *string
, *searchStr
, *replaceStr
;
2443 char *argStr
, *replacedStr
;
2444 int searchType
= SEARCH_LITERAL
, copyStart
, copyEnd
;
2445 int replacedLen
, replaceEnd
, force
=False
, i
;
2447 /* Validate arguments and convert to proper types */
2448 if (nArgs
< 3 || nArgs
> 5)
2449 return wrongNArgsErr(errMsg
);
2450 if (!readStringArg(argList
[0], &string
, stringStorage
[0], errMsg
))
2452 if (!readStringArg(argList
[1], &searchStr
, stringStorage
[1], errMsg
))
2454 if (!readStringArg(argList
[2], &replaceStr
, stringStorage
[2], errMsg
))
2456 for (i
= 3; i
< nArgs
; i
++) {
2457 /* Read the optional search type and force arguments */
2458 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
2460 if (!StringToSearchType(argStr
, &searchType
)) {
2461 /* It's not a search type. is it "copy"? */
2462 if (!strcmp(argStr
, "copy")) {
2465 *errMsg
= "unrecognized argument to %s";
2471 /* Do the replace */
2472 replacedStr
= ReplaceAllInString(string
, searchStr
, replaceStr
, searchType
,
2473 ©Start
, ©End
, &replacedLen
, GetWindowDelimiters(window
));
2475 /* Return the results */
2476 result
->tag
= STRING_TAG
;
2477 if (replacedStr
== NULL
) {
2479 /* Just copy the original DataValue */
2480 result
->val
.str
= argList
[0].val
.str
;
2482 result
->val
.str
= PERM_ALLOC_STR("");
2485 replaceEnd
= copyStart
+ replacedLen
;
2486 result
->val
.str
= AllocString(replaceEnd
+ strlen(&string
[copyEnd
])+1);
2487 strncpy(result
->val
.str
, string
, copyStart
);
2488 strcpy(&result
->val
.str
[copyStart
], replacedStr
);
2489 strcpy(&result
->val
.str
[replaceEnd
], &string
[copyEnd
]);
2490 XtFree(replacedStr
);
2495 static int readSearchArgs(DataValue
*argList
, int nArgs
, int *searchDirection
,
2496 int *searchType
, int *wrap
, char **errMsg
)
2499 char *argStr
, stringStorage
[9][TYPE_INT_STR_SIZE(int)];
2502 *searchDirection
= SEARCH_FORWARD
;
2503 *searchType
= SEARCH_LITERAL
;
2504 for (i
=0; i
<nArgs
; i
++) {
2505 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[i
], errMsg
))
2507 else if (!strcmp(argStr
, "wrap"))
2509 else if (!strcmp(argStr
, "nowrap"))
2511 else if (!strcmp(argStr
, "backward"))
2512 *searchDirection
= SEARCH_BACKWARD
;
2513 else if (!strcmp(argStr
, "forward"))
2514 *searchDirection
= SEARCH_FORWARD
;
2515 else if (!StringToSearchType(argStr
, searchType
)) {
2516 *errMsg
= "Unrecognized argument to %s";
2523 static int setCursorPosMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2524 DataValue
*result
, char **errMsg
)
2528 /* Get argument and convert to int */
2530 return wrongNArgsErr(errMsg
);
2531 if (!readIntArg(argList
[0], &pos
, errMsg
))
2534 /* Set the position */
2535 TextSetCursorPos(window
->lastFocus
, pos
);
2536 result
->tag
= NO_TAG
;
2540 static int selectMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2541 DataValue
*result
, char **errMsg
)
2543 int start
, end
, startTmp
;
2545 /* Get arguments and convert to int */
2547 return wrongNArgsErr(errMsg
);
2548 if (!readIntArg(argList
[0], &start
, errMsg
))
2550 if (!readIntArg(argList
[1], &end
, errMsg
))
2553 /* Verify integrity of arguments */
2559 if (start
< 0) start
= 0;
2560 if (start
> window
->buffer
->length
) start
= window
->buffer
->length
;
2561 if (end
< 0) end
= 0;
2562 if (end
> window
->buffer
->length
) end
= window
->buffer
->length
;
2564 /* Make the selection */
2565 BufSelect(window
->buffer
, start
, end
);
2566 result
->tag
= NO_TAG
;
2570 static int selectRectangleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2571 DataValue
*result
, char **errMsg
)
2573 int start
, end
, left
, right
;
2575 /* Get arguments and convert to int */
2577 return wrongNArgsErr(errMsg
);
2578 if (!readIntArg(argList
[0], &start
, errMsg
))
2580 if (!readIntArg(argList
[1], &end
, errMsg
))
2582 if (!readIntArg(argList
[2], &left
, errMsg
))
2584 if (!readIntArg(argList
[3], &right
, errMsg
))
2587 /* Make the selection */
2588 BufRectSelect(window
->buffer
, start
, end
, left
, right
);
2589 result
->tag
= NO_TAG
;
2594 ** Macro subroutine to ring the bell
2596 static int beepMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2597 DataValue
*result
, char **errMsg
)
2600 return wrongNArgsErr(errMsg
);
2601 XBell(XtDisplay(window
->shell
), 0);
2602 result
->tag
= NO_TAG
;
2606 static int tPrintMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2607 DataValue
*result
, char **errMsg
)
2609 char stringStorage
[TYPE_INT_STR_SIZE(int)], *string
;
2613 return tooFewArgsErr(errMsg
);
2614 for (i
=0; i
<nArgs
; i
++) {
2615 if (!readStringArg(argList
[i
], &string
, stringStorage
, errMsg
))
2617 printf("%s%s", string
, i
==nArgs
-1 ? "" : " ");
2620 result
->tag
= NO_TAG
;
2625 ** Built-in macro subroutine for getting the value of an environment variable
2627 static int getenvMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2628 DataValue
*result
, char **errMsg
)
2632 /* Get name of variable to get */
2634 return wrongNArgsErr(errMsg
);
2635 if (argList
[0].tag
!= STRING_TAG
) {
2636 *errMsg
= "argument to %s must be a string";
2639 value
= getenv(argList
[0].val
.str
);
2643 /* Return the text as an allocated string */
2644 result
->tag
= STRING_TAG
;
2645 result
->val
.str
= AllocString(strlen(value
) + 1);
2646 strcpy(result
->val
.str
, value
);
2650 static int shellCmdMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2651 DataValue
*result
, char **errMsg
)
2653 char stringStorage
[2][TYPE_INT_STR_SIZE(int)], *cmdString
, *inputString
;
2656 return wrongNArgsErr(errMsg
);
2657 if (!readStringArg(argList
[0], &cmdString
, stringStorage
[0], errMsg
))
2659 if (!readStringArg(argList
[1], &inputString
, stringStorage
[1], errMsg
))
2662 /* Shell command execution requires that the macro be suspended, so
2663 this subroutine can't be run if macro execution can't be interrupted */
2664 if (MacroRunWindow()->macroCmdData
== NULL
) {
2665 *errMsg
= "%s can't be called from non-suspendable context";
2670 *errMsg
= "Shell commands not supported under VMS";
2673 ShellCmdToMacroString(window
, cmdString
, inputString
);
2674 result
->tag
= INT_TAG
;
2681 ** Method used by ShellCmdToMacroString (called by shellCmdMS), for returning
2682 ** macro string and exit status after the execution of a shell command is
2683 ** complete. (Sorry about the poor modularity here, it's just not worth
2684 ** teaching other modules about macro return globals, since other than this,
2685 ** they're not used outside of macro.c)
2687 void ReturnShellCommandOutput(WindowInfo
*window
, const char *outText
, int status
)
2690 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2692 if (cmdData
== NULL
)
2694 retVal
.tag
= STRING_TAG
;
2695 retVal
.val
.str
= AllocString(strlen(outText
)+1);
2696 strcpy(retVal
.val
.str
, outText
);
2697 ModifyReturnedValue(cmdData
->context
, retVal
);
2698 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.tag
= INT_TAG
;
2699 ReturnGlobals
[SHELL_CMD_STATUS
]->value
.val
.n
= status
;
2702 static int dialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2703 DataValue
*result
, char **errMsg
)
2705 macroCmdInfo
*cmdData
;
2706 char stringStorage
[9][TYPE_INT_STR_SIZE(int)], *btnLabels
[8], *message
;
2713 /* Ignore the focused window passed as the function argument and put
2714 the dialog up over the window which is executing the macro */
2715 window
= MacroRunWindow();
2716 cmdData
= window
->macroCmdData
;
2718 /* Dialogs require macro to be suspended and interleaved with other macros.
2719 This subroutine can't be run if macro execution can't be interrupted */
2721 *errMsg
= "%s can't be called from non-suspendable context";
2725 /* Read and check the arguments. The first being the dialog message,
2726 and the rest being the button labels */
2728 *errMsg
= "%s subroutine called with no arguments";
2731 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
2733 for (i
=1; i
<nArgs
; i
++)
2734 if (!readStringArg(argList
[i
], &btnLabels
[i
-1], stringStorage
[i
],
2738 btnLabels
[0] = "Dismiss";
2743 /* Create the message box dialog widget and its dialog shell parent */
2745 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2746 XtSetArg(al
[ac
], XmNmessageString
, s1
=MKSTRING(message
)); ac
++;
2747 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0]));
2749 dialog
= CreateMessageDialog(window
->shell
, "macroDialog", al
, ac
);
2752 AddMotifCloseCallback(XtParent(dialog
), dialogCloseCB
, window
);
2753 XtAddCallback(dialog
, XmNokCallback
, dialogBtnCB
, window
);
2754 XtVaSetValues(XmMessageBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2755 XmNuserData
, (XtPointer
)1, NULL
);
2756 cmdData
->dialog
= dialog
;
2758 /* Unmanage default buttons, except for "OK" */
2759 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2760 XtUnmanageChild(XmMessageBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2762 /* Make callback for the unmanaged cancel button (which can
2763 still get executed via the esc key) activate close box action */
2764 XtAddCallback(XmMessageBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2765 XmNactivateCallback
, dialogCloseCB
, window
);
2767 /* Add user specified buttons (1st is already done) */
2768 for (i
=1; i
<nBtns
; i
++) {
2769 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2770 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
2771 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2772 XtAddCallback(btn
, XmNactivateCallback
, dialogBtnCB
, window
);
2776 #ifdef LESSTIF_VERSION
2777 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2778 the escape key for closing the dialog (probably because the
2779 cancel button is not managed). */
2780 XtAddEventHandler(dialog
, KeyPressMask
, False
, dialogEscCB
,
2782 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
2783 True
, GrabModeAsync
, GrabModeAsync
);
2784 #endif /* LESSTIF_VERSION */
2786 /* Put up the dialog */
2787 ManageDialogCenteredOnPointer(dialog
);
2789 /* Stop macro execution until the dialog is complete */
2792 /* Return placeholder result. Value will be changed by button callback */
2793 result
->tag
= INT_TAG
;
2798 static void dialogBtnCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2800 WindowInfo
*window
= (WindowInfo
*)clientData
;
2801 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2805 /* Return the index of the button which was pressed (stored in the userData
2806 field of the button widget). The 1st button, being a gadget, is not
2808 if (cmdData
== NULL
)
2809 return; /* shouldn't happen */
2810 if (XtClass(w
) == xmPushButtonWidgetClass
) {
2811 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
2812 retVal
.val
.n
= (int)userData
;
2815 retVal
.tag
= INT_TAG
;
2816 ModifyReturnedValue(cmdData
->context
, retVal
);
2818 /* Pop down the dialog */
2819 XtDestroyWidget(XtParent(cmdData
->dialog
));
2820 cmdData
->dialog
= NULL
;
2822 /* Continue preempted macro execution */
2823 ResumeMacroExecution(window
);
2826 static void dialogCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
)
2828 WindowInfo
*window
= (WindowInfo
*)clientData
;
2829 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2832 /* Return 0 to show that the dialog was closed via the window close box */
2834 retVal
.tag
= INT_TAG
;
2835 ModifyReturnedValue(cmdData
->context
, retVal
);
2837 /* Pop down the dialog */
2838 XtDestroyWidget(XtParent(cmdData
->dialog
));
2839 cmdData
->dialog
= NULL
;
2841 /* Continue preempted macro execution */
2842 ResumeMacroExecution(window
);
2845 #ifdef LESSTIF_VERSION
2846 static void dialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
2849 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
2851 if (clientData
!= NULL
) {
2852 dialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
2856 #endif /* LESSTIF_VERSION */
2858 static int stringDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
2859 DataValue
*result
, char **errMsg
)
2861 macroCmdInfo
*cmdData
;
2862 char stringStorage
[9][TYPE_INT_STR_SIZE(int)], *btnLabels
[8], *message
;
2869 /* Ignore the focused window passed as the function argument and put
2870 the dialog up over the window which is executing the macro */
2871 window
= MacroRunWindow();
2872 cmdData
= window
->macroCmdData
;
2874 /* Dialogs require macro to be suspended and interleaved with other macros.
2875 This subroutine can't be run if macro execution can't be interrupted */
2877 *errMsg
= "%s can't be called from non-suspendable context";
2881 /* Read and check the arguments. The first being the dialog message,
2882 and the rest being the button labels */
2884 *errMsg
= "%s subroutine called with no arguments";
2887 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
2889 for (i
=1; i
<nArgs
; i
++)
2890 if (!readStringArg(argList
[i
], &btnLabels
[i
-1], stringStorage
[i
],
2894 btnLabels
[0] = "Dismiss";
2899 /* Create the selection box dialog widget and its dialog shell parent */
2901 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
2902 XtSetArg(al
[ac
], XmNselectionLabelString
, s1
=MKSTRING(message
)); ac
++;
2903 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0]));
2905 dialog
= CreatePromptDialog(window
->shell
, "macroStringDialog", al
, ac
);
2908 AddMotifCloseCallback(XtParent(dialog
), stringDialogCloseCB
, window
);
2909 XtAddCallback(dialog
, XmNokCallback
, stringDialogBtnCB
, window
);
2910 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
2911 XmNuserData
, (XtPointer
)1, NULL
);
2912 cmdData
->dialog
= dialog
;
2914 /* Unmanage unneded widgets */
2915 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
2916 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
2918 /* Make callback for the unmanaged cancel button (which can
2919 still get executed via the esc key) activate close box action */
2920 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
2921 XmNactivateCallback
, stringDialogCloseCB
, window
);
2923 /* Add user specified buttons (1st is already done). Selection box
2924 requires a place-holder widget to be added before buttons can be
2925 added, that's what the separator below is for */
2926 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
2927 for (i
=1; i
<nBtns
; i
++) {
2928 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
2929 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
2930 XmNuserData
, (XtPointer
)(i
+1), NULL
);
2931 XtAddCallback(btn
, XmNactivateCallback
, stringDialogBtnCB
, window
);
2935 #ifdef LESSTIF_VERSION
2936 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
2937 the escape key for closing the dialog (probably because the
2938 cancel button is not managed). */
2939 XtAddEventHandler(dialog
, KeyPressMask
, False
, stringDialogEscCB
,
2941 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
2942 True
, GrabModeAsync
, GrabModeAsync
);
2943 #endif /* LESSTIF_VERSION */
2945 /* Put up the dialog */
2946 ManageDialogCenteredOnPointer(dialog
);
2948 /* Stop macro execution until the dialog is complete */
2951 /* Return placeholder result. Value will be changed by button callback */
2952 result
->tag
= INT_TAG
;
2957 static void stringDialogBtnCB(Widget w
, XtPointer clientData
,
2960 WindowInfo
*window
= (WindowInfo
*)clientData
;
2961 macroCmdInfo
*cmdData
= window
->macroCmdData
;
2967 /* shouldn't happen, but would crash if it did */
2968 if (cmdData
== NULL
)
2971 /* Return the string entered in the selection text area */
2972 text
= XmTextGetString(XmSelectionBoxGetChild(cmdData
->dialog
,
2974 retVal
.tag
= STRING_TAG
;
2975 retVal
.val
.str
= AllocString(strlen(text
)+1);
2976 strcpy(retVal
.val
.str
, text
);
2978 ModifyReturnedValue(cmdData
->context
, retVal
);
2980 /* Find the index of the button which was pressed (stored in the userData
2981 field of the button widget). The 1st button, being a gadget, is not
2983 if (XtClass(w
) == xmPushButtonWidgetClass
) {
2984 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
2985 btnNum
= (int)userData
;
2989 /* Return the button number in the global variable $string_dialog_button */
2990 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
2991 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
2993 /* Pop down the dialog */
2994 XtDestroyWidget(XtParent(cmdData
->dialog
));
2995 cmdData
->dialog
= NULL
;
2997 /* Continue preempted macro execution */
2998 ResumeMacroExecution(window
);
3001 static void stringDialogCloseCB(Widget w
, XtPointer clientData
,
3004 WindowInfo
*window
= (WindowInfo
*)clientData
;
3005 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3008 /* shouldn't happen, but would crash if it did */
3009 if (cmdData
== NULL
)
3012 /* Return an empty string */
3013 retVal
.tag
= STRING_TAG
;
3014 retVal
.val
.str
= PERM_ALLOC_STR("");
3015 ModifyReturnedValue(cmdData
->context
, retVal
);
3017 /* Return button number 0 in the global variable $string_dialog_button */
3018 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3019 ReturnGlobals
[STRING_DIALOG_BUTTON
]->value
.val
.n
= 0;
3021 /* Pop down the dialog */
3022 XtDestroyWidget(XtParent(cmdData
->dialog
));
3023 cmdData
->dialog
= NULL
;
3025 /* Continue preempted macro execution */
3026 ResumeMacroExecution(window
);
3029 #ifdef LESSTIF_VERSION
3030 static void stringDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3033 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3035 if (clientData
!= NULL
) {
3036 stringDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3040 #endif /* LESSTIF_VERSION */
3043 ** A subroutine to put up a calltip
3044 ** First arg is either text to be displayed or a key for tip/tag lookup.
3045 ** Optional second arg is the buffer position beneath which to display the
3046 ** upper-left corner of the tip. Default (or -1) puts it under the cursor.
3047 ** Additional optional arguments:
3048 ** "tipText": (default) Indicates first arg is text to be displayed in tip.
3049 ** "tipKey": Indicates first arg is key in calltips database. If key
3050 ** is not found in tip database then the tags database is also
3052 ** "tagKey": Indicates first arg is key in tags database. (Skips
3053 ** search in calltips database.)
3054 ** "center": Horizontally center the calltip at the position
3055 ** "right": Put the right edge of the calltip at the position
3056 ** "center" and "right" cannot both be specified.
3057 ** "above": Place the calltip above the position
3058 ** "strict": Don't move the calltip to keep it on-screen and away
3059 ** from the cursor's line.
3061 ** Returns the new calltip's ID on success, 0 on failure.
3063 ** Does this need to go on IgnoredActions? I don't think so, since
3064 ** showing a calltip may be part of the action you want to learn.
3066 static int calltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3067 DataValue
*result
, char **errMsg
)
3069 char stringStorage
[TYPE_INT_STR_SIZE(int)], *tipText
, *txtArg
;
3070 Boolean anchored
= False
, lookup
= True
;
3072 int anchorPos
, hAlign
= TIP_LEFT
, vAlign
= TIP_BELOW
,
3073 alignMode
= TIP_SLOPPY
;
3075 /* Read and check the string */
3077 *errMsg
= "%s subroutine called with too few arguments";
3081 *errMsg
= "%s subroutine called with too many arguments";
3085 /* Read the tip text or key */
3086 if (!readStringArg(argList
[0], &tipText
, stringStorage
, errMsg
))
3089 /* Read the anchor position (-1 for unanchored) */
3091 if (!readIntArg(argList
[1], &anchorPos
, errMsg
))
3096 if (anchorPos
>= 0) anchored
= True
;
3098 /* Any further args are directives for relative positioning */
3099 for (i
= 2; i
< nArgs
; ++i
) {
3100 if (!readStringArg(argList
[i
], &txtArg
, stringStorage
, errMsg
)){
3103 switch( txtArg
[0] ) {
3105 if (strcmp(txtArg
, "center"))
3107 hAlign
= TIP_CENTER
;
3110 if (strcmp(txtArg
, "right"))
3115 if (strcmp(txtArg
, "above"))
3120 if (strcmp(txtArg
, "strict"))
3122 alignMode
= TIP_STRICT
;
3125 if (!strcmp(txtArg
, "tipText"))
3127 else if (!strcmp(txtArg
, "tipKey"))
3129 else if (!strcmp(txtArg
, "tagKey"))
3130 mode
= TIP_FROM_TAG
;
3139 result
->tag
= INT_TAG
;
3140 if (mode
< 0) lookup
= False
;
3141 /* Look up (maybe) a calltip and display it */
3142 result
->val
.n
= ShowTipString( window
, tipText
, anchored
, anchorPos
, lookup
,
3143 mode
, hAlign
, vAlign
, alignMode
);
3148 /* This is how the (more informative) global var. version would work,
3149 assuming there was a global buffer called msg. */
3150 /* sprintf(msg, "unrecognized argument to %%s: \"%s\"", txtArg);
3152 *errMsg
= "unrecognized argument to %s";
3157 ** A subroutine to kill the current calltip
3159 static int killCalltipMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3160 DataValue
*result
, char **errMsg
)
3165 *errMsg
= "%s subroutine called with too many arguments";
3169 if (!readIntArg(argList
[0], &calltipID
, errMsg
))
3173 KillCalltip( window
, calltipID
);
3175 result
->tag
= NO_TAG
;
3180 * A subroutine to get the ID of the current calltip, or 0 if there is none.
3182 static int calltipIDMV(WindowInfo
*window
, DataValue
*argList
,
3183 int nArgs
, DataValue
*result
, char **errMsg
)
3185 result
->tag
= INT_TAG
;
3186 result
->val
.n
= GetCalltipID(window
, 0);
3191 static int listDialogMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3192 DataValue
*result
, char **errMsg
)
3194 macroCmdInfo
*cmdData
;
3195 char stringStorage
[9][TYPE_INT_STR_SIZE(int)], *btnLabels
[8], *message
, *text
;
3200 char *p
, *old_p
, **text_lines
, *tmp
;
3203 XmString
*test_strings
;
3209 /* Ignore the focused window passed as the function argument and put
3210 the dialog up over the window which is executing the macro */
3211 window
= MacroRunWindow();
3212 cmdData
= window
->macroCmdData
;
3214 /* Dialogs require macro to be suspended and interleaved with other macros.
3215 This subroutine can't be run if macro execution can't be interrupted */
3217 *errMsg
= "%s can't be called from non-suspendable context";
3221 /* Read and check the arguments. The first being the dialog message,
3222 and the rest being the button labels */
3224 *errMsg
= "%s subroutine called with no message, string or arguments";
3228 if (!readStringArg(argList
[0], &message
, stringStorage
[0], errMsg
))
3231 if (!readStringArg(argList
[1], &text
, stringStorage
[0], errMsg
))
3234 if (!text
|| text
[0] == '\0') {
3235 *errMsg
= "%s subroutine called with empty list data";
3239 for (i
=2; i
<nArgs
; i
++)
3240 if (!readStringArg(argList
[i
], &btnLabels
[i
-2], stringStorage
[i
],
3244 btnLabels
[0] = "Dismiss";
3249 /* count the lines in the text - add one for unterminated last line */
3251 for (p
= text
; *p
; p
++)
3255 /* now set up arrays of pointers to lines */
3256 /* test_strings to hold the display strings (tab expanded) */
3257 /* text_lines to hold the original text lines (without the '\n's) */
3258 test_strings
= (XmString
*) XtMalloc(sizeof(XmString
) * nlines
);
3259 text_lines
= (char **)XtMalloc(sizeof(char *) * (nlines
+ 1));
3260 for (n
= 0; n
< nlines
; n
++) {
3261 test_strings
[n
] = (XmString
)0;
3262 text_lines
[n
] = (char *)0;
3264 text_lines
[n
] = (char *)0; /* make sure this is a null-terminated table */
3266 /* pick up the tabDist value */
3267 tabDist
= window
->buffer
->tabDist
;
3269 /* load the table */
3273 tmp_len
= 0; /* current allocated size of temporary buffer tmp */
3274 tmp
= malloc(1); /* temporary buffer into which to expand tabs */
3276 is_last
= (*p
== '\0');
3277 if (*p
== '\n' || is_last
) {
3279 if (strlen(old_p
) > 0) { /* only include non-empty lines */
3283 /* save the actual text line in text_lines[n] */
3284 text_lines
[n
] = (char *)XtMalloc(strlen(old_p
) + 1);
3285 strcpy(text_lines
[n
], old_p
);
3287 /* work out the tabs expanded length */
3288 for (s
= old_p
, l
= 0; *s
; s
++)
3289 l
+= (*s
== '\t') ? tabDist
- (l
% tabDist
) : 1;
3291 /* verify tmp is big enough then tab-expand old_p into tmp */
3293 tmp
= realloc(tmp
, (tmp_len
= l
) + 1);
3294 for (s
= old_p
, t
= tmp
, l
= 0; *s
; s
++) {
3296 for (i
= tabDist
- (l
% tabDist
); i
--; l
++)
3305 /* that's it: tmp is the tab-expanded version of old_p */
3306 test_strings
[n
] = MKSTRING(tmp
);
3311 *p
= '\n'; /* put back our newline */
3316 free(tmp
); /* don't need this anymore */
3319 test_strings
[0] = MKSTRING("");
3323 /* Create the selection box dialog widget and its dialog shell parent */
3325 XtSetArg(al
[ac
], XmNtitle
, " "); ac
++;
3326 XtSetArg(al
[ac
], XmNlistLabelString
, s1
=MKSTRING(message
)); ac
++;
3327 XtSetArg(al
[ac
], XmNlistItems
, test_strings
); ac
++;
3328 XtSetArg(al
[ac
], XmNlistItemCount
, nlines
); ac
++;
3329 XtSetArg(al
[ac
], XmNlistVisibleItemCount
, (nlines
> 10) ? 10 : nlines
); ac
++;
3330 XtSetArg(al
[ac
], XmNokLabelString
, s2
=XmStringCreateSimple(btnLabels
[0])); ac
++;
3331 dialog
= CreateSelectionDialog(window
->shell
, "macroListDialog", al
, ac
);
3332 AddMotifCloseCallback(XtParent(dialog
), listDialogCloseCB
, window
);
3333 XtAddCallback(dialog
, XmNokCallback
, listDialogBtnCB
, window
);
3334 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_OK_BUTTON
),
3335 XmNuserData
, (XtPointer
)1, NULL
);
3338 cmdData
->dialog
= dialog
;
3340 /* forget lines stored in list */
3342 XmStringFree(test_strings
[n
]);
3343 XtFree((char *)test_strings
);
3345 /* modify the list */
3346 XtVaSetValues(XmSelectionBoxGetChild(dialog
, XmDIALOG_LIST
),
3347 XmNselectionPolicy
, XmSINGLE_SELECT
,
3348 XmNuserData
, (XtPointer
)text_lines
, NULL
);
3350 /* Unmanage unneeded widgets */
3351 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_APPLY_BUTTON
));
3352 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
));
3353 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_HELP_BUTTON
));
3354 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_TEXT
));
3355 XtUnmanageChild(XmSelectionBoxGetChild(dialog
, XmDIALOG_SELECTION_LABEL
));
3357 /* Make callback for the unmanaged cancel button (which can
3358 still get executed via the esc key) activate close box action */
3359 XtAddCallback(XmSelectionBoxGetChild(dialog
, XmDIALOG_CANCEL_BUTTON
),
3360 XmNactivateCallback
, listDialogCloseCB
, window
);
3362 /* Add user specified buttons (1st is already done). Selection box
3363 requires a place-holder widget to be added before buttons can be
3364 added, that's what the separator below is for */
3365 XtVaCreateWidget("x", xmSeparatorWidgetClass
, dialog
, NULL
);
3366 for (i
=1; i
<nBtns
; i
++) {
3367 btn
= XtVaCreateManagedWidget("mdBtn", xmPushButtonWidgetClass
, dialog
,
3368 XmNlabelString
, s1
=XmStringCreateSimple(btnLabels
[i
]),
3369 XmNuserData
, (XtPointer
)(i
+1), NULL
);
3370 XtAddCallback(btn
, XmNactivateCallback
, listDialogBtnCB
, window
);
3374 #ifdef LESSTIF_VERSION
3375 /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
3376 the escape key for closing the dialog. */
3377 XtAddEventHandler(dialog
, KeyPressMask
, False
, listDialogEscCB
,
3379 XtGrabKey(dialog
, XKeysymToKeycode(XtDisplay(dialog
), XK_Escape
), 0,
3380 True
, GrabModeAsync
, GrabModeAsync
);
3381 #endif /* LESSTIF_VERSION */
3383 /* Put up the dialog */
3384 ManageDialogCenteredOnPointer(dialog
);
3386 /* Stop macro execution until the dialog is complete */
3389 /* Return placeholder result. Value will be changed by button callback */
3390 result
->tag
= INT_TAG
;
3395 static void listDialogBtnCB(Widget w
, XtPointer clientData
,
3398 WindowInfo
*window
= (WindowInfo
*)clientData
;
3399 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3405 int n_sel
, *seltable
, sel_index
= 0;
3408 /* shouldn't happen, but would crash if it did */
3409 if (cmdData
== NULL
)
3412 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3413 /* Return the string selected in the selection list area */
3414 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3415 if (!XmListGetSelectedPos(theList
, &seltable
, &n_sel
)) {
3419 sel_index
= seltable
[0] - 1;
3420 XtFree((XtPointer
)seltable
);
3424 text
= PERM_ALLOC_STR("");
3427 text
= AllocString(strlen((char *)text_lines
[sel_index
]) + 1);
3428 strcpy(text
, text_lines
[sel_index
]);
3431 /* don't need text_lines anymore: free it */
3432 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3433 XtFree((XtPointer
)text_lines
[sel_index
]);
3434 XtFree((XtPointer
)text_lines
);
3436 retVal
.tag
= STRING_TAG
;
3437 retVal
.val
.str
= text
;
3438 ModifyReturnedValue(cmdData
->context
, retVal
);
3440 /* Find the index of the button which was pressed (stored in the userData
3441 field of the button widget). The 1st button, being a gadget, is not
3443 if (XtClass(w
) == xmPushButtonWidgetClass
) {
3444 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
3445 btnNum
= (int)userData
;
3449 /* Return the button number in the global variable $list_dialog_button */
3450 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3451 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= btnNum
;
3453 /* Pop down the dialog */
3454 XtDestroyWidget(XtParent(cmdData
->dialog
));
3455 cmdData
->dialog
= NULL
;
3457 /* Continue preempted macro execution */
3458 ResumeMacroExecution(window
);
3461 static void listDialogCloseCB(Widget w
, XtPointer clientData
,
3464 WindowInfo
*window
= (WindowInfo
*)clientData
;
3465 macroCmdInfo
*cmdData
= window
->macroCmdData
;
3471 /* shouldn't happen, but would crash if it did */
3472 if (cmdData
== NULL
)
3475 /* don't need text_lines anymore: retrieve it then free it */
3476 theList
= XmSelectionBoxGetChild(cmdData
->dialog
, XmDIALOG_LIST
);
3477 XtVaGetValues(theList
, XmNuserData
, &text_lines
, NULL
);
3478 for (sel_index
= 0; text_lines
[sel_index
]; sel_index
++)
3479 XtFree((XtPointer
)text_lines
[sel_index
]);
3480 XtFree((XtPointer
)text_lines
);
3482 /* Return an empty string */
3483 retVal
.tag
= STRING_TAG
;
3484 retVal
.val
.str
= PERM_ALLOC_STR("");
3485 ModifyReturnedValue(cmdData
->context
, retVal
);
3487 /* Return button number 0 in the global variable $list_dialog_button */
3488 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.tag
= INT_TAG
;
3489 ReturnGlobals
[LIST_DIALOG_BUTTON
]->value
.val
.n
= 0;
3491 /* Pop down the dialog */
3492 XtDestroyWidget(XtParent(cmdData
->dialog
));
3493 cmdData
->dialog
= NULL
;
3495 /* Continue preempted macro execution */
3496 ResumeMacroExecution(window
);
3498 /* T Balinski End */
3500 #ifdef LESSTIF_VERSION
3501 static void listDialogEscCB(Widget w
, XtPointer clientData
, XEvent
*event
,
3504 if (event
->xkey
.keycode
!= XKeysymToKeycode(XtDisplay(w
), XK_Escape
))
3506 if (clientData
!= NULL
) {
3507 listDialogCloseCB(w
, (WindowInfo
*)clientData
, NULL
);
3511 #endif /* LESSTIF_VERSION */
3514 static int stringCompareMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3515 DataValue
*result
, char **errMsg
)
3517 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3518 char *leftStr
, *rightStr
, *argStr
;
3519 int considerCase
= True
;
3524 return(wrongNArgsErr(errMsg
));
3526 if (!readStringArg(argList
[0], &leftStr
, stringStorage
[0], errMsg
))
3528 if (!readStringArg(argList
[1], &rightStr
, stringStorage
[1], errMsg
))
3530 for (i
= 2; i
< nArgs
; ++i
) {
3531 if (!readStringArg(argList
[i
], &argStr
, stringStorage
[2], errMsg
))
3533 else if (!strcmp(argStr
, "case"))
3534 considerCase
= True
;
3535 else if (!strcmp(argStr
, "nocase"))
3536 considerCase
= False
;
3538 *errMsg
= "Unrecognized argument to %s";
3543 compareResult
= strcmp(leftStr
, rightStr
);
3544 compareResult
= (compareResult
> 0) ? 1 : ((compareResult
< 0) ? -1 : 0);
3547 compareResult
= strCaseCmp(leftStr
, rightStr
);
3549 result
->tag
= INT_TAG
;
3550 result
->val
.n
= compareResult
;
3555 ** This function is intended to split strings into an array of substrings
3556 ** Importatnt note: It should always return at least one entry with key 0
3557 ** split("", ",") result[0] = ""
3558 ** split("1,2", ",") result[0] = "1" result[1] = "2"
3559 ** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
3561 ** This behavior is specifically important when used to break up
3562 ** array sub-scripts
3565 static int splitMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3566 DataValue
*result
, char **errMsg
)
3568 char stringStorage
[3][TYPE_INT_STR_SIZE(int)];
3569 char *sourceStr
, *splitStr
, *typeSplitStr
;
3570 int searchType
, beginPos
, foundStart
, foundEnd
, strLength
;
3571 int found
, elementEnd
, indexNum
;
3572 char indexStr
[28], *allocIndexStr
;
3577 return(wrongNArgsErr(errMsg
));
3579 if (!readStringArg(argList
[0], &sourceStr
, stringStorage
[0], errMsg
)) {
3580 *errMsg
= "first argument must be a string: %s";
3583 if (!readStringArg(argList
[1], &splitStr
, stringStorage
[1], errMsg
)) {
3587 if (splitStr
[0] == 0) {
3591 if (splitStr
== NULL
) {
3592 *errMsg
= "second argument must be a non-empty string: %s";
3595 if (nArgs
> 2 && readStringArg(argList
[2], &typeSplitStr
, stringStorage
[2], errMsg
)) {
3596 if (!StringToSearchType(typeSplitStr
, &searchType
)) {
3597 *errMsg
= "unrecognized argument to %s";
3602 searchType
= SEARCH_LITERAL
;
3605 result
->tag
= ARRAY_TAG
;
3606 result
->val
.arrayPtr
= ArrayNew();
3610 strLength
= strlen(sourceStr
);
3612 while (found
&& beginPos
< strLength
) {
3613 sprintf(indexStr
, "%d", indexNum
);
3614 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3615 if (!allocIndexStr
) {
3616 *errMsg
= "array element failed to allocate key: %s";
3619 strcpy(allocIndexStr
, indexStr
);
3620 found
= SearchString(sourceStr
, splitStr
, SEARCH_FORWARD
, searchType
,
3621 False
, beginPos
, &foundStart
, &foundEnd
,
3622 NULL
, NULL
, GetWindowDelimiters(window
));
3623 elementEnd
= found
? foundStart
: strLength
;
3624 elementLen
= elementEnd
- beginPos
;
3625 element
.tag
= STRING_TAG
;
3626 element
.val
.str
= AllocString(elementLen
+ 1);
3627 if (!element
.val
.str
) {
3628 *errMsg
= "failed to allocate element value: %s";
3631 strncpy(element
.val
.str
, &sourceStr
[beginPos
], elementLen
);
3632 element
.val
.str
[elementLen
] = 0;
3634 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3635 M_ARRAY_INSERT_FAILURE();
3638 beginPos
= found
? foundEnd
: strLength
;
3642 sprintf(indexStr
, "%d", indexNum
);
3643 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
3644 if (!allocIndexStr
) {
3645 *errMsg
= "array element failed to allocate key: %s";
3648 strcpy(allocIndexStr
, indexStr
);
3649 element
.tag
= STRING_TAG
;
3650 element
.val
.str
= PERM_ALLOC_STR("");
3652 if (!ArrayInsert(result
, allocIndexStr
, &element
)) {
3653 M_ARRAY_INSERT_FAILURE();
3660 ** Set the backlighting string resource for the current window. If no parameter
3661 ** is passed or the value "default" is passed, it attempts to set the preference
3662 ** value of the resource. If the empty string is passed, the backlighting string
3663 ** will be cleared, turning off backlighting.
3666 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
3667 int nArgs, DataValue *result, char **errMsg)
3669 char *backlightString;
3672 backlightString = GetPrefBacklightCharTypes();
3674 else if (nArgs == 1) {
3675 if (argList[0].tag != STRING_TAG) {
3676 *errMsg = "%s not called with a string parameter";
3679 backlightString = argList[0].val.str;
3682 return wrongNArgsErr(errMsg);
3684 if (strcmp(backlightString, "default") == 0)
3685 backlightString = GetPrefBacklightCharTypes();
3686 if (backlightString && *backlightString == '\0') / * empty string param * /
3687 backlightString = NULL; / * turns of backlighting * /
3689 SetBacklightChars(window, backlightString);
3693 static int cursorMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3694 DataValue
*result
, char **errMsg
)
3696 result
->tag
= INT_TAG
;
3697 result
->val
.n
= TextGetCursorPos(window
->lastFocus
);
3701 static int lineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3702 DataValue
*result
, char **errMsg
)
3704 int line
, cursorPos
, colNum
;
3706 result
->tag
= INT_TAG
;
3707 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3708 if (!TextPosToLineAndCol(window
->lastFocus
, cursorPos
, &line
, &colNum
))
3709 line
= BufCountLines(window
->buffer
, 0, cursorPos
) + 1;
3710 result
->val
.n
= line
;
3714 static int columnMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3715 DataValue
*result
, char **errMsg
)
3717 textBuffer
*buf
= window
->buffer
;
3720 result
->tag
= INT_TAG
;
3721 cursorPos
= TextGetCursorPos(window
->lastFocus
);
3722 result
->val
.n
= BufCountDispChars(buf
, BufStartOfLine(buf
, cursorPos
),
3727 static int fileNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3728 DataValue
*result
, char **errMsg
)
3730 result
->tag
= STRING_TAG
;
3731 result
->val
.str
= AllocString(strlen(window
->filename
) + 1);
3732 strcpy(result
->val
.str
, window
->filename
);
3736 static int filePathMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3737 DataValue
*result
, char **errMsg
)
3739 result
->tag
= STRING_TAG
;
3740 result
->val
.str
= AllocString(strlen(window
->path
) + 1);
3741 strcpy(result
->val
.str
, window
->path
);
3745 static int lengthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3746 DataValue
*result
, char **errMsg
)
3748 result
->tag
= INT_TAG
;
3749 result
->val
.n
= window
->buffer
->length
;
3753 static int selectionStartMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3754 DataValue
*result
, char **errMsg
)
3756 result
->tag
= INT_TAG
;
3757 result
->val
.n
= window
->buffer
->primary
.selected
?
3758 window
->buffer
->primary
.start
: -1;
3762 static int selectionEndMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3763 DataValue
*result
, char **errMsg
)
3765 result
->tag
= INT_TAG
;
3766 result
->val
.n
= window
->buffer
->primary
.selected
?
3767 window
->buffer
->primary
.end
: -1;
3771 static int selectionLeftMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3772 DataValue
*result
, char **errMsg
)
3774 selection
*sel
= &window
->buffer
->primary
;
3776 result
->tag
= INT_TAG
;
3777 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectStart
: -1;
3781 static int selectionRightMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3782 DataValue
*result
, char **errMsg
)
3784 selection
*sel
= &window
->buffer
->primary
;
3786 result
->tag
= INT_TAG
;
3787 result
->val
.n
= sel
->selected
&& sel
->rectangular
? sel
->rectEnd
: -1;
3791 static int wrapMarginMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3792 DataValue
*result
, char **errMsg
)
3796 XtVaGetValues(window
->textArea
, textNcolumns
, &nCols
,
3797 textNwrapMargin
, &margin
, NULL
);
3798 result
->tag
= INT_TAG
;
3799 result
->val
.n
= margin
== 0 ? nCols
: margin
;
3803 static int statisticsLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3804 DataValue
*result
, char **errMsg
)
3806 result
->tag
= INT_TAG
;
3807 result
->val
.n
= window
->showStats
? 1 : 0;
3811 static int incSearchLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3812 DataValue
*result
, char **errMsg
)
3814 result
->tag
= INT_TAG
;
3815 result
->val
.n
= window
->showISearchLine
? 1 : 0;
3819 static int showLineNumbersMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3820 DataValue
*result
, char **errMsg
)
3822 result
->tag
= INT_TAG
;
3823 result
->val
.n
= window
->showLineNumbers
? 1 : 0;
3827 static int autoIndentMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3828 DataValue
*result
, char **errMsg
)
3832 switch (window
->indentStyle
) {
3833 case NO_AUTO_INDENT
:
3834 res
= PERM_ALLOC_STR("off");
3837 res
= PERM_ALLOC_STR("on");
3840 res
= PERM_ALLOC_STR("smart");
3843 *errMsg
= "Invalid indent style value encountered in %s";
3847 result
->tag
= STRING_TAG
;
3848 result
->val
.str
= res
;
3852 static int wrapTextMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3853 DataValue
*result
, char **errMsg
)
3857 switch (window
->wrapMode
) {
3859 res
= PERM_ALLOC_STR("none");
3862 res
= PERM_ALLOC_STR("auto");
3864 case CONTINUOUS_WRAP
:
3865 res
= PERM_ALLOC_STR("continuous");
3868 *errMsg
= "Invalid wrap style value encountered in %s";
3872 result
->tag
= STRING_TAG
;
3873 result
->val
.str
= res
;
3877 static int highlightSyntaxMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3878 DataValue
*result
, char **errMsg
)
3880 result
->tag
= INT_TAG
;
3881 result
->val
.n
= window
->highlightSyntax
? 1 : 0;
3885 static int makeBackupCopyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3886 DataValue
*result
, char **errMsg
)
3888 result
->tag
= INT_TAG
;
3889 result
->val
.n
= window
->saveOldVersion
? 1 : 0;
3893 static int incBackupMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3894 DataValue
*result
, char **errMsg
)
3896 result
->tag
= INT_TAG
;
3897 result
->val
.n
= window
->autoSave
? 1 : 0;
3901 static int showMatchingMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3902 DataValue
*result
, char **errMsg
)
3906 switch (window
->showMatchingStyle
) {
3908 res
= PERM_ALLOC_STR(NO_FLASH_STRING
);
3911 res
= PERM_ALLOC_STR(FLASH_DELIMIT_STRING
);
3914 res
= PERM_ALLOC_STR(FLASH_RANGE_STRING
);
3917 *errMsg
= "Invalid match flashing style value encountered in %s";
3921 result
->tag
= STRING_TAG
;
3922 result
->val
.str
= res
;
3926 static int overTypeModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3927 DataValue
*result
, char **errMsg
)
3929 result
->tag
= INT_TAG
;
3930 result
->val
.n
= window
->overstrike
? 1 : 0;
3934 static int readOnlyMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3935 DataValue
*result
, char **errMsg
)
3937 result
->tag
= INT_TAG
;
3938 result
->val
.n
= (IS_ANY_LOCKED(window
->lockReasons
)) ? 1 : 0;
3942 static int lockedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3943 DataValue
*result
, char **errMsg
)
3945 result
->tag
= INT_TAG
;
3946 result
->val
.n
= (IS_USER_LOCKED(window
->lockReasons
)) ? 1 : 0;
3950 static int fileFormatMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3951 DataValue
*result
, char **errMsg
)
3955 switch (window
->fileFormat
) {
3956 case UNIX_FILE_FORMAT
:
3957 res
= PERM_ALLOC_STR("unix");
3959 case DOS_FILE_FORMAT
:
3960 res
= PERM_ALLOC_STR("dos");
3962 case MAC_FILE_FORMAT
:
3963 res
= PERM_ALLOC_STR("macintosh");
3966 *errMsg
= "Invalid linefeed style value encountered in %s";
3969 result
->tag
= STRING_TAG
;
3970 result
->val
.str
= res
;
3974 static int fontNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3975 DataValue
*result
, char **errMsg
)
3977 result
->tag
= STRING_TAG
;
3978 result
->val
.str
= AllocString(strlen(window
->fontName
) + 1);
3979 strcpy(result
->val
.str
, window
->fontName
);
3983 static int fontNameItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3984 DataValue
*result
, char **errMsg
)
3986 result
->tag
= STRING_TAG
;
3987 result
->val
.str
= AllocString(strlen(window
->italicFontName
) + 1);
3988 strcpy(result
->val
.str
, window
->italicFontName
);
3992 static int fontNameBoldMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
3993 DataValue
*result
, char **errMsg
)
3995 result
->tag
= STRING_TAG
;
3996 result
->val
.str
= AllocString(strlen(window
->boldFontName
) + 1);
3997 strcpy(result
->val
.str
, window
->boldFontName
);
4001 static int fontNameBoldItalicMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4002 DataValue
*result
, char **errMsg
)
4004 result
->tag
= STRING_TAG
;
4005 result
->val
.str
= AllocString(strlen(window
->boldItalicFontName
) + 1);
4006 strcpy(result
->val
.str
, window
->boldItalicFontName
);
4010 static int subscriptSepMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4011 DataValue
*result
, char **errMsg
)
4013 result
->tag
= STRING_TAG
;
4014 result
->val
.str
= PERM_ALLOC_STR(ARRAY_DIM_SEP
);
4018 static int minFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4019 DataValue
*result
, char **errMsg
)
4021 result
->tag
= INT_TAG
;
4022 result
->val
.n
= TextGetMinFontWidth(window
->textArea
, window
->highlightSyntax
);
4026 static int maxFontWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4027 DataValue
*result
, char **errMsg
)
4029 result
->tag
= INT_TAG
;
4030 result
->val
.n
= TextGetMaxFontWidth(window
->textArea
, window
->highlightSyntax
);
4034 static int topLineMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4035 DataValue
*result
, char **errMsg
)
4037 result
->tag
= INT_TAG
;
4038 result
->val
.n
= TextFirstVisibleLine(window
->lastFocus
);
4042 static int numDisplayLinesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4043 DataValue
*result
, char **errMsg
)
4045 result
->tag
= INT_TAG
;
4046 result
->val
.n
= TextNumVisibleLines(window
->lastFocus
);
4050 static int displayWidthMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4051 DataValue
*result
, char **errMsg
)
4053 result
->tag
= INT_TAG
;
4054 result
->val
.n
= TextVisibleWidth(window
->lastFocus
);
4058 static int activePaneMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4059 DataValue
*result
, char **errMsg
)
4061 result
->tag
= INT_TAG
;
4062 result
->val
.n
= WidgetToPaneIndex(window
, window
->lastFocus
) + 1;
4066 static int nPanesMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4067 DataValue
*result
, char **errMsg
)
4069 result
->tag
= INT_TAG
;
4070 result
->val
.n
= window
->nPanes
+ 1;
4074 static int emptyArrayMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4075 DataValue
*result
, char **errMsg
)
4077 result
->tag
= ARRAY_TAG
;
4078 result
->val
.arrayPtr
= NULL
;
4082 static int serverNameMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4083 DataValue
*result
, char **errMsg
)
4085 char *serverName
= GetPrefServerName();
4087 result
->tag
= STRING_TAG
;
4088 result
->val
.str
= AllocString(strlen(serverName
) + 1);
4089 strcpy(result
->val
.str
, serverName
);
4093 static int tabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4094 DataValue
*result
, char **errMsg
)
4096 result
->tag
= INT_TAG
;
4097 result
->val
.n
= window
->buffer
->tabDist
;
4101 static int emTabDistMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4102 DataValue
*result
, char **errMsg
)
4106 XtVaGetValues(window
->textArea
, textNemulateTabs
, &dist
, NULL
);
4107 result
->tag
= INT_TAG
;
4108 result
->val
.n
= dist
== 0 ? -1 : dist
;
4112 static int useTabsMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4113 DataValue
*result
, char **errMsg
)
4115 result
->tag
= INT_TAG
;
4116 result
->val
.n
= window
->buffer
->useTabs
;
4120 static int modifiedMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4121 DataValue
*result
, char **errMsg
)
4123 result
->tag
= INT_TAG
;
4124 result
->val
.n
= window
->fileChanged
;
4128 static int languageModeMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4129 DataValue
*result
, char **errMsg
)
4131 char *lmName
= LanguageModeName(window
->languageMode
);
4135 result
->tag
= STRING_TAG
;
4136 result
->val
.str
= AllocString(strlen(lmName
) + 1);
4137 strcpy(result
->val
.str
, lmName
);
4142 static int backlightStringMV(WindowInfo *window, DataValue *argList,
4143 int nArgs, DataValue *result, char **errMsg)
4145 char *backlightString = window->backlightCharTypes;
4147 result->tag = STRING_TAG;
4148 if (!backlightString || !window->backlightChars)
4149 backlightString = "";
4150 result->val.str = AllocString(strlen(backlightString) + 1);
4151 strcpy(result->val.str, backlightString);
4155 /* -------------------------------------------------------------------------- */
4158 ** Range set macro variables and functions
4161 static int rangesetListMV(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4162 DataValue
*result
, char **errMsg
)
4164 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4165 unsigned char *rangesetList
;
4166 char *allocIndexStr
;
4171 result
->tag
= ARRAY_TAG
;
4172 result
->val
.arrayPtr
= ArrayNew();
4174 if (rangesetTable
== NULL
) {
4178 rangesetList
= RangesetGetList(rangesetTable
);
4179 nRangesets
= strlen((char*)rangesetList
);
4180 for(i
= 0; i
< nRangesets
; i
++) {
4181 element
.tag
= INT_TAG
;
4182 element
.val
.n
= rangesetList
[i
];
4184 sprintf(indexStr
, "%d", nRangesets
- i
- 1);
4185 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4186 if (allocIndexStr
== NULL
)
4187 M_FAILURE("Failed to allocate array key in %s");
4188 strcpy(allocIndexStr
, indexStr
);
4190 if (!ArrayInsert(result
, allocIndexStr
, &element
))
4191 M_FAILURE("Failed to insert array element in %s");
4199 ** Built-in macro subroutine to create a new rangeset or rangesets.
4200 ** If called with one argument: $1 is the number of rangesets required and
4201 ** return value is an array indexed 0 to n, with the rangeset labels as values;
4202 ** (or an empty array if the requested number of rangesets are not available).
4203 ** If called with no arguments, returns a single rangeset label (not an array),
4204 ** or an empty string if there are no rangesets available.
4206 static int rangesetCreateMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4207 DataValue
*result
, char **errMsg
)
4210 int i
, nRangesetsRequired
;
4212 char indexStr
[3], *allocIndexStr
;
4214 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4217 return wrongNArgsErr(errMsg
);
4219 if (rangesetTable
== NULL
) {
4220 window
->buffer
->rangesetTable
= rangesetTable
=
4221 RangesetTableAlloc(window
->buffer
);
4225 label
= RangesetCreate(rangesetTable
);
4227 result
->tag
= INT_TAG
;
4228 result
->val
.n
= label
;
4232 if (!readIntArg(argList
[0], &nRangesetsRequired
, errMsg
))
4235 result
->tag
= ARRAY_TAG
;
4236 result
->val
.arrayPtr
= ArrayNew();
4238 if (nRangesetsRequired
> nRangesetsAvailable(rangesetTable
))
4241 for (i
= 0; i
< nRangesetsRequired
; i
++) {
4242 element
.tag
= INT_TAG
;
4243 element
.val
.n
= RangesetCreate(rangesetTable
);
4245 sprintf(indexStr
, "%d", i
);
4246 allocIndexStr
= AllocString(strlen(indexStr
) + 1);
4247 if (!allocIndexStr
) {
4248 *errMsg
= "Array element failed to allocate key: %s";
4251 strcpy(allocIndexStr
, indexStr
);
4252 ArrayInsert(result
, allocIndexStr
, &element
);
4261 ** Built-in macro subroutine for forgetting a range set.
4264 static int rangesetDestroyMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4265 DataValue
*result
, char **errMsg
)
4267 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4271 char deleteLabels
[N_RANGESETS
];
4275 return wrongNArgsErr(errMsg
);
4278 if (argList
[0].tag
== ARRAY_TAG
) {
4279 array
= &argList
[0];
4280 arraySize
= ArraySize(array
);
4282 if (arraySize
> N_RANGESETS
) {
4283 M_FAILURE("Too many elements in array in %s");
4286 for (i
= 0; i
< arraySize
; i
++) {
4287 sprintf(keyString
, "%d", i
);
4289 if (!ArrayGet(array
, keyString
, &element
)) {
4290 M_FAILURE("Invalid key in array in %s");
4293 if (element
.tag
!= INT_TAG
4294 || !RangesetLabelOK(element
.val
.n
)) {
4295 M_FAILURE("Invalid rangeset label in array in %s");
4298 deleteLabels
[i
] = element
.val
.n
;
4301 for (i
= 0; i
< arraySize
; i
++) {
4302 RangesetForget(rangesetTable
, deleteLabels
[i
]);
4307 if (argList
[0].tag
!= INT_TAG
4308 || !RangesetLabelOK(argList
[0].val
.n
)) {
4309 M_FAILURE("Invalid rangeset label in %s");
4312 if(rangesetTable
!= NULL
) {
4313 RangesetForget(rangesetTable
, argList
[0].val
.n
);
4318 result
->tag
= NO_TAG
;
4323 ** Built-in macro subroutine for adding to a range set. Arguments are $1: range
4324 ** set label (one integer), then either (a) $2: source range set label,
4325 ** (b) $2: int start-range, $3: int end-range, (c) nothing (use selection
4326 ** if any to specify range to add - must not be rectangular). Returns the
4327 ** index of the newly added range (cases b and c), or 0 (case a).
4330 static int rangesetAddMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4331 DataValue
*result
, char **errMsg
)
4333 textBuffer
*buffer
= window
->buffer
;
4334 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4335 Rangeset
*targetRangeset
, *sourceRangeset
;
4336 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
, index
;
4338 if (nArgs
< 1 || nArgs
> 3)
4339 return wrongNArgsErr(errMsg
);
4341 if (argList
[0].tag
!= INT_TAG
4342 || !RangesetLabelOK(argList
[0].val
.n
)) {
4343 M_FAILURE("First parameter is an invalid rangeset label in %s");
4346 if (rangesetTable
== NULL
) {
4347 M_FAILURE("Rangeset does not exist in %s");
4350 targetRangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4352 if (targetRangeset
== NULL
) {
4353 M_FAILURE("Rangeset does not exist in %s");
4359 /* pick up current selection in this window */
4360 if (!BufGetSelectionPos(buffer
, &start
, &end
,
4361 &isRect
, &rectStart
, &rectEnd
) || isRect
) {
4362 M_FAILURE("Selection missing or rectangular in call to %s");
4364 if (!RangesetAddBetween(targetRangeset
, start
, end
)) {
4365 M_FAILURE("Failure to add selection in %s");
4370 /* add ranges taken from a second set */
4371 if (argList
[1].tag
!= INT_TAG
4372 || !RangesetLabelOK(argList
[1].val
.n
)) {
4373 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4376 sourceRangeset
= RangesetFetch(rangesetTable
, argList
[1].val
.n
);
4377 if (sourceRangeset
== NULL
) {
4378 M_FAILURE("Second rangeset does not exist in %s");
4381 if (!RangesetAdd(targetRangeset
, sourceRangeset
)) {
4382 M_FAILURE("Failed to merge rangesets in %s");
4387 /* add a range bounded by the start and end positions in $2, $3 */
4388 if (!readIntArg(argList
[1], &start
, errMsg
)) {
4391 if (!readIntArg(argList
[2], &end
, errMsg
)) {
4395 /* make sure range is in order and fits buffer size */
4396 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4397 if (start
< 0) start
= 0;
4398 if (start
> maxpos
) start
= maxpos
;
4399 if (end
< 0) end
= 0;
4400 if (end
> maxpos
) end
= maxpos
;
4401 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4403 if (!RangesetAddBetween(targetRangeset
, start
, end
)) {
4404 M_FAILURE("Failed to add range in %s");
4408 /* (to) which range did we just add? */
4409 if (nArgs
!= 2 && start
>= 0) {
4410 start
= (start
+ end
) / 2; /* "middle" of added range */
4411 index
= 1 + RangesetFindRangeOfPos(targetRangeset
, start
, False
);
4418 result
->tag
= INT_TAG
;
4419 result
->val
.n
= index
;
4425 ** Built-in macro subroutine for removing from a range set. Almost identical to
4426 ** rangesetAddMS() - only changes are from RangesetAdd()/RangesetAddBetween()
4427 ** to RangesetSubtract()/RangesetSubtractBetween(), the handling of an
4428 ** undefined destination range, and that it returns no value.
4431 static int rangesetSubtractMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4432 DataValue
*result
, char **errMsg
)
4434 textBuffer
*buffer
= window
->buffer
;
4435 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4436 Rangeset
*targetRangeset
, *sourceRangeset
;
4437 int start
, end
, isRect
, rectStart
, rectEnd
, maxpos
;
4439 if (nArgs
< 1 || nArgs
> 3) {
4440 return wrongNArgsErr(errMsg
);
4443 if (argList
[0].tag
!= INT_TAG
4444 || !RangesetLabelOK(argList
[0].val
.n
)) {
4445 M_FAILURE("First parameter is an invalid rangeset label in %s");
4448 if (rangesetTable
== NULL
) {
4449 M_FAILURE("Rangeset does not exist in %s");
4452 targetRangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4453 if (targetRangeset
== NULL
) {
4454 M_FAILURE("Rangeset does not exist in %s");
4458 /* remove current selection in this window */
4459 if (!BufGetSelectionPos(buffer
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)
4461 M_FAILURE("Selection missing or rectangular in call to %s");
4463 RangesetRemoveBetween(targetRangeset
, start
, end
);
4467 /* remove ranges taken from a second set */
4468 if (argList
[1].tag
!= INT_TAG
4469 || !RangesetLabelOK(argList
[1].val
.n
)) {
4470 M_FAILURE("Second parameter is an invalid rangeset label in %s");
4473 sourceRangeset
= RangesetFetch(rangesetTable
, argList
[1].val
.n
);
4474 if (sourceRangeset
== NULL
) {
4475 M_FAILURE("Second rangeset does not exist in %s");
4477 RangesetRemove(targetRangeset
, sourceRangeset
);
4481 /* remove a range bounded by the start and end positions in $2, $3 */
4482 if (!readIntArg(argList
[1], &start
, errMsg
))
4484 if (!readIntArg(argList
[2], &end
, errMsg
))
4487 /* make sure range is in order and fits buffer size */
4488 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4489 if (start
< 0) start
= 0;
4490 if (start
> maxpos
) start
= maxpos
;
4491 if (end
< 0) end
= 0;
4492 if (end
> maxpos
) end
= maxpos
;
4493 if (start
> end
) {int temp
= start
; start
= end
; end
= temp
;}
4495 RangesetRemoveBetween(targetRangeset
, start
, end
);
4499 result
->tag
= NO_TAG
;
4505 ** Built-in macro subroutine to invert a range set. Argument is $1: range set
4506 ** label (one alphabetic character). Returns nothing. Fails if range set
4510 static int rangesetInvertMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4511 DataValue
*result
, char **errMsg
)
4514 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4518 return wrongNArgsErr(errMsg
);
4520 if (argList
[0].tag
!= INT_TAG
4521 || !RangesetLabelOK(argList
[0].val
.n
)) {
4522 M_FAILURE("First parameter is an invalid rangeset label in %s");
4525 if (rangesetTable
== NULL
) {
4526 M_FAILURE("Rangeset does not exist in %s");
4529 rangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4530 if (rangeset
== NULL
) {
4531 M_FAILURE("Rangeset does not exist in %s");
4534 if (RangesetInverse(rangeset
) < 0) {
4535 M_FAILURE("Problem inverting rangeset in %s");
4539 result
->tag
= NO_TAG
;
4545 ** Built-in macro subroutine for finding out info about a rangeset. Takes one
4546 ** argument of a rangeset label. Returns an array with the following keys:
4547 ** defined, count, color, mode.
4550 static int rangesetInfoMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4551 DataValue
*result
, char **errMsg
)
4553 RangesetTable
*rangesetTable
= window
->buffer
->rangesetTable
;
4554 Rangeset
*rangeset
= NULL
;
4555 unsigned char label
;
4560 static char *definedIndex
= "\001defined";
4561 static char *countIndex
= "\001count";
4562 static char *colorIndex
= "\001color";
4563 static char *modeIndex
= "\001mode";
4566 return wrongNArgsErr(errMsg
);
4568 if (argList
[0].tag
!= INT_TAG
4569 || !RangesetLabelOK(argList
[0].val
.n
)) {
4570 M_FAILURE("First parameter is an invalid rangeset label in %s");
4573 label
= argList
[0].val
.n
;
4575 if (rangesetTable
!= NULL
) {
4576 rangeset
= RangesetFetch(rangesetTable
, label
);
4579 RangesetGetInfo(rangeset
, &defined
, &label
, &count
, &color
, &mode
);
4582 result
->tag
= ARRAY_TAG
;
4583 result
->val
.arrayPtr
= ArrayNew();
4585 element
.tag
= INT_TAG
;
4586 element
.val
.n
= defined
;
4587 if (!ArrayInsert(result
, definedIndex
+1, &element
))
4588 M_FAILURE("Failed to insert array element \"defined\" in %s");
4590 element
.tag
= INT_TAG
;
4591 element
.val
.n
= count
;
4592 if (!ArrayInsert(result
, countIndex
+1, &element
))
4593 M_FAILURE("Failed to insert array element \"count\" in %s");
4595 element
.tag
= STRING_TAG
;
4596 element
.val
.str
= AllocString(strlen(color
) + 1);
4597 if (element
.val
.str
== NULL
)
4598 M_FAILURE("Failed to allocate array value \"color\" in %s");
4599 strcpy(element
.val
.str
, color
);
4600 if (!ArrayInsert(result
, colorIndex
+1, &element
))
4601 M_FAILURE("Failed to insert array element \"color\" in %s");
4603 element
.tag
= STRING_TAG
;
4604 element
.val
.str
= AllocString(strlen(mode
) + 1);
4605 if (element
.val
.str
== NULL
)
4606 M_FAILURE("Failed to allocate array value \"mode\" in %s");
4607 strcpy(element
.val
.str
, mode
);
4608 if (!ArrayInsert(result
, modeIndex
+1, &element
))
4609 M_FAILURE("Failed to insert array element \"mode\" in %s");
4615 ** Built-in macro subroutine for finding the extent of a range in a set.
4616 ** If only one parameter is supplied, use the spanning range of all
4617 ** ranges, otherwise select the individual range specified. Returns
4618 ** an array with the keys "start" and "end" and values
4621 static int rangesetRangeMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4622 DataValue
*result
, char **errMsg
)
4624 textBuffer
*buffer
= window
->buffer
;
4625 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4627 int start
, end
, dummy
, rangeIndex
, ok
;
4630 static char *startIndex
= "\001start";
4631 static char *endIndex
= "\001end";
4633 if (nArgs
< 1 || nArgs
> 2) {
4634 return wrongNArgsErr(errMsg
);
4637 if (argList
[0].tag
!= INT_TAG
4638 || !RangesetLabelOK(argList
[0].val
.n
)) {
4639 M_FAILURE("First parameter is an invalid rangeset label in %s");
4642 if (rangesetTable
== NULL
) {
4643 M_FAILURE("Rangeset does not exist in %s");
4647 rangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4648 if (rangeset
!= NULL
) {
4650 rangeIndex
= RangesetGetNRanges(rangeset
) - 1;
4651 ok
= RangesetFindRangeNo(rangeset
, 0, &start
, &dummy
);
4652 ok
&= RangesetFindRangeNo(rangeset
, rangeIndex
, &dummy
, &end
);
4655 else if (nArgs
== 2) {
4656 if (!readIntArg(argList
[1], &rangeIndex
, errMsg
)) {
4659 ok
= RangesetFindRangeNo(rangeset
, rangeIndex
-1, &start
, &end
);
4664 result
->tag
= ARRAY_TAG
;
4665 result
->val
.arrayPtr
= ArrayNew();
4670 element
.tag
= INT_TAG
;
4671 element
.val
.n
= start
;
4672 if (!ArrayInsert(result
, startIndex
+1, &element
))
4673 M_FAILURE("Failed to insert array element \"start\" in %s");
4675 element
.tag
= INT_TAG
;
4676 element
.val
.n
= end
;
4677 if (!ArrayInsert(result
, endIndex
+1, &element
))
4678 M_FAILURE("Failed to insert array element \"end\" in %s");
4684 ** Built-in macro subroutine for checking a position against a range. If only
4685 ** one parameter is supplied, the current cursor position is used. Returns
4686 ** false (zero) if not in a range, range index (1-based) if in a range;
4687 ** fails if parameters were bad.
4690 static int rangesetIncludesPosMS(WindowInfo
*window
, DataValue
*argList
,
4691 int nArgs
, DataValue
*result
, char **errMsg
)
4693 textBuffer
*buffer
= window
->buffer
;
4694 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4696 int pos
, rangeIndex
, maxpos
;
4698 if (nArgs
< 1 || nArgs
> 2) {
4699 return wrongNArgsErr(errMsg
);
4702 if (argList
[0].tag
!= INT_TAG
4703 || !RangesetLabelOK(argList
[0].val
.n
)) {
4704 M_FAILURE("First parameter is an invalid rangeset label in %s");
4707 if (rangesetTable
== NULL
) {
4708 M_FAILURE("Rangeset does not exist in %s");
4711 rangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4712 if (rangeset
== NULL
) {
4713 M_FAILURE("Rangeset does not exist in %s");
4717 pos
= TextGetCursorPos(window
->lastFocus
);
4719 else if (nArgs
== 2) {
4720 if (!readIntArg(argList
[1], &pos
, errMsg
))
4724 maxpos
= buffer
->gapEnd
- buffer
->gapStart
+ buffer
->length
;
4725 if (pos
< 0 || pos
> maxpos
) {
4729 rangeIndex
= RangesetFindRangeOfPos(rangeset
, pos
, False
) + 1;
4733 result
->tag
= INT_TAG
;
4734 result
->val
.n
= rangeIndex
;
4739 ** Set the color of a range set's ranges. it is ignored if the color cannot be
4740 ** found/applied. If no color is applied, any current color is removed. Returns
4741 ** true if the rangeset is valid.
4744 static int rangesetSetColorMS(WindowInfo
*window
, DataValue
*argList
,
4745 int nArgs
, DataValue
*result
, char **errMsg
)
4747 textBuffer
*buffer
= window
->buffer
;
4748 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4753 return wrongNArgsErr(errMsg
);
4756 if (argList
[0].tag
!= INT_TAG
4757 || !RangesetLabelOK(argList
[0].val
.n
)) {
4758 M_FAILURE("First parameter is an invalid rangeset label in %s");
4761 if (rangesetTable
== NULL
) {
4762 M_FAILURE("Rangeset does not exist in %s");
4765 rangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4766 if (rangeset
== NULL
) {
4767 M_FAILURE("Rangeset does not exist in %s");
4771 if (rangeset
!= NULL
) {
4772 if (argList
[1].tag
!= STRING_TAG
) {
4773 M_FAILURE("Second parameter is not a color name string in %s");
4775 color_name
= argList
[1].val
.str
;
4778 RangesetAssignColorName(rangeset
, color_name
);
4781 result
->tag
= NO_TAG
;
4786 ** Change a range's modification response. Returns true if the rangeset is
4787 ** valid and the response type name is valid.
4790 static int rangesetSetModeMS(WindowInfo
*window
, DataValue
*argList
,
4791 int nArgs
, DataValue
*result
, char **errMsg
)
4793 textBuffer
*buffer
= window
->buffer
;
4794 RangesetTable
*rangesetTable
= buffer
->rangesetTable
;
4796 char *update_fn_name
;
4799 if (nArgs
< 1 || nArgs
> 2) {
4800 return wrongNArgsErr(errMsg
);
4803 if (argList
[0].tag
!= INT_TAG
4804 || !RangesetLabelOK(argList
[0].val
.n
)) {
4805 M_FAILURE("First parameter is an invalid rangeset label in %s");
4808 if (rangesetTable
== NULL
) {
4809 M_FAILURE("Rangeset does not exist in %s");
4812 rangeset
= RangesetFetch(rangesetTable
, argList
[0].val
.n
);
4813 if (rangeset
== NULL
) {
4814 M_FAILURE("Rangeset does not exist in %s");
4817 update_fn_name
= "";
4818 if (rangeset
!= NULL
) {
4820 if (argList
[1].tag
!= STRING_TAG
) {
4821 M_FAILURE("Second parameter is not a string in %s");
4823 update_fn_name
= argList
[1].val
.str
;
4827 ok
= RangesetChangeModifyResponse(rangeset
, update_fn_name
);
4830 M_FAILURE("Second parameter is not a valid mode in %s");
4834 result
->tag
= NO_TAG
;
4838 /* -------------------------------------------------------------------------- */
4842 ** Routines to get details directly from the window.
4846 ** Returns an array containing information about the style of position $1
4848 ** ["style"] Name of style
4849 ** ["color"] Color of style
4850 ** ["rgb"] RGB representation of color of style
4851 ** ["bold"] '1' if style is bold, '0' otherwise
4852 ** ["italic"] '1' if style is italic, '0' otherwise
4853 ** ["background"] Background color of style if specified
4854 ** ["back_rgb"] RGB representation of background color of style
4857 static int getStyleMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4858 DataValue
*result
, char **errMsg
)
4865 char colorValue
[20];
4868 /* Validate number of arguments */
4870 return wrongNArgsErr(errMsg
);
4873 /* Prepare result */
4874 result
->tag
= ARRAY_TAG
;
4875 result
->val
.arrayPtr
= NULL
;
4877 /* Convert argument to whatever its type is */
4878 if (argList
[0].tag
== STRING_TAG
) {
4879 styleName
= argList
[0].val
.str
;
4880 if (!NamedStyleExists(styleName
)) {
4881 /* if the given name is invalid we just return an empty array. */
4887 textBuffer
*buf
= window
->buffer
;
4889 if (!readIntArg(argList
[0], &cursorPos
, errMsg
)) {
4893 /* Verify sane cursor position */
4894 if ((cursorPos
< 0) || (cursorPos
>= buf
->length
))
4896 /* If the position is not legal, we cannot guess anything about
4897 the style, so we return an empty array. */
4901 /* Determine style name */
4902 styleCode
= HighlightCodeOfPos(window
, cursorPos
);
4903 if (styleCode
== 0) {
4904 /* if there is no style we just return an empty array. */
4907 styleName
= AllocStringCpy(HighlightStyleOfCode(window
, styleCode
));
4910 /* initialize array */
4911 result
->val
.arrayPtr
= ArrayNew();
4913 /* the following array entries will be strings */
4914 DV
.tag
= STRING_TAG
;
4916 /* insert style name */
4917 DV
.val
.str
= styleName
;
4918 M_STR_ALLOC_ASSERT(DV
);
4919 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
)) {
4920 M_ARRAY_INSERT_FAILURE();
4923 /* insert color name */
4924 DV
.val
.str
= AllocStringCpy(ColorOfNamedStyle(styleName
));
4925 M_STR_ALLOC_ASSERT(DV
);
4926 if (!ArrayInsert(result
, PERM_ALLOC_STR("color"), &DV
)) {
4927 M_ARRAY_INSERT_FAILURE();
4930 /* Prepare array element for color value */
4931 HighlightColorValueOfCode(window
, styleCode
, &r
, &g
, &b
);
4932 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
4933 DV
.val
.str
= AllocStringCpy(colorValue
);
4934 M_STR_ALLOC_ASSERT(DV
);
4935 if (!ArrayInsert(result
, PERM_ALLOC_STR("rgb"), &DV
)) {
4936 M_ARRAY_INSERT_FAILURE();
4939 /* Prepare array element for background color name */
4940 DV
.val
.str
= AllocStringCpy(BgColorOfNamedStyle(styleName
));
4941 M_STR_ALLOC_ASSERT(DV
);
4942 if (!ArrayInsert(result
, PERM_ALLOC_STR("background"), &DV
)) {
4943 M_ARRAY_INSERT_FAILURE();
4946 /* Prepare array element for background color value */
4947 GetHighlightBGColorOfCode(window
, styleCode
,&r
,&g
,&b
);
4948 sprintf(colorValue
, "#%02x%02x%02x", r
/256, g
/256, b
/256);
4949 DV
.val
.str
= AllocStringCpy(colorValue
);
4950 M_STR_ALLOC_ASSERT(DV
);
4951 if (!ArrayInsert(result
, PERM_ALLOC_STR("back_rgb"), &DV
)) {
4952 M_ARRAY_INSERT_FAILURE();
4955 /* the following array entries will be integers */
4958 /* Put boldness value in array */
4959 DV
.val
.n
= FontOfNamedStyleIsBold(styleName
);
4960 if (!ArrayInsert(result
, PERM_ALLOC_STR("bold"), &DV
)) {
4961 M_ARRAY_INSERT_FAILURE();
4964 /* Put italicity value in array */
4965 DV
.val
.n
= FontOfNamedStyleIsItalic(styleName
);
4966 if (!ArrayInsert(result
, PERM_ALLOC_STR("italic"), &DV
)) {
4967 M_ARRAY_INSERT_FAILURE();
4974 ** Returns an array containing information about a highligting pattern. The
4975 ** single parameter contains the position this information is requested for.
4976 ** The returned array looks like this:
4977 ** ["pattern"] Name of pattern
4978 ** ["style"] Name of style
4979 ** ["extension"] Distance this style continues
4981 ** A second option is to call get_pattern() with a pattern name, to learn
4982 ** about a patterns style. In this case, the 'extension' element is not set.
4984 static int getPatternMS(WindowInfo
*window
, DataValue
*argList
, int nArgs
,
4985 DataValue
*result
, char **errMsg
)
4988 textBuffer
*buffer
= window
->buffer
;
4991 char* styleName
= NULL
;
4992 char* patternName
= NULL
;
4993 highlightPattern
* pattern
= NULL
;
4995 Boolean extensionRequired
= True
;
4999 /* Begin of building the result. */
5000 result
->tag
= ARRAY_TAG
;
5001 result
->val
.arrayPtr
= NULL
;
5003 /* Validate number of arguments */
5006 return wrongNArgsErr(errMsg
);
5009 /* Convert argument to whatever its type is and set styleName and
5010 patternName accordingly. */
5011 if (argList
[0].tag
== INT_TAG
)
5013 /* The most straightforward case: Get a pattern, style and extension
5014 for a cursor position. */
5015 if (!readIntArg(argList
[0], &cursorPos
, errMsg
))
5020 /* Verify sane cursor position
5021 * You would expect that buffer->length would be among the sane
5022 * positions, but we have n characters and n+1 cursor positions. */
5023 if ((cursorPos
< 0) || (cursorPos
>= buffer
->length
))
5025 /* If the position is not legal, we cannot guess anything about
5026 the style, so we return an empty array. */
5030 /* Determine style name */
5031 styleCode
= HighlightCodeOfPos(window
, cursorPos
);
5034 /* if there is no style we just return an empty array. */
5038 styleName
= AllocStringCpy(HighlightStyleOfCode(window
, styleCode
));
5039 patternName
= AllocStringCpy(HighlightNameOfCode(window
, styleCode
));
5040 } else if (argList
[0].tag
== STRING_TAG
)
5042 /* This is used to learn about a pattern's style. */
5043 patternName
= argList
[0].val
.str
;
5044 pattern
= FindPatternOfWindow(window
, patternName
);
5045 if (pattern
== NULL
)
5047 /* The pattern's name is unknown. */
5050 styleName
= AllocStringCpy(pattern
->style
);
5051 extensionRequired
= False
; /* no position -> no extension */
5054 *errMsg
= "Position or pattern name string expected as parameter to %s";
5058 /* initialize array */
5059 result
->val
.arrayPtr
= ArrayNew();
5061 /* the following array entries will be strings */
5062 DV
.tag
= STRING_TAG
;
5064 /* insert pattern name */
5065 DV
.val
.str
= patternName
;
5066 M_STR_ALLOC_ASSERT(DV
);
5067 if (!ArrayInsert(result
, PERM_ALLOC_STR("pattern"), &DV
))
5069 M_ARRAY_INSERT_FAILURE();
5072 /* insert style name */
5073 DV
.val
.str
= styleName
;
5074 M_STR_ALLOC_ASSERT(DV
);
5075 if (!ArrayInsert(result
, PERM_ALLOC_STR("style"), &DV
))
5077 M_ARRAY_INSERT_FAILURE();
5080 /* the following array entry will be integer */
5084 if (extensionRequired
)
5087 DV
.val
.n
= HighlightLengthOfCodeFromPos(window
, cursorPos
, &checkCode
);
5088 if (!ArrayInsert(result
, PERM_ALLOC_STR("extension"), &DV
))
5090 M_ARRAY_INSERT_FAILURE();
5097 static int wrongNArgsErr(char **errMsg
)
5099 *errMsg
= "Wrong number of arguments to function %s";
5103 static int tooFewArgsErr(char **errMsg
)
5105 *errMsg
= "Too few arguments to function %s";
5110 ** strCaseCmp compares its arguments and returns 0 if the two strings
5111 ** are equal IGNORING case differences. Otherwise returns 1 or -1
5112 ** depending on relative comparison.
5114 static int strCaseCmp(char *str1
, char *str2
)
5118 for (c1
= str1
, c2
= str2
;
5119 (*c1
!= '\0' && *c2
!= '\0')
5120 && toupper((unsigned char)*c1
) == toupper((unsigned char)*c2
);
5125 if (((unsigned char)toupper((unsigned char)*c1
))
5126 > ((unsigned char)toupper((unsigned char)*c2
)))
5129 } else if (((unsigned char)toupper((unsigned char)*c1
))
5130 < ((unsigned char)toupper((unsigned char)*c2
)))
5140 ** Get an integer value from a tagged DataValue structure. Return True
5141 ** if conversion succeeded, and store result in *result, otherwise
5142 ** return False with an error message in *errMsg.
5144 static int readIntArg(DataValue dv
, int *result
, char **errMsg
)
5148 if (dv
.tag
== INT_TAG
) {
5151 } else if (dv
.tag
== STRING_TAG
) {
5152 for (c
=dv
.val
.str
; *c
!= '\0'; c
++) {
5153 if (!(isdigit((unsigned char)*c
) || *c
== ' ' || *c
== '\t')) {
5157 sscanf(dv
.val
.str
, "%d", result
);
5162 *errMsg
= "%s called with non-integer argument";
5167 ** Get an string value from a tagged DataValue structure. Return True
5168 ** if conversion succeeded, and store result in *result, otherwise
5169 ** return False with an error message in *errMsg. If an integer value
5170 ** is converted, write the string in the space provided by "stringStorage",
5171 ** which must be large enough to handle ints of the maximum size.
5173 static int readStringArg(DataValue dv
, char **result
, char *stringStorage
,
5176 if (dv
.tag
== STRING_TAG
) {
5177 *result
= dv
.val
.str
;
5179 } else if (dv
.tag
== INT_TAG
) {
5180 sprintf(stringStorage
, "%d", dv
.val
.n
);
5181 *result
= stringStorage
;
5184 *errMsg
= "%s called with unknown object";