1/*
2 * Copyright 1991 by OMRON Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name OMRON not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  OMRON makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as is" without express or implied warranty.
13 *
14 * OMRON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL OMRON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 *
22 *      Authors: Chris Peterson MIT X Consortium
23 *               Li Yuhong      OMRON Corporation
24 *               Frank Sheeran  OMRON Corporation
25 *
26 * Much code taken from X11R3 String and Disk Sources.
27 */
28
29/*
30
31Copyright 1991, 1994, 1998  The Open Group
32
33Permission to use, copy, modify, distribute, and sell this software and its
34documentation for any purpose is hereby granted without fee, provided that
35the above copyright notice appear in all copies and that both that
36copyright notice and this permission notice appear in supporting
37documentation.
38
39The above copyright notice and this permission notice shall be included in
40all copies or substantial portions of the Software.
41
42THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
45OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
46AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
47CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48
49Except as contained in this notice, the name of The Open Group shall not be
50used in advertising or otherwise to promote the sale, use or other dealings
51in this Software without prior written authorization from The Open Group.
52
53*/
54
55#ifdef HAVE_CONFIG_H
56#include <config.h>
57#endif
58#include <stdio.h>
59#include <stdlib.h>
60#include <ctype.h>
61#include <errno.h>
62#include <X11/IntrinsicP.h>
63#include <X11/StringDefs.h>
64#include <X11/Xfuncs.h>
65#include <X11/Xos.h>
66#include <X11/Xmu/CharSet.h>
67#include <X11/Xmu/Misc.h>
68#include <X11/Xaw/XawInit.h>
69#include <X11/Xaw/MultiSrcP.h>
70#include <X11/Xaw/XawImP.h>
71#include "XawI18n.h"
72#include "Private.h"
73
74#include <sys/types.h>
75#include <sys/stat.h>
76#include <fcntl.h>
77
78#ifndef O_CLOEXEC
79#define O_CLOEXEC 0
80#endif
81
82#define MAGIC_VALUE	((XawTextPosition)-1)
83#define streq(a, b)	(strcmp((a), (b)) == 0)
84
85
86/*
87 * Class Methods
88 */
89static XawTextPosition ReadText(Widget, XawTextPosition, XawTextBlock*, int);
90static int  ReplaceText(Widget, XawTextPosition, XawTextPosition,
91			XawTextBlock*);
92static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType,
93			    XawTextScanDirection, int, Bool);
94static XawTextPosition  Search(Widget, XawTextPosition, XawTextScanDirection,
95			       XawTextBlock*);
96static void XawMultiSrcClassInitialize(void);
97static void XawMultiSrcDestroy(Widget);
98static void XawMultiSrcInitialize(Widget, Widget, ArgList, Cardinal*);
99static Boolean XawMultiSrcSetValues(Widget, Widget, Widget,
100				    ArgList, Cardinal*);
101static void XawMultiSrcGetValuesHook(Widget, ArgList, Cardinal*);
102
103/*
104 * Prototypes
105 */
106static MultiPiece *AllocNewPiece(MultiSrcObject, MultiPiece*);
107static void BreakPiece(MultiSrcObject, MultiPiece*);
108static Boolean CvtMultiTypeToString(Display*, XrmValuePtr, Cardinal*,
109				    XrmValuePtr, XrmValuePtr, XtPointer*);
110static void CvtStringToMultiType(XrmValuePtr, Cardinal*,
111				 XrmValuePtr, XrmValuePtr);
112static MultiPiece *FindPiece(MultiSrcObject, XawTextPosition,
113			     XawTextPosition*);
114static void FreeAllPieces(MultiSrcObject);
115static FILE *InitStringOrFile(MultiSrcObject, Bool);
116static void LoadPieces(MultiSrcObject, FILE*, char*);
117static void RemovePiece(MultiSrcObject, MultiPiece*);
118static void RemoveOldStringOrFile(MultiSrcObject, Bool);
119static char * StorePiecesInString(MultiSrcObject);
120static Bool WriteToFile(String, String);
121static void GetDefaultPieceSize(Widget, int, XrmValue*);
122
123/*
124 * Initialization
125 */
126#define offset(field) XtOffsetOf(MultiSrcRec, multi_src.field)
127static XtResource resources[] = {
128  {
129    XtNstring,
130    XtCString,
131    XtRString,
132    sizeof(XtPointer),
133    offset(string),
134    XtRPointer,
135    NULL
136  },
137  {
138    XtNtype,
139    XtCType,
140    XtRMultiType,
141    sizeof(XawAsciiType),
142    offset(type),
143    XtRImmediate,
144    (XtPointer)XawAsciiString
145  },
146  {
147    XtNdataCompression,
148    XtCDataCompression,
149    XtRBoolean,
150    sizeof(Boolean),
151    offset(data_compression),
152    XtRImmediate,
153    (XtPointer)False
154  },
155  {
156    XtNpieceSize,
157    XtCPieceSize,
158    XtRInt,
159    sizeof(XawTextPosition),
160    offset(piece_size),
161    XtRCallProc,
162    (XtPointer)GetDefaultPieceSize
163  },
164#ifdef OLDXAW
165  {
166    XtNcallback,
167    XtCCallback,
168    XtRCallback,
169    sizeof(XtPointer),
170    offset(callback),
171    XtRCallback,
172    (XtPointer)NULL
173  },
174#endif
175  {
176    XtNuseStringInPlace,
177    XtCUseStringInPlace,
178    XtRBoolean,
179    sizeof(Boolean),
180    offset(use_string_in_place),
181    XtRImmediate,
182    (XtPointer)False
183  },
184  {
185    XtNlength,
186    XtCLength,
187    XtRInt,
188    sizeof(int),
189    offset(multi_length),
190    XtRImmediate,
191    (XtPointer)MAGIC_VALUE
192  },
193};
194#undef offset
195
196#define superclass		(&textSrcClassRec)
197MultiSrcClassRec multiSrcClassRec = {
198  /* object */
199  {
200    (WidgetClass)superclass,		/* superclass */
201    "MultiSrc",				/* class_name */
202    sizeof(MultiSrcRec),		/* widget_size */
203    XawMultiSrcClassInitialize,		/* class_initialize */
204    NULL,				/* class_part_initialize */
205    False,				/* class_inited */
206    XawMultiSrcInitialize,		/* initialize */
207    NULL,				/* initialize_hook */
208    NULL,				/* obj1 */
209    NULL,				/* obj2 */
210    0,					/* obj3 */
211    resources,				/* resources */
212    XtNumber(resources),		/* num_resources */
213    NULLQUARK,				/* xrm_class */
214    False,				/* obj4 */
215    False,				/* obj5 */
216    False,				/* obj6 */
217    False,				/* obj7 */
218    XawMultiSrcDestroy,			/* destroy */
219    NULL,				/* obj8 */
220    NULL,				/* obj9 */
221    XawMultiSrcSetValues,		/* set_values */
222    NULL,				/* set_values_hook */
223    NULL,				/* obj10 */
224    XawMultiSrcGetValuesHook,		/* get_values_hook */
225    NULL,				/* obj11 */
226    XtVersion,				/* version */
227    NULL,				/* callback_private */
228    NULL,				/* obj12 */
229    NULL,				/* obj13 */
230    NULL,				/* obj14 */
231    NULL,				/* extension */
232  },
233  /* text_src */
234  {
235    ReadText,				/* Read */
236    ReplaceText,			/* Replace */
237    Scan,				/* Scan */
238    Search,				/* Search */
239    XtInheritSetSelection,		/* SetSelection */
240    XtInheritConvertSelection,		/* ConvertSelection */
241#ifndef OLDXAW
242    NULL
243#endif
244  },
245  /* multi_src */
246  {
247    NULL,				/* extension */
248  },
249};
250
251WidgetClass multiSrcObjectClass = (WidgetClass)&multiSrcClassRec;
252
253static XrmQuark Qstring, Qfile;
254
255/*
256 * Implementation
257 */
258static void
259XawMultiSrcClassInitialize(void)
260{
261    XawInitializeWidgetSet();
262    Qstring = XrmPermStringToQuark(XtEstring);
263    Qfile = XrmPermStringToQuark(XtEfile);
264    XtAddConverter(XtRString, XtRMultiType, CvtStringToMultiType, NULL, 0);
265    XtSetTypeConverter(XtRMultiType, XtRString, CvtMultiTypeToString, NULL, 0,
266		       XtCacheNone, NULL);
267}
268
269/*
270 * Function:
271 *	XawMultiSrcInitialize
272 *
273 * Parameters:
274 *	request  - widget requested by the argument list
275 *	cnew	 - the new widget with both resource and non resource values
276 *	args	 - (unused)
277 *	num_args - (unused)
278 *
279 * Description:
280 *	Initializes the multi src object
281 */
282/*ARGSUSED*/
283static void
284XawMultiSrcInitialize(Widget request _X_UNUSED, Widget cnew,
285		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
286{
287    MultiSrcObject src = (MultiSrcObject)cnew;
288    FILE *file;
289
290    /*
291     * Set correct flags (override resources) depending upon widget class
292     */
293#ifdef OLDXAW
294    src->multi_src.changes = False;
295#else
296    src->text_src.changed = False;
297#endif
298    src->multi_src.allocated_string = False;
299
300    if (src->multi_src.use_string_in_place && src->multi_src.string == NULL)
301	src->multi_src.use_string_in_place = False;
302
303    file = InitStringOrFile(src, src->multi_src.type == XawAsciiFile);
304    LoadPieces(src, file, NULL);
305
306    if (file != NULL)
307	fclose(file);
308    src->text_src.text_format = (XrmQuark)XawFmtWide;
309}
310
311/*
312 * Function:
313 *	ReadText
314 *
315 * Parameters:
316 *	w      - MultiSource object
317 *	pos    - position of the text to retrieve
318 *	text   - text block that will contain returned text
319 *	length - maximum number of characters to read
320 *
321 * Description:
322 *	This function reads the source.
323 *
324 * Returns:
325 *	The character position following the retrieved text.
326 */
327static XawTextPosition
328ReadText(Widget w, XawTextPosition pos, XawTextBlock *text, int length)
329{
330    MultiSrcObject src = (MultiSrcObject)w;
331    XawTextPosition count, start;
332    MultiPiece *piece = FindPiece(src, pos, &start);
333
334    text->format = XawFmtWide;
335    text->firstPos = (int)pos;
336    text->ptr = (char *)(piece->text + (pos - start));
337    count = piece->used - (pos - start);
338    text->length = (int)(Max(0, (length > count) ? count : length));
339
340    return (pos + text->length);
341}
342
343/*
344 * Function:
345 *	ReplaceText
346 *
347 * Parameters:
348 *	w	 - MultiSource object
349 *	startPos - ends of text that will be removed
350 *	endPos	 - ""
351 *	text	 - new text to be inserted into buffer at startPos
352 *
353 * Description:
354 *	Replaces a block of text with new text.
355 *
356 * Returns:
357 *	XawEditDone on success, XawEditError otherwise
358 */
359/*ARGSUSED*/
360static int
361ReplaceText(Widget w, XawTextPosition startPos, XawTextPosition endPos,
362	    XawTextBlock *u_text_p)
363{
364    MultiSrcObject src = (MultiSrcObject)w;
365    MultiPiece *start_piece, *end_piece, *temp_piece;
366    XawTextPosition start_first, end_first;
367    int length, firstPos;
368    wchar_t *wptr;
369    Bool local_artificial_block = False;
370    XawTextBlock text;
371
372    /* STEP 1: The user handed me a text block called `u_text' that may be
373     * in either FMTWIDE or FMT8BIT (ie MB.)  Later code needs the block
374     * `text' to hold FMTWIDE.	So, this copies `u_text' to `text', and if
375     * `u_text' was MB, I knock it up to WIDE
376     */
377    if (u_text_p->length == 0)	/* if so, the block contents never ref'd */
378	text.length = 0;
379
380    else if (u_text_p->format == XawFmtWide) {
381	local_artificial_block = False; /* don't have to free it ourselves */
382	text.firstPos = u_text_p->firstPos;
383	text.length =	u_text_p->length;
384	text.ptr =	u_text_p->ptr;
385    }
386    else {
387	/*
388	 * WARNING! u_text->firstPos and length are in units of CHAR,
389	 * not CHARACTERS!
390	 */
391	local_artificial_block = True;	/* have to free it ourselves */
392	text.firstPos = 0;
393	text.length = u_text_p->length; /* _XawTextMBToWC converts this
394					 * to wchar len
395					 */
396
397	text.ptr = (char*)_XawTextMBToWC(XtDisplay(XtParent(w)),
398					 &u_text_p->ptr[u_text_p->firstPos],
399					 &text.length);
400
401	/* I assert the following assignment is not needed - since Step 4
402	   depends on length, it has no need of a terminating NULL.  I think
403	   the ASCII-version has the same needless NULL. */
404	/*((wchar_t*)text.ptr)[ text.length ] = NULL;*/
405    }
406
407    /* STEP 2: some initialization... */
408    if (src->text_src.edit_mode == XawtextRead)
409	return (XawEditError);
410
411    if ((start_piece = FindPiece(src, startPos, &start_first)) == NULL)
412	return XawEditError;
413    if ((end_piece = FindPiece(src, endPos, &end_first)) == NULL)
414	return XawEditError;
415
416    /* STEP 3: remove the empty pieces... */
417    if (start_piece != end_piece) {
418	if ((temp_piece = start_piece->next) == NULL)
419	    return XawEditError;
420
421	/* If empty and not the only piece then remove it */
422	if (((start_piece->used = startPos - start_first) == 0)
423	    &&	!(start_piece->next == NULL && start_piece->prev == NULL))
424	    RemovePiece(src, start_piece);
425
426	while (temp_piece != end_piece) {
427	    temp_piece = temp_piece->next;
428	    RemovePiece(src, temp_piece->prev);
429	}
430	end_piece->used -= endPos - end_first;
431	if (end_piece->used != 0)
432	    memmove(end_piece->text, end_piece->text + endPos - end_first,
433		    (size_t)end_piece->used * sizeof(wchar_t));
434    }
435    else {		    /* We are fully in one piece */
436	if ((start_piece->used -= endPos - startPos) == 0) {
437	    if (!(start_piece->next == NULL && start_piece->prev == NULL))
438		RemovePiece(src, start_piece);
439	}
440	else {
441	    memmove(start_piece->text + (startPos - start_first),
442		    start_piece->text + (endPos - start_first),
443		    (size_t)(start_piece->used - (startPos - start_first)) *
444		    sizeof(wchar_t));
445	    if (src->multi_src.use_string_in_place &&
446		((src->multi_src.length - (endPos - startPos))
447		< src->multi_src.piece_size - 1))
448		start_piece->text[src->multi_src.length - (endPos - startPos)] =
449		  (wchar_t)0;
450	}
451    }
452
453    src->multi_src.length += text.length -(endPos - startPos);
454
455    /* STEP 4: insert the new stuff */
456    if ( text.length != 0) {
457        start_piece = FindPiece(src, startPos, &start_first);
458        length = text.length;
459        firstPos = text.firstPos;
460
461	while (length > 0) {
462	    wchar_t *ptr;
463	    int fill;
464
465	    if (src->multi_src.use_string_in_place) {
466		if (start_piece->used == src->multi_src.piece_size - 1)  {
467
468		    /*
469		     * The string is used in place, then the string
470		     * is not allowed to grow
471		     */
472		    start_piece->used = src->multi_src.length =
473			src->multi_src.piece_size - 1;
474
475		    start_piece->text[src->multi_src.length] = (wchar_t)0;
476		    return (XawEditError);
477		}
478	    }
479
480	    if (start_piece->used == src->multi_src.piece_size) {
481		BreakPiece(src, start_piece);
482		start_piece = FindPiece(src, startPos, &start_first);
483	    }
484
485	    fill = Min((int)(src->multi_src.piece_size - start_piece->used), length);
486
487	    ptr = start_piece->text + (startPos - start_first);
488	    memmove(ptr + fill, ptr, (size_t)(start_piece->used -
489		    (startPos - start_first)) * sizeof(wchar_t));
490	    wptr =(wchar_t *)text.ptr;
491	    (void)wcsncpy(ptr, wptr + firstPos, (size_t)fill);
492
493	    startPos += fill;
494	    firstPos += fill;
495	    start_piece->used += fill;
496	    length -= fill;
497	}
498    }
499
500    if (local_artificial_block == True)
501	/* In other words, text is not the u_text that the user handed me but
502	   one I made myself.  I only care, because I need to free the string */
503	XtFree(text.ptr);
504
505    if (src->multi_src.use_string_in_place)
506	start_piece->text[start_piece->used] = (wchar_t)0;
507
508#ifdef OLDXAW
509    src->multi_src.changes = True;
510    XtCallCallbacks(w, XtNcallback, NULL);
511#endif
512
513    return (XawEditDone);
514}
515
516/*
517 * Function:
518 *	Scan
519 *
520 * Parameters:
521 *	w	 - MultiSource widget
522 *	position - position to start scanning
523 *	type	 - type of thing to scan for
524 *	dir	 - direction to scan
525 *	count	 - which occurrence of this thing to search for
526 *	include  - whether or not to include the character found in
527 *		   the position that is returned
528 *
529 * Description:
530 *	Scans the text source for the number and type of item specified.
531 *
532 * Returns:
533 *	The position of the item found
534 *
535 * Note:
536 *	While there are only 'n' characters in the file there are n+1
537 *	possible cursor positions (one before the first character and
538 *	one after the last character
539 */
540static XawTextPosition
541Scan(Widget w, register XawTextPosition position, XawTextScanType type,
542     XawTextScanDirection dir, int count, Bool include)
543{
544    MultiSrcObject src = (MultiSrcObject)w;
545    register char inc;
546    MultiPiece *piece;
547    XawTextPosition first, first_eol_position = position;
548    register wchar_t *ptr;
549    int cnt = count;
550
551    if (type == XawstAll) {
552	if (dir == XawsdRight)
553	    return (src->multi_src.length);
554	return (0);
555    }
556
557    /* STEP 1: basic sanity checks */
558    if (position > src->multi_src.length)
559	position = src->multi_src.length;
560
561    if (dir == XawsdRight) {
562	if (position == src->multi_src.length)
563	    return (src->multi_src.length);
564	inc = 1;
565    }
566    else {
567	if (position == 0)
568	    return (0);
569	inc = -1;
570	position--;
571    }
572
573    piece = FindPiece(src, position, &first);
574
575    if (piece->used == 0)
576	return (0);
577
578    ptr = (position - first) + piece->text;
579
580    switch (type) {
581	case XawstEOL:
582	case XawstParagraph:
583	case XawstWhiteSpace:
584	case XawstAlphaNumeric:
585	    for (; cnt > 0 ; cnt--) {
586		Bool non_space = False, first_eol = True;
587
588		/*CONSTCOND*/
589		while (True) {
590		    register wchar_t c;
591
592		    if (ptr < piece->text) {
593			piece = piece->prev;
594			if (piece == NULL)	/* Beginning of text */
595			    return (0);
596			ptr = piece->text + piece->used - 1;
597		    }
598		    else if (ptr >= piece->text + piece->used) {
599			piece = piece->next;
600			if (piece == NULL)	/* End of text */
601			    return (src->multi_src.length);
602			ptr = piece->text;
603		    }
604
605		    c = *ptr;
606		    ptr += inc;
607		    position += inc;
608
609		    if (type == XawstAlphaNumeric) {
610			if (!iswalnum((wint_t)c)) {
611			    if (non_space)
612				break;
613			}
614			else
615			    non_space = True;
616		    }
617		    else if (type == XawstWhiteSpace) {
618			if (iswspace(c)) {
619			    if (non_space)
620			      break;
621			}
622			else
623			    non_space = True;
624		    }
625		    else if (type == XawstEOL) {
626			if (c == _Xaw_atowc(XawLF))
627			    break;
628		    }
629		    else {	/* XawstParagraph */
630			if (first_eol) {
631			    if (c == _Xaw_atowc(XawLF)) {
632				first_eol_position = position;
633				first_eol = False;
634			    }
635			}
636			else
637			    if (c == _Xaw_atowc(XawLF))
638				break;
639			else if (!iswspace(c))
640			    first_eol = True;
641		    }
642		}
643	    }
644	    if (!include) {
645		if (type == XawstParagraph)
646		    position = first_eol_position;
647		if (count)
648		    position -= inc;
649	    }
650	    break;
651	case XawstPositions:
652	    position += count * inc;
653	    break;
654	default:
655	    break;
656    }
657
658    if (dir == XawsdLeft)
659	position++;
660
661    if (position >= src->multi_src.length)
662	return (src->multi_src.length);
663    if (position < 0)
664	return (0);
665
666    return (position);
667}
668
669/*
670 * Function:
671 *	Search
672 *
673 * Parameters:
674 *	w	 - MultiSource objecy
675 *	position - position to start scanning
676 *	dir	 - direction to scan
677 *	text	 - text block to search for
678 *
679 * Description:
680 *	Searches the text source for the text block passed.
681 *
682 * Returns:
683 *	The position of the item found
684 */
685static XawTextPosition
686Search(Widget w, register XawTextPosition position, XawTextScanDirection dir,
687       XawTextBlock *text)
688{
689    MultiSrcObject src = (MultiSrcObject)w;
690    register int count = 0;
691    wchar_t *ptr;
692    wchar_t *wtarget;
693    int wtarget_len;
694    Display *d = XtDisplay(XtParent(w));
695    MultiPiece *piece;
696    wchar_t *buf;
697    XawTextPosition first;
698    register char inc;
699    int cnt;
700
701    /* STEP 1: First, a brief sanity check */
702    if (dir == XawsdRight)
703	inc = 1;
704    else  {
705	inc = -1;
706	if (position == 0)
707	    return (XawTextSearchError);
708	position--;
709    }
710
711    /* STEP 2: Ensure I have a local wide string.. */
712
713    /* Since this widget stores 32bit chars, I check here to see if
714       I'm being passed a string claiming to be 8bit chars (ie, MB text.)
715       If that is the case, naturally I convert to 32bit format */
716
717    /*if the block was FMT8BIT, length will convert to REAL wchar count below */
718    wtarget_len = text->length;
719
720    if (text->format == XawFmtWide)
721	wtarget = &(((wchar_t*)text->ptr) [text->firstPos]);
722    else {
723	/* The following converts wtarget_len from byte len to wchar count */
724	   wtarget = _XawTextMBToWC(d, &text->ptr[text->firstPos], &wtarget_len);
725    }
726
727    /* OK, I can now assert that wtarget holds wide characters, wtarget_len
728       holds an accurate count of those characters, and that firstPos has been
729       effectively factored out of the following computations */
730
731    /* STEP 3: SEARCH! */
732    buf = (wchar_t *)XtMalloc((Cardinal)(sizeof(wchar_t) * (size_t)wtarget_len));
733    (void)wcsncpy(buf, wtarget, (size_t)wtarget_len);
734    piece = FindPiece(src, position, &first);
735    ptr = (position - first) + piece->text;
736
737    /*CONSTCOND*/
738    while (True) {
739	if (*ptr == (dir == XawsdRight ? *(buf + count)
740		     : *(buf + wtarget_len - count - 1))) {
741	    if (count == text->length - 1)
742		break;
743	    else
744		count++;
745	}
746	else {
747	    if (count != 0) {
748		position -=inc * count;
749		ptr -= inc * count;
750	    }
751	    count = 0;
752	}
753
754	ptr += inc;
755	position += inc;
756
757	while (ptr < piece->text) {
758	    cnt = (int)(piece->text - ptr);
759
760	    piece = piece->prev;
761	    if (piece == NULL) {	/* Beginning of text */
762		XtFree((char *)buf);
763		return (XawTextSearchError);
764	    }
765	    ptr = piece->text + piece->used - cnt;
766	}
767
768	while (ptr >= piece->text + piece->used) {
769	    cnt = (int)(ptr - (piece->text + piece->used));
770
771	    piece = piece->next;
772	    if (piece == NULL) {	/* End of text */
773		XtFree((char *)buf);
774		return (XawTextSearchError);
775	    }
776	    ptr = piece->text + cnt;
777	}
778    }
779
780    XtFree((char *)buf);
781    if (dir == XawsdLeft)
782	return(position);
783
784    return(position - (wtarget_len - 1));
785}
786
787/*
788 * Function:
789 *	XawMultiSrcSetValues
790 *
791 * Parameters:
792 *	current  - current state of the widget
793 *	request  - what was requested
794 *	cnew	 - what the widget will become
795 *	args	 - representation of resources that have changed
796 *	num_args - number of changed resources
797 *
798 * Description:
799 *	Sets the values for the MultiSource.
800 *
801 * Returns:
802 *	True if redisplay is needed
803 */
804static Boolean
805XawMultiSrcSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
806		     ArgList args, Cardinal *num_args)
807{
808    MultiSrcObject src = (MultiSrcObject)cnew;
809    MultiSrcObject old_src = (MultiSrcObject)current;
810    XtAppContext app_con = XtWidgetToApplicationContext(cnew);
811    Bool total_reset = False, string_set = False;
812    FILE *file;
813    unsigned int i;
814
815    if (old_src->multi_src.use_string_in_place
816	!= src->multi_src.use_string_in_place) {
817	XtAppWarning(app_con,
818		     "MultiSrc: The XtNuseStringInPlace resources "
819		     "may not be changed.");
820	src->multi_src.use_string_in_place =
821	    old_src->multi_src.use_string_in_place;
822    }
823
824    for (i = 0; i < *num_args ; i++)
825	if (streq(args[i].name, XtNstring)) {
826	    string_set = True;
827	    break;
828	}
829
830    if (string_set || old_src->multi_src.type != src->multi_src.type) {
831	RemoveOldStringOrFile(old_src, string_set);
832	src->multi_src.allocated_string = old_src->multi_src.allocated_string;
833	file = InitStringOrFile(src, string_set);
834
835        LoadPieces(src, file, NULL);
836	if (file != NULL)
837	    fclose(file);
838#ifndef OLDXAW
839	for (i = 0; i < src->text_src.num_text; i++)
840	    /* Tell text widget what happened */
841	    XawTextSetSource(src->text_src.text[i], cnew, 0);
842#else
843	XawTextSetSource(XtParent(cnew), cnew, 0);
844#endif
845	total_reset = True;
846    }
847
848    if (old_src->multi_src.multi_length != src->multi_src.multi_length)
849	src->multi_src.piece_size = src->multi_src.multi_length + 1;
850
851    if ( !total_reset && old_src->multi_src.piece_size
852	 != src->multi_src.piece_size) {
853	char * mb_string = StorePiecesInString(old_src);
854
855	if (mb_string != 0) {
856	    FreeAllPieces(old_src);
857	    LoadPieces(src, NULL, mb_string);
858	    XtFree(mb_string);
859	}
860	else {
861	    /* If the buffer holds bad chars, don't touch it... */
862	    XtAppWarningMsg(app_con,
863			    "convertError", "multiSource", "XawError",
864			     XtName(XtParent((Widget)old_src)), NULL, NULL);
865	    XtAppWarningMsg(app_con,
866			    "convertError", "multiSource", "XawError",
867			    "Non-character code(s) in buffer.", NULL, NULL);
868	}
869    }
870
871    return (False);
872}
873
874static void
875XawMultiSrcGetValuesHook(Widget w, ArgList args, Cardinal *num_args)
876{
877    MultiSrcObject src = (MultiSrcObject)w;
878    Cardinal i;
879
880    if (src->multi_src.type == XawAsciiString) {
881	for (i = 0; i < *num_args ; i++) {
882	    if (streq(args[i].name, XtNstring)) {
883		if (src->multi_src.use_string_in_place)
884		    *((char **)args[i].value) = (char *)
885			src->multi_src.first_piece->text;
886		else if (_XawMultiSave(w))	/* If save successful */
887		    *((char **)args[i].value) = (char *)src->multi_src.string;
888		break;
889	    }
890	}
891    }
892}
893
894static void
895XawMultiSrcDestroy(Widget w)
896{
897    RemoveOldStringOrFile((MultiSrcObject) w, True);
898}
899
900/*
901 * Public routines
902 */
903/*
904 * Function:
905 *	XawMultiSourceFreeString
906 *
907 * Parameters:
908 *	w - MultiSrc widget
909 *
910 * Description:
911 *	  Frees the string returned by a get values call
912 *                   on the string when the source is of type string.
913 *
914 * Note:
915 * The public interface is XawAsciiSourceFreeString!
916 */
917void
918_XawMultiSourceFreeString(Widget w)
919{
920    MultiSrcObject src = (MultiSrcObject)w;
921
922    if (src->multi_src.allocated_string) {
923	XtFree((char *)src->multi_src.string);
924	src->multi_src.allocated_string = False;
925	src->multi_src.string = NULL;
926    }
927}
928
929/*
930 * Function:
931 *	_XawMultiSave
932 *
933 * Parameters:
934 *	w - multiSrc Widget
935 *
936 * Description:
937 *	Saves all the pieces into a file or string as required.
938 *
939 * Returns:
940 *	True if the save was successful
941 *
942 * Note:
943 * The public interface is XawAsciiSave(w)!
944 */
945Bool
946_XawMultiSave(Widget w)
947{
948    MultiSrcObject src = (MultiSrcObject)w;
949    XtAppContext app_con = XtWidgetToApplicationContext(w);
950    char *mb_string;
951
952    /*
953     * If using the string in place then there is no need to play games
954     * to get the internal info into a readable string
955     */
956    if (src->multi_src.use_string_in_place)
957	return (True);
958
959    if (src->multi_src.type == XawAsciiFile) {
960#ifdef OLDXAW
961	 if (!src->multi_src.changes)
962#else
963	if (!src->text_src.changed)		/* No changes to save */
964#endif
965	    return (True);
966
967	mb_string = StorePiecesInString(src);
968
969	if (mb_string != 0) {
970	    if (WriteToFile(mb_string, (String)src->multi_src.string) == False) {
971		XtFree(mb_string);
972		return (False);
973	    }
974	    XtFree(mb_string);
975#ifndef OLDXAW
976	    src->text_src.changed = False;
977#else
978	    src->multi_src.changes = False;
979#endif
980	    return (True);
981	}
982	else {
983	    /* If the buffer holds bad chars, don't touch it... */
984	    XtAppWarningMsg(app_con,
985			    "convertError", "multiSource", "XawError",
986			    "Due to illegal characters, file not saved.",
987			    NULL, NULL);
988	    return (False);
989	}
990    }
991    else  {
992    /* THIS FUNCTIONALITY IS UNDOCUMENTED, probably UNNEEDED?  The manual
993	   says this routine's only function is to save files to
994	   disk.  -Sheeran */
995	mb_string = StorePiecesInString(src);
996
997	if (mb_string == 0) {
998	    /* If the buffer holds bad chars, don't touch it... */
999	    XtAppWarningMsg(app_con,
1000			    "convertError", "multiSource", "XawError",
1001			    XtName(XtParent((Widget)src)), NULL, NULL);
1002	    return (False);
1003	}
1004
1005	/* assert: mb_string holds good characters so the buffer is fine */
1006	if (src->multi_src.allocated_string == True)
1007	    XtFree((char *)src->multi_src.string);
1008	else
1009	    src->multi_src.allocated_string = True;
1010
1011        src->multi_src.string = mb_string;
1012    }
1013#ifdef OLDXAW
1014    src->multi_src.changes = False;
1015#else
1016    src->text_src.changed = False;
1017#endif
1018
1019    return (True);
1020}
1021
1022/*
1023 * Function:
1024 *	XawMultiSaveAsFile
1025 *
1026 * Parameters:
1027 *	w - MultiSrc widget
1028 *	name - name of the file to save this file into
1029 *
1030 * Description:
1031 *	Save the current buffer as a file.
1032 *
1033 * Returns:
1034 *	True if the save was successful
1035 *
1036 * Note:
1037 * The public interface is XawAsciiSaveAsFile!
1038 */
1039Bool
1040_XawMultiSaveAsFile(Widget w, _Xconst char* name)
1041{
1042    MultiSrcObject src = (MultiSrcObject)w;
1043    char *mb_string = StorePiecesInString(src);
1044
1045    if (mb_string != 0) {
1046	Bool ret = WriteToFile(mb_string, (String)name);
1047	XtFree(mb_string);
1048
1049	return (ret);
1050    }
1051
1052    /* otherwise there was a conversion error.	So print widget name too */
1053    XtAppWarningMsg(XtWidgetToApplicationContext(w),
1054		    "convertError", "multiSource", "XawError",
1055		    XtName(XtParent(w)), NULL, NULL);
1056
1057    return (False);
1058}
1059
1060/*
1061 * Private Functions
1062 */
1063static void
1064RemoveOldStringOrFile(MultiSrcObject src, Bool checkString)
1065{
1066    FreeAllPieces(src);
1067
1068    if (checkString && src->multi_src.allocated_string) {
1069	XtFree((char *)src->multi_src.string);
1070	src->multi_src.allocated_string = False;
1071	src->multi_src.string = NULL;
1072    }
1073}
1074
1075/*
1076 * Function:
1077 *	WriteToFile
1078 *
1079 * Parameters:
1080 *	string - string to write
1081 *	name   - name of the file
1082 *
1083 * Description:
1084 *	Write the string specified to the beginning of the file  specified.
1085 *
1086 * Returns:
1087 *	Returns True if successful, False otherwise
1088 */
1089static Bool
1090WriteToFile(String string, String name)
1091{
1092    int fd;
1093    Bool result = True;
1094
1095    if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) == -1)
1096	return (False);
1097
1098    if (write(fd, string, strlen(string)) == -1)
1099        result = False;
1100
1101    if (close(fd) == -1)
1102	return (False);
1103
1104    return (result);
1105}
1106
1107
1108/*
1109 * Function:
1110 *	StorePiecesInString
1111 *
1112 * Parameters:
1113 *	src - the multiSrc object to gather data from
1114 *
1115 * Description:
1116 *	Store the pieces in memory into a char string.
1117 *
1118 * Returns:
1119 *	mb_string:	Caller must free
1120 *	(or)
1121 *	NULL:		conversion error
1122 */
1123static char *
1124StorePiecesInString(MultiSrcObject src)
1125{
1126    wchar_t *wc_string;
1127    char *mb_string;
1128    int char_count = (int)src->multi_src.length;
1129    XawTextPosition first;
1130    MultiPiece *piece;
1131
1132    /* I believe the char_count + 1 and the NULL termination are unneeded! FS */
1133    wc_string = (wchar_t*)XtMalloc((Cardinal)((size_t)(char_count + 1) * sizeof(wchar_t)));
1134
1135    for (first = 0, piece = src->multi_src.first_piece ; piece != NULL;
1136	 first += piece->used, piece = piece->next)
1137	(void)wcsncpy(wc_string + first, piece->text, (size_t)piece->used);
1138
1139    wc_string[char_count] = 0;
1140
1141    /* This will refill all pieces to capacity */
1142    if (src->multi_src.data_compression) {
1143	FreeAllPieces(src);
1144	LoadPieces(src, NULL, (char *)wc_string);
1145    }
1146
1147    /* Lastly, convert it to a MB format and send it back */
1148    mb_string = _XawTextWCToMB(XtDisplayOfObject((Widget)src),
1149			       wc_string, &char_count);
1150
1151    /* NOTE THAT mb_string MAY BE ZERO IF THE CONVERSION FAILED */
1152    XtFree((char*)wc_string);
1153
1154    return (mb_string);
1155}
1156
1157/*
1158 * Function:
1159 *	InitStringOrFile
1160 *
1161 * Parameters:
1162 *	src - MultiSource
1163 *
1164 * Description:
1165 *	Initializes the string or file.
1166 */
1167static FILE *
1168InitStringOrFile(MultiSrcObject src, Bool newString)
1169{
1170    mode_t open_mode = 0;
1171    const char *fdopen_mode = NULL;
1172    int fd;
1173    FILE *file;
1174    Display *d = XtDisplayOfObject((Widget)src);
1175
1176    if (src->multi_src.type == XawAsciiString) {
1177	if (src->multi_src.string == NULL)
1178	    src->multi_src.length = 0;
1179
1180	else if (!src->multi_src.use_string_in_place) {
1181	    int length;
1182	    char * temp = XtNewString((char *)src->multi_src.string);
1183
1184	    if (src->multi_src.allocated_string)
1185		XtFree((char *)src->multi_src.string);
1186	    src->multi_src.allocated_string = True;
1187	    src->multi_src.string = temp;
1188
1189	    length = (int)strlen((char *)src->multi_src.string);
1190
1191	    /* Wasteful, throwing away the WC string, but need side effect! */
1192	    (void)_XawTextMBToWC(d, (char *)src->multi_src.string, &length);
1193	    src->multi_src.length = (XawTextPosition)length;
1194	}
1195	else {
1196	    src->multi_src.length = (XawTextPosition)strlen((char *)src->multi_src.string);
1197	    /* In case the length resource is incorrectly set */
1198	    if (src->multi_src.length > src->multi_src.multi_length)
1199		src->multi_src.multi_length = (int)src->multi_src.length;
1200
1201	    if (src->multi_src.multi_length == MAGIC_VALUE)
1202		src->multi_src.piece_size = src->multi_src.length;
1203	    else
1204		src->multi_src.piece_size = src->multi_src.multi_length + 1;
1205	}
1206
1207	return (NULL);
1208    }
1209
1210    /*
1211     * type is XawAsciiFile
1212     */
1213    src->multi_src.is_tempfile = False;
1214
1215    switch (src->text_src.edit_mode) {
1216	case XawtextRead:
1217	    if (src->multi_src.string == NULL)
1218		XtErrorMsg("NoFile", "multiSourceCreate", "XawError",
1219			   "Creating a read only disk widget and no file specified.",
1220			   NULL, 0);
1221	    open_mode = O_RDONLY | O_CLOEXEC;
1222	    fdopen_mode = "r";
1223	    break;
1224	case XawtextAppend:
1225	case XawtextEdit:
1226	    if (src->multi_src.string == NULL) {
1227		src->multi_src.string = (char *)"*multi-src*";
1228		src->multi_src.is_tempfile = True;
1229	    }
1230	    else {
1231/* O_NOFOLLOW was a FreeBSD & Linux extension, now adopted by POSIX */
1232#ifdef O_NOFOLLOW
1233		open_mode = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
1234#else
1235		open_mode = O_RDWR; /* unsafe; subject to race conditions */
1236#endif
1237		fdopen_mode = "r+";
1238	    }
1239	    break;
1240	default:
1241	    XtErrorMsg("badMode", "multiSourceCreate", "XawError",
1242		       "Bad editMode for multi source; must be "
1243		       "Read, Append or Edit.", NULL, NULL);
1244    }
1245
1246    /* If is_tempfile, allocate a private copy of the text
1247     * Unlikely to be changed, just to set allocated_string */
1248    if (newString || src->multi_src.is_tempfile) {
1249	char * temp = XtNewString((char *)src->multi_src.string);
1250
1251	if (src->multi_src.allocated_string)
1252	    XtFree((char *)src->multi_src.string);
1253	src->multi_src.string = temp;
1254	src->multi_src.allocated_string = True;
1255    }
1256
1257    if (!src->multi_src.is_tempfile) {
1258	if ((fd = open((char *)src->multi_src.string, (int)open_mode, 0666)) != -1) {
1259	    if ((file = fdopen(fd, fdopen_mode)) != NULL) {
1260		(void)fseek(file, 0, SEEK_END);
1261		src->multi_src.length = (XawTextPosition)ftell(file);
1262		return(file);
1263	    }
1264	    else
1265		close(fd);
1266	}
1267	{
1268	    String params[2];
1269	    Cardinal num_params = 2;
1270
1271	    params[0] = (String)src->multi_src.string;
1272	    params[1] = strerror(errno);
1273	    XtAppWarningMsg(XtWidgetToApplicationContext((Widget)src),
1274			    "openError", "multiSourceCreate", "XawWarning",
1275			    "Cannot open file %s; %s", params, &num_params);
1276	}
1277    }
1278    src->multi_src.length = 0;
1279    return (NULL);
1280}
1281
1282/* LoadPieces:  This routine takes either the MB contents of open file
1283   `file' or the MB contents of string or the MB contents of
1284   src->multi_src.string and places them in Pieces in WC format.
1285
1286   CAUTION: You must have src->multi_src.length set to file length bytes
1287   when src->multi_src.type == XawAsciiFile.  src->multi_src.length must be
1288   the length of the parameter string if string is non-NULL
1289*/
1290static void
1291LoadPieces(MultiSrcObject src, FILE *file, char *string)
1292{
1293    Display *d = XtDisplayOfObject((Widget)src);
1294    wchar_t* local_str, *ptr;
1295    MultiPiece* piece = NULL;
1296    XawTextPosition left;
1297    int bytes = sizeof(wchar_t);
1298    char* temp_mb_holder = NULL;
1299
1300    /*
1301     * This is tricky - the _XawTextMBtoWC converter uses its 3rd arg
1302     * in as MB length, out as WC length.  We want local_length to be
1303     * WC count.
1304     */
1305    int local_length = (int)src->multi_src.length;
1306
1307    if (string != NULL) {
1308	/*
1309	 * ASSERT: IF our caller passed a non-null string, THEN
1310	 * src->multi_src.length is currently string's * byte count,
1311	 * AND string is in a MB format
1312	*/
1313	local_str = _XawTextMBToWC(d, (char *)string, &local_length);
1314	src->multi_src.length = (XawTextPosition) local_length;
1315    }
1316    else if (src->multi_src.type != XawAsciiFile) {
1317	/*
1318	 * here, we are not changing the contents, just reloading,
1319	 * so don't change len...
1320	 */
1321	local_length = (int)(src->multi_src.string ?
1322	    strlen((char *)src->multi_src.string) : 0);
1323	local_str = _XawTextMBToWC(d, (char *)src->multi_src.string,
1324				   &local_length);
1325    }
1326    else {
1327	if (src->multi_src.length != 0) {
1328	    temp_mb_holder =
1329		XtMalloc((Cardinal)((size_t)(src->multi_src.length + 1) * sizeof(unsigned char)));
1330	    fseek(file, 0, SEEK_SET);
1331	    src->multi_src.length = (XawTextPosition)fread(temp_mb_holder,
1332					  sizeof(unsigned char),
1333					  (size_t)src->multi_src.length, file);
1334	    if (src->multi_src.length <= 0)
1335		XtAppErrorMsg(XtWidgetToApplicationContext ((Widget) src),
1336			      "readError", "multiSource", "XawError",
1337			      "fread returned error.", NULL, NULL);
1338	    local_length = (int)src->multi_src.length;
1339	    local_str = _XawTextMBToWC(d, temp_mb_holder, &local_length);
1340	    src->multi_src.length = local_length;
1341
1342	    if (local_str == 0) {
1343		String params[2];
1344		Cardinal num_params;
1345		static char err_text[] =
1346		    "<<< FILE CONTENTS NOT REPRESENTABLE IN THIS LOCALE >>>";
1347
1348		params[0] = XtName(XtParent((Widget)src));
1349		params[1] = src->multi_src.string;
1350		num_params = 2;
1351
1352		XtAppWarningMsg(XtWidgetToApplicationContext((Widget)src),
1353				"readLocaleError", "multiSource", "XawError",
1354				"%s: The file `%s' contains characters "
1355				"not representable in this locale.",
1356				params, &num_params);
1357		src->multi_src.length = sizeof err_text;
1358		local_length = (int)src->multi_src.length;
1359		local_str = _XawTextMBToWC(d, err_text, &local_length);
1360		src->multi_src.length = local_length;
1361	    }
1362	}
1363	else
1364	    /* ASSERT that since following while loop looks at local_length
1365	       this isn't needed.	Sheeran, Omron KK, 1993/07/15
1366	       temp_mb_holder[src->multi_src.length] = '\0'; */
1367	    local_str = (wchar_t*)temp_mb_holder;
1368    }
1369
1370    if (src->multi_src.use_string_in_place) {
1371	piece = AllocNewPiece(src, piece);
1372	piece->used = Min(src->multi_src.length, src->multi_src.piece_size);
1373	piece->text = (wchar_t*)src->multi_src.string;
1374	return;
1375    }
1376
1377    ptr = local_str;
1378    left = local_length;
1379
1380    do {
1381	piece = AllocNewPiece(src, piece);
1382
1383	piece->text = (wchar_t*)XtMalloc((unsigned)(src->multi_src.piece_size
1384						    * bytes));
1385	piece->used = Min(left, src->multi_src.piece_size);
1386	if (piece->used != 0)
1387	(void)wcsncpy(piece->text, ptr, (size_t)piece->used);
1388
1389	left -= piece->used;
1390	ptr += piece->used;
1391    } while (left > 0);
1392
1393    if (temp_mb_holder)
1394	XtFree((char*)temp_mb_holder);
1395}
1396
1397/*
1398 * Function:
1399 *	AllocNewPiece
1400 *
1401 * Parameters:
1402 *	src  - MultiSrc Widget
1403 *	prev - the piece just before this one, or NULL
1404 *
1405 * Description:
1406 *	Allocates a new piece of memory.
1407 *
1408 * Returns:
1409 *	The allocated piece
1410 */
1411static MultiPiece *
1412AllocNewPiece(MultiSrcObject src, MultiPiece *prev)
1413{
1414    MultiPiece *piece = XtNew(MultiPiece);
1415
1416    if (prev == NULL) {
1417	src->multi_src.first_piece = piece;
1418	piece->next = NULL;
1419    }
1420    else {
1421	if (prev->next != NULL)
1422	    (prev->next)->prev = piece;
1423	piece->next = prev->next;
1424	prev->next = piece;
1425    }
1426
1427    piece->prev = prev;
1428
1429    return (piece);
1430}
1431
1432/*
1433 * Function:
1434 *	FreeAllPieces
1435 *
1436 * Parameters:
1437 *	src - MultiSrc Widget
1438 *
1439 * Description:
1440 *	Frees all the pieces
1441 */
1442static void
1443FreeAllPieces(MultiSrcObject src)
1444{
1445    MultiPiece *next, *first = src->multi_src.first_piece;
1446
1447#ifdef DEBUG
1448    if (first->prev != NULL)
1449	printf("Xaw MultiSrc Object: possible memory leak in FreeAllPieces().\n");
1450#endif
1451
1452    for (; first != NULL ; first = next) {
1453	next = first->next;
1454	RemovePiece(src, first);
1455    }
1456}
1457
1458/*
1459 * Function:
1460 *	RemovePiece
1461 *
1462 * Parameters:
1463 *	piece - piece to remove
1464 *
1465 * Description:
1466 *	Removes a piece from the list.
1467 */
1468static void
1469RemovePiece(MultiSrcObject src, MultiPiece *piece)
1470{
1471    if (piece->prev == NULL)
1472	src->multi_src.first_piece = piece->next;
1473    else
1474	piece->prev->next = piece->next;
1475
1476    if (piece->next != NULL)
1477	piece->next->prev = piece->prev;
1478
1479    if (!src->multi_src.use_string_in_place)
1480	XtFree((char *)piece->text);
1481
1482    XtFree((char *)piece);
1483}
1484
1485/*
1486 * Function:
1487 *	FindPiece
1488 *
1489 * Parameters:
1490 *	src - MultiSrc Widget
1491 *	position - position that we are searching for
1492 *	first - position of the first character in this piece (return)
1493 *
1494 * Description:
1495 *	Finds the piece containing the position indicated.
1496 *
1497 * Returns:
1498 *	Piece that contains this position
1499 */
1500static MultiPiece *
1501FindPiece(MultiSrcObject src, XawTextPosition position, XawTextPosition *first)
1502{
1503    MultiPiece *old_piece, *piece;
1504    XawTextPosition temp;
1505
1506    for (old_piece = NULL, piece = src->multi_src.first_piece, temp = 0;
1507         piece; old_piece = piece, piece = piece->next)
1508	if ((temp += piece->used) > position) {
1509	    *first = temp - piece->used;
1510	    return (piece);
1511	}
1512
1513    *first = temp - (old_piece ? old_piece->used : 0);
1514
1515    return (old_piece);	  /* if we run off the end the return the last piece */
1516}
1517
1518/*
1519 * Function:
1520 *	BreakPiece
1521 *
1522 * Parameters:
1523 *	src - MultiSrc Widget
1524 *	piece - piece to break
1525 *
1526 * Description:
1527 *	Breaks a full piece into two new pieces.
1528 */
1529#define HALF_PIECE (src->multi_src.piece_size >> 1)
1530static void
1531BreakPiece(MultiSrcObject src, MultiPiece *piece)
1532{
1533    MultiPiece *cnew = AllocNewPiece(src, piece);
1534
1535    cnew->text = (wchar_t *)
1536	XtMalloc((Cardinal)((size_t)src->multi_src.piece_size * sizeof(wchar_t)));
1537    (void)wcsncpy(cnew->text, piece->text + HALF_PIECE,
1538		  (size_t)(src->multi_src.piece_size - HALF_PIECE));
1539    piece->used = HALF_PIECE;
1540    cnew->used = src->multi_src.piece_size - HALF_PIECE;
1541}
1542
1543/*ARGSUSED*/
1544static void
1545CvtStringToMultiType(XrmValuePtr args _X_UNUSED, Cardinal *num_args _X_UNUSED,
1546		     XrmValuePtr fromVal, XrmValuePtr toVal)
1547{
1548    static XawAsciiType type = XawAsciiString;
1549    XrmQuark q;
1550    char name[7];
1551
1552    XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name));
1553    q = XrmStringToQuark(name);
1554
1555    if (q == Qstring)
1556	type = XawAsciiString;
1557    if (q == Qfile)
1558	type = XawAsciiFile;
1559    else {
1560	toVal->size = 0;
1561	toVal->addr = NULL;
1562	XtStringConversionWarning((char *)fromVal->addr, XtRAsciiType);
1563    }
1564
1565    toVal->size = sizeof(XawAsciiType);
1566    toVal->addr = (XPointer)&type;
1567}
1568
1569/*ARGSUSED*/
1570static Boolean
1571CvtMultiTypeToString(Display *dpy, XrmValuePtr args _X_UNUSED, Cardinal *num_args _X_UNUSED,
1572		     XrmValuePtr fromVal, XrmValuePtr toVal,
1573		     XtPointer *data _X_UNUSED)
1574{
1575    static String buffer;
1576    Cardinal size;
1577
1578    switch (*(XawAsciiType *)fromVal->addr) {
1579	case XawAsciiFile:
1580	    buffer = XtEfile;
1581	    break;
1582	case XawAsciiString:
1583	    buffer = XtEstring;
1584	    break;
1585	default:
1586	    XawTypeToStringWarning(dpy, XtRAsciiType);
1587	    toVal->addr = NULL;
1588	    toVal->size = 0;
1589	    return (False);
1590    }
1591
1592    size = (Cardinal)strlen(buffer) + 1;
1593    if (toVal->addr != NULL) {
1594	if (toVal->size < size) {
1595	    toVal->size = size;
1596	    return (False);
1597	}
1598	strcpy((char *)toVal->addr, buffer);
1599    }
1600    else
1601	toVal->addr = (XPointer)buffer;
1602    toVal->size = sizeof(String);
1603
1604    return (True);
1605}
1606
1607/*ARGSUSED*/
1608static void
1609GetDefaultPieceSize(Widget w _X_UNUSED, int offset _X_UNUSED, XrmValue *value)
1610{
1611    static XPointer pagesize;
1612
1613    if (pagesize == 0) {
1614	pagesize = (XPointer)((long)_XawGetPageSize());
1615	if (pagesize < (XPointer)BUFSIZ)
1616	    pagesize = (XPointer)BUFSIZ;
1617    }
1618
1619    value->addr = (XPointer)&pagesize;
1620}
1621