/* Copyright 1989, 1994, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ /* * AsciiSrc.c - AsciiSrc object. (For use with the text widget). * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef OLDXAW #include #include #endif #include "Private.h" #include #include #include #if (defined(ASCII_STRING) || defined(ASCII_DISK)) #include /* for Widget Classes */ #endif #ifdef X_NOT_POSIX #define Off_t long #define Size_t unsigned int #else #define Off_t off_t #define Size_t size_t #endif #define MAGIC_VALUE ((XawTextPosition)-1) #define streq(a, b) (strcmp((a), (b)) == 0) /* * Class Methods */ static void XawAsciiSrcClassInitialize(void); static void XawAsciiSrcDestroy(Widget); static void XawAsciiSrcGetValuesHook(Widget, ArgList, Cardinal*); static void XawAsciiSrcInitialize(Widget, Widget, ArgList, Cardinal*); static Boolean XawAsciiSrcSetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XawTextPosition ReadText(Widget, XawTextPosition, XawTextBlock*, int); static int ReplaceText(Widget, XawTextPosition, XawTextPosition, XawTextBlock*); static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType, XawTextScanDirection, int, Bool); static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection, XawTextBlock*); /* * Prototypes */ static Piece *AllocNewPiece(AsciiSrcObject, Piece*); static void BreakPiece(AsciiSrcObject, Piece*); static Boolean CvtAsciiTypeToString(Display*, XrmValuePtr, Cardinal*, XrmValuePtr, XrmValuePtr, XtPointer*); static void CvtStringToAsciiType(XrmValuePtr, Cardinal*, XrmValuePtr, XrmValuePtr); static Piece *FindPiece(AsciiSrcObject, XawTextPosition, XawTextPosition*); static void FreeAllPieces(AsciiSrcObject); static FILE *InitStringOrFile(AsciiSrcObject, Bool); static void LoadPieces(AsciiSrcObject, FILE*, char*); static void RemoveOldStringOrFile(AsciiSrcObject, Bool); static void RemovePiece(AsciiSrcObject, Piece*); static String StorePiecesInString(AsciiSrcObject); static Bool WriteToFile(String, String, unsigned); static Bool WritePiecesToFile(AsciiSrcObject, String); static void GetDefaultPieceSize(Widget, int, XrmValue*); /* * More Prototypes */ #ifdef ASCII_DISK Widget XawAsciiDiskSourceCreate(Widget, ArgList, Cardinal); #endif #ifdef ASCII_STRING Widget XawStringSourceCreate(Widget, ArgList, Cardinal); void XawTextSetLastPos(Widget, XawTextPosition); #endif /* * Initialization */ #define offset(field) XtOffsetOf(AsciiSrcRec, ascii_src.field) static XtResource resources[] = { { XtNstring, XtCString, XtRString, sizeof(char*), offset(string), XtRString, NULL }, { XtNtype, XtCType, XtRAsciiType, sizeof(XawAsciiType), offset(type), XtRImmediate, (XtPointer)XawAsciiString }, { XtNdataCompression, XtCDataCompression, XtRBoolean, sizeof(Boolean), offset(data_compression), XtRImmediate, (XtPointer)True }, { XtNpieceSize, XtCPieceSize, XtRInt, sizeof(XawTextPosition), offset(piece_size), XtRCallProc, (XtPointer)GetDefaultPieceSize }, #ifdef OLDXAW { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(callback), XtRCallback, (XtPointer)NULL }, #endif { XtNuseStringInPlace, XtCUseStringInPlace, XtRBoolean, sizeof(Boolean), offset(use_string_in_place), XtRImmediate, (XtPointer)False }, { XtNlength, XtCLength, XtRInt, sizeof(int), offset(ascii_length), XtRImmediate, (XtPointer)MAGIC_VALUE }, #ifdef ASCII_DISK { XtNfile, XtCFile, XtRString, sizeof(String), offset(filename), XtRString, NULL }, #endif /* ASCII_DISK */ }; #undef offset #define Superclass (&textSrcClassRec) AsciiSrcClassRec asciiSrcClassRec = { /* object */ { (WidgetClass)Superclass, /* superclass */ "AsciiSrc", /* class_name */ sizeof(AsciiSrcRec), /* widget_size */ XawAsciiSrcClassInitialize, /* class_initialize */ NULL, /* class_part_initialize */ False, /* class_inited */ XawAsciiSrcInitialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ False, /* compress_motion */ False, /* compress_exposure */ False, /* compress_enterleave */ False, /* visible_interest */ XawAsciiSrcDestroy, /* destroy */ NULL, /* resize */ NULL, /* expose */ XawAsciiSrcSetValues, /* set_values */ NULL, /* set_values_hook */ NULL, /* set_values_almost */ XawAsciiSrcGetValuesHook, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ NULL, /* display_accelerator */ NULL, /* extension */ }, /* text_src */ { ReadText, /* Read */ ReplaceText, /* Replace */ Scan, /* Scan */ Search, /* Search */ XtInheritSetSelection, /* SetSelection */ XtInheritConvertSelection, /* ConvertSelection */ }, /* ascii_src */ { NULL, /* extension */ }, }; WidgetClass asciiSrcObjectClass = (WidgetClass)&asciiSrcClassRec; static XrmQuark Qstring, Qfile; /* * Implementation */ /* * Function: * XawAsciiSrcClassInitialize() * * Description: * Initializes the asciiSrcObjectClass and install the converters for * AsciiType <-> String. */ static void XawAsciiSrcClassInitialize(void) { XawInitializeWidgetSet(); Qstring = XrmPermStringToQuark(XtEstring); Qfile = XrmPermStringToQuark(XtEfile); XtAddConverter(XtRString, XtRAsciiType, CvtStringToAsciiType, NULL, 0); XtSetTypeConverter(XtRAsciiType, XtRString, CvtAsciiTypeToString, NULL, 0, XtCacheNone, NULL); } /* * Function: * XawAsciiSrcInitialize * * Parameters: * request - widget requested by the argument list * cnew - new widget with both resource and non resource values * args - (unused) * num_args - (unused) * * Description: * Initializes the ascii src object. */ /*ARGSUSED*/ static void XawAsciiSrcInitialize(Widget request, Widget cnew, ArgList args, Cardinal *num_args) { AsciiSrcObject src = (AsciiSrcObject)cnew; FILE *file; /* * Set correct flags (override resources) depending upon widget class */ src->text_src.text_format = XawFmt8Bit; #ifdef ASCII_DISK if (XtIsSubclass(XtParent(cnew), asciiDiskWidgetClass)) { src->ascii_src.type = XawAsciiFile; src->ascii_src.string = src->ascii_src.filename; } #endif #ifdef ASCII_STRING if (XtIsSubclass(XtParent(cnew), asciiStringWidgetClass)) { src->ascii_src.use_string_in_place = True; src->ascii_src.type = XawAsciiString; } #endif #ifdef OLDXAW src->ascii_src.changes = False; #else src->text_src.changed = False; #endif src->ascii_src.allocated_string = False; if (src->ascii_src.use_string_in_place && src->ascii_src.string == NULL) src->ascii_src.use_string_in_place = False; file = InitStringOrFile(src, src->ascii_src.type == XawAsciiFile); LoadPieces(src, file, NULL); if (file != NULL) fclose(file); } /* * Function: * ReadText * * Parameters: * w - AsciiSource widget * pos - position of the text to retreive. * text - text block that will contain returned text * length - maximum number of characters to read * * Description: * This function reads the source. * * Returns: * The character position following the retrieved text. */ static XawTextPosition ReadText(Widget w, XawTextPosition pos, XawTextBlock *text, int length) { AsciiSrcObject src = (AsciiSrcObject)w; XawTextPosition count, start; Piece *piece; #ifndef OLDXAW XawTextAnchor *anchor; XawTextEntity *entity; XawTextPosition offset, end = pos + length; Bool state; end = XawMin(end, src->ascii_src.length); while ((state = XawTextSourceAnchorAndEntity(w, pos, &anchor, &entity)) && (entity->flags & XAW_TENTF_HIDE)) pos = anchor->position + entity->offset + entity->length; if (state == False || !(entity->flags & XAW_TENTF_REPLACE)) { while (entity) { offset = anchor->position + entity->offset; if (offset >= end) break; if (offset > pos && (entity->flags & (XAW_TENTF_HIDE | XAW_TENTF_REPLACE))) { end = XawMin(end, offset); break; } if ((entity = entity->next) == NULL && (anchor = XawTextSourceNextAnchor(w, anchor)) != NULL) entity = anchor->entities; } } else if (state && (entity->flags & XAW_TENTF_REPLACE) && pos < end) { XawTextBlock *block = (XawTextBlock*)entity->data; offset = anchor->position + entity->offset; end = XawMin(end, offset + block->length); if ((length = end - pos) < 0) length = 0; text->length = length; text->format = XawFmt8Bit; if (length == 0) { text->firstPos = end = offset + entity->length; text->ptr = ""; } else { text->firstPos = pos; text->ptr = block->ptr + (pos - offset); if (pos + length < offset + block->length) end = pos + length; /* there is data left to be read */ else end = offset + entity->length; } return (end); } if ((length = end - pos) < 0) length = 0; #endif piece = FindPiece(src, pos, &start); text->firstPos = pos; text->ptr = piece->text + (pos - start); count = piece->used - (pos - start); text->length = Max(0, (length > count) ? count : length); text->format = XawFmt8Bit; return (pos + text->length); } /* * Function: * ReplaceText * * Parameters: * w - AsciiSource object * startPos - ends of text that will be replaced * endPos - "" * text - new text to be inserted into buffer at startPos * * Description: * Replaces a block of text with new text. * * Returns: * XawEditDone on success, XawEditError otherwise */ /*ARGSUSED*/ static int ReplaceText(Widget w, XawTextPosition startPos, XawTextPosition endPos, XawTextBlock *text) { AsciiSrcObject src = (AsciiSrcObject)w; Piece *start_piece, *end_piece, *temp_piece; XawTextPosition start_first, end_first; int length, firstPos; /* * Editing a read only source is not allowed */ if (src->text_src.edit_mode == XawtextRead) return (XawEditError); start_piece = FindPiece(src, startPos, &start_first); end_piece = FindPiece(src, endPos, &end_first); #ifndef OLDXAW /* * This is a big hack, but I can't think about a clever way to know * if the character being moved forward has a negative lbearing. * */ if (start_piece->used) { int i; for (i = 0; i < src->text_src.num_text; i++) { int line; TextWidget ctx = (TextWidget)src->text_src.text[i]; for (line = 0; line < ctx->text.lt.lines; line++) if (startPos < ctx->text.lt.info[line + 1].position) break; if (i < ctx->text.lt.lines && startPos > ctx->text.lt.info[i].position) { AsciiSinkObject sink = (AsciiSinkObject)ctx->text.sink; XawTextAnchor *anchor; XawTextEntity *entity; XawTextProperty *property; XFontStruct *font; if (XawTextSourceAnchorAndEntity(w, startPos, &anchor, &entity) && (property = XawTextSinkGetProperty(ctx->text.sink, entity->property)) != NULL && (property->mask & XAW_TPROP_FONT)) font = property->font; else font = sink->ascii_sink.font; if (font->min_bounds.lbearing < 0) { int lbearing = font->min_bounds.lbearing; unsigned char c = *(unsigned char*) (start_piece->text + (startPos - start_first)); if (c == '\t' || c == '\n') c = ' '; else if ((c & 0177) < XawSP || c == 0177) { if (sink->ascii_sink.display_nonprinting) c = c > 0177 ? '\\' : c + '^'; else c = ' '; } if (font->per_char && (c >= font->min_char_or_byte2 && c <= font->max_char_or_byte2)) lbearing = font->per_char[c - font->min_char_or_byte2].lbearing; if (lbearing < 0) _XawTextNeedsUpdating(ctx, startPos - 1, startPos); } } } } #endif /* * Remove Old Stuff */ if (start_piece != end_piece) { temp_piece = start_piece->next; /* * If empty and not the only piece then remove it. */ if (((start_piece->used = startPos - start_first) == 0) && !(start_piece->next == NULL && start_piece->prev == NULL)) RemovePiece(src, start_piece); while (temp_piece != end_piece) { temp_piece = temp_piece->next; RemovePiece(src, temp_piece->prev); } end_piece->used -= endPos - end_first; if (end_piece->used != 0) memmove(end_piece->text, end_piece->text + endPos - end_first, (unsigned)end_piece->used); } else { /* We are fully in one piece */ if ((start_piece->used -= endPos - startPos) == 0) { if (!(start_piece->next == NULL && start_piece->prev == NULL)) RemovePiece(src, start_piece); } else { memmove(start_piece->text + (startPos - start_first), start_piece->text + (endPos - start_first), (unsigned)(start_piece->used - (startPos - start_first))); if (src->ascii_src.use_string_in_place && src->ascii_src.length - (endPos - startPos) < src->ascii_src.piece_size - 1) start_piece->text[src->ascii_src.length - (endPos - startPos)] = '\0'; } } src->ascii_src.length += -(endPos - startPos) + text->length; if ( text->length != 0) { /* * Put in the New Stuff */ start_piece = FindPiece(src, startPos, &start_first); length = text->length; firstPos = text->firstPos; while (length > 0) { char *ptr; int fill; if (src->ascii_src.use_string_in_place) { if (start_piece->used == src->ascii_src.piece_size - 1) { /* * If we are in ascii string emulation mode. Then the * string is not allowed to grow */ start_piece->used = src->ascii_src.length = src->ascii_src.piece_size - 1; start_piece->text[src->ascii_src.length] = '\0'; return (XawEditError); } } if (start_piece->used == src->ascii_src.piece_size) { BreakPiece(src, start_piece); start_piece = FindPiece(src, startPos, &start_first); } fill = Min((int)(src->ascii_src.piece_size - start_piece->used), length); ptr = start_piece->text + (startPos - start_first); memmove(ptr + fill, ptr, (unsigned)(start_piece->used - (startPos - start_first))); memcpy(ptr, text->ptr + firstPos, (unsigned)fill); startPos += fill; firstPos += fill; start_piece->used += fill; length -= fill; } } if (src->ascii_src.use_string_in_place) start_piece->text[start_piece->used] = '\0'; #ifdef OLDXAW src->ascii_src.changes = True; XtCallCallbacks(w, XtNcallback, NULL); #endif return (XawEditDone); } /* * Function: * Scan * * Parameters: * w - AsciiSource object * position - position to start scanning * type - type of thing to scan for * dir - direction to scan * count - which occurance if this thing to search for. * include - whether or not to include the character found in * the position that is returned * * Description: * Scans the text source for the number and type of item specified. * * Returns: * The position of the item found * * Note: * While there are only 'n' characters in the file there are n+1 * possible cursor positions (one before the first character and * one after the last character */ static XawTextPosition Scan(Widget w, register XawTextPosition position, XawTextScanType type, XawTextScanDirection dir, int count, Bool include) { AsciiSrcObject src = (AsciiSrcObject)w; Piece *piece; XawTextPosition first, first_eol_position = 0; register char *ptr, *lim; register int cnt = count; register unsigned char c; if (dir == XawsdLeft) { if (position <= 0) return (0); --position; } else if (position >= src->ascii_src.length) return (src->ascii_src.length); piece = FindPiece(src, position, &first); if (piece->used == 0) return (0); ptr = (position - first) + piece->text; if (dir == XawsdRight) { lim = piece->text + piece->used; switch (type) { case XawstEOL: case XawstParagraph: case XawstWhiteSpace: case XawstAlphaNumeric: for (; cnt > 0; cnt--) { Bool non_space = False, first_eol = True; /*CONSTCOND*/ while (True) { if (ptr >= lim) { piece = piece->next; if (piece == NULL) /* End of text */ return (src->ascii_src.length); ptr = piece->text; lim = piece->text + piece->used; } c = *ptr++; ++position; if (type == XawstEOL) { if (c == '\n') break; } else if (type == XawstAlphaNumeric) { if (!isalnum(c)) { if (non_space) break; } else non_space = True; } else if (type == XawstWhiteSpace) { if (isspace(c)) { if (non_space) break; } else non_space = True; } else { /* XawstParagraph */ if (first_eol) { if (c == '\n') { first_eol_position = position; first_eol = False; } } else if (c == '\n') break; else if (!isspace(c)) first_eol = True; } } } break; case XawstPositions: position += count; return (position < src->ascii_src.length ? position : src->ascii_src.length); case XawstAll: return (src->ascii_src.length); default: break; } if (!include) { if (type == XawstParagraph) position = first_eol_position; if (count) --position; } } else { lim = piece->text; switch (type) { case XawstEOL: case XawstParagraph: case XawstWhiteSpace: case XawstAlphaNumeric: for (; cnt > 0; cnt--) { Bool non_space = False, first_eol = True; /*CONSTCOND*/ while (True) { if (ptr < lim) { piece = piece->prev; if (piece == NULL) /* Begining of text */ return (0); ptr = piece->text + piece->used - 1; lim = piece->text; } c = *ptr--; --position; if (type == XawstEOL) { if (c == '\n') break; } else if (type == XawstAlphaNumeric) { if (!isalnum(c)) { if (non_space) break; } else non_space = True; } else if (type == XawstWhiteSpace) { if (isspace(c)) { if (non_space) break; } else non_space = True; } else { /* XawstParagraph */ if (first_eol) { if (c == '\n') { first_eol_position = position; first_eol = False; } } else if (c == '\n') break; else if (!isspace(c)) first_eol = True; } } } break; case XawstPositions: position -= count - 1; return (position > 0 ? position : 0); case XawstAll: return (0); default: break; } if (!include) { if (type == XawstParagraph) position = first_eol_position; if (count) ++position; } position++; } return (position); } /* * Function: * Search * * Parameters: * w - AsciiSource object * position - the position to start scanning * dir - direction to scan * text - text block to search for * * Description: * Searchs the text source for the text block passed. * * Returns: * The position of the item found */ static XawTextPosition Search(Widget w, register XawTextPosition position, XawTextScanDirection dir, XawTextBlock *text) { AsciiSrcObject src = (AsciiSrcObject)w; register int count = 0; register char *ptr, c; char *str; Piece *piece; char *buf; XawTextPosition first; int cnt, case_sensitive; if (dir == XawsdLeft) { if (position == 0) return (XawTextSearchError); position--; } buf = XtMalloc((unsigned)sizeof(unsigned char) * text->length); memcpy(buf, text->ptr, (unsigned)text->length); piece = FindPiece(src, position, &first); ptr = (position - first) + piece->text; case_sensitive = text->firstPos; if (dir == XawsdRight) { str = buf; c = *str; /*CONSTCOND*/ while (1) { if (*ptr++ == c || (case_sensitive && isalpha(c) && isalpha(ptr[-1]) && toupper(c) == toupper(ptr[-1]))) { if (++count == text->length) break; c = *++str; } else if (count) { ptr -= count; str -= count; position -= count; count = 0; c = *str; if (ptr < piece->text) { do { cnt = piece->text - ptr; piece = piece->prev; if (piece == NULL) { XtFree(buf); return (XawTextSearchError); } ptr = piece->text + piece->used - cnt; } while (ptr < piece->text); } } position++; if (ptr >= (piece->text + piece->used)) { do { cnt = ptr - (piece->text + piece->used); piece = piece->next; if (piece == NULL) { XtFree(buf); return (XawTextSearchError); } ptr = piece->text + cnt; } while (ptr >= (piece->text + piece->used)); } } position -= text->length - 1; } else { str = buf + text->length - 1; c = *str; /*CONSTCOND*/ while (1) { if (*ptr-- == c || (case_sensitive && isalpha(c) && isalpha(ptr[1]) && toupper(c) == toupper(ptr[1]))) { if (++count == text->length) break; c = *--str; } else if (count) { ptr += count; str += count; position += count; count = 0; c = *str; if (ptr >= (piece->text + piece->used)) { do { cnt = ptr - (piece->text + piece->used); piece = piece->next; if (piece == NULL) { XtFree(buf); return (XawTextSearchError); } ptr = piece->text + cnt; } while (ptr >= (piece->text + piece->used)); } } position--; if (ptr < piece->text) { do { cnt = piece->text - ptr; piece = piece->prev; if (piece == NULL) { XtFree(buf); return (XawTextSearchError); } ptr = piece->text + piece->used - cnt; } while (ptr < piece->text); } } } XtFree(buf); return (position); } /* * Function: * XawAsciiSrcSetValues * * Parameters: * current - current state of the widget * request - what was requested * cnew - what the widget will become * args - representation of changed resources * num_args - number of resources that have changed * * Description: * Sets the values for the AsciiSource. * * Returns: * True if redisplay is needed */ static Boolean XawAsciiSrcSetValues(Widget current, Widget request, Widget cnew, ArgList args, Cardinal *num_args) { AsciiSrcObject src = (AsciiSrcObject)cnew; AsciiSrcObject old_src = (AsciiSrcObject)current; Bool total_reset = False, string_set = False; FILE *file; unsigned int i; if (old_src->ascii_src.use_string_in_place != src->ascii_src.use_string_in_place) { XtAppWarning(XtWidgetToApplicationContext(cnew), "AsciiSrc: The XtNuseStringInPlace resource may " "not be changed."); src->ascii_src.use_string_in_place = old_src->ascii_src.use_string_in_place; } for (i = 0; i < *num_args ; i++) if (streq(args[i].name, XtNstring)) { string_set = True; break; } if (string_set || (old_src->ascii_src.type != src->ascii_src.type)) { RemoveOldStringOrFile(old_src, string_set); /* remove old info */ file = InitStringOrFile(src, string_set); /* Init new info */ LoadPieces(src, file, NULL); /* load new info into internal buffers */ if (file != NULL) fclose(file); #ifndef OLDXAW for (i = 0; i < src->text_src.num_text; i++) /* Tell text widget what happened */ XawTextSetSource(src->text_src.text[i], cnew, 0); #else XawTextSetSource(XtParent(cnew), cnew, 0); #endif total_reset = True; } if (old_src->ascii_src.ascii_length != src->ascii_src.ascii_length) src->ascii_src.piece_size = src->ascii_src.ascii_length + 1; if (!total_reset && old_src->ascii_src.piece_size != src->ascii_src.piece_size) { String string = StorePiecesInString(old_src); FreeAllPieces(old_src); LoadPieces(src, NULL, string); XtFree(string); } return (False); } /* * Function: * XawAsciiSrcGetValuesHook * * Parameters: * w - AsciiSource Widget * args - argument list * num_args - number of args * * Description: * This is a get values hook routine that sets the * values specific to the ascii source. */ static void XawAsciiSrcGetValuesHook(Widget w, ArgList args, Cardinal *num_args) { AsciiSrcObject src = (AsciiSrcObject)w; unsigned int i; if (src->ascii_src.type == XawAsciiString) { for (i = 0; i < *num_args ; i++) if (streq(args[i].name, XtNstring)) { if (src->ascii_src.use_string_in_place) *((char **)args[i].value) = src->ascii_src.first_piece->text; else if (XawAsciiSave(w)) /* If save sucessful */ *((char **)args[i].value) = src->ascii_src.string; break; } } } /* * Function: * XawAsciiSrcDestroy * * Parameters: * src - Ascii source object to free * * Description: * Destroys an ascii source (frees all data) */ static void XawAsciiSrcDestroy(Widget w) { RemoveOldStringOrFile((AsciiSrcObject) w, True); } /* * Public routines */ /* * Function: * XawAsciiSourceFreeString * * Parameters: * w - AsciiSrc widget * * Description: * Frees the string returned by a get values call * on the string when the source is of type string. */ void XawAsciiSourceFreeString(Widget w) { AsciiSrcObject src = (AsciiSrcObject)w; /* If the src is really a multi, call the multi routine */ if (XtIsSubclass(w, multiSrcObjectClass)) { _XawMultiSourceFreeString(w); return; } else if (!XtIsSubclass(w, asciiSrcObjectClass)) { XtErrorMsg("bad argument", "asciiSource", "XawError", "XawAsciiSourceFreeString's parameter must be " "an asciiSrc or multiSrc.", NULL, NULL); } if (src->ascii_src.allocated_string && src->ascii_src.type != XawAsciiFile) { src->ascii_src.allocated_string = False; XtFree(src->ascii_src.string); src->ascii_src.string = NULL; } } /* * Function: * XawAsciiSave * * Parameters: * w - asciiSrc Widget * * Description: * Saves all the pieces into a file or string as required. * * Returns: * True if the save was successful */ Bool XawAsciiSave(Widget w) { AsciiSrcObject src = (AsciiSrcObject)w; /* If the src is really a multi, call the multi save */ if (XtIsSubclass(w, multiSrcObjectClass )) return (_XawMultiSave(w)); else if (!XtIsSubclass(w, asciiSrcObjectClass)) XtErrorMsg("bad argument", "asciiSource", "XawError", "XawAsciiSave's parameter must be an asciiSrc or multiSrc.", NULL, NULL); /* * If using the string in place then there is no need to play games * to get the internal info into a readable string. */ if (src->ascii_src.use_string_in_place) return (True); if (src->ascii_src.type == XawAsciiFile) { #ifdef OLDXAW if (!src->ascii_src.changes) #else if (!src->text_src.changed) /* No changes to save */ #endif return (True); if (WritePiecesToFile(src, src->ascii_src.string) == False) return (False); } else { if (src->ascii_src.allocated_string == True) XtFree(src->ascii_src.string); else src->ascii_src.allocated_string = True; src->ascii_src.string = StorePiecesInString(src); } #ifdef OLDXAW src->ascii_src.changes = False; #else src->text_src.changed = False; #endif return (True); } /* * Function: * XawAsciiSaveAsFile * * Arguments: * w - AsciiSrc widget * name - name of the file to save this file into * * Description: * Save the current buffer as a file. * * Returns: * True if the save was sucessful */ Bool XawAsciiSaveAsFile(Widget w, _Xconst char *name) { AsciiSrcObject src = (AsciiSrcObject)w; Bool ret; /* If the src is really a multi, call the multi save */ if (XtIsSubclass( w, multiSrcObjectClass)) return (_XawMultiSaveAsFile(w, name)); else if (!XtIsSubclass(w, asciiSrcObjectClass)) XtErrorMsg("bad argument", "asciiSource", "XawError", "XawAsciiSaveAsFile's 1st parameter must be an " "asciiSrc or multiSrc.", NULL, NULL); if (src->ascii_src.type == XawAsciiFile) ret = WritePiecesToFile(src, (String)name); else { String string = StorePiecesInString(src); ret = WriteToFile(string, (String)name, src->ascii_src.length); XtFree(string); } return (ret); } /* * Function: * XawAsciiSourceChanged * * Parameters: * w - ascii source widget * * Description: * Returns true if the source has changed since last saved. * * Returns: * A Boolean (see description). */ Bool XawAsciiSourceChanged(Widget w) { #ifdef OLDXAW if (XtIsSubclass(w, multiSrcObjectClass)) return (((MultiSrcObject)w)->multi_src.changes); if (XtIsSubclass(w, asciiSrcObjectClass)) return (((AsciiSrcObject)w)->ascii_src.changes); #else if (XtIsSubclass(w, textSrcObjectClass)) return (((TextSrcObject)w)->textSrc.changed); #endif XtErrorMsg("bad argument", "asciiSource", "XawError", "XawAsciiSourceChanged parameter must be an " "asciiSrc or multiSrc.", NULL, NULL); return (True); } /* * Private Functions */ static void RemoveOldStringOrFile(AsciiSrcObject src, Bool checkString) { FreeAllPieces(src); if (checkString && src->ascii_src.allocated_string) { XtFree(src->ascii_src.string); src->ascii_src.allocated_string = False; src->ascii_src.string = NULL; } } /* * Function: * WriteToFile * * Parameters: * string - string to write * name - the name of the file * * Description: * Write the string specified to the begining of the file specified. * * Returns: * returns True if sucessful, False otherwise */ static Bool WriteToFile(String string, String name, unsigned length) { int fd; if ((fd = creat(name, 0666)) == -1) return (False); if (write(fd, string, length) == -1) { close(fd); return (False); } if (close(fd) == -1) return (False); return (True); } /* * Function: * WritePiecesToFile * * Parameters: * src - ascii source object * name - name of the file * * Description: * Almost identical to WriteToFile, but only works for ascii src objects * of type XawAsciiFile. This function avoids allocating temporary memory, * what can be useful when editing very large files. * * Returns: * returns True if sucessful, False otherwise */ static Bool WritePiecesToFile(AsciiSrcObject src, String name) { Piece *piece; int fd; if (src->ascii_src.data_compression) { Piece *tmp; piece = src->ascii_src.first_piece; while (piece) { int bytes = src->ascii_src.piece_size - piece->used; if (bytes > 0 && (tmp = piece->next) != NULL) { bytes = XawMin(bytes, tmp->used); memcpy(piece->text + piece->used, tmp->text, bytes); memmove(tmp->text, tmp->text + bytes, tmp->used - bytes); piece->used += bytes; if ((tmp->used -= bytes) == 0) { RemovePiece(src, tmp); continue; } } piece = piece->next; } } if ((fd = creat(name, 0666)) == -1) return (False); for (piece = src->ascii_src.first_piece; piece; piece = piece->next) if (write(fd, piece->text, piece->used) == -1) { close(fd); return (False); } if (close(fd) == -1) return (False); return (True); } /* * Function: * StorePiecesInString * * Parameters: * data - ascii pointer data * * Description: * Store the pieces in memory into a standard ascii string. */ static String StorePiecesInString(AsciiSrcObject src) { String string; XawTextPosition first; Piece *piece; string = XtMalloc((unsigned)(src->ascii_src.length + 1)); for (first = 0, piece = src->ascii_src.first_piece ; piece != NULL; first += piece->used, piece = piece->next) memcpy(string + first, piece->text, (unsigned)piece->used); string[src->ascii_src.length] = '\0'; /* * This will refill all pieces to capacity */ if (src->ascii_src.data_compression) { FreeAllPieces(src); LoadPieces(src, NULL, string); } return (string); } /* * Function: * InitStringOrFile * * Parameters: * src - AsciiSource * * Description: * Initializes the string or file. */ static FILE * InitStringOrFile(AsciiSrcObject src, Bool newString) { mode_t open_mode = 0; const char *fdopen_mode = NULL; int fd; FILE *file; if (src->ascii_src.type == XawAsciiString) { if (src->ascii_src.string == NULL) src->ascii_src.length = 0; else if (!src->ascii_src.use_string_in_place) { src->ascii_src.string = XtNewString(src->ascii_src.string); src->ascii_src.allocated_string = True; src->ascii_src.length = strlen(src->ascii_src.string); } if (src->ascii_src.use_string_in_place) { if (src->ascii_src.string != NULL) src->ascii_src.length = strlen(src->ascii_src.string); /* In case the length resource is incorrectly set */ if (src->ascii_src.length > src->ascii_src.ascii_length) src->ascii_src.ascii_length = src->ascii_src.length; if (src->ascii_src.ascii_length == MAGIC_VALUE) src->ascii_src.piece_size = src->ascii_src.length; else src->ascii_src.piece_size = src->ascii_src.ascii_length + 1; } return (NULL); } /* * type is XawAsciiFile */ src->ascii_src.is_tempfile = False; switch (src->text_src.edit_mode) { case XawtextRead: if (src->ascii_src.string == NULL) XtErrorMsg("NoFile", "asciiSourceCreate", "XawError", "Creating a read only disk widget and no file specified.", NULL, NULL); open_mode = O_RDONLY; fdopen_mode = "r"; break; case XawtextAppend: case XawtextEdit: if (src->ascii_src.string == NULL) { src->ascii_src.string = "*ascii-src*"; src->ascii_src.is_tempfile = True; } else { /* O_NOFOLLOW is a FreeBSD & Linux extension */ #ifdef O_NOFOLLOW open_mode = O_RDWR | O_NOFOLLOW; #else open_mode = O_RDWR; /* unsafe; subject to race conditions */ #endif /* O_NOFOLLOW */ fdopen_mode = "r+"; } break; default: XtErrorMsg("badMode", "asciiSourceCreate", "XawError", "Bad editMode for ascii source; must be Read, " "Append or Edit.", NULL, NULL); } /* If is_tempfile, allocate a private copy of the text * Unlikely to be changed, just to set allocated_string */ if (newString || src->ascii_src.is_tempfile) { src->ascii_src.string = XtNewString(src->ascii_src.string); src->ascii_src.allocated_string = True; } if (!src->ascii_src.is_tempfile) { if ((fd = open(src->ascii_src.string, open_mode, 0666)) != -1) { if ((file = fdopen(fd, fdopen_mode))) { (void)fseek(file, 0, SEEK_END); src->ascii_src.length = (XawTextPosition)ftell(file); return (file); } } { String params[2]; Cardinal num_params = 2; params[0] = src->ascii_src.string; params[1] = strerror(errno); XtAppWarningMsg(XtWidgetToApplicationContext((Widget)src), "openError", "asciiSourceCreate", "XawWarning", "Cannot open file %s; %s", params, &num_params); } } src->ascii_src.length = 0; return (NULL); } static void LoadPieces(AsciiSrcObject src, FILE *file, char *string) { char *ptr; Piece *piece = NULL; XawTextPosition left; if (string == NULL) { if (src->ascii_src.type == XawAsciiFile) { if (src->ascii_src.length != 0) { int len; left = 0; fseek(file, 0, 0); while (left < src->ascii_src.length) { ptr = XtMalloc((unsigned)src->ascii_src.piece_size); if ((len = fread(ptr, (Size_t)sizeof(unsigned char), (Size_t)src->ascii_src.piece_size, file)) < 0) XtErrorMsg("readError", "asciiSourceCreate", "XawError", "fread returned error.", NULL, NULL); piece = AllocNewPiece(src, piece); piece->text = ptr; piece->used = XawMin(len, src->ascii_src.piece_size); left += piece->used; } } else { piece = AllocNewPiece(src, NULL); piece->text = XtMalloc((unsigned)src->ascii_src.piece_size); piece->used = 0; } return; } else string = src->ascii_src.string; } if (src->ascii_src.use_string_in_place) { piece = AllocNewPiece(src, piece); piece->used = XawMin(src->ascii_src.length, src->ascii_src.piece_size); piece->text = src->ascii_src.string; return; } ptr = string; left = src->ascii_src.length; do { piece = AllocNewPiece(src, piece); piece->text = XtMalloc((unsigned)src->ascii_src.piece_size); piece->used = XawMin(left, src->ascii_src.piece_size); if (piece->used != 0) memcpy(piece->text, ptr, (unsigned)piece->used); left -= piece->used; ptr += piece->used; } while (left > 0); } /* * Function: * AllocNewPiece * * Parameters: * src - AsciiSrc Widget * prev - piece just before this one, or NULL * * Description: * Allocates a new piece of memory. * * Returns: * The allocated piece */ static Piece * AllocNewPiece(AsciiSrcObject src, Piece *prev) { Piece *piece = XtNew(Piece); if (prev == NULL) { src->ascii_src.first_piece = piece; piece->next = NULL; } else { if (prev->next != NULL) (prev->next)->prev = piece; piece->next = prev->next; prev->next = piece; } piece->prev = prev; return (piece); } /* * Function: * FreeAllPieces * * Parameters: * src - AsciiSrc Widget * * Description: * Frees all the pieces. */ static void FreeAllPieces(AsciiSrcObject src) { Piece *next, * first = src->ascii_src.first_piece; #ifdef DEBUG if (first->prev != NULL) printf("Xaw AsciiSrc Object: possible memory leak in FreeAllPieces().\n"); #endif for (; first != NULL ; first = next) { next = first->next; RemovePiece(src, first); } } /* * Function: * RemovePiece * * Parameters: * piece - piece to remove * * Description: * Removes a piece from the list. */ static void RemovePiece(AsciiSrcObject src, Piece *piece) { if (piece->prev == NULL) src->ascii_src.first_piece = piece->next; else piece->prev->next = piece->next; if (piece->next != NULL) piece->next->prev = piece->prev; if (!src->ascii_src.use_string_in_place) XtFree(piece->text); XtFree((char *)piece); } /* * Function: * FindPiece * * Parameters: * src - AsciiSrc Widget * position - position that we are searching for * first - position of the first character in this piece (return) * * Description: * Finds the piece containing the position indicated. * * Returns: * the piece that contains this position */ static Piece * FindPiece(AsciiSrcObject src, XawTextPosition position, XawTextPosition *first) { Piece *old_piece, *piece; XawTextPosition temp; for (old_piece = NULL, piece = src->ascii_src.first_piece, temp = 0; piece; old_piece = piece, piece = piece->next) if ((temp += piece->used) > position) { *first = temp - piece->used; return (piece); } *first = temp - (old_piece ? old_piece->used : 0); return (old_piece); /* if we run off the end the return the last piece */ } /* * Function: * BreakPiece * * Parameters: * src - AsciiSrc Widget * piece - piece to break * * Description: * Breaks a full piece into two new pieces. */ #define HALF_PIECE (src->ascii_src.piece_size >> 1) static void BreakPiece(AsciiSrcObject src, Piece *piece) { Piece *cnew = AllocNewPiece(src, piece); cnew->text = XtMalloc((unsigned)src->ascii_src.piece_size); memcpy(cnew->text, piece->text + HALF_PIECE, (unsigned)(src->ascii_src.piece_size - HALF_PIECE)); piece->used = HALF_PIECE; cnew->used = src->ascii_src.piece_size - HALF_PIECE; } /*ARGSUSED*/ static void CvtStringToAsciiType(XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal) { static XawAsciiType type; XrmQuark q; char name[7]; XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name)); q = XrmStringToQuark(name); if (q == Qstring) type = XawAsciiString; else if (q == Qfile) type = XawAsciiFile; else { toVal->size = 0; toVal->addr = NULL; XtStringConversionWarning((char *)fromVal->addr, XtRAsciiType); } toVal->size = sizeof(XawAsciiType); toVal->addr = (XPointer)&type; } /*ARGSUSED*/ static Boolean CvtAsciiTypeToString(Display *dpy, XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal, XtPointer *data) { static String buffer; Cardinal size; switch (*(XawAsciiType *)fromVal->addr) { case XawAsciiFile: buffer = XtEfile; break; case XawAsciiString: buffer = XtEstring; break; default: XawTypeToStringWarning(dpy, XtRAsciiType); toVal->addr = NULL; toVal->size = 0; return (False); } size = strlen(buffer) + 1; if (toVal->addr != NULL) { if (toVal->size < size) { toVal->size = size; return (False); } strcpy((char *)toVal->addr, buffer); } else toVal->addr = (XPointer)buffer; toVal->size = sizeof(String); return (True); } /*ARGSUSED*/ static void GetDefaultPieceSize(Widget w, int offset, XrmValue *value) { static XPointer pagesize; if (pagesize == NULL) { pagesize = (XPointer)((long)_XawGetPageSize()); if (pagesize < (XPointer)BUFSIZ) pagesize = (XPointer)BUFSIZ; } value->addr = (XPointer)&pagesize; } #if (defined(ASCII_STRING) || defined(ASCII_DISK)) # include #endif #ifdef ASCII_STRING /* * Compatability functions. */ /* * Function: * AsciiStringSourceCreate * * Parameters: * parent - widget that will own this source * args - the argument list * num_args - "" * * Description: * Creates a string source. * * Returns: * A pointer to the new text source. */ Widget XawStringSourceCreate(Widget parent, ArgList args, Cardinal num_args) { XawTextSource src; ArgList ascii_args; Arg temp[2]; XtSetArg(temp[0], XtNtype, XawAsciiString); XtSetArg(temp[1], XtNuseStringInPlace, True); ascii_args = XtMergeArgLists(temp, TWO, args, num_args); src = XtCreateWidget("genericAsciiString", asciiSrcObjectClass, parent, ascii_args, num_args + TWO); XtFree((char *)ascii_args); return (src); } /* * This is hacked up to try to emulate old functionality, it * may not work, as I have not old code to test it on. * * Chris D. Peterson 8/31/89. */ void XawTextSetLastPos(Widget w, XawTextPosition lastPos) { AsciiSrcObject src = (AsciiSrcObject)XawTextGetSource(w); src->ascii_src.piece_size = lastPos; } #endif /* ASCII_STRING */ #ifdef ASCII_DISK /* * Function: * AsciiDiskSourceCreate * * Parameters: * parent - widget that will own this source * args - argument list * num_args - "" * * Description: * Creates a disk source. * * Returns: * A pointer to the new text source */ Widget XawDiskSourceCreate(Widget parent, ArgList args, Cardinal num_args) { XawTextSource src; ArgList ascii_args; Arg temp[1]; int i; XtSetArg(temp[0], XtNtype, XawAsciiFile); ascii_args = XtMergeArgLists(temp, ONE, args, num_args); num_args++; for (i = 0; i < num_args; i++) if (streq(ascii_args[i].name, XtNfile) || streq(ascii_args[i].name, XtCFile)) ascii_args[i].name = XtNstring; src = XtCreateWidget("genericAsciiDisk", asciiSrcObjectClass, parent, ascii_args, num_args); XtFree((char *)ascii_args); return (src); } #endif /* ASCII_DISK */