Files
cdesktop/cde/programs/dtmail/dtmail/DtEditor.C
Lev Kujawski a6ea2a2d52 Centralize catgets() calls through MsgCat
CDE has relied upon catgets() implementations following a relaxed
interpretation of the XPG internationalization standard that ignored
-1, the standard error value returned by catopen, as the catalog
argument. However, this same behavior causes segmentation faults with
the musl C library.

This patch:

- Centralizes (with the exception of ToolTalk) all calls to catopen(),
  catgets(), and catclose() through MsgCat within the DtSvc library.
- Prevents calls to catgets() and catclose() that rely upon
  undefined behavior.
- Eliminates a number of bespoke catgets() wrappers, including multiple
  redundant caching implementations designed to work around a design
  peculiarity in HP/UX.
- Eases building CDE without XPG internationalization support by providing
  the appropriate macros.
2021-06-02 19:55:15 -06:00

839 lines
18 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
*/
/*
*+SNOTICE
*
* $TOG: DtEditor.C /main/11 1998/02/03 10:28:15 mgreess $
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special
* restrictions in a confidential disclosure agreement between
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
* document outside HP, IBM, Sun, USL, SCO, or Univel without
* Sun's specific written approval. This document and all copies
* and derivative works thereof must be returned or destroyed at
* Sun's request.
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*
*+ENOTICE
*/
#ifndef I_HAVE_NO_IDENT
#else
#endif
#ifdef DTEDITOR
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Text.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <X11/IntrinsicP.h>
#include <Xm/Text.h>
#include <Xm/CutPaste.h>
#include "Help.hh"
#include "RoamApp.h"
#include "DtEditor.hh"
#include "MailMsg.h" // DT_catd defined here
CDEM_DtWidgetEditor::CDEM_DtWidgetEditor(
Widget parent,
DtMailEditor *owner_of_editor
)
{
my_parent = parent;
my_owner = owner_of_editor;
my_text = NULL;
my_text_core = NULL;
_modified_text = NULL;
_modified_text_buflen = 0;
begin_ins_bracket = NULL;
indent_str = NULL;
end_ins_bracket = NULL;
_auto_show_cursor = FALSE;
_buffer = NULL;
_buf_len = (unsigned long) 0;
text_already_selected = FALSE;
}
CDEM_DtWidgetEditor::~CDEM_DtWidgetEditor()
{
if (my_text) {
// No DtEditor API equivalent
// Remove the callbacks first.
XtRemoveCallback(my_text, DtNtextSelectCallback,
&CDEM_DtWidgetEditor::text_selected_callback, this);
XtRemoveCallback(my_text, DtNtextDeselectCallback,
&CDEM_DtWidgetEditor::text_unselected_callback, this);
XtRemoveCallback( my_text, XmNhelpCallback, HelpTexteditCB, this ) ;
XtDestroyWidget(my_text);
}
if (_buffer) {
delete _buffer;
_buffer = NULL;
}
if(_modified_text ) {
if(_modified_text->ptr) {
free(_modified_text->ptr);
_modified_text->ptr = NULL;
}
free(_modified_text);
_modified_text = NULL;
}
if (NULL != indent_str)
free((void*) indent_str);
}
void
CDEM_DtWidgetEditor::initialize()
{
int i = 0;
Arg args[10];
int n = 0;
#if 0
short rows, cols;
DtMailEnv error;
DtMail::Session * d_session = theRoamApp.session()->session();
DtMail::MailRc * mailrc = d_session->mailRc(error);
const char * value = NULL;
mailrc->getValue(error, "popuplines", &value);
if (error.isSet()) {
value = strdup("24");
}
rows = strtol(value, NULL, 10);
if (NULL != value)
free((void*) value);
// If toolcols is set, overwrite the column width with "toolcols" value.
// Otherwise, default resource value will be used.
value = NULL;
mailrc->getValue(error, "toolcols", &value);
if (!error.isSet()){
cols = strtol(value, NULL, 10);
XtSetArg(args[i], DtNcolumns, cols); i++;
if (NULL != value)
free((void*) value);
} else {
/*
* Default XmNcolumns
* MB_CUR_MAX == 1 : SingleByteLanguage
* MB_CUR_MAX > 1 : MultiByteLanguage
*/
if ( MB_CUR_MAX == 1 )
value = "80";
else
value = "40";
cols = strtol(value, NULL, 10);
XtSetArg(args[i], DtNcolumns, cols); i++;
}
#endif
XtSetArg(args[i], DtNeditable, FALSE); i++;
XtSetArg(args[i], DtNrows, 24); i++;
if ( MB_CUR_MAX == 1 ) {
XtSetArg(args[i], DtNcolumns, 80); i++;
} else {
XtSetArg(args[i], DtNcolumns, 40); i++;
}
XtSetArg(args[i], DtNcursorPositionVisible, FALSE); i++;
my_text = DtCreateEditor(my_parent, "Text", args, i);
update_display_from_props();
XtAddCallback(my_text, DtNtextSelectCallback,
&CDEM_DtWidgetEditor::text_selected_callback, this);
XtAddCallback(my_text, DtNtextDeselectCallback,
&CDEM_DtWidgetEditor::text_unselected_callback, this);
XtAddCallback( my_text, XmNhelpCallback, HelpTexteditCB, this ) ;
XtAddEventHandler(my_text, ButtonPressMask,
FALSE, MenuButtonHandler,
(XtPointer) this);
XtManageChild(my_text);
}
char*
CDEM_DtWidgetEditor::get_contents()
{
DtEditorErrorCode status;
static DtEditorContentRec content;
content.type = DtEDITOR_TEXT;
// Get the contents with hardCarriageReturns = TRUE and
// markContentsAsSaved = TRUE.
/*
* If hardCarriageReturns = TRUE, the performace of DtEditorGetContents()
* suffers since according to man 3 DtEditorGetContents,
*
* The hardCarriageReturns argument, if set to True, indicates
* that the DtEditor widget should replace any soft line feeds
* (word wraps) with <newline>s when saving the data. When
* hardCarriageReturns is set to False, any line wrapped
* because it reaches the right edge of the window is saved as
* one complete line.
*
* And current default value of DtNwordWrap is TRUE. See dtmail/Dtmail.
* My temporary and non-good solution is
* - Change default to False.
* - If DtNwordWarp == TRUE,
* call DtEditorGetContents(my_text, &content, TRUE, TRUE)
* if not,
* call DtEditorGetContents(my_text, &content, False, TRUE)
* This value can be controllable by a resource file or Format menu.
*
* Goofy ? but...................;-(
*/
Arg args[1];
Boolean ww;
XtSetArg( args[0], DtNwordWrap, &ww );
XtGetValues( my_text, args, 1 );
status = DtEditorGetContents(my_text, &content, ww, TRUE);
return(content.value.string);
}
void
CDEM_DtWidgetEditor::set_contents(
const char *contents,
const unsigned long len
)
{
DtEditorContentRec content;
DtEditorErrorCode status;
this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
this->my_owner->stripCRLF(&_buffer, contents, len);
content.type = DtEDITOR_TEXT;
content.value.string = _buffer;
status = DtEditorSetContents(my_text, &content);
}
void
CDEM_DtWidgetEditor::set_contents(
const char *path
)
{
DtEditorSetContentsFromFile(my_text, (char *)path);
}
void
CDEM_DtWidgetEditor::clear_contents()
{
// Doesn't work yet. Work around with setting an empty string...
// DtEditorReset(my_text);
DtEditorContentRec content;
DtEditorErrorCode status;
content.type = DtEDITOR_TEXT;
content.value.string = NULL;
status = DtEditorSetContents(my_text, &content);
}
void
CDEM_DtWidgetEditor::append_to_contents(
const char *contents,
const unsigned long len
)
{
DtEditorContentRec rec;
rec.type = DtEDITOR_TEXT;
if ( contents[len - 1] == 0 ) {
rec.value.string = (char *)contents;
} else {
this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
this->my_owner->stripCRLF(&_buffer, contents, len);
rec.value.string = _buffer;
}
DtEditorInsert(my_text, &rec);
}
void
CDEM_DtWidgetEditor::append_to_contents(
const char *path
)
{
DtEditorAppendFromFile(my_text, (char *)path);
}
void
CDEM_DtWidgetEditor::append_at_cursor(
const char *path
)
{
DtEditorInsertFromFile(my_text, (char *) path);
}
void
CDEM_DtWidgetEditor::append_at_cursor(
const char *contents,
const unsigned long len
)
{
DtEditorContentRec rec;
rec.type = DtEDITOR_TEXT;
if ( contents[len - 1] == 0 ) {
rec.value.string = (char *)contents;
} else {
this->my_owner->needBuf(&_buffer, &_buf_len, len + 1);
this->my_owner->stripCRLF(&_buffer, contents, len);
rec.value.string = _buffer;
}
//DtEditorAppend(my_text, &rec);
// Fix for the defect 179186 05-25-95
// The above API will insert "contents" to the end of the buffer
// (appending). It should change to DtEditorInsert which insert
// string to the current position (the cursor's position)
DtEditorInsert(my_text, &rec);
}
Widget
CDEM_DtWidgetEditor::get_text_widget()
{
// We actually need to return the text widget contained
// within DtEditor. For now, just return the DtEditor.
return(my_text);
}
Pixel
CDEM_DtWidgetEditor::get_text_foreground()
{
Pixel fg;
XtVaGetValues(my_text,
DtNtextForeground, &fg,
NULL);
return(fg);
}
// DtEditor returns the bg color of the Form widget, not the
// text widget that the Form contains.
// This explains why the attachment pane color is that of the scroll bar...
// DtEditor needs to return the color of its text widget.
// OBTW, DtNtextBackground and DtNtextForeground don't work. They
// return uninitialized values
Pixel
CDEM_DtWidgetEditor::get_text_background()
{
Pixel bg;
XtVaGetValues(my_text,
DtNtextBackground, &bg,
NULL);
return(bg);
}
XmFontList
CDEM_DtWidgetEditor::get_text_fontList()
{
XmFontList fl;
XtVaGetValues(my_text,
DtNtextFontList, &fl,
NULL);
return(fl);
}
Dimension
CDEM_DtWidgetEditor::get_text_width()
{
Dimension wid;
XtVaGetValues(my_text,
XmNwidth, &wid,
NULL);
return (wid);
}
Widget
CDEM_DtWidgetEditor::get_editor()
{
return(my_text);
}
int
CDEM_DtWidgetEditor::get_columns()
{
short ncolumns;
XtVaGetValues(my_text, DtNcolumns, &ncolumns, NULL);
return (int) ncolumns;
}
int
CDEM_DtWidgetEditor::get_rows()
{
short nrows;
XtVaGetValues(my_text, DtNrows, &nrows, NULL);
return (int) nrows;
}
void
CDEM_DtWidgetEditor::set_editable(Boolean bval)
{
XtVaSetValues(my_text,
DtNeditable, bval,
DtNcursorPositionVisible, bval,
NULL);
}
void
CDEM_DtWidgetEditor::set_columns(int ncolumns)
{
XtVaSetValues(my_text, DtNcolumns, ncolumns, NULL);
}
void
CDEM_DtWidgetEditor::set_rows(int nrows)
{
XtVaSetValues(my_text, DtNrows, nrows, NULL);
}
void
CDEM_DtWidgetEditor::undo_edit()
{
DtEditorUndoEdit(my_text);
}
void
CDEM_DtWidgetEditor::cut_selection()
{
DtEditorCutToClipboard(my_text);
}
void
CDEM_DtWidgetEditor::copy_selection()
{
DtEditorCopyToClipboard(my_text);
}
void
CDEM_DtWidgetEditor::paste_from_clipboard()
{
DtEditorPasteFromClipboard(my_text);
}
void
CDEM_DtWidgetEditor::paste_special_from_clipboard(
Editor::InsertFormat format
)
{
int status;
unsigned long length, recvd;
char *clipboard_data;
Display *dpy = XtDisplayOfObject(my_text);
Window window = XtWindowOfObject(my_text);
do {
status = XmClipboardInquireLength(dpy, window, "STRING", &length);
} while (status == ClipboardLocked);
if (length == 0) {
return;
}
clipboard_data = XtMalloc((unsigned)length);
do {
status = XmClipboardRetrieve(
dpy, window, "STRING", clipboard_data,
length, &recvd, NULL
);
} while (status == ClipboardLocked);
if (status != ClipboardSuccess || recvd != length) {
// Couldn't get all
XtFree(clipboard_data);
return;
}
// Now modify the data such that the necessary formatting occurs
// within it. Bracketting will cause a line at the beginning and
// end of the data. Indenting will prepend a ">" before each line,
// realigning the lines if necessary.
// The results are stored in _modified_text so clipboard_data can
// be freed immediately after this call.
this->modifyData(clipboard_data, (unsigned) length, format);
XtFree(clipboard_data);
// Now copy the modified data stripped of CRLFs to a buffer.
// Put that buffer into the structure appropriate for DtEditor.
DtEditorContentRec rec;
rec.type = DtEDITOR_TEXT;
// Length needs to be reset since the text now contains
// new characters that do the necessary formatting.
length = _modified_text->length;
if ( _modified_text->ptr[(unsigned) length - 1] == 0 ) {
rec.value.string = (char *)_modified_text->ptr;
} else {
this->my_owner->needBuf(
&_buffer, &_buf_len,
(unsigned) length + 1
);
this->my_owner->stripCRLF(
&_buffer, _modified_text->ptr,
(unsigned) length);
rec.value.string = _buffer;
}
DtEditorInsert(my_text, &rec);
}
void
CDEM_DtWidgetEditor::clear_selection()
{
DtEditorClearSelection(my_text);
}
void
CDEM_DtWidgetEditor::delete_selection()
{
DtEditorDeleteSelection(my_text);
}
void
CDEM_DtWidgetEditor::set_word_wrap(
Boolean bval
)
{
XtVaSetValues(my_text, DtNwordWrap, bval, NULL);
}
void
CDEM_DtWidgetEditor::set_to_top()
{
XtVaSetValues(my_text,
DtNtopCharacter, 0,
DtNcursorPosition, 0,
NULL);
}
void
CDEM_DtWidgetEditor::text_selected_callback(
Widget,
void * clientData,
void *
)
{
CDEM_DtWidgetEditor *obj=(CDEM_DtWidgetEditor *) clientData;
obj->text_selected();
}
void
CDEM_DtWidgetEditor::text_unselected_callback(
Widget,
void * clientData,
void *
)
{
CDEM_DtWidgetEditor *obj=(CDEM_DtWidgetEditor *) clientData;
obj->text_unselected();
}
void
CDEM_DtWidgetEditor::text_selected()
{
if (!text_already_selected) {
text_already_selected = TRUE;
my_owner->owner()->text_selected();
}
}
void
CDEM_DtWidgetEditor::text_unselected()
{
my_owner->owner()->text_unselected();
text_already_selected = FALSE;
}
void
CDEM_DtWidgetEditor::find_change()
{
DtEditorInvokeFindChangeDialog(my_text);
}
void
CDEM_DtWidgetEditor::spell()
{
DtEditorInvokeSpellDialog(my_text);
}
void
CDEM_DtWidgetEditor::format()
{
DtEditorInvokeFormatDialog(my_text);
}
void
CDEM_DtWidgetEditor::auto_show_cursor_off()
{
}
void
CDEM_DtWidgetEditor::auto_show_cursor_restore()
{
}
void
CDEM_DtWidgetEditor::select_all()
{
DtEditorSelectAll(my_text);
}
void
CDEM_DtWidgetEditor::set_to_bottom()
{
}
int
CDEM_DtWidgetEditor::no_text()
{
char *text = get_contents();
int text_len = strlen(text);
int result = 1;
for ( int k = 0; k < text_len; k++ ) {
if ( isgraph(text[k]) ) {
result = 0;
break;
}
}
XtFree(text);
return result;
}
void
CDEM_DtWidgetEditor::disable_redisplay()
{
DtEditorDisableRedisplay(my_text);
}
void
CDEM_DtWidgetEditor::enable_redisplay()
{
DtEditorEnableRedisplay(my_text);
}
/*
* This fucntion modifies the pasted data
* with an indent prefix before each new line or brackets it.
*/
void
CDEM_DtWidgetEditor::modifyData(
char *sp, // source pointer to the insert string
int length, // length does not include '\0' char
Editor::InsertFormat insert_format
)
{
if(_modified_text == NULL)
_modified_text = (XmTextBlockRec *)calloc(1,sizeof(XmTextBlockRec));
char *maxsp = sp + length; // maxmimum source ptr
// Allocate memory rounded off to the nearest BUFINC
size_t size_req = (size_t)(((length/BUFINC)+1)*BUFINC);
if((_modified_text_buflen < size_req) ||
((_modified_text_buflen > CDEM_DtWidgetEditor::MAXBUFSZ) &&
(size_req < CDEM_DtWidgetEditor::MAXBUFSZ)) )
reallocPasteBuf(size_req);
if(_modified_text->ptr == NULL)
return; // No memory available
switch( insert_format) {
case IF_INDENTED:
{
DtMailEnv error;
int ip = 0;
// Get the indent prefix string
DtMail::Session *m_session = theRoamApp.session()->session();
m_session->mailRc(error)->getValue(error,"indentprefix", &indent_str);
if (error.isSet() || NULL == indent_str)
indent_str = strdup("> ");
size_t indlen = strlen(indent_str);
// Copy the src buf into dest, inserting indent before '\n'
while(sp < maxsp) {
// Make sure there is enough space
// for an indent prefix, one char and a terminating '\0'
if(!((ip+indlen+2) < _modified_text_buflen) ) {
size_req = (size_t)((((_modified_text_buflen +
indlen+2)/BUFINC)+1)*BUFINC);
reallocPasteBuf(size_req);
if(_modified_text->ptr == NULL)
return; // No memory available
}
// Copy the indent string at the beginning
if(!ip) {
memcpy(_modified_text->ptr, indent_str, indlen);
ip += indlen;
}
// Copy the next byte and check for new line
_modified_text->ptr[ip++] = *sp++;
if(*(sp-1) == '\n') {
memcpy(&_modified_text->ptr[ip], indent_str, indlen);
ip += indlen;
}
}
_modified_text->ptr[ip] = '\0'; // terminate with a null char
_modified_text->length = ip; // Do not include '\0' char in len
}
break;
case IF_BRACKETED:
{
if( !begin_ins_bracket)
begin_ins_bracket = CATGETS(DT_catd, 1, 201,
"\n------------- Begin Included Message -------------\n");
if(!end_ins_bracket)
end_ins_bracket = CATGETS(DT_catd, 1, 202,
"\n------------- End Included Message -------------\n");
size_t begin_len = strlen(begin_ins_bracket);
size_t end_len = strlen(end_ins_bracket);
// Make sure there is enough space
if((size_req = length + begin_len + end_len + 1) >
_modified_text_buflen) {
size_req = (size_t) ((((size_req)/BUFINC)+1)*BUFINC);
reallocPasteBuf(size_req);
}
if(_modified_text->ptr == NULL)
return;
strcpy(_modified_text->ptr, begin_ins_bracket);
strncat(_modified_text->ptr,sp,length);
strcat(_modified_text->ptr, end_ins_bracket);
_modified_text->length = end_len + begin_len + length;
}
break;
default:
break;
}
}
void
CDEM_DtWidgetEditor::MenuButtonHandler(
Widget ,
XtPointer cd,
XEvent *event,
Boolean *)
{
CDEM_DtWidgetEditor *obj = (CDEM_DtWidgetEditor *)cd;
if(event->xany.type != ButtonPress)
return;
XButtonEvent *be = (XButtonEvent *)event;
if(be->button == Button3)
obj->my_owner->owner()->postTextPopup(event);
}
#endif