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