Home | History | Annotate | Line # | Download | only in src
      1 /*
      2 
      3 Copyright 1989, 1994, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24 
     25 */
     26 
     27 /*
     28  * AsciiSrc.c - AsciiSrc object. (For use with the text widget).
     29  *
     30  */
     31 
     32 #ifdef HAVE_CONFIG_H
     33 #include <config.h>
     34 #endif
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <ctype.h>
     38 #include <errno.h>
     39 #include <X11/IntrinsicP.h>
     40 #include <X11/StringDefs.h>
     41 #include <X11/Xos.h>
     42 #include <X11/Xfuncs.h>
     43 #include <X11/Xmu/CharSet.h>
     44 #include <X11/Xmu/Misc.h>
     45 #include <X11/Xaw/XawInit.h>
     46 #include <X11/Xaw/AsciiSrcP.h>
     47 #include <X11/Xaw/MultiSrcP.h>
     48 #ifndef OLDXAW
     49 #include <X11/Xaw/TextSinkP.h>
     50 #include <X11/Xaw/AsciiSinkP.h>
     51 #endif
     52 #include "Private.h"
     53 
     54 #include <sys/types.h>
     55 #include <sys/stat.h>
     56 #include <fcntl.h>
     57 
     58 #ifndef O_CLOEXEC
     59 #define O_CLOEXEC 0
     60 #endif
     61 
     62 #if (defined(ASCII_STRING) || defined(ASCII_DISK))
     63 #include <X11/Xaw/AsciiText.h>		/* for Widget Classes */
     64 #endif
     65 
     66 #define MAGIC_VALUE	((XawTextPosition)-1)
     67 #define streq(a, b)	(strcmp((a), (b)) == 0)
     68 
     69 /*
     70  * Class Methods
     71  */
     72 static void XawAsciiSrcClassInitialize(void);
     73 static void XawAsciiSrcDestroy(Widget);
     74 static void XawAsciiSrcGetValuesHook(Widget, ArgList, Cardinal*);
     75 static void XawAsciiSrcInitialize(Widget, Widget, ArgList, Cardinal*);
     76 static Boolean XawAsciiSrcSetValues(Widget, Widget, Widget,
     77 				    ArgList, Cardinal*);
     78 static XawTextPosition ReadText(Widget, XawTextPosition, XawTextBlock*, int);
     79 static int ReplaceText(Widget, XawTextPosition, XawTextPosition,
     80 		       XawTextBlock*);
     81 static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType,
     82 			    XawTextScanDirection, int, Bool);
     83 static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection,
     84 			      XawTextBlock*);
     85 
     86 /*
     87  * Prototypes
     88  */
     89 static Piece *AllocNewPiece(AsciiSrcObject, Piece*);
     90 static void BreakPiece(AsciiSrcObject, Piece*);
     91 static Boolean CvtAsciiTypeToString(Display*, XrmValuePtr, Cardinal*,
     92 				    XrmValuePtr, XrmValuePtr, XtPointer*);
     93 static void CvtStringToAsciiType(XrmValuePtr, Cardinal*,
     94 				 XrmValuePtr, XrmValuePtr);
     95 static Piece *FindPiece(AsciiSrcObject, XawTextPosition, XawTextPosition*);
     96 static void FreeAllPieces(AsciiSrcObject);
     97 static FILE *InitStringOrFile(AsciiSrcObject, Bool);
     98 static void LoadPieces(AsciiSrcObject, FILE*, char*);
     99 static void RemoveOldStringOrFile(AsciiSrcObject, Bool);
    100 static void RemovePiece(AsciiSrcObject, Piece*);
    101 static char * StorePiecesInString(AsciiSrcObject);
    102 static Bool WriteToFile(String, String, unsigned);
    103 static Bool WritePiecesToFile(AsciiSrcObject, String);
    104 static void GetDefaultPieceSize(Widget, int, XrmValue*);
    105 
    106 /*
    107  * More Prototypes
    108  */
    109 #ifdef ASCII_DISK
    110 Widget XawAsciiDiskSourceCreate(Widget, ArgList, Cardinal);
    111 #endif
    112 #ifdef ASCII_STRING
    113 Widget XawStringSourceCreate(Widget, ArgList, Cardinal);
    114 void XawTextSetLastPos(Widget, XawTextPosition);
    115 #endif
    116 
    117 /*
    118  * Initialization
    119  */
    120 #define offset(field) XtOffsetOf(AsciiSrcRec, ascii_src.field)
    121 static XtResource resources[] = {
    122   {
    123     XtNstring,
    124     XtCString,
    125     XtRString,
    126     sizeof(char*),
    127     offset(string),
    128     XtRString,
    129     NULL
    130   },
    131   {
    132     XtNtype,
    133     XtCType,
    134     XtRAsciiType,
    135     sizeof(XawAsciiType),
    136     offset(type),
    137     XtRImmediate,
    138     (XtPointer)XawAsciiString
    139   },
    140   {
    141     XtNdataCompression,
    142     XtCDataCompression,
    143     XtRBoolean,
    144     sizeof(Boolean),
    145     offset(data_compression),
    146     XtRImmediate,
    147     (XtPointer)True
    148   },
    149   {
    150     XtNpieceSize,
    151     XtCPieceSize,
    152     XtRInt,
    153     sizeof(XawTextPosition),
    154     offset(piece_size),
    155     XtRCallProc,
    156     (XtPointer)GetDefaultPieceSize
    157   },
    158 #ifdef OLDXAW
    159   {
    160     XtNcallback,
    161     XtCCallback,
    162     XtRCallback,
    163     sizeof(XtPointer),
    164     offset(callback),
    165     XtRCallback,
    166     (XtPointer)NULL
    167   },
    168 #endif
    169   {
    170     XtNuseStringInPlace,
    171     XtCUseStringInPlace,
    172     XtRBoolean,
    173     sizeof(Boolean),
    174     offset(use_string_in_place),
    175     XtRImmediate,
    176     (XtPointer)False
    177   },
    178   {
    179     XtNlength,
    180     XtCLength,
    181     XtRInt,
    182     sizeof(int),
    183     offset(ascii_length),
    184     XtRImmediate,
    185     (XtPointer)MAGIC_VALUE
    186   },
    187 #ifdef ASCII_DISK
    188   {
    189     XtNfile,
    190     XtCFile,
    191     XtRString,
    192     sizeof(String),
    193     offset(filename),
    194     XtRString,
    195     NULL
    196   },
    197 #endif /* ASCII_DISK */
    198 };
    199 #undef offset
    200 
    201 
    202 #define Superclass	(&textSrcClassRec)
    203 AsciiSrcClassRec asciiSrcClassRec = {
    204   /* object */
    205   {
    206     (WidgetClass)Superclass,		/* superclass */
    207     "AsciiSrc",				/* class_name */
    208     sizeof(AsciiSrcRec),		/* widget_size */
    209     XawAsciiSrcClassInitialize,		/* class_initialize */
    210     NULL,				/* class_part_initialize */
    211     False,				/* class_inited */
    212     XawAsciiSrcInitialize,		/* initialize */
    213     NULL,				/* initialize_hook */
    214     NULL,				/* realize */
    215     NULL,				/* actions */
    216     0,					/* num_actions */
    217     resources,				/* resources */
    218     XtNumber(resources),		/* num_resources */
    219     NULLQUARK,				/* xrm_class */
    220     False,				/* compress_motion */
    221     False,				/* compress_exposure */
    222     False,				/* compress_enterleave */
    223     False,				/* visible_interest */
    224     XawAsciiSrcDestroy,			/* destroy */
    225     NULL,				/* resize */
    226     NULL,				/* expose */
    227     XawAsciiSrcSetValues,		/* set_values */
    228     NULL,				/* set_values_hook */
    229     NULL,				/* set_values_almost */
    230     XawAsciiSrcGetValuesHook,		/* get_values_hook */
    231     NULL,				/* accept_focus */
    232     XtVersion,				/* version */
    233     NULL,				/* callback_private */
    234     NULL,				/* tm_table */
    235     NULL,				/* query_geometry */
    236     NULL,				/* display_accelerator */
    237     NULL,				/* extension */
    238   },
    239   /* text_src */
    240   {
    241     ReadText,				/* Read */
    242     ReplaceText,			/* Replace */
    243     Scan,				/* Scan */
    244     Search,				/* Search */
    245     XtInheritSetSelection,		/* SetSelection */
    246     XtInheritConvertSelection,		/* ConvertSelection */
    247 #ifndef OLDXAW
    248     NULL,
    249 #endif
    250   },
    251   /* ascii_src */
    252   {
    253     NULL,				/* extension */
    254   },
    255 };
    256 
    257 WidgetClass asciiSrcObjectClass = (WidgetClass)&asciiSrcClassRec;
    258 
    259 static XrmQuark Qstring, Qfile;
    260 
    261 /*
    262  * Implementation
    263  */
    264 /*
    265  * Function:
    266  *	XawAsciiSrcClassInitialize()
    267  *
    268  * Description:
    269  *	  Initializes the asciiSrcObjectClass and install the converters for
    270  *	AsciiType <-> String.
    271  */
    272 static void
    273 XawAsciiSrcClassInitialize(void)
    274 {
    275     XawInitializeWidgetSet();
    276     Qstring = XrmPermStringToQuark(XtEstring);
    277     Qfile = XrmPermStringToQuark(XtEfile);
    278     XtAddConverter(XtRString, XtRAsciiType, CvtStringToAsciiType, NULL, 0);
    279     XtSetTypeConverter(XtRAsciiType, XtRString, CvtAsciiTypeToString,
    280 		       NULL, 0, XtCacheNone, NULL);
    281 }
    282 
    283 /*
    284  * Function:
    285  *	XawAsciiSrcInitialize
    286  *
    287  * Parameters:
    288  *	request	 - widget requested by the argument list
    289  *	cnew	 - new widget with both resource and non resource values
    290  *	args	 - (unused)
    291  *	num_args - (unused)
    292  *
    293  * Description:
    294  *	Initializes the ascii src object.
    295  */
    296 /*ARGSUSED*/
    297 static void
    298 XawAsciiSrcInitialize(Widget request _X_UNUSED, Widget cnew,
    299 		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    300 {
    301     AsciiSrcObject src = (AsciiSrcObject)cnew;
    302     FILE *file;
    303 
    304     /*
    305      * Set correct flags (override resources) depending upon widget class
    306      */
    307     src->text_src.text_format = (XrmQuark)XawFmt8Bit;
    308 
    309 #ifdef ASCII_DISK
    310     if (XtIsSubclass(XtParent(cnew), asciiDiskWidgetClass)) {
    311 	src->ascii_src.type = XawAsciiFile;
    312 	src->ascii_src.string = src->ascii_src.filename;
    313     }
    314 #endif
    315 
    316 #ifdef ASCII_STRING
    317     if (XtIsSubclass(XtParent(cnew), asciiStringWidgetClass)) {
    318 	src->ascii_src.use_string_in_place = True;
    319 	src->ascii_src.type = XawAsciiString;
    320     }
    321 #endif
    322 
    323 #ifdef OLDXAW
    324     src->ascii_src.changes = False;
    325 #else
    326     src->text_src.changed = False;
    327 #endif
    328     src->ascii_src.allocated_string = False;
    329 
    330     if (src->ascii_src.use_string_in_place && src->ascii_src.string == NULL)
    331 	src->ascii_src.use_string_in_place = False;
    332 
    333     file = InitStringOrFile(src, src->ascii_src.type == XawAsciiFile);
    334     LoadPieces(src, file, NULL);
    335 
    336     if (file != NULL)
    337 	fclose(file);
    338 }
    339 
    340 /*
    341  * Function:
    342  *	ReadText
    343  *
    344  * Parameters:
    345  *	w	- AsciiSource widget
    346  *	pos	- position of the text to retrieve.
    347  *	text	- text block that will contain returned text
    348  *	length	- maximum number of characters to read
    349  *
    350  * Description:
    351  *	This function reads the source.
    352  *
    353  * Returns:
    354  *	The character position following the retrieved text.
    355  */
    356 static XawTextPosition
    357 ReadText(Widget w, XawTextPosition pos, XawTextBlock *text, int length)
    358 {
    359     AsciiSrcObject src = (AsciiSrcObject)w;
    360     XawTextPosition count, start;
    361     Piece *piece;
    362 #ifndef OLDXAW
    363     XawTextAnchor *anchor;
    364     XawTextEntity *entity;
    365     XawTextPosition offset, end = pos + length;
    366     Bool state;
    367 
    368     end = XawMin(end, src->ascii_src.length);
    369     while ((state = XawTextSourceAnchorAndEntity(w, pos, &anchor, &entity)) &&
    370 	(entity->flags & XAW_TENTF_HIDE))
    371 	pos = anchor->position + entity->offset + entity->length;
    372     if (state == False ||
    373 	!(entity->flags & XAW_TENTF_REPLACE)) {
    374 	while (entity) {
    375 	    offset = anchor->position + entity->offset;
    376 	    if (offset >= end)
    377 		break;
    378 	    if (offset > pos &&
    379 		(entity->flags & (XAW_TENTF_HIDE | XAW_TENTF_REPLACE))) {
    380 		end = XawMin(end, offset);
    381 		break;
    382 	    }
    383 	    if ((entity = entity->next) == NULL &&
    384 		(anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)
    385 		entity = anchor->entities;
    386 	}
    387     }
    388     else if (state && (entity->flags & XAW_TENTF_REPLACE) && pos < end) {
    389 	XawTextBlock *block = (XawTextBlock*)entity->data;
    390 
    391 	offset = anchor->position + entity->offset;
    392 	end = XawMin(end, offset + block->length);
    393 	if ((length = (int)(end - pos)) < 0)
    394 	    length = 0;
    395 	text->length = length;
    396 	text->format = XawFmt8Bit;
    397 	if (length == 0) {
    398 	    text->firstPos = (int)(end = (offset + entity->length));
    399 	    text->ptr = (char*)"";
    400 	}
    401 	else {
    402 	    text->firstPos = (int)pos;
    403 	    text->ptr = block->ptr + (pos - offset);
    404 	    if (pos + length < offset + block->length)
    405 		end = pos + length;	/* there is data left to be read */
    406 	    else
    407 		end = offset + entity->length;
    408 	}
    409 
    410 	return (end);
    411     }
    412 
    413     if ((length = (int)(end - pos)) < 0)
    414 	length = 0;
    415 #endif
    416 
    417     piece = FindPiece(src, pos, &start);
    418     text->firstPos = (int)pos;
    419     text->ptr = piece->text + (pos - start);
    420     count = piece->used - (pos - start);
    421     text->length = (int)(Max(0, (length > count) ? count : length));
    422     text->format = XawFmt8Bit;
    423 
    424     return (pos + text->length);
    425 }
    426 
    427 /*
    428  * Function:
    429  *	ReplaceText
    430  *
    431  * Parameters:
    432  *	w	 - AsciiSource object
    433  *	startPos - ends of text that will be replaced
    434  *	endPos	 - ""
    435  *	text	 - new text to be inserted into buffer at startPos
    436  *
    437  * Description:
    438  *	Replaces a block of text with new text.
    439  *
    440  * Returns:
    441  *	XawEditDone on success, XawEditError otherwise
    442  */
    443 /*ARGSUSED*/
    444 static int
    445 ReplaceText(Widget w, XawTextPosition startPos, XawTextPosition endPos,
    446 	    XawTextBlock *text)
    447 {
    448     AsciiSrcObject src = (AsciiSrcObject)w;
    449     Piece *start_piece, *end_piece, *temp_piece;
    450     XawTextPosition start_first, end_first;
    451     int length, firstPos;
    452 
    453     /*
    454      * Editing a read only source is not allowed
    455      */
    456     if (src->text_src.edit_mode == XawtextRead)
    457 	return (XawEditError);
    458 
    459     if ((start_piece = FindPiece(src, startPos, &start_first)) == NULL)
    460 	return XawEditError;
    461     if ((end_piece = FindPiece(src, endPos, &end_first)) == NULL)
    462 	return XawEditError;
    463 
    464 #ifndef OLDXAW
    465     /*
    466      * This is a big hack, but I can't think about a clever way to know
    467      * if the character being moved forward has a negative lbearing.
    468      *
    469      */
    470     if (start_piece->used) {
    471 	int i;
    472 
    473 	for (i = 0; i < (int)src->text_src.num_text; i++) {
    474 	    int line;
    475 	    TextWidget ctx = (TextWidget)src->text_src.text[i];
    476 
    477 	    for (line = 0; line < ctx->text.lt.lines; line++)
    478 		if (startPos < ctx->text.lt.info[line + 1].position)
    479 		    break;
    480 	    if (i < ctx->text.lt.lines &&
    481 		startPos > ctx->text.lt.info[i].position) {
    482 		AsciiSinkObject sink = (AsciiSinkObject)ctx->text.sink;
    483 		XawTextAnchor *anchor;
    484 		XawTextEntity *entity;
    485 		XawTextProperty *property;
    486 		XFontStruct *font;
    487 
    488 		if (XawTextSourceAnchorAndEntity(w, startPos, &anchor, &entity) &&
    489 		    (property = XawTextSinkGetProperty(ctx->text.sink,
    490 						       entity->property)) != NULL &&
    491 		    (property->mask & XAW_TPROP_FONT))
    492 		    font = property->font;
    493 		else
    494 		    font = sink->ascii_sink.font;
    495 
    496 		if (font->min_bounds.lbearing < 0) {
    497 		    int lbearing = font->min_bounds.lbearing;
    498 		    unsigned char c = *(unsigned char*)
    499 			(start_piece->text + (startPos - start_first));
    500 
    501 		    if (c == '\t' || c == '\n')
    502 			c = ' ';
    503 		    else if ((c & 0177) < XawSP || c == 0177) {
    504 			if (sink->ascii_sink.display_nonprinting)
    505 			    c = (unsigned char)(c > 0177 ? '\\' : c + '^');
    506 			else
    507 			    c = ' ';
    508 		    }
    509 		    if (font->per_char &&
    510 			(c >= font->min_char_or_byte2 && c <= font->max_char_or_byte2))
    511 			lbearing = font->per_char[c - font->min_char_or_byte2].lbearing;
    512 		    if (lbearing < 0)
    513 			_XawTextNeedsUpdating(ctx, startPos - 1, startPos);
    514 		}
    515 	    }
    516 	}
    517     }
    518 
    519 
    520 #endif
    521 
    522     /*
    523      * Remove Old Stuff
    524      */
    525     if (start_piece != end_piece) {
    526 	if ((temp_piece = start_piece->next) == NULL)
    527 	    return XawEditError;
    528 
    529 	/*
    530 	 * If empty and not the only piece then remove it.
    531 	 */
    532 	if (((start_piece->used = startPos - start_first) == 0)
    533 	    && !(start_piece->next == NULL && start_piece->prev == NULL))
    534 	    RemovePiece(src, start_piece);
    535 
    536 	while (temp_piece != end_piece) {
    537 	    temp_piece = temp_piece->next;
    538 	    RemovePiece(src, temp_piece->prev);
    539 	}
    540 
    541 	end_piece->used -= endPos - end_first;
    542 	if (end_piece->used != 0)
    543 	    memmove(end_piece->text, end_piece->text + endPos - end_first,
    544 		    (unsigned)end_piece->used);
    545     }
    546     else {		    /* We are fully in one piece */
    547 	if ((start_piece->used -= endPos - startPos) == 0) {
    548 	    if (!(start_piece->next == NULL && start_piece->prev == NULL))
    549 		RemovePiece(src, start_piece);
    550 	}
    551 	else {
    552 	    memmove(start_piece->text + (startPos - start_first),
    553 		    start_piece->text + (endPos - start_first),
    554 		    (unsigned)(start_piece->used - (startPos - start_first)));
    555 	    if (src->ascii_src.use_string_in_place
    556 		&& src->ascii_src.length - (endPos - startPos)
    557 		< src->ascii_src.piece_size - 1)
    558 		start_piece->text[src->ascii_src.length - (endPos - startPos)] =
    559 		    '\0';
    560 	}
    561     }
    562 
    563     src->ascii_src.length += -(endPos - startPos) + text->length;
    564 
    565     if ( text->length != 0) {
    566 	/*
    567 	 * Put in the New Stuff
    568 	 */
    569 	start_piece = FindPiece(src, startPos, &start_first);
    570 
    571 	length = text->length;
    572 	firstPos = text->firstPos;
    573 
    574 	while (length > 0) {
    575 	    char *ptr;
    576 	    int fill;
    577 
    578 	    if (src->ascii_src.use_string_in_place) {
    579 		if (start_piece->used == src->ascii_src.piece_size - 1) {
    580 		    /*
    581 		     * If we are in ascii string emulation mode. Then the
    582 		     *	string is not allowed to grow
    583 		     */
    584 		    start_piece->used = src->ascii_src.length =
    585 			src->ascii_src.piece_size - 1;
    586 		    start_piece->text[src->ascii_src.length] = '\0';
    587 		    return (XawEditError);
    588 		}
    589 	    }
    590 
    591 	    if (start_piece->used == src->ascii_src.piece_size) {
    592 		BreakPiece(src, start_piece);
    593 		start_piece = FindPiece(src, startPos, &start_first);
    594 	    }
    595 
    596 	    fill = Min((int)(src->ascii_src.piece_size - start_piece->used),
    597 		       length);
    598 
    599 	    ptr = start_piece->text + (startPos - start_first);
    600 	    memmove(ptr + fill, ptr,
    601 		    (unsigned)(start_piece->used - (startPos - start_first)));
    602 	    memcpy(ptr, text->ptr + firstPos, (unsigned)fill);
    603 
    604 	    startPos += fill;
    605 	    firstPos += fill;
    606 	    start_piece->used += fill;
    607 	    length -= fill;
    608 	}
    609     }
    610 
    611     if (src->ascii_src.use_string_in_place)
    612 	start_piece->text[start_piece->used] = '\0';
    613 
    614 #ifdef OLDXAW
    615     src->ascii_src.changes = True;
    616     XtCallCallbacks(w, XtNcallback, NULL);
    617 #endif
    618 
    619     return (XawEditDone);
    620 }
    621 
    622 /*
    623  * Function:
    624  *	Scan
    625  *
    626  * Parameters:
    627  *	w	 - AsciiSource object
    628  *	position - position to start scanning
    629  *	type	 - type of thing to scan for
    630  *	dir	 - direction to scan
    631  *		   count - which occurrence if this thing to search for.
    632  *		   include - whether or not to include the character found in
    633  *		   the position that is returned
    634  *
    635  * Description:
    636  *	Scans the text source for the number and type of item specified.
    637  *
    638  * Returns:
    639  *	The position of the item found
    640  *
    641  * Note:
    642  *	  While there are only 'n' characters in the file there are n+1
    643  *	 possible cursor positions (one before the first character and
    644  *	one after the last character
    645  */
    646 static XawTextPosition
    647 Scan(Widget w, register XawTextPosition position, XawTextScanType type,
    648      XawTextScanDirection dir, int count, Bool include)
    649 {
    650     AsciiSrcObject src = (AsciiSrcObject)w;
    651     Piece *piece;
    652     XawTextPosition first, first_eol_position = 0;
    653     register char *ptr, *lim;
    654     register int cnt = count;
    655     register unsigned char c;
    656 
    657     if (dir == XawsdLeft) {
    658 	if (position <= 0)
    659 	    return (0);
    660 	--position;
    661     }
    662     else if (position >= src->ascii_src.length)
    663 	return (src->ascii_src.length);
    664 
    665     piece = FindPiece(src, position, &first);
    666     if (piece->used == 0)
    667 	return (0);
    668 
    669     ptr = (position - first) + piece->text;
    670 
    671     if (dir == XawsdRight) {
    672 	lim = piece->text + piece->used;
    673 	switch (type) {
    674 	    case XawstEOL:
    675 	    case XawstParagraph:
    676 	    case XawstWhiteSpace:
    677 	    case XawstAlphaNumeric:
    678 		for (; cnt > 0; cnt--) {
    679 		    Bool non_space = False, first_eol = True;
    680 
    681 		    /*CONSTCOND*/
    682 		    while (True) {
    683 			if (ptr >= lim) {
    684 			    piece = piece->next;
    685 			    if (piece == NULL)	/* End of text */
    686 				return (src->ascii_src.length);
    687 			    ptr = piece->text;
    688 			    lim = piece->text + piece->used;
    689 			}
    690 
    691 			c = (unsigned char)*ptr++;
    692 			++position;
    693 
    694 			if (type == XawstEOL) {
    695 			    if (c == '\n')
    696 				break;
    697 			}
    698 			else if (type == XawstAlphaNumeric) {
    699 			    if (!isalnum(c)) {
    700 				if (non_space)
    701 				    break;
    702 			    }
    703 			    else
    704 				non_space = True;
    705 			}
    706 			else if (type == XawstWhiteSpace) {
    707 			    if (isspace(c)) {
    708 				if (non_space)
    709 				    break;
    710 			    }
    711 			    else
    712 				non_space = True;
    713 			}
    714 			else {	/* XawstParagraph */
    715 			    if (first_eol) {
    716 				if (c == '\n') {
    717 				    first_eol_position = position;
    718 				    first_eol = False;
    719 				}
    720 			    }
    721 			    else if (c == '\n')
    722 				break;
    723 			    else if (!isspace(c))
    724 				first_eol = True;
    725 			}
    726 		    }
    727 		}
    728 		break;
    729 	    case XawstPositions:
    730 		position += count;
    731 		return (position < src->ascii_src.length ?
    732 			position : src->ascii_src.length);
    733 	    case XawstAll:
    734 		return (src->ascii_src.length);
    735 	    default:
    736 		break;
    737 	}
    738 	if (!include) {
    739 	    if (type == XawstParagraph)
    740 		position = first_eol_position;
    741 	    if (count)
    742 		--position;
    743 	}
    744     }
    745     else {
    746 	lim = piece->text;
    747 	switch (type) {
    748 	    case XawstEOL:
    749 	    case XawstParagraph:
    750 	    case XawstWhiteSpace:
    751 	    case XawstAlphaNumeric:
    752 		for (; cnt > 0; cnt--) {
    753 		    Bool non_space = False, first_eol = True;
    754 
    755 		    /*CONSTCOND*/
    756 		    while (True) {
    757 			if (ptr < lim) {
    758 			    piece = piece->prev;
    759 			    if (piece == NULL)	/* Beginning of text */
    760 				return (0);
    761 			    ptr = piece->text + piece->used - 1;
    762 			    lim = piece->text;
    763 			}
    764 
    765 			c = (unsigned char)*ptr--;
    766 			--position;
    767 
    768 			if (type == XawstEOL) {
    769 			    if (c == '\n')
    770 				break;
    771 			}
    772 			else if (type == XawstAlphaNumeric) {
    773 			    if (!isalnum(c)) {
    774 				if (non_space)
    775 				    break;
    776 			    }
    777 			    else
    778 				non_space = True;
    779 			}
    780 			else if (type == XawstWhiteSpace) {
    781 			    if (isspace(c)) {
    782 				if (non_space)
    783 				    break;
    784 			    }
    785 			    else
    786 				non_space = True;
    787 			}
    788 			else {	/* XawstParagraph */
    789 			    if (first_eol) {
    790 				if (c == '\n') {
    791 				    first_eol_position = position;
    792 				    first_eol = False;
    793 				}
    794 			    }
    795 			    else if (c == '\n')
    796 				break;
    797 			    else if (!isspace(c))
    798 				first_eol = True;
    799 			}
    800 		    }
    801 		}
    802 		break;
    803 	    case XawstPositions:
    804 		position -= count - 1;
    805 		return (position > 0 ? position : 0);
    806 	    case XawstAll:
    807 		return (0);
    808 	    default:
    809 		break;
    810 	}
    811 	if (!include) {
    812 	    if (type == XawstParagraph)
    813 		position = first_eol_position;
    814 	    if (count)
    815 		++position;
    816 	}
    817 	position++;
    818     }
    819 
    820     return (position);
    821 }
    822 
    823 /*
    824  * Function:
    825  *	Search
    826  *
    827  * Parameters:
    828  *	w	 - AsciiSource object
    829  *	position - the position to start scanning
    830  *	dir	 - direction to scan
    831  *	text	 - text block to search for
    832  *
    833  * Description:
    834  *	Searches the text source for the text block passed.
    835  *
    836  * Returns:
    837  *	The position of the item found
    838  */
    839 static XawTextPosition
    840 Search(Widget w, register XawTextPosition position, XawTextScanDirection dir,
    841        XawTextBlock *text)
    842 {
    843     AsciiSrcObject src = (AsciiSrcObject)w;
    844     register int count = 0;
    845     register char *ptr, c;
    846     char *str;
    847     Piece *piece;
    848     char *buf;
    849     XawTextPosition first;
    850     int cnt, case_sensitive;
    851 
    852     if (dir == XawsdLeft) {
    853 	if (position == 0)
    854 	    return (XawTextSearchError);
    855 	position--;
    856     }
    857 
    858     buf = XtMalloc((unsigned)sizeof(unsigned char) * (unsigned)text->length);
    859     memcpy(buf, text->ptr, (unsigned)text->length);
    860     piece = FindPiece(src, position, &first);
    861     ptr = (position - first) + piece->text;
    862     case_sensitive = text->firstPos;
    863 
    864     if (dir == XawsdRight) {
    865 	str = buf;
    866 	c = *str;
    867 	/*CONSTCOND*/
    868 	while (1) {
    869 	    if (*ptr++ == c
    870 		|| (case_sensitive && isalpha((unsigned char)c) && isalpha((unsigned char)ptr[-1])
    871 		    && toupper((unsigned char)c) == toupper((unsigned char)ptr[-1]))) {
    872 		if (++count == text->length)
    873 		    break;
    874 		c = *++str;
    875 	    }
    876 	    else if (count) {
    877 		ptr -= count;
    878 		str -= count;
    879 		position -= count;
    880 		count = 0;
    881 		c = *str;
    882 
    883 		if (ptr < piece->text) {
    884 		    do {
    885 			cnt = (int)(piece->text - ptr);
    886 			piece = piece->prev;
    887 			if (piece == NULL) {
    888 			    XtFree(buf);
    889 			    return (XawTextSearchError);
    890 			}
    891 			ptr = piece->text + piece->used - cnt;
    892 		    } while (ptr < piece->text);
    893 		}
    894 	    }
    895 	    position++;
    896 	    if (ptr >= (piece->text + piece->used)) {
    897 		do {
    898 		    cnt = (int)(ptr - (piece->text + piece->used));
    899 		    piece = piece->next;
    900 		    if (piece == NULL) {
    901 			XtFree(buf);
    902 			return (XawTextSearchError);
    903 		    }
    904 		    ptr = piece->text + cnt;
    905 		} while (ptr >= (piece->text + piece->used));
    906 	    }
    907 	}
    908 
    909 	position -= text->length - 1;
    910     }
    911     else {
    912 	str = buf + text->length - 1;
    913 	c = *str;
    914 	/*CONSTCOND*/
    915 	while (1) {
    916 	    if (*ptr-- == c
    917 		|| (case_sensitive && isalpha((unsigned char)c) && isalpha((unsigned char)ptr[1])
    918 		    && toupper((unsigned char)c) == toupper((unsigned char)ptr[1]))) {
    919 		if (++count == text->length)
    920 		    break;
    921 		c = *--str;
    922 	    }
    923 	    else if (count) {
    924 		ptr += count;
    925 		str += count;
    926 		position += count;
    927 		count = 0;
    928 		c = *str;
    929 
    930 		if (ptr >= (piece->text + piece->used)) {
    931 		    do {
    932 			cnt = (int)(ptr - (piece->text + piece->used));
    933 			piece = piece->next;
    934 			if (piece == NULL) {
    935 			    XtFree(buf);
    936 			    return (XawTextSearchError);
    937 			}
    938 			ptr = piece->text + cnt;
    939 		    } while (ptr >= (piece->text + piece->used));
    940 		}
    941 	    }
    942 	    position--;
    943 	    if (ptr < piece->text) {
    944 		do {
    945 		    cnt = (int)(piece->text - ptr);
    946 		    piece = piece->prev;
    947 		    if (piece == NULL) {
    948 			XtFree(buf);
    949 			return (XawTextSearchError);
    950 		    }
    951 		    ptr = piece->text + piece->used - cnt;
    952 		} while (ptr < piece->text);
    953 	    }
    954 	}
    955     }
    956 
    957     XtFree(buf);
    958 
    959     return (position);
    960 }
    961 
    962 /*
    963  * Function:
    964  *	XawAsciiSrcSetValues
    965  *
    966  * Parameters:
    967  *	current  - current state of the widget
    968  *	request  - what was requested
    969  *	cnew	 - what the widget will become
    970  *	args	 - representation of changed resources
    971  *	num_args - number of resources that have changed
    972  *
    973  * Description:
    974  *	Sets the values for the AsciiSource.
    975  *
    976  * Returns:
    977  *	True if redisplay is needed
    978  */
    979 static Boolean
    980 XawAsciiSrcSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
    981 		     ArgList args, Cardinal *num_args)
    982 {
    983     AsciiSrcObject src = (AsciiSrcObject)cnew;
    984     AsciiSrcObject old_src = (AsciiSrcObject)current;
    985     Bool total_reset = False, string_set = False;
    986     Cardinal i;
    987 
    988     if (old_src->ascii_src.use_string_in_place
    989 	!= src->ascii_src.use_string_in_place) {
    990 	XtAppWarning(XtWidgetToApplicationContext(cnew),
    991 		     "AsciiSrc: The XtNuseStringInPlace resource may "
    992 		     "not be changed.");
    993 	src->ascii_src.use_string_in_place =
    994 	    old_src->ascii_src.use_string_in_place;
    995     }
    996 
    997     for (i = 0; i < *num_args ; i++)
    998 	if (streq(args[i].name, XtNstring)) {
    999 	    string_set = True;
   1000 	    break;
   1001 	}
   1002 
   1003     if (string_set || (old_src->ascii_src.type != src->ascii_src.type)) {
   1004 	FILE *file;
   1005 
   1006 	RemoveOldStringOrFile(old_src, string_set); /* remove old info */
   1007 	file = InitStringOrFile(src, string_set);   /* Init new info */
   1008 	LoadPieces(src, file, NULL);   /* load new info into internal buffers */
   1009 	if (file != NULL)
   1010 	    fclose(file);
   1011 #ifndef OLDXAW
   1012 	for (i = 0; i < src->text_src.num_text; i++)
   1013 	    /* Tell text widget what happened */
   1014 	    XawTextSetSource(src->text_src.text[i], cnew, 0);
   1015 #else
   1016 	XawTextSetSource(XtParent(cnew), cnew, 0);
   1017 #endif
   1018 	total_reset = True;
   1019     }
   1020 
   1021     if (old_src->ascii_src.ascii_length != src->ascii_src.ascii_length)
   1022 	src->ascii_src.piece_size = src->ascii_src.ascii_length + 1;
   1023 
   1024     if (!total_reset &&
   1025 	old_src->ascii_src.piece_size != src->ascii_src.piece_size) {
   1026 	char * string = StorePiecesInString(old_src);
   1027 
   1028 	FreeAllPieces(old_src);
   1029 	LoadPieces(src, NULL, string);
   1030 	XtFree(string);
   1031     }
   1032 
   1033     return (False);
   1034 }
   1035 
   1036 /*
   1037  * Function:
   1038  *	XawAsciiSrcGetValuesHook
   1039  *
   1040  * Parameters:
   1041  *	w	 - AsciiSource Widget
   1042  *	args	 - argument list
   1043  *	num_args - number of args
   1044  *
   1045  * Description:
   1046  *	  This is a get values hook routine that sets the
   1047  *		     values specific to the ascii source.
   1048  */
   1049 static void
   1050 XawAsciiSrcGetValuesHook(Widget w, ArgList args, Cardinal *num_args)
   1051 {
   1052     AsciiSrcObject src = (AsciiSrcObject)w;
   1053     Cardinal i;
   1054 
   1055     if (src->ascii_src.type == XawAsciiString) {
   1056 	for (i = 0; i < *num_args ; i++)
   1057 	    if (streq(args[i].name, XtNstring)) {
   1058 		if (src->ascii_src.use_string_in_place)
   1059 		    *((char **)args[i].value) = src->ascii_src.first_piece->text;
   1060 		else if (XawAsciiSave(w))   /* If save successful */
   1061 		    *((char **)args[i].value) = src->ascii_src.string;
   1062 		break;
   1063 	    }
   1064 	}
   1065     }
   1066 
   1067 /*
   1068  * Function:
   1069  *	XawAsciiSrcDestroy
   1070  *
   1071  * Parameters:
   1072  *	src - Ascii source object to free
   1073  *
   1074  * Description:
   1075  *	Destroys an ascii source (frees all data)
   1076  */
   1077 static void
   1078 XawAsciiSrcDestroy(Widget w)
   1079 {
   1080     RemoveOldStringOrFile((AsciiSrcObject) w, True);
   1081 }
   1082 
   1083 /*
   1084  * Public routines
   1085  */
   1086 /*
   1087  * Function:
   1088  *	XawAsciiSourceFreeString
   1089  *
   1090  * Parameters:
   1091  *	w - AsciiSrc widget
   1092  *
   1093  * Description:
   1094  *	  Frees the string returned by a get values call
   1095  *		     on the string when the source is of type string.
   1096  */
   1097 void
   1098 XawAsciiSourceFreeString(Widget w)
   1099 {
   1100     AsciiSrcObject src = (AsciiSrcObject)w;
   1101 
   1102     /* If the src is really a multi, call the multi routine */
   1103     if (XtIsSubclass(w, multiSrcObjectClass)) {
   1104 	_XawMultiSourceFreeString(w);
   1105 	return;
   1106     }
   1107     else if (!XtIsSubclass(w, asciiSrcObjectClass)) {
   1108 	XtErrorMsg("bad argument", "asciiSource", "XawError",
   1109 		   "XawAsciiSourceFreeString's parameter must be "
   1110 		   "an asciiSrc or multiSrc.",
   1111 		   NULL, NULL);
   1112     }
   1113 
   1114     if (src->ascii_src.allocated_string && src->ascii_src.type != XawAsciiFile) {
   1115 	src->ascii_src.allocated_string = False;
   1116 	XtFree(src->ascii_src.string);
   1117 	src->ascii_src.string = NULL;
   1118     }
   1119 }
   1120 
   1121 /*
   1122  * Function:
   1123  *	XawAsciiSave
   1124  *
   1125  * Parameters:
   1126  *	w - asciiSrc Widget
   1127  *
   1128  * Description:
   1129  *	Saves all the pieces into a file or string as required.
   1130  *
   1131  * Returns:
   1132  *	True if the save was successful
   1133  */
   1134 Bool
   1135 XawAsciiSave(Widget w)
   1136 {
   1137     AsciiSrcObject src = (AsciiSrcObject)w;
   1138 
   1139     /* If the src is really a multi, call the multi save */
   1140     if (XtIsSubclass(w, multiSrcObjectClass ))
   1141 	return (_XawMultiSave(w));
   1142 
   1143     else if (!XtIsSubclass(w, asciiSrcObjectClass))
   1144 	XtErrorMsg("bad argument", "asciiSource", "XawError",
   1145 		   "XawAsciiSave's parameter must be an asciiSrc or multiSrc.",
   1146 		   NULL, NULL);
   1147 
   1148     /*
   1149      * If using the string in place then there is no need to play games
   1150      * to get the internal info into a readable string.
   1151      */
   1152     if (src->ascii_src.use_string_in_place)
   1153 	return (True);
   1154 
   1155     if (src->ascii_src.type == XawAsciiFile) {
   1156 #ifdef OLDXAW
   1157 	if (!src->ascii_src.changes)
   1158 #else
   1159 	if (!src->text_src.changed) 		/* No changes to save */
   1160 #endif
   1161 	    return (True);
   1162 
   1163 	if (WritePiecesToFile(src, src->ascii_src.string) == False)
   1164 	    return (False);
   1165     }
   1166     else  {
   1167 	if (src->ascii_src.allocated_string == True)
   1168 	    XtFree(src->ascii_src.string);
   1169 	else
   1170 	    src->ascii_src.allocated_string = True;
   1171 
   1172 	src->ascii_src.string = StorePiecesInString(src);
   1173     }
   1174 #ifdef OLDXAW
   1175     src->ascii_src.changes = False;
   1176 #else
   1177     src->text_src.changed = False;
   1178 #endif
   1179 
   1180     return (True);
   1181 }
   1182 
   1183 /*
   1184  * Function:
   1185  *	XawAsciiSaveAsFile
   1186  *
   1187  * Arguments:
   1188  *	w    - AsciiSrc widget
   1189  *	name - name of the file to save this file into
   1190  *
   1191  * Description:
   1192  *	Save the current buffer as a file.
   1193  *
   1194  * Returns:
   1195  *	True if the save was successful
   1196  */
   1197 Bool
   1198 XawAsciiSaveAsFile(Widget w, _Xconst char *name)
   1199 {
   1200     AsciiSrcObject src = (AsciiSrcObject)w;
   1201     Bool ret;
   1202 
   1203     /* If the src is really a multi, call the multi save */
   1204 
   1205     if (XtIsSubclass( w, multiSrcObjectClass))
   1206 	return (_XawMultiSaveAsFile(w, name));
   1207 
   1208     else if (!XtIsSubclass(w, asciiSrcObjectClass))
   1209 	XtErrorMsg("bad argument", "asciiSource", "XawError",
   1210 		   "XawAsciiSaveAsFile's 1st parameter must be an "
   1211 		   "asciiSrc or multiSrc.",
   1212 		   NULL, NULL);
   1213 
   1214     if (src->ascii_src.type == XawAsciiFile)
   1215 	ret = WritePiecesToFile(src, (String)name);
   1216     else {
   1217 	char * string = StorePiecesInString(src);
   1218 
   1219 	ret = WriteToFile(string, (String)name, (unsigned)src->ascii_src.length);
   1220 	XtFree(string);
   1221     }
   1222 
   1223     return (ret);
   1224 }
   1225 
   1226 /*
   1227  * Function:
   1228  *	XawAsciiSourceChanged
   1229  *
   1230  * Parameters:
   1231  *	w - ascii source widget
   1232  *
   1233  * Description:
   1234  *	Returns true if the source has changed since last saved.
   1235  *
   1236  * Returns:
   1237  *	A Boolean (see description).
   1238  */
   1239 Bool
   1240 XawAsciiSourceChanged(Widget w)
   1241 {
   1242 #ifdef OLDXAW
   1243     if (XtIsSubclass(w, multiSrcObjectClass))
   1244 	return (((MultiSrcObject)w)->multi_src.changes);
   1245 
   1246     if (XtIsSubclass(w, asciiSrcObjectClass))
   1247 	return (((AsciiSrcObject)w)->ascii_src.changes);
   1248 #else
   1249     if (XtIsSubclass(w, textSrcObjectClass))
   1250 	return (((TextSrcObject)w)->textSrc.changed);
   1251 #endif
   1252     XtErrorMsg("bad argument", "asciiSource", "XawError",
   1253 	       "XawAsciiSourceChanged parameter must be an "
   1254 	       "asciiSrc or multiSrc.",
   1255 	       NULL, NULL);
   1256 
   1257     return (True);
   1258 }
   1259 
   1260 /*
   1261  * Private Functions
   1262  */
   1263 static void
   1264 RemoveOldStringOrFile(AsciiSrcObject src, Bool checkString)
   1265 {
   1266     FreeAllPieces(src);
   1267 
   1268     if (checkString && src->ascii_src.allocated_string) {
   1269 	XtFree(src->ascii_src.string);
   1270 	src->ascii_src.allocated_string = False;
   1271 	src->ascii_src.string = NULL;
   1272     }
   1273 }
   1274 
   1275 /*
   1276  * Function:
   1277  *	WriteToFile
   1278  *
   1279  * Parameters:
   1280  *	string - string to write
   1281  *	name   - the name of the file
   1282  *
   1283  * Description:
   1284  *	Write the string specified to the beginning of the file specified.
   1285  *
   1286  * Returns:
   1287  *	returns True if successful, False otherwise
   1288  */
   1289 static Bool
   1290 WriteToFile(String string, String name, unsigned length)
   1291 {
   1292     int fd;
   1293 
   1294     if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) == -1)
   1295 	return (False);
   1296 
   1297     if (write(fd, string, length) == -1) {
   1298 	close(fd);
   1299 	return (False);
   1300     }
   1301 
   1302     if (close(fd) == -1)
   1303 	return (False);
   1304 
   1305     return (True);
   1306 }
   1307 
   1308 /*
   1309  * Function:
   1310  *	WritePiecesToFile
   1311  *
   1312  * Parameters:
   1313  *	src  - ascii source object
   1314  *	name - name of the file
   1315  *
   1316  * Description:
   1317  *	  Almost identical to WriteToFile, but only works for ascii src objects
   1318  *	of type XawAsciiFile. This function avoids allocating temporary memory,
   1319  *	what can be useful when editing very large files.
   1320  *
   1321  * Returns:
   1322  *	returns True if successful, False otherwise
   1323  */
   1324 static Bool
   1325 WritePiecesToFile(AsciiSrcObject src, String name)
   1326 {
   1327     Piece *piece;
   1328     int fd;
   1329 
   1330     if (src->ascii_src.data_compression) {
   1331 	piece = src->ascii_src.first_piece;
   1332 	while (piece) {
   1333 	    int bytes = (int)(src->ascii_src.piece_size - piece->used);
   1334 	    Piece *tmp;
   1335 
   1336 	    if (bytes > 0 && (tmp = piece->next) != NULL) {
   1337 		bytes = (int)(XawMin(bytes, tmp->used));
   1338 		memcpy(piece->text + piece->used, tmp->text, (size_t)bytes);
   1339 		memmove(tmp->text, tmp->text + bytes, (size_t)(tmp->used - bytes));
   1340 		piece->used += bytes;
   1341 		if ((tmp->used -= bytes) == 0) {
   1342 		    RemovePiece(src, tmp);
   1343 		    continue;
   1344 		}
   1345 	    }
   1346 	    piece = piece->next;
   1347 	}
   1348     }
   1349 
   1350     if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) == -1)
   1351 	return (False);
   1352 
   1353     for (piece = src->ascii_src.first_piece; piece; piece = piece->next)
   1354 	if (write(fd, piece->text, (size_t)piece->used) == -1) {
   1355 	    close(fd);
   1356 	    return (False);
   1357 	}
   1358 
   1359     if (close(fd) == -1)
   1360 	return (False);
   1361 
   1362     return (True);
   1363 }
   1364 
   1365 /*
   1366  * Function:
   1367  *	StorePiecesInString
   1368  *
   1369  * Parameters:
   1370  *	data - ascii pointer data
   1371  *
   1372  * Description:
   1373  *	Store the pieces in memory into a standard ascii string.
   1374  */
   1375 static char *
   1376 StorePiecesInString(AsciiSrcObject src)
   1377 {
   1378     char * string;
   1379     XawTextPosition first;
   1380     Piece *piece;
   1381 
   1382     string = XtMalloc((unsigned)(src->ascii_src.length + 1));
   1383 
   1384     for (first = 0, piece = src->ascii_src.first_piece ; piece != NULL;
   1385 	 first += piece->used, piece = piece->next)
   1386       memcpy(string + first, piece->text, (unsigned)piece->used);
   1387 
   1388     string[src->ascii_src.length] = '\0';
   1389 
   1390     /*
   1391      * This will refill all pieces to capacity
   1392      */
   1393     if (src->ascii_src.data_compression) {
   1394 	FreeAllPieces(src);
   1395 	LoadPieces(src, NULL, string);
   1396     }
   1397 
   1398     return (string);
   1399 }
   1400 
   1401 /*
   1402  * Function:
   1403  *	InitStringOrFile
   1404  *
   1405  * Parameters:
   1406  *	src - AsciiSource
   1407  *
   1408  * Description:
   1409  *	Initializes the string or file.
   1410  */
   1411 static FILE *
   1412 InitStringOrFile(AsciiSrcObject src, Bool newString)
   1413 {
   1414     mode_t open_mode = 0;
   1415     const char *fdopen_mode = NULL;
   1416 
   1417     if (src->ascii_src.type == XawAsciiString) {
   1418 	if (src->ascii_src.string == NULL)
   1419 	    src->ascii_src.length = 0;
   1420 
   1421 	else if (!src->ascii_src.use_string_in_place) {
   1422 	    src->ascii_src.string = XtNewString(src->ascii_src.string);
   1423 	    src->ascii_src.allocated_string = True;
   1424 	    src->ascii_src.length = (XawTextPosition)strlen(src->ascii_src.string);
   1425 	}
   1426 
   1427 	if (src->ascii_src.use_string_in_place) {
   1428 	    if (src->ascii_src.string != NULL)
   1429 	    src->ascii_src.length = (XawTextPosition)strlen(src->ascii_src.string);
   1430 	    /* In case the length resource is incorrectly set */
   1431 	    if (src->ascii_src.length > src->ascii_src.ascii_length)
   1432 		src->ascii_src.ascii_length = (int)src->ascii_src.length;
   1433 
   1434 	    if (src->ascii_src.ascii_length == MAGIC_VALUE)
   1435 		src->ascii_src.piece_size = src->ascii_src.length;
   1436 	    else
   1437 		src->ascii_src.piece_size = src->ascii_src.ascii_length + 1;
   1438 	}
   1439 
   1440 	return (NULL);
   1441     }
   1442 
   1443     /*
   1444      * type is XawAsciiFile
   1445      */
   1446     src->ascii_src.is_tempfile = False;
   1447 
   1448     switch (src->text_src.edit_mode) {
   1449 	case XawtextRead:
   1450 	    if (src->ascii_src.string == NULL)
   1451 		XtErrorMsg("NoFile", "asciiSourceCreate", "XawError",
   1452 			   "Creating a read only disk widget and no file specified.",
   1453 			   NULL, NULL);
   1454 	    open_mode = O_RDONLY | O_CLOEXEC;
   1455 	    fdopen_mode = "r";
   1456 	    break;
   1457 	case XawtextAppend:
   1458 	case XawtextEdit:
   1459 	    if (src->ascii_src.string == NULL) {
   1460 		src->ascii_src.string = (char*)"*ascii-src*";
   1461 		src->ascii_src.is_tempfile = True;
   1462 	    }
   1463 	    else {
   1464 /* O_NOFOLLOW was a FreeBSD & Linux extension, now adopted by POSIX */
   1465 #ifdef O_NOFOLLOW
   1466 		open_mode = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
   1467 #else
   1468 		open_mode = O_RDWR; /* unsafe; subject to race conditions */
   1469 #endif /* O_NOFOLLOW */
   1470 		fdopen_mode = "r+";
   1471 	    }
   1472 	    break;
   1473 	default:
   1474 	    XtErrorMsg("badMode", "asciiSourceCreate", "XawError",
   1475 		       "Bad editMode for ascii source; must be Read, "
   1476 		       "Append or Edit.",
   1477 		       NULL, NULL);
   1478     }
   1479 
   1480     /* If is_tempfile, allocate a private copy of the text
   1481      * Unlikely to be changed, just to set allocated_string */
   1482     if (newString || src->ascii_src.is_tempfile) {
   1483 	src->ascii_src.string = XtNewString(src->ascii_src.string);
   1484 	src->ascii_src.allocated_string = True;
   1485     }
   1486 
   1487     if (!src->ascii_src.is_tempfile) {
   1488 	int fd = open(src->ascii_src.string, (int)open_mode, 0666);
   1489 
   1490 	if (fd != -1) {
   1491 	    FILE *file = fdopen(fd, fdopen_mode);
   1492 
   1493 	    if (file != NULL) {
   1494 		(void)fseek(file, 0, SEEK_END);
   1495 		src->ascii_src.length = (XawTextPosition)ftell(file);
   1496 		return (file);
   1497 	    }
   1498 	    else
   1499 		close(fd);
   1500 	}
   1501 	{
   1502 	    String params[2];
   1503 	    Cardinal num_params = 2;
   1504 
   1505 	    params[0] = src->ascii_src.string;
   1506 	    params[1] = strerror(errno);
   1507 	    XtAppWarningMsg(XtWidgetToApplicationContext((Widget)src),
   1508 			    "openError", "asciiSourceCreate", "XawWarning",
   1509 			    "Cannot open file %s; %s", params, &num_params);
   1510 	}
   1511     }
   1512     src->ascii_src.length = 0;
   1513     return (NULL);
   1514 }
   1515 
   1516 static void
   1517 LoadPieces(AsciiSrcObject src, FILE *file, char *string)
   1518 {
   1519     char *ptr;
   1520     Piece *piece = NULL;
   1521     XawTextPosition left;
   1522 
   1523     if (string == NULL) {
   1524 	if (src->ascii_src.type == XawAsciiFile) {
   1525 	    if (src->ascii_src.length != 0) {
   1526 		left = 0;
   1527 		fseek(file, 0, SEEK_SET);
   1528 		while (left < src->ascii_src.length) {
   1529 		    int len;
   1530 
   1531 		    ptr = XtMalloc((unsigned)src->ascii_src.piece_size);
   1532 		    if ((len = (int)fread(ptr, sizeof(unsigned char),
   1533 				     (size_t)src->ascii_src.piece_size, file)) < 0)
   1534 			XtErrorMsg("readError", "asciiSourceCreate", "XawError",
   1535 				   "fread returned error.", NULL, NULL);
   1536 		    piece = AllocNewPiece(src, piece);
   1537 		    piece->text = ptr;
   1538 		    piece->used = XawMin(len, src->ascii_src.piece_size);
   1539 		    left += piece->used;
   1540 		}
   1541 	    }
   1542 	    else {
   1543 		piece = AllocNewPiece(src, NULL);
   1544 		piece->text = XtMalloc((unsigned)src->ascii_src.piece_size);
   1545 		piece->used = 0;
   1546 	    }
   1547 	    return;
   1548 	}
   1549 	else
   1550 	    string = src->ascii_src.string;
   1551     }
   1552 
   1553     if (src->ascii_src.use_string_in_place) {
   1554 	piece = AllocNewPiece(src, piece);
   1555 	piece->used = XawMin(src->ascii_src.length, src->ascii_src.piece_size);
   1556 	piece->text = src->ascii_src.string;
   1557 	return;
   1558     }
   1559 
   1560     ptr = string;
   1561     left = src->ascii_src.length;
   1562     do {
   1563 	piece = AllocNewPiece(src, piece);
   1564 
   1565 	piece->text = XtMalloc((unsigned)src->ascii_src.piece_size);
   1566 	piece->used = XawMin(left, src->ascii_src.piece_size);
   1567 	if (piece->used != 0)
   1568 	    memcpy(piece->text, ptr, (unsigned)piece->used);
   1569 
   1570 	left -= piece->used;
   1571 	ptr += piece->used;
   1572     } while (left > 0);
   1573 }
   1574 
   1575 /*
   1576  * Function:
   1577  *	AllocNewPiece
   1578  *
   1579  * Parameters:
   1580  *	src - AsciiSrc Widget
   1581  *	prev - piece just before this one, or NULL
   1582  *
   1583  * Description:
   1584  *	Allocates a new piece of memory.
   1585  *
   1586  * Returns:
   1587  *	The allocated piece
   1588  */
   1589 static Piece *
   1590 AllocNewPiece(AsciiSrcObject src, Piece *prev)
   1591 {
   1592     Piece *piece = XtNew(Piece);
   1593 
   1594     if (prev == NULL) {
   1595 	src->ascii_src.first_piece = piece;
   1596 	piece->next = NULL;
   1597     }
   1598     else  {
   1599 	if (prev->next != NULL)
   1600 	    (prev->next)->prev = piece;
   1601 	piece->next = prev->next;
   1602 	prev->next = piece;
   1603     }
   1604 
   1605     piece->prev = prev;
   1606 
   1607     return (piece);
   1608 }
   1609 
   1610 /*
   1611  * Function:
   1612  *	FreeAllPieces
   1613  *
   1614  * Parameters:
   1615  *	src - AsciiSrc Widget
   1616  *
   1617  * Description:
   1618  *	Frees all the pieces.
   1619  */
   1620 static void
   1621 FreeAllPieces(AsciiSrcObject src)
   1622 {
   1623     Piece *next, * first = src->ascii_src.first_piece;
   1624 
   1625 #ifdef DEBUG
   1626     if (first->prev != NULL)
   1627 	printf("Xaw AsciiSrc Object: possible memory leak in FreeAllPieces().\n");
   1628 #endif
   1629 
   1630     for (; first != NULL ; first = next) {
   1631 	next = first->next;
   1632 	RemovePiece(src, first);
   1633     }
   1634 }
   1635 
   1636 /*
   1637  * Function:
   1638  *	RemovePiece
   1639  *
   1640  * Parameters:
   1641  *	piece - piece to remove
   1642  *
   1643  * Description:
   1644  *	Removes a piece from the list.
   1645  */
   1646 static void
   1647 RemovePiece(AsciiSrcObject src, Piece *piece)
   1648 {
   1649     if (piece->prev == NULL)
   1650 	src->ascii_src.first_piece = piece->next;
   1651     else
   1652 	piece->prev->next = piece->next;
   1653 
   1654     if (piece->next != NULL)
   1655 	piece->next->prev = piece->prev;
   1656 
   1657     if (!src->ascii_src.use_string_in_place)
   1658 	XtFree(piece->text);
   1659 
   1660     XtFree((char *)piece);
   1661 }
   1662 
   1663 /*
   1664  * Function:
   1665  *	FindPiece
   1666  *
   1667  * Parameters:
   1668  *	src	 - AsciiSrc Widget
   1669  *	position - position that we are searching for
   1670  *	first	 - position of the first character in this piece (return)
   1671  *
   1672  * Description:
   1673  *	Finds the piece containing the position indicated.
   1674  *
   1675  * Returns:
   1676  *	the piece that contains this position
   1677  */
   1678 static Piece *
   1679 FindPiece(AsciiSrcObject src, XawTextPosition position, XawTextPosition *first)
   1680 {
   1681     Piece *old_piece, *piece;
   1682     XawTextPosition temp;
   1683 
   1684     for (old_piece = NULL, piece = src->ascii_src.first_piece, temp = 0;
   1685 	piece; old_piece = piece, piece = piece->next)
   1686 	if ((temp += piece->used) > position) {
   1687 	    *first = temp - piece->used;
   1688 	    return (piece);
   1689 	}
   1690 
   1691     *first = temp - (old_piece ? old_piece->used : 0);
   1692 
   1693     return (old_piece);	/* if we run off the end the return the last piece */
   1694 }
   1695 
   1696 /*
   1697  * Function:
   1698  *	BreakPiece
   1699  *
   1700  * Parameters:
   1701  *	src - AsciiSrc Widget
   1702  *	piece - piece to break
   1703  *
   1704  * Description:
   1705  *	Breaks a full piece into two new pieces.
   1706  */
   1707 #define HALF_PIECE (src->ascii_src.piece_size >> 1)
   1708 static void
   1709 BreakPiece(AsciiSrcObject src, Piece *piece)
   1710 {
   1711     Piece *cnew = AllocNewPiece(src, piece);
   1712 
   1713     cnew->text = XtMalloc((unsigned)src->ascii_src.piece_size);
   1714     memcpy(cnew->text, piece->text + HALF_PIECE,
   1715 	   (unsigned)(src->ascii_src.piece_size - HALF_PIECE));
   1716     piece->used = HALF_PIECE;
   1717     cnew->used = src->ascii_src.piece_size - HALF_PIECE;
   1718 }
   1719 
   1720 /*ARGSUSED*/
   1721 static void
   1722 CvtStringToAsciiType(XrmValuePtr args _X_UNUSED, Cardinal *num_args _X_UNUSED,
   1723 		     XrmValuePtr fromVal, XrmValuePtr toVal)
   1724 {
   1725     static XawAsciiType type;
   1726     XrmQuark q;
   1727     char name[7];
   1728 
   1729     XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name));
   1730     q = XrmStringToQuark(name);
   1731 
   1732     if (q == Qstring)
   1733 	type = XawAsciiString;
   1734     else if (q == Qfile)
   1735 	type = XawAsciiFile;
   1736     else  {
   1737 	toVal->size = 0;
   1738 	toVal->addr = NULL;
   1739 	XtStringConversionWarning((char *)fromVal->addr, XtRAsciiType);
   1740     }
   1741 
   1742     toVal->size = sizeof(XawAsciiType);
   1743     toVal->addr = (XPointer)&type;
   1744 }
   1745 
   1746 /*ARGSUSED*/
   1747 static Boolean
   1748 CvtAsciiTypeToString(Display *dpy, XrmValuePtr args _X_UNUSED, Cardinal *num_args _X_UNUSED,
   1749 		     XrmValuePtr fromVal, XrmValuePtr toVal,
   1750 		     XtPointer *data _X_UNUSED)
   1751 {
   1752     static String buffer;
   1753     Cardinal size;
   1754 
   1755     switch (*(XawAsciiType *)fromVal->addr) {
   1756 	case XawAsciiFile:
   1757 	    buffer = XtEfile;
   1758 	    break;
   1759 	case XawAsciiString:
   1760 	    buffer = XtEstring;
   1761 	    break;
   1762 	default:
   1763 	    XawTypeToStringWarning(dpy, XtRAsciiType);
   1764 	    toVal->addr = NULL;
   1765 	    toVal->size = 0;
   1766 	    return (False);
   1767     }
   1768 
   1769     size = (Cardinal)(strlen(buffer) + 1);
   1770     if (toVal->addr != NULL) {
   1771 	if (toVal->size < size) {
   1772 	    toVal->size = size;
   1773 	    return (False);
   1774 	}
   1775 	strcpy((char *)toVal->addr, buffer);
   1776     }
   1777     else
   1778 	toVal->addr = (XPointer)buffer;
   1779     toVal->size = sizeof(String);
   1780 
   1781     return (True);
   1782 }
   1783 
   1784 /*ARGSUSED*/
   1785 static void
   1786 GetDefaultPieceSize(Widget w _X_UNUSED, int offset _X_UNUSED, XrmValue *value)
   1787 {
   1788     static XPointer pagesize;
   1789 
   1790     if (pagesize == NULL) {
   1791 	pagesize = (XPointer)((long)_XawGetPageSize());
   1792 	if (pagesize < (XPointer)BUFSIZ)
   1793 	    pagesize = (XPointer)BUFSIZ;
   1794     }
   1795 
   1796     value->addr = (XPointer)&pagesize;
   1797 }
   1798 
   1799 #if (defined(ASCII_STRING) || defined(ASCII_DISK))
   1800 #  include <X11/Xaw/Cardinals.h>
   1801 #endif
   1802 
   1803 #ifdef ASCII_STRING
   1804 /*
   1805  * Compatibility functions.
   1806  */
   1807 /*
   1808  * Function:
   1809  *	AsciiStringSourceCreate
   1810  *
   1811  * Parameters:
   1812  *	parent	 - widget that will own this source
   1813  *	args	 - the argument list
   1814  *	num_args - ""
   1815  *
   1816  * Description:
   1817  *	Creates a string source.
   1818  *
   1819  * Returns:
   1820  *	A pointer to the new text source.
   1821  */
   1822 Widget
   1823 XawStringSourceCreate(Widget parent, ArgList args, Cardinal num_args)
   1824 {
   1825     XawTextSource src;
   1826     ArgList ascii_args;
   1827     Arg temp[2];
   1828 
   1829     XtSetArg(temp[0], XtNtype, XawAsciiString);
   1830     XtSetArg(temp[1], XtNuseStringInPlace, True);
   1831     ascii_args = XtMergeArgLists(temp, TWO, args, num_args);
   1832 
   1833     src = XtCreateWidget("genericAsciiString", asciiSrcObjectClass, parent,
   1834 			 ascii_args, num_args + TWO);
   1835     XtFree((char *)ascii_args);
   1836 
   1837     return (src);
   1838 }
   1839 
   1840 /*
   1841  * This is hacked up to try to emulate old functionality, it
   1842  * may not work, as I have not old code to test it on.
   1843  *
   1844  * Chris D. Peterson  8/31/89.
   1845  */
   1846 void
   1847 XawTextSetLastPos(Widget w, XawTextPosition lastPos)
   1848 {
   1849     AsciiSrcObject src = (AsciiSrcObject)XawTextGetSource(w);
   1850 
   1851     src->ascii_src.piece_size = lastPos;
   1852 }
   1853 #endif /* ASCII_STRING */
   1854 
   1855 #ifdef ASCII_DISK
   1856 /*
   1857  * Function:
   1858  *	AsciiDiskSourceCreate
   1859  *
   1860  * Parameters:
   1861  *	parent	 - widget that will own this source
   1862  *	args	 - argument list
   1863  *	num_args - ""
   1864  *
   1865  * Description:
   1866  *	Creates a disk source.
   1867  *
   1868  * Returns:
   1869  *	A pointer to the new text source
   1870  */
   1871 Widget
   1872 XawDiskSourceCreate(Widget parent, ArgList args, Cardinal num_args)
   1873 {
   1874     XawTextSource src;
   1875     ArgList ascii_args;
   1876     Arg temp[1];
   1877     int i;
   1878 
   1879     XtSetArg(temp[0], XtNtype, XawAsciiFile);
   1880     ascii_args = XtMergeArgLists(temp, ONE, args, num_args);
   1881     num_args++;
   1882 
   1883     for (i = 0; i < num_args; i++)
   1884 	if (streq(ascii_args[i].name, XtNfile)
   1885 	    || streq(ascii_args[i].name, XtCFile))
   1886 	    ascii_args[i].name = XtNstring;
   1887 
   1888     src = XtCreateWidget("genericAsciiDisk", asciiSrcObjectClass, parent,
   1889 			 ascii_args, num_args);
   1890     XtFree((char *)ascii_args);
   1891 
   1892     return (src);
   1893 }
   1894 #endif /* ASCII_DISK */
   1895