xedit.c revision 5dfecf96
1/*
2 * Copyright (c) 2002 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/xedit.c,v 1.25 2003/04/27 18:17:35 tsi Exp $ */
31
32#include "../xedit.h"
33#include <X11/Xaw/TextSrcP.h>	/* Needs some private definitions */
34#include <X11/Xaw/TextSinkP.h>	/* Also needs private definitions... */
35#include <X11/Xmu/Xmu.h>
36#define XEDIT_LISP_PRIVATE
37#include "xedit.h"
38#include <signal.h>
39
40/* Initialize to enter lisp */
41#define LISP_SETUP()						\
42    int lisp__running = lisp__data.running
43
44/* XXX Maybe should use ualarm or better, setitimer, but one
45 *     second seens good enough to check for interrupts */
46
47#define	ENABLE_SIGALRM()					\
48    old_sigalrm = signal(SIGALRM, SigalrmHandler);		\
49    alarm(1)
50
51#define DISABLE_SIGALRM()					\
52    alarm(0);							\
53    signal(SIGALRM, old_sigalrm)
54
55/* Enter lisp */
56#define LISP_ENTER()						\
57    if (!lisp__running) {					\
58	lisp__data.running = 1;					\
59	XFlush(XtDisplay(textwindow));				\
60	ENABLE_SIGALRM();					\
61	if (sigsetjmp(lisp__data.jmp, 1) != 0) {		\
62	    DISABLE_SIGALRM();					\
63	    lisp__data.running = 0;				\
64	    return;						\
65	}							\
66    }
67
68/* Leave lisp */
69#define LISP_LEAVE()						\
70    if (!lisp__running) {					\
71	DISABLE_SIGALRM();					\
72	LispTopLevel();						\
73	lisp__data.running = 0;					\
74    }
75
76/*
77 * Types
78 */
79typedef struct {
80    XawTextPosition left, right;
81    XrmQuark property;
82} EntityInfo;
83
84/*
85 * Prototypes
86 */
87static Bool ControlGPredicate(Display*, XEvent*, XPointer);
88static ssize_t WriteToStdout(int, const void*, size_t);
89static ssize_t WriteToStderr(int, const void*, size_t);
90static ssize_t WrapWrite(Widget, const void*, size_t);
91static void XeditUpdateModeInfos(void);
92static void XeditPrint(Widget, LispObj*, int);
93static void XeditInteractiveCallback(Widget, XtPointer, XtPointer);
94static void XeditIndentationCallback(Widget, XtPointer, XtPointer);
95static LispObj *XeditCharAt(LispBuiltin*, int);
96static LispObj *XeditSearch(LispBuiltin*, XawTextScanDirection);
97
98/*
99 * Initialization
100 */
101#ifdef SIGNALRETURNSINT
102static int (*old_sigalrm)(int);
103#else
104static void (*old_sigalrm)(int);
105#endif
106
107EditModeInfo *mode_infos;
108Cardinal num_mode_infos;
109
110static LispObj *Oauto_modes, *Oauto_mode, *Osyntax_highlight, *Osyntable_indent;
111
112/* Just to make calling interactive reparse easier */
113static LispObj interactive_arguments[4];
114
115static LispObj *justify_modes[4];
116static LispObj *wrap_modes[3];
117static LispObj *scan_types[6];
118static LispObj *scan_directions[2];
119static LispObj execute_stream;
120static LispString execute_string;
121static LispObj result_stream;
122static LispString result_string;
123static XawTextPropertyList **property_lists;
124static Cardinal num_property_lists;
125
126/* Some hacks to (at lest try to) avoid problems reentering Xlib while
127 * testing for user interrupts */
128static volatile int disable_timeout, request_timeout;
129
130extern int pagesize;
131
132static LispBuiltin xeditbuiltins[] = {
133    {LispFunction, Xedit_AddEntity, "add-entity offset length identifier"},
134    {LispFunction, Xedit_AutoFill, "auto-fill &optional value"},
135    {LispFunction, Xedit_Background, "background &optional color"},
136    {LispFunction, Xedit_CharAfter, "char-after &optional offset"},
137    {LispFunction, Xedit_CharBefore, "char-before &optional offset"},
138    {LispFunction, Xedit_ClearEntities, "clear-entities left right"},
139    {LispFunction, Xedit_ConvertPropertyList, "convert-property-list name definition"},
140    {LispFunction, Xedit_Font, "font &optional font"},
141    {LispFunction, Xedit_Foreground, "foreground &optional color"},
142    {LispFunction, Xedit_GotoChar, "goto-char offset"},
143    {LispFunction, Xedit_HorizontalScrollbar, "horizontal-scrollbar &optional state"},
144    {LispFunction, Xedit_Insert, "insert text"},
145    {LispFunction, Xedit_Justification, "justification &optional value"},
146    {LispFunction, Xedit_LeftColumn, "left-column &optional left"},
147    {LispFunction, Xedit_Point, "point"},
148    {LispFunction, Xedit_PointMax, "point-max"},
149    {LispFunction, Xedit_PointMin, "point-min"},
150    {LispFunction, Xedit_PropertyList, "property-list &optional value"},
151    {LispFunction, Xedit_ReadText, "read-text offset length"},
152    {LispFunction, Xedit_ReplaceText, "replace-text left right text"},
153    {LispFunction, Xedit_RightColumn, "right-column &optional right"},
154    {LispFunction, Xedit_Scan, "scan offset type direction &key count include"},
155    {LispFunction, Xedit_SearchBackward, "search-backward string &optional offset ignore-case"},
156    {LispFunction, Xedit_SearchForward, "search-forward string &optional offset ignore-case"},
157    {LispFunction, Xedit_VerticalScrollbar, "vertical-scrollbar &optional state"},
158    {LispFunction, Xedit_WrapMode, "wrap-mode &optional value"},
159
160	/* This should be available from elsewhere at some time... */
161    {LispFunction, Xedit_XrmStringToQuark, "xrm-string-to-quark string"},
162};
163
164/*
165 * Implementation
166 */
167/*ARGUSED*/
168static Bool
169ControlGPredicate(Display *display, XEvent *event, XPointer arguments)
170{
171    char buffer[2];
172
173    return ((event->type == KeyPress || event->type == KeyRelease) &&
174	    (event->xkey.state & ControlMask) &&
175	    XLookupString(&(event->xkey), buffer, sizeof(buffer), NULL, NULL) &&
176	    buffer[0] == '\a');
177}
178
179/*ARGSUSED*/
180static
181#ifdef SIGNALRETURNSINT
182int
183#else
184void
185#endif
186SigalrmHandler(int signum)
187{
188    XEvent event;
189
190    if (disable_timeout) {
191	request_timeout = 1;
192	return;
193    }
194
195    /* Check if user pressed C-g */
196    if (XCheckIfEvent(XtDisplay(textwindow), &event, ControlGPredicate, NULL)) {
197	XPutBackEvent(XtDisplay(textwindow), &event);
198	alarm(0);
199	/* Tell a signal was received, print message for SIGINT */
200	LispSignal(SIGINT);
201    }
202    else
203	alarm(1);
204#ifdef SIGNALRETURNSINT
205    return (0);
206#endif
207}
208
209static ssize_t
210WrapWrite(Widget output, const void *buffer, size_t nbytes)
211{
212    XawTextBlock block;
213    XawTextPosition position;
214
215    disable_timeout = 1;
216    position = XawTextGetInsertionPoint(output);
217    block.firstPos = 0;
218    block.format = FMT8BIT;
219    block.length = nbytes;
220    block.ptr = (String)buffer;
221    XawTextReplace(output, position, position, &block);
222    XawTextSetInsertionPoint(output, position + block.length);
223    disable_timeout = 0;
224
225    if (request_timeout) {
226	XFlush(XtDisplay(output));
227	request_timeout = 0;
228	SigalrmHandler(SIGALRM);
229    }
230
231    return ((ssize_t)nbytes);
232}
233
234static ssize_t
235WriteToStdout(int fd, const void *buffer, size_t nbytes)
236{
237    return (WrapWrite(textwindow, buffer, nbytes));
238}
239
240static ssize_t
241WriteToStderr(int fd, const void *buffer, size_t nbytes)
242{
243    return (WrapWrite(messwidget, buffer, nbytes));
244}
245
246void
247LispXeditInitialize(void)
248{
249    int i;
250    char *string;
251    LispObj *xedit, *list, *savepackage;
252
253    LispSetFileWrite(Stdout, WriteToStdout);
254    LispSetFileWrite(Stderr, WriteToStderr);
255
256    justify_modes[0]	= KEYWORD("LEFT");
257    justify_modes[1]	= KEYWORD("RIGHT");
258    justify_modes[2]	= KEYWORD("CENTER");
259    justify_modes[3]	= KEYWORD("FULL");
260
261    wrap_modes[0]	= KEYWORD("NEVER");
262    wrap_modes[1]	= KEYWORD("LINE");
263    wrap_modes[2]	= KEYWORD("WORD");
264
265    scan_types[0]	= KEYWORD("POSITIONS");
266    scan_types[1]	= KEYWORD("WHITE-SPACE");
267    scan_types[2]	= KEYWORD("EOL");
268    scan_types[3]	= KEYWORD("PARAGRAPH");
269    scan_types[4]	= KEYWORD("ALL");
270    scan_types[5]	= KEYWORD("ALPHA-NUMERIC");
271
272    scan_directions[0]	= justify_modes[0];
273    scan_directions[1]	= justify_modes[1];
274
275    /* Remember value of current package */
276    savepackage = PACKAGE;
277
278    /* Create the XEDIT package */
279    xedit = LispNewPackage(STRING("XEDIT"), NIL);
280
281    /* Update list of packages */
282    PACK = CONS(xedit, PACK);
283
284    /* Temporarily switch to the XEDIT package */
285    lisp__data.pack = lisp__data.savepack = xedit->data.package.package;
286    PACKAGE = xedit;
287
288    /* Add XEDIT builtin functions */
289    for (i = 0; i < sizeof(xeditbuiltins) / sizeof(xeditbuiltins[0]); i++)
290	LispAddBuiltinFunction(&xeditbuiltins[i]);
291
292    /* Create these objects in the xedit package */
293    Oauto_modes		= STATIC_ATOM("*AUTO-MODES*");
294    Oauto_mode		= STATIC_ATOM("AUTO-MODE");
295    Osyntax_highlight	= STATIC_ATOM("SYNTAX-HIGHLIGHT");
296    Osyntable_indent	= STATIC_ATOM("SYNTABLE-INDENT");
297
298    /*  Import symbols from the LISP and EXT packages */
299    for (list = PACK; CONSP(list); list = CDR(list)) {
300	string = THESTR(CAR(list)->data.package.name);
301	if (strcmp(string, "LISP") == 0 || strcmp(string, "EXT") == 0)
302	    LispUsePackage(CAR(list));
303    }
304
305    /* Restore previous package */
306    lisp__data.pack = savepackage->data.package.package;
307    PACKAGE = savepackage;
308
309    /* Initialize helper static objects used when executing expressions */
310    execute_stream.type = LispStream_t;
311    execute_stream.data.stream.source.string = &execute_string;
312    execute_stream.data.stream.pathname = NIL;
313    execute_stream.data.stream.type = LispStreamString;
314    execute_stream.data.stream.readable = 1;
315    execute_stream.data.stream.writable = 0;
316    execute_string.output = 0;
317    result_stream.type = LispStream_t;
318    result_stream.data.stream.source.string = &result_string;
319    result_stream.data.stream.pathname = NIL;
320    result_stream.data.stream.type = LispStreamString;
321    result_stream.data.stream.readable = 0;
322    result_stream.data.stream.writable = 1;
323    result_string.string = XtMalloc(pagesize);
324    result_string.space = pagesize;
325
326    /* Initialize interactive edition function arguments */
327    /* first argument is syntax table */
328    interactive_arguments[0].type = LispCons_t;
329    interactive_arguments[0].data.cons.cdr = &interactive_arguments[1];
330    /* second argument is where to start reparsing */
331    interactive_arguments[1].type = LispCons_t;
332    interactive_arguments[1].data.cons.cdr = &interactive_arguments[2];
333    /* third argument is where to stop reparsing */
334    interactive_arguments[2].type = LispCons_t;
335    interactive_arguments[2].data.cons.cdr = &interactive_arguments[3];
336    /* fourth argument is interactive flag */
337    interactive_arguments[3].type = LispCons_t;
338    interactive_arguments[3].data.cons.car = T;
339    interactive_arguments[3].data.cons.cdr = NIL;
340
341    /* Load extra functions and data type definitions */
342    EXECUTE("(require \"xedit\")");
343
344
345    /*
346     *	This assumes that the *auto-modes* variable is a list where every
347     * item has the format:
348     *	    (regexp string-desc load-file-desc . symbol-name)
349     *	Minimal error checking is done.
350     */
351
352    if (Oauto_modes->data.atom->a_object) {
353	LispObj *desc, *modes = Oauto_modes->data.atom->property->value;
354
355	for (; CONSP(modes); modes = CDR(modes)) {
356	    list = CAR(modes);
357
358	    desc = NIL;
359	    for (i = 0; i < 3 && CONSP(list); i++, list = CDR(list)) {
360		if (i == 1)
361		    desc = CAR(list);
362	    }
363	    if (i == 3 && STRINGP(desc)) {
364		mode_infos = (EditModeInfo*)
365		    XtRealloc((XtPointer)mode_infos, sizeof(EditModeInfo) *
366			      (num_mode_infos + 1));
367		mode_infos[num_mode_infos].desc = XtNewString(THESTR(desc));
368		mode_infos[num_mode_infos].symbol = list;
369		mode_infos[num_mode_infos].syntax = NULL;
370		++num_mode_infos;
371	    }
372	}
373    }
374}
375
376static void
377XeditUpdateModeInfos(void)
378{
379    int i;
380
381    for (i = 0; i < num_mode_infos; i++) {
382	if (mode_infos[i].symbol &&
383	    mode_infos[i].syntax == NULL &&
384	    XSYMBOLP(mode_infos[i].symbol) &&
385	    mode_infos[i].symbol->data.atom->a_object)
386	    mode_infos[i].syntax =
387		mode_infos[i].symbol->data.atom->property->value;
388    }
389}
390
391void
392XeditLispExecute(Widget output, XawTextPosition left, XawTextPosition right)
393{
394    GC_ENTER();
395    LISP_SETUP();
396    int alloced, return_count;
397    XawTextBlock block;
398    XawTextPosition position;
399    char *string, *ptr;
400    LispObj *result, *code, *_cod, *returns;
401
402    LISP_ENTER();
403
404    position = left;
405    XawTextSourceRead(XawTextGetSource(textwindow), left, &block, right - left);
406    if (block.length < right - left) {
407	alloced = 1;
408	string = ptr = LispMalloc(right - left);
409	memcpy(ptr, block.ptr, block.length);
410	position = left + block.length;
411	ptr += block.length;
412	for (; position < right;) {
413	    XawTextSourceRead(XawTextGetSource(textwindow),
414			      position, &block, right - position);
415	    memcpy(ptr, block.ptr, block.length);
416	    position += block.length;
417	    ptr += block.length;
418	}
419    }
420    else {
421	alloced = 0;
422	string = block.ptr;
423    }
424
425    execute_string.string = string;
426    execute_string.length = right - left;
427    execute_string.input = 0;
428    LispPushInput(&execute_stream);
429    _cod = COD;
430    result = NIL;
431    if ((code = LispRead()) != NULL)
432	result = EVAL(code);
433    COD = _cod;
434    LispPopInput(&execute_stream);
435
436    returns = NIL;
437    if (RETURN_COUNT > 0) {
438	GC_PROTECT(result);
439	returns = _cod = CONS(RETURN(0), NIL);
440	GC_PROTECT(returns);
441	for (return_count = 1; return_count < RETURN_COUNT; return_count++) {
442	    RPLACD(_cod, CONS(RETURN(return_count), NIL));
443	    _cod = CDR(_cod);
444	}
445    }
446    LispFflush(Stdout);
447    LispUpdateResults(code, result);
448    if (RETURN_COUNT >= 0) {
449	XeditPrint(output, result, 1);
450	for (; CONSP(returns); returns = CDR(returns))
451	    XeditPrint(output, CAR(returns), 0);
452    }
453
454    if (alloced)
455	LispFree(string);
456    GC_LEAVE();
457
458    LISP_LEAVE();
459}
460
461static void
462XeditPrint(Widget output, LispObj *object, int newline)
463{
464    XawTextBlock block;
465    XawTextPosition position;
466
467    result_string.length = result_string.output = 0;
468    if (newline) {
469	position = XawTextGetInsertionPoint(output);
470	if (position != XawTextSourceScan(XawTextGetSource(output),
471					  position, XawstEOL,
472					  XawsdLeft, 1, False))
473	    LispSputc(&result_string, '\n');
474    }
475    LispWriteObject(&result_stream, object);
476    LispSputc(&result_string, '\n');
477
478    position = XawTextGetInsertionPoint(output);
479    block.firstPos = 0;
480    block.format = FMT8BIT;
481    block.length = result_string.length;
482    block.ptr = result_string.string;
483    XawTextReplace(output, position, position, &block);
484    XawTextSetInsertionPoint(output, position + block.length);
485}
486
487/*
488 *  This function is defined here to avoid exporting all the lisp interfaces
489 * to the core xedit code.
490 */
491void
492XeditLispSetEditMode(xedit_flist_item *item, LispObj *symbol)
493{
494    GC_ENTER();
495    LISP_SETUP();
496    LispObj *syntax, *name;
497
498    item->xldata = (XeditLispData*)XtCalloc(1, sizeof(XeditLispData));
499
500    LISP_ENTER();
501
502    /* Create an object that represents the buffer filename.
503     * Note that the entire path is passed to the auto-mode
504     * function, so that directory names may be also be used
505     * when determining a file type. */
506    name = STRING(item->filename);
507    GC_PROTECT(name);
508
509    /*  Call the AUTO-MODE function to check if there is a
510     * syntax definition for the file being loaded */
511    if (symbol == NULL)
512	syntax = APPLY1(Oauto_mode, name);
513    else
514	syntax = APPLY2(Oauto_mode, name, symbol);
515
516    /* Don't need the name object anymore */
517    GC_LEAVE();
518
519    if (syntax != NIL) {
520	Arg arg[1];
521	LispObj arguments;
522	XawTextPropertyList *property_list;
523
524	item->xldata->syntax = syntax;
525
526	/* Apply the syntax highlight to the current buffer */
527	arguments.type = LispCons_t;
528	arguments.data.cons.car = syntax;
529	arguments.data.cons.cdr = NIL;
530	LispFuncall(Osyntax_highlight, &arguments, 1);
531
532	/*  The previous call added the property list to the widget,
533	 * remember it when switching sources. */
534	XtSetArg(arg[0], XawNtextProperties, &property_list);
535	XtGetValues(XawTextGetSink(textwindow), arg, 1);
536	item->properties = property_list;
537
538	/* Add callback for interactive changes */
539	XtAddCallback(item->source, XtNpropertyCallback,
540		      XeditInteractiveCallback, item->xldata);
541
542	/* Update information as a new file may have been loaded */
543	XeditUpdateModeInfos();
544    }
545    else
546	item->properties = NULL;
547
548    LISP_LEAVE();
549}
550
551void
552XeditLispUnsetEditMode(xedit_flist_item *item)
553{
554    if (item->xldata) {
555	XtRemoveCallback(item->source, XtNpropertyCallback,
556			 XeditInteractiveCallback, item->xldata);
557	XtFree((XtPointer)item->xldata);
558	item->xldata = NULL;
559    }
560}
561
562#define MAX_INFOS	32
563/*
564 *  This callback tries to do it's best in generating correct output while
565 * also doing minimal work/redrawing of the screen. It probably will fail
566 * for some syntax-definitions, or will just not properly repaint the
567 * screen. In the later case, just press Ctrl+L.
568 *  There isn't yet any command to force reparsing of some regions, and if
569 * the parser becomes confused, you may need to go to a line, press a space
570 * and undo, just to force it to reparse the line, and possibly some extra
571 * lines until the parser thinks the display is in sync.
572 *  Sometimes it will repaint a lot more of text than what is being requested
573 * by this callback, this should be fixed at some time, as for certain cases
574 * it is also required some redesign in the Xaw interface.
575 */
576static void
577XeditInteractiveCallback(Widget w, XtPointer client_data, XtPointer call_data)
578{
579    LISP_SETUP();
580    XeditLispData *data = (XeditLispData*)client_data;
581    LispObj *syntax = data->syntax;
582    XawTextPropertyInfo *info = (XawTextPropertyInfo*)call_data;
583    LispObj *result, *syntable;
584    XawTextAnchor *anchor;
585    XawTextEntity *entity;
586    XawTextPosition first, last, left, right, begin, next, tmp, position;
587    int i, j, indent;
588    TextSrcObject src = (TextSrcObject)w;
589    EntityInfo oinfo[MAX_INFOS], ninfo[MAX_INFOS];
590    XrmQuark props[MAX_INFOS];
591    int num_oinfo, num_ninfo, num_props;
592    XmuScanline *clip, *oclip, *nclip;
593    XmuSegment segment, *seg;
594
595    if (data->disable_highlight)
596	return;
597
598    LISP_ENTER();
599
600    first = XawTextSourceScan(w, 0, XawstAll, XawsdLeft, 1, True);
601    last = XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True);
602
603    left = info->left;
604    right = left + info->block->length;
605
606    /* For now, only call the indent hook if a single character was typed */
607    indent = (info->right == left) && (right == left + 1);
608
609    /* Always reparse full lines */
610    left = begin = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, False);
611    right = next = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 1, False);
612
613
614    /*  Check properties in the modified text. If a complex nested syntax
615     * table was parsed, the newline has it's default property, so, while
616     * the newline has a property, backup a line to make sure everything is
617     * properly parsed.
618     *  Maybe should limit the number of backuped lines, but if the parsing
619     * becomes noticeable slow, better to rethink the syntax definition. */
620    while (left > first) {
621	position = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, True);
622	if (XawTextSourceAnchorAndEntity(w, position, &anchor, &entity))
623	    left = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False);
624	else
625	    break;
626    }
627
628    /*	While the newline after the right position has a "hidden" property,
629     * keep incrementing a line to be reparsed. */
630    while (right < last) {
631	if (XawTextSourceAnchorAndEntity(w, right, &anchor, &entity))
632	    right = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 2, False);
633	else
634	    break;
635    }
636
637#ifndef MAX
638#define MAX(a, b)	((a) > (b) ? (a) : (b))
639#endif
640
641#ifndef MIN
642#define MIN(a, b)	((a) < (b) ? (a) : (b))
643#endif
644
645#define STORE_STATE(count, info, from, to)				\
646    (count) = 0;							\
647    if ((anchor = XawTextSourceFindAnchor(w, (from))) != NULL) {	\
648	entity = anchor->entities;					\
649	/* Find first entity in the region to parse */			\
650	while (entity &&						\
651	       anchor->position + entity->offset + entity->length <=	\
652	       (from))							\
653	    entity = entity->next;					\
654	/* Loop storing information */					\
655	while (entity &&						\
656	    (position = anchor->position + entity->offset) < (to)) {	\
657	    (info)[(count)].left = MAX(position, (from));		\
658	    position += entity->length;					\
659	    (info)[(count)].right = MIN(position, (to));		\
660	    (info)[(count)].property = entity->property;		\
661	    /* If the changes are so complex, user need press Ctrl+L */	\
662	    if (++(count) >= MAX_INFOS)					\
663		break;							\
664	    if ((entity = entity->next) == NULL &&			\
665		(anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)	\
666		entity = anchor->entities;				\
667	}								\
668    }
669
670    /* Remember old state */
671    STORE_STATE(num_oinfo, oinfo, begin, right);
672
673    /* Reparse the lines in the modified/edited range of text */
674    interactive_arguments[0].data.cons.car = syntax;
675    interactive_arguments[1].data.cons.car = FIXNUM(left);
676    interactive_arguments[2].data.cons.car = FIXNUM(right);
677    result = APPLY(Osyntax_highlight, &interactive_arguments[0]);
678    /* Indent table is the second return value */
679    if (RETURN_COUNT)
680	syntable = RETURN(0);
681    else
682	syntable = NIL;
683
684    /* This normally is the same value as right, but the parser may have
685     * continued when the syntax table stack did not finish. */
686    if (FIXNUMP(result))
687	right = FIXNUM_VALUE(result);
688
689    LISP_LEAVE();
690
691    /* Check what have changed */
692    STORE_STATE(num_ninfo, ninfo, begin, right);
693
694    /* Initialize to redraw everything. */
695    clip = XmuNewScanline(0, begin, right);
696
697#define CLIP_MASK(mask, from, to)					\
698    if ((from) < (to)) {						\
699	segment.x1 = (from);						\
700	segment.x2 = (to);						\
701	XmuScanlineOrSegment((mask), &segment);				\
702    }
703
704    oclip = XmuNewScanline(0, 0, 0);
705    nclip = XmuNewScanline(0, 0, 0);
706
707#define CLIP_DEFAULT(mask, from, info, num_info)			\
708    for (tmp = (from), i = 0; i < (num_info); i++) {			\
709	CLIP_MASK((mask), tmp, (info)[i].left);				\
710	tmp = (info)[i].right;						\
711    }
712
713    /* First generate masks of regions with the default property */
714    CLIP_DEFAULT(oclip, begin, oinfo, num_oinfo);
715    CLIP_DEFAULT(nclip, begin, ninfo, num_ninfo);
716
717    /* Store unchanged region in oclip */
718    XmuScanlineAnd(oclip, nclip);
719
720    /* Don't need to redraw the region in oclip */
721    XmuScanlineXor(clip, oclip);
722
723#define LIST_PROPERTIES(prop, num_prop, info, num_info)			\
724    (num_prop) = 0;							\
725    for (i = 0; i < (num_info); i++) {					\
726	for (j = 0; j < (num_prop); j++)				\
727	    if ((prop)[j] == (info)[i].property)			\
728		break;							\
729	if (j == (num_prop))						\
730	    (prop)[(num_prop)++] = (info)[i].property;			\
731    }
732
733    /* Prepare to generate masks of regions of text with defined properties */
734    LIST_PROPERTIES(props, num_props, oinfo, num_oinfo);
735
736#define CLIP_PROPERTY(mask, prop, info, num_info)			\
737    for (j = 0; j < (num_info); j++) {					\
738	if ((info)[j].property == (prop)) {				\
739	    CLIP_MASK((mask), (info)[j].left, (info)[j].right);		\
740	}								\
741    }
742
743    /* Only care about the old properties, new ones need to be redrawn */
744    for (i = 0; i < num_props; i++) {
745	XrmQuark property = props[i];
746
747	/* Reset oclip and nclip */
748	XmuScanlineXor(oclip, oclip);
749	XmuScanlineXor(nclip, nclip);
750
751	/* Generate masks */
752	CLIP_PROPERTY(oclip, property, oinfo, num_oinfo);
753	CLIP_PROPERTY(nclip, property, ninfo, num_ninfo);
754
755	/* Store unchanged region in oclip */
756	XmuScanlineAnd(oclip, nclip);
757
758	/* Don't need to redraw the region in oclip */
759	XmuScanlineXor(clip, oclip);
760	XmuOptimizeScanline(clip);
761    }
762
763    XmuDestroyScanline(oclip);
764    XmuDestroyScanline(nclip);
765
766    /* Tell Xaw that need update some regions */
767    for (seg = clip->segment; seg; seg = seg->next) {
768	for (i = 0; i < src->textSrc.num_text; i++)
769	    /* This really should have an exported interface... */
770	    _XawTextNeedsUpdating((TextWidget)(src->textSrc.text[i]),
771				  seg->x1, seg->x2 + (seg->x2 > next));
772    }
773    XmuDestroyScanline(clip);
774
775    data->syntable = syntable;
776    /* XXX check lisp__running to know if at the toplevel parsing state */
777    if (indent && syntable != NIL && !lisp__running &&
778	/* Doing an undo, probably will need an exported interface for this
779	 * case. Should not change the text now. */
780	(!src->textSrc.enable_undo || !src->textSrc.undo_state))
781	XtAddCallback(textwindow, XtNpositionCallback,
782		      XeditIndentationCallback, data);
783}
784
785/*
786 * This callback is called if the syntax table where the cursor is located
787 * defines an indentation function.
788 */
789static void
790XeditIndentationCallback(Widget w, XtPointer client_data, XtPointer call_data)
791{
792    LISP_SETUP();
793    LispObj *indentp;
794    XeditLispData *data = (XeditLispData*)client_data;
795
796    data->disable_highlight = True;
797    XtRemoveCallback(w, XtNpositionCallback, XeditIndentationCallback, data);
798
799    LISP_ENTER();
800
801    /* Get pointer to indentation function */
802    indentp = APPLY1(Osyntable_indent, data->syntable);
803
804    /* Execute indentation function */
805    if (indentp != NIL)
806	APPLY2(indentp, data->syntax, data->syntable);
807
808    data->disable_highlight = False;
809
810    LISP_LEAVE();
811}
812
813/************************************************************************
814 * Builtin functions
815 ************************************************************************/
816LispObj *
817Xedit_AddEntity(LispBuiltin *builtin)
818/*
819 add-entity offset length identifier
820 */
821{
822    LispObj *offset, *length, *identifier;
823
824    identifier = ARGUMENT(2);
825    length = ARGUMENT(1);
826    offset = ARGUMENT(0);
827
828    CHECK_INDEX(offset);
829    CHECK_INDEX(length);
830    CHECK_LONGINT(identifier);
831
832    return (XawTextSourceAddEntity(XawTextGetSource(textwindow), 0, 0, NULL,
833				   FIXNUM_VALUE(offset), FIXNUM_VALUE(length),
834				   LONGINT_VALUE(identifier)) ? T : NIL);
835}
836
837LispObj *
838Xedit_AutoFill(LispBuiltin *builtin)
839/*
840 auto-fill &optional value
841 */
842{
843    Arg arg[1];
844    Boolean state;
845
846    LispObj *value;
847
848    value = ARGUMENT(0);
849
850    if (value != UNSPEC) {
851	XtSetArg(arg[0], XtNautoFill, value == NIL ? False : True);
852	XtSetValues(textwindow, arg, 1);
853    }
854    else {
855	XtSetArg(arg[0], XtNautoFill, &state);
856	XtGetValues(textwindow, arg, 1);
857	value = state ? T : NIL;
858    }
859
860    return (value);
861}
862
863LispObj *
864Xedit_Background(LispBuiltin *builtin)
865/*
866 background &optional color
867 */
868{
869    Pixel pixel;
870    Arg arg[1];
871    XrmValue from, to;
872
873    LispObj *color;
874
875    color = ARGUMENT(0);
876
877    if (color != UNSPEC) {
878	CHECK_STRING(color);
879
880	from.size = STRLEN(color);
881	from.addr = (XtPointer)THESTR(color);
882	to.size = sizeof(Pixel);
883	to.addr = (XtPointer)&pixel;
884
885	if (!XtConvertAndStore(XawTextGetSink(textwindow),
886			       XtRString, &from, XtRPixel, &to))
887	    LispDestroy("cannot convert %s to Pixel", STROBJ(color));
888
889	XtSetArg(arg[0], XtNbackground, pixel);
890	XtSetValues(textwindow, arg, 1);
891    }
892    else {
893	from.size = sizeof(Pixel);
894	from.addr = (XtPointer)&pixel;
895	to.size = 0;
896	to.addr = NULL;
897
898	XtSetArg(arg[0], XtNbackground, &pixel);
899	XtGetValues(XawTextGetSink(textwindow), arg, 1);
900	/* This cannot fail */
901	XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
902
903	color = STRING(to.addr);
904    }
905
906    return (color);
907}
908
909static LispObj *
910XeditCharAt(LispBuiltin *builtin, int before)
911{
912    Widget source = XawTextGetSource(textwindow);
913    XawTextPosition first, point, last;
914    XawTextBlock block;
915
916    LispObj *offset;
917
918    offset = ARGUMENT(0);
919    if (offset != UNSPEC) {
920	CHECK_INDEX(offset);
921    }
922
923    first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True);
924    if (FIXNUMP(offset))
925	point = FIXNUM_VALUE(offset);
926    else
927	point = XawTextGetInsertionPoint(textwindow);
928    if (before && point > first) {
929	XawTextPosition position =
930	    XawTextSourceScan(source, point, XawstPositions, XawsdLeft, 1, True);
931
932	if (position < point)
933	    point = position;
934	else
935	    return (NIL);
936    }
937    last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
938
939    if (point < first || point > last)
940	return (NIL);
941
942    XawTextSourceRead(source, point, &block, 1);
943
944    return (block.length ? SCHAR(*(unsigned char*)block.ptr) : NIL);
945}
946
947LispObj *
948Xedit_CharAfter(LispBuiltin *builtin)
949/*
950 char-after &optional offset
951 */
952{
953    return (XeditCharAt(builtin, 0));
954}
955
956LispObj *
957Xedit_CharBefore(LispBuiltin *builtin)
958/*
959 char-before &optional offset
960 */
961{
962    return (XeditCharAt(builtin, 1));
963}
964
965LispObj *
966Xedit_ClearEntities(LispBuiltin *builtin)
967/*
968 clear-entities left right
969 */
970{
971    LispObj *left, *right;
972
973    right = ARGUMENT(1);
974    left = ARGUMENT(0);
975
976    CHECK_INDEX(left);
977    CHECK_INDEX(right);
978
979    XawTextSourceClearEntities(XawTextGetSource(textwindow),
980			       FIXNUM_VALUE(left), FIXNUM_VALUE(right));
981
982    return (T);
983}
984
985LispObj *
986Xedit_ConvertPropertyList(LispBuiltin *builtin)
987/*
988 convert-property-list name definition
989 */
990{
991    LispObj *result;
992    XawTextPropertyList *property_list;
993
994    LispObj *name, *definition;
995
996    definition = ARGUMENT(1);
997    name = ARGUMENT(0);
998
999    CHECK_STRING(name);
1000    CHECK_STRING(definition);
1001
1002    result = NIL;
1003    property_list = XawTextSinkConvertPropertyList(THESTR(name),
1004						   THESTR(definition),
1005						   topwindow->core.screen,
1006						   topwindow->core.colormap,
1007						   topwindow->core.depth);
1008
1009    if (property_list) {
1010	Cardinal i;
1011
1012	for (i = 0; i < num_property_lists; i++)
1013	    /* Check if a new property list was created */
1014	    if (property_lists[i]->identifier == property_list->identifier)
1015		break;
1016
1017	/* Remember this pointer when asked back for it */
1018	if (i == num_property_lists) {
1019	    property_lists = (XawTextPropertyList**)
1020		XtRealloc((XtPointer)property_lists,
1021			  sizeof(XawTextPropertyList) *
1022			  (num_property_lists + 1));
1023	    property_lists[num_property_lists++] = property_list;
1024	}
1025	result = INTEGER(property_list->identifier);
1026    }
1027
1028    return (result);
1029}
1030
1031LispObj *
1032Xedit_Font(LispBuiltin *builtin)
1033/*
1034 font &optional font
1035 */
1036{
1037    XFontStruct *font_struct;
1038    Arg arg[1];
1039    XrmValue from, to;
1040
1041    LispObj *font;
1042
1043    font = ARGUMENT(0);
1044
1045    if (font != UNSPEC) {
1046	CHECK_STRING(font);
1047
1048	from.size = STRLEN(font);
1049	from.addr = (XtPointer)THESTR(font);
1050	to.size = sizeof(XFontStruct*);
1051	to.addr = (XtPointer)&font_struct;
1052
1053	if (!XtConvertAndStore(textwindow, XtRString, &from, XtRFontStruct, &to))
1054	    LispDestroy("cannot convert %s to FontStruct", STROBJ(font));
1055
1056	XtSetArg(arg[0], XtNfont, font_struct);
1057	XtSetValues(textwindow, arg, 1);
1058    }
1059    else {
1060	from.size = sizeof(XFontStruct*);
1061	from.addr = (XtPointer)&font_struct;
1062	to.size = 0;
1063	to.addr = NULL;
1064
1065	XtSetArg(arg[0], XtNfont, &font_struct);
1066	XtGetValues(XawTextGetSink(textwindow), arg, 1);
1067	/* This cannot fail */
1068	XtConvertAndStore(textwindow, XtRFontStruct, &from, XtRString, &to);
1069
1070	font = STRING(to.addr);
1071    }
1072
1073    return (font);
1074}
1075
1076LispObj *
1077Xedit_Foreground(LispBuiltin *builtin)
1078/*
1079 foreground &optional color
1080 */
1081{
1082    Pixel pixel;
1083    Arg arg[1];
1084    XrmValue from, to;
1085
1086    LispObj *color;
1087
1088    color = ARGUMENT(0);
1089
1090    if (color != UNSPEC) {
1091	CHECK_STRING(color);
1092
1093	from.size = STRLEN(color);
1094	from.addr = (XtPointer)THESTR(color);
1095	to.size = sizeof(Pixel);
1096	to.addr = (XtPointer)&pixel;
1097
1098	if (!XtConvertAndStore(XawTextGetSink(textwindow),
1099			       XtRString, &from, XtRPixel, &to))
1100	    LispDestroy("cannot convert %s to Pixel", STROBJ(color));
1101
1102	XtSetArg(arg[0], XtNforeground, pixel);
1103	XtSetValues(textwindow, arg, 1);
1104    }
1105    else {
1106	from.size = sizeof(Pixel);
1107	from.addr = (XtPointer)&pixel;
1108	to.size = 0;
1109	to.addr = NULL;
1110
1111	XtSetArg(arg[0], XtNforeground, &pixel);
1112	XtGetValues(XawTextGetSink(textwindow), arg, 1);
1113	/* This cannot fail */
1114	XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
1115
1116	color = STRING(to.addr);
1117    }
1118
1119    return (color);
1120}
1121
1122LispObj *
1123Xedit_GotoChar(LispBuiltin *builtin)
1124/*
1125 goto-char offset
1126 */
1127{
1128    LispObj *offset;
1129    XawTextPosition point;
1130
1131    offset = ARGUMENT(0);
1132
1133    CHECK_INDEX(offset);
1134    XawTextSetInsertionPoint(textwindow, FIXNUM_VALUE(offset));
1135    point = XawTextGetInsertionPoint(textwindow);
1136    if (point != FIXNUM_VALUE(offset))
1137	offset = FIXNUM(point);
1138
1139    return (offset);
1140}
1141
1142LispObj *
1143Xedit_HorizontalScrollbar(LispBuiltin *builtin)
1144/*
1145 horizontal-scrollbar &optional state
1146 */
1147{
1148    Arg arg[1];
1149    XawTextScrollMode scroll;
1150
1151    LispObj *state;
1152
1153    state = ARGUMENT(0);
1154
1155    if (state != UNSPEC) {
1156	scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
1157	XtSetArg(arg[0], XtNscrollHorizontal, scroll);
1158	XtSetValues(textwindow, arg, 1);
1159    }
1160    else {
1161	XtSetArg(arg[0], XtNscrollHorizontal, &scroll);
1162	XtGetValues(textwindow, arg, 1);
1163	state = scroll == XawtextScrollAlways ? T : NIL;
1164    }
1165
1166    return (state);
1167}
1168
1169LispObj *
1170Xedit_Insert(LispBuiltin *builtin)
1171/*
1172 insert text
1173 */
1174{
1175    XawTextPosition point = XawTextGetInsertionPoint(textwindow);
1176    XawTextBlock block;
1177
1178    LispObj *text;
1179
1180    text = ARGUMENT(0);
1181
1182    CHECK_STRING(text);
1183
1184    block.firstPos = 0;
1185    block.format = FMT8BIT;
1186    block.length = STRLEN(text);
1187    block.ptr = THESTR(text);
1188    XawTextReplace(textwindow, point, point, &block);
1189    XawTextSetInsertionPoint(textwindow, point + block.length);
1190
1191    return (text);
1192}
1193
1194LispObj *
1195Xedit_Justification(LispBuiltin *builtin)
1196/*
1197 justification &optional value
1198 */
1199{
1200    int i;
1201    Arg arg[1];
1202    XawTextJustifyMode justify;
1203
1204    LispObj *value;
1205
1206    value = ARGUMENT(0);
1207
1208    if (value != UNSPEC) {
1209	for (i = 0; i < 4; i++)
1210	    if (value == justify_modes[i])
1211		break;
1212	if (i >= 4)
1213	    LispDestroy("%s: argument must be "
1214			":LEFT, :RIGHT, :CENTER, or :FULL, not %s",
1215			STRFUN(builtin), STROBJ(value));
1216	XtSetArg(arg[0], XtNjustifyMode, (XawTextJustifyMode)i);
1217	XtSetValues(textwindow, arg, 1);
1218    }
1219    else {
1220	XtSetArg(arg[0], XtNjustifyMode, &justify);
1221	XtGetValues(textwindow, arg, 1);
1222	i = (int)justify;
1223	if (i <= 0 || i >= 4)
1224	    i = 0;
1225	value = justify_modes[i];
1226    }
1227
1228    return (value);
1229}
1230
1231LispObj *
1232Xedit_LeftColumn(LispBuiltin *builtin)
1233/*
1234 left-column &optional left
1235 */
1236{
1237    short left;
1238    Arg arg[1];
1239
1240    LispObj *oleft;
1241
1242    oleft = ARGUMENT(0);
1243
1244    if (oleft != UNSPEC) {
1245	CHECK_INDEX(oleft);
1246	if (FIXNUM_VALUE(oleft) >= 32767)
1247	    left = 32767;
1248	else
1249	    left = FIXNUM_VALUE(oleft);
1250
1251	XtSetArg(arg[0], XtNleftColumn, left);
1252	XtSetValues(textwindow, arg, 1);
1253    }
1254    else {
1255	XtSetArg(arg[0], XtNleftColumn, &left);
1256	XtGetValues(textwindow, arg, 1);
1257
1258	oleft = FIXNUM((long)left);
1259    }
1260
1261    return (oleft);
1262}
1263
1264LispObj *
1265Xedit_Point(LispBuiltin *builtin)
1266/*
1267 point
1268 */
1269{
1270    return (FIXNUM(XawTextGetInsertionPoint(textwindow)));
1271}
1272
1273LispObj *
1274Xedit_PointMax(LispBuiltin *builtin)
1275/*
1276 point-max
1277 */
1278{
1279    return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
1280				     XawstAll, XawsdRight, 1, True)));
1281}
1282
1283LispObj *
1284Xedit_PointMin(LispBuiltin *builtin)
1285/*
1286 point-min
1287 */
1288{
1289    return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
1290				     XawstAll, XawsdLeft, 1, True)));
1291}
1292
1293LispObj *
1294Xedit_PropertyList(LispBuiltin *builtin)
1295/*
1296 property-list &optional value
1297 */
1298{
1299    Arg arg[1];
1300    XawTextPropertyList *property_list;
1301
1302    LispObj *value;
1303
1304    value = ARGUMENT(0);
1305
1306    if (value != UNSPEC) {
1307	Cardinal i;
1308	XrmQuark quark;
1309
1310	CHECK_LONGINT(value);
1311	property_list = NULL;
1312	quark = LONGINT_VALUE(value);
1313	for (i = 0; i < num_property_lists; i++)
1314	    if (property_lists[i]->identifier == quark) {
1315		property_list = property_lists[i];
1316		break;
1317	    }
1318
1319	if (property_list) {
1320	    XtSetArg(arg[0], XawNtextProperties, property_list);
1321	    XtSetValues(XawTextGetSink(textwindow), arg, 1);
1322	}
1323	else
1324	    /* Maybe should generate an error here */
1325	    value = NIL;
1326    }
1327    else {
1328	XtSetArg(arg[0], XawNtextProperties, &property_list);
1329	XtGetValues(XawTextGetSink(textwindow), arg, 1);
1330	if (property_list)
1331	    value = INTEGER(property_list->identifier);
1332    }
1333
1334    return (value);
1335}
1336
1337LispObj *
1338Xedit_ReadText(LispBuiltin *builtin)
1339/*
1340 read-text offset length
1341 */
1342{
1343    XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
1344					     XawstAll, XawsdRight, 1, True);
1345    XawTextPosition from, to, len;
1346    XawTextBlock block;
1347    char *string, *ptr;
1348
1349    LispObj *offset, *length;
1350
1351    length = ARGUMENT(1);
1352    offset = ARGUMENT(0);
1353
1354    CHECK_INDEX(offset);
1355    CHECK_INDEX(length);
1356
1357    from = FIXNUM_VALUE(offset);
1358    to = from + FIXNUM_VALUE(length);
1359    if (from > last)
1360	from = last;
1361    if (to > last)
1362	to = last;
1363
1364    if (from == to)
1365	return (STRING(""));
1366
1367    len = to - from;
1368    string = LispMalloc(len);
1369
1370    for (ptr = string; from < to;) {
1371	XawTextSourceRead(XawTextGetSource(textwindow), from, &block, to - from);
1372	memcpy(ptr, block.ptr, block.length);
1373	ptr += block.length;
1374	from += block.length;
1375    }
1376
1377    return (LSTRING2(string, len));
1378}
1379
1380LispObj *
1381Xedit_ReplaceText(LispBuiltin *builtin)
1382/*
1383 replace-text left right text
1384 */
1385{
1386    XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
1387					     XawstAll, XawsdRight, 1, True);
1388    XawTextPosition left, right;
1389    XawTextBlock block;
1390
1391    LispObj *oleft, *oright, *text;
1392
1393    text = ARGUMENT(2);
1394    oright = ARGUMENT(1);
1395    oleft = ARGUMENT(0);
1396
1397    CHECK_INDEX(oleft);
1398    CHECK_INDEX(oright);
1399    CHECK_STRING(text);
1400
1401    left = FIXNUM_VALUE(oleft);
1402    right = FIXNUM_VALUE(oright);
1403    if (left > last)
1404	left = last;
1405    if (left > right)
1406	right = left;
1407    else if (right > last)
1408	right = last;
1409
1410    block.firstPos = 0;
1411    block.format = FMT8BIT;
1412    block.length = STRLEN(text);
1413    block.ptr = THESTR(text);
1414    XawTextReplace(textwindow, left, right, &block);
1415
1416    return (text);
1417}
1418
1419LispObj *
1420Xedit_RightColumn(LispBuiltin *builtin)
1421/*
1422 right-column &optional right
1423 */
1424{
1425    short right;
1426    Arg arg[1];
1427
1428    LispObj *oright;
1429
1430    oright = ARGUMENT(0);
1431
1432    if (oright != UNSPEC) {
1433	CHECK_INDEX(oright);
1434	if (FIXNUM_VALUE(oright) >= 32767)
1435	    right = 32767;
1436	else
1437	    right = FIXNUM_VALUE(oright);
1438
1439	XtSetArg(arg[0], XtNrightColumn, right);
1440	XtSetValues(textwindow, arg, 1);
1441    }
1442    else {
1443	XtSetArg(arg[0], XtNrightColumn, &right);
1444	XtGetValues(textwindow, arg, 1);
1445
1446	oright = FIXNUM(right);
1447    }
1448
1449    return (oright);
1450}
1451
1452LispObj *
1453Xedit_Scan(LispBuiltin *builtin)
1454/*
1455 scan offset type direction &key count include
1456 */
1457{
1458    int i;
1459    XawTextPosition offset;
1460    XawTextScanType type;
1461    XawTextScanDirection direction;
1462    int count;
1463
1464    LispObj *ooffset, *otype, *odirection, *ocount, *include;
1465
1466    include = ARGUMENT(4);
1467    if (include == UNSPEC)
1468	include = NIL;
1469    ocount = ARGUMENT(3);
1470    odirection = ARGUMENT(2);
1471    otype = ARGUMENT(1);
1472    ooffset = ARGUMENT(0);
1473
1474    CHECK_INDEX(ooffset);
1475    offset = FIXNUM_VALUE(ooffset);
1476
1477    for (i = 0; i < 2; i++)
1478	if (odirection == scan_directions[i])
1479	    break;
1480    if (i >= 2)
1481	LispDestroy("%s: direction must be "
1482		    ":LEFT or :RIGHT, not %s",
1483		    STRFUN(builtin), STROBJ(odirection));
1484    direction = (XawTextScanDirection)i;
1485
1486    for (i = 0; i < 6; i++)
1487	if (otype == scan_types[i])
1488	    break;
1489    if (i >= 6)
1490	LispDestroy("%s: direction must be "
1491		    ":POSITIONS, :WHITE-SPACE, :EOL, "
1492		    ":PARAGRAPH, :ALL, or :ALPHA-NUMERIC, not %s",
1493		    STRFUN(builtin), STROBJ(otype));
1494    type = (XawTextScanType)i;
1495
1496    if (ocount == UNSPEC)
1497	count = 1;
1498    else {
1499	CHECK_INDEX(ocount);
1500	count = FIXNUM_VALUE(ocount);
1501    }
1502
1503    offset = XawTextSourceScan(XawTextGetSource(textwindow),
1504			       offset, type, direction, count,
1505			       include != NIL);
1506
1507    return (FIXNUM(offset));
1508}
1509
1510static LispObj *
1511XeditSearch(LispBuiltin *builtin, XawTextScanDirection direction)
1512{
1513    XawTextBlock block;
1514    XawTextPosition position;
1515
1516    LispObj *string, *offset, *ignore_case;
1517
1518    ignore_case = ARGUMENT(2);
1519    offset = ARGUMENT(1);
1520    string = ARGUMENT(0);
1521
1522    CHECK_STRING(string);
1523    if (offset != UNSPEC) {
1524	CHECK_INDEX(offset);
1525	position = FIXNUM_VALUE(offset);
1526    }
1527    else
1528	position = XawTextGetInsertionPoint(textwindow);
1529
1530    block.firstPos = (ignore_case != UNSPEC && ignore_case != NIL) ? 1 : 0;
1531    block.format = FMT8BIT;
1532    block.length = STRLEN(string);
1533    block.ptr = THESTR(string);
1534    position = XawTextSourceSearch(XawTextGetSource(textwindow),
1535				   position, direction, &block);
1536
1537    return (position != XawTextSearchError ? FIXNUM(position) : NIL);
1538}
1539
1540
1541LispObj *
1542Xedit_SearchBackward(LispBuiltin *builtin)
1543/*
1544 search-backward string &optional offset ignore-case
1545 */
1546{
1547    return (XeditSearch(builtin, XawsdLeft));
1548}
1549
1550LispObj *
1551Xedit_SearchForward(LispBuiltin *builtin)
1552/*
1553 search-forward string &optional offset ignore-case
1554 */
1555{
1556    return (XeditSearch(builtin, XawsdRight));
1557}
1558
1559LispObj *
1560Xedit_VerticalScrollbar(LispBuiltin *builtin)
1561/*
1562 vertical-scrollbar &optional state
1563 */
1564{
1565    Arg arg[1];
1566    XawTextScrollMode scroll;
1567
1568    LispObj *state;
1569
1570    state = ARGUMENT(0);
1571
1572    if (state != UNSPEC) {
1573	scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
1574	XtSetArg(arg[0], XtNscrollVertical, scroll);
1575	XtSetValues(textwindow, arg, 1);
1576    }
1577    else {
1578	XtSetArg(arg[0], XtNscrollVertical, &scroll);
1579	XtGetValues(textwindow, arg, 1);
1580	state = scroll == XawtextScrollAlways ? T : NIL;
1581    }
1582
1583    return (state);
1584}
1585
1586LispObj *
1587Xedit_WrapMode(LispBuiltin *builtin)
1588/*
1589 wrap-mode &optional value
1590 */
1591{
1592    int i;
1593    Arg arg[1];
1594    XawTextWrapMode wrap;
1595
1596    LispObj *value;
1597
1598    value = ARGUMENT(0);
1599
1600    if (value != UNSPEC) {
1601	for (i = 0; i < 3; i++)
1602	    if (value == wrap_modes[i])
1603		break;
1604	if (i >= 3)
1605	    LispDestroy("%s: argument must be "
1606			":NEVER, :LINE, or :WORD, not %s",
1607			STRFUN(builtin), STROBJ(value));
1608	XtSetArg(arg[0], XtNwrap, (XawTextWrapMode)i);
1609	XtSetValues(textwindow, arg, 1);
1610    }
1611    else {
1612	XtSetArg(arg[0], XtNwrap, &wrap);
1613	XtGetValues(textwindow, arg, 1);
1614	i = (int)wrap;
1615	if (i <= 0 || i >= 3)
1616	    i = 0;
1617	value = wrap_modes[i];
1618    }
1619
1620    return (value);
1621}
1622
1623LispObj *
1624Xedit_XrmStringToQuark(LispBuiltin *builtin)
1625/*
1626 xrm-string-to-quark string
1627 */
1628{
1629    LispObj *string;
1630
1631    string = ARGUMENT(0);
1632
1633    CHECK_STRING(string);
1634
1635    return (INTEGER(XrmStringToQuark(THESTR(string))));
1636}
1637