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