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