cdesktopenv/cde/lib/DtWidget/EditAreaData.c

1881 lines
49 KiB
C

/*
* 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 <X11/Xutil.h>
#include <Xm/TextP.h>
#include <unistd.h>
#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 */