2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $TOG: EditAreaData.c /main/6 1998/03/03 16:18:13 mgreess $ */
24 /**********************************<+>*************************************
25 ***************************************************************************
27 ** File: EditAreaData.c
29 ** Project: DtEditor widget for editing services
31 ** Description: Contains functions for getting and setting the data
32 ** on which the editor operates.
35 *******************************************************************
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.
46 ********************************************************************
49 **************************************************************************
50 **********************************<+>*************************************/
52 #include <X11/Xutil.h>
55 #include "DtWidgetI.h"
58 typedef enum _LoadActionType {
65 static DtEditorErrorCode Check4EnoughMemory(
68 static DtEditorErrorCode StripEmbeddedNulls(
72 static DtEditorErrorCode LoadFile(
75 LoadActionType action,
76 XmTextPosition startReplace,
77 XmTextPosition endReplace );
79 #ifdef NEED_STRCASECMP
82 * in case strcasecmp is not provided by the system here is one
83 * which does the trick
87 register char *s1, *s2;
92 c1 = isupper(*s1) ? tolower(*s1) : *s1;
93 c2 = isupper(*s2) ? tolower(*s2) : *s2;
106 /*****************************************************************************
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.
113 * Returns DtEDITOR_NO_ERRORS
114 * DtEDITOR_ILLEGAL_SIZE
115 * DtEDITOR_INSUFFICIENT_MEMORY
117 *****************************************************************************/
118 static DtEditorErrorCode
123 DtEditorErrorCode returnVal = DtEDITOR_ILLEGAL_SIZE;
126 char *tmpString = (char *)malloc((2 * numBytes) + (numBytes/10));
128 if(tmpString == (char *)NULL)
129 returnVal = DtEDITOR_INSUFFICIENT_MEMORY;
131 returnVal = DtEDITOR_NO_ERRORS;
138 } /* end Check4EnoughMemory */
141 /*****************************************************************************
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
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.
152 *****************************************************************************/
153 static DtEditorErrorCode
158 DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
160 if (strlen(stringData) != *length)
164 returnVal = DtEDITOR_NULLS_REMOVED;
167 * The file contains NULL characters, so we strip them out and
168 * report that we have done so.
170 while((firstNull = strlen(stringData)) != *length)
172 int lastNull = firstNull;
174 while((lastNull + 1) < *length &&
175 stringData[lastNull + 1] == (char)'\0')
178 memcpy(&stringData[firstNull], &stringData[lastNull + 1],
180 *length -= 1 + lastNull - firstNull;
187 } /* end StripEmbeddedNulls */
190 /*****************************************************************************
192 * Retrieves the current location of the insert cursor
194 *****************************************************************************/
197 DtEditorGetInsertionPosition(
200 DtEditorWidget editor = (DtEditorWidget) widget;
201 XmTextPosition result;
202 _DtWidgetToAppContext(widget);
205 result = XmTextGetInsertionPosition(M_text(editor));
212 /*****************************************************************************
214 * Retrieves the current location of the last character in the widget
216 *****************************************************************************/
219 DtEditorGetLastPosition(
222 DtEditorWidget editor = (DtEditorWidget) widget;
223 XmTextPosition result;
224 _DtWidgetToAppContext(widget);
227 result = XmTextGetLastPosition(M_text(editor));
234 /*****************************************************************************
236 * Changes the current location of the insert cursor
238 *****************************************************************************/
241 DtEditorSetInsertionPosition(
243 XmTextPosition position)
245 DtEditorWidget editor = (DtEditorWidget) widget;
246 _DtWidgetToAppContext(widget);
249 XmTextSetInsertionPosition(M_text(editor), position);
255 static DtEditorErrorCode
257 DtEditorWidget editor,
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.
265 M_loadingAllNewData(editor) = True;
267 XmTextSetString( M_text(editor), data );
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.
275 if (M_loadingAllNewData(editor) == True) {
276 M_loadingAllNewData(editor) = False;
277 M_unreadChanges(editor) = False;
278 _DtEditorResetUndo(editor);
281 return( DtEDITOR_NO_ERRORS );
283 } /* end setStringValue */
286 static DtEditorErrorCode
288 DtEditorWidget widget,
292 DtEditorErrorCode status = DtEDITOR_NULL_ITEM, tmpError;
297 if (rawData != (void *)NULL)
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
304 * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS,
305 * DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY.
308 status = Check4EnoughMemory( length );
309 if (status == DtEDITOR_NO_ERRORS)
313 * Convert the data buffer into a string & insert into the widget
315 char *textData = (char *)XtMalloc(length + 1);
316 memcpy( textData, rawData, length );
317 textData[length] = '\0';
320 * Strip out any embedded NULLs because the text widget will only
321 * accept data up to the first NULL.
323 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
324 * DtEDITOR_NULLS_REMOVED
326 status = StripEmbeddedNulls( textData, &length );
329 * Now, insert the converted string into the text widget
331 tmpError = setStringValue( widget, textData );
332 if (tmpError != DtEDITOR_NO_ERRORS)
335 XtFree( (char *)textData );
341 } /* end setDataValue */
344 static DtEditorErrorCode
346 DtEditorWidget editor,
349 DtEditorErrorCode status;
351 int result, num_chars=0;
352 char *mb_value = (char *)NULL;
356 * Convert the wide char string to a multi-byte string & stick it in
361 * Determine how big the resulting mb string may be
363 for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
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.
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 );
377 * Convert the wchar string
378 * If wcstombs fails it returns (size_t) -1, so pass in empty
381 result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
382 if (result == (size_t)-1)
386 * wcstombs doesn't guarantee string is NULL terminated
388 mb_value[result] = 0;
390 status = setStringValue( editor, mb_value );
396 } /* end setWcharValue */
399 static DtEditorErrorCode
401 DtEditorWidget editor,
403 LoadActionType typeOfInsert,
404 XmTextPosition beginInsert,
405 XmTextPosition endInsert)
409 switch( typeOfInsert )
413 beginInsert = endInsert = XmTextGetInsertionPosition( M_text(editor) );
419 beginInsert = endInsert = XmTextGetLastPosition( M_text(editor) );
434 * Insert/Replace/Append the data and move the insertion cursor to
435 * the end of the inserted data.
437 numInserted = _DtEditor_CountCharacters( data, strlen(data) );
438 XmTextReplace(M_text(editor), beginInsert, endInsert, data);
439 XmTextSetInsertionPosition( M_text(editor),
440 (XmTextPosition)(beginInsert + numInserted) );
442 return( DtEDITOR_NO_ERRORS );
443 } /* insertStringValue */
445 static DtEditorErrorCode
447 DtEditorWidget widget,
450 LoadActionType typeOfInsert,
451 XmTextPosition beginInsert,
452 XmTextPosition endInsert)
454 DtEditorErrorCode status = DtEDITOR_NULL_ITEM, loadError;
459 if (rawData != (void *) NULL)
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
466 * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or
467 * DtEDITOR_INSUFFICIENT_MEMORY.
469 status = Check4EnoughMemory( length );
470 if (status == DtEDITOR_NO_ERRORS)
474 * Convert the data buffer into a string & insert into the widget
476 char *textData = (char *)XtMalloc(length + 1);
477 memcpy( textData, rawData, length );
478 textData[length] = '\0';
481 * Strip out any embedded NULLs because the text widget will only
482 * accept data up to the first NULL.
484 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
485 * DtEDITOR_NULLS_REMOVED
487 status = StripEmbeddedNulls( textData, &length );
490 * Now, insert the converted string into the text widget
492 loadError = insertStringValue( widget, textData, typeOfInsert,
493 beginInsert, endInsert );
494 if (loadError != DtEDITOR_NO_ERRORS)
497 XtFree( (char *)textData );
504 } /* insertDataValue */
507 static DtEditorErrorCode
509 DtEditorWidget editor,
511 LoadActionType typeOfInsert,
512 XmTextPosition beginInsert,
513 XmTextPosition endInsert)
517 int result, num_chars=0;
518 char *mb_value = (char *)NULL;
519 DtEditorErrorCode status;
523 * Convert the wide char string to a multi-byte string & insert it into
528 * Determine how big the resulting mb string may be
530 for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
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.
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 );
545 * Convert the wchar string.
546 * If wcstombs fails it returns (size_t) -1, so pass in empty
549 result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
550 if (result == (size_t)-1)
554 * wcstombs doesn't guarantee string is NULL terminated
556 mb_value[result] = 0;
558 status = insertStringValue( editor, mb_value, typeOfInsert,
559 beginInsert, endInsert );
564 } /* insertWcharValue */
567 /***************************************************************************
569 * DtEditorSetContents - sets the contents of the DtEditor widget.
571 * Inputs: widget to set the contents
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:
577 * string - \0-terminated string of characters
578 * data - the data, the size of the data
580 * Returns 0 - contents were set sucessfully
581 * !0 - an error occured while setting the contents
583 ***************************************************************************/
585 extern DtEditorErrorCode
588 DtEditorContentRec *data )
590 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
591 DtEditorWidget editor = (DtEditorWidget) widget;
592 _DtWidgetToAppContext(widget);
599 error = setStringValue ( editor, data->value.string );
604 error = setDataValue ( editor, data->value.data.buf,
605 data->value.data.length);
610 error = setWcharValue ( editor, data->value.wchar );
615 error = DtEDITOR_INVALID_TYPE;
620 * Update the current-line-display in the status line
622 if (error == DtEDITOR_NO_ERRORS)
623 _DtEditorUpdateLineDisplay(editor, 1, False );
630 /***************************************************************************
632 * DtEditorSetContentsFromFile - read a data file, putting the contents
633 * into a DtEditor widget.
635 * Inputs: widget to load the file into
637 * to indicate the type of contents loaded from the file:
638 * string - a \0-terminated string of characters
639 * data - untyped data
641 * filename - name of the file to read
643 * Returns 0 - contents were loaded sucessfully
644 * !0 - an error occured while loading the contents
646 ***************************************************************************/
648 extern DtEditorErrorCode
649 DtEditorSetContentsFromFile(
653 DtEditorErrorCode result;
654 _DtWidgetToAppContext(widget);
657 result = LoadFile(widget, fileName, LOAD_DATA, 0, 0);
664 /***************************************************************************
666 * DtEditorAppend - append data to the contents of the DtEditor widget.
668 * Inputs: widget to add to the contents
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:
674 * string - \0-terminated string of characters
675 * data - the data, the size of the data
677 * Returns 0 - contents were set sucessfully
678 * !0 - an error occured while setting the contents
680 ***************************************************************************/
682 extern DtEditorErrorCode
685 DtEditorContentRec *data )
687 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
688 DtEditorWidget editor = (DtEditorWidget) widget;
689 _DtWidgetToAppContext(widget);
696 error = insertStringValue ( editor, data->value.string,
702 error = insertDataValue ( editor, data->value.data.buf,
703 data->value.data.length, APPEND_DATA, 0,0);
708 error = insertWcharValue ( editor, data->value.wchar,
714 error = DtEDITOR_INVALID_TYPE;
723 /***************************************************************************
725 * DtEditorAppendFromFile - read a data file, appending the contents
726 * into a DtEditor widget.
728 * Inputs: widget to append the file to
730 * to indicate the type of contents appended from the file:
731 * string - a \0-terminated string of characters
732 * data - untyped data
734 * filename - name of the file to read
736 * Returns 0 - contents were appended sucessfully
737 * !0 - an error occured while appending the contents
739 ***************************************************************************/
741 extern DtEditorErrorCode
742 DtEditorAppendFromFile(
746 DtEditorErrorCode result;
747 _DtWidgetToAppContext(widget);
750 result = LoadFile(widget, fileName, APPEND_DATA, 0, 0);
757 /***************************************************************************
759 * DtEditorInsert - insert data into the contents of the DtEditor widget.
761 * Inputs: widget to add to the contents
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:
767 * string - \0-terminated string of characters
768 * data - the data, the size of the data
770 * Returns 0 - contents were set sucessfully
771 * !0 - an error occured while setting the contents
773 ***************************************************************************/
775 extern DtEditorErrorCode
778 DtEditorContentRec *data )
780 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
781 DtEditorWidget editor = (DtEditorWidget) widget;
782 _DtWidgetToAppContext(widget);
789 error = insertStringValue ( editor, data->value.string,
795 error = insertDataValue ( editor, data->value.data.buf,
796 data->value.data.length, INSERT_DATA, 0,0);
801 error = insertWcharValue ( editor, data->value.wchar,
807 error = DtEDITOR_INVALID_TYPE;
816 /***************************************************************************
818 * DtEditorInsertFromFile - read a data file, inserting the contents
819 * into a DtEditor widget.
821 * Inputs: widget to insert the file to
823 * to indicate the type of contents inserted from the file:
824 * string - a \0-terminated string of characters
825 * data - untyped data
827 * filename - name of the file to read
829 * Returns 0 - contents were inserted sucessfully
830 * !0 - an error occured while inserting the contents
832 ***************************************************************************/
834 extern DtEditorErrorCode
835 DtEditorInsertFromFile(
839 DtEditorErrorCode result;
840 _DtWidgetToAppContext(widget);
843 result = LoadFile(widget, fileName, INSERT_DATA, 0, 0);
850 /***************************************************************************
852 * DtEditorReplace - replace a specified portion of the contents of the
853 * DtEditor widget with the supplied data.
855 * Inputs: widget to replace a portion of its contents
857 * starting character position of the portion to replace
859 * ending character position of the portion to replace
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:
865 * string - \0-terminated string of characters
866 * data - the data, the size of the data
869 * Returns 0 - the portion was replaced sucessfully
870 * !0 - an error occured while replacing the portion
872 ***************************************************************************/
874 extern DtEditorErrorCode
877 XmTextPosition startPos,
878 XmTextPosition endPos,
879 DtEditorContentRec *data)
881 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
882 DtEditorWidget editor = (DtEditorWidget) widget;
884 _DtWidgetToAppContext(widget);
887 tw = (XmTextWidget) M_text(editor);
891 if( startPos > tw->text.last_position )
892 startPos = tw->text.last_position;
896 if( endPos > tw->text.last_position )
897 endPos = tw->text.last_position;
899 if( startPos > endPos )
901 error = DtEDITOR_INVALID_RANGE;
909 error = insertStringValue ( editor, data->value.string,
910 REPLACE_DATA, startPos, endPos );
915 error = insertDataValue ( editor, data->value.data.buf,
916 data->value.data.length, REPLACE_DATA,
922 error = insertWcharValue ( editor, data->value.wchar,
923 REPLACE_DATA, startPos, endPos );
928 error = DtEDITOR_INVALID_TYPE;
939 /***************************************************************************
941 * DtEditorReplaceFromFile - read a data file, using the contents to replace
942 * a specified portion of the contntes of a
945 * Inputs: widget to insert the file to
947 * starting character position of the portion to replace
949 * ending character position of the portion to replace
951 * to indicate the type of contents inserted from the file:
952 * string - a \0-terminated string of characters
953 * data - untyped data
955 * filename - local name of the file to read
957 * Returns 0 - contents were inserted sucessfully
958 * !0 - an error occured while inserting the contents
960 ***************************************************************************/
962 extern DtEditorErrorCode
963 DtEditorReplaceFromFile(
965 XmTextPosition startPos,
966 XmTextPosition endPos,
970 DtEditorWidget editor = (DtEditorWidget) widget;
972 DtEditorErrorCode result;
973 _DtWidgetToAppContext(widget);
976 tw = (XmTextWidget) M_text(editor);
980 if( startPos > tw->text.last_position )
981 startPos = tw->text.last_position;
985 if( endPos > tw->text.last_position )
986 endPos = tw->text.last_position;
988 if(startPos > endPos)
990 result = DtEDITOR_INVALID_RANGE;
994 result = LoadFile(widget, fileName, REPLACE_DATA, startPos, endPos);
1002 /***************************************************************************
1004 * _DtEditorValidateFileAccess - check to see if file exists, whether we
1005 * can get to it, and whether it is readable
1008 * Note: does not check whether files for reading are read only.
1010 * Inputs: filename - name of the local file to read
1011 * flag indicating whether we want to read or write
1014 * Returns 0 file exists & we have read or write permissions.
1016 * >0 if file cannot be read from/written to.
1017 * errno is set to one of the following values:
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
1029 * additional READ_ACCESS errors:
1030 * DtEDITOR_UNREADABLE_FILE -
1032 * additional WRITE_ACCESS errors:
1033 * DtEDITOR_UNWRITABLE_FILE -
1034 * file or directory is write protected for
1037 ***************************************************************************/
1039 extern DtEditorErrorCode
1040 _DtEditorValidateFileAccess(
1044 struct stat statbuf; /* Information on a file. */
1046 DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
1049 * First, make sure we were given a name
1051 if (fileName && *fileName )
1055 * Does the file already exist?
1057 if ( access(fileName, F_OK) != 0 )
1058 error = DtEDITOR_NONEXISTENT_FILE;
1061 error = DtEDITOR_NO_ERRORS;
1064 * The file exists, so lets do some type checking
1067 if( stat(fileName, &statbuf) != 0 )
1068 error = DtEDITOR_NO_FILE_ACCESS;
1072 /* if its a directory - can't save */
1073 if( (statbuf.st_mode & S_IFMT) == S_IFDIR )
1075 error = DtEDITOR_DIRECTORY;
1079 /* if its a character special device - can't save */
1080 if( (statbuf.st_mode & S_IFMT) == S_IFCHR )
1082 error = DtEDITOR_CHAR_SPECIAL_FILE;
1086 /* if its a block mode device - can't save */
1087 if((statbuf.st_mode & S_IFMT) == S_IFBLK)
1089 error = DtEDITOR_BLOCK_MODE_FILE;
1094 * We now know that it's a regular file so check to whether we
1095 * can read or write to it, as appropriate.
1098 switch( accessType )
1103 if( access(fileName, R_OK) != 0 )
1104 error = DtEDITOR_UNREADABLE_FILE;
1112 if( access(fileName, W_OK) == 0 )
1117 error = DtEDITOR_WRITABLE_FILE;
1122 * Can't write to it.
1124 error = DtEDITOR_UNWRITABLE_FILE;
1126 } /* end no write permission */
1137 } /* end stat suceeded */
1139 } /* end file exists */
1141 } /* end filename passed in */
1145 } /* end _DtEditorValidateFileAccess */
1148 /************************************************************************
1150 * LoadFile - Check if file exists, whether we can get to it, etc.
1151 * If so, type and read its contents.
1153 * Inputs: widget to set, add, or insert contents of file into
1155 * name of file to read
1157 * type of file (NULL). This will be set by LoadFile
1159 * action to perform with the data (load, append, insert,
1160 * replace a portion, attach)
1162 * The following information will be used if the file
1163 * contents will replace a portion of the widget's contents:
1165 * starting character position of the portion to replace
1167 * ending character position of the portion to replace
1169 * Returns: DtEDITOR_NO_ERRORS - file was read sucessfully
1170 * DtEDITOR_READ_ONLY_FILE - file was read sucessfully but
1172 * DtEDITOR_DIRECTORY - the file is a directory
1173 * DtEDITOR_CHAR_SPECIAL_FILE - the file is a character
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
1179 * DtEDITOR_INSUFFICIENT_MEMORY - unable to allocate
1180 * enough memory for contents of file
1182 ************************************************************************/
1184 static DtEditorErrorCode
1188 LoadActionType action,
1189 XmTextPosition startReplace,
1190 XmTextPosition endReplace )
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 */
1203 * First, make sure we were given a name
1206 if (fileName && *fileName )
1210 * Can we read the file?
1212 returnVal = _DtEditorValidateFileAccess( fileName, READ_ACCESS );
1214 if( returnVal == DtEDITOR_NO_ERRORS )
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.
1221 if( (fp = fopen(fileName, "r+")) == NULL )
1224 * We can't update (read/write) the file so try opening read-
1227 if( (fp = fopen(fileName, "r")) == NULL )
1230 * We can't read from the file.
1232 return ( DtEDITOR_UNREADABLE_FILE );
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.
1241 returnVal = DtEDITOR_READ_ONLY_FILE;
1244 } /* end open for read/write */
1246 } /* end try to read the file */
1249 } /* end if no filename */
1251 /* If a file is open, get the bytes */
1255 stat( fileName, &statbuf );
1256 file_length = statbuf.st_size;
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.
1264 loadError = Check4EnoughMemory( file_length );
1265 if (loadError == DtEDITOR_INSUFFICIENT_MEMORY)
1266 returnVal = loadError;
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.
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';
1280 * Strip out any embedded NULLs because the text widget will only
1281 * accept data up to the first NULL.
1283 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
1284 * DtEDITOR_NULLS_REMOVED
1286 loadError = StripEmbeddedNulls( file_string, &file_length );
1287 if ( loadError != DtEDITOR_NO_ERRORS )
1288 returnVal = loadError;
1291 * Insert it as a string, otherwise the following DtEditor*()
1292 * functions will make another copy of the data.
1294 cr.type = DtEDITOR_TEXT;
1295 cr.value.string = file_string;
1299 * Load, insert, append, or attach the file, as specified
1305 loadError = DtEditorSetContents ( w, &cr );
1311 loadError = DtEditorInsert ( w, &cr );
1317 loadError = DtEditorAppend ( w, &cr );
1323 loadError = DtEditorReplace(w, startReplace, endReplace, &cr);
1332 if ( loadError != DtEDITOR_NO_ERRORS )
1333 returnVal = loadError;
1336 * The file is loaded, clean up.
1338 XtFree( file_string );
1340 } /* end there is enough memory */
1342 /* Close the file */
1345 } /* end if a file is open */
1347 return( returnVal );
1349 } /* end LoadFile */
1358 memcpy(destination, source, number);
1359 destination[number] = (char)'\0';
1360 destination += number;
1364 /***************************************************************************
1366 * CopySubstring - copies out a portion of the text, optionally
1367 * adding newlines at any and all wordwrap-caused
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.
1379 ***************************************************************************/
1383 XmTextWidget widget,
1384 XmTextPosition startPos,
1385 XmTextPosition endPos,
1387 Boolean addNewlines)
1389 register XmTextLineTable line_table = widget->text.line_table;
1390 int currLine, firstLine;
1391 char *pString, *pCurrChar, *pLastChar;
1396 if(startPos > widget->text.last_position)
1397 startPos = widget->text.last_position;
1401 if(endPos > widget->text.last_position)
1402 endPos = widget->text.last_position;
1404 if(startPos > endPos)
1407 pString = XmTextGetString((Widget)widget);
1409 if(addNewlines == False)
1411 pCurrChar = _DtEditorGetPointer(pString, startPos);
1412 pLastChar = _DtEditorGetPointer(pString, endPos);
1413 numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1415 buf = StringAdd(buf, pCurrChar, numToCopy);
1419 int *mb_str_loc, total, z, siz;
1422 mb_str_loc = (int *) XtMalloc(sizeof(int) * ((endPos-startPos)+1));
1423 if (NULL == mb_str_loc)
1425 /* Should figure out some way to pass back an error code. */
1426 buf = CopySubstring(widget, startPos, endPos, buf, False);
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[].
1440 /* Because startPos may not always == 0: */
1441 /* mb_str_loc[0] = startPos */
1442 /* mb_str_loc[endPos - startPos] = endPos */
1444 /* So when accessing items, dereference off of */
1448 for(total=0, bptr=pString, z=1;
1449 z <= (endPos - startPos); bptr += siz, z++)
1453 if ( (siz = mblen(bptr, MB_CUR_MAX)) < 0)
1467 mb_str_loc[z] = total;
1471 firstLine = currLine = _DtEditorGetLineIndex(widget, startPos);
1474 if(startPos > (XmTextPosition)line_table[currLine].start_pos)
1475 pCurrChar = pString + mb_str_loc[0];
1478 z = line_table[currLine].start_pos;
1479 pCurrChar = pString +
1480 mb_str_loc[z - startPos];
1483 if(addNewlines == True && currLine > firstLine &&
1484 line_table[currLine].virt_line != 0)
1486 buf[0] = (char)'\n';
1487 buf[1] = (char)'\0';
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)
1496 z = line_table[currLine+1].start_pos - 1;
1497 pLastChar = pString +
1498 mb_str_loc[z - startPos];
1501 pLastChar = pString +
1502 mb_str_loc[endPos - startPos];
1504 numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1506 buf = StringAdd(buf, pCurrChar, numToCopy);
1509 } while(currLine < widget->text.total_lines &&
1510 (XmTextPosition)line_table[currLine].start_pos <= endPos);
1511 XtFree((char*)mb_str_loc);
1518 /*************************************************************************
1520 * _DtEditorCopyDataOut - Writes the entire text editor buffer contents to
1521 * the specified character array.
1523 * Inputs: tw, to supply the data.
1524 * buf, specifying the array to which to write the data.
1526 *************************************************************************/
1529 _DtEditorCopyDataOut(
1532 Boolean addNewlines)
1534 buf = CopySubstring(tw, 0, tw->text.last_position, buf, addNewlines);
1539 static DtEditorErrorCode
1541 DtEditorWidget editor,
1543 Boolean insertNewlines)
1545 XmTextWidget tw = (XmTextWidget) M_text(editor);
1548 DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
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.
1556 bufSize = tw->text.last_position * MB_CUR_MAX;
1557 if(insertNewlines == True)
1558 bufSize += tw->text.total_lines;
1561 returnVal = Check4EnoughMemory(bufSize);
1562 if (DtEDITOR_NO_ERRORS != returnVal) return returnVal;
1564 *buf = (char *) XtMalloc(bufSize);
1565 lastChar = _DtEditorCopyDataOut(tw, *buf, insertNewlines);
1568 } /* end getStringValue */
1571 static DtEditorErrorCode
1573 DtEditorWidget editor,
1576 Boolean insertNewlines)
1578 DtEditorErrorCode error;
1580 error = getStringValue(editor, (char **)buf, insertNewlines);
1581 *size = strlen( *buf ); /* remember, strlen doesn't count \0 at end */
1585 } /* end getDataValue */
1588 static DtEditorErrorCode
1590 DtEditorWidget editor,
1592 Boolean insertNewlines)
1594 DtEditorErrorCode error;
1596 wchar_t *pWchar_value;
1597 int num_char, result;
1600 error = getStringValue(editor, &mb_value, insertNewlines);
1602 if (error == DtEDITOR_NO_ERRORS)
1605 * Allocate space for the wide character string
1607 num_char = _DtEditor_CountCharacters(mb_value, strlen(mb_value)) + 1;
1608 nbytes = (size_t) num_char * sizeof(wchar_t);
1610 error = Check4EnoughMemory(nbytes);
1611 if (DtEDITOR_NO_ERRORS != error) return error;
1612 pWchar_value = (wchar_t*) XtMalloc(nbytes);
1615 * Convert the multi-byte string to wide character
1617 result = mbstowcs(pWchar_value, mb_value, num_char*sizeof(wchar_t) );
1618 if (result < 0) pWchar_value[0] = 0L;
1619 *data = pWchar_value;
1625 } /* end getWcharValue */
1628 /***************************************************************************
1630 * DtEditorGetContents - gets the contents of the DtEditor widget.
1632 * Inputs: widget to retrieve the contents
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
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.
1649 * Returns 0 - contents were retrieved sucessfully
1650 * !0 - an error occured while retrieving the contents
1652 * The structure passed in will be set according to the
1654 * string - a \0-terminated string of characters with
1656 * container - handle to a Bento container
1657 * data - the data, the size of the data
1659 * The application is responsible for free'ing any data in the
1662 ***************************************************************************/
1664 extern DtEditorErrorCode
1665 DtEditorGetContents(
1667 DtEditorContentRec *data,
1668 Boolean hardCarriageReturns,
1669 Boolean markContentsAsSaved )
1671 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
1672 DtEditorWidget editor = (DtEditorWidget) widget;
1673 _DtWidgetToAppContext(widget);
1676 switch( data->type )
1680 error = getStringValue( editor, &(data->value.string),
1681 hardCarriageReturns );
1687 error = getDataValue( editor, &(data->value.data.buf),
1688 &(data->value.data.length),
1689 hardCarriageReturns );
1693 case DtEDITOR_WCHAR:
1695 error = getWcharValue( editor, &(data->value.wchar),
1696 hardCarriageReturns );
1702 error = DtEDITOR_INVALID_TYPE;
1707 * If there were no errors, mark there are now no unsaved changes (unless
1708 * we were told not to).
1710 if ( error == DtEDITOR_NO_ERRORS && markContentsAsSaved == True )
1711 M_unreadChanges( editor ) = False;
1718 /***************************************************************************
1720 * DtEditorSaveContentsToFile - saves the contents of the DtEditor
1721 * widget to a disc file as string/data
1722 * or a CDE Document (Bento container).
1724 * Inputs: widget to retrieve the contents
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.
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
1743 * OR any errors from DtEditorGetContents
1745 ***************************************************************************/
1747 extern DtEditorErrorCode
1748 DtEditorSaveContentsToFile(
1751 Boolean overwriteIfExists,
1752 Boolean hardCarriageReturns,
1753 Boolean markContentsAsSaved )
1757 DtEditorContentRec cr; /* Structure for retrieving contents of widget */
1758 DtEditorWidget editor = (DtEditorWidget) widget;
1760 DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
1761 _DtWidgetToAppContext(widget);
1765 * First, make sure we were given a name
1768 if (fileName && *fileName )
1772 * Can we save to the file?
1774 error = _DtEditorValidateFileAccess( fileName, WRITE_ACCESS );
1776 if( error == DtEDITOR_NO_ERRORS ||
1777 error == DtEDITOR_NONEXISTENT_FILE ||
1778 error == DtEDITOR_WRITABLE_FILE )
1781 * Don't overwrite an existing file if we've been told not to
1783 if( error == DtEDITOR_WRITABLE_FILE && overwriteIfExists == False )
1790 * Open the file for writing
1792 if ( (pFile = fopen(fileName, "w")) == NULL )
1795 return( DtEDITOR_UNWRITABLE_FILE );
1801 * Save the unsaved changes flag so we can restore it if the write
1802 * to the file fails.
1804 Boolean saved_state = M_unreadChanges( editor );
1807 * Now, get the contents of the widget and write it to the file,
1808 * depending upon the format requested.
1811 cr.type = DtEDITOR_DATA;
1812 error = DtEditorGetContents( widget, &cr, hardCarriageReturns,
1813 markContentsAsSaved );
1815 if ( error == DtEDITOR_NO_ERRORS )
1819 * Write it to the file
1821 size_t size_written = fwrite( cr.value.data.buf, 1,
1822 cr.value.data.length, pFile );
1824 if( cr.value.data.length != size_written )
1825 error = DtEDITOR_SAVE_FAILED;
1827 XtFree( cr.value.data.buf );
1832 if( error == DtEDITOR_SAVE_FAILED )
1835 * Restore the unsaved changes flag since the save failed
1837 M_unreadChanges( editor ) = saved_state;
1840 } /* end file is writable */
1842 } /* end filename is valid */
1848 } /* end DtEditorSaveContentsToFile */
1852 * _DtEditorGetPointer returns a pointer to the _character_
1853 * numbered by startChar within the string pString.
1854 * It accounts for possible multibyte chars.
1857 _DtEditorGetPointer(
1862 int curChar, char_size;
1866 for(bptr = pString, curChar = 0;
1867 curChar < startChar && *bptr != (char)'\0';
1868 curChar++, bptr += char_size)
1870 if ( (char_size = mblen(bptr, MB_CUR_MAX)) < 0)
1876 bptr = pString + startChar;
1879 } /* end _DtEditorGetPointer */