/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: EditAreaData.c /main/6 1998/03/03 16:18:13 mgreess $ */ /**********************************<+>************************************* *************************************************************************** ** ** File: EditAreaData.c ** ** Project: DtEditor widget for editing services ** ** Description: Contains functions for getting and setting the data ** on which the editor operates. ** ----------- ** ******************************************************************* * * (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc. * (c) Copyright 1996 Digital Equipment Corporation. * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company. * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc. * (c) Copyright 1996 Novell, Inc. * (c) Copyright 1996 FUJITSU LIMITED. * (c) Copyright 1996 Hitachi. * ******************************************************************** ** ** ************************************************************************** **********************************<+>*************************************/ #include "EditorP.h" #include #include #include #include "DtWidgetI.h" typedef enum _LoadActionType { LOAD_DATA, INSERT_DATA, APPEND_DATA, REPLACE_DATA } LoadActionType; static DtEditorErrorCode Check4EnoughMemory( int numBytes); static DtEditorErrorCode StripEmbeddedNulls( char *stringData, int *length); static DtEditorErrorCode LoadFile( Widget w, char *fileName, LoadActionType action, XmTextPosition startReplace, XmTextPosition endReplace ); #ifdef NEED_STRCASECMP /* * in case strcasecmp is not provided by the system here is one * which does the trick */ static int strcasecmp(s1, s2) char *s1, *s2; { int c1, c2; while (*s1 && *s2) { c1 = isupper(*s1) ? tolower(*s1) : *s1; c2 = isupper(*s2) ? tolower(*s2) : *s2; if (c1 != c2) return (1); s1++; s2++; } if (*s1 || *s2) return (1); return (0); } #endif /***************************************************************************** * * Check4EnoughMemory - estimates whether there is enough memory to malloc * "numBytes" of memory. This routine doubles the amount needed because the * routines that use it are putting data into the text widget & we must make * sure the widget will have room, too. * * Returns DtEDITOR_NO_ERRORS * DtEDITOR_ILLEGAL_SIZE * DtEDITOR_INSUFFICIENT_MEMORY * *****************************************************************************/ static DtEditorErrorCode Check4EnoughMemory( int numBytes) { DtEditorErrorCode returnVal = DtEDITOR_ILLEGAL_SIZE; if (numBytes > 0) { char *tmpString = (char *)malloc((2 * numBytes) + (numBytes/10)); if(tmpString == (char *)NULL) returnVal = DtEDITOR_INSUFFICIENT_MEMORY; else { returnVal = DtEDITOR_NO_ERRORS; free(tmpString); } } return( returnVal ); } /* end Check4EnoughMemory */ /***************************************************************************** * * StripEmbeddedNulls - removes any embedded NULLs (\0) in a string of length * 'length'. The removal occurs in place, with 'length' set to the * new, stripped length. The resulting string is terminated with a * trailing NULL. * * Returns DtEDITOR_NO_ERRORS - the string did not contain any embedded NULLs * DtEDITOR_NULLS_REMOVED - the string did contain embedded * NULLs that were removed. * *****************************************************************************/ static DtEditorErrorCode StripEmbeddedNulls( char *stringData, int *length) { DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS; if (strlen(stringData) != *length) { int firstNull; returnVal = DtEDITOR_NULLS_REMOVED; /* * The file contains NULL characters, so we strip them out and * report that we have done so. */ while((firstNull = strlen(stringData)) != *length) { int lastNull = firstNull; while((lastNull + 1) < *length && stringData[lastNull + 1] == (char)'\0') lastNull++; memcpy(&stringData[firstNull], &stringData[lastNull + 1], *length - lastNull); *length -= 1 + lastNull - firstNull; } } return( returnVal); } /* end StripEmbeddedNulls */ /***************************************************************************** * * Retrieves the current location of the insert cursor * *****************************************************************************/ XmTextPosition DtEditorGetInsertionPosition( Widget widget) { DtEditorWidget editor = (DtEditorWidget) widget; XmTextPosition result; _DtWidgetToAppContext(widget); _DtAppLock(app); result = XmTextGetInsertionPosition(M_text(editor)); _DtAppUnlock(app); return result; } /***************************************************************************** * * Retrieves the current location of the last character in the widget * *****************************************************************************/ XmTextPosition DtEditorGetLastPosition( Widget widget) { DtEditorWidget editor = (DtEditorWidget) widget; XmTextPosition result; _DtWidgetToAppContext(widget); _DtAppLock(app); result = XmTextGetLastPosition(M_text(editor)); _DtAppUnlock(app); return result; } /***************************************************************************** * * Changes the current location of the insert cursor * *****************************************************************************/ void DtEditorSetInsertionPosition( Widget widget, XmTextPosition position) { DtEditorWidget editor = (DtEditorWidget) widget; _DtWidgetToAppContext(widget); _DtAppLock(app); XmTextSetInsertionPosition(M_text(editor), position); _DtAppUnlock(app); } static DtEditorErrorCode setStringValue( DtEditorWidget editor, char *data) { /* * Tell _DtEditorModifyVerifyCB() that we're replacing the entire * contents, so it doesn't try to save the current document in an * undo structure for a later undo. */ M_loadingAllNewData(editor) = True; XmTextSetString( M_text(editor), data ); /* * If the _DtEditorModifyVerifyCB() did not get called, reset the * things which usually get reset there. The modifyVerify callback * will not get called if the contents are being set to a null string * and the widget is already empty. */ if (M_loadingAllNewData(editor) == True) { M_loadingAllNewData(editor) = False; M_unreadChanges(editor) = False; _DtEditorResetUndo(editor); } return( DtEDITOR_NO_ERRORS ); } /* end setStringValue */ static DtEditorErrorCode setDataValue( DtEditorWidget widget, void *rawData, int length) { DtEditorErrorCode status = DtEDITOR_NULL_ITEM, tmpError; /* * Validate input */ if (rawData != (void *)NULL) { /* * Check to see if we have a valid buffer size & enough memory to * load the buffer into the text widget. This is only an estimate * of our needs. * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS, * DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY. */ status = Check4EnoughMemory( length ); if (status == DtEDITOR_NO_ERRORS) { /* * Convert the data buffer into a string & insert into the widget */ char *textData = (char *)XtMalloc(length + 1); memcpy( textData, rawData, length ); textData[length] = '\0'; /* * Strip out any embedded NULLs because the text widget will only * accept data up to the first NULL. * * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or * DtEDITOR_NULLS_REMOVED */ status = StripEmbeddedNulls( textData, &length ); /* * Now, insert the converted string into the text widget */ tmpError = setStringValue( widget, textData ); if (tmpError != DtEDITOR_NO_ERRORS) status = tmpError; XtFree( (char *)textData ); } } return( status ); } /* end setDataValue */ static DtEditorErrorCode setWcharValue( DtEditorWidget editor, wchar_t *data) { DtEditorErrorCode status; wchar_t *tmp_wc; int result, num_chars=0; char *mb_value = (char *)NULL; /* * Convert the wide char string to a multi-byte string & stick it in * the text widget. */ /* * Determine how big the resulting mb string may be */ for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++) tmp_wc++; /* * Check to see if we have enough memory to load the string * into the text widget. This is only an estimate of our needs. * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or * DtEDITOR_INSUFFICIENT_MEMORY. */ status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX ); if (status != DtEDITOR_NO_ERRORS) return status; mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX ); /* * Convert the wchar string * If wcstombs fails it returns (size_t) -1, so pass in empty * string. */ result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX ); if (result == (size_t)-1) result = 0; /* * wcstombs doesn't guarantee string is NULL terminated */ mb_value[result] = 0; status = setStringValue( editor, mb_value ); XtFree(mb_value); return( status ); } /* end setWcharValue */ static DtEditorErrorCode insertStringValue( DtEditorWidget editor, char *data, LoadActionType typeOfInsert, XmTextPosition beginInsert, XmTextPosition endInsert) { int numInserted; switch( typeOfInsert ) { case INSERT_DATA: { beginInsert = endInsert = XmTextGetInsertionPosition( M_text(editor) ); break; } case APPEND_DATA: { beginInsert = endInsert = XmTextGetLastPosition( M_text(editor) ); break; } case REPLACE_DATA: { break; } default: { } } /* end switch */ /* * Insert/Replace/Append the data and move the insertion cursor to * the end of the inserted data. */ numInserted = _DtEditor_CountCharacters( data, strlen(data) ); XmTextReplace(M_text(editor), beginInsert, endInsert, data); XmTextSetInsertionPosition( M_text(editor), (XmTextPosition)(beginInsert + numInserted) ); return( DtEDITOR_NO_ERRORS ); } /* insertStringValue */ static DtEditorErrorCode insertDataValue( DtEditorWidget widget, void *rawData, int length, LoadActionType typeOfInsert, XmTextPosition beginInsert, XmTextPosition endInsert) { DtEditorErrorCode status = DtEDITOR_NULL_ITEM, loadError; /* * Validate input */ if (rawData != (void *) NULL) { /* * Check to see if we have a valid buffer size & enough memory to * insert the buffer into the text widget. This is only an estimate * of our needs. * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or * DtEDITOR_INSUFFICIENT_MEMORY. */ status = Check4EnoughMemory( length ); if (status == DtEDITOR_NO_ERRORS) { /* * Convert the data buffer into a string & insert into the widget */ char *textData = (char *)XtMalloc(length + 1); memcpy( textData, rawData, length ); textData[length] = '\0'; /* * Strip out any embedded NULLs because the text widget will only * accept data up to the first NULL. * * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or * DtEDITOR_NULLS_REMOVED */ status = StripEmbeddedNulls( textData, &length ); /* * Now, insert the converted string into the text widget */ loadError = insertStringValue( widget, textData, typeOfInsert, beginInsert, endInsert ); if (loadError != DtEDITOR_NO_ERRORS) status = loadError; XtFree( (char *)textData ); } } return( status ); } /* insertDataValue */ static DtEditorErrorCode insertWcharValue( DtEditorWidget editor, wchar_t *data, LoadActionType typeOfInsert, XmTextPosition beginInsert, XmTextPosition endInsert) { wchar_t *tmp_wc; int result, num_chars=0; char *mb_value = (char *)NULL; DtEditorErrorCode status; /* * Convert the wide char string to a multi-byte string & insert it into * the text widget. */ /* * Determine how big the resulting mb string may be */ for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++) tmp_wc++; /* * Check to see if we have enough memory to insert the string * into the text widget. This is only an estimate of our needs. * status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or * DtEDITOR_INSUFFICIENT_MEMORY. */ status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX ); if(status != DtEDITOR_NO_ERRORS) return status; mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX ); /* * Convert the wchar string. * If wcstombs fails it returns (size_t) -1, so pass in empty * string. */ result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX ); if (result == (size_t)-1) result = 0; /* * wcstombs doesn't guarantee string is NULL terminated */ mb_value[result] = 0; status = insertStringValue( editor, mb_value, typeOfInsert, beginInsert, endInsert ); XtFree( mb_value ); return( status ); } /* insertWcharValue */ /*************************************************************************** * * DtEditorSetContents - sets the contents of the DtEditor widget. * * Inputs: widget to set the contents * * a data structure containing the data to put into the * widget. Depending upon the type of data being set, this * structure will contain various fields: * * string - \0-terminated string of characters * data - the data, the size of the data * * Returns 0 - contents were set sucessfully * !0 - an error occurred while setting the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorSetContents( Widget widget, DtEditorContentRec *data ) { DtEditorErrorCode error = DtEDITOR_INVALID_TYPE; DtEditorWidget editor = (DtEditorWidget) widget; _DtWidgetToAppContext(widget); _DtAppLock(app); switch( data->type ) { case DtEDITOR_TEXT: { error = setStringValue ( editor, data->value.string ); break; } case DtEDITOR_DATA: { error = setDataValue ( editor, data->value.data.buf, data->value.data.length); break; } case DtEDITOR_WCHAR: { error = setWcharValue ( editor, data->value.wchar ); break; } default : { error = DtEDITOR_INVALID_TYPE; } } /* end switch */ /* * Update the current-line-display in the status line */ if (error == DtEDITOR_NO_ERRORS) _DtEditorUpdateLineDisplay(editor, 1, False ); _DtAppUnlock(app); return( error ); } /*************************************************************************** * * DtEditorSetContentsFromFile - read a data file, putting the contents * into a DtEditor widget. * * Inputs: widget to load the file into * * to indicate the type of contents loaded from the file: * string - a \0-terminated string of characters * data - untyped data * * filename - name of the file to read * * Returns 0 - contents were loaded sucessfully * !0 - an error occurred while loading the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorSetContentsFromFile( Widget widget, char *fileName) { DtEditorErrorCode result; _DtWidgetToAppContext(widget); _DtAppLock(app); result = LoadFile(widget, fileName, LOAD_DATA, 0, 0); _DtAppUnlock(app); return result; } /*************************************************************************** * * DtEditorAppend - append data to the contents of the DtEditor widget. * * Inputs: widget to add to the contents * * a data structure containing the data to append to the * widget. Depending upon the type of data being set, this * structure will contain various fields: * * string - \0-terminated string of characters * data - the data, the size of the data * * Returns 0 - contents were set sucessfully * !0 - an error occurred while setting the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorAppend( Widget widget, DtEditorContentRec *data ) { DtEditorErrorCode error = DtEDITOR_INVALID_TYPE; DtEditorWidget editor = (DtEditorWidget) widget; _DtWidgetToAppContext(widget); _DtAppLock(app); switch( data->type ) { case DtEDITOR_TEXT: { error = insertStringValue ( editor, data->value.string, APPEND_DATA, 0, 0 ); break; } case DtEDITOR_DATA: { error = insertDataValue ( editor, data->value.data.buf, data->value.data.length, APPEND_DATA, 0,0); break; } case DtEDITOR_WCHAR: { error = insertWcharValue ( editor, data->value.wchar, APPEND_DATA, 0, 0 ); break; } default: { error = DtEDITOR_INVALID_TYPE; } } /* end switch */ _DtAppUnlock(app); return( error ); } /*************************************************************************** * * DtEditorAppendFromFile - read a data file, appending the contents * into a DtEditor widget. * * Inputs: widget to append the file to * * to indicate the type of contents appended from the file: * string - a \0-terminated string of characters * data - untyped data * * filename - name of the file to read * * Returns 0 - contents were appended sucessfully * !0 - an error occurred while appending the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorAppendFromFile( Widget widget, char *fileName) { DtEditorErrorCode result; _DtWidgetToAppContext(widget); _DtAppLock(app); result = LoadFile(widget, fileName, APPEND_DATA, 0, 0); _DtAppUnlock(app); return result; } /*************************************************************************** * * DtEditorInsert - insert data into the contents of the DtEditor widget. * * Inputs: widget to add to the contents * * a data structure containing the data to insert into the * widget. Depending upon the type of data being set, this * structure will contain various fields: * * string - \0-terminated string of characters * data - the data, the size of the data * * Returns 0 - contents were set sucessfully * !0 - an error occurred while setting the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorInsert( Widget widget, DtEditorContentRec *data ) { DtEditorErrorCode error = DtEDITOR_INVALID_TYPE; DtEditorWidget editor = (DtEditorWidget) widget; _DtWidgetToAppContext(widget); _DtAppLock(app); switch( data->type ) { case DtEDITOR_TEXT: { error = insertStringValue ( editor, data->value.string, INSERT_DATA, 0, 0 ); break; } case DtEDITOR_DATA: { error = insertDataValue ( editor, data->value.data.buf, data->value.data.length, INSERT_DATA, 0,0); break; } case DtEDITOR_WCHAR: { error = insertWcharValue ( editor, data->value.wchar, INSERT_DATA, 0, 0 ); break; } default : { error = DtEDITOR_INVALID_TYPE; } } /* end switch */ _DtAppUnlock(app); return( error ); } /*************************************************************************** * * DtEditorInsertFromFile - read a data file, inserting the contents * into a DtEditor widget. * * Inputs: widget to insert the file to * * to indicate the type of contents inserted from the file: * string - a \0-terminated string of characters * data - untyped data * * filename - name of the file to read * * Returns 0 - contents were inserted sucessfully * !0 - an error occurred while inserting the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorInsertFromFile( Widget widget, char *fileName) { DtEditorErrorCode result; _DtWidgetToAppContext(widget); _DtAppLock(app); result = LoadFile(widget, fileName, INSERT_DATA, 0, 0); _DtAppUnlock(app); return result; } /*************************************************************************** * * DtEditorReplace - replace a specified portion of the contents of the * DtEditor widget with the supplied data. * * Inputs: widget to replace a portion of its contents * * starting character position of the portion to replace * * ending character position of the portion to replace * * a data structure containing the data to replace some data * in the widget. Depending upon the type of data being set, * this structure will contain various fields: * * string - \0-terminated string of characters * data - the data, the size of the data * * * Returns 0 - the portion was replaced sucessfully * !0 - an error occurred while replacing the portion * ***************************************************************************/ extern DtEditorErrorCode DtEditorReplace( Widget widget, XmTextPosition startPos, XmTextPosition endPos, DtEditorContentRec *data) { DtEditorErrorCode error = DtEDITOR_INVALID_TYPE; DtEditorWidget editor = (DtEditorWidget) widget; XmTextWidget tw; _DtWidgetToAppContext(widget); _DtAppLock(app); tw = (XmTextWidget) M_text(editor); if( startPos < 0 ) startPos = 0; if( startPos > tw->text.last_position ) startPos = tw->text.last_position; if( endPos < 0 ) endPos = 0; if( endPos > tw->text.last_position ) endPos = tw->text.last_position; if( startPos > endPos ) { error = DtEDITOR_INVALID_RANGE; } else { switch( data->type ) { case DtEDITOR_TEXT: { error = insertStringValue ( editor, data->value.string, REPLACE_DATA, startPos, endPos ); break; } case DtEDITOR_DATA: { error = insertDataValue ( editor, data->value.data.buf, data->value.data.length, REPLACE_DATA, startPos, endPos ); break; } case DtEDITOR_WCHAR: { error = insertWcharValue ( editor, data->value.wchar, REPLACE_DATA, startPos, endPos ); break; } default : { error = DtEDITOR_INVALID_TYPE; } } /* end switch */ } _DtAppUnlock(app); return( error ); } /*************************************************************************** * * DtEditorReplaceFromFile - read a data file, using the contents to replace * a specified portion of the contntes of a * DtEditor widget. * * Inputs: widget to insert the file to * * starting character position of the portion to replace * * ending character position of the portion to replace * * to indicate the type of contents inserted from the file: * string - a \0-terminated string of characters * data - untyped data * * filename - local name of the file to read * * Returns 0 - contents were inserted sucessfully * !0 - an error occurred while inserting the contents * ***************************************************************************/ extern DtEditorErrorCode DtEditorReplaceFromFile( Widget widget, XmTextPosition startPos, XmTextPosition endPos, char *fileName) { DtEditorWidget editor = (DtEditorWidget) widget; XmTextWidget tw; DtEditorErrorCode result; _DtWidgetToAppContext(widget); _DtAppLock(app); tw = (XmTextWidget) M_text(editor); if( startPos < 0) startPos = 0; if( startPos > tw->text.last_position ) startPos = tw->text.last_position; if( endPos < 0 ) endPos = 0; if( endPos > tw->text.last_position ) endPos = tw->text.last_position; if(startPos > endPos) { result = DtEDITOR_INVALID_RANGE; } else { result = LoadFile(widget, fileName, REPLACE_DATA, startPos, endPos); } _DtAppUnlock(app); return result; } /*************************************************************************** * * _DtEditorValidateFileAccess - check to see if file exists, whether we * can get to it, and whether it is readable * or writable. * * Note: does not check whether files for reading are read only. * * Inputs: filename - name of the local file to read * flag indicating whether we want to read or write * the file. * * Returns 0 file exists & we have read or write permissions. * * >0 if file cannot be read from/written to. * errno is set to one of the following values: * * General errors: * DtEDITOR_INVALID_FILENAME - 0 length filename * DtEDITOR_NONEXISTENT_FILE - file does not exist * (Note: this may not be considered an error when saving * to a file. The file may just need to be created.) * DtEDITOR_NO_FILE_ACCESS - cannot stat existing file * DtEDITOR_DIRECTORY - file is a directory * DtEDITOR_CHAR_SPECIAL_FILE - file is a device special file * DtEDITOR_BLOCK_MODE_FILE - file is a block mode file * * additional READ_ACCESS errors: * DtEDITOR_UNREADABLE_FILE - * * additional WRITE_ACCESS errors: * DtEDITOR_UNWRITABLE_FILE - * file or directory is write protected for * another reason * ***************************************************************************/ extern DtEditorErrorCode _DtEditorValidateFileAccess( char *fileName, int accessType ) { struct stat statbuf; /* Information on a file. */ DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME; /* * First, make sure we were given a name */ if (fileName && *fileName ) { /* * Does the file already exist? */ if ( access(fileName, F_OK) != 0 ) error = DtEDITOR_NONEXISTENT_FILE; else { error = DtEDITOR_NO_ERRORS; /* * The file exists, so lets do some type checking */ if( stat(fileName, &statbuf) != 0 ) error = DtEDITOR_NO_FILE_ACCESS; else { /* if its a directory - can't save */ if( (statbuf.st_mode & S_IFMT) == S_IFDIR ) { error = DtEDITOR_DIRECTORY; return( error ); } /* if its a character special device - can't save */ if( (statbuf.st_mode & S_IFMT) == S_IFCHR ) { error = DtEDITOR_CHAR_SPECIAL_FILE; return( error ); } /* if its a block mode device - can't save */ if((statbuf.st_mode & S_IFMT) == S_IFBLK) { error = DtEDITOR_BLOCK_MODE_FILE; return( error ); } /* * We now know that it's a regular file so check to whether we * can read or write to it, as appropriate. */ switch( accessType ) { case READ_ACCESS: { if( access(fileName, R_OK) != 0 ) error = DtEDITOR_UNREADABLE_FILE; break; } case WRITE_ACCESS: { if( access(fileName, W_OK) == 0 ) { /* * Can write to it. */ error = DtEDITOR_WRITABLE_FILE; } else { /* * Can't write to it. */ error = DtEDITOR_UNWRITABLE_FILE; } /* end no write permission */ break; } default: { break; } } /* end switch */ } /* end stat suceeded */ } /* end file exists */ } /* end filename passed in */ return( error ); } /* end _DtEditorValidateFileAccess */ /************************************************************************ * * LoadFile - Check if file exists, whether we can get to it, etc. * If so, type and read its contents. * * Inputs: widget to set, add, or insert contents of file into * * name of file to read * * type of file (NULL). This will be set by LoadFile * * action to perform with the data (load, append, insert, * replace a portion, attach) * * The following information will be used if the file * contents will replace a portion of the widget's contents: * * starting character position of the portion to replace * * ending character position of the portion to replace * * Returns: DtEDITOR_NO_ERRORS - file was read sucessfully * DtEDITOR_READ_ONLY_FILE - file was read sucessfully but * is read only * DtEDITOR_DIRECTORY - the file is a directory * DtEDITOR_CHAR_SPECIAL_FILE - the file is a character * special device * DtEDITOR_BLOCK_MODE_FILE - the file is a block mode device * DtEDITOR_NONEXISTENT_FILE - file does not exist * DtEDITOR_NULLS_REMOVED - file contained embedded NULLs * that were removed * DtEDITOR_INSUFFICIENT_MEMORY - unable to allocate * enough memory for contents of file * ************************************************************************/ static DtEditorErrorCode LoadFile( Widget w, char *fileName, LoadActionType action, XmTextPosition startReplace, XmTextPosition endReplace ) { DtEditorContentRec cr; /* Structure for passing data to widget */ struct stat statbuf; /* Information on a file. */ int file_length; /* Length of file. */ FILE *fp = NULL; /* Pointer to open file */ DtEditorErrorCode returnVal = DtEDITOR_NONEXISTENT_FILE; /* Error accessing file & reading contents */ DtEditorErrorCode loadError=DtEDITOR_NO_ERRORS; /* Error from placing bits into text widget */ /* * First, make sure we were given a name */ if (fileName && *fileName ) { /* * Can we read the file? */ returnVal = _DtEditorValidateFileAccess( fileName, READ_ACCESS ); if( returnVal == DtEDITOR_NO_ERRORS ) { /* * Open the file for reading. If we can read/write, then we're * cool, otherwise we might need to tell the user that the * file's read-only, or that we can't even read from it. */ if( (fp = fopen(fileName, "r+")) == NULL ) { /* * We can't update (read/write) the file so try opening read- * only */ if( (fp = fopen(fileName, "r")) == NULL ) { /* * We can't read from the file. */ return ( DtEDITOR_UNREADABLE_FILE ); } else { /* * Tell the application that the file's read-only. * Becareful not to overwrite this value with one of the calls * to set the widget's contents. */ returnVal = DtEDITOR_READ_ONLY_FILE; } } /* end open for read/write */ } /* end try to read the file */ } /* end if no filename */ /* If a file is open, get the bytes */ if ( fp ) { stat( fileName, &statbuf ); file_length = statbuf.st_size; /* * Check to see if we have enough memory to load the file contents * into the text widget. This is only an estimate of our needs. * Check4EnoughMemory() returns DtEDITOR_NO_ERRORS, * DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY. */ loadError = Check4EnoughMemory( file_length ); if (loadError == DtEDITOR_INSUFFICIENT_MEMORY) returnVal = loadError; else { /* * Read the file contents (with room for null) & convert to a * string. We want to use a string because the * DtEditorSetContents/Append/Insert/... functions create another * copy of the data before actually putting it into the widget. */ char *file_string = (char*) XtMalloc(file_length + 1); file_length = fread(file_string, sizeof(char), file_length, fp); file_string[file_length] = '\0'; /* * Strip out any embedded NULLs because the text widget will only * accept data up to the first NULL. * * StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or * DtEDITOR_NULLS_REMOVED */ loadError = StripEmbeddedNulls( file_string, &file_length ); if ( loadError != DtEDITOR_NO_ERRORS ) returnVal = loadError; /* * Insert it as a string, otherwise the following DtEditor*() * functions will make another copy of the data. */ cr.type = DtEDITOR_TEXT; cr.value.string = file_string; /* * Load, insert, append, or attach the file, as specified */ switch( action ) { case LOAD_DATA: { loadError = DtEditorSetContents ( w, &cr ); break; } case INSERT_DATA: { loadError = DtEditorInsert ( w, &cr ); break; } case APPEND_DATA: { loadError = DtEditorAppend ( w, &cr ); break; } case REPLACE_DATA: { loadError = DtEditorReplace(w, startReplace, endReplace, &cr); break; } default: { } } /* end switch */ if ( loadError != DtEDITOR_NO_ERRORS ) returnVal = loadError; /* * The file is loaded, clean up. */ XtFree( file_string ); } /* end there is enough memory */ /* Close the file */ fclose(fp); } /* end if a file is open */ return( returnVal ); } /* end LoadFile */ static char * StringAdd( char *destination, char *source, int number) { memcpy(destination, source, number); destination[number] = (char)'\0'; destination += number; return destination; } /*************************************************************************** * * CopySubstring - copies out a portion of the text, optionally * adding newlines at any and all wordwrap-caused * "virtual" lines. * * Inputs: widget from which we get the data to write; * startPos determines the first character to write out; * endPos determines the last character to write out; * buf is the character buffer into which we write. It * is assumed to be large enough - be careful. * addNewlines specifies whether to add '/n' to "virtual" lines. * Returns Nuthin' * * ***************************************************************************/ static char * CopySubstring( XmTextWidget widget, XmTextPosition startPos, XmTextPosition endPos, char *buf, Boolean addNewlines) { XmTextLineTable line_table = widget->text.line_table; int currLine, firstLine; char *pString, *pCurrChar, *pLastChar; int numToCopy; if(startPos < 0) startPos = 0; if(startPos > widget->text.last_position) startPos = widget->text.last_position; if(endPos < 0) endPos = 0; if(endPos > widget->text.last_position) endPos = widget->text.last_position; if(startPos > endPos) return buf; pString = XmTextGetString((Widget)widget); if(addNewlines == False) { pCurrChar = _DtEditorGetPointer(pString, startPos); pLastChar = _DtEditorGetPointer(pString, endPos); numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX); buf = StringAdd(buf, pCurrChar, numToCopy); } else { int *mb_str_loc, total, z, siz; char *bptr; mb_str_loc = (int *) XtMalloc(sizeof(int) * ((endPos-startPos)+1)); if (NULL == mb_str_loc) { /* Should figure out some way to pass back an error code. */ buf = CopySubstring(widget, startPos, endPos, buf, False); return buf; } /* * mb_str_loc[] is being used to replace the call * to _DtEditorGetPointer. That function used * mbtowc() to count the number of chars between the * beginning of pString and startChar. The problem * was that it sat in a loop and was also called for * every line, so it was SLOW. Now, we count once * and store the results in mb_str_loc[]. */ /* Because startPos may not always == 0: */ /* mb_str_loc[0] = startPos */ /* mb_str_loc[endPos - startPos] = endPos */ /* */ /* So when accessing items, dereference off of */ /* startPos. */ mb_str_loc[0] = 0; for(total=0, bptr=pString, z=1; z <= (endPos - startPos); bptr += siz, z++) { if (MB_CUR_MAX > 1) { if ( (siz = mblen(bptr, MB_CUR_MAX)) < 0) { siz = 1; total += 1; } else total += siz; } else { siz = 1; total += 1; } mb_str_loc[z] = total; } firstLine = currLine = _DtEditorGetLineIndex(widget, startPos); do { if(startPos > (XmTextPosition)line_table[currLine].start_pos) pCurrChar = pString + mb_str_loc[0]; else { z = line_table[currLine].start_pos; pCurrChar = pString + mb_str_loc[z - startPos]; } if(addNewlines == True && currLine > firstLine && line_table[currLine].virt_line != 0) { buf[0] = (char)'\n'; buf[1] = (char)'\0'; buf++; } if(currLine >= (widget->text.total_lines - 1)) pLastChar = pString + mb_str_loc[endPos - startPos]; else if((XmTextPosition)line_table[currLine + 1].start_pos <= endPos) { z = line_table[currLine+1].start_pos - 1; pLastChar = pString + mb_str_loc[z - startPos]; } else pLastChar = pString + mb_str_loc[endPos - startPos]; numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX); buf = StringAdd(buf, pCurrChar, numToCopy); currLine++; } while(currLine < widget->text.total_lines && (XmTextPosition)line_table[currLine].start_pos <= endPos); XtFree((char*)mb_str_loc); } XtFree(pString); return buf; } /************************************************************************* * * _DtEditorCopyDataOut - Writes the entire text editor buffer contents to * the specified character array. * * Inputs: tw, to supply the data. * buf, specifying the array to which to write the data. * *************************************************************************/ static char * _DtEditorCopyDataOut( XmTextWidget tw, char *buf, Boolean addNewlines) { buf = CopySubstring(tw, 0, tw->text.last_position, buf, addNewlines); return buf; } static DtEditorErrorCode getStringValue( DtEditorWidget editor, char **buf, Boolean insertNewlines) { XmTextWidget tw = (XmTextWidget) M_text(editor); int bufSize; char *lastChar; DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS; /* * Calculate the size of the buffer we need for the data. * 1. Start with MB_CUR_MAX for each char in the text. * 3. Add in 1 char for each line, if we have to insert newlines. * 4. Add 1 for a terminating NULL. */ bufSize = tw->text.last_position * MB_CUR_MAX; if(insertNewlines == True) bufSize += tw->text.total_lines; bufSize += 1; returnVal = Check4EnoughMemory(bufSize); if (DtEDITOR_NO_ERRORS != returnVal) return returnVal; *buf = (char *) XtMalloc(bufSize); lastChar = _DtEditorCopyDataOut(tw, *buf, insertNewlines); return returnVal; } /* end getStringValue */ static DtEditorErrorCode getDataValue( DtEditorWidget editor, void **buf, unsigned int *size, Boolean insertNewlines) { DtEditorErrorCode error; error = getStringValue(editor, (char **)buf, insertNewlines); *size = strlen( *buf ); /* remember, strlen doesn't count \0 at end */ return( error ); } /* end getDataValue */ static DtEditorErrorCode getWcharValue( DtEditorWidget editor, wchar_t **data, Boolean insertNewlines) { DtEditorErrorCode error; char *mb_value; wchar_t *pWchar_value; int num_char, result; size_t nbytes; error = getStringValue(editor, &mb_value, insertNewlines); if (error == DtEDITOR_NO_ERRORS) { /* * Allocate space for the wide character string */ num_char = _DtEditor_CountCharacters(mb_value, strlen(mb_value)) + 1; nbytes = (size_t) num_char * sizeof(wchar_t); error = Check4EnoughMemory(nbytes); if (DtEDITOR_NO_ERRORS != error) return error; pWchar_value = (wchar_t*) XtMalloc(nbytes); /* * Convert the multi-byte string to wide character */ result = mbstowcs(pWchar_value, mb_value, num_char*sizeof(wchar_t) ); if (result < 0) pWchar_value[0] = 0L; *data = pWchar_value; XtFree( mb_value ); } return( error ); } /* end getWcharValue */ /*************************************************************************** * * DtEditorGetContents - gets the contents of the DtEditor widget. * * Inputs: widget to retrieve the contents * * pointer to a data structure indicating how the retrieved * data should be formatted. Depending upon the type of format, * this structure will contain various fields: * string - a NULL pointer (char *) to hold the data * a new container will be created. * data - void pointer to hold the data, unsigned int for the * size of the data, * a Boolean indicating whether Newline characters should be * inserted at the end of each line, in string format. * a Boolean indicating whether the the unsaved changes * flag should be cleared. There may be times when an * application will want to request a copy of the contents * without effecting whether DtEditorCheckForUnsavedChanges * reports there are unsaved changes. * * Returns 0 - contents were retrieved sucessfully * !0 - an error occurred while retrieving the contents * * The structure passed in will be set according to the * requested format: * string - a \0-terminated string of characters with * optional Newlines * container - handle to a Bento container * data - the data, the size of the data * * The application is responsible for free'ing any data in the * above structure. * ***************************************************************************/ extern DtEditorErrorCode DtEditorGetContents( Widget widget, DtEditorContentRec *data, Boolean hardCarriageReturns, Boolean markContentsAsSaved ) { DtEditorErrorCode error = DtEDITOR_INVALID_TYPE; DtEditorWidget editor = (DtEditorWidget) widget; _DtWidgetToAppContext(widget); _DtAppLock(app); switch( data->type ) { case DtEDITOR_TEXT: { error = getStringValue( editor, &(data->value.string), hardCarriageReturns ); break; } case DtEDITOR_DATA: { error = getDataValue( editor, &(data->value.data.buf), &(data->value.data.length), hardCarriageReturns ); break; } case DtEDITOR_WCHAR: { error = getWcharValue( editor, &(data->value.wchar), hardCarriageReturns ); break; } default : { error = DtEDITOR_INVALID_TYPE; } } /* end switch */ /* * If there were no errors, mark there are now no unsaved changes (unless * we were told not to). */ if ( error == DtEDITOR_NO_ERRORS && markContentsAsSaved == True ) M_unreadChanges( editor ) = False; _DtAppUnlock(app); return( error ); } /*************************************************************************** * * DtEditorSaveContentsToFile - saves the contents of the DtEditor * widget to a disc file as string/data * or a CDE Document (Bento container). * * Inputs: widget to retrieve the contents * * filename - name of the file to read * a Boolean indicating whether the file should be * overwritten if it currently exists. * a Boolean indicating whether Newline characters should be * inserted at the end of each line (string format only). * a Boolean indicating whether the the unsaved changes * flag should be cleared. There may be times when an * application will want to request a copy of the contents * without effecting whether DtEditorCheckForUnsavedChanges * reports there are unsaved changes. * * Returns DtEDITOR_NO_ERRORS - contents were saved sucessfully * DtEDITOR_UNWRITABLE_FILE - file is write protected * DtEDITOR_WRITABLE_FILE - file exists and the * overwriteIfExists parameter is False. * DtEDITOR_SAVE_FAILED - write to the file failed; check * disk space, etc. * OR any errors from DtEditorGetContents * ***************************************************************************/ extern DtEditorErrorCode DtEditorSaveContentsToFile( Widget widget, char *fileName, Boolean overwriteIfExists, Boolean hardCarriageReturns, Boolean markContentsAsSaved ) { FILE *pFile; DtEditorContentRec cr; /* Structure for retrieving contents of widget */ DtEditorWidget editor = (DtEditorWidget) widget; DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME; _DtWidgetToAppContext(widget); _DtAppLock(app); /* * First, make sure we were given a name */ if (fileName && *fileName ) { /* * Can we save to the file? */ error = _DtEditorValidateFileAccess( fileName, WRITE_ACCESS ); if( error == DtEDITOR_NO_ERRORS || error == DtEDITOR_NONEXISTENT_FILE || error == DtEDITOR_WRITABLE_FILE ) { /* * Don't overwrite an existing file if we've been told not to */ if( error == DtEDITOR_WRITABLE_FILE && overwriteIfExists == False ) { _DtAppUnlock(app); return( error ); } /* * Open the file for writing */ if ( (pFile = fopen(fileName, "w")) == NULL ) { _DtAppUnlock(app); return( DtEDITOR_UNWRITABLE_FILE ); } else { /* * Save the unsaved changes flag so we can restore it if the write * to the file fails. */ Boolean saved_state = M_unreadChanges( editor ); /* * Now, get the contents of the widget and write it to the file, * depending upon the format requested. */ cr.type = DtEDITOR_DATA; error = DtEditorGetContents( widget, &cr, hardCarriageReturns, markContentsAsSaved ); if ( error == DtEDITOR_NO_ERRORS ) { /* * Write it to the file */ size_t size_written = fwrite( cr.value.data.buf, 1, cr.value.data.length, pFile ); if( cr.value.data.length != size_written ) error = DtEDITOR_SAVE_FAILED; XtFree( cr.value.data.buf ); } fclose(pFile); if( error == DtEDITOR_SAVE_FAILED ) { /* * Restore the unsaved changes flag since the save failed */ M_unreadChanges( editor ) = saved_state; } } /* end file is writable */ } /* end filename is valid */ } _DtAppUnlock(app); return( error ); } /* end DtEditorSaveContentsToFile */ /* * _DtEditorGetPointer returns a pointer to the _character_ * numbered by startChar within the string pString. * It accounts for possible multibyte chars. */ char * _DtEditorGetPointer( char *pString, int startChar) { char *bptr; int curChar, char_size; if(MB_CUR_MAX > 1) { for(bptr = pString, curChar = 0; curChar < startChar && *bptr != (char)'\0'; curChar++, bptr += char_size) { if ( (char_size = mblen(bptr, MB_CUR_MAX)) < 0) char_size = 1; } } else { bptr = pString + startChar; } return bptr; } /* end _DtEditorGetPointer */