/* * Copyright (c) 1999 by The XFree86 Project, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the XFree86 Project shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from the * XFree86 Project. * * Author: Paulo César Pereira de Andrade */ /* $XFree86: xc/programs/xedit/hook.c,v 1.9 2003/01/08 05:07:40 paulo Exp $ */ /* * This file is intended to be used to add all the necessary hooks to xedit * emulate certain features of emacs (and other text editors) that are better * kept only in xedit, to avoid unnecessary code in the Text Widget. * * The code here is not finished, and will probably be changed frequently. */ #include "xedit.h" #include "re.h" #include "util.h" #include #include #include /* * Types */ typedef struct _ReplaceEntry { hash_key *word; struct _ReplaceEntry *next; char *replace; } ReplaceEntry; typedef enum { SubstituteDisabled, SubstituteAsk, SubstituteNo, SubstituteYes } SubstitutionState; typedef struct _EditInfo { /* Xedit regex data */ re_cod regex; re_mat mats[10]; /* Last command entered */ char command[128]; /* String and flags used to compile regex */ char pattern[64]; char subst_pattern[64]; int pat_length; int flags; /* Substitution buffer */ char subst[64]; int soff, slen, sref; /* For interactive substitution */ int callback; Widget widget; char *text_line; SubstitutionState state; XawTextPosition from, to, start, end, first, last; /* Use if need to allocate a buffer to pass the entire line to reexec */ char *line; long lsize; /* Buffer to prepare replacement, if needs to expand backreferences */ char *buffer; long bsize; } EditInfo; /* * Prototypes */ static void ActionHook(Widget, XtPointer, String, XEvent*, String*, Cardinal*); static void AutoReplaceHook(Widget, String, XEvent*); static Bool StartAutoReplace(void); static char *ReplacedWord(char*, char*); static void AutoReplace(Widget, XEvent*); static void AutoReplaceCallback(Widget, XtPointer, XtPointer); static void SubstituteHook(Widget w, String action, XEvent *event); static void SubstituteCallback(Widget, XtPointer, XtPointer); /* * Initialization */ #define STRTBLSZ 11 static hash_table *replace_hash; static EditInfo einfo; extern Widget scratch; /* * Implementation */ Bool StartHooks(XtAppContext app) { static Bool first_time = True; if (first_time) { StartAutoReplace(); (void)XtAppAddActionHook(app, ActionHook, NULL); first_time = False; return (True); } return (False); } /*ARGSUSED*/ static void ActionHook(Widget w, XtPointer client_data, String action, XEvent *event, String *params, Cardinal *num_params) { AutoReplaceHook(w, action, event); SubstituteHook(w, action, event); } /*** auto replace ***/ struct { Widget widget; String text; Cardinal length; XawTextPosition left, right; Bool replace; Bool enabled; } auto_replace; static void AutoReplaceHook(Widget w, String action, XEvent *event) { static Bool multiply; if (w != textwindow || !auto_replace.enabled) return; if (auto_replace.widget != textwindow) { if (auto_replace.replace) { auto_replace.replace = False; XtRemoveCallback(auto_replace.widget, XtNpositionCallback, AutoReplaceCallback, NULL); } } else if (strcmp(action, "multiply") == 0) { multiply = True; return; } else if (strcmp(action, "numeric") == 0) { if (multiply) return; } else if (strcmp(action, "insert-char") && strcmp(action, "newline") && strcmp(action, "newline-and-indent")) { return; } multiply = False; AutoReplace(w, event); } static Bool StartAutoReplace(void) { Bool esc; int len, llen, rlen, count = 0; char ch, *tmp, *left, *right, *replace = app_resources.auto_replace; if (!replace || !*replace) return (False); replace_hash = hash_new(STRTBLSZ, NULL); left = XtMalloc(llen = 256); right = XtMalloc(rlen = 256); while (*replace) { /* skip white spaces */ while (*replace && isspace(*replace)) ++replace; if (!*replace) break; /* read left */ tmp = replace; while (*replace && !isspace(*replace)) ++replace; len = replace - tmp; if (len >= llen) left = XtRealloc(left, llen = len + 1); strncpy(left, tmp, len); left[len] = '\0'; /* skip white spaces */ while (*replace && isspace(*replace)) ++replace; /* read right */ len = 0; esc = False; while ((ch = *replace) != '\0') { ++replace; if (len + 2 >= rlen) right = XtRealloc(right, rlen += 256); if (ch == '\\') { if (esc) right[len++] = '\\'; esc = !esc; continue; } else if (ch == '\n' && !esc) break; else right[len++] = ch; esc = False; } right[len] = '\0'; (void)ReplacedWord(left, right); ++count; } XtFree(left); XtFree(right); return (auto_replace.enabled = count > 0); } static char * ReplacedWord(char *word, char *replace) { int length; ReplaceEntry *entry; length = strlen(word); entry = (ReplaceEntry *)hash_check(replace_hash, word, length); if (entry == NULL && replace != NULL) { entry = XtNew(ReplaceEntry); entry->word = XtNew(hash_key); entry->word->value = XtNewString(word); entry->word->length = length; entry->next = NULL; entry->replace = XtNewString(replace); hash_put(replace_hash, (hash_entry *)entry); } else if (replace) { XtFree(entry->replace); entry->replace = XtNewString(replace); } return (entry ? entry->replace : NULL); } static void AutoReplace(Widget w, XEvent *event) { static XComposeStatus compose = {NULL, 0}; KeySym keysym; XawTextBlock block; XawTextPosition left, right, pos; Widget source; int i, len, size; char *str, buf[32], mb[sizeof(wchar_t)]; size = XLookupString((XKeyEvent*)event, mb, sizeof(mb), &keysym, &compose); if (size != 1 || isalnum(*mb)) return; source = XawTextGetSource(w); right = XawTextGetInsertionPoint(w); left = XawTextSourceScan(source, right, XawstWhiteSpace, XawsdLeft, 1, False); if (left < 0 || left == right) return; len = 0; str = buf; size = sizeof(buf); pos = left; while (pos < right) { pos = XawTextSourceRead(source, pos, &block, right - pos); for (i = 0; i < block.length; i++) { if (block.format == FMT8BIT) *mb = block.ptr[i]; else wctomb(mb, ((wchar_t*)block.ptr)[i]); str[len++] = *mb; if (len + 2 >= size) { if (str == buf) str = XtMalloc(size += sizeof(buf)); else str = XtRealloc(str, size += sizeof(buf)); } } } str[len] = '\0'; if ((auto_replace.text = ReplacedWord(str, NULL)) != NULL) { auto_replace.length = strlen(auto_replace.text); auto_replace.left = left; auto_replace.right = right; auto_replace.replace = True; XtAddCallback(auto_replace.widget = w, XtNpositionCallback, AutoReplaceCallback, NULL); } if (str != buf) XtFree(str); } /*ARGSUSED*/ static void AutoReplaceCallback(Widget w, XtPointer client_data, XtPointer call_data) { int i, inc; XawTextBlock block, text; char buffer[1024], mb[sizeof(wchar_t)]; XawTextPosition left, right, pos; if (!auto_replace.replace || w != auto_replace.widget) return; XtRemoveCallback(auto_replace.widget, XtNpositionCallback, AutoReplaceCallback, NULL); auto_replace.replace = False; inc = XawTextGetInsertionPoint(w) - auto_replace.right; if (auto_replace.length + inc > sizeof(buffer)) block.ptr = XtMalloc(auto_replace.length + inc); else block.ptr = buffer; memcpy(block.ptr, auto_replace.text, auto_replace.length); block.length = auto_replace.length; pos = left = auto_replace.right; right = left + inc; while (pos < right) { pos = XawTextSourceRead(XawTextGetSource(w), pos, &text, inc); for (i = 0; i < text.length; i++) { if (text.format == FMT8BIT) *mb = text.ptr[i]; else wctomb(mb, ((wchar_t*)text.ptr)[i]); block.ptr[block.length++] = *mb; } } block.firstPos = 0; block.format = FMT8BIT; if (XawTextReplace(w, auto_replace.left, auto_replace.right + inc, &block) == XawEditDone) XawTextSetInsertionPoint(w, auto_replace.left + block.length); if (block.ptr != buffer) XtFree(block.ptr); } /*ARGUSED*/ void LineEditAction(Widget w, XEvent *event, String *params, Cardinal *num_params) { XawTextBlock block; if (international) { /* XXX FIXME */ fprintf(stderr, "LineEditAction: Not working in international mode.\n"); return; } block.firstPos = 0; block.format = FMT8BIT; block.ptr = einfo.command; block.length = strlen(einfo.command); XawTextReplace(filenamewindow, 0, XawTextLastPosition(filenamewindow), &block); XtSetKeyboardFocus(topwindow, filenamewindow); line_edit = True; } void LineEdit(Widget w) { /* Global usage variables */ XawTextPosition from, to, first, last, position, length, redisplay; int replace, compile, ecode, nth, flags, count, etype; char *command, *line, buffer[128]; XawTextBlock block; Widget source; XawTextScanDirection direction; xedit_flist_item *item; /* Variables used while parsing command */ int state, action, offset, icase, confirm; long lfrom, lto, lfinc, ltinc, number; char *ptr, *pstart, *pend, *rstart, *rend, *tmp; /* Variables used in the search/replace loop */ int len; XawTextPosition adjust = 0; command = GetString(filenamewindow); length = strlen(command); if (length >= sizeof(einfo.command)) { Feep(); return; } item = FindTextSource(XawTextGetSource(w), NULL); source = item->source; position = XawTextGetInsertionPoint(w); first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True); last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True); compile = redisplay = nth = count = confirm = 0; direction = XawsdRight; flags = RE_STARTEND; /* Error types */ #define T_NONE 0 #define T_OPTION 1 #define T_ICASE 2 #define T_COMMAND 3 #define T_REPLACE 4 #define T_SEARCH 5 #define T_BACKSLASH 6 #define T_DIRECTION 7 #define T_COMMA 8 #define T_OFFSET 9 #define T_INCREMENT 10 #define T_NUMBER 11 #define T_UNFINISHED 12 #define T_RANGE 13 #define T_BACKREF 14 #define T_EDIT 15 etype = T_NONE; #define FAIL(code) { etype = code; goto fail; } /* Value for the line value, anything else is the line number */ #define L_FIRST -1 #define L_CURRENT -2 #define L_LAST -3 lfrom = L_FIRST; lto = L_LAST; /* Parsing states */ #define E_FINC 0 #define E_FROM 1 #define E_COMMA 2 #define E_TINC 3 #define E_TO 4 #define E_COMMAND 5 #define E_REGEX 6 #define E_SUBST 7 #define E_OPTIONS 8 state = E_FROM; /* Beginning interpretation */ /* Known commands */ #define A_SEARCH 0 #define A_REPLACE 1 action = A_SEARCH; /* Flag to replace all occurrences */ #define O_ALL -1 number = 1; lfinc = ltinc = 0; icase = offset = 0; pstart = pend = rstart = rend = NULL; if (einfo.state != SubstituteDisabled) { if (einfo.widget != w || strcmp(einfo.command, command)) { einfo.widget = w; einfo.state = SubstituteAsk; } else { XawTextPosition s_start, s_end; XawTextGetSelectionPos(w, &s_start, &s_end); if (s_start != einfo.start || s_end != einfo.end) einfo.state = SubstituteAsk; confirm = replace = 1; from = einfo.from; to = einfo.to; first = einfo.first; last = einfo.last; goto confirm_label; } } /* Remember last command */ strcpy(einfo.command, command); /* Loop parsing command */ for (ptr = einfo.command; *ptr;) { switch (*ptr++) { case 'c': if (state != E_OPTIONS && state != E_COMMAND && state != E_REGEX) FAIL(T_OPTION) confirm = 1; break; case 'g': if (state != E_OPTIONS && state != E_COMMAND && state != E_REGEX) FAIL(T_OPTION) offset = O_ALL; break; case 'i': if (state != E_OPTIONS && state != E_COMMAND && state != E_REGEX && state != E_FROM) FAIL(T_ICASE) icase = 1; break; case 's': if (state == E_FROM) lfrom = lto = L_CURRENT; else if (state == E_COMMA) { lto = L_CURRENT; ltinc = lfinc; } else if (state == E_TO) lto = L_LAST; else if (state == E_FINC) { ltinc = lfinc; lto = L_CURRENT; } else if (state != E_COMMAND && state != E_TINC) FAIL(T_COMMAND) action = A_REPLACE; state = E_REGEX; break; case '?': if (action == A_REPLACE) FAIL(T_REPLACE) case '/': if (state == E_TINC) state = action == A_REPLACE ? E_REGEX : E_FROM; else if (state == E_COMMA || state == E_FINC) { lto = L_LAST; state = E_FROM; } else if (state == E_TO) { if (ltinc == 0) lto = L_LAST; state = E_FROM; } else if (state == E_COMMAND) state = E_FROM; else if (state != E_REGEX && state != E_SUBST && state != E_FROM) FAIL(T_SEARCH) if (state != E_SUBST) direction = ptr[-1] == '/' ? XawsdRight : XawsdLeft; for (tmp = ptr; *tmp; tmp++) { if (*tmp == '\\') { if (*++tmp == '\0') FAIL(T_BACKSLASH) } else if (*tmp == ptr[-1]) break; } if (state == E_REGEX) { if (*tmp != ptr[-1]) FAIL(T_DIRECTION) pstart = ptr; pend = ptr = tmp; state = E_SUBST; } else if (state == E_FROM) { pstart = ptr; pend = ptr = tmp; state = E_OPTIONS; if (*ptr) ++ptr; } else { /* E_SUBST */ rstart = ptr; rend = tmp; state = E_OPTIONS; ptr = tmp; if (*ptr) ++ptr; } break; case ',': if (state == E_FROM) lfrom = L_FIRST; else if (state == E_FINC) lfrom = L_CURRENT; else if (state != E_COMMA) FAIL(T_COMMA) state = E_TO; break; case '%': if (state == E_FROM) { lfrom = L_FIRST; lto = L_LAST; state = E_COMMAND; } else FAIL(T_OFFSET) break; case '$': if (state != E_TO) FAIL(T_OFFSET) lto = L_LAST; state = E_COMMAND; break; case '.': if (state == E_FROM) { lfrom = L_CURRENT; state = E_COMMA; } else if (state == E_TO) { lto = L_CURRENT; state = E_COMMAND; } else FAIL(T_OFFSET) break; case '+': if (state == E_FROM) { lfinc = 1; lfrom = L_CURRENT; state = E_FINC; } else if (state == E_TO) { ltinc = 1; lto = L_CURRENT; state = E_TINC; } else FAIL(T_INCREMENT) break; case '-': case '^': if (state == E_FROM) { lfinc = -1; lfrom = L_CURRENT; state = E_FINC; } else if (state == E_TO) { ltinc = -1; lto = L_CURRENT; state = E_TINC; } else FAIL(T_INCREMENT) number = -1; break; case ';': if (state != E_FROM) FAIL(T_OFFSET) lfrom = L_CURRENT; lto = L_LAST; state = E_COMMAND; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': number = number * (ptr[-1] - '0'); while (isdigit(*ptr)) number = number * 10 + (*ptr++ - '0'); if (state == E_FROM) { lfrom = number; state = E_COMMA; } else if (state == E_FINC) { lfinc = number; state = E_COMMA; } else if (state == E_TO) { lto = number; state = E_COMMAND; } else if (state == E_TINC) { ltinc = number; state = E_COMMAND; } else if (state == E_OPTIONS && action == A_REPLACE) offset = number - 1; else FAIL(T_NUMBER) number = 1; break; case '\0': if (state == E_OPTIONS) break; default: FAIL(T_UNFINISHED) } } replace = action == A_REPLACE; switch (lfrom) { case L_FIRST: from = first; break; case L_LAST: from = LSCAN(last, 1, False); break; case L_CURRENT: if (lfinc <= 0) from = LSCAN(position, -lfinc + 1, False); else { from = RSCAN(position, lfinc + 1, False); from = LSCAN(from, 1, False); } break; default: from = RSCAN(first, lfrom, False); from = LSCAN(from, 1, False); break; } /* Just requesting to go to the numbered line */ if (state == E_COMMA || state == E_FINC) { XawTextSetInsertionPoint(w, from); return; } length = pend - pstart; if (pstart == NULL || (replace && rstart == NULL) || length >= sizeof(einfo.pattern) - 1) FAIL(T_UNFINISHED) /* Need to (re)compile regular expression pattern? */ if ((!!(einfo.flags & RE_ICASE) ^ icase) || einfo.pat_length != length || memcmp(pstart, einfo.pattern, length > einfo.pat_length ? einfo.pat_length : length)) { compile = 1; memcpy(einfo.pattern, pstart, length); einfo.pattern[length] = '\0'; einfo.flags = icase ? RE_ICASE : 0; } /* Check range of lines to operate on */ switch (lto) { case L_FIRST: to = RSCAN(first, 1, True); break; case L_LAST: to = last; break; case L_CURRENT: if (ltinc < 0) { to = LSCAN(position, -ltinc + 1, True); to = RSCAN(to, 2, True); } else to = RSCAN(position, ltinc + 1, True); break; default: to = RSCAN(first, lto, True); break; } if (from >= to) FAIL(T_RANGE) /* Set first and last position allowed to search/replace */ first = from; last = to; /* Check bounds to work on */ if (replace) { int i, j, ch; /* Check number of required match results and remove/parse backslashes */ einfo.slen = rend - rstart; einfo.sref = 0; einfo.soff = offset; for (i = j = 0; i < einfo.slen; i++) { ch = rstart[i]; if (ch == '\\') { ++i; switch (rstart[i]) { case '0': ch = '\0'; break; case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'v': ch = '\v'; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': einfo.subst[j++] = '\\'; if (rstart[i] - '0' > einfo.sref) einfo.sref = rstart[i] - '0'; /* FALLTHROUGH */ default: ch = rstart[i]; break; } } einfo.subst[j++] = ch; } einfo.slen = j; } else if (einfo.widget != w) { /* Just a flag for backward search */ einfo.from = last; einfo.widget = w; } /* Compile pattern if required */ if (compile) { int ch; char *eptr, *pptr; /* Parse backslashes */ pptr = einfo.subst_pattern; for (eptr = einfo.pattern, ch = *eptr++; ch; ch = *eptr++) { if (ch == '\\') { switch (*eptr) { case '0': ch = '\0'; einfo.flags |= RE_PEND; break; case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'v': ch = '\v'; break; default: break; } if (ch != '\\') ++eptr; } *pptr++ = ch; } *pptr = '\0'; refree(&einfo.regex); /* Allow nuls in search regex */ einfo.regex.re_endp = pptr; ecode = recomp(&einfo.regex, einfo.subst_pattern, einfo.flags); if (ecode) goto print; } if (!replace && position >= first && position <= last) { from = position; /* The backwards repetition currently is only backwards when * changing lines, so remember from where started, to also * search in the first line. */ if (LSCAN(from, 1, False) == from) { if (direction == XawsdLeft) einfo.from = from; } else flags |= RE_NOTBOL; } to = RSCAN(from, 1, True); if (confirm) { if (!replace) FAIL(T_UNFINISHED) einfo.widget = w; einfo.state = SubstituteAsk; einfo.from = from; einfo.to = to; einfo.first = first; einfo.last = last; } else einfo.state = SubstituteDisabled; confirm_label: if (replace) { redisplay = 1; XawTextDisableRedisplay(w); } for (;;) { if (confirm && einfo.state != SubstituteAsk) { /* Restore state from previous call */ ecode = 0; nth = einfo.soff; /* einfo.mats should not have changed */ if (einfo.state == SubstituteYes) { einfo.state = SubstituteAsk; line = einfo.text_line; goto substitute_label; } else { ++nth; einfo.state = SubstituteAsk; from = einfo.from = einfo.end; goto no_substitute_label; } } /* Read or use a line of text inplace */ position = from; length = to - from; XawTextSourceRead(source, position, &block, to - position); if (block.length >= length) line = block.ptr; else { if (length > einfo.lsize) { einfo.line = XtRealloc(einfo.line, to - from); einfo.lsize = to - from; } memcpy(einfo.line, block.ptr, block.length); length = block.length; for (position += length; position < to && block.length; position += block.length) { XawTextSourceRead(source, position, &block, to - position); memcpy(einfo.line + length, block.ptr, block.length); length += block.length; } line = einfo.line; } /* Execute expression */ einfo.mats[0].rm_so = 0; einfo.mats[0].rm_eo = to - from; /* If not last line or if it ends in a newline */ if (to != from) { if (to < last || (to > from && line[einfo.mats[0].rm_eo - 1] == '\n')) --einfo.mats[0].rm_eo; ecode = reexec(&einfo.regex, line, einfo.sref + 1, &einfo.mats[0], flags); if (replace && einfo.mats[0].rm_so == einfo.mats[0].rm_eo) /* Ignore empty matches */ ecode = RE_NOMATCH; if (ecode == 0 && confirm && (einfo.soff == O_ALL || nth == einfo.soff)) { einfo.end = from + einfo.mats[0].rm_eo; einfo.start = from + einfo.mats[0].rm_so; XawTextSetInsertionPoint(w, einfo.end); XawTextSetSelection(w, einfo.start, einfo.end); einfo.state = SubstituteAsk; einfo.from = from; einfo.to = to; einfo.first = first; einfo.last = last; einfo.text_line = line; break; } } else /* Check bellow will update offsets */ ecode = RE_NOMATCH; substitute_label: if (ecode == 0) { from += einfo.mats[0].rm_so; len = einfo.mats[0].rm_eo - einfo.mats[0].rm_so; /* Found match */ if (replace) { /* If not replacing all ocurrences, or if not * at the correct offset */ if (einfo.soff != O_ALL && nth < einfo.soff) { from += len; ++nth; continue; } /* Do the substitution */ block.firstPos = 0; block.format = FMT8BIT; if (einfo.sref) { /* Hard way */ int i, ref, xlen; for (i = length = 0; i < einfo.slen; i++) { if (length + 2 >= einfo.bsize) { einfo.bsize = einfo.bsize + 1024; einfo.buffer = XtRealloc(einfo.buffer, einfo.bsize); } if (einfo.subst[i] == '\\') { ++i; if (einfo.subst[i] >= '1' && einfo.subst[i] <= '9') { ref = einfo.subst[i] - '0'; xlen = einfo.mats[ref].rm_eo - einfo.mats[ref].rm_so; if (xlen < 0) /* Oops, something went wrong... */ FAIL(T_BACKREF) if (length + xlen >= einfo.bsize) { einfo.bsize += xlen + 1024 - (xlen % 1024); einfo.buffer = XtRealloc(einfo.buffer, einfo.bsize); } memcpy(einfo.buffer + length, line + einfo.mats[ref].rm_so, xlen); length += xlen; } else { einfo.buffer[length++] = einfo.subst[i - 1]; einfo.buffer[length++] = einfo.subst[i]; } } else einfo.buffer[length++] = einfo.subst[i]; } block.ptr = einfo.buffer; block.length = length; } else { block.ptr = einfo.subst; block.length = length = einfo.slen; } adjust = length - len; if (XawTextReplace(w, from, from + len, &block) != XawEditDone) FAIL(T_EDIT) last += adjust; to += adjust; from += length; no_substitute_label: if (einfo.soff != O_ALL) { nth = 0; to = RSCAN(from, 1, True); from = LSCAN(to, 1, False); if (to == last) { XawTextSetInsertionPoint(w, from); break; } } else flags |= RE_NOTBOL; } else { XawTextSetInsertionPoint(w, from + len); XawTextSetSelection(w, from, from + len); break; } } else if (ecode == RE_NOMATCH) { nth = 0; /* Try again in the next/previous line */ if (direction == XawsdLeft) { from = LSCAN(to - 1, 1 + (from != to), False); if (einfo.from <= first) { Feep(); if (++count > 1) { XawTextSetInsertionPoint(w, position); XawTextUnsetSelection(w); break; } from = LSCAN(last, 1, False); } to = RSCAN(from, 1, True); /* Can use einfo.from because replace is only done forward */ einfo.from = from; } else { if (to >= last) { Feep(); if (replace || ++count > 1) { XawTextSetInsertionPoint(w, position); XawTextUnsetSelection(w); einfo.state = SubstituteDisabled; confirm = 0; break; } to = first; } from = LSCAN(to + 1, 1, False); to = RSCAN(from, 1, True); } /* Reset flags now */ flags = RE_STARTEND; } else goto print; } if (redisplay) XawTextEnableRedisplay(w); /* If replacing not interatively return to the edit window after finished */ if (replace && !confirm) { Arg args[1]; XtSetKeyboardFocus(topwindow, textwindow); if (item->source != scratch) XtSetArg(args[0], XtNstring, item->name); else XtSetArg(args[0], XtNstring, NULL); XtSetValues(filenamewindow, args, 1); line_edit = False; } return; print: if (redisplay) XawTextEnableRedisplay(w); strcpy(buffer, "Regex error: "); length = 13; reerror(ecode, &einfo.regex, buffer + length, sizeof(buffer) - length - 2); strcat(buffer, "\n"); XeditPrintf("%s", buffer); refree(&einfo.regex); einfo.state = SubstituteDisabled; Feep(); return; fail: if (etype != T_NONE) { const char *errptr; switch (etype) { case T_OPTION: errptr = "Option needs a command"; break; case T_ICASE: errptr = "Icase needs an command defined or none for search"; break; case T_COMMAND: errptr = "Command incorrectly specified"; break; case T_REPLACE: errptr = "Can only search backwards"; break; case T_SEARCH: errptr = "Badly placed search/replace specifier"; break; case T_BACKSLASH: errptr = "A single backslash cannot be the last command character"; break; case T_DIRECTION: errptr = "Regular expression must be separeted by / or ? not both"; break; case T_COMMA: errptr = "Badly placed comma"; break; case T_OFFSET: errptr = "Badly placed line offset specifier"; break; case T_INCREMENT: errptr = "Badly placed line offset increment specifier"; break; case T_NUMBER: errptr = "Numeric argument not expected"; break; case T_UNFINISHED: errptr = "Unfinished command"; break; case T_RANGE: errptr = "Bad line range"; break; case T_BACKREF: /* This may be an internal re error, but most likely the * user asked for something like "s/re0(re1)re2/\2/" */ errptr = "Bad backreference"; break; case T_EDIT: errptr = "Failed to replace text"; break; default: errptr = "Unknown error"; break; } XeditPrintf("Error: %s.\n", errptr); } if (redisplay) XawTextEnableRedisplay(w); einfo.state = SubstituteDisabled; Feep(); } static void SubstituteHook(Widget w, String action, XEvent *event) { if (w != filenamewindow) return; if (line_edit && einfo.state == SubstituteAsk) { if (strcmp(action, "newline") == 0 || strcmp(action, "load-file") == 0) einfo.state = SubstituteAsk; else if (strcmp(action, "insert-char") == 0) { static XComposeStatus compose = {NULL, 0}; KeySym keysym; char mb[sizeof(wchar_t)]; if (XLookupString((XKeyEvent*)event, mb, sizeof(mb), &keysym, &compose) == 1) { if (*mb == 'y' || *mb == 'Y') einfo.state = SubstituteYes; else if (*mb == 'n' || *mb == 'N') einfo.state = SubstituteNo; else einfo.state = SubstituteDisabled; if (einfo.state != SubstituteDisabled) { einfo.callback = 1; XtAddCallback(filenamewindow, XtNpositionCallback, SubstituteCallback, NULL); } } } else if (strcmp(action, "cancel-find-file") == 0) einfo.state = SubstituteDisabled; } if (einfo.state == SubstituteDisabled && einfo.callback) { einfo.callback = 0; XtRemoveCallback(filenamewindow, XtNpositionCallback, SubstituteCallback, NULL); } } /*ARGSUSED*/ static void SubstituteCallback(Widget w, XtPointer client_data, XtPointer call_data) { XawTextBlock block; einfo.callback = 0; XtRemoveCallback(filenamewindow, XtNpositionCallback, SubstituteCallback, NULL); block.firstPos = 0; block.format = FMT8BIT; block.ptr = einfo.command; block.length = strlen(einfo.command); XawTextReplace(filenamewindow, 0, XawTextLastPosition(filenamewindow), &block); LineEdit(einfo.widget); }