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