AsciiSrc.c revision 775e7de9
1/* $Xorg: AsciiSrc.c,v 1.4 2001/02/09 02:03:42 xorgcvs Exp $ */
2
3/*
4
5Copyright 1989, 1994, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall not be
24used in advertising or otherwise to promote the sale, use or other dealings
25in this Software without prior written authorization from The Open Group.
26
27*/
28
29/* $XFree86: xc/lib/Xaw/AsciiSrc.c,v 1.33 2002/07/04 17:04:20 paulo Exp $ */
30
31/*
32 * AsciiSrc.c - AsciiSrc object. (For use with the text widget).
33 *
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include <stdio.h>
40#include <stdlib.h>
41#include <ctype.h>
42#include <errno.h>
43#include <X11/IntrinsicP.h>
44#include <X11/StringDefs.h>
45#include <X11/Xos.h>
46#include <X11/Xfuncs.h>
47#include <X11/Xmu/CharSet.h>
48#include <X11/Xmu/Misc.h>
49#include <X11/Xaw/XawInit.h>
50#include <X11/Xaw/AsciiSrcP.h>
51#include <X11/Xaw/MultiSrcP.h>
52#ifndef OLDXAW
53#include <X11/Xaw/TextSinkP.h>
54#include <X11/Xaw/AsciiSinkP.h>
55#endif
56#include "Private.h"
57
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <fcntl.h>
61
62#if (defined(ASCII_STRING) || defined(ASCII_DISK))
63#include <X11/Xaw/AsciiText.h>		/* for Widget Classes */
64#endif
65
66#ifdef X_NOT_POSIX
67#define Off_t long
68#define Size_t unsigned int
69#else
70#define Off_t off_t
71#define Size_t size_t
72#endif
73
74#define MAGIC_VALUE	((XawTextPosition)-1)
75#define streq(a, b)	(strcmp((a), (b)) == 0)
76
77/*
78 * Class Methods
79 */
80static void XawAsciiSrcClassInitialize(void);
81static void XawAsciiSrcDestroy(Widget);
82static void XawAsciiSrcGetValuesHook(Widget, ArgList, Cardinal*);
83static void XawAsciiSrcInitialize(Widget, Widget, ArgList, Cardinal*);
84static Boolean XawAsciiSrcSetValues(Widget, Widget, Widget,
85				    ArgList, Cardinal*);
86static XawTextPosition ReadText(Widget, XawTextPosition, XawTextBlock*, int);
87static int ReplaceText(Widget, XawTextPosition, XawTextPosition,
88		       XawTextBlock*);
89static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType,
90			    XawTextScanDirection, int, Bool);
91static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection,
92			      XawTextBlock*);
93
94/*
95 * Prototypes
96 */
97static Piece *AllocNewPiece(AsciiSrcObject, Piece*);
98static void BreakPiece(AsciiSrcObject, Piece*);
99static Boolean CvtAsciiTypeToString(Display*, XrmValuePtr, Cardinal*,
100				    XrmValuePtr, XrmValuePtr, XtPointer*);
101static void CvtStringToAsciiType(XrmValuePtr, Cardinal*,
102				 XrmValuePtr, XrmValuePtr);
103static Piece *FindPiece(AsciiSrcObject, XawTextPosition, XawTextPosition*);
104static void FreeAllPieces(AsciiSrcObject);
105static FILE *InitStringOrFile(AsciiSrcObject, Bool);
106static void LoadPieces(AsciiSrcObject, FILE*, char*);
107static void RemoveOldStringOrFile(AsciiSrcObject, Bool);
108static void RemovePiece(AsciiSrcObject, Piece*);
109static String StorePiecesInString(AsciiSrcObject);
110static Bool WriteToFile(String, String, unsigned);
111static Bool WritePiecesToFile(AsciiSrcObject, String);
112static void GetDefaultPieceSize(Widget, int, XrmValue*);
113
114/*
115 * More Prototypes
116 */
117#ifdef ASCII_DISK
118Widget XawAsciiDiskSourceCreate(Widget, ArgList, Cardinal);
119#endif
120#ifdef ASCII_STRING
121Widget XawStringSourceCreate(Widget, ArgList, Cardinal);
122void XawTextSetLastPos(Widget, XawTextPosition);
123#endif
124
125/*
126 * Initialization
127 */
128#define offset(field) XtOffsetOf(AsciiSrcRec, ascii_src.field)
129static XtResource resources[] = {
130  {
131    XtNstring,
132    XtCString,
133    XtRString,
134    sizeof(char*),
135    offset(string),
136    XtRString,
137    NULL
138  },
139  {
140    XtNtype,
141    XtCType,
142    XtRAsciiType,
143    sizeof(XawAsciiType),
144    offset(type),
145    XtRImmediate,
146    (XtPointer)XawAsciiString
147  },
148  {
149    XtNdataCompression,
150    XtCDataCompression,
151    XtRBoolean,
152    sizeof(Boolean),
153    offset(data_compression),
154    XtRImmediate,
155    (XtPointer)True
156  },
157  {
158    XtNpieceSize,
159    XtCPieceSize,
160    XtRInt,
161    sizeof(XawTextPosition),
162    offset(piece_size),
163    XtRCallProc,
164    (XtPointer)GetDefaultPieceSize
165  },
166#ifdef OLDXAW
167  {
168    XtNcallback,
169    XtCCallback,
170    XtRCallback,
171    sizeof(XtPointer),
172    offset(callback),
173    XtRCallback,
174    (XtPointer)NULL
175  },
176#endif
177  {
178    XtNuseStringInPlace,
179    XtCUseStringInPlace,
180    XtRBoolean,
181    sizeof(Boolean),
182    offset(use_string_in_place),
183    XtRImmediate,
184    (XtPointer)False
185  },
186  {
187    XtNlength,
188    XtCLength,
189    XtRInt,
190    sizeof(int),
191    offset(ascii_length),
192    XtRImmediate,
193    (XtPointer)MAGIC_VALUE
194  },
195#ifdef ASCII_DISK
196  {
197    XtNfile,
198    XtCFile,
199    XtRString,
200    sizeof(String),
201    offset(filename),
202    XtRString,
203    NULL
204  },
205#endif /* ASCII_DISK */
206};
207#undef offset
208
209
210#define Superclass	(&textSrcClassRec)
211AsciiSrcClassRec asciiSrcClassRec = {
212  /* object */
213  {
214    (WidgetClass)Superclass,		/* superclass */
215    "AsciiSrc",				/* class_name */
216    sizeof(AsciiSrcRec),		/* widget_size */
217    XawAsciiSrcClassInitialize,		/* class_initialize */
218    NULL,				/* class_part_initialize */
219    False,				/* class_inited */
220    XawAsciiSrcInitialize,		/* initialize */
221    NULL,				/* initialize_hook */
222    NULL,				/* realize */
223    NULL,				/* actions */
224    0,					/* num_actions */
225    resources,				/* resources */
226    XtNumber(resources),		/* num_resources */
227    NULLQUARK,				/* xrm_class */
228    False,				/* compress_motion */
229    False,				/* compress_exposure */
230    False,				/* compress_enterleave */
231    False,				/* visible_interest */
232    XawAsciiSrcDestroy,			/* destroy */
233    NULL,				/* resize */
234    NULL,				/* expose */
235    XawAsciiSrcSetValues,		/* set_values */
236    NULL,				/* set_values_hook */
237    NULL,				/* set_values_almost */
238    XawAsciiSrcGetValuesHook,		/* get_values_hook */
239    NULL,				/* accept_focus */
240    XtVersion,				/* version */
241    NULL,				/* callback_private */
242    NULL,				/* tm_table */
243    NULL,				/* query_geometry */
244    NULL,				/* display_accelerator */
245    NULL,				/* extension */
246  },
247  /* text_src */
248  {
249    ReadText,				/* Read */
250    ReplaceText,			/* Replace */
251    Scan,				/* Scan */
252    Search,				/* Search */
253    XtInheritSetSelection,		/* SetSelection */
254    XtInheritConvertSelection,		/* ConvertSelection */
255  },
256  /* ascii_src */
257  {
258    NULL,				/* extension */
259  },
260};
261
262WidgetClass asciiSrcObjectClass = (WidgetClass)&asciiSrcClassRec;
263
264static XrmQuark Qstring, Qfile;
265
266/*
267 * Implementation
268 */
269/*
270 * Function:
271 *	XawAsciiSrcClassInitialize()
272 *
273 * Description:
274 *	  Initializes the asciiSrcObjectClass and install the converters for
275 *	AsciiType <-> String.
276 */
277static void
278XawAsciiSrcClassInitialize(void)
279{
280    XawInitializeWidgetSet();
281    Qstring = XrmPermStringToQuark(XtEstring);
282    Qfile = XrmPermStringToQuark(XtEfile);
283    XtAddConverter(XtRString, XtRAsciiType, CvtStringToAsciiType, NULL, 0);
284    XtSetTypeConverter(XtRAsciiType, XtRString, CvtAsciiTypeToString,
285		       NULL, 0, XtCacheNone, NULL);
286}
287
288/*
289 * Function:
290 *	XawAsciiSrcInitialize
291 *
292 * Parameters:
293 *	request	 - widget requested by the argument list
294 *	cnew	 - new widget with both resource and non resource values
295 *	args	 - (unused)
296 *	num_args - (unused)
297 *
298 * Description:
299 *	Initializes the ascii src object.
300 */
301/*ARGSUSED*/
302static void
303XawAsciiSrcInitialize(Widget request, Widget cnew,
304		      ArgList args, Cardinal *num_args)
305{
306    AsciiSrcObject src = (AsciiSrcObject)cnew;
307    FILE *file;
308
309    /*
310     * Set correct flags (override resources) depending upon widget class
311     */
312    src->text_src.text_format = XawFmt8Bit;
313
314#ifdef ASCII_DISK
315    if (XtIsSubclass(XtParent(cnew), asciiDiskWidgetClass)) {
316	src->ascii_src.type = XawAsciiFile;
317	src->ascii_src.string = src->ascii_src.filename;
318    }
319#endif
320
321#ifdef ASCII_STRING
322    if (XtIsSubclass(XtParent(cnew), asciiStringWidgetClass)) {
323	src->ascii_src.use_string_in_place = True;
324	src->ascii_src.type = XawAsciiString;
325    }
326#endif
327
328#ifdef OLDXAW
329    src->ascii_src.changes = False;
330#else
331    src->text_src.changed = False;
332#endif
333    src->ascii_src.allocated_string = False;
334
335    if (src->ascii_src.use_string_in_place && src->ascii_src.string == NULL)
336	src->ascii_src.use_string_in_place = False;
337
338    file = InitStringOrFile(src, src->ascii_src.type == XawAsciiFile);
339    LoadPieces(src, file, NULL);
340
341    if (file != NULL)
342	fclose(file);
343}
344
345/*
346 * Function:
347 *	ReadText
348 *
349 * Parameters:
350 *	w	- AsciiSource widget
351 *	pos	- position of the text to retreive.
352 *	text	- text block that will contain returned text
353 *	length	- maximum number of characters to read
354 *
355 * Description:
356 *	This function reads the source.
357 *
358 * Returns:
359 *	The character position following the retrieved text.
360 */
361static XawTextPosition
362ReadText(Widget w, XawTextPosition pos, XawTextBlock *text, int length)
363{
364    AsciiSrcObject src = (AsciiSrcObject)w;
365    XawTextPosition count, start;
366    Piece *piece;
367#ifndef OLDXAW
368    XawTextAnchor *anchor;
369    XawTextEntity *entity;
370    XawTextPosition offset, end = pos + length;
371    Bool state;
372
373    end = XawMin(end, src->ascii_src.length);
374    while ((state = XawTextSourceAnchorAndEntity(w, pos, &anchor, &entity)) &&
375	(entity->flags & XAW_TENTF_HIDE))
376	pos = anchor->position + entity->offset + entity->length;
377    if (state == False ||
378	!(entity->flags & XAW_TENTF_REPLACE)) {
379	while (entity) {
380	    offset = anchor->position + entity->offset;
381	    if (offset >= end)
382		break;
383	    if (offset > pos &&
384		(entity->flags & (XAW_TENTF_HIDE | XAW_TENTF_REPLACE))) {
385		end = XawMin(end, offset);
386		break;
387	    }
388	    if ((entity = entity->next) == NULL &&
389		(anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)
390		entity = anchor->entities;
391	}
392    }
393    else if (state && (entity->flags & XAW_TENTF_REPLACE) && pos < end) {
394	XawTextBlock *block = (XawTextBlock*)entity->data;
395
396	offset = anchor->position + entity->offset;
397	end = XawMin(end, offset + block->length);
398	if ((length = end - pos) < 0)
399	    length = 0;
400	text->length = length;
401	text->format = XawFmt8Bit;
402	if (length == 0) {
403	    text->firstPos = end = offset + entity->length;
404	    text->ptr = "";
405	}
406	else {
407	    text->firstPos = pos;
408	    text->ptr = block->ptr + (pos - offset);
409	    if (pos + length < offset + block->length)
410		end = pos + length;	/* there is data left to be read */
411	    else
412		end = offset + entity->length;
413	}
414
415	return (end);
416    }
417
418    if ((length = end - pos) < 0)
419	length = 0;
420#endif
421
422    piece = FindPiece(src, pos, &start);
423    text->firstPos = pos;
424    text->ptr = piece->text + (pos - start);
425    count = piece->used - (pos - start);
426    text->length = Max(0, (length > count) ? count : length);
427    text->format = XawFmt8Bit;
428
429    return (pos + text->length);
430}
431
432/*
433 * Function:
434 *	ReplaceText
435 *
436 * Parameters:
437 *	w	 - AsciiSource object
438 *	startPos - ends of text that will be replaced
439 *	endPos	 - ""
440 *	text	 - new text to be inserted into buffer at startPos
441 *
442 * Description:
443 *	Replaces a block of text with new text.
444 *
445 * Returns:
446 *	XawEditDone on success, XawEditError otherwise
447 */
448/*ARGSUSED*/
449static int
450ReplaceText(Widget w, XawTextPosition startPos, XawTextPosition endPos,
451	    XawTextBlock *text)
452{
453    AsciiSrcObject src = (AsciiSrcObject)w;
454    Piece *start_piece, *end_piece, *temp_piece;
455    XawTextPosition start_first, end_first;
456    int length, firstPos;
457
458    /*
459     * Editing a read only source is not allowed
460     */
461    if (src->text_src.edit_mode == XawtextRead)
462	return (XawEditError);
463
464    start_piece = FindPiece(src, startPos, &start_first);
465    end_piece = FindPiece(src, endPos, &end_first);
466
467#ifndef OLDXAW
468    /*
469     * This is a big hack, but I can't think about a clever way to know
470     * if the character being moved forward has a negative lbearing.
471     *
472     */
473    if (start_piece->used) {
474	int i;
475
476	for (i = 0; i < src->text_src.num_text; i++) {
477	    int line;
478	    TextWidget ctx = (TextWidget)src->text_src.text[i];
479
480	    for (line = 0; line < ctx->text.lt.lines; line++)
481		if (startPos < ctx->text.lt.info[line + 1].position)
482		    break;
483	    if (i < ctx->text.lt.lines &&
484		startPos > ctx->text.lt.info[i].position) {
485		AsciiSinkObject sink = (AsciiSinkObject)ctx->text.sink;
486		XawTextAnchor *anchor;
487		XawTextEntity *entity;
488		XawTextProperty *property;
489		XFontStruct *font;
490
491		if (XawTextSourceAnchorAndEntity(w, startPos, &anchor, &entity) &&
492		    (property = XawTextSinkGetProperty(ctx->text.sink,
493						       entity->property)) != NULL &&
494		    (property->mask & XAW_TPROP_FONT))
495		    font = property->font;
496		else
497		    font = sink->ascii_sink.font;
498
499		if (font->min_bounds.lbearing < 0) {
500		    int lbearing = font->min_bounds.lbearing;
501		    unsigned char c = *(unsigned char*)
502			(start_piece->text + (startPos - start_first));
503
504		    if (c == '\t' || c == '\n')
505			c = ' ';
506		    else if ((c & 0177) < XawSP || c == 0177) {
507			if (sink->ascii_sink.display_nonprinting)
508			    c = c > 0177 ? '\\' : c + '^';
509			else
510			    c = ' ';
511		    }
512		    if (font->per_char &&
513			(c >= font->min_char_or_byte2 && c <= font->max_char_or_byte2))
514			lbearing = font->per_char[c - font->min_char_or_byte2].lbearing;
515		    if (lbearing < 0)
516			_XawTextNeedsUpdating(ctx, startPos - 1, startPos);
517		}
518	    }
519	}
520    }
521
522
523#endif
524
525    /*
526     * Remove Old Stuff
527     */
528    if (start_piece != end_piece) {
529	temp_piece = start_piece->next;
530
531	/*
532	 * If empty and not the only piece then remove it.
533	 */
534	if (((start_piece->used = startPos - start_first) == 0)
535	    && !(start_piece->next == NULL && start_piece->prev == NULL))
536	    RemovePiece(src, start_piece);
537
538	while (temp_piece != end_piece) {
539	    temp_piece = temp_piece->next;
540	    RemovePiece(src, temp_piece->prev);
541	}
542
543	end_piece->used -= endPos - end_first;
544	if (end_piece->used != 0)
545	    memmove(end_piece->text, end_piece->text + endPos - end_first,
546		    (unsigned)end_piece->used);
547    }
548    else {		    /* We are fully in one piece */
549	if ((start_piece->used -= endPos - startPos) == 0) {
550	    if (!(start_piece->next == NULL && start_piece->prev == NULL))
551		RemovePiece(src, start_piece);
552	}
553	else {
554	    memmove(start_piece->text + (startPos - start_first),
555		    start_piece->text + (endPos - start_first),
556		    (unsigned)(start_piece->used - (startPos - start_first)));
557	    if (src->ascii_src.use_string_in_place
558		&& src->ascii_src.length - (endPos - startPos)
559		< src->ascii_src.piece_size - 1)
560		start_piece->text[src->ascii_src.length - (endPos - startPos)] =
561		    '\0';
562	}
563    }
564
565    src->ascii_src.length += -(endPos - startPos) + text->length;
566
567    if ( text->length != 0) {
568	/*
569	 * Put in the New Stuff
570	 */
571	start_piece = FindPiece(src, startPos, &start_first);
572
573	length = text->length;
574	firstPos = text->firstPos;
575
576	while (length > 0) {
577	    char *ptr;
578	    int fill;
579
580	    if (src->ascii_src.use_string_in_place) {
581		if (start_piece->used == src->ascii_src.piece_size - 1) {
582		    /*
583		     * If we are in ascii string emulation mode. Then the
584		     *	string is not allowed to grow
585		     */
586		    start_piece->used = src->ascii_src.length =
587			src->ascii_src.piece_size - 1;
588		    start_piece->text[src->ascii_src.length] = '\0';
589		    return (XawEditError);
590		}
591	    }
592
593	    if (start_piece->used == src->ascii_src.piece_size) {
594		BreakPiece(src, start_piece);
595		start_piece = FindPiece(src, startPos, &start_first);
596	    }
597
598	    fill = Min((int)(src->ascii_src.piece_size - start_piece->used),
599		       length);
600
601	    ptr = start_piece->text + (startPos - start_first);
602	    memmove(ptr + fill, ptr,
603		    (unsigned)(start_piece->used - (startPos - start_first)));
604	    memcpy(ptr, text->ptr + firstPos, (unsigned)fill);
605
606	    startPos += fill;
607	    firstPos += fill;
608	    start_piece->used += fill;
609	    length -= fill;
610	}
611    }
612
613    if (src->ascii_src.use_string_in_place)
614	start_piece->text[start_piece->used] = '\0';
615
616#ifdef OLDXAW
617    src->ascii_src.changes = True;
618    XtCallCallbacks(w, XtNcallback, NULL);
619#endif
620
621    return (XawEditDone);
622}
623
624/*
625 * Function:
626 *	Scan
627 *
628 * Parameters:
629 *	w	 - AsciiSource object
630 *	position - position to start scanning
631 *	type	 - type of thing to scan for
632 *	dir	 - direction to scan
633 *		   count - which occurance if this thing to search for.
634 *		   include - whether or not to include the character found in
635 *		   the position that is returned
636 *
637 * Description:
638 *	Scans the text source for the number and type of item specified.
639 *
640 * Returns:
641 *	The position of the item found
642 *
643 * Note:
644 *	  While there are only 'n' characters in the file there are n+1
645 *	 possible cursor positions (one before the first character and
646 *	one after the last character
647 */
648static XawTextPosition
649Scan(Widget w, register XawTextPosition position, XawTextScanType type,
650     XawTextScanDirection dir, int count, Bool include)
651{
652    AsciiSrcObject src = (AsciiSrcObject)w;
653    Piece *piece;
654    XawTextPosition first, first_eol_position = 0;
655    register char *ptr, *lim;
656    register int cnt = count;
657    register unsigned char c;
658
659    if (dir == XawsdLeft) {
660	if (position <= 0)
661	    return (0);
662	--position;
663    }
664    else if (position >= src->ascii_src.length)
665	return (src->ascii_src.length);
666
667    piece = FindPiece(src, position, &first);
668    if (piece->used == 0)
669	return (0);
670
671    ptr = (position - first) + piece->text;
672
673    if (dir == XawsdRight) {
674	lim = piece->text + piece->used;
675	switch (type) {
676	    case XawstEOL:
677	    case XawstParagraph:
678	    case XawstWhiteSpace:
679	    case XawstAlphaNumeric:
680		for (; cnt > 0; cnt--) {
681		    Bool non_space = False, first_eol = True;
682
683		    /*CONSTCOND*/
684		    while (True) {
685			if (ptr >= lim) {
686			    piece = piece->next;
687			    if (piece == NULL)	/* End of text */
688				return (src->ascii_src.length);
689			    ptr = piece->text;
690			    lim = piece->text + piece->used;
691			}
692
693			c = *ptr++;
694			++position;
695
696			if (type == XawstEOL) {
697			    if (c == '\n')
698				break;
699			}
700			else if (type == XawstAlphaNumeric) {
701			    if (!isalnum(c)) {
702				if (non_space)
703				    break;
704			    }
705			    else
706				non_space = True;
707			}
708			else if (type == XawstWhiteSpace) {
709			    if (isspace(c)) {
710				if (non_space)
711				    break;
712			    }
713			    else
714				non_space = True;
715			}
716			else {	/* XawstParagraph */
717			    if (first_eol) {
718				if (c == '\n') {
719				    first_eol_position = position;
720				    first_eol = False;
721				}
722			    }
723			    else if (c == '\n')
724				break;
725			    else if (!isspace(c))
726				first_eol = True;
727			}
728		    }
729		}
730		break;
731	    case XawstPositions:
732		position += count;
733		return (position < src->ascii_src.length ?
734			position : src->ascii_src.length);
735	    case XawstAll:
736		return (src->ascii_src.length);
737	    default:
738		break;
739	}
740	if (!include) {
741	    if (type == XawstParagraph)
742		position = first_eol_position;
743	    if (count)
744		--position;
745	}
746    }
747    else {
748	lim = piece->text;
749	switch (type) {
750	    case XawstEOL:
751	    case XawstParagraph:
752	    case XawstWhiteSpace:
753	    case XawstAlphaNumeric:
754		for (; cnt > 0; cnt--) {
755		    Bool non_space = False, first_eol = True;
756
757		    /*CONSTCOND*/
758		    while (True) {
759			if (ptr < lim) {
760			    piece = piece->prev;
761			    if (piece == NULL)	/* Begining of text */
762				return (0);
763			    ptr = piece->text + piece->used - 1;
764			    lim = piece->text;
765			}
766
767			c = *ptr--;
768			--position;
769
770			if (type == XawstEOL) {
771			    if (c == '\n')
772				break;
773			}
774			else if (type == XawstAlphaNumeric) {
775			    if (!isalnum(c)) {
776				if (non_space)
777				    break;
778			    }
779			    else
780				non_space = True;
781			}
782			else if (type == XawstWhiteSpace) {
783			    if (isspace(c)) {
784				if (non_space)
785				    break;
786			    }
787			    else
788				non_space = True;
789			}
790			else {	/* XawstParagraph */
791			    if (first_eol) {
792				if (c == '\n') {
793				    first_eol_position = position;
794				    first_eol = False;
795				}
796			    }
797			    else if (c == '\n')
798				break;
799			    else if (!isspace(c))
800				first_eol = True;
801			}
802		    }
803		}
804		break;
805	    case XawstPositions:
806		position -= count - 1;
807		return (position > 0 ? position : 0);
808	    case XawstAll:
809		return (0);
810	    default:
811		break;
812	}
813	if (!include) {
814	    if (type == XawstParagraph)
815		position = first_eol_position;
816	    if (count)
817		++position;
818	}
819	position++;
820    }
821
822    return (position);
823}
824
825/*
826 * Function:
827 *	Search
828 *
829 * Parameters:
830 *	w	 - AsciiSource object
831 *	position - the position to start scanning
832 *	dir	 - direction to scan
833 *	text	 - text block to search for
834 *
835 * Description:
836 *	Searchs the text source for the text block passed.
837 *
838 * Returns:
839 *	The position of the item found
840 */
841static XawTextPosition
842Search(Widget w, register XawTextPosition position, XawTextScanDirection dir,
843       XawTextBlock *text)
844{
845    AsciiSrcObject src = (AsciiSrcObject)w;
846    register int count = 0;
847    register char *ptr, c;
848    char *str;
849    Piece *piece;
850    char *buf;
851    XawTextPosition first;
852    int cnt, case_sensitive;
853
854    if (dir == XawsdLeft) {
855	if (position == 0)
856	    return (XawTextSearchError);
857	position--;
858    }
859
860    buf = XtMalloc((unsigned)sizeof(unsigned char) * text->length);
861    memcpy(buf, text->ptr, (unsigned)text->length);
862    piece = FindPiece(src, position, &first);
863    ptr = (position - first) + piece->text;
864    case_sensitive = text->firstPos;
865
866    if (dir == XawsdRight) {
867	str = buf;
868	c = *str;
869	/*CONSTCOND*/
870	while (1) {
871	    if (*ptr++ == c
872		|| (case_sensitive && isalpha(c) && isalpha(ptr[-1])
873		    && toupper(c) == toupper(ptr[-1]))) {
874		if (++count == text->length)
875		    break;
876		c = *++str;
877	    }
878	    else if (count) {
879		ptr -= count;
880		str -= count;
881		position -= count;
882		count = 0;
883		c = *str;
884
885		if (ptr < piece->text) {
886		    do {
887			cnt = piece->text - ptr;
888			piece = piece->prev;
889			if (piece == NULL) {
890			    XtFree(buf);
891			    return (XawTextSearchError);
892			}
893			ptr = piece->text + piece->used - cnt;
894		    } while (ptr < piece->text);
895		}
896	    }
897	    position++;
898	    if (ptr >= (piece->text + piece->used)) {
899		do {
900		    cnt = ptr - (piece->text + piece->used);
901		    piece = piece->next;
902		    if (piece == NULL) {
903			XtFree(buf);
904			return (XawTextSearchError);
905		    }
906		    ptr = piece->text + cnt;
907		} while (ptr >= (piece->text + piece->used));
908	    }
909	}
910
911	position -= text->length - 1;
912    }
913    else {
914	str = buf + text->length - 1;
915	c = *str;
916	/*CONSTCOND*/
917	while (1) {
918	    if (*ptr-- == c
919		|| (case_sensitive && isalpha(c) && isalpha(ptr[1])
920		    && toupper(c) == toupper(ptr[1]))) {
921		if (++count == text->length)
922		    break;
923		c = *--str;
924	    }
925	    else if (count) {
926		ptr += count;
927		str += count;
928		position += count;
929		count = 0;
930		c = *str;
931
932		if (ptr >= (piece->text + piece->used)) {
933		    do {
934			cnt = ptr - (piece->text + piece->used);
935			piece = piece->next;
936			if (piece == NULL) {
937			    XtFree(buf);
938			    return (XawTextSearchError);
939			}
940			ptr = piece->text + cnt;
941		    } while (ptr >= (piece->text + piece->used));
942		}
943	    }
944	    position--;
945	    if (ptr < piece->text) {
946		do {
947		    cnt = piece->text - ptr;
948		    piece = piece->prev;
949		    if (piece == NULL) {
950			XtFree(buf);
951			return (XawTextSearchError);
952		    }
953		    ptr = piece->text + piece->used - cnt;
954		} while (ptr < piece->text);
955	    }
956	}
957    }
958
959    XtFree(buf);
960
961    return (position);
962}
963
964/*
965 * Function:
966 *	XawAsciiSrcSetValues
967 *
968 * Parameters:
969 *	current  - current state of the widget
970 *	request  - what was requested
971 *	cnew	 - what the widget will become
972 *	args	 - representation of changed resources
973 *	num_args - number of resources that have changed
974 *
975 * Description:
976 *	Sets the values for the AsciiSource.
977 *
978 * Returns:
979 *	True if redisplay is needed
980 */
981static Boolean
982XawAsciiSrcSetValues(Widget current, Widget request, Widget cnew,
983		     ArgList args, Cardinal *num_args)
984{
985    AsciiSrcObject src = (AsciiSrcObject)cnew;
986    AsciiSrcObject old_src = (AsciiSrcObject)current;
987    Bool total_reset = False, string_set = False;
988    FILE *file;
989    unsigned int i;
990
991    if (old_src->ascii_src.use_string_in_place
992	!= src->ascii_src.use_string_in_place) {
993	XtAppWarning(XtWidgetToApplicationContext(cnew),
994		     "AsciiSrc: The XtNuseStringInPlace resource may "
995		     "not be changed.");
996	src->ascii_src.use_string_in_place =
997	    old_src->ascii_src.use_string_in_place;
998    }
999
1000    for (i = 0; i < *num_args ; i++)
1001	if (streq(args[i].name, XtNstring)) {
1002	    string_set = True;
1003	    break;
1004	}
1005
1006    if (string_set || (old_src->ascii_src.type != src->ascii_src.type)) {
1007	RemoveOldStringOrFile(old_src, string_set); /* remove old info */
1008	file = InitStringOrFile(src, string_set);   /* Init new info */
1009	LoadPieces(src, file, NULL);   /* load new info into internal buffers */
1010	if (file != NULL)
1011	    fclose(file);
1012#ifndef OLDXAW
1013	for (i = 0; i < src->text_src.num_text; i++)
1014	    /* Tell text widget what happened */
1015	    XawTextSetSource(src->text_src.text[i], cnew, 0);
1016#else
1017	XawTextSetSource(XtParent(cnew), cnew, 0);
1018#endif
1019	total_reset = True;
1020    }
1021
1022    if (old_src->ascii_src.ascii_length != src->ascii_src.ascii_length)
1023	src->ascii_src.piece_size = src->ascii_src.ascii_length + 1;
1024
1025    if (!total_reset &&
1026	old_src->ascii_src.piece_size != src->ascii_src.piece_size) {
1027	String string = StorePiecesInString(old_src);
1028
1029	FreeAllPieces(old_src);
1030	LoadPieces(src, NULL, string);
1031	XtFree(string);
1032    }
1033
1034    return (False);
1035}
1036
1037/*
1038 * Function:
1039 *	XawAsciiSrcGetValuesHook
1040 *
1041 * Parameters:
1042 *	w	 - AsciiSource Widget
1043 *	args	 - argument list
1044 *	num_args - number of args
1045 *
1046 * Description:
1047 *	  This is a get values hook routine that sets the
1048 *		     values specific to the ascii source.
1049 */
1050static void
1051XawAsciiSrcGetValuesHook(Widget w, ArgList args, Cardinal *num_args)
1052{
1053    AsciiSrcObject src = (AsciiSrcObject)w;
1054    unsigned int i;
1055
1056    if (src->ascii_src.type == XawAsciiString) {
1057	for (i = 0; i < *num_args ; i++)
1058	    if (streq(args[i].name, XtNstring)) {
1059		if (src->ascii_src.use_string_in_place)
1060		    *((char **)args[i].value) = src->ascii_src.first_piece->text;
1061		else if (XawAsciiSave(w))   /* If save sucessful */
1062		    *((char **)args[i].value) = src->ascii_src.string;
1063		break;
1064	    }
1065	}
1066    }
1067
1068/*
1069 * Function:
1070 *	XawAsciiSrcDestroy
1071 *
1072 * Parameters:
1073 *	src - Ascii source object to free
1074 *
1075 * Description:
1076 *	Destroys an ascii source (frees all data)
1077 */
1078static void
1079XawAsciiSrcDestroy(Widget w)
1080{
1081    RemoveOldStringOrFile((AsciiSrcObject) w, True);
1082}
1083
1084/*
1085 * Public routines
1086 */
1087/*
1088 * Function:
1089 *	XawAsciiSourceFreeString
1090 *
1091 * Parameters:
1092 *	w - AsciiSrc widget
1093 *
1094 * Description:
1095 *	  Frees the string returned by a get values call
1096 *		     on the string when the source is of type string.
1097 */
1098void
1099XawAsciiSourceFreeString(Widget w)
1100{
1101    AsciiSrcObject src = (AsciiSrcObject)w;
1102
1103    /* If the src is really a multi, call the multi routine */
1104    if (XtIsSubclass(w, multiSrcObjectClass)) {
1105	_XawMultiSourceFreeString(w);
1106	return;
1107    }
1108    else if (!XtIsSubclass(w, asciiSrcObjectClass)) {
1109	XtErrorMsg("bad argument", "asciiSource", "XawError",
1110		   "XawAsciiSourceFreeString's parameter must be "
1111		   "an asciiSrc or multiSrc.",
1112		   NULL, NULL);
1113    }
1114
1115    if (src->ascii_src.allocated_string && src->ascii_src.type != XawAsciiFile) {
1116	src->ascii_src.allocated_string = False;
1117	XtFree(src->ascii_src.string);
1118	src->ascii_src.string = NULL;
1119    }
1120}
1121
1122/*
1123 * Function:
1124 *	XawAsciiSave
1125 *
1126 * Parameters:
1127 *	w - asciiSrc Widget
1128 *
1129 * Description:
1130 *	Saves all the pieces into a file or string as required.
1131 *
1132 * Returns:
1133 *	True if the save was successful
1134 */
1135Bool
1136XawAsciiSave(Widget w)
1137{
1138    AsciiSrcObject src = (AsciiSrcObject)w;
1139
1140    /* If the src is really a multi, call the multi save */
1141    if (XtIsSubclass(w, multiSrcObjectClass ))
1142	return (_XawMultiSave(w));
1143
1144    else if (!XtIsSubclass(w, asciiSrcObjectClass))
1145	XtErrorMsg("bad argument", "asciiSource", "XawError",
1146		   "XawAsciiSave's parameter must be an asciiSrc or multiSrc.",
1147		   NULL, NULL);
1148
1149    /*
1150     * If using the string in place then there is no need to play games
1151     * to get the internal info into a readable string.
1152     */
1153    if (src->ascii_src.use_string_in_place)
1154	return (True);
1155
1156    if (src->ascii_src.type == XawAsciiFile) {
1157#ifdef OLDXAW
1158	if (!src->ascii_src.changes)
1159#else
1160	if (!src->text_src.changed) 		/* No changes to save */
1161#endif
1162	    return (True);
1163
1164	if (WritePiecesToFile(src, src->ascii_src.string) == False)
1165	    return (False);
1166    }
1167    else  {
1168	if (src->ascii_src.allocated_string == True)
1169	    XtFree(src->ascii_src.string);
1170	else
1171	    src->ascii_src.allocated_string = True;
1172
1173	src->ascii_src.string = StorePiecesInString(src);
1174    }
1175#ifdef OLDXAW
1176    src->ascii_src.changes = False;
1177#else
1178    src->text_src.changed = False;
1179#endif
1180
1181    return (True);
1182}
1183
1184/*
1185 * Function:
1186 *	XawAsciiSaveAsFile
1187 *
1188 * Arguments:
1189 *	w    - AsciiSrc widget
1190 *	name - name of the file to save this file into
1191 *
1192 * Description:
1193 *	Save the current buffer as a file.
1194 *
1195 * Returns:
1196 *	True if the save was sucessful
1197 */
1198Bool
1199XawAsciiSaveAsFile(Widget w, _Xconst char *name)
1200{
1201    AsciiSrcObject src = (AsciiSrcObject)w;
1202    Bool ret;
1203
1204    /* If the src is really a multi, call the multi save */
1205
1206    if (XtIsSubclass( w, multiSrcObjectClass))
1207	return (_XawMultiSaveAsFile(w, name));
1208
1209    else if (!XtIsSubclass(w, asciiSrcObjectClass))
1210	XtErrorMsg("bad argument", "asciiSource", "XawError",
1211		   "XawAsciiSaveAsFile's 1st parameter must be an "
1212		   "asciiSrc or multiSrc.",
1213		   NULL, NULL);
1214
1215    if (src->ascii_src.type == XawAsciiFile)
1216	ret = WritePiecesToFile(src, (String)name);
1217    else {
1218	String string = StorePiecesInString(src);
1219
1220	ret = WriteToFile(string, (String)name, src->ascii_src.length);
1221	XtFree(string);
1222    }
1223
1224    return (ret);
1225}
1226
1227/*
1228 * Function:
1229 *	XawAsciiSourceChanged
1230 *
1231 * Parameters:
1232 *	w - ascii source widget
1233 *
1234 * Description:
1235 *	Returns true if the source has changed since last saved.
1236 *
1237 * Returns:
1238 *	A Boolean (see description).
1239 */
1240Bool
1241XawAsciiSourceChanged(Widget w)
1242{
1243#ifdef OLDXAW
1244    if (XtIsSubclass(w, multiSrcObjectClass))
1245	return (((MultiSrcObject)w)->multi_src.changes);
1246
1247    if (XtIsSubclass(w, asciiSrcObjectClass))
1248	return (((AsciiSrcObject)w)->ascii_src.changes);
1249#else
1250    if (XtIsSubclass(w, textSrcObjectClass))
1251	return (((TextSrcObject)w)->textSrc.changed);
1252#endif
1253    XtErrorMsg("bad argument", "asciiSource", "XawError",
1254	       "XawAsciiSourceChanged parameter must be an "
1255	       "asciiSrc or multiSrc.",
1256	       NULL, NULL);
1257
1258    return (True);
1259}
1260
1261/*
1262 * Private Functions
1263 */
1264static void
1265RemoveOldStringOrFile(AsciiSrcObject src, Bool checkString)
1266{
1267    FreeAllPieces(src);
1268
1269    if (checkString && src->ascii_src.allocated_string) {
1270	XtFree(src->ascii_src.string);
1271	src->ascii_src.allocated_string = False;
1272	src->ascii_src.string = NULL;
1273    }
1274}
1275
1276/*
1277 * Function:
1278 *	WriteToFile
1279 *
1280 * Parameters:
1281 *	string - string to write
1282 *	name   - the name of the file
1283 *
1284 * Description:
1285 *	Write the string specified to the begining of the file specified.
1286 *
1287 * Returns:
1288 *	returns True if sucessful, False otherwise
1289 */
1290static Bool
1291WriteToFile(String string, String name, unsigned length)
1292{
1293    int fd;
1294
1295    if ((fd = creat(name, 0666)) == -1
1296	|| write(fd, string, length) == -1)
1297	return (False);
1298
1299    if (close(fd) == -1)
1300	return (False);
1301
1302    return (True);
1303}
1304
1305/*
1306 * Function:
1307 *	WritePiecesToFile
1308 *
1309 * Parameters:
1310 *	src  - ascii source object
1311 *	name - name of the file
1312 *
1313 * Description:
1314 *	  Almost identical to WriteToFile, but only works for ascii src objects
1315 *	of type XawAsciiFile. This function avoids allocating temporary memory,
1316 *	what can be useful when editing very large files.
1317 *
1318 * Returns:
1319 *	returns True if sucessful, False otherwise
1320 */
1321static Bool
1322WritePiecesToFile(AsciiSrcObject src, String name)
1323{
1324    Piece *piece;
1325    int fd;
1326
1327    if (src->ascii_src.data_compression) {
1328	Piece *tmp;
1329
1330	piece = src->ascii_src.first_piece;
1331	while (piece) {
1332	    int bytes = src->ascii_src.piece_size - piece->used;
1333
1334	    if (bytes > 0 && (tmp = piece->next) != NULL) {
1335		bytes = XawMin(bytes, tmp->used);
1336		memcpy(piece->text + piece->used, tmp->text, bytes);
1337		memmove(tmp->text, tmp->text + bytes, tmp->used - bytes);
1338		piece->used += bytes;
1339		if ((tmp->used -= bytes) == 0) {
1340		    RemovePiece(src, tmp);
1341		    continue;
1342		}
1343	    }
1344	    piece = piece->next;
1345	}
1346    }
1347
1348    if ((fd = creat(name, 0666)) == -1)
1349	return (False);
1350
1351    for (piece = src->ascii_src.first_piece; piece; piece = piece->next)
1352	if (write(fd, piece->text, piece->used) == -1)
1353	    return (False);
1354
1355    if (close(fd) == -1)
1356	return (False);
1357
1358    return (True);
1359}
1360
1361/*
1362 * Function:
1363 *	StorePiecesInString
1364 *
1365 * Parameters:
1366 *	data - ascii pointer data
1367 *
1368 * Description:
1369 *	Store the pieces in memory into a standard ascii string.
1370 */
1371static String
1372StorePiecesInString(AsciiSrcObject src)
1373{
1374    String string;
1375    XawTextPosition first;
1376    Piece *piece;
1377
1378    string = XtMalloc((unsigned)(src->ascii_src.length + 1));
1379
1380    for (first = 0, piece = src->ascii_src.first_piece ; piece != NULL;
1381	 first += piece->used, piece = piece->next)
1382      memcpy(string + first, piece->text, (unsigned)piece->used);
1383
1384    string[src->ascii_src.length] = '\0';
1385
1386    /*
1387     * This will refill all pieces to capacity
1388     */
1389    if (src->ascii_src.data_compression) {
1390	FreeAllPieces(src);
1391	LoadPieces(src, NULL, string);
1392    }
1393
1394    return (string);
1395}
1396
1397/*
1398 * Function:
1399 *	InitStringOrFile
1400 *
1401 * Parameters:
1402 *	src - AsciiSource
1403 *
1404 * Description:
1405 *	Initializes the string or file.
1406 */
1407static FILE *
1408InitStringOrFile(AsciiSrcObject src, Bool newString)
1409{
1410    mode_t open_mode = 0;
1411    const char *fdopen_mode = NULL;
1412    int fd;
1413    FILE *file;
1414
1415    if (src->ascii_src.type == XawAsciiString) {
1416	if (src->ascii_src.string == NULL)
1417	    src->ascii_src.length = 0;
1418
1419	else if (!src->ascii_src.use_string_in_place) {
1420	    src->ascii_src.string = XtNewString(src->ascii_src.string);
1421	    src->ascii_src.allocated_string = True;
1422	    src->ascii_src.length = strlen(src->ascii_src.string);
1423	}
1424
1425	if (src->ascii_src.use_string_in_place) {
1426	    if (src->ascii_src.string != NULL)
1427	    src->ascii_src.length = strlen(src->ascii_src.string);
1428	    /* In case the length resource is incorrectly set */
1429	    if (src->ascii_src.length > src->ascii_src.ascii_length)
1430		src->ascii_src.ascii_length = src->ascii_src.length;
1431
1432	    if (src->ascii_src.ascii_length == MAGIC_VALUE)
1433		src->ascii_src.piece_size = src->ascii_src.length;
1434	    else
1435		src->ascii_src.piece_size = src->ascii_src.ascii_length + 1;
1436	}
1437
1438	return (NULL);
1439    }
1440
1441    /*
1442     * type is XawAsciiFile
1443     */
1444    src->ascii_src.is_tempfile = False;
1445
1446    switch (src->text_src.edit_mode) {
1447	case XawtextRead:
1448	    if (src->ascii_src.string == NULL)
1449		XtErrorMsg("NoFile", "asciiSourceCreate", "XawError",
1450			   "Creating a read only disk widget and no file specified.",
1451			   NULL, NULL);
1452	    open_mode = O_RDONLY;
1453	    fdopen_mode = "r";
1454	    break;
1455	case XawtextAppend:
1456	case XawtextEdit:
1457	    if (src->ascii_src.string == NULL) {
1458		src->ascii_src.string = "*ascii-src*";
1459		src->ascii_src.is_tempfile = True;
1460	    }
1461	    else {
1462/* O_NOFOLLOW is a FreeBSD & Linux extension */
1463#ifdef O_NOFOLLOW
1464		open_mode = O_RDWR | O_NOFOLLOW;
1465#else
1466		open_mode = O_RDWR; /* unsafe; subject to race conditions */
1467#endif /* O_NOFOLLOW */
1468		fdopen_mode = "r+";
1469	    }
1470	    break;
1471	default:
1472	    XtErrorMsg("badMode", "asciiSourceCreate", "XawError",
1473		       "Bad editMode for ascii source; must be Read, "
1474		       "Append or Edit.",
1475		       NULL, NULL);
1476    }
1477
1478    /* If is_tempfile, allocate a private copy of the text
1479     * Unlikely to be changed, just to set allocated_string */
1480    if (newString || src->ascii_src.is_tempfile) {
1481	src->ascii_src.string = XtNewString(src->ascii_src.string);
1482	src->ascii_src.allocated_string = True;
1483    }
1484
1485    if (!src->ascii_src.is_tempfile) {
1486	if ((fd = open(src->ascii_src.string, open_mode, 0666)) != -1) {
1487	    if ((file = fdopen(fd, fdopen_mode))) {
1488		(void)fseek(file, 0, SEEK_END);
1489		src->ascii_src.length = (XawTextPosition)ftell(file);
1490		return (file);
1491	    }
1492	}
1493	{
1494	    String params[2];
1495	    Cardinal num_params = 2;
1496
1497	    params[0] = src->ascii_src.string;
1498	    params[1] = strerror(errno);
1499	    XtAppWarningMsg(XtWidgetToApplicationContext((Widget)src),
1500			    "openError", "asciiSourceCreate", "XawWarning",
1501			    "Cannot open file %s; %s", params, &num_params);
1502	}
1503    }
1504    src->ascii_src.length = 0;
1505    return (NULL);
1506}
1507
1508static void
1509LoadPieces(AsciiSrcObject src, FILE *file, char *string)
1510{
1511    char *ptr;
1512    Piece *piece = NULL;
1513    XawTextPosition left;
1514
1515    if (string == NULL) {
1516	if (src->ascii_src.type == XawAsciiFile) {
1517	    if (src->ascii_src.length != 0) {
1518		int len;
1519
1520		left = 0;
1521		fseek(file, 0, 0);
1522		while (left < src->ascii_src.length) {
1523		    ptr = XtMalloc((unsigned)src->ascii_src.piece_size);
1524		    if ((len = fread(ptr, (Size_t)sizeof(unsigned char),
1525				     (Size_t)src->ascii_src.piece_size, file)) < 0)
1526			XtErrorMsg("readError", "asciiSourceCreate", "XawError",
1527				   "fread returned error.", NULL, NULL);
1528		    piece = AllocNewPiece(src, piece);
1529		    piece->text = ptr;
1530		    piece->used = XawMin(len, src->ascii_src.piece_size);
1531		    left += piece->used;
1532		}
1533	    }
1534	    else {
1535		piece = AllocNewPiece(src, NULL);
1536		piece->text = XtMalloc((unsigned)src->ascii_src.piece_size);
1537		piece->used = 0;
1538	    }
1539	    return;
1540	}
1541	else
1542	    string = src->ascii_src.string;
1543    }
1544
1545    if (src->ascii_src.use_string_in_place) {
1546	piece = AllocNewPiece(src, piece);
1547	piece->used = XawMin(src->ascii_src.length, src->ascii_src.piece_size);
1548	piece->text = src->ascii_src.string;
1549	return;
1550    }
1551
1552    ptr = string;
1553    left = src->ascii_src.length;
1554    do {
1555	piece = AllocNewPiece(src, piece);
1556
1557	piece->text = XtMalloc((unsigned)src->ascii_src.piece_size);
1558	piece->used = XawMin(left, src->ascii_src.piece_size);
1559	if (piece->used != 0)
1560	    memcpy(piece->text, ptr, (unsigned)piece->used);
1561
1562	left -= piece->used;
1563	ptr += piece->used;
1564    } while (left > 0);
1565}
1566
1567/*
1568 * Function:
1569 *	AllocNewPiece
1570 *
1571 * Parameters:
1572 *	src - AsciiSrc Widget
1573 *	prev - piece just before this one, or NULL
1574 *
1575 * Description:
1576 *	Allocates a new piece of memory.
1577 *
1578 * Returns:
1579 *	The allocated piece
1580 */
1581static Piece *
1582AllocNewPiece(AsciiSrcObject src, Piece *prev)
1583{
1584    Piece *piece = XtNew(Piece);
1585
1586    if (prev == NULL) {
1587	src->ascii_src.first_piece = piece;
1588	piece->next = NULL;
1589    }
1590    else  {
1591	if (prev->next != NULL)
1592	    (prev->next)->prev = piece;
1593	piece->next = prev->next;
1594	prev->next = piece;
1595    }
1596
1597    piece->prev = prev;
1598
1599    return (piece);
1600}
1601
1602/*
1603 * Function:
1604 *	FreeAllPieces
1605 *
1606 * Parameters:
1607 *	src - AsciiSrc Widget
1608 *
1609 * Description:
1610 *	Frees all the pieces.
1611 */
1612static void
1613FreeAllPieces(AsciiSrcObject src)
1614{
1615    Piece *next, * first = src->ascii_src.first_piece;
1616
1617#ifdef DEBUG
1618    if (first->prev != NULL)
1619	printf("Xaw AsciiSrc Object: possible memory leak in FreeAllPieces().\n");
1620#endif
1621
1622    for (; first != NULL ; first = next) {
1623	next = first->next;
1624	RemovePiece(src, first);
1625    }
1626}
1627
1628/*
1629 * Function:
1630 *	RemovePiece
1631 *
1632 * Parameters:
1633 *	piece - piece to remove
1634 *
1635 * Description:
1636 *	Removes a piece from the list.
1637 */
1638static void
1639RemovePiece(AsciiSrcObject src, Piece *piece)
1640{
1641    if (piece->prev == NULL)
1642	src->ascii_src.first_piece = piece->next;
1643    else
1644	piece->prev->next = piece->next;
1645
1646    if (piece->next != NULL)
1647	piece->next->prev = piece->prev;
1648
1649    if (!src->ascii_src.use_string_in_place)
1650	XtFree(piece->text);
1651
1652    XtFree((char *)piece);
1653}
1654
1655/*
1656 * Function:
1657 *	FindPiece
1658 *
1659 * Parameters:
1660 *	src	 - AsciiSrc Widget
1661 *	position - position that we are searching for
1662 * 	first	 - position of the first character in this piece (return)
1663 *
1664 * Description:
1665 *	Finds the piece containing the position indicated.
1666 *
1667 * Returns:
1668 *	the piece that contains this position
1669 */
1670static Piece *
1671FindPiece(AsciiSrcObject src, XawTextPosition position, XawTextPosition *first)
1672{
1673    Piece *old_piece, *piece;
1674    XawTextPosition temp;
1675
1676    for (old_piece = NULL, piece = src->ascii_src.first_piece, temp = 0;
1677	piece; old_piece = piece, piece = piece->next)
1678	if ((temp += piece->used) > position) {
1679	    *first = temp - piece->used;
1680	    return (piece);
1681	}
1682
1683    *first = temp - (old_piece ? old_piece->used : 0);
1684
1685    return (old_piece);	/* if we run off the end the return the last piece */
1686}
1687
1688/*
1689 * Function:
1690 *	BreakPiece
1691 *
1692 * Parameters:
1693 *	src - AsciiSrc Widget
1694 *	piece - piece to break
1695 *
1696 * Description:
1697 *	Breaks a full piece into two new pieces.
1698 */
1699#define HALF_PIECE (src->ascii_src.piece_size >> 1)
1700static void
1701BreakPiece(AsciiSrcObject src, Piece *piece)
1702{
1703    Piece *cnew = AllocNewPiece(src, piece);
1704
1705    cnew->text = XtMalloc((unsigned)src->ascii_src.piece_size);
1706    memcpy(cnew->text, piece->text + HALF_PIECE,
1707	   (unsigned)(src->ascii_src.piece_size - HALF_PIECE));
1708    piece->used = HALF_PIECE;
1709    cnew->used = src->ascii_src.piece_size - HALF_PIECE;
1710}
1711
1712/*ARGSUSED*/
1713static void
1714CvtStringToAsciiType(XrmValuePtr args, Cardinal *num_args,
1715		     XrmValuePtr fromVal, XrmValuePtr toVal)
1716{
1717    static XawAsciiType type;
1718    XrmQuark q;
1719    char name[7];
1720
1721    XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name));
1722    q = XrmStringToQuark(name);
1723
1724    if (q == Qstring)
1725	type = XawAsciiString;
1726    else if (q == Qfile)
1727	type = XawAsciiFile;
1728    else  {
1729	toVal->size = 0;
1730	toVal->addr = NULL;
1731	XtStringConversionWarning((char *)fromVal->addr, XtRAsciiType);
1732    }
1733
1734    toVal->size = sizeof(XawAsciiType);
1735    toVal->addr = (XPointer)&type;
1736}
1737
1738/*ARGSUSED*/
1739static Boolean
1740CvtAsciiTypeToString(Display *dpy, XrmValuePtr args, Cardinal *num_args,
1741		     XrmValuePtr fromVal, XrmValuePtr toVal,
1742		     XtPointer *data)
1743{
1744    static String buffer;
1745    Cardinal size;
1746
1747    switch (*(XawAsciiType *)fromVal->addr) {
1748	case XawAsciiFile:
1749	    buffer = XtEfile;
1750	    break;
1751	case XawAsciiString:
1752	    buffer = XtEstring;
1753	    break;
1754	default:
1755	    XawTypeToStringWarning(dpy, XtRAsciiType);
1756	    toVal->addr = NULL;
1757	    toVal->size = 0;
1758	    return (False);
1759    }
1760
1761    size = strlen(buffer) + 1;
1762    if (toVal->addr != NULL) {
1763	if (toVal->size < size) {
1764	    toVal->size = size;
1765	    return (False);
1766	}
1767	strcpy((char *)toVal->addr, buffer);
1768    }
1769    else
1770	toVal->addr = (XPointer)buffer;
1771    toVal->size = sizeof(String);
1772
1773    return (True);
1774}
1775
1776/*ARGSUSED*/
1777static void
1778GetDefaultPieceSize(Widget w, int offset, XrmValue *value)
1779{
1780    static XPointer pagesize;
1781
1782    if (pagesize == NULL) {
1783	pagesize = (XPointer)((long)_XawGetPageSize());
1784	if (pagesize < (XPointer)BUFSIZ)
1785	    pagesize = (XPointer)BUFSIZ;
1786    }
1787
1788    value->addr = (XPointer)&pagesize;
1789}
1790
1791#if (defined(ASCII_STRING) || defined(ASCII_DISK))
1792#  include <X11/Xaw/Cardinals.h>
1793#endif
1794
1795#ifdef ASCII_STRING
1796/*
1797 * Compatability functions.
1798 */
1799/*
1800 * Function:
1801 *	AsciiStringSourceCreate
1802 *
1803 * Parameters:
1804 *	parent	 - widget that will own this source
1805 *	args	 - the argument list
1806 *	num_args - ""
1807 *
1808 * Description:
1809 *	Creates a string source.
1810 *
1811 * Returns:
1812 *	A pointer to the new text source.
1813 */
1814Widget
1815XawStringSourceCreate(Widget parent, ArgList args, Cardinal num_args)
1816{
1817    XawTextSource src;
1818    ArgList ascii_args;
1819    Arg temp[2];
1820
1821    XtSetArg(temp[0], XtNtype, XawAsciiString);
1822    XtSetArg(temp[1], XtNuseStringInPlace, True);
1823    ascii_args = XtMergeArgLists(temp, TWO, args, num_args);
1824
1825    src = XtCreateWidget("genericAsciiString", asciiSrcObjectClass, parent,
1826			 ascii_args, num_args + TWO);
1827    XtFree((char *)ascii_args);
1828
1829    return (src);
1830}
1831
1832/*
1833 * This is hacked up to try to emulate old functionality, it
1834 * may not work, as I have not old code to test it on.
1835 *
1836 * Chris D. Peterson  8/31/89.
1837 */
1838void
1839XawTextSetLastPos(Widget w, XawTextPosition lastPos)
1840{
1841    AsciiSrcObject src = (AsciiSrcObject)XawTextGetSource(w);
1842
1843    src->ascii_src.piece_size = lastPos;
1844}
1845#endif /* ASCII_STRING */
1846
1847#ifdef ASCII_DISK
1848/*
1849 * Function:
1850 *	AsciiDiskSourceCreate
1851 *
1852 * Parameters:
1853 *	parent	 - widget that will own this source
1854 *	args	 - argument list
1855 *	num_args - ""
1856 *
1857 * Description:
1858 *	Creates a disk source.
1859 *
1860 * Returns:
1861 *	A pointer to the new text source
1862 */
1863Widget
1864XawDiskSourceCreate(Widget parent, ArgList args, Cardinal num_args)
1865{
1866    XawTextSource src;
1867    ArgList ascii_args;
1868    Arg temp[1];
1869    int i;
1870
1871    XtSetArg(temp[0], XtNtype, XawAsciiFile);
1872    ascii_args = XtMergeArgLists(temp, ONE, args, num_args);
1873    num_args++;
1874
1875    for (i = 0; i < num_args; i++)
1876	if (streq(ascii_args[i].name, XtNfile)
1877	    || streq(ascii_args[i].name, XtCFile))
1878	    ascii_args[i].name = XtNstring;
1879
1880    src = XtCreateWidget("genericAsciiDisk", asciiSrcObjectClass, parent,
1881			 ascii_args, num_args);
1882    XtFree((char *)ascii_args);
1883
1884    return (src);
1885}
1886#endif /* ASCII_DISK */
1887