hook.c revision f765521f
15dfecf96Smrg/* 25dfecf96Smrg * Copyright (c) 1999 by The XFree86 Project, Inc. 35dfecf96Smrg * 45dfecf96Smrg * Permission is hereby granted, free of charge, to any person obtaining a 55dfecf96Smrg * copy of this software and associated documentation files (the "Software"), 65dfecf96Smrg * to deal in the Software without restriction, including without limitation 75dfecf96Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 85dfecf96Smrg * and/or sell copies of the Software, and to permit persons to whom the 95dfecf96Smrg * Software is furnished to do so, subject to the following conditions: 105dfecf96Smrg * 115dfecf96Smrg * The above copyright notice and this permission notice shall be included in 125dfecf96Smrg * all copies or substantial portions of the Software. 135dfecf96Smrg * 145dfecf96Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 155dfecf96Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 165dfecf96Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 175dfecf96Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 185dfecf96Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 195dfecf96Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 205dfecf96Smrg * SOFTWARE. 215dfecf96Smrg * 225dfecf96Smrg * Except as contained in this notice, the name of the XFree86 Project shall 235dfecf96Smrg * not be used in advertising or otherwise to promote the sale, use or other 245dfecf96Smrg * dealings in this Software without prior written authorization from the 255dfecf96Smrg * XFree86 Project. 265dfecf96Smrg * 275dfecf96Smrg * Author: Paulo César Pereira de Andrade 285dfecf96Smrg */ 295dfecf96Smrg 305dfecf96Smrg/* $XFree86: xc/programs/xedit/hook.c,v 1.9 2003/01/08 05:07:40 paulo Exp $ */ 315dfecf96Smrg 325dfecf96Smrg/* 335dfecf96Smrg * This file is intended to be used to add all the necessary hooks to xedit 345dfecf96Smrg * emulate certain features of emacs (and other text editors) that are better 355dfecf96Smrg * kept only in xedit, to avoid unnecessary code in the Text Widget. 365dfecf96Smrg * 375dfecf96Smrg * The code here is not finished, and will probably be changed frequently. 385dfecf96Smrg */ 395dfecf96Smrg 405dfecf96Smrg#include "xedit.h" 415dfecf96Smrg#include "re.h" 42f14f4646Smrg#include "util.h" 435dfecf96Smrg#include <stdlib.h> 445dfecf96Smrg#include <string.h> 455dfecf96Smrg#include <ctype.h> 465dfecf96Smrg 475dfecf96Smrg/* 485dfecf96Smrg * Types 495dfecf96Smrg */ 50f14f4646Smrgtypedef struct _ReplaceEntry { 51f14f4646Smrg hash_key *word; 52f14f4646Smrg struct _ReplaceEntry *next; 535dfecf96Smrg char *replace; 54f14f4646Smrg} ReplaceEntry; 555dfecf96Smrg 565dfecf96Smrgtypedef enum { 575dfecf96Smrg SubstituteDisabled, 585dfecf96Smrg SubstituteAsk, 595dfecf96Smrg SubstituteNo, 605dfecf96Smrg SubstituteYes 615dfecf96Smrg} SubstitutionState; 625dfecf96Smrg 635dfecf96Smrgtypedef struct _EditInfo { 645dfecf96Smrg /* Xedit regex data */ 655dfecf96Smrg re_cod regex; 665dfecf96Smrg re_mat mats[10]; 675dfecf96Smrg 685dfecf96Smrg /* Last command entered */ 695dfecf96Smrg char command[128]; 705dfecf96Smrg 715dfecf96Smrg /* String and flags used to compile regex */ 725dfecf96Smrg char pattern[64]; 73f14f4646Smrg char subst_pattern[64]; 74f14f4646Smrg int pat_length; 755dfecf96Smrg int flags; 765dfecf96Smrg 775dfecf96Smrg /* Substitution buffer */ 785dfecf96Smrg char subst[64]; 795dfecf96Smrg int soff, slen, sref; 805dfecf96Smrg 815dfecf96Smrg /* For interactive substitution */ 825dfecf96Smrg int callback; 835dfecf96Smrg Widget widget; 845dfecf96Smrg char *text_line; 855dfecf96Smrg SubstitutionState state; 865dfecf96Smrg XawTextPosition from, to, start, end, first, last; 875dfecf96Smrg 885dfecf96Smrg /* Use if need to allocate a buffer to pass the entire line to reexec */ 895dfecf96Smrg char *line; 905dfecf96Smrg long lsize; 915dfecf96Smrg 925dfecf96Smrg /* Buffer to prepare replacement, if needs to expand backreferences */ 935dfecf96Smrg char *buffer; 945dfecf96Smrg long bsize; 955dfecf96Smrg} EditInfo; 965dfecf96Smrg 975dfecf96Smrg/* 985dfecf96Smrg * Prototypes 995dfecf96Smrg */ 1005dfecf96Smrgstatic void ActionHook(Widget, XtPointer, String, XEvent*, String*, Cardinal*); 1015dfecf96Smrgstatic void AutoReplaceHook(Widget, String, XEvent*); 1025dfecf96Smrgstatic Bool StartAutoReplace(void); 1035dfecf96Smrgstatic char *ReplacedWord(char*, char*); 1045dfecf96Smrgstatic void AutoReplace(Widget, XEvent*); 1055dfecf96Smrgstatic void AutoReplaceCallback(Widget, XtPointer, XtPointer); 1065dfecf96Smrg 1075dfecf96Smrgstatic void SubstituteHook(Widget w, String action, XEvent *event); 1085dfecf96Smrgstatic void SubstituteCallback(Widget, XtPointer, XtPointer); 1095dfecf96Smrg 1105dfecf96Smrg/* 1115dfecf96Smrg * Initialization 1125dfecf96Smrg */ 1135dfecf96Smrg#define STRTBLSZ 11 114f14f4646Smrgstatic hash_table *replace_hash; 1155dfecf96Smrgstatic EditInfo einfo; 1165dfecf96Smrgextern Widget scratch; 1175dfecf96Smrg 1185dfecf96Smrg/* 1195dfecf96Smrg * Implementation 1205dfecf96Smrg */ 1215dfecf96SmrgBool 1225dfecf96SmrgStartHooks(XtAppContext app) 1235dfecf96Smrg{ 1245dfecf96Smrg static Bool first_time = True; 1255dfecf96Smrg 1265dfecf96Smrg if (first_time) { 1275dfecf96Smrg StartAutoReplace(); 1285dfecf96Smrg (void)XtAppAddActionHook(app, ActionHook, NULL); 1295dfecf96Smrg first_time = False; 1305dfecf96Smrg 1315dfecf96Smrg return (True); 1325dfecf96Smrg } 1335dfecf96Smrg return (False); 1345dfecf96Smrg} 1355dfecf96Smrg 1365dfecf96Smrg/*ARGSUSED*/ 1375dfecf96Smrgstatic void 1385dfecf96SmrgActionHook(Widget w, XtPointer client_data, String action, XEvent *event, 1395dfecf96Smrg String *params, Cardinal *num_params) 1405dfecf96Smrg{ 1415dfecf96Smrg AutoReplaceHook(w, action, event); 1425dfecf96Smrg SubstituteHook(w, action, event); 1435dfecf96Smrg} 1445dfecf96Smrg 1455dfecf96Smrg/*** auto replace ***/ 1465dfecf96Smrgstruct { 1475dfecf96Smrg Widget widget; 1485dfecf96Smrg String text; 1495dfecf96Smrg Cardinal length; 1505dfecf96Smrg XawTextPosition left, right; 1515dfecf96Smrg Bool replace; 1525dfecf96Smrg Bool enabled; 1535dfecf96Smrg} auto_replace; 1545dfecf96Smrg 1555dfecf96Smrgstatic void 1565dfecf96SmrgAutoReplaceHook(Widget w, String action, XEvent *event) 1575dfecf96Smrg{ 1585dfecf96Smrg static Bool multiply; 1595dfecf96Smrg 1605dfecf96Smrg if (w != textwindow || !auto_replace.enabled) 1615dfecf96Smrg return; 1625dfecf96Smrg 1635dfecf96Smrg if (auto_replace.widget != textwindow) { 1645dfecf96Smrg if (auto_replace.replace) { 1655dfecf96Smrg auto_replace.replace = False; 1665dfecf96Smrg XtRemoveCallback(auto_replace.widget, XtNpositionCallback, 1675dfecf96Smrg AutoReplaceCallback, NULL); 1685dfecf96Smrg } 1695dfecf96Smrg } 1705dfecf96Smrg else if (strcmp(action, "multiply") == 0) { 1715dfecf96Smrg multiply = True; 1725dfecf96Smrg return; 1735dfecf96Smrg } 1745dfecf96Smrg else if (strcmp(action, "numeric") == 0) { 1755dfecf96Smrg if (multiply) 1765dfecf96Smrg return; 1775dfecf96Smrg } 1785dfecf96Smrg else if (strcmp(action, "insert-char") && strcmp(action, "newline") && 1795dfecf96Smrg strcmp(action, "newline-and-indent")) { 1805dfecf96Smrg return; 1815dfecf96Smrg } 1825dfecf96Smrg multiply = False; 1835dfecf96Smrg 1845dfecf96Smrg AutoReplace(w, event); 1855dfecf96Smrg} 1865dfecf96Smrg 1875dfecf96Smrgstatic Bool 1885dfecf96SmrgStartAutoReplace(void) 1895dfecf96Smrg{ 1905dfecf96Smrg Bool esc; 1915dfecf96Smrg int len, llen, rlen, count = 0; 1925dfecf96Smrg char ch, *tmp, *left, *right, *replace = app_resources.auto_replace; 1935dfecf96Smrg 1945dfecf96Smrg if (!replace || !*replace) 1955dfecf96Smrg return (False); 1965dfecf96Smrg 197f14f4646Smrg replace_hash = hash_new(STRTBLSZ, NULL); 198f14f4646Smrg 1995dfecf96Smrg left = XtMalloc(llen = 256); 2005dfecf96Smrg right = XtMalloc(rlen = 256); 2015dfecf96Smrg while (*replace) { 2025dfecf96Smrg /* skip white spaces */ 2035dfecf96Smrg while (*replace && isspace(*replace)) 2045dfecf96Smrg ++replace; 2055dfecf96Smrg if (!*replace) 2065dfecf96Smrg break; 2075dfecf96Smrg 2085dfecf96Smrg /* read left */ 2095dfecf96Smrg tmp = replace; 2105dfecf96Smrg while (*replace && !isspace(*replace)) 2115dfecf96Smrg ++replace; 2125dfecf96Smrg len = replace - tmp; 2135dfecf96Smrg if (len >= llen) 2145dfecf96Smrg left = XtRealloc(left, llen = len + 1); 2155dfecf96Smrg strncpy(left, tmp, len); 2165dfecf96Smrg left[len] = '\0'; 2175dfecf96Smrg 2185dfecf96Smrg /* skip white spaces */ 2195dfecf96Smrg while (*replace && isspace(*replace)) 2205dfecf96Smrg ++replace; 2215dfecf96Smrg 2225dfecf96Smrg /* read right */ 2235dfecf96Smrg len = 0; 2245dfecf96Smrg esc = False; 2255dfecf96Smrg while ((ch = *replace) != '\0') { 2265dfecf96Smrg ++replace; 2275dfecf96Smrg if (len + 2 >= rlen) 2285dfecf96Smrg right = XtRealloc(right, rlen += 256); 2295dfecf96Smrg if (ch == '\\') { 2305dfecf96Smrg if (esc) 2315dfecf96Smrg right[len++] = '\\'; 2325dfecf96Smrg esc = !esc; 2335dfecf96Smrg continue; 2345dfecf96Smrg } 2355dfecf96Smrg else if (ch == '\n' && !esc) 2365dfecf96Smrg break; 2375dfecf96Smrg else 2385dfecf96Smrg right[len++] = ch; 2395dfecf96Smrg esc = False; 2405dfecf96Smrg } 2415dfecf96Smrg right[len] = '\0'; 2425dfecf96Smrg 2435dfecf96Smrg (void)ReplacedWord(left, right); 2445dfecf96Smrg ++count; 2455dfecf96Smrg } 2465dfecf96Smrg XtFree(left); 2475dfecf96Smrg XtFree(right); 2485dfecf96Smrg 2495dfecf96Smrg return (auto_replace.enabled = count > 0); 2505dfecf96Smrg} 2515dfecf96Smrg 2525dfecf96Smrgstatic char * 2535dfecf96SmrgReplacedWord(char *word, char *replace) 2545dfecf96Smrg{ 255f14f4646Smrg int length; 256f14f4646Smrg ReplaceEntry *entry; 257f14f4646Smrg 258f14f4646Smrg length = strlen(word); 259f14f4646Smrg entry = (ReplaceEntry *)hash_check(replace_hash, word, length); 260f14f4646Smrg if (entry == NULL && replace != NULL) { 261f14f4646Smrg entry = XtNew(ReplaceEntry); 262f14f4646Smrg entry->word = XtNew(hash_key); 263f14f4646Smrg entry->word->value = XtNewString(word); 264f14f4646Smrg entry->word->length = length; 265f14f4646Smrg entry->next = NULL; 266f14f4646Smrg entry->replace = XtNewString(replace); 267f14f4646Smrg hash_put(replace_hash, (hash_entry *)entry); 268f14f4646Smrg } 269f14f4646Smrg else if (replace) { 270f14f4646Smrg XtFree(entry->replace); 271f14f4646Smrg entry->replace = XtNewString(replace); 272f14f4646Smrg } 2735dfecf96Smrg 274f14f4646Smrg return (entry ? entry->replace : NULL); 2755dfecf96Smrg} 2765dfecf96Smrg 2775dfecf96Smrgstatic void 2785dfecf96SmrgAutoReplace(Widget w, XEvent *event) 2795dfecf96Smrg{ 2805dfecf96Smrg static XComposeStatus compose = {NULL, 0}; 2815dfecf96Smrg KeySym keysym; 2825dfecf96Smrg XawTextBlock block; 2835dfecf96Smrg XawTextPosition left, right, pos; 2845dfecf96Smrg Widget source; 2855dfecf96Smrg int i, len, size; 2865dfecf96Smrg char *str, buf[32], mb[sizeof(wchar_t)]; 2875dfecf96Smrg 2885dfecf96Smrg size = XLookupString((XKeyEvent*)event, mb, sizeof(mb), &keysym, &compose); 2895dfecf96Smrg 2905dfecf96Smrg if (size != 1 || isalnum(*mb)) 2915dfecf96Smrg return; 2925dfecf96Smrg 2935dfecf96Smrg source = XawTextGetSource(w); 2945dfecf96Smrg right = XawTextGetInsertionPoint(w); 2955dfecf96Smrg left = XawTextSourceScan(source, right, XawstWhiteSpace, 2965dfecf96Smrg XawsdLeft, 1, False); 2975dfecf96Smrg 2985dfecf96Smrg if (left < 0 || left == right) 2995dfecf96Smrg return; 3005dfecf96Smrg 3015dfecf96Smrg len = 0; 3025dfecf96Smrg str = buf; 3035dfecf96Smrg size = sizeof(buf); 3045dfecf96Smrg pos = left; 3055dfecf96Smrg while (pos < right) { 3065dfecf96Smrg pos = XawTextSourceRead(source, pos, &block, right - pos); 3075dfecf96Smrg for (i = 0; i < block.length; i++) { 3085dfecf96Smrg if (block.format == FMT8BIT) 3095dfecf96Smrg *mb = block.ptr[i]; 3105dfecf96Smrg else 3115dfecf96Smrg wctomb(mb, ((wchar_t*)block.ptr)[i]); 3125dfecf96Smrg str[len++] = *mb; 3135dfecf96Smrg if (len + 2 >= size) { 3145dfecf96Smrg if (str == buf) 3155dfecf96Smrg str = XtMalloc(size += sizeof(buf)); 3165dfecf96Smrg else 3175dfecf96Smrg str = XtRealloc(str, size += sizeof(buf)); 3185dfecf96Smrg } 3195dfecf96Smrg } 3205dfecf96Smrg } 3215dfecf96Smrg str[len] = '\0'; 3225dfecf96Smrg if ((auto_replace.text = ReplacedWord(str, NULL)) != NULL) { 3235dfecf96Smrg auto_replace.length = strlen(auto_replace.text); 3245dfecf96Smrg auto_replace.left = left; 3255dfecf96Smrg auto_replace.right = right; 3265dfecf96Smrg auto_replace.replace = True; 3275dfecf96Smrg XtAddCallback(auto_replace.widget = w, XtNpositionCallback, 3285dfecf96Smrg AutoReplaceCallback, NULL); 3295dfecf96Smrg } 3305dfecf96Smrg if (str != buf) 3315dfecf96Smrg XtFree(str); 3325dfecf96Smrg} 3335dfecf96Smrg 3345dfecf96Smrg/*ARGSUSED*/ 3355dfecf96Smrgstatic void 3365dfecf96SmrgAutoReplaceCallback(Widget w, XtPointer client_data, XtPointer call_data) 3375dfecf96Smrg{ 3385dfecf96Smrg int i, inc; 3395dfecf96Smrg XawTextBlock block, text; 3405dfecf96Smrg char buffer[1024], mb[sizeof(wchar_t)]; 3415dfecf96Smrg XawTextPosition left, right, pos; 3425dfecf96Smrg 3435dfecf96Smrg if (!auto_replace.replace || w != auto_replace.widget) 3445dfecf96Smrg return; 3455dfecf96Smrg 3465dfecf96Smrg XtRemoveCallback(auto_replace.widget, XtNpositionCallback, 3475dfecf96Smrg AutoReplaceCallback, NULL); 3485dfecf96Smrg auto_replace.replace = False; 3495dfecf96Smrg 3505dfecf96Smrg inc = XawTextGetInsertionPoint(w) - auto_replace.right; 3515dfecf96Smrg if (auto_replace.length + inc > sizeof(buffer)) 3525dfecf96Smrg block.ptr = XtMalloc(auto_replace.length + inc); 3535dfecf96Smrg else 3545dfecf96Smrg block.ptr = buffer; 3555dfecf96Smrg memcpy(block.ptr, auto_replace.text, auto_replace.length); 3565dfecf96Smrg 3575dfecf96Smrg block.length = auto_replace.length; 3585dfecf96Smrg pos = left = auto_replace.right; 3595dfecf96Smrg right = left + inc; 3605dfecf96Smrg while (pos < right) { 3615dfecf96Smrg pos = XawTextSourceRead(XawTextGetSource(w), pos, &text, inc); 3625dfecf96Smrg for (i = 0; i < text.length; i++) { 3635dfecf96Smrg if (text.format == FMT8BIT) 3645dfecf96Smrg *mb = text.ptr[i]; 3655dfecf96Smrg else 3665dfecf96Smrg wctomb(mb, ((wchar_t*)text.ptr)[i]); 3675dfecf96Smrg block.ptr[block.length++] = *mb; 3685dfecf96Smrg } 3695dfecf96Smrg } 3705dfecf96Smrg 3715dfecf96Smrg block.firstPos = 0; 3725dfecf96Smrg block.format = FMT8BIT; 3735dfecf96Smrg 3745dfecf96Smrg if (XawTextReplace(w, auto_replace.left, auto_replace.right + inc, 3755dfecf96Smrg &block) == XawEditDone) 3765dfecf96Smrg XawTextSetInsertionPoint(w, auto_replace.left + block.length); 3775dfecf96Smrg 3785dfecf96Smrg if (block.ptr != buffer) 3795dfecf96Smrg XtFree(block.ptr); 3805dfecf96Smrg} 3815dfecf96Smrg 3825dfecf96Smrg/*ARGUSED*/ 3835dfecf96Smrgvoid 3845dfecf96SmrgLineEditAction(Widget w, XEvent *event, String *params, Cardinal *num_params) 3855dfecf96Smrg{ 3865dfecf96Smrg XawTextBlock block; 3875dfecf96Smrg 388f14f4646Smrg if (international) { 3895dfecf96Smrg /* XXX FIXME */ 3905dfecf96Smrg fprintf(stderr, "LineEditAction: Not working in international mode.\n"); 3915dfecf96Smrg return; 3925dfecf96Smrg } 3935dfecf96Smrg 3945dfecf96Smrg block.firstPos = 0; 3955dfecf96Smrg block.format = FMT8BIT; 3965dfecf96Smrg block.ptr = einfo.command; 3975dfecf96Smrg block.length = strlen(einfo.command); 3985dfecf96Smrg 3995dfecf96Smrg XawTextReplace(filenamewindow, 0, 4005dfecf96Smrg XawTextLastPosition(filenamewindow), &block); 4015dfecf96Smrg XtSetKeyboardFocus(topwindow, filenamewindow); 4025dfecf96Smrg line_edit = True; 4035dfecf96Smrg} 4045dfecf96Smrg 4055dfecf96Smrgvoid 4065dfecf96SmrgLineEdit(Widget w) 4075dfecf96Smrg{ 4085dfecf96Smrg /* Global usage variables */ 4095dfecf96Smrg XawTextPosition from, to, first, last, position, length, redisplay; 4105dfecf96Smrg int replace, compile, ecode, nth, flags, count, etype; 4115dfecf96Smrg char *command, *line, buffer[128]; 4125dfecf96Smrg XawTextBlock block; 4135dfecf96Smrg Widget source; 4145dfecf96Smrg XawTextScanDirection direction; 4155dfecf96Smrg xedit_flist_item *item; 4165dfecf96Smrg 4175dfecf96Smrg /* Variables used while parsing command */ 4185dfecf96Smrg int state, action, offset, icase, confirm; 4195dfecf96Smrg long lfrom, lto, lfinc, ltinc, number; 4205dfecf96Smrg char *ptr, *pstart, *pend, *rstart, *rend, *tmp; 4215dfecf96Smrg 4225dfecf96Smrg /* Variables used in the search/replace loop */ 4235dfecf96Smrg int len; 4245dfecf96Smrg XawTextPosition adjust = 0; 4255dfecf96Smrg 4265dfecf96Smrg command = GetString(filenamewindow); 4275dfecf96Smrg length = strlen(command); 4285dfecf96Smrg if (length >= sizeof(einfo.command)) { 4295dfecf96Smrg Feep(); 4305dfecf96Smrg return; 4315dfecf96Smrg } 4325dfecf96Smrg 4335dfecf96Smrg item = FindTextSource(XawTextGetSource(w), NULL); 4345dfecf96Smrg source = item->source; 4355dfecf96Smrg position = XawTextGetInsertionPoint(w); 4365dfecf96Smrg first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True); 4375dfecf96Smrg last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True); 4385dfecf96Smrg compile = redisplay = nth = count = confirm = 0; 4395dfecf96Smrg direction = XawsdRight; 4405dfecf96Smrg flags = RE_STARTEND; 4415dfecf96Smrg 4425dfecf96Smrg /* Error types */ 4435dfecf96Smrg#define T_NONE 0 4445dfecf96Smrg#define T_OPTION 1 4455dfecf96Smrg#define T_ICASE 2 4465dfecf96Smrg#define T_COMMAND 3 4475dfecf96Smrg#define T_REPLACE 4 4485dfecf96Smrg#define T_SEARCH 5 4495dfecf96Smrg#define T_BACKSLASH 6 4505dfecf96Smrg#define T_DIRECTION 7 4515dfecf96Smrg#define T_COMMA 8 4525dfecf96Smrg#define T_OFFSET 9 4535dfecf96Smrg#define T_INCREMENT 10 4545dfecf96Smrg#define T_NUMBER 11 4555dfecf96Smrg#define T_UNFINISHED 12 4565dfecf96Smrg#define T_RANGE 13 4575dfecf96Smrg#define T_BACKREF 14 4585dfecf96Smrg#define T_EDIT 15 4595dfecf96Smrg etype = T_NONE; 4605dfecf96Smrg 4615dfecf96Smrg#define FAIL(code) { etype = code; goto fail; } 4625dfecf96Smrg 4635dfecf96Smrg /* Value for the line value, anything else is the line number */ 4645dfecf96Smrg#define L_FIRST -1 4655dfecf96Smrg#define L_CURRENT -2 4665dfecf96Smrg#define L_LAST -3 4675dfecf96Smrg lfrom = L_FIRST; 4685dfecf96Smrg lto = L_LAST; 4695dfecf96Smrg 4705dfecf96Smrg /* Parsing states */ 4715dfecf96Smrg#define E_FINC 0 4725dfecf96Smrg#define E_FROM 1 4735dfecf96Smrg#define E_COMMA 2 4745dfecf96Smrg#define E_TINC 3 4755dfecf96Smrg#define E_TO 4 4765dfecf96Smrg#define E_COMMAND 5 4775dfecf96Smrg#define E_REGEX 6 4785dfecf96Smrg#define E_SUBST 7 4795dfecf96Smrg#define E_OPTIONS 8 4805dfecf96Smrg state = E_FROM; /* Beginning interpretation */ 4815dfecf96Smrg 4825dfecf96Smrg /* Known commands */ 4835dfecf96Smrg#define A_SEARCH 0 4845dfecf96Smrg#define A_REPLACE 1 4855dfecf96Smrg action = A_SEARCH; 4865dfecf96Smrg 4875dfecf96Smrg /* Flag to replace all occurrences */ 4885dfecf96Smrg#define O_ALL -1 4895dfecf96Smrg 4905dfecf96Smrg number = 1; 4915dfecf96Smrg lfinc = ltinc = 0; 4925dfecf96Smrg icase = offset = 0; 4935dfecf96Smrg pstart = pend = rstart = rend = NULL; 4945dfecf96Smrg 4955dfecf96Smrg if (einfo.state != SubstituteDisabled) { 4965dfecf96Smrg if (einfo.widget != w || strcmp(einfo.command, command)) { 4975dfecf96Smrg einfo.widget = w; 4985dfecf96Smrg einfo.state = SubstituteAsk; 4995dfecf96Smrg } 5005dfecf96Smrg else { 5015dfecf96Smrg XawTextPosition s_start, s_end; 5025dfecf96Smrg 5035dfecf96Smrg XawTextGetSelectionPos(w, &s_start, &s_end); 5045dfecf96Smrg if (s_start != einfo.start || s_end != einfo.end) 5055dfecf96Smrg einfo.state = SubstituteAsk; 5065dfecf96Smrg confirm = replace = 1; 5075dfecf96Smrg from = einfo.from; 5085dfecf96Smrg to = einfo.to; 5095dfecf96Smrg first = einfo.first; 5105dfecf96Smrg last = einfo.last; 5115dfecf96Smrg goto confirm_label; 5125dfecf96Smrg } 5135dfecf96Smrg } 5145dfecf96Smrg 5155dfecf96Smrg /* Remember last command */ 5165dfecf96Smrg strcpy(einfo.command, command); 5175dfecf96Smrg 5185dfecf96Smrg /* Loop parsing command */ 5195dfecf96Smrg for (ptr = einfo.command; *ptr;) { 5205dfecf96Smrg switch (*ptr++) { 5215dfecf96Smrg case 'c': 5225dfecf96Smrg if (state != E_OPTIONS && 5235dfecf96Smrg state != E_COMMAND && 5245dfecf96Smrg state != E_REGEX) 5255dfecf96Smrg FAIL(T_OPTION) 5265dfecf96Smrg confirm = 1; 5275dfecf96Smrg break; 5285dfecf96Smrg case 'g': 5295dfecf96Smrg if (state != E_OPTIONS && 5305dfecf96Smrg state != E_COMMAND && 5315dfecf96Smrg state != E_REGEX) 5325dfecf96Smrg FAIL(T_OPTION) 5335dfecf96Smrg offset = O_ALL; 5345dfecf96Smrg break; 5355dfecf96Smrg case 'i': 5365dfecf96Smrg if (state != E_OPTIONS && 5375dfecf96Smrg state != E_COMMAND && 5385dfecf96Smrg state != E_REGEX && 5395dfecf96Smrg state != E_FROM) 5405dfecf96Smrg FAIL(T_ICASE) 5415dfecf96Smrg icase = 1; 5425dfecf96Smrg break; 5435dfecf96Smrg case 's': 5445dfecf96Smrg if (state == E_FROM) 5455dfecf96Smrg lfrom = lto = L_CURRENT; 5465dfecf96Smrg else if (state == E_COMMA) { 5475dfecf96Smrg lto = L_CURRENT; 5485dfecf96Smrg ltinc = lfinc; 5495dfecf96Smrg } 5505dfecf96Smrg else if (state == E_TO) 5515dfecf96Smrg lto = L_LAST; 5525dfecf96Smrg else if (state == E_FINC) { 5535dfecf96Smrg ltinc = lfinc; 5545dfecf96Smrg lto = L_CURRENT; 5555dfecf96Smrg } 5565dfecf96Smrg else if (state != E_COMMAND && state != E_TINC) 5575dfecf96Smrg FAIL(T_COMMAND) 5585dfecf96Smrg action = A_REPLACE; 5595dfecf96Smrg state = E_REGEX; 5605dfecf96Smrg break; 5615dfecf96Smrg case '?': 5625dfecf96Smrg if (action == A_REPLACE) 5635dfecf96Smrg FAIL(T_REPLACE) 5645dfecf96Smrg case '/': 5655dfecf96Smrg if (state == E_TINC) 5665dfecf96Smrg state = action == A_REPLACE ? E_REGEX : E_FROM; 5675dfecf96Smrg else if (state == E_COMMA || state == E_FINC) { 5685dfecf96Smrg lto = L_LAST; 5695dfecf96Smrg state = E_FROM; 5705dfecf96Smrg } 5715dfecf96Smrg else if (state == E_TO) { 5725dfecf96Smrg if (ltinc == 0) 5735dfecf96Smrg lto = L_LAST; 5745dfecf96Smrg state = E_FROM; 5755dfecf96Smrg } 5765dfecf96Smrg else if (state == E_COMMAND) 5775dfecf96Smrg state = E_FROM; 5785dfecf96Smrg else if (state != E_REGEX && 5795dfecf96Smrg state != E_SUBST && 5805dfecf96Smrg state != E_FROM) 5815dfecf96Smrg FAIL(T_SEARCH) 5825dfecf96Smrg if (state != E_SUBST) 5835dfecf96Smrg direction = ptr[-1] == '/' ? XawsdRight : XawsdLeft; 5845dfecf96Smrg for (tmp = ptr; *tmp; tmp++) { 5855dfecf96Smrg if (*tmp == '\\') { 5865dfecf96Smrg if (*++tmp == '\0') 5875dfecf96Smrg FAIL(T_BACKSLASH) 5885dfecf96Smrg } 5895dfecf96Smrg else if (*tmp == ptr[-1]) 5905dfecf96Smrg break; 5915dfecf96Smrg } 5925dfecf96Smrg if (state == E_REGEX) { 5935dfecf96Smrg if (*tmp != ptr[-1]) 5945dfecf96Smrg FAIL(T_DIRECTION) 5955dfecf96Smrg pstart = ptr; 5965dfecf96Smrg pend = ptr = tmp; 5975dfecf96Smrg state = E_SUBST; 5985dfecf96Smrg } 5995dfecf96Smrg else if (state == E_FROM) { 6005dfecf96Smrg pstart = ptr; 6015dfecf96Smrg pend = ptr = tmp; 6025dfecf96Smrg state = E_OPTIONS; 6035dfecf96Smrg if (*ptr) 6045dfecf96Smrg ++ptr; 6055dfecf96Smrg } 6065dfecf96Smrg else { /* E_SUBST */ 6075dfecf96Smrg rstart = ptr; 6085dfecf96Smrg rend = tmp; 6095dfecf96Smrg state = E_OPTIONS; 6105dfecf96Smrg ptr = tmp; 6115dfecf96Smrg if (*ptr) 6125dfecf96Smrg ++ptr; 6135dfecf96Smrg } 6145dfecf96Smrg break; 6155dfecf96Smrg case ',': 6165dfecf96Smrg if (state == E_FROM) 6175dfecf96Smrg lfrom = L_FIRST; 6185dfecf96Smrg else if (state == E_FINC) 6195dfecf96Smrg lfrom = L_CURRENT; 6205dfecf96Smrg else if (state != E_COMMA) 6215dfecf96Smrg FAIL(T_COMMA) 6225dfecf96Smrg state = E_TO; 6235dfecf96Smrg break; 6245dfecf96Smrg case '%': 6255dfecf96Smrg if (state == E_FROM) { 6265dfecf96Smrg lfrom = L_FIRST; 6275dfecf96Smrg lto = L_LAST; 6285dfecf96Smrg state = E_COMMAND; 6295dfecf96Smrg } 6305dfecf96Smrg else 6315dfecf96Smrg FAIL(T_OFFSET) 6325dfecf96Smrg break; 6335dfecf96Smrg case '$': 6345dfecf96Smrg if (state != E_TO) 6355dfecf96Smrg FAIL(T_OFFSET) 6365dfecf96Smrg lto = L_LAST; 6375dfecf96Smrg state = E_COMMAND; 6385dfecf96Smrg break; 6395dfecf96Smrg case '.': 6405dfecf96Smrg if (state == E_FROM) { 6415dfecf96Smrg lfrom = L_CURRENT; 6425dfecf96Smrg state = E_COMMA; 6435dfecf96Smrg } 6445dfecf96Smrg else if (state == E_TO) { 6455dfecf96Smrg lto = L_CURRENT; 6465dfecf96Smrg state = E_COMMAND; 6475dfecf96Smrg } 6485dfecf96Smrg else 6495dfecf96Smrg FAIL(T_OFFSET) 6505dfecf96Smrg break; 6515dfecf96Smrg case '+': 6525dfecf96Smrg if (state == E_FROM) { 6535dfecf96Smrg lfinc = 1; 6545dfecf96Smrg lfrom = L_CURRENT; 6555dfecf96Smrg state = E_FINC; 6565dfecf96Smrg } 6575dfecf96Smrg else if (state == E_TO) { 6585dfecf96Smrg ltinc = 1; 6595dfecf96Smrg lto = L_CURRENT; 6605dfecf96Smrg state = E_TINC; 6615dfecf96Smrg } 6625dfecf96Smrg else 6635dfecf96Smrg FAIL(T_INCREMENT) 6645dfecf96Smrg break; 6655dfecf96Smrg case '-': case '^': 6665dfecf96Smrg if (state == E_FROM) { 6675dfecf96Smrg lfinc = -1; 6685dfecf96Smrg lfrom = L_CURRENT; 6695dfecf96Smrg state = E_FINC; 6705dfecf96Smrg } 6715dfecf96Smrg else if (state == E_TO) { 6725dfecf96Smrg ltinc = -1; 6735dfecf96Smrg lto = L_CURRENT; 6745dfecf96Smrg state = E_TINC; 6755dfecf96Smrg } 6765dfecf96Smrg else 6775dfecf96Smrg FAIL(T_INCREMENT) 6785dfecf96Smrg number = -1; 6795dfecf96Smrg break; 6805dfecf96Smrg case ';': 6815dfecf96Smrg if (state != E_FROM) 6825dfecf96Smrg FAIL(T_OFFSET) 6835dfecf96Smrg lfrom = L_CURRENT; 6845dfecf96Smrg lto = L_LAST; 6855dfecf96Smrg state = E_COMMAND; 6865dfecf96Smrg break; 6875dfecf96Smrg case '1': case '2': case '3': 6885dfecf96Smrg case '4': case '5': case '6': 6895dfecf96Smrg case '7': case '8': case '9': 6905dfecf96Smrg number = number * (ptr[-1] - '0'); 6915dfecf96Smrg while (isdigit(*ptr)) 6925dfecf96Smrg number = number * 10 + (*ptr++ - '0'); 6935dfecf96Smrg if (state == E_FROM) { 6945dfecf96Smrg lfrom = number; 6955dfecf96Smrg state = E_COMMA; 6965dfecf96Smrg } 6975dfecf96Smrg else if (state == E_FINC) { 6985dfecf96Smrg lfinc = number; 6995dfecf96Smrg state = E_COMMA; 7005dfecf96Smrg } 7015dfecf96Smrg else if (state == E_TO) { 7025dfecf96Smrg lto = number; 7035dfecf96Smrg state = E_COMMAND; 7045dfecf96Smrg } 7055dfecf96Smrg else if (state == E_TINC) { 7065dfecf96Smrg ltinc = number; 7075dfecf96Smrg state = E_COMMAND; 7085dfecf96Smrg } 7095dfecf96Smrg else if (state == E_OPTIONS && action == A_REPLACE) 7105dfecf96Smrg offset = number - 1; 7115dfecf96Smrg else 7125dfecf96Smrg FAIL(T_NUMBER) 7135dfecf96Smrg number = 1; 7145dfecf96Smrg break; 7155dfecf96Smrg case '\0': 7165dfecf96Smrg if (state == E_OPTIONS) 7175dfecf96Smrg break; 7185dfecf96Smrg default: 7195dfecf96Smrg FAIL(T_UNFINISHED) 7205dfecf96Smrg } 7215dfecf96Smrg } 7225dfecf96Smrg 7235dfecf96Smrg replace = action == A_REPLACE; 7245dfecf96Smrg 7255dfecf96Smrg switch (lfrom) { 7265dfecf96Smrg case L_FIRST: 7275dfecf96Smrg from = first; 7285dfecf96Smrg break; 7295dfecf96Smrg case L_LAST: 7305dfecf96Smrg from = LSCAN(last, 1, False); 7315dfecf96Smrg break; 7325dfecf96Smrg case L_CURRENT: 7335dfecf96Smrg if (lfinc <= 0) 7345dfecf96Smrg from = LSCAN(position, -lfinc + 1, False); 7355dfecf96Smrg else { 7365dfecf96Smrg from = RSCAN(position, lfinc + 1, False); 7375dfecf96Smrg from = LSCAN(from, 1, False); 7385dfecf96Smrg } 7395dfecf96Smrg break; 7405dfecf96Smrg default: 7415dfecf96Smrg from = RSCAN(first, lfrom, False); 7425dfecf96Smrg from = LSCAN(from, 1, False); 7435dfecf96Smrg break; 7445dfecf96Smrg } 7455dfecf96Smrg /* Just requesting to go to the numbered line */ 7465dfecf96Smrg if (state == E_COMMA || state == E_FINC) { 7475dfecf96Smrg XawTextSetInsertionPoint(w, from); 7485dfecf96Smrg return; 7495dfecf96Smrg } 7505dfecf96Smrg 7515dfecf96Smrg length = pend - pstart; 7525dfecf96Smrg if (pstart == NULL || (replace && rstart == NULL) || 7535dfecf96Smrg length >= sizeof(einfo.pattern) - 1) 7545dfecf96Smrg FAIL(T_UNFINISHED) 7555dfecf96Smrg 7565dfecf96Smrg /* Need to (re)compile regular expression pattern? */ 7575dfecf96Smrg if ((!!(einfo.flags & RE_ICASE) ^ icase) || 758f14f4646Smrg einfo.pat_length != length || 759f14f4646Smrg memcmp(pstart, einfo.pattern, 760f14f4646Smrg length > einfo.pat_length ? einfo.pat_length : length)) { 7615dfecf96Smrg compile = 1; 7625dfecf96Smrg memcpy(einfo.pattern, pstart, length); 7635dfecf96Smrg einfo.pattern[length] = '\0'; 7645dfecf96Smrg einfo.flags = icase ? RE_ICASE : 0; 7655dfecf96Smrg } 7665dfecf96Smrg 7675dfecf96Smrg /* Check range of lines to operate on */ 7685dfecf96Smrg switch (lto) { 7695dfecf96Smrg case L_FIRST: 7705dfecf96Smrg to = RSCAN(first, 1, True); 7715dfecf96Smrg break; 7725dfecf96Smrg case L_LAST: 7735dfecf96Smrg to = last; 7745dfecf96Smrg break; 7755dfecf96Smrg case L_CURRENT: 7765dfecf96Smrg if (ltinc < 0) { 7775dfecf96Smrg to = LSCAN(position, -ltinc + 1, True); 7785dfecf96Smrg to = RSCAN(to, 2, True); 7795dfecf96Smrg } 7805dfecf96Smrg else 7815dfecf96Smrg to = RSCAN(position, ltinc + 1, True); 7825dfecf96Smrg break; 7835dfecf96Smrg default: 7845dfecf96Smrg to = RSCAN(first, lto, True); 7855dfecf96Smrg break; 7865dfecf96Smrg } 7875dfecf96Smrg if (from >= to) 7885dfecf96Smrg FAIL(T_RANGE) 7895dfecf96Smrg 7905dfecf96Smrg /* Set first and last position allowed to search/replace */ 7915dfecf96Smrg first = from; 7925dfecf96Smrg last = to; 7935dfecf96Smrg 7945dfecf96Smrg /* Check bounds to work on */ 7955dfecf96Smrg if (replace) { 796f14f4646Smrg int i, j, ch; 7975dfecf96Smrg 7985dfecf96Smrg /* Check number of required match results and remove/parse backslashes */ 799f14f4646Smrg einfo.slen = rend - rstart; 8005dfecf96Smrg einfo.sref = 0; 8015dfecf96Smrg einfo.soff = offset; 802f14f4646Smrg for (i = j = 0; i < einfo.slen; i++) { 803f14f4646Smrg ch = rstart[i]; 804f14f4646Smrg if (ch == '\\') { 805f14f4646Smrg ++i; 806f14f4646Smrg switch (rstart[i]) { 807f14f4646Smrg case '0': ch = '\0'; break; 808f14f4646Smrg case 'a': ch = '\a'; break; 809f14f4646Smrg case 'b': ch = '\b'; break; 810f14f4646Smrg case 'f': ch = '\f'; break; 811f14f4646Smrg case 'n': ch = '\n'; break; 812f14f4646Smrg case 'r': ch = '\r'; break; 813f14f4646Smrg case 't': ch = '\t'; break; 814f14f4646Smrg case 'v': ch = '\v'; break; 815f14f4646Smrg case '1': case '2': case '3': 816f14f4646Smrg case '4': case '5': case '6': 817f14f4646Smrg case '7': case '8': case '9': 818f14f4646Smrg einfo.subst[j++] = '\\'; 819f14f4646Smrg if (rstart[i] - '0' > einfo.sref) 820f14f4646Smrg einfo.sref = rstart[i] - '0'; 821f14f4646Smrg /* FALLTHROUGH */ 8225dfecf96Smrg default: 823f14f4646Smrg ch = rstart[i]; 8245dfecf96Smrg break; 8255dfecf96Smrg } 8265dfecf96Smrg } 827f14f4646Smrg einfo.subst[j++] = ch; 8285dfecf96Smrg } 829f14f4646Smrg einfo.slen = j; 8305dfecf96Smrg } 8315dfecf96Smrg else if (einfo.widget != w) { 8325dfecf96Smrg /* Just a flag for backward search */ 8335dfecf96Smrg einfo.from = last; 8345dfecf96Smrg einfo.widget = w; 8355dfecf96Smrg } 8365dfecf96Smrg 8375dfecf96Smrg /* Compile pattern if required */ 8385dfecf96Smrg if (compile) { 839f14f4646Smrg int ch; 840f14f4646Smrg char *eptr, *pptr; 841f14f4646Smrg 842f14f4646Smrg /* Parse backslashes */ 843f14f4646Smrg pptr = einfo.subst_pattern; 844f14f4646Smrg for (eptr = einfo.pattern, ch = *eptr++; ch; ch = *eptr++) { 845f14f4646Smrg if (ch == '\\') { 846f14f4646Smrg switch (*eptr) { 847f14f4646Smrg case '0': ch = '\0'; einfo.flags |= RE_PEND; break; 848f14f4646Smrg case 'a': ch = '\a'; break; 849f14f4646Smrg case 'b': ch = '\b'; break; 850f14f4646Smrg case 'f': ch = '\f'; break; 851f14f4646Smrg case 'n': ch = '\n'; break; 852f14f4646Smrg case 'r': ch = '\r'; break; 853f14f4646Smrg case 't': ch = '\t'; break; 854f14f4646Smrg case 'v': ch = '\v'; break; 855f14f4646Smrg default: break; 856f14f4646Smrg } 857f14f4646Smrg if (ch != '\\') 858f14f4646Smrg ++eptr; 859f14f4646Smrg } 860f14f4646Smrg *pptr++ = ch; 861f14f4646Smrg } 862f14f4646Smrg *pptr = '\0'; 863f14f4646Smrg 8645dfecf96Smrg refree(&einfo.regex); 865f14f4646Smrg /* Allow nuls in search regex */ 866f14f4646Smrg einfo.regex.re_endp = pptr; 867f14f4646Smrg ecode = recomp(&einfo.regex, einfo.subst_pattern, einfo.flags); 868f14f4646Smrg if (ecode) 8695dfecf96Smrg goto print; 8705dfecf96Smrg } 8715dfecf96Smrg 8725dfecf96Smrg if (!replace && position >= first && position <= last) { 8735dfecf96Smrg from = position; 8745dfecf96Smrg /* The backwards repetition currently is only backwards when 8755dfecf96Smrg * changing lines, so remember from where started, to also 8765dfecf96Smrg * search in the first line. */ 8775dfecf96Smrg if (LSCAN(from, 1, False) == from) { 8785dfecf96Smrg if (direction == XawsdLeft) 8795dfecf96Smrg einfo.from = from; 8805dfecf96Smrg } 8815dfecf96Smrg else 8825dfecf96Smrg flags |= RE_NOTBOL; 8835dfecf96Smrg } 8845dfecf96Smrg to = RSCAN(from, 1, True); 8855dfecf96Smrg 8865dfecf96Smrg if (confirm) { 8875dfecf96Smrg if (!replace) 8885dfecf96Smrg FAIL(T_UNFINISHED) 8895dfecf96Smrg einfo.widget = w; 8905dfecf96Smrg einfo.state = SubstituteAsk; 8915dfecf96Smrg einfo.from = from; 8925dfecf96Smrg einfo.to = to; 8935dfecf96Smrg einfo.first = first; 8945dfecf96Smrg einfo.last = last; 8955dfecf96Smrg } 8965dfecf96Smrg else 8975dfecf96Smrg einfo.state = SubstituteDisabled; 8985dfecf96Smrg 8995dfecf96Smrgconfirm_label: 9005dfecf96Smrg if (replace) { 9015dfecf96Smrg redisplay = 1; 9025dfecf96Smrg XawTextDisableRedisplay(w); 9035dfecf96Smrg } 9045dfecf96Smrg 9055dfecf96Smrg for (;;) { 9065dfecf96Smrg if (confirm && einfo.state != SubstituteAsk) { 9075dfecf96Smrg /* Restore state from previous call */ 9085dfecf96Smrg ecode = 0; 9095dfecf96Smrg nth = einfo.soff; 9105dfecf96Smrg /* einfo.mats should not have changed */ 9115dfecf96Smrg if (einfo.state == SubstituteYes) { 9125dfecf96Smrg einfo.state = SubstituteAsk; 9135dfecf96Smrg line = einfo.text_line; 9145dfecf96Smrg goto substitute_label; 9155dfecf96Smrg } 9165dfecf96Smrg else { 9175dfecf96Smrg ++nth; 9185dfecf96Smrg einfo.state = SubstituteAsk; 9195dfecf96Smrg from = einfo.from = einfo.end; 9205dfecf96Smrg goto no_substitute_label; 9215dfecf96Smrg } 9225dfecf96Smrg } 9235dfecf96Smrg 9245dfecf96Smrg /* Read or use a line of text inplace */ 9255dfecf96Smrg position = from; 9265dfecf96Smrg length = to - from; 9275dfecf96Smrg XawTextSourceRead(source, position, &block, to - position); 9285dfecf96Smrg if (block.length >= length) 9295dfecf96Smrg line = block.ptr; 9305dfecf96Smrg else { 9315dfecf96Smrg if (length > einfo.lsize) { 9325dfecf96Smrg einfo.line = XtRealloc(einfo.line, to - from); 9335dfecf96Smrg einfo.lsize = to - from; 9345dfecf96Smrg } 9355dfecf96Smrg memcpy(einfo.line, block.ptr, block.length); 9365dfecf96Smrg length = block.length; 937f14f4646Smrg for (position += length; 938f14f4646Smrg position < to && block.length; 939f14f4646Smrg position += block.length) { 9405dfecf96Smrg XawTextSourceRead(source, position, &block, to - position); 9415dfecf96Smrg memcpy(einfo.line + length, block.ptr, block.length); 9425dfecf96Smrg length += block.length; 9435dfecf96Smrg } 9445dfecf96Smrg line = einfo.line; 9455dfecf96Smrg } 9465dfecf96Smrg 9475dfecf96Smrg /* Execute expression */ 9485dfecf96Smrg einfo.mats[0].rm_so = 0; 949f14f4646Smrg einfo.mats[0].rm_eo = to - from; 9505dfecf96Smrg 951f14f4646Smrg /* If not last line or if it ends in a newline */ 952f14f4646Smrg if (to != from) { 953f14f4646Smrg if (to < last || (to > from && line[einfo.mats[0].rm_eo - 1] == '\n')) 954f14f4646Smrg --einfo.mats[0].rm_eo; 9555dfecf96Smrg 956f14f4646Smrg ecode = reexec(&einfo.regex, line, 957f14f4646Smrg einfo.sref + 1, &einfo.mats[0], flags); 9585dfecf96Smrg 959f14f4646Smrg if (replace && einfo.mats[0].rm_so == einfo.mats[0].rm_eo) 960f14f4646Smrg /* Ignore empty matches */ 961f14f4646Smrg ecode = RE_NOMATCH; 962f14f4646Smrg 963f14f4646Smrg if (ecode == 0 && confirm && 964f14f4646Smrg (einfo.soff == O_ALL || nth == einfo.soff)) { 965f14f4646Smrg einfo.end = from + einfo.mats[0].rm_eo; 966f14f4646Smrg einfo.start = from + einfo.mats[0].rm_so; 967f14f4646Smrg XawTextSetInsertionPoint(w, einfo.end); 968f14f4646Smrg XawTextSetSelection(w, einfo.start, einfo.end); 969f14f4646Smrg 970f14f4646Smrg einfo.state = SubstituteAsk; 971f14f4646Smrg einfo.from = from; 972f14f4646Smrg einfo.to = to; 973f14f4646Smrg einfo.first = first; 974f14f4646Smrg einfo.last = last; 975f14f4646Smrg einfo.text_line = line; 976f14f4646Smrg break; 977f14f4646Smrg } 9785dfecf96Smrg } 979f14f4646Smrg else 980f14f4646Smrg /* Check bellow will update offsets */ 981f14f4646Smrg ecode = RE_NOMATCH; 9825dfecf96Smrg 9835dfecf96Smrgsubstitute_label: 9845dfecf96Smrg if (ecode == 0) { 9855dfecf96Smrg from += einfo.mats[0].rm_so; 9865dfecf96Smrg len = einfo.mats[0].rm_eo - einfo.mats[0].rm_so; 9875dfecf96Smrg 9885dfecf96Smrg /* Found match */ 9895dfecf96Smrg if (replace) { 9905dfecf96Smrg /* If not replacing all ocurrences, or if not 9915dfecf96Smrg * at the correct offset */ 9925dfecf96Smrg if (einfo.soff != O_ALL && nth < einfo.soff) { 9935dfecf96Smrg from += len; 9945dfecf96Smrg ++nth; 9955dfecf96Smrg continue; 9965dfecf96Smrg } 9975dfecf96Smrg 9985dfecf96Smrg /* Do the substitution */ 9995dfecf96Smrg block.firstPos = 0; 10005dfecf96Smrg block.format = FMT8BIT; 10015dfecf96Smrg if (einfo.sref) { 10025dfecf96Smrg /* Hard way */ 10035dfecf96Smrg int i, ref, xlen; 10045dfecf96Smrg 10055dfecf96Smrg for (i = length = 0; i < einfo.slen; i++) { 10065dfecf96Smrg if (length + 2 >= einfo.bsize) { 10075dfecf96Smrg einfo.bsize = einfo.bsize + 1024; 10085dfecf96Smrg einfo.buffer = XtRealloc(einfo.buffer, einfo.bsize); 10095dfecf96Smrg } 10105dfecf96Smrg if (einfo.subst[i] == '\\') { 10115dfecf96Smrg ++i; 10125dfecf96Smrg if (einfo.subst[i] >= '1' && einfo.subst[i] <= '9') { 10135dfecf96Smrg ref = einfo.subst[i] - '0'; 10145dfecf96Smrg xlen = einfo.mats[ref].rm_eo - 10155dfecf96Smrg einfo.mats[ref].rm_so; 10165dfecf96Smrg if (xlen < 0) 10175dfecf96Smrg /* Oops, something went wrong... */ 10185dfecf96Smrg FAIL(T_BACKREF) 10195dfecf96Smrg if (length + xlen >= einfo.bsize) { 10205dfecf96Smrg einfo.bsize += xlen + 1024 - (xlen % 1024); 10215dfecf96Smrg einfo.buffer = XtRealloc(einfo.buffer, 10225dfecf96Smrg einfo.bsize); 10235dfecf96Smrg } 10245dfecf96Smrg memcpy(einfo.buffer + length, 10255dfecf96Smrg line + einfo.mats[ref].rm_so, xlen); 10265dfecf96Smrg length += xlen; 10275dfecf96Smrg } 10285dfecf96Smrg else { 10295dfecf96Smrg einfo.buffer[length++] = einfo.subst[i - 1]; 10305dfecf96Smrg einfo.buffer[length++] = einfo.subst[i]; 10315dfecf96Smrg } 10325dfecf96Smrg } 10335dfecf96Smrg else 10345dfecf96Smrg einfo.buffer[length++] = einfo.subst[i]; 10355dfecf96Smrg } 10365dfecf96Smrg block.ptr = einfo.buffer; 10375dfecf96Smrg block.length = length; 10385dfecf96Smrg } 10395dfecf96Smrg else { 10405dfecf96Smrg block.ptr = einfo.subst; 10415dfecf96Smrg block.length = length = einfo.slen; 10425dfecf96Smrg } 10435dfecf96Smrg adjust = length - len; 10445dfecf96Smrg if (XawTextReplace(w, from, from + len, &block) != XawEditDone) 10455dfecf96Smrg FAIL(T_EDIT) 10465dfecf96Smrg last += adjust; 10475dfecf96Smrg to += adjust; 10485dfecf96Smrg from += length; 10495dfecf96Smrg 10505dfecf96Smrgno_substitute_label: 10515dfecf96Smrg if (einfo.soff != O_ALL) { 10525dfecf96Smrg nth = 0; 10535dfecf96Smrg to = RSCAN(from, 1, True); 10545dfecf96Smrg from = LSCAN(to, 1, False); 10555dfecf96Smrg if (to == last) { 10565dfecf96Smrg XawTextSetInsertionPoint(w, from); 10575dfecf96Smrg break; 10585dfecf96Smrg } 10595dfecf96Smrg } 10605dfecf96Smrg else 10615dfecf96Smrg flags |= RE_NOTBOL; 10625dfecf96Smrg } 10635dfecf96Smrg else { 10645dfecf96Smrg XawTextSetInsertionPoint(w, from + len); 10655dfecf96Smrg XawTextSetSelection(w, from, from + len); 10665dfecf96Smrg break; 10675dfecf96Smrg } 10685dfecf96Smrg } 10695dfecf96Smrg else if (ecode == RE_NOMATCH) { 10705dfecf96Smrg nth = 0; 10715dfecf96Smrg 10725dfecf96Smrg /* Try again in the next/previous line */ 10735dfecf96Smrg if (direction == XawsdLeft) { 10745dfecf96Smrg from = LSCAN(to - 1, 1 + (from != to), False); 10755dfecf96Smrg if (einfo.from <= first) { 10765dfecf96Smrg Feep(); 10775dfecf96Smrg if (++count > 1) { 10785dfecf96Smrg XawTextSetInsertionPoint(w, position); 10795dfecf96Smrg XawTextUnsetSelection(w); 10805dfecf96Smrg break; 10815dfecf96Smrg } 10825dfecf96Smrg from = LSCAN(last, 1, False); 10835dfecf96Smrg } 10845dfecf96Smrg to = RSCAN(from, 1, True); 10855dfecf96Smrg /* Can use einfo.from because replace is only done forward */ 10865dfecf96Smrg einfo.from = from; 10875dfecf96Smrg } 10885dfecf96Smrg else { 10895dfecf96Smrg if (to >= last) { 10905dfecf96Smrg Feep(); 10915dfecf96Smrg if (replace || ++count > 1) { 10925dfecf96Smrg XawTextSetInsertionPoint(w, position); 10935dfecf96Smrg XawTextUnsetSelection(w); 10945dfecf96Smrg einfo.state = SubstituteDisabled; 10955dfecf96Smrg confirm = 0; 10965dfecf96Smrg break; 10975dfecf96Smrg } 10985dfecf96Smrg to = first; 10995dfecf96Smrg } 11005dfecf96Smrg from = LSCAN(to + 1, 1, False); 11015dfecf96Smrg to = RSCAN(from, 1, True); 11025dfecf96Smrg } 11035dfecf96Smrg 11045dfecf96Smrg /* Reset flags now */ 11055dfecf96Smrg flags = RE_STARTEND; 11065dfecf96Smrg } 11075dfecf96Smrg else 11085dfecf96Smrg goto print; 11095dfecf96Smrg } 11105dfecf96Smrg 11115dfecf96Smrg if (redisplay) 11125dfecf96Smrg XawTextEnableRedisplay(w); 11135dfecf96Smrg /* If replacing not interatively return to the edit window after finished */ 11145dfecf96Smrg if (replace && !confirm) { 11155dfecf96Smrg Arg args[1]; 11165dfecf96Smrg 11175dfecf96Smrg XtSetKeyboardFocus(topwindow, textwindow); 11185dfecf96Smrg if (item->source != scratch) 11195dfecf96Smrg XtSetArg(args[0], XtNstring, item->name); 11205dfecf96Smrg else 11215dfecf96Smrg XtSetArg(args[0], XtNstring, NULL); 11225dfecf96Smrg XtSetValues(filenamewindow, args, 1); 1123f14f4646Smrg line_edit = False; 11245dfecf96Smrg } 11255dfecf96Smrg return; 11265dfecf96Smrg 11275dfecf96Smrgprint: 11285dfecf96Smrg if (redisplay) 11295dfecf96Smrg XawTextEnableRedisplay(w); 11305dfecf96Smrg 11315dfecf96Smrg strcpy(buffer, "Regex error: "); 11325dfecf96Smrg length = 13; 11335dfecf96Smrg reerror(ecode, &einfo.regex, 11345dfecf96Smrg buffer + length, sizeof(buffer) - length - 2); 11355dfecf96Smrg strcat(buffer, "\n"); 1136f14f4646Smrg XeditPrintf("%s", buffer); 11375dfecf96Smrg refree(&einfo.regex); 11385dfecf96Smrg einfo.state = SubstituteDisabled; 11395dfecf96Smrg Feep(); 11405dfecf96Smrg return; 11415dfecf96Smrg 11425dfecf96Smrg 11435dfecf96Smrgfail: 11445dfecf96Smrg if (etype != T_NONE) { 1145f765521fSmrg const char *errptr; 11465dfecf96Smrg switch (etype) { 11475dfecf96Smrg case T_OPTION: 1148f765521fSmrg errptr = "Option needs a command"; 11495dfecf96Smrg break; 11505dfecf96Smrg case T_ICASE: 1151f765521fSmrg errptr = "Icase needs an command defined or none for search"; 11525dfecf96Smrg break; 11535dfecf96Smrg case T_COMMAND: 1154f765521fSmrg errptr = "Command incorrectly specified"; 11555dfecf96Smrg break; 11565dfecf96Smrg case T_REPLACE: 1157f765521fSmrg errptr = "Can only search backwards"; 11585dfecf96Smrg break; 11595dfecf96Smrg case T_SEARCH: 1160f765521fSmrg errptr = "Badly placed search/replace specifier"; 11615dfecf96Smrg break; 11625dfecf96Smrg case T_BACKSLASH: 1163f765521fSmrg errptr = "A single backslash cannot be the last command character"; 11645dfecf96Smrg break; 11655dfecf96Smrg case T_DIRECTION: 1166f765521fSmrg errptr = "Regular expression must be separeted by / or ? not both"; 11675dfecf96Smrg break; 11685dfecf96Smrg case T_COMMA: 1169f765521fSmrg errptr = "Badly placed comma"; 11705dfecf96Smrg break; 11715dfecf96Smrg case T_OFFSET: 1172f765521fSmrg errptr = "Badly placed line offset specifier"; 11735dfecf96Smrg break; 11745dfecf96Smrg case T_INCREMENT: 1175f765521fSmrg errptr = "Badly placed line offset increment specifier"; 11765dfecf96Smrg break; 11775dfecf96Smrg case T_NUMBER: 1178f765521fSmrg errptr = "Numeric argument not expected"; 11795dfecf96Smrg break; 11805dfecf96Smrg case T_UNFINISHED: 1181f765521fSmrg errptr = "Unfinished command"; 11825dfecf96Smrg break; 11835dfecf96Smrg case T_RANGE: 1184f765521fSmrg errptr = "Bad line range"; 11855dfecf96Smrg break; 11865dfecf96Smrg case T_BACKREF: 11875dfecf96Smrg /* This may be an internal re error, but most likely the 11885dfecf96Smrg * user asked for something like "s/re0(re1)re2/\2/" */ 1189f765521fSmrg errptr = "Bad backreference"; 11905dfecf96Smrg break; 11915dfecf96Smrg case T_EDIT: 1192f765521fSmrg errptr = "Failed to replace text"; 11935dfecf96Smrg break; 11945dfecf96Smrg default: 1195f765521fSmrg errptr = "Unknown error"; 11965dfecf96Smrg break; 11975dfecf96Smrg } 1198f765521fSmrg XeditPrintf("Error: %s.\n", errptr); 11995dfecf96Smrg } 12005dfecf96Smrg if (redisplay) 12015dfecf96Smrg XawTextEnableRedisplay(w); 12025dfecf96Smrg einfo.state = SubstituteDisabled; 12035dfecf96Smrg Feep(); 12045dfecf96Smrg} 12055dfecf96Smrg 12065dfecf96Smrgstatic void 12075dfecf96SmrgSubstituteHook(Widget w, String action, XEvent *event) 12085dfecf96Smrg{ 12095dfecf96Smrg if (w != filenamewindow) 12105dfecf96Smrg return; 12115dfecf96Smrg 12125dfecf96Smrg if (line_edit && einfo.state == SubstituteAsk) { 12135dfecf96Smrg if (strcmp(action, "newline") == 0 || 12145dfecf96Smrg strcmp(action, "load-file") == 0) 12155dfecf96Smrg einfo.state = SubstituteAsk; 12165dfecf96Smrg else if (strcmp(action, "insert-char") == 0) { 12175dfecf96Smrg static XComposeStatus compose = {NULL, 0}; 12185dfecf96Smrg KeySym keysym; 12195dfecf96Smrg char mb[sizeof(wchar_t)]; 12205dfecf96Smrg 12215dfecf96Smrg if (XLookupString((XKeyEvent*)event, mb, sizeof(mb), 12225dfecf96Smrg &keysym, &compose) == 1) { 12235dfecf96Smrg if (*mb == 'y' || *mb == 'Y') 12245dfecf96Smrg einfo.state = SubstituteYes; 12255dfecf96Smrg else if (*mb == 'n' || *mb == 'N') 12265dfecf96Smrg einfo.state = SubstituteNo; 12275dfecf96Smrg else 12285dfecf96Smrg einfo.state = SubstituteDisabled; 12295dfecf96Smrg 12305dfecf96Smrg if (einfo.state != SubstituteDisabled) { 12315dfecf96Smrg einfo.callback = 1; 12325dfecf96Smrg XtAddCallback(filenamewindow, XtNpositionCallback, 12335dfecf96Smrg SubstituteCallback, NULL); 12345dfecf96Smrg } 12355dfecf96Smrg } 12365dfecf96Smrg } 12375dfecf96Smrg else if (strcmp(action, "cancel-find-file") == 0) 12385dfecf96Smrg einfo.state = SubstituteDisabled; 12395dfecf96Smrg } 12405dfecf96Smrg if (einfo.state == SubstituteDisabled && einfo.callback) { 12415dfecf96Smrg einfo.callback = 0; 12425dfecf96Smrg XtRemoveCallback(filenamewindow, XtNpositionCallback, 12435dfecf96Smrg SubstituteCallback, NULL); 12445dfecf96Smrg } 12455dfecf96Smrg} 12465dfecf96Smrg 12475dfecf96Smrg/*ARGSUSED*/ 12485dfecf96Smrgstatic void 12495dfecf96SmrgSubstituteCallback(Widget w, XtPointer client_data, XtPointer call_data) 12505dfecf96Smrg{ 12515dfecf96Smrg XawTextBlock block; 12525dfecf96Smrg 12535dfecf96Smrg einfo.callback = 0; 12545dfecf96Smrg XtRemoveCallback(filenamewindow, XtNpositionCallback, 12555dfecf96Smrg SubstituteCallback, NULL); 12565dfecf96Smrg 12575dfecf96Smrg block.firstPos = 0; 12585dfecf96Smrg block.format = FMT8BIT; 12595dfecf96Smrg block.ptr = einfo.command; 12605dfecf96Smrg block.length = strlen(einfo.command); 12615dfecf96Smrg 12625dfecf96Smrg XawTextReplace(filenamewindow, 0, 12635dfecf96Smrg XawTextLastPosition(filenamewindow), &block); 12645dfecf96Smrg 12655dfecf96Smrg LineEdit(einfo.widget); 12665dfecf96Smrg} 1267