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