1 /* $TOG: EditAreaData.c /main/6 1998/03/03 16:18:13 mgreess $ */
2 /**********************************<+>*************************************
3 ***************************************************************************
5 ** File: EditAreaData.c
7 ** Project: DtEditor widget for editing services
9 ** Description: Contains functions for getting and setting the data
10 ** on which the editor operates.
13 *******************************************************************
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.
24 ********************************************************************
27 **************************************************************************
28 **********************************<+>*************************************/
30 #include <X11/Xutil.h>
33 #include "DtWidgetI.h"
36 typedef enum _LoadActionType {
43 static DtEditorErrorCode Check4EnoughMemory(
46 static DtEditorErrorCode StripEmbeddedNulls(
50 static DtEditorErrorCode LoadFile(
53 LoadActionType action,
54 XmTextPosition startReplace,
55 XmTextPosition endReplace );
57 #ifdef NEED_STRCASECMP
60 * in case strcasecmp is not provided by the system here is one
61 * which does the trick
65 register char *s1, *s2;
70 c1 = isupper(*s1) ? tolower(*s1) : *s1;
71 c2 = isupper(*s2) ? tolower(*s2) : *s2;
84 /*****************************************************************************
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.
91 * Returns DtEDITOR_NO_ERRORS
92 * DtEDITOR_ILLEGAL_SIZE
93 * DtEDITOR_INSUFFICIENT_MEMORY
95 *****************************************************************************/
96 static DtEditorErrorCode
101 DtEditorErrorCode returnVal = DtEDITOR_ILLEGAL_SIZE;
104 char *tmpString = (char *)malloc((2 * numBytes) + (numBytes/10));
106 if(tmpString == (char *)NULL)
107 returnVal = DtEDITOR_INSUFFICIENT_MEMORY;
109 returnVal = DtEDITOR_NO_ERRORS;
116 } /* end Check4EnoughMemory */
119 /*****************************************************************************
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
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.
130 *****************************************************************************/
131 static DtEditorErrorCode
136 DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
138 if (strlen(stringData) != *length)
142 returnVal = DtEDITOR_NULLS_REMOVED;
145 * The file contains NULL characters, so we strip them out and
146 * report that we have done so.
148 while((firstNull = strlen(stringData)) != *length)
150 int lastNull = firstNull;
152 while((lastNull + 1) < *length &&
153 stringData[lastNull + 1] == (char)'\0')
156 memcpy(&stringData[firstNull], &stringData[lastNull + 1],
158 *length -= 1 + lastNull - firstNull;
165 } /* end StripEmbeddedNulls */
168 /*****************************************************************************
170 * Retrieves the current location of the insert cursor
172 *****************************************************************************/
175 DtEditorGetInsertionPosition(
178 DtEditorWidget editor = (DtEditorWidget) widget;
179 XmTextPosition result;
180 _DtWidgetToAppContext(widget);
183 result = XmTextGetInsertionPosition(M_text(editor));
190 /*****************************************************************************
192 * Retrieves the current location of the last character in the widget
194 *****************************************************************************/
197 DtEditorGetLastPosition(
200 DtEditorWidget editor = (DtEditorWidget) widget;
201 XmTextPosition result;
202 _DtWidgetToAppContext(widget);
205 result = XmTextGetLastPosition(M_text(editor));
212 /*****************************************************************************
214 * Changes the current location of the insert cursor
216 *****************************************************************************/
219 DtEditorSetInsertionPosition(
221 XmTextPosition position)
223 DtEditorWidget editor = (DtEditorWidget) widget;
224 _DtWidgetToAppContext(widget);
227 XmTextSetInsertionPosition(M_text(editor), position);
233 static DtEditorErrorCode
235 DtEditorWidget editor,
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.
243 M_loadingAllNewData(editor) = True;
245 XmTextSetString( M_text(editor), data );
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.
253 if (M_loadingAllNewData(editor) == True) {
254 M_loadingAllNewData(editor) = False;
255 M_unreadChanges(editor) = False;
256 _DtEditorResetUndo(editor);
259 return( DtEDITOR_NO_ERRORS );
261 } /* end setStringValue */
264 static DtEditorErrorCode
266 DtEditorWidget widget,
270 DtEditorErrorCode status = DtEDITOR_NULL_ITEM, tmpError;
275 if (rawData != (void *)NULL)
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
282 * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS,
283 * DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY.
286 status = Check4EnoughMemory( length );
287 if (status == DtEDITOR_NO_ERRORS)
291 * Convert the data buffer into a string & insert into the widget
293 char *textData = (char *)XtMalloc(length + 1);
294 memcpy( textData, rawData, length );
295 textData[length] = '\0';
298 * Strip out any embedded NULLs because the text widget will only
299 * accept data up to the first NULL.
301 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
302 * DtEDITOR_NULLS_REMOVED
304 status = StripEmbeddedNulls( textData, &length );
307 * Now, insert the converted string into the text widget
309 tmpError = setStringValue( widget, textData );
310 if (tmpError != DtEDITOR_NO_ERRORS)
313 XtFree( (char *)textData );
319 } /* end setDataValue */
322 static DtEditorErrorCode
324 DtEditorWidget editor,
327 DtEditorErrorCode status;
329 int result, num_chars=0;
330 char *mb_value = (char *)NULL;
334 * Convert the wide char string to a multi-byte string & stick it in
339 * Determine how big the resulting mb string may be
341 for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
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.
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 );
355 * Convert the wchar string
356 * If wcstombs fails it returns (size_t) -1, so pass in empty
359 result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
360 if (result == (size_t)-1)
364 * wcstombs doesn't guarantee string is NULL terminated
366 mb_value[result] = 0;
368 status = setStringValue( editor, mb_value );
374 } /* end setWcharValue */
377 static DtEditorErrorCode
379 DtEditorWidget editor,
381 LoadActionType typeOfInsert,
382 XmTextPosition beginInsert,
383 XmTextPosition endInsert)
387 switch( typeOfInsert )
391 beginInsert = endInsert = XmTextGetInsertionPosition( M_text(editor) );
397 beginInsert = endInsert = XmTextGetLastPosition( M_text(editor) );
412 * Insert/Replace/Append the data and move the insertion cursor to
413 * the end of the inserted data.
415 numInserted = _DtEditor_CountCharacters( data, strlen(data) );
416 XmTextReplace(M_text(editor), beginInsert, endInsert, data);
417 XmTextSetInsertionPosition( M_text(editor),
418 (XmTextPosition)(beginInsert + numInserted) );
420 return( DtEDITOR_NO_ERRORS );
421 } /* insertStringValue */
423 static DtEditorErrorCode
425 DtEditorWidget widget,
428 LoadActionType typeOfInsert,
429 XmTextPosition beginInsert,
430 XmTextPosition endInsert)
433 DtEditorErrorCode status = DtEDITOR_NULL_ITEM, loadError;
438 if (rawData != (void *) NULL)
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
445 * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or
446 * DtEDITOR_INSUFFICIENT_MEMORY.
448 status = Check4EnoughMemory( length );
449 if (status == DtEDITOR_NO_ERRORS)
453 * Convert the data buffer into a string & insert into the widget
455 char *textData = (char *)XtMalloc(length + 1);
456 memcpy( textData, rawData, length );
457 textData[length] = '\0';
460 * Strip out any embedded NULLs because the text widget will only
461 * accept data up to the first NULL.
463 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
464 * DtEDITOR_NULLS_REMOVED
466 status = StripEmbeddedNulls( textData, &length );
469 * Now, insert the converted string into the text widget
471 loadError = insertStringValue( widget, textData, typeOfInsert,
472 beginInsert, endInsert );
473 if (loadError != DtEDITOR_NO_ERRORS)
476 XtFree( (char *)textData );
483 } /* insertDataValue */
486 static DtEditorErrorCode
488 DtEditorWidget editor,
490 LoadActionType typeOfInsert,
491 XmTextPosition beginInsert,
492 XmTextPosition endInsert)
496 int result, num_chars=0;
497 char *mb_value = (char *)NULL;
498 DtEditorErrorCode status;
502 * Convert the wide char string to a multi-byte string & insert it into
507 * Determine how big the resulting mb string may be
509 for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
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.
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 );
524 * Convert the wchar string.
525 * If wcstombs fails it returns (size_t) -1, so pass in empty
528 result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
529 if (result == (size_t)-1)
533 * wcstombs doesn't guarantee string is NULL terminated
535 mb_value[result] = 0;
537 status = insertStringValue( editor, mb_value, typeOfInsert,
538 beginInsert, endInsert );
543 } /* insertWcharValue */
546 /***************************************************************************
548 * DtEditorSetContents - sets the contents of the DtEditor widget.
550 * Inputs: widget to set the contents
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:
556 * string - \0-terminated string of characters
557 * data - the data, the size of the data
559 * Returns 0 - contents were set sucessfully
560 * !0 - an error occured while setting the contents
562 ***************************************************************************/
564 extern DtEditorErrorCode
567 DtEditorContentRec *data )
569 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
570 DtEditorWidget editor = (DtEditorWidget) widget;
571 _DtWidgetToAppContext(widget);
578 error = setStringValue ( editor, data->value.string );
583 error = setDataValue ( editor, data->value.data.buf,
584 data->value.data.length);
589 error = setWcharValue ( editor, data->value.wchar );
594 error = DtEDITOR_INVALID_TYPE;
599 * Update the current-line-display in the status line
601 if (error == DtEDITOR_NO_ERRORS)
602 _DtEditorUpdateLineDisplay(editor, 1, False );
609 /***************************************************************************
611 * DtEditorSetContentsFromFile - read a data file, putting the contents
612 * into a DtEditor widget.
614 * Inputs: widget to load the file into
616 * to indicate the type of contents loaded from the file:
617 * string - a \0-terminated string of characters
618 * data - untyped data
620 * filename - name of the file to read
622 * Returns 0 - contents were loaded sucessfully
623 * !0 - an error occured while loading the contents
625 ***************************************************************************/
627 extern DtEditorErrorCode
628 DtEditorSetContentsFromFile(
632 DtEditorErrorCode result;
633 _DtWidgetToAppContext(widget);
636 result = LoadFile(widget, fileName, LOAD_DATA, 0, 0);
643 /***************************************************************************
645 * DtEditorAppend - append data to the contents of the DtEditor widget.
647 * Inputs: widget to add to the contents
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:
653 * string - \0-terminated string of characters
654 * data - the data, the size of the data
656 * Returns 0 - contents were set sucessfully
657 * !0 - an error occured while setting the contents
659 ***************************************************************************/
661 extern DtEditorErrorCode
664 DtEditorContentRec *data )
666 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
667 DtEditorWidget editor = (DtEditorWidget) widget;
668 _DtWidgetToAppContext(widget);
675 error = insertStringValue ( editor, data->value.string,
681 error = insertDataValue ( editor, data->value.data.buf,
682 data->value.data.length, APPEND_DATA, 0,0);
687 error = insertWcharValue ( editor, data->value.wchar,
693 error = DtEDITOR_INVALID_TYPE;
702 /***************************************************************************
704 * DtEditorAppendFromFile - read a data file, appending the contents
705 * into a DtEditor widget.
707 * Inputs: widget to append the file to
709 * to indicate the type of contents appended from the file:
710 * string - a \0-terminated string of characters
711 * data - untyped data
713 * filename - name of the file to read
715 * Returns 0 - contents were appended sucessfully
716 * !0 - an error occured while appending the contents
718 ***************************************************************************/
720 extern DtEditorErrorCode
721 DtEditorAppendFromFile(
725 DtEditorErrorCode result;
726 _DtWidgetToAppContext(widget);
729 result = LoadFile(widget, fileName, APPEND_DATA, 0, 0);
736 /***************************************************************************
738 * DtEditorInsert - insert data into the contents of the DtEditor widget.
740 * Inputs: widget to add to the contents
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:
746 * string - \0-terminated string of characters
747 * data - the data, the size of the data
749 * Returns 0 - contents were set sucessfully
750 * !0 - an error occured while setting the contents
752 ***************************************************************************/
754 extern DtEditorErrorCode
757 DtEditorContentRec *data )
759 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
760 DtEditorWidget editor = (DtEditorWidget) widget;
761 _DtWidgetToAppContext(widget);
768 error = insertStringValue ( editor, data->value.string,
774 error = insertDataValue ( editor, data->value.data.buf,
775 data->value.data.length, INSERT_DATA, 0,0);
780 error = insertWcharValue ( editor, data->value.wchar,
786 error = DtEDITOR_INVALID_TYPE;
795 /***************************************************************************
797 * DtEditorInsertFromFile - read a data file, inserting the contents
798 * into a DtEditor widget.
800 * Inputs: widget to insert the file to
802 * to indicate the type of contents inserted from the file:
803 * string - a \0-terminated string of characters
804 * data - untyped data
806 * filename - name of the file to read
808 * Returns 0 - contents were inserted sucessfully
809 * !0 - an error occured while inserting the contents
811 ***************************************************************************/
813 extern DtEditorErrorCode
814 DtEditorInsertFromFile(
818 DtEditorErrorCode result;
819 _DtWidgetToAppContext(widget);
822 result = LoadFile(widget, fileName, INSERT_DATA, 0, 0);
829 /***************************************************************************
831 * DtEditorReplace - replace a specified portion of the contents of the
832 * DtEditor widget with the supplied data.
834 * Inputs: widget to replace a portion of its contents
836 * starting character position of the portion to replace
838 * ending character position of the portion to replace
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:
844 * string - \0-terminated string of characters
845 * data - the data, the size of the data
848 * Returns 0 - the portion was replaced sucessfully
849 * !0 - an error occured while replacing the portion
851 ***************************************************************************/
853 extern DtEditorErrorCode
856 XmTextPosition startPos,
857 XmTextPosition endPos,
858 DtEditorContentRec *data)
860 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
861 DtEditorWidget editor = (DtEditorWidget) widget;
863 _DtWidgetToAppContext(widget);
866 tw = (XmTextWidget) M_text(editor);
870 if( startPos > tw->text.last_position )
871 startPos = tw->text.last_position;
875 if( endPos > tw->text.last_position )
876 endPos = tw->text.last_position;
878 if( startPos > endPos )
880 error = DtEDITOR_INVALID_RANGE;
888 error = insertStringValue ( editor, data->value.string,
889 REPLACE_DATA, startPos, endPos );
894 error = insertDataValue ( editor, data->value.data.buf,
895 data->value.data.length, REPLACE_DATA,
901 error = insertWcharValue ( editor, data->value.wchar,
902 REPLACE_DATA, startPos, endPos );
907 error = DtEDITOR_INVALID_TYPE;
918 /***************************************************************************
920 * DtEditorReplaceFromFile - read a data file, using the contents to replace
921 * a specified portion of the contntes of a
924 * Inputs: widget to insert the file to
926 * starting character position of the portion to replace
928 * ending character position of the portion to replace
930 * to indicate the type of contents inserted from the file:
931 * string - a \0-terminated string of characters
932 * data - untyped data
934 * filename - local name of the file to read
936 * Returns 0 - contents were inserted sucessfully
937 * !0 - an error occured while inserting the contents
939 ***************************************************************************/
941 extern DtEditorErrorCode
942 DtEditorReplaceFromFile(
944 XmTextPosition startPos,
945 XmTextPosition endPos,
949 DtEditorWidget editor = (DtEditorWidget) widget;
951 DtEditorErrorCode result;
952 _DtWidgetToAppContext(widget);
955 tw = (XmTextWidget) M_text(editor);
959 if( startPos > tw->text.last_position )
960 startPos = tw->text.last_position;
964 if( endPos > tw->text.last_position )
965 endPos = tw->text.last_position;
967 if(startPos > endPos)
969 result = DtEDITOR_INVALID_RANGE;
973 result = LoadFile(widget, fileName, REPLACE_DATA, startPos, endPos);
981 /***************************************************************************
983 * _DtEditorValidateFileAccess - check to see if file exists, whether we
984 * can get to it, and whether it is readable
987 * Note: does not check whether files for reading are read only.
989 * Inputs: filename - name of the local file to read
990 * flag indicating whether we want to read or write
993 * Returns 0 file exists & we have read or write permissions.
995 * >0 if file cannot be read from/written to.
996 * errno is set to one of the following values:
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
1008 * additional READ_ACCESS errors:
1009 * DtEDITOR_UNREADABLE_FILE -
1011 * additional WRITE_ACCESS errors:
1012 * DtEDITOR_UNWRITABLE_FILE -
1013 * file or directory is write protected for
1016 ***************************************************************************/
1018 extern DtEditorErrorCode
1019 _DtEditorValidateFileAccess(
1023 struct stat statbuf; /* Information on a file. */
1024 unsigned short tmpMode;
1026 DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
1029 * First, make sure we were given a name
1031 if (fileName && *fileName )
1035 * Does the file already exist?
1037 if ( access(fileName, F_OK) != 0 )
1038 error = DtEDITOR_NONEXISTENT_FILE;
1041 error = DtEDITOR_NO_ERRORS;
1044 * The file exists, so lets do some type checking
1047 if( stat(fileName, &statbuf) != 0 )
1048 error = DtEDITOR_NO_FILE_ACCESS;
1052 /* if its a directory - can't save */
1053 if( (statbuf.st_mode & S_IFMT) == S_IFDIR )
1055 error = DtEDITOR_DIRECTORY;
1059 /* if its a character special device - can't save */
1060 if( (statbuf.st_mode & S_IFMT) == S_IFCHR )
1062 error = DtEDITOR_CHAR_SPECIAL_FILE;
1066 /* if its a block mode device - can't save */
1067 if((statbuf.st_mode & S_IFMT) == S_IFBLK)
1069 error = DtEDITOR_BLOCK_MODE_FILE;
1074 * We now know that it's a regular file so check to whether we
1075 * can read or write to it, as appropriate.
1078 switch( accessType )
1083 if( access(fileName, R_OK) != 0 )
1084 error = DtEDITOR_UNREADABLE_FILE;
1092 if( access(fileName, W_OK) == 0 )
1097 error = DtEDITOR_WRITABLE_FILE;
1102 * Can't write to it.
1104 error = DtEDITOR_UNWRITABLE_FILE;
1106 } /* end no write permission */
1117 } /* end stat suceeded */
1119 } /* end file exists */
1121 } /* end filename passed in */
1125 } /* end _DtEditorValidateFileAccess */
1128 /************************************************************************
1130 * LoadFile - Check if file exists, whether we can get to it, etc.
1131 * If so, type and read its contents.
1133 * Inputs: widget to set, add, or insert contents of file into
1135 * name of file to read
1137 * type of file (NULL). This will be set by LoadFile
1139 * action to perform with the data (load, append, insert,
1140 * replace a portion, attach)
1142 * The following information will be used if the file
1143 * contents will replace a portion of the widget's contents:
1145 * starting character position of the portion to replace
1147 * ending character position of the portion to replace
1149 * Returns: DtEDITOR_NO_ERRORS - file was read sucessfully
1150 * DtEDITOR_READ_ONLY_FILE - file was read sucessfully but
1152 * DtEDITOR_DIRECTORY - the file is a directory
1153 * DtEDITOR_CHAR_SPECIAL_FILE - the file is a character
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
1159 * DtEDITOR_INSUFFICIENT_MEMORY - unable to allocate
1160 * enough memory for contents of file
1162 ************************************************************************/
1164 static DtEditorErrorCode
1168 LoadActionType action,
1169 XmTextPosition startReplace,
1170 XmTextPosition endReplace )
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 */
1184 * First, make sure we were given a name
1187 if (fileName && *fileName )
1191 * Can we read the file?
1193 returnVal = _DtEditorValidateFileAccess( fileName, READ_ACCESS );
1195 if( returnVal == DtEDITOR_NO_ERRORS )
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.
1202 if( (fp = fopen(fileName, "r+")) == NULL )
1205 * We can't update (read/write) the file so try opening read-
1208 if( (fp = fopen(fileName, "r")) == NULL )
1211 * We can't read from the file.
1213 return ( DtEDITOR_UNREADABLE_FILE );
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.
1222 returnVal = DtEDITOR_READ_ONLY_FILE;
1225 } /* end open for read/write */
1227 } /* end try to read the file */
1230 } /* end if no filename */
1232 /* If a file is open, get the bytes */
1236 stat( fileName, &statbuf );
1237 file_length = statbuf.st_size;
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.
1245 loadError = Check4EnoughMemory( file_length );
1246 if (loadError == DtEDITOR_INSUFFICIENT_MEMORY)
1247 returnVal == loadError;
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.
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';
1261 * Strip out any embedded NULLs because the text widget will only
1262 * accept data up to the first NULL.
1264 * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
1265 * DtEDITOR_NULLS_REMOVED
1267 loadError = StripEmbeddedNulls( file_string, &file_length );
1268 if ( loadError != DtEDITOR_NO_ERRORS )
1269 returnVal = loadError;
1272 * Insert it as a string, otherwise the following DtEditor*()
1273 * functions will make another copy of the data.
1275 cr.type = DtEDITOR_TEXT;
1276 cr.value.string = file_string;
1280 * Load, insert, append, or attach the file, as specified
1286 loadError = DtEditorSetContents ( w, &cr );
1292 loadError = DtEditorInsert ( w, &cr );
1298 loadError = DtEditorAppend ( w, &cr );
1304 loadError = DtEditorReplace(w, startReplace, endReplace, &cr);
1313 if ( loadError != DtEDITOR_NO_ERRORS )
1314 returnVal = loadError;
1317 * The file is loaded, clean up.
1319 XtFree( file_string );
1321 } /* end there is enough memory */
1323 /* Close the file */
1326 } /* end if a file is open */
1328 return( returnVal );
1330 } /* end LoadFile */
1339 memcpy(destination, source, number);
1340 destination[number] = (char)'\0';
1341 destination += number;
1345 /***************************************************************************
1347 * CopySubstring - copies out a portion of the text, optionally
1348 * adding newlines at any and all wordwrap-caused
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.
1360 ***************************************************************************/
1364 XmTextWidget widget,
1365 XmTextPosition startPos,
1366 XmTextPosition endPos,
1368 Boolean addNewlines)
1370 register XmTextLineTable line_table = widget->text.line_table;
1371 int last_line_index, currLine, firstLine;
1372 char *pString, *pCurrChar, *pLastChar;
1377 if(startPos > widget->text.last_position)
1378 startPos = widget->text.last_position;
1382 if(endPos > widget->text.last_position)
1383 endPos = widget->text.last_position;
1385 if(startPos > endPos)
1388 pString = XmTextGetString((Widget)widget);
1390 if(addNewlines == False)
1392 pCurrChar = _DtEditorGetPointer(pString, startPos);
1393 pLastChar = _DtEditorGetPointer(pString, endPos);
1394 numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1396 buf = StringAdd(buf, pCurrChar, numToCopy);
1400 int *mb_str_loc, total, z, siz;
1403 mb_str_loc = (int *) XtMalloc(sizeof(int) * ((endPos-startPos)+1));
1404 if (NULL == mb_str_loc)
1406 /* Should figure out some way to pass back an error code. */
1407 buf = CopySubstring(widget, startPos, endPos, buf, False);
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[].
1421 /* Because startPos may not always == 0: */
1422 /* mb_str_loc[0] = startPos */
1423 /* mb_str_loc[endPos - startPos] = endPos */
1425 /* So when accessing items, dereference off of */
1429 for(total=0, bptr=pString, z=1;
1430 z <= (endPos - startPos); bptr += siz, z++)
1434 if ( (siz = mblen(bptr, MB_CUR_MAX)) < 0)
1448 mb_str_loc[z] = total;
1452 firstLine = currLine = _DtEditorGetLineIndex(widget, startPos);
1455 if(startPos > (XmTextPosition)line_table[currLine].start_pos)
1456 pCurrChar = pString + mb_str_loc[0];
1459 z = line_table[currLine].start_pos;
1460 pCurrChar = pString +
1461 mb_str_loc[z - startPos];
1464 if(addNewlines == True && currLine > firstLine &&
1465 line_table[currLine].virt_line != 0)
1467 buf[0] = (char)'\n';
1468 buf[1] = (char)'\0';
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)
1477 z = line_table[currLine+1].start_pos - 1;
1478 pLastChar = pString +
1479 mb_str_loc[z - startPos];
1482 pLastChar = pString +
1483 mb_str_loc[endPos - startPos];
1485 numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
1487 buf = StringAdd(buf, pCurrChar, numToCopy);
1490 } while(currLine < widget->text.total_lines &&
1491 (XmTextPosition)line_table[currLine].start_pos <= endPos);
1492 XtFree((char*)mb_str_loc);
1499 /*************************************************************************
1501 * _DtEditorCopyDataOut - Writes the entire text editor buffer contents to
1502 * the specified character array.
1504 * Inputs: tw, to supply the data.
1505 * buf, specifying the array to which to write the data.
1507 *************************************************************************/
1510 _DtEditorCopyDataOut(
1513 Boolean addNewlines)
1515 XmTextPosition curCharNum;
1516 DtEditorWidget editor = M_editor(tw);
1518 buf = CopySubstring(tw, 0, tw->text.last_position, buf, addNewlines);
1523 static DtEditorErrorCode
1525 DtEditorWidget editor,
1527 Boolean insertNewlines)
1529 XmTextWidget tw = (XmTextWidget) M_text(editor);
1532 DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
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.
1540 bufSize = tw->text.last_position * MB_CUR_MAX;
1541 if(insertNewlines == True)
1542 bufSize += tw->text.total_lines;
1545 returnVal = Check4EnoughMemory(bufSize);
1546 if (DtEDITOR_NO_ERRORS != returnVal) return returnVal;
1548 *buf = (char *) XtMalloc(bufSize);
1549 lastChar = _DtEditorCopyDataOut(tw, *buf, insertNewlines);
1552 } /* end getStringValue */
1555 static DtEditorErrorCode
1557 DtEditorWidget editor,
1560 Boolean insertNewlines)
1562 DtEditorErrorCode error;
1564 error = getStringValue(editor, (char **)buf, insertNewlines);
1565 *size = strlen( *buf ); /* remember, strlen doesn't count \0 at end */
1569 } /* end getDataValue */
1572 static DtEditorErrorCode
1574 DtEditorWidget editor,
1576 Boolean insertNewlines)
1578 DtEditorErrorCode error;
1580 wchar_t *pWchar_value;
1581 int num_char, result;
1584 error = getStringValue(editor, &mb_value, insertNewlines);
1586 if (error == DtEDITOR_NO_ERRORS)
1589 * Allocate space for the wide character string
1591 num_char = _DtEditor_CountCharacters(mb_value, strlen(mb_value)) + 1;
1592 nbytes = (size_t) num_char * sizeof(wchar_t);
1594 error = Check4EnoughMemory(nbytes);
1595 if (DtEDITOR_NO_ERRORS != error) return error;
1596 pWchar_value = (wchar_t*) XtMalloc(nbytes);
1599 * Convert the multi-byte string to wide character
1601 result = mbstowcs(pWchar_value, mb_value, num_char*sizeof(wchar_t) );
1602 if (result < 0) pWchar_value[0] = 0L;
1603 *data = pWchar_value;
1609 } /* end getWcharValue */
1612 /***************************************************************************
1614 * DtEditorGetContents - gets the contents of the DtEditor widget.
1616 * Inputs: widget to retrieve the contents
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
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.
1633 * Returns 0 - contents were retrieved sucessfully
1634 * !0 - an error occured while retrieving the contents
1636 * The structure passed in will be set according to the
1638 * string - a \0-terminated string of characters with
1640 * container - handle to a Bento container
1641 * data - the data, the size of the data
1643 * The application is responsible for free'ing any data in the
1646 ***************************************************************************/
1648 extern DtEditorErrorCode
1649 DtEditorGetContents(
1651 DtEditorContentRec *data,
1652 Boolean hardCarriageReturns,
1653 Boolean markContentsAsSaved )
1655 DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
1656 DtEditorWidget editor = (DtEditorWidget) widget;
1657 _DtWidgetToAppContext(widget);
1660 switch( data->type )
1664 error = getStringValue( editor, &(data->value.string),
1665 hardCarriageReturns );
1671 error = getDataValue( editor, &(data->value.data.buf),
1672 &(data->value.data.length),
1673 hardCarriageReturns );
1677 case DtEDITOR_WCHAR:
1679 error = getWcharValue( editor, &(data->value.wchar),
1680 hardCarriageReturns );
1686 error = DtEDITOR_INVALID_TYPE;
1691 * If there were no errors, mark there are now no unsaved changes (unless
1692 * we were told not to).
1694 if ( error == DtEDITOR_NO_ERRORS && markContentsAsSaved == True )
1695 M_unreadChanges( editor ) = False;
1702 /***************************************************************************
1704 * DtEditorSaveContentsToFile - saves the contents of the DtEditor
1705 * widget to a disc file as string/data
1706 * or a CDE Document (Bento container).
1708 * Inputs: widget to retrieve the contents
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.
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
1727 * OR any errors from DtEditorGetContents
1729 ***************************************************************************/
1731 extern DtEditorErrorCode
1732 DtEditorSaveContentsToFile(
1735 Boolean overwriteIfExists,
1736 Boolean hardCarriageReturns,
1737 Boolean markContentsAsSaved )
1740 struct stat statbuf; /* Information on a file. */
1742 DtEditorContentRec cr; /* Structure for retrieving contents of widget */
1743 DtEditorWidget editor = (DtEditorWidget) widget;
1745 DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
1746 _DtWidgetToAppContext(widget);
1750 * First, make sure we were given a name
1753 if (fileName && *fileName )
1757 * Can we save to the file?
1759 error = _DtEditorValidateFileAccess( fileName, WRITE_ACCESS );
1761 if( error == DtEDITOR_NO_ERRORS ||
1762 error == DtEDITOR_NONEXISTENT_FILE ||
1763 error == DtEDITOR_WRITABLE_FILE )
1766 * Don't overwrite an existing file if we've been told not to
1768 if( error == DtEDITOR_WRITABLE_FILE && overwriteIfExists == False )
1775 * Open the file for writing
1777 if ( (pFile = fopen(fileName, "w")) == NULL )
1780 return( DtEDITOR_UNWRITABLE_FILE );
1786 * Save the unsaved changes flag so we can restore it if the write
1787 * to the file fails.
1789 Boolean saved_state = M_unreadChanges( editor );
1792 * Now, get the contents of the widget and write it to the file,
1793 * depending upon the format requested.
1796 cr.type = DtEDITOR_DATA;
1797 error = DtEditorGetContents( widget, &cr, hardCarriageReturns,
1798 markContentsAsSaved );
1800 if ( error == DtEDITOR_NO_ERRORS )
1804 * Write it to the file
1806 size_t size_written = fwrite( cr.value.data.buf, 1,
1807 cr.value.data.length, pFile );
1809 if( cr.value.data.length != size_written )
1810 error = DtEDITOR_SAVE_FAILED;
1812 XtFree( cr.value.data.buf );
1817 if( error == DtEDITOR_SAVE_FAILED )
1820 * Restore the unsaved changes flag since the save failed
1822 M_unreadChanges( editor ) = saved_state;
1825 } /* end file is writable */
1827 } /* end filename is valid */
1833 } /* end DtEditorSaveContentsToFile */
1837 * _DtEditorGetPointer returns a pointer to the _character_
1838 * numbered by startChar within the string pString.
1839 * It accounts for possible multibyte chars.
1842 _DtEditorGetPointer(
1847 int curChar, char_size;
1851 for(bptr = pString, curChar = 0;
1852 curChar < startChar && *bptr != (char)'\0';
1853 curChar++, bptr += char_size)
1855 if ( (char_size = mblen(bptr, MB_CUR_MAX)) < 0)
1861 bptr = pString + startChar;
1864 } /* end _DtEditorGetPointer */