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