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