Edit

IABSD.fr/xenocara/app/xedit/lisp/xedit.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2015-05-10 10:07:47
    Hash : 99a72825
    Message : Update to xedit 1.2.2

  • app/xedit/lisp/xedit.c
  • /*
     * Copyright (c) 2002 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/lisp/xedit.c,v 1.25 2003/04/27 18:17:35 tsi Exp $ */
    
    #include "../xedit.h"
    #include <X11/Xaw/TextSrcP.h>	/* Needs some private definitions */
    #include <X11/Xaw/TextSinkP.h>	/* Also needs private definitions... */
    #include <X11/Xmu/Xmu.h>
    #define XEDIT_LISP_PRIVATE
    #include "xedit.h"
    #include <signal.h>
    
    /* Initialize to enter lisp */
    #define LISP_SETUP()						\
        int lisp__running = lisp__data.running
    
    /* XXX Maybe should use ualarm or better, setitimer, but one
     *     second seens good enough to check for interrupts */
    
    #define	ENABLE_SIGALRM()					\
        old_sigalrm = signal(SIGALRM, SigalrmHandler);		\
        alarm(1)
    
    #define DISABLE_SIGALRM()					\
        alarm(0);							\
        signal(SIGALRM, old_sigalrm)
    
    /* Enter lisp */
    #define LISP_ENTER()						\
        if (!lisp__running) {					\
    	lisp__data.running = 1;					\
    	XFlush(XtDisplay(textwindow));				\
    	ENABLE_SIGALRM();					\
    	if (sigsetjmp(lisp__data.jmp, 1) != 0) {		\
    	    DISABLE_SIGALRM();					\
    	    lisp__data.running = 0;				\
    	    return;						\
    	}							\
        }
    
    /* Leave lisp */
    #define LISP_LEAVE()						\
        if (!lisp__running) {					\
    	DISABLE_SIGALRM();					\
    	LispTopLevel();						\
    	lisp__data.running = 0;					\
        }
    
    /*
     * Types
     */
    typedef struct {
        XawTextPosition left, right;
        XrmQuark property;
    } EntityInfo;
    
    /*
     * Prototypes
     */
    static Bool ControlGPredicate(Display*, XEvent*, XPointer);
    static ssize_t WriteToStdout(int, const void*, size_t);
    static ssize_t WriteToStderr(int, const void*, size_t);
    static ssize_t WrapWrite(Widget, const void*, size_t);
    static void XeditUpdateModeInfos(void);
    static void XeditPrint(Widget, LispObj*, int);
    static void XeditInteractiveCallback(Widget, XtPointer, XtPointer);
    static void XeditIndentationCallback(Widget, XtPointer, XtPointer);
    static LispObj *XeditCharAt(LispBuiltin*, int);
    static LispObj *XeditSearch(LispBuiltin*, XawTextScanDirection);
    
    /*
     * Initialization
     */
    static void (*old_sigalrm)(int);
    
    EditModeInfo *mode_infos;
    Cardinal num_mode_infos;
    
    static LispObj *Oauto_modes, *Oauto_mode, *Osyntax_highlight, *Osyntable_indent;
    
    /* Just to make calling interactive reparse easier */
    static LispObj interactive_arguments[4];
    
    static LispObj *justify_modes[4];
    static LispObj *wrap_modes[3];
    static LispObj *scan_types[6];
    static LispObj *scan_directions[2];
    static LispObj execute_stream;
    static LispString execute_string;
    static LispObj result_stream;
    static LispString result_string;
    static XawTextPropertyList **property_lists;
    static Cardinal num_property_lists;
    
    /* Some hacks to (at lest try to) avoid problems reentering Xlib while
     * testing for user interrupts */
    static volatile int disable_timeout, request_timeout;
    
    extern int pagesize;
    
    static LispBuiltin xeditbuiltins[] = {
        {LispFunction, Xedit_AddEntity, "add-entity offset length identifier"},
        {LispFunction, Xedit_AutoFill, "auto-fill &optional value"},
        {LispFunction, Xedit_Background, "background &optional color"},
        {LispFunction, Xedit_CharAfter, "char-after &optional offset"},
        {LispFunction, Xedit_CharBefore, "char-before &optional offset"},
        {LispFunction, Xedit_ClearEntities, "clear-entities left right"},
        {LispFunction, Xedit_ConvertPropertyList, "convert-property-list name definition"},
        {LispFunction, Xedit_Font, "font &optional font"},
        {LispFunction, Xedit_Foreground, "foreground &optional color"},
        {LispFunction, Xedit_GotoChar, "goto-char offset"},
        {LispFunction, Xedit_HorizontalScrollbar, "horizontal-scrollbar &optional state"},
        {LispFunction, Xedit_Insert, "insert text"},
        {LispFunction, Xedit_Justification, "justification &optional value"},
        {LispFunction, Xedit_LeftColumn, "left-column &optional left"},
        {LispFunction, Xedit_Point, "point"},
        {LispFunction, Xedit_PointMax, "point-max"},
        {LispFunction, Xedit_PointMin, "point-min"},
        {LispFunction, Xedit_PropertyList, "property-list &optional value"},
        {LispFunction, Xedit_ReadText, "read-text offset length"},
        {LispFunction, Xedit_ReplaceText, "replace-text left right text"},
        {LispFunction, Xedit_RightColumn, "right-column &optional right"},
        {LispFunction, Xedit_Scan, "scan offset type direction &key count include"},
        {LispFunction, Xedit_SearchBackward, "search-backward string &optional offset ignore-case"},
        {LispFunction, Xedit_SearchForward, "search-forward string &optional offset ignore-case"},
        {LispFunction, Xedit_VerticalScrollbar, "vertical-scrollbar &optional state"},
        {LispFunction, Xedit_WrapMode, "wrap-mode &optional value"},
    
    	/* This should be available from elsewhere at some time... */
        {LispFunction, Xedit_XrmStringToQuark, "xrm-string-to-quark string"},
    };
    
    /*
     * Implementation
     */
    /*ARGUSED*/
    static Bool
    ControlGPredicate(Display *display, XEvent *event, XPointer arguments)
    {
        char buffer[2];
    
        return ((event->type == KeyPress || event->type == KeyRelease) &&
    	    (event->xkey.state & ControlMask) &&
    	    XLookupString(&(event->xkey), buffer, sizeof(buffer), NULL, NULL) &&
    	    buffer[0] == '\a');
    }
    
    /*ARGSUSED*/
    static void
    SigalrmHandler(int signum)
    {
        XEvent event;
    
        if (disable_timeout) {
    	request_timeout = 1;
    	return;
        }
    
        /* Check if user pressed C-g */
        if (XCheckIfEvent(XtDisplay(textwindow), &event, ControlGPredicate, NULL)) {
    	XPutBackEvent(XtDisplay(textwindow), &event);
    	alarm(0);
    	/* Tell a signal was received, print message for SIGINT */
    	LispSignal(SIGINT);
        }
        else
    	alarm(1);
    }
    
    static ssize_t
    WrapWrite(Widget output, const void *buffer, size_t nbytes)
    {
        XawTextBlock block;
        XawTextPosition position;
    
        disable_timeout = 1;
        position = XawTextGetInsertionPoint(output);
        block.firstPos = 0;
        block.format = FMT8BIT;
        block.length = nbytes;
        block.ptr = (String)buffer;
        XawTextReplace(output, position, position, &block);
        XawTextSetInsertionPoint(output, position + block.length);
        disable_timeout = 0;
    
        if (request_timeout) {
    	XFlush(XtDisplay(output));
    	request_timeout = 0;
    	SigalrmHandler(SIGALRM);
        }
    
        return ((ssize_t)nbytes);
    }
    
    static ssize_t
    WriteToStdout(int fd, const void *buffer, size_t nbytes)
    {
        return (WrapWrite(textwindow, buffer, nbytes));
    }
    
    static ssize_t
    WriteToStderr(int fd, const void *buffer, size_t nbytes)
    {
        return (WrapWrite(messwidget, buffer, nbytes));
    }
    
    void
    LispXeditInitialize(void)
    {
        int i;
        char *string;
        LispObj *xedit, *list, *savepackage;
    
        LispSetFileWrite(Stdout, WriteToStdout);
        LispSetFileWrite(Stderr, WriteToStderr);
    
        justify_modes[0]	= KEYWORD("LEFT");
        justify_modes[1]	= KEYWORD("RIGHT");
        justify_modes[2]	= KEYWORD("CENTER");
        justify_modes[3]	= KEYWORD("FULL");
    
        wrap_modes[0]	= KEYWORD("NEVER");
        wrap_modes[1]	= KEYWORD("LINE");
        wrap_modes[2]	= KEYWORD("WORD");
    
        scan_types[0]	= KEYWORD("POSITIONS");
        scan_types[1]	= KEYWORD("WHITE-SPACE");
        scan_types[2]	= KEYWORD("EOL");
        scan_types[3]	= KEYWORD("PARAGRAPH");
        scan_types[4]	= KEYWORD("ALL");
        scan_types[5]	= KEYWORD("ALPHA-NUMERIC");
    
        scan_directions[0]	= justify_modes[0];
        scan_directions[1]	= justify_modes[1];
    
        /* Remember value of current package */
        savepackage = PACKAGE;
    
        /* Create the XEDIT package */
        xedit = LispNewPackage(STRING("XEDIT"), NIL);
    
        /* Update list of packages */
        PACK = CONS(xedit, PACK);
    
        /* Temporarily switch to the XEDIT package */
        lisp__data.pack = lisp__data.savepack = xedit->data.package.package;
        PACKAGE = xedit;
    
        /* Add XEDIT builtin functions */
        for (i = 0; i < sizeof(xeditbuiltins) / sizeof(xeditbuiltins[0]); i++)
    	LispAddBuiltinFunction(&xeditbuiltins[i]);
    
        /* Create these objects in the xedit package */
        Oauto_modes		= STATIC_ATOM("*AUTO-MODES*");
        Oauto_mode		= STATIC_ATOM("AUTO-MODE");
        Osyntax_highlight	= STATIC_ATOM("SYNTAX-HIGHLIGHT");
        Osyntable_indent	= STATIC_ATOM("SYNTABLE-INDENT");
    
        /*  Import symbols from the LISP and EXT packages */
        for (list = PACK; CONSP(list); list = CDR(list)) {
    	string = THESTR(CAR(list)->data.package.name);
    	if (strcmp(string, "LISP") == 0 || strcmp(string, "EXT") == 0)
    	    LispUsePackage(CAR(list));
        }
    
        /* Restore previous package */
        lisp__data.pack = savepackage->data.package.package;
        PACKAGE = savepackage;
    
        /* Initialize helper static objects used when executing expressions */
        execute_stream.type = LispStream_t;
        execute_stream.data.stream.source.string = &execute_string;
        execute_stream.data.stream.pathname = NIL;
        execute_stream.data.stream.type = LispStreamString;
        execute_stream.data.stream.readable = 1;
        execute_stream.data.stream.writable = 0;
        execute_string.output = 0;
        result_stream.type = LispStream_t;
        result_stream.data.stream.source.string = &result_string;
        result_stream.data.stream.pathname = NIL;
        result_stream.data.stream.type = LispStreamString;
        result_stream.data.stream.readable = 0;
        result_stream.data.stream.writable = 1;
        result_string.string = XtMalloc(pagesize);
        result_string.space = pagesize;
    
        /* Initialize interactive edition function arguments */
        /* first argument is syntax table */
        interactive_arguments[0].type = LispCons_t;
        interactive_arguments[0].data.cons.cdr = &interactive_arguments[1];
        /* second argument is where to start reparsing */
        interactive_arguments[1].type = LispCons_t;
        interactive_arguments[1].data.cons.cdr = &interactive_arguments[2];
        /* third argument is where to stop reparsing */
        interactive_arguments[2].type = LispCons_t;
        interactive_arguments[2].data.cons.cdr = &interactive_arguments[3];
        /* fourth argument is interactive flag */
        interactive_arguments[3].type = LispCons_t;
        interactive_arguments[3].data.cons.car = T;
        interactive_arguments[3].data.cons.cdr = NIL;
    
        /* Load extra functions and data type definitions */
        EXECUTE("(require \"xedit\")");
    
    
        /*
         *	This assumes that the *auto-modes* variable is a list where every
         * item has the format:
         *	    (regexp string-desc load-file-desc . symbol-name)
         *	Minimal error checking is done.
         */
    
        if (Oauto_modes->data.atom->a_object) {
    	LispObj *desc, *modes = Oauto_modes->data.atom->property->value;
    
    	for (; CONSP(modes); modes = CDR(modes)) {
    	    list = CAR(modes);
    
    	    desc = NIL;
    	    for (i = 0; i < 3 && CONSP(list); i++, list = CDR(list)) {
    		if (i == 1)
    		    desc = CAR(list);
    	    }
    	    if (i == 3 && STRINGP(desc)) {
    		mode_infos = (EditModeInfo*)
    		    XtRealloc((XtPointer)mode_infos, sizeof(EditModeInfo) *
    			      (num_mode_infos + 1));
    		mode_infos[num_mode_infos].desc = XtNewString(THESTR(desc));
    		mode_infos[num_mode_infos].symbol = list;
    		mode_infos[num_mode_infos].syntax = NULL;
    		++num_mode_infos;
    	    }
    	}
        }
    }
    
    static void
    XeditUpdateModeInfos(void)
    {
        int i;
    
        for (i = 0; i < num_mode_infos; i++) {
    	if (mode_infos[i].symbol &&
    	    mode_infos[i].syntax == NULL &&
    	    XSYMBOLP(mode_infos[i].symbol) &&
    	    mode_infos[i].symbol->data.atom->a_object)
    	    mode_infos[i].syntax =
    		mode_infos[i].symbol->data.atom->property->value;
        }
    }
    
    void
    XeditLispExecute(Widget output, XawTextPosition left, XawTextPosition right)
    {
        GC_ENTER();
        LISP_SETUP();
        int alloced, return_count;
        XawTextBlock block;
        XawTextPosition position;
        char *string, *ptr;
        LispObj *result, *code, *_cod, *returns;
    
        LISP_ENTER();
    
        position = left;
        XawTextSourceRead(XawTextGetSource(textwindow), left, &block, right - left);
        if (block.length < right - left) {
    	alloced = 1;
    	string = ptr = LispMalloc(right - left);
    	memcpy(ptr, block.ptr, block.length);
    	position = left + block.length;
    	ptr += block.length;
    	for (; position < right;) {
    	    XawTextSourceRead(XawTextGetSource(textwindow),
    			      position, &block, right - position);
    	    memcpy(ptr, block.ptr, block.length);
    	    position += block.length;
    	    ptr += block.length;
    	}
        }
        else {
    	alloced = 0;
    	string = block.ptr;
        }
    
        execute_string.string = string;
        execute_string.length = right - left;
        execute_string.input = 0;
        LispPushInput(&execute_stream);
        _cod = COD;
        result = NIL;
        if ((code = LispRead()) != NULL)
    	result = EVAL(code);
        COD = _cod;
        LispPopInput(&execute_stream);
    
        returns = NIL;
        if (RETURN_COUNT > 0) {
    	GC_PROTECT(result);
    	returns = _cod = CONS(RETURN(0), NIL);
    	GC_PROTECT(returns);
    	for (return_count = 1; return_count < RETURN_COUNT; return_count++) {
    	    RPLACD(_cod, CONS(RETURN(return_count), NIL));
    	    _cod = CDR(_cod);
    	}
        }
        LispFflush(Stdout);
        LispUpdateResults(code, result);
        if (RETURN_COUNT >= 0) {
    	XeditPrint(output, result, 1);
    	for (; CONSP(returns); returns = CDR(returns))
    	    XeditPrint(output, CAR(returns), 0);
        }
    
        if (alloced)
    	LispFree(string);
        GC_LEAVE();
    
        LISP_LEAVE();
    }
    
    static void
    XeditPrint(Widget output, LispObj *object, int newline)
    {
        XawTextBlock block;
        XawTextPosition position;
    
        result_string.length = result_string.output = 0;
        if (newline) {
    	position = XawTextGetInsertionPoint(output);
    	if (position != XawTextSourceScan(XawTextGetSource(output),
    					  position, XawstEOL,
    					  XawsdLeft, 1, False))
    	    LispSputc(&result_string, '\n');
        }
        LispWriteObject(&result_stream, object);
        LispSputc(&result_string, '\n');
    
        position = XawTextGetInsertionPoint(output);
        block.firstPos = 0;
        block.format = FMT8BIT;
        block.length = result_string.length;
        block.ptr = result_string.string;
        XawTextReplace(output, position, position, &block);
        XawTextSetInsertionPoint(output, position + block.length);
    }
    
    /*
     *  This function is defined here to avoid exporting all the lisp interfaces
     * to the core xedit code.
     */
    void
    XeditLispSetEditMode(xedit_flist_item *item, LispObj *symbol)
    {
        GC_ENTER();
        LISP_SETUP();
        LispObj *syntax, *name;
    
        item->xldata = (XeditLispData*)XtCalloc(1, sizeof(XeditLispData));
    
        LISP_ENTER();
    
        /* Create an object that represents the buffer filename.
         * Note that the entire path is passed to the auto-mode
         * function, so that directory names may be also be used
         * when determining a file type. */
        name = STRING(item->filename);
        GC_PROTECT(name);
    
        /*  Call the AUTO-MODE function to check if there is a
         * syntax definition for the file being loaded */
        if (symbol == NULL)
    	syntax = APPLY1(Oauto_mode, name);
        else
    	syntax = APPLY2(Oauto_mode, name, symbol);
    
        /* Don't need the name object anymore */
        GC_LEAVE();
    
        if (syntax != NIL) {
    	Arg arg[1];
    	LispObj arguments;
    	XawTextPropertyList *property_list;
    
    	item->xldata->syntax = syntax;
    
    	/* Apply the syntax highlight to the current buffer */
    	arguments.type = LispCons_t;
    	arguments.data.cons.car = syntax;
    	arguments.data.cons.cdr = NIL;
    	LispFuncall(Osyntax_highlight, &arguments, 1);
    
    	/*  The previous call added the property list to the widget,
    	 * remember it when switching sources. */
    	XtSetArg(arg[0], XawNtextProperties, &property_list);
    	XtGetValues(XawTextGetSink(textwindow), arg, 1);
    	item->properties = property_list;
    
    	/* Add callback for interactive changes */
    	XtAddCallback(item->source, XtNpropertyCallback,
    		      XeditInteractiveCallback, item->xldata);
    
    	/* Update information as a new file may have been loaded */
    	XeditUpdateModeInfos();
        }
        else
    	item->properties = NULL;
    
        LISP_LEAVE();
    }
    
    void
    XeditLispUnsetEditMode(xedit_flist_item *item)
    {
        if (item->xldata) {
    	XtRemoveCallback(item->source, XtNpropertyCallback,
    			 XeditInteractiveCallback, item->xldata);
    	XtFree((XtPointer)item->xldata);
    	item->xldata = NULL;
        }
    }
    
    #define MAX_INFOS	32
    /*
     *  This callback tries to do it's best in generating correct output while
     * also doing minimal work/redrawing of the screen. It probably will fail
     * for some syntax-definitions, or will just not properly repaint the
     * screen. In the later case, just press Ctrl+L.
     *  There isn't yet any command to force reparsing of some regions, and if
     * the parser becomes confused, you may need to go to a line, press a space
     * and undo, just to force it to reparse the line, and possibly some extra
     * lines until the parser thinks the display is in sync.
     *  Sometimes it will repaint a lot more of text than what is being requested
     * by this callback, this should be fixed at some time, as for certain cases
     * it is also required some redesign in the Xaw interface.
     */
    static void
    XeditInteractiveCallback(Widget w, XtPointer client_data, XtPointer call_data)
    {
        LISP_SETUP();
        XeditLispData *data = (XeditLispData*)client_data;
        LispObj *syntax = data->syntax;
        XawTextPropertyInfo *info = (XawTextPropertyInfo*)call_data;
        LispObj *result, *syntable;
        XawTextAnchor *anchor;
        XawTextEntity *entity;
        XawTextPosition first, last, left, right, begin, next, tmp, position;
        int i, j, indent;
        TextSrcObject src = (TextSrcObject)w;
        EntityInfo oinfo[MAX_INFOS], ninfo[MAX_INFOS];
        XrmQuark props[MAX_INFOS];
        int num_oinfo, num_ninfo, num_props;
        XmuScanline *clip, *oclip, *nclip;
        XmuSegment segment, *seg;
    
        if (data->disable_highlight)
    	return;
    
        LISP_ENTER();
    
        first = XawTextSourceScan(w, 0, XawstAll, XawsdLeft, 1, True);
        last = XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True);
    
        left = info->left;
        right = left + info->block->length;
    
        /* For now, only call the indent hook if a single character was typed */
        indent = (info->right == left) && (right == left + 1);
    
        /* Always reparse full lines */
        left = begin = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, False);
        right = next = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 1, False);
    
    
        /*  Check properties in the modified text. If a complex nested syntax
         * table was parsed, the newline has it's default property, so, while
         * the newline has a property, backup a line to make sure everything is
         * properly parsed.
         *  Maybe should limit the number of backuped lines, but if the parsing
         * becomes noticeable slow, better to rethink the syntax definition. */
        while (left > first) {
    	position = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, True);
    	if (XawTextSourceAnchorAndEntity(w, position, &anchor, &entity))
    	    left = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False);
    	else
    	    break;
        }
    
        /*	While the newline after the right position has a "hidden" property,
         * keep incrementing a line to be reparsed. */
        while (right < last) {
    	if (XawTextSourceAnchorAndEntity(w, right, &anchor, &entity))
    	    right = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 2, False);
    	else
    	    break;
        }
    
    #ifndef MAX
    #define MAX(a, b)	((a) > (b) ? (a) : (b))
    #endif
    
    #ifndef MIN
    #define MIN(a, b)	((a) < (b) ? (a) : (b))
    #endif
    
    #define STORE_STATE(count, info, from, to)				\
        (count) = 0;							\
        if ((anchor = XawTextSourceFindAnchor(w, (from))) != NULL) {	\
    	entity = anchor->entities;					\
    	/* Find first entity in the region to parse */			\
    	while (entity &&						\
    	       anchor->position + entity->offset + entity->length <=	\
    	       (from))							\
    	    entity = entity->next;					\
    	/* Loop storing information */					\
    	while (entity &&						\
    	    (position = anchor->position + entity->offset) < (to)) {	\
    	    (info)[(count)].left = MAX(position, (from));		\
    	    position += entity->length;					\
    	    (info)[(count)].right = MIN(position, (to));		\
    	    (info)[(count)].property = entity->property;		\
    	    /* If the changes are so complex, user need press Ctrl+L */	\
    	    if (++(count) >= MAX_INFOS)					\
    		break;							\
    	    if ((entity = entity->next) == NULL &&			\
    		(anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)	\
    		entity = anchor->entities;				\
    	}								\
        }
    
        /* Remember old state */
        STORE_STATE(num_oinfo, oinfo, begin, right);
    
        /* Reparse the lines in the modified/edited range of text */
        interactive_arguments[0].data.cons.car = syntax;
        interactive_arguments[1].data.cons.car = FIXNUM(left);
        interactive_arguments[2].data.cons.car = FIXNUM(right);
        result = APPLY(Osyntax_highlight, &interactive_arguments[0]);
        /* Indent table is the second return value */
        if (RETURN_COUNT)
    	syntable = RETURN(0);
        else
    	syntable = NIL;
    
        /* This normally is the same value as right, but the parser may have
         * continued when the syntax table stack did not finish. */
        if (FIXNUMP(result))
    	right = FIXNUM_VALUE(result);
    
        LISP_LEAVE();
    
        /* Check what have changed */
        STORE_STATE(num_ninfo, ninfo, begin, right);
    
        /* Initialize to redraw everything. */
        clip = XmuNewScanline(0, begin, right);
    
    #define CLIP_MASK(mask, from, to)					\
        if ((from) < (to)) {						\
    	segment.x1 = (from);						\
    	segment.x2 = (to);						\
    	XmuScanlineOrSegment((mask), &segment);				\
        }
    
        oclip = XmuNewScanline(0, 0, 0);
        nclip = XmuNewScanline(0, 0, 0);
    
    #define CLIP_DEFAULT(mask, from, info, num_info)			\
        for (tmp = (from), i = 0; i < (num_info); i++) {			\
    	CLIP_MASK((mask), tmp, (info)[i].left);				\
    	tmp = (info)[i].right;						\
        }
    
        /* First generate masks of regions with the default property */
        CLIP_DEFAULT(oclip, begin, oinfo, num_oinfo);
        CLIP_DEFAULT(nclip, begin, ninfo, num_ninfo);
    
        /* Store unchanged region in oclip */
        XmuScanlineAnd(oclip, nclip);
    
        /* Don't need to redraw the region in oclip */
        XmuScanlineXor(clip, oclip);
    
    #define LIST_PROPERTIES(prop, num_prop, info, num_info)			\
        (num_prop) = 0;							\
        for (i = 0; i < (num_info); i++) {					\
    	for (j = 0; j < (num_prop); j++)				\
    	    if ((prop)[j] == (info)[i].property)			\
    		break;							\
    	if (j == (num_prop))						\
    	    (prop)[(num_prop)++] = (info)[i].property;			\
        }
    
        /* Prepare to generate masks of regions of text with defined properties */
        LIST_PROPERTIES(props, num_props, oinfo, num_oinfo);
    
    #define CLIP_PROPERTY(mask, prop, info, num_info)			\
        for (j = 0; j < (num_info); j++) {					\
    	if ((info)[j].property == (prop)) {				\
    	    CLIP_MASK((mask), (info)[j].left, (info)[j].right);		\
    	}								\
        }
    
        /* Only care about the old properties, new ones need to be redrawn */
        for (i = 0; i < num_props; i++) {
    	XrmQuark property = props[i];
    
    	/* Reset oclip and nclip */
    	XmuScanlineXor(oclip, oclip);
    	XmuScanlineXor(nclip, nclip);
    
    	/* Generate masks */
    	CLIP_PROPERTY(oclip, property, oinfo, num_oinfo);
    	CLIP_PROPERTY(nclip, property, ninfo, num_ninfo);
    
    	/* Store unchanged region in oclip */
    	XmuScanlineAnd(oclip, nclip);
    
    	/* Don't need to redraw the region in oclip */
    	XmuScanlineXor(clip, oclip);
    	XmuOptimizeScanline(clip);
        }
    
        XmuDestroyScanline(oclip);
        XmuDestroyScanline(nclip);
    
        /* Tell Xaw that need update some regions */
        for (seg = clip->segment; seg; seg = seg->next) {
    	for (i = 0; i < src->textSrc.num_text; i++)
    	    /* This really should have an exported interface... */
    	    _XawTextNeedsUpdating((TextWidget)(src->textSrc.text[i]),
    				  seg->x1, seg->x2 + (seg->x2 > next));
        }
        XmuDestroyScanline(clip);
    
        data->syntable = syntable;
        /* XXX check lisp__running to know if at the toplevel parsing state */
        if (indent && syntable != NIL && !lisp__running &&
    	/* Doing an undo, probably will need an exported interface for this
    	 * case. Should not change the text now. */
    	(!src->textSrc.enable_undo || !src->textSrc.undo_state))
    	XtAddCallback(textwindow, XtNpositionCallback,
    		      XeditIndentationCallback, data);
    }
    
    /*
     * This callback is called if the syntax table where the cursor is located
     * defines an indentation function.
     */
    static void
    XeditIndentationCallback(Widget w, XtPointer client_data, XtPointer call_data)
    {
        LISP_SETUP();
        LispObj *indentp;
        XeditLispData *data = (XeditLispData*)client_data;
    
        data->disable_highlight = True;
        XtRemoveCallback(w, XtNpositionCallback, XeditIndentationCallback, data);
    
        LISP_ENTER();
    
        /* Get pointer to indentation function */
        indentp = APPLY1(Osyntable_indent, data->syntable);
    
        /* Execute indentation function */
        if (indentp != NIL)
    	APPLY2(indentp, data->syntax, data->syntable);
    
        data->disable_highlight = False;
    
        LISP_LEAVE();
    }
    
    /************************************************************************
     * Builtin functions
     ************************************************************************/
    LispObj *
    Xedit_AddEntity(LispBuiltin *builtin)
    /*
     add-entity offset length identifier
     */
    {
        LispObj *offset, *length, *identifier;
    
        identifier = ARGUMENT(2);
        length = ARGUMENT(1);
        offset = ARGUMENT(0);
    
        CHECK_INDEX(offset);
        CHECK_INDEX(length);
        CHECK_LONGINT(identifier);
    
        return (XawTextSourceAddEntity(XawTextGetSource(textwindow), 0, 0, NULL,
    				   FIXNUM_VALUE(offset), FIXNUM_VALUE(length),
    				   LONGINT_VALUE(identifier)) ? T : NIL);
    }
    
    LispObj *
    Xedit_AutoFill(LispBuiltin *builtin)
    /*
     auto-fill &optional value
     */
    {
        Arg arg[1];
        Boolean state;
    
        LispObj *value;
    
        value = ARGUMENT(0);
    
        if (value != UNSPEC) {
    	XtSetArg(arg[0], XtNautoFill, value == NIL ? False : True);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNautoFill, &state);
    	XtGetValues(textwindow, arg, 1);
    	value = state ? T : NIL;
        }
    
        return (value);
    }
    
    LispObj *
    Xedit_Background(LispBuiltin *builtin)
    /*
     background &optional color
     */
    {
        Pixel pixel;
        Arg arg[1];
        XrmValue from, to;
    
        LispObj *color;
    
        color = ARGUMENT(0);
    
        if (color != UNSPEC) {
    	CHECK_STRING(color);
    
    	from.size = STRLEN(color);
    	from.addr = (XtPointer)THESTR(color);
    	to.size = sizeof(Pixel);
    	to.addr = (XtPointer)&pixel;
    
    	if (!XtConvertAndStore(XawTextGetSink(textwindow),
    			       XtRString, &from, XtRPixel, &to))
    	    LispDestroy("cannot convert %s to Pixel", STROBJ(color));
    
    	XtSetArg(arg[0], XtNbackground, pixel);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	from.size = sizeof(Pixel);
    	from.addr = (XtPointer)&pixel;
    	to.size = 0;
    	to.addr = NULL;
    
    	XtSetArg(arg[0], XtNbackground, &pixel);
    	XtGetValues(XawTextGetSink(textwindow), arg, 1);
    	/* This cannot fail */
    	XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
    
    	color = STRING(to.addr);
        }
    
        return (color);
    }
    
    static LispObj *
    XeditCharAt(LispBuiltin *builtin, int before)
    {
        Widget source = XawTextGetSource(textwindow);
        XawTextPosition first, point, last;
        XawTextBlock block;
    
        LispObj *offset;
    
        offset = ARGUMENT(0);
        if (offset != UNSPEC) {
    	CHECK_INDEX(offset);
        }
    
        first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True);
        if (FIXNUMP(offset))
    	point = FIXNUM_VALUE(offset);
        else
    	point = XawTextGetInsertionPoint(textwindow);
        if (before && point > first) {
    	XawTextPosition position =
    	    XawTextSourceScan(source, point, XawstPositions, XawsdLeft, 1, True);
    
    	if (position < point)
    	    point = position;
    	else
    	    return (NIL);
        }
        last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
    
        if (point < first || point > last)
    	return (NIL);
    
        XawTextSourceRead(source, point, &block, 1);
    
        return (block.length ? SCHAR(*(unsigned char*)block.ptr) : NIL);
    }
    
    LispObj *
    Xedit_CharAfter(LispBuiltin *builtin)
    /*
     char-after &optional offset
     */
    {
        return (XeditCharAt(builtin, 0));
    }
    
    LispObj *
    Xedit_CharBefore(LispBuiltin *builtin)
    /*
     char-before &optional offset
     */
    {
        return (XeditCharAt(builtin, 1));
    }
    
    LispObj *
    Xedit_ClearEntities(LispBuiltin *builtin)
    /*
     clear-entities left right
     */
    {
        LispObj *left, *right;
    
        right = ARGUMENT(1);
        left = ARGUMENT(0);
    
        CHECK_INDEX(left);
        CHECK_INDEX(right);
    
        XawTextSourceClearEntities(XawTextGetSource(textwindow),
    			       FIXNUM_VALUE(left), FIXNUM_VALUE(right));
    
        return (T);
    }
    
    LispObj *
    Xedit_ConvertPropertyList(LispBuiltin *builtin)
    /*
     convert-property-list name definition
     */
    {
        LispObj *result;
        XawTextPropertyList *property_list;
    
        LispObj *name, *definition;
    
        definition = ARGUMENT(1);
        name = ARGUMENT(0);
    
        CHECK_STRING(name);
        CHECK_STRING(definition);
    
        result = NIL;
        property_list = XawTextSinkConvertPropertyList(THESTR(name),
    						   THESTR(definition),
    						   topwindow->core.screen,
    						   topwindow->core.colormap,
    						   topwindow->core.depth);
    
        if (property_list) {
    	Cardinal i;
    
    	for (i = 0; i < num_property_lists; i++)
    	    /* Check if a new property list was created */
    	    if (property_lists[i]->identifier == property_list->identifier)
    		break;
    
    	/* Remember this pointer when asked back for it */
    	if (i == num_property_lists) {
    	    property_lists = (XawTextPropertyList**)
    		XtRealloc((XtPointer)property_lists,
    			  sizeof(XawTextPropertyList) *
    			  (num_property_lists + 1));
    	    property_lists[num_property_lists++] = property_list;
    	}
    	result = INTEGER(property_list->identifier);
        }
    
        return (result);
    }
    
    LispObj *
    Xedit_Font(LispBuiltin *builtin)
    /*
     font &optional font
     */
    {
        XFontStruct *font_struct;
        Arg arg[1];
        XrmValue from, to;
    
        LispObj *font;
    
        font = ARGUMENT(0);
    
        if (font != UNSPEC) {
    	CHECK_STRING(font);
    
    	from.size = STRLEN(font);
    	from.addr = (XtPointer)THESTR(font);
    	to.size = sizeof(XFontStruct*);
    	to.addr = (XtPointer)&font_struct;
    
    	if (!XtConvertAndStore(textwindow, XtRString, &from, XtRFontStruct, &to))
    	    LispDestroy("cannot convert %s to FontStruct", STROBJ(font));
    
    	XtSetArg(arg[0], XtNfont, font_struct);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	from.size = sizeof(XFontStruct*);
    	from.addr = (XtPointer)&font_struct;
    	to.size = 0;
    	to.addr = NULL;
    
    	XtSetArg(arg[0], XtNfont, &font_struct);
    	XtGetValues(XawTextGetSink(textwindow), arg, 1);
    	/* This cannot fail */
    	XtConvertAndStore(textwindow, XtRFontStruct, &from, XtRString, &to);
    
    	font = STRING(to.addr);
        }
    
        return (font);
    }
    
    LispObj *
    Xedit_Foreground(LispBuiltin *builtin)
    /*
     foreground &optional color
     */
    {
        Pixel pixel;
        Arg arg[1];
        XrmValue from, to;
    
        LispObj *color;
    
        color = ARGUMENT(0);
    
        if (color != UNSPEC) {
    	CHECK_STRING(color);
    
    	from.size = STRLEN(color);
    	from.addr = (XtPointer)THESTR(color);
    	to.size = sizeof(Pixel);
    	to.addr = (XtPointer)&pixel;
    
    	if (!XtConvertAndStore(XawTextGetSink(textwindow),
    			       XtRString, &from, XtRPixel, &to))
    	    LispDestroy("cannot convert %s to Pixel", STROBJ(color));
    
    	XtSetArg(arg[0], XtNforeground, pixel);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	from.size = sizeof(Pixel);
    	from.addr = (XtPointer)&pixel;
    	to.size = 0;
    	to.addr = NULL;
    
    	XtSetArg(arg[0], XtNforeground, &pixel);
    	XtGetValues(XawTextGetSink(textwindow), arg, 1);
    	/* This cannot fail */
    	XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
    
    	color = STRING(to.addr);
        }
    
        return (color);
    }
    
    LispObj *
    Xedit_GotoChar(LispBuiltin *builtin)
    /*
     goto-char offset
     */
    {
        LispObj *offset;
        XawTextPosition point;
    
        offset = ARGUMENT(0);
    
        CHECK_INDEX(offset);
        XawTextSetInsertionPoint(textwindow, FIXNUM_VALUE(offset));
        point = XawTextGetInsertionPoint(textwindow);
        if (point != FIXNUM_VALUE(offset))
    	offset = FIXNUM(point);
    
        return (offset);
    }
    
    LispObj *
    Xedit_HorizontalScrollbar(LispBuiltin *builtin)
    /*
     horizontal-scrollbar &optional state
     */
    {
        Arg arg[1];
        XawTextScrollMode scroll;
    
        LispObj *state;
    
        state = ARGUMENT(0);
    
        if (state != UNSPEC) {
    	scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
    	XtSetArg(arg[0], XtNscrollHorizontal, scroll);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNscrollHorizontal, &scroll);
    	XtGetValues(textwindow, arg, 1);
    	state = scroll == XawtextScrollAlways ? T : NIL;
        }
    
        return (state);
    }
    
    LispObj *
    Xedit_Insert(LispBuiltin *builtin)
    /*
     insert text
     */
    {
        XawTextPosition point = XawTextGetInsertionPoint(textwindow);
        XawTextBlock block;
    
        LispObj *text;
    
        text = ARGUMENT(0);
    
        CHECK_STRING(text);
        
        block.firstPos = 0;
        block.format = FMT8BIT;
        block.length = STRLEN(text);
        block.ptr = THESTR(text);
        XawTextReplace(textwindow, point, point, &block);
        XawTextSetInsertionPoint(textwindow, point + block.length);
    
        return (text);
    }
    
    LispObj *
    Xedit_Justification(LispBuiltin *builtin)
    /*
     justification &optional value
     */
    {
        int i;
        Arg arg[1];
        XawTextJustifyMode justify;
    
        LispObj *value;
    
        value = ARGUMENT(0);
    
        if (value != UNSPEC) {
    	for (i = 0; i < 4; i++)
    	    if (value == justify_modes[i])
    		break;
    	if (i >= 4)
    	    LispDestroy("%s: argument must be "
    			":LEFT, :RIGHT, :CENTER, or :FULL, not %s",
    			STRFUN(builtin), STROBJ(value));
    	XtSetArg(arg[0], XtNjustifyMode, (XawTextJustifyMode)i);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNjustifyMode, &justify);
    	XtGetValues(textwindow, arg, 1);
    	i = (int)justify;
    	if (i <= 0 || i >= 4)
    	    i = 0;
    	value = justify_modes[i];
        }
    
        return (value);
    }
    
    LispObj *
    Xedit_LeftColumn(LispBuiltin *builtin)
    /*
     left-column &optional left
     */
    {
        short left;
        Arg arg[1];
    
        LispObj *oleft;
    
        oleft = ARGUMENT(0);
    
        if (oleft != UNSPEC) {
    	CHECK_INDEX(oleft);
    	if (FIXNUM_VALUE(oleft) >= 32767)
    	    left = 32767;
    	else
    	    left = FIXNUM_VALUE(oleft);
    
    	XtSetArg(arg[0], XtNleftColumn, left);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNleftColumn, &left);
    	XtGetValues(textwindow, arg, 1);
    
    	oleft = FIXNUM((long)left);
        }
    
        return (oleft);
    }
    
    LispObj *
    Xedit_Point(LispBuiltin *builtin)
    /*
     point
     */
    {
        return (FIXNUM(XawTextGetInsertionPoint(textwindow)));
    }
    
    LispObj *
    Xedit_PointMax(LispBuiltin *builtin)
    /*
     point-max
     */
    {
        return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
    				     XawstAll, XawsdRight, 1, True)));
    }
    
    LispObj *
    Xedit_PointMin(LispBuiltin *builtin)
    /*
     point-min
     */
    {
        return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
    				     XawstAll, XawsdLeft, 1, True)));
    }
    
    LispObj *
    Xedit_PropertyList(LispBuiltin *builtin)
    /*
     property-list &optional value
     */
    {
        Arg arg[1];
        XawTextPropertyList *property_list;
    
        LispObj *value;
    
        value = ARGUMENT(0);
    
        if (value != UNSPEC) {
    	Cardinal i;
    	XrmQuark quark;
    
    	CHECK_LONGINT(value);
    	property_list = NULL;
    	quark = LONGINT_VALUE(value);
    	for (i = 0; i < num_property_lists; i++)
    	    if (property_lists[i]->identifier == quark) {
    		property_list = property_lists[i];
    		break;
    	    }
    
    	if (property_list) {
    	    XtSetArg(arg[0], XawNtextProperties, property_list);
    	    XtSetValues(XawTextGetSink(textwindow), arg, 1);
    	}
    	else
    	    /* Maybe should generate an error here */
    	    value = NIL;
        }
        else {
    	XtSetArg(arg[0], XawNtextProperties, &property_list);
    	XtGetValues(XawTextGetSink(textwindow), arg, 1);
    	if (property_list)
    	    value = INTEGER(property_list->identifier);
        }
    
        return (value);
    }
    
    LispObj *
    Xedit_ReadText(LispBuiltin *builtin)
    /*
     read-text offset length
     */
    {
        XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
    					     XawstAll, XawsdRight, 1, True);
        XawTextPosition from, to, len;
        XawTextBlock block;
        char *string, *ptr;
    
        LispObj *offset, *length;
    
        length = ARGUMENT(1);
        offset = ARGUMENT(0);
    
        CHECK_INDEX(offset);
        CHECK_INDEX(length);
    
        from = FIXNUM_VALUE(offset);
        to = from + FIXNUM_VALUE(length);
        if (from > last)
    	from = last;
        if (to > last)
    	to = last;
    
        if (from == to)
    	return (STRING(""));
    
        len = to - from;
        string = LispMalloc(len);
    
        for (ptr = string; from < to;) {
    	XawTextSourceRead(XawTextGetSource(textwindow), from, &block, to - from);
    	memcpy(ptr, block.ptr, block.length);
    	ptr += block.length;
    	from += block.length;
        }
    
        return (LSTRING2(string, len));
    }
    
    LispObj *
    Xedit_ReplaceText(LispBuiltin *builtin)
    /*
     replace-text left right text
     */
    {
        XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
    					     XawstAll, XawsdRight, 1, True);
        XawTextPosition left, right;
        XawTextBlock block;
    
        LispObj *oleft, *oright, *text;
    
        text = ARGUMENT(2);
        oright = ARGUMENT(1);
        oleft = ARGUMENT(0);
    
        CHECK_INDEX(oleft);
        CHECK_INDEX(oright);
        CHECK_STRING(text);
    
        left = FIXNUM_VALUE(oleft);
        right = FIXNUM_VALUE(oright);
        if (left > last)
    	left = last;
        if (left > right)
    	right = left;
        else if (right > last)
    	right = last;
    
        block.firstPos = 0;
        block.format = FMT8BIT;
        block.length = STRLEN(text);
        block.ptr = THESTR(text);
        XawTextReplace(textwindow, left, right, &block);
    
        return (text);
    }
    
    LispObj *
    Xedit_RightColumn(LispBuiltin *builtin)
    /*
     right-column &optional right
     */
    {
        short right;
        Arg arg[1];
    
        LispObj *oright;
    
        oright = ARGUMENT(0);
    
        if (oright != UNSPEC) {
    	CHECK_INDEX(oright);
    	if (FIXNUM_VALUE(oright) >= 32767)
    	    right = 32767;
    	else
    	    right = FIXNUM_VALUE(oright);
    
    	XtSetArg(arg[0], XtNrightColumn, right);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNrightColumn, &right);
    	XtGetValues(textwindow, arg, 1);
    
    	oright = FIXNUM(right);
        }
    
        return (oright);
    }
    
    LispObj *
    Xedit_Scan(LispBuiltin *builtin)
    /*
     scan offset type direction &key count include
     */
    {
        int i;
        XawTextPosition offset;
        XawTextScanType type;
        XawTextScanDirection direction;
        int count;
    
        LispObj *ooffset, *otype, *odirection, *ocount, *include;
    
        include = ARGUMENT(4);
        if (include == UNSPEC)
    	include = NIL;
        ocount = ARGUMENT(3);
        odirection = ARGUMENT(2);
        otype = ARGUMENT(1);
        ooffset = ARGUMENT(0);
    
        CHECK_INDEX(ooffset);
        offset = FIXNUM_VALUE(ooffset);
    
        for (i = 0; i < 2; i++)
    	if (odirection == scan_directions[i])
    	    break;
        if (i >= 2)
    	LispDestroy("%s: direction must be "
    		    ":LEFT or :RIGHT, not %s",
    		    STRFUN(builtin), STROBJ(odirection));
        direction = (XawTextScanDirection)i;
    
        for (i = 0; i < 6; i++)
    	if (otype == scan_types[i])
    	    break;
        if (i >= 6)
    	LispDestroy("%s: direction must be "
    		    ":POSITIONS, :WHITE-SPACE, :EOL, "
    		    ":PARAGRAPH, :ALL, or :ALPHA-NUMERIC, not %s",
    		    STRFUN(builtin), STROBJ(otype));
        type = (XawTextScanType)i;
    
        if (ocount == UNSPEC)
    	count = 1;
        else {
    	CHECK_INDEX(ocount);
    	count = FIXNUM_VALUE(ocount);
        }
    
        offset = XawTextSourceScan(XawTextGetSource(textwindow),
    			       offset, type, direction, count,
    			       include != NIL);
    
        return (FIXNUM(offset));
    }
    
    static LispObj *
    XeditSearch(LispBuiltin *builtin, XawTextScanDirection direction)
    {
        XawTextBlock block;
        XawTextPosition position;
    
        LispObj *string, *offset, *ignore_case;
    
        ignore_case = ARGUMENT(2);
        offset = ARGUMENT(1);
        string = ARGUMENT(0);
    
        CHECK_STRING(string);
        if (offset != UNSPEC) {
    	CHECK_INDEX(offset);
    	position = FIXNUM_VALUE(offset);
        }
        else
    	position = XawTextGetInsertionPoint(textwindow);
    
        block.firstPos = (ignore_case != UNSPEC && ignore_case != NIL) ? 1 : 0;
        block.format = FMT8BIT;
        block.length = STRLEN(string);
        block.ptr = THESTR(string);
        position = XawTextSourceSearch(XawTextGetSource(textwindow),
    				   position, direction, &block);
    
        return (position != XawTextSearchError ? FIXNUM(position) : NIL);
    }
    
    
    LispObj *
    Xedit_SearchBackward(LispBuiltin *builtin)
    /*
     search-backward string &optional offset ignore-case
     */
    {
        return (XeditSearch(builtin, XawsdLeft));
    }
    
    LispObj *
    Xedit_SearchForward(LispBuiltin *builtin)
    /*
     search-forward string &optional offset ignore-case
     */
    {
        return (XeditSearch(builtin, XawsdRight));
    }
    
    LispObj *
    Xedit_VerticalScrollbar(LispBuiltin *builtin)
    /*
     vertical-scrollbar &optional state
     */
    {
        Arg arg[1];
        XawTextScrollMode scroll;
    
        LispObj *state;
    
        state = ARGUMENT(0);
    
        if (state != UNSPEC) {
    	scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
    	XtSetArg(arg[0], XtNscrollVertical, scroll);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNscrollVertical, &scroll);
    	XtGetValues(textwindow, arg, 1);
    	state = scroll == XawtextScrollAlways ? T : NIL;
        }
    
        return (state);
    }
    
    LispObj *
    Xedit_WrapMode(LispBuiltin *builtin)
    /*
     wrap-mode &optional value
     */
    {
        int i;
        Arg arg[1];
        XawTextWrapMode wrap;
    
        LispObj *value;
    
        value = ARGUMENT(0);
    
        if (value != UNSPEC) {
    	for (i = 0; i < 3; i++)
    	    if (value == wrap_modes[i])
    		break;
    	if (i >= 3)
    	    LispDestroy("%s: argument must be "
    			":NEVER, :LINE, or :WORD, not %s",
    			STRFUN(builtin), STROBJ(value));
    	XtSetArg(arg[0], XtNwrap, (XawTextWrapMode)i);
    	XtSetValues(textwindow, arg, 1);
        }
        else {
    	XtSetArg(arg[0], XtNwrap, &wrap);
    	XtGetValues(textwindow, arg, 1);
    	i = (int)wrap;
    	if (i <= 0 || i >= 3)
    	    i = 0;
    	value = wrap_modes[i];
        }
    
        return (value);
    }
    
    LispObj *
    Xedit_XrmStringToQuark(LispBuiltin *builtin)
    /*
     xrm-string-to-quark string
     */
    {
        LispObj *string;
    
        string = ARGUMENT(0);
    
        CHECK_STRING(string);
    
        return (INTEGER(XrmStringToQuark(THESTR(string))));
    }