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