dtcalc: change from obsoleted MAXFLOAT to FLT_MAX from std C
[oweals/cde.git] / cde / lib / DtWidget / EditAreaData.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: EditAreaData.c /main/6 1998/03/03 16:18:13 mgreess $ */
24 /**********************************<+>*************************************
25  ***************************************************************************
26  **
27  **  File:        EditAreaData.c
28  **
29  **  Project:     DtEditor widget for editing services
30  **
31  **  Description:  Contains functions for getting and setting the data
32  **                on which the editor operates.
33  **  -----------
34  **
35  *******************************************************************
36  *
37  * (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
38  * (c) Copyright 1996 Digital Equipment Corporation.
39  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company.
40  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp.
41  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.
42  * (c) Copyright 1996 Novell, Inc. 
43  * (c) Copyright 1996 FUJITSU LIMITED.
44  * (c) Copyright 1996 Hitachi.
45  *
46  ********************************************************************
47  **
48  **
49  **************************************************************************
50  **********************************<+>*************************************/
51 #include "EditorP.h" 
52 #include <X11/Xutil.h>
53 #include <Xm/TextP.h>
54 #include <unistd.h>
55 #include "DtWidgetI.h"
56
57
58 typedef enum _LoadActionType {
59           LOAD_DATA,
60           INSERT_DATA,
61           APPEND_DATA,
62           REPLACE_DATA
63 } LoadActionType;
64
65 static DtEditorErrorCode Check4EnoughMemory(
66         int numBytes);
67
68 static DtEditorErrorCode StripEmbeddedNulls(
69         char *stringData,
70         int *length);
71
72 static DtEditorErrorCode LoadFile(
73         Widget w,
74         char *fileName,
75         LoadActionType action,
76         XmTextPosition   startReplace,
77         XmTextPosition   endReplace );
78
79 #ifdef NEED_STRCASECMP
80
81 /*
82  * in case strcasecmp is not provided by the system here is one
83  * which does the trick
84  */
85 static int
86 strcasecmp(s1, s2)
87     register char *s1, *s2;
88 {
89     register int c1, c2;
90
91     while (*s1 && *s2) {
92         c1 = isupper(*s1) ? tolower(*s1) : *s1;
93         c2 = isupper(*s2) ? tolower(*s2) : *s2;
94         if (c1 != c2)
95             return (1);
96         s1++;
97         s2++;
98     }
99     if (*s1 || *s2)
100         return (1);
101     return (0);
102 }
103
104 #endif
105
106 /*****************************************************************************
107  * 
108  * Check4EnoughMemory - estimates whether there is enough memory to malloc 
109  * "numBytes" of memory.  This routine doubles the amount needed because the 
110  * routines  that use it are putting data into the text widget & we must make
111  * sure the widget will have room, too.
112  * 
113  * Returns DtEDITOR_NO_ERRORS 
114  *         DtEDITOR_ILLEGAL_SIZE
115  *         DtEDITOR_INSUFFICIENT_MEMORY
116  * 
117  *****************************************************************************/
118 static DtEditorErrorCode 
119 Check4EnoughMemory(
120         int numBytes)
121 {
122
123     DtEditorErrorCode returnVal = DtEDITOR_ILLEGAL_SIZE;
124
125     if (numBytes > 0) {
126        char *tmpString = (char *)malloc((2 * numBytes) + (numBytes/10));
127
128        if(tmpString == (char *)NULL)
129          returnVal = DtEDITOR_INSUFFICIENT_MEMORY;  
130        else {
131          returnVal = DtEDITOR_NO_ERRORS;
132          free(tmpString);
133        }
134     }
135
136     return( returnVal );
137
138 } /* end Check4EnoughMemory */
139
140
141 /*****************************************************************************
142  * 
143  * StripEmbeddedNulls - removes any embedded NULLs (\0) in a string of length 
144  *      'length'.  The removal occurs in place, with 'length' set to the
145  *      new, stripped length.  The resulting string is terminated with a 
146  *      trailing NULL.  
147  * 
148  * Returns DtEDITOR_NO_ERRORS - the string did not contain any embedded NULLs
149  *         DtEDITOR_NULLS_REMOVED - the string did contain embedded
150  *                                  NULLs that were removed.
151  * 
152  *****************************************************************************/
153 static DtEditorErrorCode 
154 StripEmbeddedNulls(
155         char *stringData,
156         int *length)
157 {
158     DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
159
160     if (strlen(stringData) != *length)
161     {
162        int firstNull;
163
164        returnVal = DtEDITOR_NULLS_REMOVED;
165
166        /*
167         * The file contains NULL characters, so we strip them out and
168         * report that we have done so.
169         */
170        while((firstNull = strlen(stringData)) != *length)
171        {
172           int lastNull = firstNull;
173
174           while((lastNull + 1) < *length &&
175                 stringData[lastNull + 1] == (char)'\0')
176               lastNull++;
177
178           memcpy(&stringData[firstNull], &stringData[lastNull + 1],
179                  *length - lastNull);
180           *length -= 1 + lastNull - firstNull;
181        }
182
183     }
184
185     return( returnVal);
186
187 } /* end StripEmbeddedNulls */
188
189
190 /*****************************************************************************
191  * 
192  * Retrieves the current location of the insert cursor
193  * 
194  *****************************************************************************/
195
196 XmTextPosition
197 DtEditorGetInsertionPosition(
198         Widget widget)
199 {
200   DtEditorWidget editor = (DtEditorWidget) widget;
201   XmTextPosition result;
202   _DtWidgetToAppContext(widget);
203   _DtAppLock(app);
204
205   result = XmTextGetInsertionPosition(M_text(editor));
206
207   _DtAppUnlock(app);
208   return result;
209 }
210
211
212 /*****************************************************************************
213  * 
214  * Retrieves the current location of the last character in the widget
215  * 
216  *****************************************************************************/
217
218 XmTextPosition
219 DtEditorGetLastPosition(
220         Widget widget)
221 {
222   DtEditorWidget editor = (DtEditorWidget) widget;
223   XmTextPosition result;
224   _DtWidgetToAppContext(widget);
225   _DtAppLock(app);
226
227   result = XmTextGetLastPosition(M_text(editor));
228
229   _DtAppUnlock(app);
230   return result;
231 }
232
233
234 /*****************************************************************************
235  * 
236  * Changes the current location of the insert cursor
237  * 
238  *****************************************************************************/
239
240 void 
241 DtEditorSetInsertionPosition(
242         Widget widget,
243         XmTextPosition position)
244 {
245   DtEditorWidget editor = (DtEditorWidget) widget;
246   _DtWidgetToAppContext(widget);
247   _DtAppLock(app);
248
249   XmTextSetInsertionPosition(M_text(editor), position);
250
251   _DtAppUnlock(app);
252 }
253
254
255 static DtEditorErrorCode 
256 setStringValue( 
257         DtEditorWidget editor,
258         char *data)
259 {
260     /* 
261      * Tell _DtEditorModifyVerifyCB() that we're replacing the entire
262      * contents, so it doesn't try to save the current document in an
263      * undo structure for a later undo.
264      */
265     M_loadingAllNewData(editor) = True;
266
267     XmTextSetString( M_text(editor), data );
268
269     /* 
270      * If the _DtEditorModifyVerifyCB() did not get called, reset the
271      * things which usually get reset there.  The modifyVerify callback
272      * will not get called if the contents are being set to a null string 
273      * and the widget is already empty.
274      */
275     if (M_loadingAllNewData(editor) == True) {
276       M_loadingAllNewData(editor) = False;
277       M_unreadChanges(editor) = False;
278       _DtEditorResetUndo(editor);
279     }
280
281     return( DtEDITOR_NO_ERRORS );
282
283 } /* end setStringValue */
284
285
286 static DtEditorErrorCode 
287 setDataValue( 
288         DtEditorWidget  widget,
289         void    *rawData,
290         int     length)
291 {
292    DtEditorErrorCode status = DtEDITOR_NULL_ITEM, tmpError;
293
294    /*
295     * Validate input
296     */
297    if (rawData != (void *)NULL)
298    {
299
300       /* 
301        * Check to see if we have a valid buffer size & enough memory to 
302        * load the buffer into the text widget.  This is only an estimate 
303        * of our needs.
304        * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS, 
305        * DtEDITOR_ILLEGAL_SIZE, or  DtEDITOR_INSUFFICIENT_MEMORY.
306        */
307
308       status = Check4EnoughMemory( length );
309       if (status == DtEDITOR_NO_ERRORS) 
310       {
311
312         /*
313          * Convert the data buffer into a string & insert into the widget
314          */
315         char *textData = (char *)XtMalloc(length + 1);
316         memcpy( textData, rawData, length );
317         textData[length] = '\0';
318
319         /* 
320          * Strip out any embedded NULLs because the text widget will only 
321          * accept data up to the first NULL.
322          * 
323          * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or 
324          * DtEDITOR_NULLS_REMOVED
325          */
326         status = StripEmbeddedNulls( textData, &length );
327
328         /*
329          * Now, insert the converted string into the text widget
330          */
331         tmpError = setStringValue( widget, textData );
332         if (tmpError != DtEDITOR_NO_ERRORS)
333           status = tmpError;
334
335         XtFree( (char *)textData );
336       }
337    }
338
339    return( status );
340
341 } /* end setDataValue */
342
343
344 static DtEditorErrorCode 
345 setWcharValue( 
346         DtEditorWidget editor,
347         wchar_t *data)
348 {
349    DtEditorErrorCode status;
350    wchar_t *tmp_wc;
351    int result, num_chars=0;
352    char *mb_value = (char *)NULL;
353
354
355    /* 
356     * Convert the wide char string to a multi-byte string & stick it in
357     * the text widget.
358     */
359     
360    /* 
361     * Determine how big the resulting mb string may be 
362     */
363    for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++) 
364      tmp_wc++;
365
366    /* 
367     * Check to see if we have enough memory to load the string
368     * into the text widget.  This is only an estimate of our needs.
369     * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or 
370     * DtEDITOR_INSUFFICIENT_MEMORY.
371     */
372    status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX );
373    if (status != DtEDITOR_NO_ERRORS) return status;       
374    mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX );
375
376    /*
377     * Convert the wchar string
378     * If wcstombs fails it returns (size_t) -1, so pass in empty
379     * string.
380     */
381    result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
382    if (result == (size_t)-1) 
383      result = 0;
384
385    /*
386     * wcstombs doesn't guarantee string is NULL terminated
387     */
388    mb_value[result] = 0;
389
390    status = setStringValue( editor, mb_value );
391
392    XtFree(mb_value);
393
394    return( status );
395
396 } /* end setWcharValue */
397
398
399 static DtEditorErrorCode 
400 insertStringValue( 
401         DtEditorWidget   editor,
402         char            *data,
403         LoadActionType   typeOfInsert,
404         XmTextPosition   beginInsert,
405         XmTextPosition   endInsert)
406 {
407     int numInserted;
408
409     switch( typeOfInsert ) 
410     {
411       case INSERT_DATA:
412       {
413         beginInsert = endInsert = XmTextGetInsertionPosition( M_text(editor) );
414         break;
415       }
416
417       case APPEND_DATA:
418       {
419         beginInsert = endInsert = XmTextGetLastPosition( M_text(editor) );
420         break;
421       }
422
423       case REPLACE_DATA:
424       {
425         break;
426       }
427
428       default:
429       {
430       }
431     } /* end switch */
432
433     /*
434      * Insert/Replace/Append the data and move the insertion cursor to
435      * the end of the inserted data.
436      */
437     numInserted = _DtEditor_CountCharacters( data, strlen(data) );
438     XmTextReplace(M_text(editor), beginInsert, endInsert, data);
439     XmTextSetInsertionPosition( M_text(editor), 
440                                 (XmTextPosition)(beginInsert + numInserted) );
441
442     return( DtEDITOR_NO_ERRORS );
443 } /* insertStringValue */
444
445 static DtEditorErrorCode 
446 insertDataValue( 
447         DtEditorWidget  widget,
448         void    *rawData,
449         int     length,
450         LoadActionType   typeOfInsert,
451         XmTextPosition   beginInsert,
452         XmTextPosition   endInsert)
453 {
454    DtEditorErrorCode status = DtEDITOR_NULL_ITEM, loadError;
455
456    /*
457     * Validate input
458     */
459    if (rawData != (void *) NULL)
460    {
461
462       /* 
463        * Check to see if we have a valid buffer size & enough memory to 
464        * insert the buffer into the text widget.  This is only an estimate 
465        * of our needs.
466        * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or 
467        * DtEDITOR_INSUFFICIENT_MEMORY.
468        */
469       status = Check4EnoughMemory( length );
470       if (status == DtEDITOR_NO_ERRORS) 
471       {
472
473          /*
474           * Convert the data buffer into a string & insert into the widget
475           */
476          char *textData = (char *)XtMalloc(length + 1);
477          memcpy( textData, rawData, length );
478          textData[length] = '\0';
479
480          /* 
481           * Strip out any embedded NULLs because the text widget will only 
482           * accept data up to the first NULL.
483           *
484           * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or 
485           * DtEDITOR_NULLS_REMOVED
486           */
487          status = StripEmbeddedNulls( textData, &length );
488
489          /*
490           * Now, insert the converted string into the text widget
491           */
492          loadError = insertStringValue( widget, textData, typeOfInsert, 
493                                         beginInsert, endInsert );
494          if (loadError != DtEDITOR_NO_ERRORS)
495            status = loadError;
496
497          XtFree( (char *)textData );
498       }
499
500    }
501
502    return( status );
503
504 } /* insertDataValue */
505
506
507 static DtEditorErrorCode 
508 insertWcharValue( 
509         DtEditorWidget   editor,
510         wchar_t          *data,
511         LoadActionType   typeOfInsert,
512         XmTextPosition   beginInsert,
513         XmTextPosition   endInsert)
514 {
515
516    wchar_t *tmp_wc;
517    int result, num_chars=0;
518    char *mb_value = (char *)NULL;
519    DtEditorErrorCode status;
520
521
522    /* 
523     * Convert the wide char string to a multi-byte string & insert it into
524     * the text widget.
525     */
526     
527    /* 
528     * Determine how big the resulting mb string may be 
529     */
530    for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++) 
531      tmp_wc++;
532
533    /* 
534     * Check to see if we have enough memory to insert the string
535     * into the text widget.  This is only an estimate of our needs.
536     * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or 
537     * DtEDITOR_INSUFFICIENT_MEMORY.
538     */
539
540    status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX );
541    if(status != DtEDITOR_NO_ERRORS) return status;        
542    mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX );
543
544    /*
545     * Convert the wchar string.
546     * If wcstombs fails it returns (size_t) -1, so pass in empty
547     * string.
548     */
549    result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
550    if (result == (size_t)-1) 
551      result = 0;
552
553    /*
554     * wcstombs doesn't guarantee string is NULL terminated
555     */
556    mb_value[result] = 0;
557
558    status = insertStringValue( editor, mb_value, typeOfInsert, 
559                                beginInsert, endInsert );
560    XtFree( mb_value );
561
562    return( status );
563
564 } /* insertWcharValue */
565
566
567 /***************************************************************************
568  *
569  * DtEditorSetContents - sets the contents of the DtEditor widget.
570  *
571  *      Inputs: widget to set the contents
572  *
573  *              a data structure containing the data to put into the
574  *              widget.  Depending upon the type of data being set, this
575  *              structure will contain various fields:
576  *
577  *              string - \0-terminated string of characters
578  *              data - the data, the size of the data
579  *
580  *      Returns  0 - contents were set sucessfully
581  *              !0 - an error occured while setting the contents
582  *
583  ***************************************************************************/
584
585 extern DtEditorErrorCode
586 DtEditorSetContents(
587         Widget                  widget,
588         DtEditorContentRec      *data )
589 {
590    DtEditorErrorCode    error = DtEDITOR_INVALID_TYPE;
591    DtEditorWidget editor = (DtEditorWidget) widget;
592    _DtWidgetToAppContext(widget);
593    _DtAppLock(app);
594
595    switch( data->type ) 
596    {
597      case DtEDITOR_TEXT:
598      {
599        error = setStringValue ( editor, data->value.string );
600        break;
601      }
602      case DtEDITOR_DATA:
603      {
604        error = setDataValue ( editor, data->value.data.buf,
605                               data->value.data.length);
606        break;
607      }
608      case DtEDITOR_WCHAR:
609      {
610        error = setWcharValue ( editor, data->value.wchar );
611        break;
612      }
613      default :
614      {
615        error = DtEDITOR_INVALID_TYPE;
616      }
617    } /* end switch */
618
619    /*
620     * Update the current-line-display in the status line 
621     */
622    if (error == DtEDITOR_NO_ERRORS)
623      _DtEditorUpdateLineDisplay(editor, 1, False );
624
625    _DtAppUnlock(app);
626    return( error );
627 }
628
629
630 /***************************************************************************
631  *
632  * DtEditorSetContentsFromFile - read a data file, putting the contents 
633  *                                 into a DtEditor widget.
634  *
635  *      Inputs: widget to load the file into 
636  *
637  *              to indicate the type of contents loaded from the file:
638  *                string - a \0-terminated string of characters
639  *                data - untyped data
640  *
641  *              filename - name of the file to read 
642  *
643  *      Returns  0 - contents were loaded sucessfully
644  *              !0 - an error occured while loading the contents
645  *
646  ***************************************************************************/
647
648 extern DtEditorErrorCode
649 DtEditorSetContentsFromFile(
650         Widget                  widget,
651         char                    *fileName)
652 {
653   DtEditorErrorCode result;
654   _DtWidgetToAppContext(widget);
655   _DtAppLock(app);
656
657   result = LoadFile(widget, fileName, LOAD_DATA, 0, 0);
658
659   _DtAppUnlock(app);
660   return result;
661 }
662
663
664 /***************************************************************************
665  *
666  * DtEditorAppend - append data to the contents of the DtEditor widget.
667  *
668  *      Inputs: widget to add to the contents
669  *
670  *              a data structure containing the data to append to the
671  *              widget.  Depending upon the type of data being set, this
672  *              structure will contain various fields:
673  *
674  *              string - \0-terminated string of characters
675  *              data - the data, the size of the data
676  *
677  *      Returns  0 - contents were set sucessfully
678  *              !0 - an error occured while setting the contents
679  *
680  ***************************************************************************/
681
682 extern DtEditorErrorCode
683 DtEditorAppend(
684         Widget                  widget,
685         DtEditorContentRec      *data )
686 {
687    DtEditorErrorCode    error = DtEDITOR_INVALID_TYPE;
688    DtEditorWidget editor = (DtEditorWidget) widget;
689    _DtWidgetToAppContext(widget);
690    _DtAppLock(app);
691
692    switch( data->type ) 
693    {
694      case DtEDITOR_TEXT:
695      {
696        error = insertStringValue ( editor, data->value.string, 
697                                    APPEND_DATA, 0, 0 );
698        break;
699      }
700      case DtEDITOR_DATA:
701      {
702        error = insertDataValue ( editor, data->value.data.buf, 
703                                  data->value.data.length, APPEND_DATA, 0,0);
704        break;
705      }
706      case DtEDITOR_WCHAR:
707      {
708        error = insertWcharValue ( editor, data->value.wchar,
709                                    APPEND_DATA, 0, 0 );
710        break;
711      }
712      default:
713      {
714        error = DtEDITOR_INVALID_TYPE;
715      }
716    } /* end switch */
717
718    _DtAppUnlock(app);
719    return( error );
720 }
721
722
723 /***************************************************************************
724  *
725  * DtEditorAppendFromFile - read a data file, appending the contents 
726  *                                 into a DtEditor widget.
727  *
728  *      Inputs: widget to append the file to 
729  *
730  *              to indicate the type of contents appended from the file:
731  *                string - a \0-terminated string of characters
732  *                data - untyped data
733  *
734  *              filename - name of the file to read 
735  *
736  *      Returns  0 - contents were appended sucessfully
737  *              !0 - an error occured while appending the contents
738  *
739  ***************************************************************************/
740
741 extern DtEditorErrorCode
742 DtEditorAppendFromFile(
743         Widget                  widget,
744         char                    *fileName)
745 {
746   DtEditorErrorCode result;
747   _DtWidgetToAppContext(widget);
748   _DtAppLock(app);
749
750   result = LoadFile(widget, fileName, APPEND_DATA, 0, 0);
751
752   _DtAppUnlock(app);
753   return result;
754 }
755
756
757 /***************************************************************************
758  *
759  * DtEditorInsert - insert data into the contents of the DtEditor widget.
760  *
761  *      Inputs: widget to add to the contents
762  *
763  *              a data structure containing the data to insert into the
764  *              widget.  Depending upon the type of data being set, this
765  *              structure will contain various fields:
766  *
767  *              string - \0-terminated string of characters
768  *              data - the data, the size of the data
769  *
770  *      Returns  0 - contents were set sucessfully
771  *              !0 - an error occured while setting the contents
772  *
773  ***************************************************************************/
774
775 extern DtEditorErrorCode
776 DtEditorInsert(
777         Widget                  widget,
778         DtEditorContentRec      *data )
779 {
780    DtEditorErrorCode    error = DtEDITOR_INVALID_TYPE;
781    DtEditorWidget editor = (DtEditorWidget) widget;
782    _DtWidgetToAppContext(widget);
783    _DtAppLock(app);
784
785    switch( data->type ) 
786    {
787      case DtEDITOR_TEXT:
788      {
789        error = insertStringValue ( editor, data->value.string, 
790                                    INSERT_DATA, 0, 0 );
791        break;
792      }
793      case DtEDITOR_DATA:
794      {
795        error = insertDataValue ( editor, data->value.data.buf, 
796                                  data->value.data.length, INSERT_DATA, 0,0);
797        break;
798      }
799      case DtEDITOR_WCHAR:
800      {
801        error = insertWcharValue ( editor, data->value.wchar,
802                                    INSERT_DATA, 0, 0 );
803        break;
804      }
805      default :
806      {
807        error = DtEDITOR_INVALID_TYPE;
808      }
809    } /* end switch */
810
811    _DtAppUnlock(app);
812    return( error );
813 }
814
815
816 /***************************************************************************
817  *
818  * DtEditorInsertFromFile - read a data file, inserting the contents 
819  *                                 into a DtEditor widget.
820  *
821  *      Inputs: widget to insert the file to 
822  *
823  *              to indicate the type of contents inserted from the file:
824  *                string - a \0-terminated string of characters
825  *                data - untyped data
826  *
827  *              filename - name of the file to read 
828  *
829  *      Returns  0 - contents were inserted sucessfully
830  *              !0 - an error occured while inserting the contents
831  *
832  ***************************************************************************/
833
834 extern DtEditorErrorCode
835 DtEditorInsertFromFile(
836         Widget                  widget,
837         char                    *fileName)
838 {
839    DtEditorErrorCode result;
840    _DtWidgetToAppContext(widget);
841    _DtAppLock(app);
842
843    result = LoadFile(widget, fileName, INSERT_DATA, 0, 0);
844
845    _DtAppUnlock(app);
846    return result;
847 }
848
849
850 /***************************************************************************
851  *
852  * DtEditorReplace - replace a specified portion of the contents of the 
853  *                   DtEditor widget with the supplied data.
854  *
855  *      Inputs: widget to replace a portion of its contents
856  *
857  *              starting character position of the portion to replace
858  *
859  *              ending character position of the portion to replace
860  *
861  *              a data structure containing the data to replace some data 
862  *              in the widget.  Depending upon the type of data being set, 
863  *              this structure will contain various fields:
864  *
865  *              string - \0-terminated string of characters
866  *              data - the data, the size of the data
867  *
868  *
869  *      Returns  0 - the portion was replaced sucessfully
870  *              !0 - an error occured while replacing the portion 
871  *
872  ***************************************************************************/
873
874 extern DtEditorErrorCode
875 DtEditorReplace(
876         Widget                  widget,
877         XmTextPosition          startPos,
878         XmTextPosition          endPos,
879         DtEditorContentRec      *data)
880 {
881    DtEditorErrorCode    error = DtEDITOR_INVALID_TYPE;
882    DtEditorWidget editor = (DtEditorWidget) widget;
883    XmTextWidget tw;
884    _DtWidgetToAppContext(widget);
885    _DtAppLock(app);
886
887    tw = (XmTextWidget) M_text(editor);
888
889    if( startPos < 0 )
890         startPos = 0;
891    if( startPos > tw->text.last_position )
892         startPos = tw->text.last_position;
893
894    if( endPos < 0 )
895         endPos = 0;
896    if( endPos > tw->text.last_position )
897         endPos = tw->text.last_position;
898
899    if( startPos > endPos )
900    {
901         error = DtEDITOR_INVALID_RANGE;
902    }
903    else
904    {
905      switch( data->type ) 
906      {
907        case DtEDITOR_TEXT:
908        {
909          error = insertStringValue ( editor, data->value.string, 
910                                      REPLACE_DATA, startPos, endPos );
911          break;
912        }
913        case DtEDITOR_DATA:
914        {
915          error = insertDataValue ( editor, data->value.data.buf, 
916                                    data->value.data.length, REPLACE_DATA, 
917                                    startPos, endPos );
918          break;
919        }
920        case DtEDITOR_WCHAR:
921        {
922          error = insertWcharValue ( editor, data->value.wchar,
923                                     REPLACE_DATA, startPos, endPos );
924          break;
925        }
926        default :
927        {
928          error = DtEDITOR_INVALID_TYPE;
929        }
930      } /* end switch */
931
932    }
933
934    _DtAppUnlock(app);
935    return( error );
936 }
937
938
939 /***************************************************************************
940  *
941  * DtEditorReplaceFromFile - read a data file, using the contents to replace
942  *                           a specified portion of the contntes of a 
943  *                           DtEditor widget.
944  *
945  *      Inputs: widget to insert the file to 
946  *
947  *              starting character position of the portion to replace
948  *
949  *              ending character position of the portion to replace
950  *
951  *              to indicate the type of contents inserted from the file:
952  *                string - a \0-terminated string of characters
953  *                data - untyped data
954  *
955  *              filename - local name of the file to read 
956  *
957  *      Returns  0 - contents were inserted sucessfully
958  *              !0 - an error occured while inserting the contents
959  *
960  ***************************************************************************/
961
962 extern DtEditorErrorCode
963 DtEditorReplaceFromFile(
964         Widget                  widget,
965         XmTextPosition          startPos,
966         XmTextPosition          endPos,
967         char                    *fileName)
968 {
969
970    DtEditorWidget editor = (DtEditorWidget) widget;
971    XmTextWidget tw;
972    DtEditorErrorCode result;
973    _DtWidgetToAppContext(widget);
974    _DtAppLock(app);
975
976    tw = (XmTextWidget) M_text(editor);
977
978    if( startPos < 0)
979         startPos = 0;
980    if( startPos > tw->text.last_position )
981         startPos = tw->text.last_position;
982
983    if( endPos < 0 )
984         endPos = 0;
985    if( endPos > tw->text.last_position )
986         endPos = tw->text.last_position;
987
988    if(startPos > endPos)
989    {
990         result = DtEDITOR_INVALID_RANGE;
991    }
992    else
993    {
994         result = LoadFile(widget, fileName, REPLACE_DATA, startPos, endPos);
995    }
996
997    _DtAppUnlock(app);
998    return result;
999 }
1000
1001
1002 /***************************************************************************
1003  *
1004  * _DtEditorValidateFileAccess - check to see if file exists, whether we
1005  *                                 can get to it, and whether it is readable
1006  *                                 or writable.
1007  *
1008  *      Note: does not check whether files for reading are read only.
1009  *
1010  *      Inputs: filename - name of the local file to read 
1011  *                         flag indicating whether we want to read or write
1012  *                         the file.
1013  *              
1014  *      Returns  0  file exists & we have read or write permissions.
1015  *
1016  *              >0  if file cannot be read from/written to.
1017  *                  errno is set to one of the following values:
1018  *
1019  *      General errors: 
1020  *              DtEDITOR_INVALID_FILENAME - 0 length filename
1021  *              DtEDITOR_NONEXISTENT_FILE - file does not exist
1022  *                (Note: this may not be considered an error when saving 
1023  *                 to a file.  The file may just need to be created.)
1024  *              DtEDITOR_NO_FILE_ACCESS - cannot stat existing file 
1025  *              DtEDITOR_DIRECTORY - file is a directory
1026  *              DtEDITOR_CHAR_SPECIAL_FILE - file is a device special file
1027  *              DtEDITOR_BLOCK_MODE_FILE - file is a block mode file
1028  *
1029  *      additional READ_ACCESS errors:  
1030  *              DtEDITOR_UNREADABLE_FILE - 
1031  *
1032  *      additional WRITE_ACCESS errors: 
1033  *              DtEDITOR_UNWRITABLE_FILE - 
1034  *                              file or directory is write protected for 
1035  *                              another reason
1036  *
1037  ***************************************************************************/
1038
1039 extern DtEditorErrorCode
1040 _DtEditorValidateFileAccess(
1041         char    *fileName,
1042         int     accessType )
1043 {
1044    struct stat statbuf; /* Information on a file. */
1045
1046    DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
1047
1048    /*
1049     * First, make sure we were given a name 
1050     */
1051    if (fileName && *fileName ) 
1052    { 
1053
1054      /*
1055       * Does the file already exist?
1056       */
1057      if ( access(fileName, F_OK) != 0 )
1058         error = DtEDITOR_NONEXISTENT_FILE; 
1059      else
1060      { 
1061         error = DtEDITOR_NO_ERRORS;
1062
1063         /*
1064          * The file exists, so lets do some type checking 
1065          */
1066
1067         if( stat(fileName, &statbuf) != 0 ) 
1068           error = DtEDITOR_NO_FILE_ACCESS;
1069         else
1070         { 
1071
1072           /* if its a directory - can't save */ 
1073           if( (statbuf.st_mode & S_IFMT) == S_IFDIR ) 
1074           { 
1075              error = DtEDITOR_DIRECTORY;
1076              return( error );
1077           }
1078
1079           /* if its a character special device - can't save */
1080           if( (statbuf.st_mode & S_IFMT) == S_IFCHR ) 
1081           {
1082              error = DtEDITOR_CHAR_SPECIAL_FILE;
1083              return( error );
1084           }
1085
1086           /* if its a block mode device - can't save */
1087           if((statbuf.st_mode & S_IFMT) == S_IFBLK) 
1088           {
1089              error = DtEDITOR_BLOCK_MODE_FILE;
1090              return( error );
1091           }
1092
1093           /* 
1094            * We now know that it's a regular file so check to whether we
1095            * can read or write to it, as appropriate.  
1096            */
1097
1098           switch( accessType )
1099           {
1100             case READ_ACCESS:
1101             {
1102
1103               if( access(fileName, R_OK) != 0 ) 
1104                  error = DtEDITOR_UNREADABLE_FILE;
1105
1106               break;
1107             }
1108
1109             case WRITE_ACCESS:
1110             {
1111
1112               if( access(fileName, W_OK) == 0 )
1113               {
1114                 /*
1115                  * Can write to it. 
1116                  */
1117                 error = DtEDITOR_WRITABLE_FILE;
1118               }
1119               else
1120               {
1121                 /*
1122                  * Can't write to it.  
1123                  */
1124                  error = DtEDITOR_UNWRITABLE_FILE;
1125
1126               } /* end no write permission */ 
1127
1128               break;
1129             }
1130
1131             default:
1132             {
1133               break;
1134             }
1135
1136           } /* end switch */
1137         } /* end stat suceeded */
1138
1139       } /* end file exists */
1140
1141    } /* end filename passed in */
1142
1143    return( error );
1144
1145 } /* end _DtEditorValidateFileAccess */
1146
1147
1148 /************************************************************************
1149  *
1150  *  LoadFile - Check if file exists, whether we can get to it, etc.  
1151  *             If so, type and read its contents.
1152  *
1153  *      Inputs: widget to set, add, or insert contents of file into
1154  *
1155  *              name of file to read
1156  *
1157  *              type of file (NULL). This will be set by LoadFile
1158  *
1159  *              action to perform with the data (load, append, insert, 
1160  *                replace a portion, attach)
1161  *
1162  *              The following information will be used if the file
1163  *                contents will replace a portion of the widget's contents:
1164  *
1165  *              starting character position of the portion to replace
1166  *
1167  *              ending character position of the portion to replace
1168  *
1169  *      Returns: DtEDITOR_NO_ERRORS - file was read sucessfully
1170  *               DtEDITOR_READ_ONLY_FILE - file was read sucessfully but 
1171  *                                           is read only
1172  *               DtEDITOR_DIRECTORY - the file is a directory
1173  *               DtEDITOR_CHAR_SPECIAL_FILE - the file is a character
1174  *                                         special device
1175  *               DtEDITOR_BLOCK_MODE_FILE - the file is a block mode device
1176  *               DtEDITOR_NONEXISTENT_FILE - file does not exist
1177  *               DtEDITOR_NULLS_REMOVED - file contained embedded NULLs
1178  *                                        that were removed
1179  *               DtEDITOR_INSUFFICIENT_MEMORY - unable to allocate
1180  *                                        enough memory for contents of file
1181  *
1182  ************************************************************************/
1183
1184 static DtEditorErrorCode 
1185 LoadFile(
1186         Widget           w,
1187         char             *fileName,
1188         LoadActionType   action,
1189         XmTextPosition   startReplace,
1190         XmTextPosition   endReplace )
1191
1192 {
1193     DtEditorContentRec cr;      /* Structure for passing data to widget */
1194     struct stat statbuf;        /* Information on a file. */
1195     int file_length;            /* Length of file. */
1196     FILE *fp = NULL;            /* Pointer to open file */
1197     DtEditorErrorCode returnVal = DtEDITOR_NONEXISTENT_FILE; 
1198                                 /* Error accessing file & reading contents */
1199     DtEditorErrorCode loadError=DtEDITOR_NO_ERRORS;
1200                                 /* Error from placing bits into text widget */
1201
1202     /*
1203      * First, make sure we were given a name 
1204      */
1205
1206     if (fileName && *fileName ) 
1207     { 
1208
1209       /*
1210        * Can we read the file?
1211        */
1212       returnVal = _DtEditorValidateFileAccess( fileName, READ_ACCESS );
1213
1214       if( returnVal == DtEDITOR_NO_ERRORS )
1215       {
1216         /*
1217          * Open the file for reading.  If we can read/write, then we're
1218          * cool, otherwise we might need to tell the user that the
1219          * file's read-only, or that we can't even read from it.
1220          */
1221         if( (fp = fopen(fileName, "r+")) == NULL ) 
1222         {
1223           /*
1224            * We can't update (read/write) the file so try opening read-
1225            * only
1226            */
1227           if( (fp = fopen(fileName, "r")) == NULL )
1228           {
1229             /*
1230              * We can't read from the file.
1231              */
1232             return ( DtEDITOR_UNREADABLE_FILE );
1233           }
1234           else
1235           {
1236             /*
1237              * Tell the application that the file's read-only.
1238              * Becareful not to overwrite this value with one of the calls 
1239              * to set the widget's contents.
1240              */
1241             returnVal = DtEDITOR_READ_ONLY_FILE;
1242           }
1243
1244         } /* end open for read/write */
1245
1246       } /* end try to read the file */ 
1247
1248
1249     } /* end if no filename */
1250
1251     /* If a file is open, get the bytes */
1252     if ( fp )
1253     {
1254
1255         stat( fileName, &statbuf );
1256         file_length = statbuf.st_size;
1257
1258         /* 
1259          * Check to see if we have enough memory to load the file contents
1260          * into the text widget.  This is only an estimate of our needs.
1261          * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS, 
1262          * DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY.
1263          */
1264         loadError = Check4EnoughMemory( file_length );
1265         if (loadError == DtEDITOR_INSUFFICIENT_MEMORY)
1266           returnVal = loadError;
1267         else {
1268
1269           /* 
1270            * Read the file contents (with room for null) & convert to a
1271            * string.  We want to use a string because the 
1272            * DtEditorSetContents/Append/Insert/... functions create another 
1273            * copy of the data before actually putting it into the widget.
1274            */
1275           char *file_string = (char*) XtMalloc(file_length + 1);
1276           file_length = fread(file_string, sizeof(char), file_length, fp);
1277           file_string[file_length] = '\0';
1278
1279           /* 
1280            * Strip out any embedded NULLs because the text widget will only 
1281            * accept data up to the first NULL.
1282            *
1283            * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or 
1284            * DtEDITOR_NULLS_REMOVED
1285            */
1286           loadError = StripEmbeddedNulls( file_string, &file_length );
1287           if ( loadError != DtEDITOR_NO_ERRORS )
1288             returnVal = loadError;
1289
1290           /* 
1291            * Insert it as a string, otherwise the following DtEditor*()
1292            * functions will make another copy of the data.
1293            */
1294           cr.type = DtEDITOR_TEXT;
1295           cr.value.string = file_string;
1296
1297
1298           /*
1299            * Load, insert, append, or attach the file, as specified
1300            */
1301           switch( action )
1302           {
1303               case LOAD_DATA:
1304               {
1305                 loadError = DtEditorSetContents ( w, &cr );
1306                 break;
1307               }
1308
1309               case INSERT_DATA:
1310               {
1311                 loadError = DtEditorInsert ( w, &cr );
1312                 break;
1313               }
1314
1315               case APPEND_DATA:
1316               {
1317                 loadError = DtEditorAppend ( w, &cr );
1318                 break;
1319               }
1320
1321               case REPLACE_DATA:
1322               {
1323                 loadError = DtEditorReplace(w, startReplace, endReplace, &cr);
1324                 break;
1325               }
1326
1327               default:
1328               {
1329               }
1330           } /* end switch */
1331             
1332           if ( loadError != DtEDITOR_NO_ERRORS )
1333             returnVal = loadError;
1334
1335           /*
1336            * The file is loaded, clean up.
1337            */
1338           XtFree( file_string );
1339
1340         } /* end there is enough memory */
1341  
1342         /* Close the file */
1343         fclose(fp);
1344
1345     } /* end if a file is open */
1346
1347     return( returnVal );
1348
1349 } /* end LoadFile */
1350
1351
1352 static char *
1353 StringAdd(
1354         char *destination,
1355         char *source,
1356         int number)
1357 {
1358     memcpy(destination, source, number);
1359     destination[number] = (char)'\0';
1360     destination += number;
1361     return destination;
1362 }
1363
1364 /***************************************************************************
1365  *
1366  * CopySubstring - copies out a portion of the text, optionally
1367  *                 adding newlines at any and all wordwrap-caused
1368  *                 "virtual" lines.
1369  *
1370  *      Inputs: widget from which we get the data to write;
1371  *              startPos determines the first character to write out;
1372  *              endPos determines the last character to write out;
1373  *              buf is the character buffer into which we write. It
1374  *                  is assumed to be large enough - be careful.
1375  *              addNewlines specifies whether to add '/n' to "virtual" lines.
1376  *      Returns Nuthin'
1377  *
1378  *
1379  ***************************************************************************/
1380
1381 static char *
1382 CopySubstring(
1383         XmTextWidget widget,
1384         XmTextPosition startPos,
1385         XmTextPosition endPos,
1386         char *buf,
1387         Boolean addNewlines)
1388 {
1389     register XmTextLineTable line_table = widget->text.line_table;
1390     int currLine, firstLine;
1391     char *pString, *pCurrChar, *pLastChar;
1392     int numToCopy;
1393
1394     if(startPos < 0)
1395         startPos = 0;
1396     if(startPos > widget->text.last_position)
1397         startPos = widget->text.last_position;
1398
1399     if(endPos < 0)
1400         endPos = 0;
1401     if(endPos > widget->text.last_position)
1402         endPos = widget->text.last_position;
1403
1404     if(startPos > endPos)
1405         return buf;
1406
1407     pString = XmTextGetString((Widget)widget);
1408
1409     if(addNewlines == False)
1410     {
1411         pCurrChar = _DtEditorGetPointer(pString, startPos);
1412         pLastChar = _DtEditorGetPointer(pString, endPos);
1413         numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1414
1415         buf = StringAdd(buf, pCurrChar, numToCopy);
1416     }
1417     else
1418     {
1419         int *mb_str_loc, total, z, siz;
1420         char *bptr;
1421
1422         mb_str_loc = (int *) XtMalloc(sizeof(int) * ((endPos-startPos)+1));
1423         if (NULL == mb_str_loc)
1424         {
1425             /* Should figure out some way to pass back an error code. */
1426             buf = CopySubstring(widget, startPos, endPos, buf, False);
1427             return buf;
1428         }
1429
1430     /*
1431      * mb_str_loc[] is being used to replace the call
1432      * to _DtEditorGetPointer.  That function used
1433      * mbtowc() to count the number of chars between the
1434      * beginning of pString and startChar.  The problem
1435      * was that it sat in a loop and was also called for
1436      * every line, so it was SLOW.  Now, we count once
1437      * and store the results in mb_str_loc[].
1438      */
1439
1440         /* Because startPos may not always == 0:        */
1441         /*     mb_str_loc[0] = startPos                 */
1442         /*     mb_str_loc[endPos - startPos] = endPos   */
1443         /*                                              */
1444         /* So when accessing items, dereference off of  */
1445         /*  startPos.                                   */
1446
1447         mb_str_loc[0] = 0;
1448         for(total=0, bptr=pString, z=1;
1449                 z <= (endPos - startPos); bptr += siz, z++)
1450         {
1451            if (MB_CUR_MAX > 1)
1452            {
1453               if ( (siz = mblen(bptr, MB_CUR_MAX)) < 0)
1454               {
1455                 siz = 1;
1456                 total += 1;
1457               }
1458               else
1459                 total += siz;
1460            }
1461            else
1462            {
1463                 siz = 1;
1464                 total += 1;
1465            }
1466
1467              mb_str_loc[z] = total;
1468         }
1469
1470
1471         firstLine = currLine = _DtEditorGetLineIndex(widget, startPos);
1472         do
1473         {
1474             if(startPos > (XmTextPosition)line_table[currLine].start_pos)
1475                 pCurrChar = pString + mb_str_loc[0];
1476             else
1477             {
1478                 z = line_table[currLine].start_pos;
1479                 pCurrChar = pString +
1480                         mb_str_loc[z - startPos];
1481             }
1482
1483             if(addNewlines == True && currLine > firstLine &&
1484                line_table[currLine].virt_line != 0)
1485             {
1486                 buf[0] = (char)'\n';
1487                 buf[1] = (char)'\0';
1488                 buf++;
1489             }
1490
1491             if(currLine >= (widget->text.total_lines - 1))
1492                   pLastChar = pString +
1493                         mb_str_loc[endPos - startPos];
1494             else if((XmTextPosition)line_table[currLine + 1].start_pos <= endPos)
1495             {
1496                   z = line_table[currLine+1].start_pos - 1;
1497                   pLastChar = pString +
1498                         mb_str_loc[z - startPos];
1499             }
1500             else
1501                   pLastChar = pString +
1502                         mb_str_loc[endPos - startPos];
1503
1504             numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1505
1506             buf = StringAdd(buf, pCurrChar, numToCopy);
1507
1508             currLine++;
1509         } while(currLine < widget->text.total_lines &&
1510                 (XmTextPosition)line_table[currLine].start_pos <= endPos);
1511         XtFree((char*)mb_str_loc);
1512     }
1513
1514     XtFree(pString);
1515     return buf;
1516 }
1517
1518 /*************************************************************************
1519  *
1520  * _DtEditorCopyDataOut - Writes the entire text editor buffer contents to 
1521  *                        the specified character array.
1522  *
1523  * Inputs: tw, to supply the data.
1524  *         buf, specifying the array to which to write the data.
1525  *
1526  *************************************************************************/
1527
1528 static char *
1529 _DtEditorCopyDataOut(
1530         XmTextWidget tw,
1531         char *buf,
1532         Boolean addNewlines)
1533 {
1534     buf = CopySubstring(tw, 0, tw->text.last_position, buf, addNewlines);
1535
1536     return buf;
1537 }
1538
1539 static DtEditorErrorCode
1540 getStringValue(
1541         DtEditorWidget  editor,
1542         char            **buf,
1543         Boolean         insertNewlines)
1544 {
1545     XmTextWidget         tw = (XmTextWidget) M_text(editor);
1546     int                  bufSize;
1547     char                *lastChar;
1548     DtEditorErrorCode    returnVal = DtEDITOR_NO_ERRORS;
1549
1550     /*
1551      * Calculate the size of the buffer we need for the data.
1552      * 1. Start with MB_CUR_MAX for each char in the text.
1553      * 3. Add in 1 char for each line, if we have to insert newlines.
1554      * 4. Add 1 for a terminating NULL.
1555      */
1556     bufSize = tw->text.last_position * MB_CUR_MAX;
1557     if(insertNewlines == True)
1558         bufSize += tw->text.total_lines;
1559     bufSize += 1;
1560
1561     returnVal = Check4EnoughMemory(bufSize);
1562     if (DtEDITOR_NO_ERRORS != returnVal) return returnVal;
1563
1564     *buf = (char *) XtMalloc(bufSize);
1565     lastChar = _DtEditorCopyDataOut(tw, *buf, insertNewlines);
1566
1567     return returnVal;
1568 } /* end getStringValue */
1569
1570
1571 static DtEditorErrorCode
1572 getDataValue(
1573         DtEditorWidget  editor,
1574         void            **buf,
1575         unsigned int    *size,
1576         Boolean         insertNewlines)
1577 {
1578     DtEditorErrorCode error;
1579
1580     error = getStringValue(editor, (char **)buf, insertNewlines);
1581     *size = strlen( *buf ); /* remember, strlen doesn't count \0 at end */
1582
1583     return( error );
1584
1585 } /* end getDataValue */
1586
1587
1588 static DtEditorErrorCode
1589 getWcharValue(
1590         DtEditorWidget  editor,
1591         wchar_t         **data,
1592         Boolean         insertNewlines)
1593 {
1594     DtEditorErrorCode error;
1595     char *mb_value;
1596     wchar_t *pWchar_value;
1597     int num_char, result;
1598     size_t nbytes;
1599
1600     error = getStringValue(editor, &mb_value, insertNewlines);
1601
1602     if (error == DtEDITOR_NO_ERRORS)
1603     {
1604        /*
1605         * Allocate space for the wide character string
1606         */
1607        num_char = _DtEditor_CountCharacters(mb_value, strlen(mb_value)) + 1;
1608        nbytes = (size_t) num_char * sizeof(wchar_t);
1609
1610        error = Check4EnoughMemory(nbytes);
1611        if (DtEDITOR_NO_ERRORS != error) return error;
1612        pWchar_value = (wchar_t*) XtMalloc(nbytes);
1613
1614        /*
1615         * Convert the multi-byte string to wide character
1616         */
1617        result = mbstowcs(pWchar_value, mb_value, num_char*sizeof(wchar_t) );
1618        if (result < 0) pWchar_value[0] = 0L;
1619        *data = pWchar_value;
1620
1621        XtFree( mb_value );
1622     }
1623
1624     return( error );
1625 } /* end getWcharValue */
1626
1627
1628 /***************************************************************************
1629  *
1630  * DtEditorGetContents - gets the contents of the DtEditor widget.
1631  *
1632  *      Inputs: widget to retrieve the contents
1633  *
1634  *              pointer to a data structure indicating how the retrieved 
1635  *              data should be formatted.  Depending upon the type of format, 
1636  *              this structure will contain various fields:
1637  *                string - a NULL pointer (char *) to hold the data
1638  *                            a new container will be created.
1639  *                data - void pointer to hold the data, unsigned int for the
1640  *                       size of the data,
1641  *              a Boolean indicating whether Newline characters should be
1642  *              inserted at the end of each line, in string format.
1643  *              a Boolean indicating whether the the unsaved changes
1644  *                flag should be cleared.  There may be times when an 
1645  *                application will want to request a copy of the contents 
1646  *                without effecting whether DtEditorCheckForUnsavedChanges 
1647  *                reports there are unsaved changes.
1648  *
1649  *      Returns  0 - contents were retrieved sucessfully
1650  *              !0 - an error occured while retrieving the contents
1651  *
1652  *              The structure passed in will be set according to the
1653  *                requested format:
1654  *              string - a \0-terminated string of characters with
1655  *                       optional Newlines
1656  *              container - handle to a Bento container
1657  *              data - the data, the size of the data
1658  *
1659  *              The application is responsible for free'ing any data in the
1660  *              above structure.
1661  *
1662  ***************************************************************************/
1663
1664 extern DtEditorErrorCode
1665 DtEditorGetContents(
1666         Widget                  widget,
1667         DtEditorContentRec      *data,
1668         Boolean                 hardCarriageReturns,
1669         Boolean                 markContentsAsSaved )
1670 {
1671    DtEditorErrorCode    error = DtEDITOR_INVALID_TYPE;
1672    DtEditorWidget editor = (DtEditorWidget) widget;
1673    _DtWidgetToAppContext(widget);
1674    _DtAppLock(app);
1675
1676    switch( data->type ) 
1677    {
1678      case DtEDITOR_TEXT:
1679      {
1680        error = getStringValue( editor, &(data->value.string), 
1681                                hardCarriageReturns );
1682        break;
1683      }
1684
1685      case DtEDITOR_DATA:
1686      {
1687        error = getDataValue( editor, &(data->value.data.buf),
1688                              &(data->value.data.length), 
1689                              hardCarriageReturns );
1690        break;
1691      }
1692
1693      case DtEDITOR_WCHAR:
1694      {
1695        error = getWcharValue( editor, &(data->value.wchar),
1696                               hardCarriageReturns );
1697        break;
1698      }
1699
1700      default :
1701      {
1702        error = DtEDITOR_INVALID_TYPE;
1703      }
1704    } /* end switch */
1705
1706    /*
1707     * If there were no errors, mark there are now no unsaved changes (unless
1708     * we were told not to).
1709     */
1710    if ( error == DtEDITOR_NO_ERRORS && markContentsAsSaved == True )
1711      M_unreadChanges( editor ) = False;
1712
1713    _DtAppUnlock(app);
1714    return( error );
1715 }
1716
1717
1718 /***************************************************************************
1719  *
1720  * DtEditorSaveContentsToFile - saves the contents of the DtEditor 
1721  *                                widget to a disc file as string/data
1722  *                                or a CDE Document (Bento container).
1723  *
1724  *      Inputs: widget to retrieve the contents
1725  *
1726  *              filename - name of the file to read 
1727  *              a Boolean indicating whether the file should be
1728  *                 overwritten if it currently exists.
1729  *              a Boolean indicating whether Newline characters should be
1730  *                inserted at the end of each line (string format only).
1731  *              a Boolean indicating whether the the unsaved changes
1732  *                flag should be cleared.  There may be times when an 
1733  *                application will want to request a copy of the contents 
1734  *                without effecting whether DtEditorCheckForUnsavedChanges 
1735  *                reports there are unsaved changes.
1736  *              
1737  *      Returns  DtEDITOR_NO_ERRORS - contents were saved sucessfully
1738  *               DtEDITOR_UNWRITABLE_FILE - file is write protected
1739  *               DtEDITOR_WRITABLE_FILE - file exists and the 
1740  *                 overwriteIfExists parameter is False.
1741  *               DtEDITOR_SAVE_FAILED - write to the file failed; check 
1742  *                                        disk space, etc.
1743  *               OR any errors from DtEditorGetContents 
1744  *
1745  ***************************************************************************/
1746
1747 extern DtEditorErrorCode
1748 DtEditorSaveContentsToFile(
1749         Widget                  widget,
1750         char                    *fileName,
1751         Boolean                 overwriteIfExists,
1752         Boolean                 hardCarriageReturns,
1753         Boolean                 markContentsAsSaved )
1754 {
1755
1756    FILE *pFile;
1757    DtEditorContentRec cr;  /* Structure for retrieving contents of widget */
1758    DtEditorWidget editor = (DtEditorWidget) widget;
1759
1760    DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME; 
1761    _DtWidgetToAppContext(widget);
1762    _DtAppLock(app);
1763
1764    /*
1765     * First, make sure we were given a name 
1766     */
1767
1768    if (fileName && *fileName ) 
1769    { 
1770  
1771       /*
1772        * Can we save to the file?
1773        */
1774       error = _DtEditorValidateFileAccess( fileName, WRITE_ACCESS );
1775
1776       if( error == DtEDITOR_NO_ERRORS || 
1777           error == DtEDITOR_NONEXISTENT_FILE ||
1778           error == DtEDITOR_WRITABLE_FILE )
1779       {
1780         /*
1781          * Don't overwrite an existing file if we've been told not to
1782          */
1783         if( error == DtEDITOR_WRITABLE_FILE && overwriteIfExists == False )
1784         {
1785            _DtAppUnlock(app);
1786            return( error );
1787         }
1788
1789         /*
1790          * Open the file for writing
1791          */
1792         if ( (pFile = fopen(fileName, "w")) == NULL ) 
1793           {
1794             _DtAppUnlock(app);
1795             return( DtEDITOR_UNWRITABLE_FILE );
1796           }
1797         else
1798         {
1799
1800           /* 
1801            * Save the unsaved changes flag so we can restore it if the write
1802            * to the file fails.
1803            */
1804           Boolean saved_state =  M_unreadChanges( editor );
1805
1806           /*
1807            * Now, get the contents of the widget and write it to the file,
1808            * depending upon the format requested.
1809            */
1810
1811           cr.type = DtEDITOR_DATA;
1812           error = DtEditorGetContents( widget, &cr, hardCarriageReturns,
1813                                        markContentsAsSaved );
1814
1815           if ( error == DtEDITOR_NO_ERRORS )
1816           {
1817
1818             /*
1819              * Write it to the file
1820              */
1821             size_t size_written = fwrite( cr.value.data.buf, 1, 
1822                                   cr.value.data.length, pFile );
1823
1824             if( cr.value.data.length != size_written ) 
1825               error = DtEDITOR_SAVE_FAILED;
1826
1827             XtFree( cr.value.data.buf );
1828           }
1829
1830           fclose(pFile);
1831
1832           if( error == DtEDITOR_SAVE_FAILED )
1833           {
1834             /* 
1835              * Restore the unsaved changes flag since the save failed
1836              */
1837             M_unreadChanges( editor ) = saved_state;
1838           }
1839
1840         } /* end file is writable */
1841
1842       } /* end filename is valid */
1843
1844    } 
1845    _DtAppUnlock(app);
1846    return( error );
1847
1848 } /* end DtEditorSaveContentsToFile */
1849
1850
1851 /*
1852  * _DtEditorGetPointer returns a pointer to the _character_ 
1853  * numbered by startChar within the string pString. 
1854  * It accounts for possible multibyte chars.
1855  */
1856 char *
1857 _DtEditorGetPointer(
1858         char *pString,
1859         int startChar)
1860 {
1861     char *bptr;
1862     int curChar, char_size;
1863
1864     if(MB_CUR_MAX > 1)
1865     {
1866         for(bptr = pString, curChar = 0;
1867             curChar < startChar && *bptr != (char)'\0';
1868             curChar++, bptr += char_size)
1869         {
1870             if ( (char_size = mblen(bptr, MB_CUR_MAX)) < 0)
1871                 char_size = 1;
1872         }
1873     }
1874     else
1875     {
1876         bptr = pString + startChar;
1877     }
1878     return bptr;
1879 } /* end _DtEditorGetPointer */
1880