summaryrefslogtreecommitdiffstats
path: root/generic/tkTextIndex.c
diff options
context:
space:
mode:
authorrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
committerrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
commit066ea7fd88d49cb456f74da71dbe875e4fc0aabb (patch)
tree8fb30cb152c4dc191be47fa043d2e6f5ea38c7ba /generic/tkTextIndex.c
parent13242623d2ff3ea02ab6a62bfb48a7dbb5c27e22 (diff)
downloadtk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.zip
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.gz
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.bz2
Initial revision
Diffstat (limited to 'generic/tkTextIndex.c')
-rw-r--r--generic/tkTextIndex.c840
1 files changed, 840 insertions, 0 deletions
diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c
new file mode 100644
index 0000000..d88d88a
--- /dev/null
+++ b/generic/tkTextIndex.c
@@ -0,0 +1,840 @@
+/*
+ * tkTextIndex.c --
+ *
+ * This module provides procedures that manipulate indices for
+ * text widgets.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkTextIndex.c 1.15 97/06/17 17:49:24
+ */
+
+#include "default.h"
+#include "tkPort.h"
+#include "tkInt.h"
+#include "tkText.h"
+
+/*
+ * Index to use to select last character in line (very large integer):
+ */
+
+#define LAST_CHAR 1000000
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static char * ForwBack _ANSI_ARGS_((char *string,
+ TkTextIndex *indexPtr));
+static char * StartEnd _ANSI_ARGS_(( char *string,
+ TkTextIndex *indexPtr));
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextMakeIndex --
+ *
+ * Given a line index and a character index, look things up
+ * in the B-tree and fill in a TkTextIndex structure.
+ *
+ * Results:
+ * The structure at *indexPtr is filled in with information
+ * about the character at lineIndex and charIndex (or the
+ * closest existing character, if the specified one doesn't
+ * exist), and indexPtr is returned as result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+TkTextIndex *
+TkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
+ TkTextBTree tree; /* Tree that lineIndex and charIndex refer
+ * to. */
+ int lineIndex; /* Index of desired line (0 means first
+ * line of text). */
+ int charIndex; /* Index of desired character. */
+ TkTextIndex *indexPtr; /* Structure to fill in. */
+{
+ register TkTextSegment *segPtr;
+ int index;
+
+ indexPtr->tree = tree;
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ charIndex = 0;
+ }
+ if (charIndex < 0) {
+ charIndex = 0;
+ }
+ indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
+ if (indexPtr->linePtr == NULL) {
+ indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
+ charIndex = 0;
+ }
+
+ /*
+ * Verify that the index is within the range of the line.
+ * If not, just use the index of the last character in the line.
+ */
+
+ for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr == NULL) {
+ indexPtr->charIndex = index-1;
+ break;
+ }
+ index += segPtr->size;
+ if (index > charIndex) {
+ indexPtr->charIndex = charIndex;
+ break;
+ }
+ }
+ return indexPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextIndexToSeg --
+ *
+ * Given an index, this procedure returns the segment and
+ * offset within segment for the index.
+ *
+ * Results:
+ * The return value is a pointer to the segment referred to
+ * by indexPtr; this will always be a segment with non-zero
+ * size. The variable at *offsetPtr is set to hold the
+ * integer offset within the segment of the character
+ * given by indexPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+TkTextSegment *
+TkTextIndexToSeg(indexPtr, offsetPtr)
+ TkTextIndex *indexPtr; /* Text index. */
+ int *offsetPtr; /* Where to store offset within
+ * segment, or NULL if offset isn't
+ * wanted. */
+{
+ register TkTextSegment *segPtr;
+ int offset;
+
+ for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
+ offset >= segPtr->size;
+ offset -= segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if (offsetPtr != NULL) {
+ *offsetPtr = offset;
+ }
+ return segPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextSegToOffset --
+ *
+ * Given a segment pointer and the line containing it, this
+ * procedure returns the offset of the segment within its
+ * line.
+ *
+ * Results:
+ * The return value is the offset (within its line) of the
+ * first character in segPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+TkTextSegToOffset(segPtr, linePtr)
+ TkTextSegment *segPtr; /* Segment whose offset is desired. */
+ TkTextLine *linePtr; /* Line containing segPtr. */
+{
+ TkTextSegment *segPtr2;
+ int offset;
+
+ offset = 0;
+ for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
+ segPtr2 = segPtr2->nextPtr) {
+ offset += segPtr2->size;
+ }
+ return offset;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextGetIndex --
+ *
+ * Given a string, return the line and character indices that
+ * it describes.
+ *
+ * Results:
+ * The return value is a standard Tcl return result. If
+ * TCL_OK is returned, then everything went well and the index
+ * at *indexPtr is filled in; otherwise TCL_ERROR is returned
+ * and an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkTextGetIndex(interp, textPtr, string, indexPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ TkText *textPtr; /* Information about text widget. */
+ char *string; /* Textual description of position. */
+ TkTextIndex *indexPtr; /* Index structure to fill in. */
+{
+ register char *p;
+ char *end, *endOfBase;
+ Tcl_HashEntry *hPtr;
+ TkTextTag *tagPtr;
+ TkTextSearch search;
+ TkTextIndex first, last;
+ int wantLast, result;
+ char c;
+
+ /*
+ *---------------------------------------------------------------------
+ * Stage 1: check to see if the index consists of nothing but a mark
+ * name. We do this check now even though it's also done later, in
+ * order to allow mark names that include funny characters such as
+ * spaces or "+1c".
+ *---------------------------------------------------------------------
+ */
+
+ if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
+ return TCL_OK;
+ }
+
+ /*
+ *------------------------------------------------
+ * Stage 2: start again by parsing the base index.
+ *------------------------------------------------
+ */
+
+ indexPtr->tree = textPtr->tree;
+
+ /*
+ * First look for the form "tag.first" or "tag.last" where "tag"
+ * is the name of a valid tag. Try to use up as much as possible
+ * of the string in this check (strrchr instead of strchr below).
+ * Doing the check now, and in this way, allows tag names to include
+ * funny characters like "@" or "+1c".
+ */
+
+ p = strrchr(string, '.');
+ if (p != NULL) {
+ if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
+ wantLast = 0;
+ endOfBase = p+6;
+ } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
+ wantLast = 1;
+ endOfBase = p+5;
+ } else {
+ goto tryxy;
+ }
+ *p = 0;
+ hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
+ *p = '.';
+ if (hPtr == NULL) {
+ goto tryxy;
+ }
+ tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
+ TkTextMakeIndex(textPtr->tree, 0, 0, &first);
+ TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
+ &last);
+ TkBTreeStartSearch(&first, &last, tagPtr, &search);
+ if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
+ Tcl_AppendResult(interp,
+ "text doesn't contain any characters tagged with \"",
+ Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = search.curIndex;
+ if (wantLast) {
+ while (TkBTreeNextTag(&search)) {
+ *indexPtr = search.curIndex;
+ }
+ }
+ goto gotBase;
+ }
+
+ tryxy:
+ if (string[0] == '@') {
+ /*
+ * Find character at a given x,y location in the window.
+ */
+
+ int x, y;
+
+ p = string+1;
+ x = strtol(p, &end, 0);
+ if ((end == p) || (*end != ',')) {
+ goto error;
+ }
+ p = end+1;
+ y = strtol(p, &end, 0);
+ if (end == p) {
+ goto error;
+ }
+ TkTextPixelIndex(textPtr, x, y, indexPtr);
+ endOfBase = end;
+ goto gotBase;
+ }
+
+ if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
+ int lineIndex, charIndex;
+
+ /*
+ * Base is identified with line and character indices.
+ */
+
+ lineIndex = strtol(string, &end, 0) - 1;
+ if ((end == string) || (*end != '.')) {
+ goto error;
+ }
+ p = end+1;
+ if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
+ charIndex = LAST_CHAR;
+ endOfBase = p+3;
+ } else {
+ charIndex = strtol(p, &end, 0);
+ if (end == p) {
+ goto error;
+ }
+ endOfBase = end;
+ }
+ TkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
+ goto gotBase;
+ }
+
+ for (p = string; *p != 0; p++) {
+ if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
+ break;
+ }
+ }
+ endOfBase = p;
+ if (string[0] == '.') {
+ /*
+ * See if the base position is the name of an embedded window.
+ */
+
+ c = *endOfBase;
+ *endOfBase = 0;
+ result = TkTextWindowIndex(textPtr, string, indexPtr);
+ *endOfBase = c;
+ if (result != 0) {
+ goto gotBase;
+ }
+ }
+ if ((string[0] == 'e')
+ && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
+ /*
+ * Base position is end of text.
+ */
+
+ TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
+ 0, indexPtr);
+ goto gotBase;
+ } else {
+ /*
+ * See if the base position is the name of a mark.
+ */
+
+ c = *endOfBase;
+ *endOfBase = 0;
+ result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
+ *endOfBase = c;
+ if (result == TCL_OK) {
+ goto gotBase;
+ }
+
+ /*
+ * See if the base position is the name of an embedded image
+ */
+
+ c = *endOfBase;
+ *endOfBase = 0;
+ result = TkTextImageIndex(textPtr, string, indexPtr);
+ *endOfBase = c;
+ if (result != 0) {
+ goto gotBase;
+ }
+ }
+ goto error;
+
+ /*
+ *-------------------------------------------------------------------
+ * Stage 3: process zero or more modifiers. Each modifier is either
+ * a keyword like "wordend" or "linestart", or it has the form
+ * "op count units" where op is + or -, count is a number, and units
+ * is "chars" or "lines".
+ *-------------------------------------------------------------------
+ */
+
+ gotBase:
+ p = endOfBase;
+ while (1) {
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p == 0) {
+ break;
+ }
+
+ if ((*p == '+') || (*p == '-')) {
+ p = ForwBack(p, indexPtr);
+ } else {
+ p = StartEnd(p, indexPtr);
+ }
+ if (p == NULL) {
+ goto error;
+ }
+ }
+ return TCL_OK;
+
+ error:
+ Tcl_AppendResult(interp, "bad text index \"", string, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextPrintIndex --
+ *
+ *
+ * This procedure generates a string description of an index,
+ * suitable for reading in again later.
+ *
+ * Results:
+ * The characters pointed to by string are modified.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkTextPrintIndex(indexPtr, string)
+ TkTextIndex *indexPtr; /* Pointer to index. */
+ char *string; /* Place to store the position. Must have
+ * at least TK_POS_CHARS characters. */
+{
+ sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
+ indexPtr->charIndex);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkTextIndexCmp --
+ *
+ * Compare two indices to see which one is earlier in
+ * the text.
+ *
+ * Results:
+ * The return value is 0 if index1Ptr and index2Ptr refer
+ * to the same position in the file, -1 if index1Ptr refers
+ * to an earlier position than index2Ptr, and 1 otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+TkTextIndexCmp(index1Ptr, index2Ptr)
+ TkTextIndex *index1Ptr; /* First index. */
+ TkTextIndex *index2Ptr; /* Second index. */
+{
+ int line1, line2;
+
+ if (index1Ptr->linePtr == index2Ptr->linePtr) {
+ if (index1Ptr->charIndex < index2Ptr->charIndex) {
+ return -1;
+ } else if (index1Ptr->charIndex > index2Ptr->charIndex) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ line1 = TkBTreeLineIndex(index1Ptr->linePtr);
+ line2 = TkBTreeLineIndex(index2Ptr->linePtr);
+ if (line1 < line2) {
+ return -1;
+ }
+ if (line1 > line2) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ForwBack --
+ *
+ * This procedure handles +/- modifiers for indices to adjust
+ * the index forwards or backwards.
+ *
+ * Results:
+ * If the modifier in string is successfully parsed then the
+ * return value is the address of the first character after the
+ * modifier, and *indexPtr is updated to reflect the modifier.
+ * If there is a syntax error in the modifier then NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+ForwBack(string, indexPtr)
+ char *string; /* String to parse for additional info
+ * about modifier (count and units).
+ * Points to "+" or "-" that starts
+ * modifier. */
+ TkTextIndex *indexPtr; /* Index to update as specified in string. */
+{
+ register char *p;
+ char *end, *units;
+ int count, lineIndex;
+ size_t length;
+
+ /*
+ * Get the count (how many units forward or backward).
+ */
+
+ p = string+1;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ count = strtol(p, &end, 0);
+ if (end == p) {
+ return NULL;
+ }
+ p = end;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+
+ /*
+ * Find the end of this modifier (next space or + or - character),
+ * then parse the unit specifier and update the position
+ * accordingly.
+ */
+
+ units = p;
+ while ((*p != 0) && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
+ p++;
+ }
+ length = p - units;
+ if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
+ if (*string == '+') {
+ TkTextIndexForwChars(indexPtr, count, indexPtr);
+ } else {
+ TkTextIndexBackChars(indexPtr, count, indexPtr);
+ }
+ } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
+ lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
+ if (*string == '+') {
+ lineIndex += count;
+ } else {
+ lineIndex -= count;
+
+ /*
+ * The check below retains the character position, even
+ * if the line runs off the start of the file. Without
+ * it, the character position will get reset to 0 by
+ * TkTextMakeIndex.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ }
+ }
+ TkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
+ indexPtr);
+ } else {
+ return NULL;
+ }
+ return p;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextIndexForwChars --
+ *
+ * Given an index for a text widget, this procedure creates a
+ * new index that points "count" characters ahead of the source
+ * index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" characters
+ * after srcPtr, or to the last character in the file if there aren't
+ * "count" characters left in the file.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+TkTextIndexForwChars(srcPtr, count, dstPtr)
+ TkTextIndex *srcPtr; /* Source index. */
+ int count; /* How many characters forward to
+ * move. May be negative. */
+ TkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ TkTextLine *linePtr;
+ TkTextSegment *segPtr;
+ int lineLength;
+
+ if (count < 0) {
+ TkTextIndexBackChars(srcPtr, -count, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+ dstPtr->charIndex += count;
+ while (1) {
+ /*
+ * Compute the length of the current line.
+ */
+
+ lineLength = 0;
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ lineLength += segPtr->size;
+ }
+
+ /*
+ * If the new index is in the same line then we're done.
+ * Otherwise go on to the next line.
+ */
+
+ if (dstPtr->charIndex < lineLength) {
+ return;
+ }
+ dstPtr->charIndex -= lineLength;
+ linePtr = TkBTreeNextLine(dstPtr->linePtr);
+ if (linePtr == NULL) {
+ dstPtr->charIndex = lineLength - 1;
+ return;
+ }
+ dstPtr->linePtr = linePtr;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkTextIndexBackChars --
+ *
+ * Given an index for a text widget, this procedure creates a
+ * new index that points "count" characters earlier than the
+ * source index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" characters
+ * before srcPtr, or to the first character in the file if there aren't
+ * "count" characters earlier than srcPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkTextIndexBackChars(srcPtr, count, dstPtr)
+ TkTextIndex *srcPtr; /* Source index. */
+ int count; /* How many characters backward to
+ * move. May be negative. */
+ TkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ TkTextSegment *segPtr;
+ int lineIndex;
+
+ if (count < 0) {
+ TkTextIndexForwChars(srcPtr, -count, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+ dstPtr->charIndex -= count;
+ lineIndex = -1;
+ while (dstPtr->charIndex < 0) {
+ /*
+ * Move back one line in the text. If we run off the beginning
+ * of the file then just return the first character in the text.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
+ }
+ if (lineIndex == 0) {
+ dstPtr->charIndex = 0;
+ return;
+ }
+ lineIndex--;
+ dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
+
+ /*
+ * Compute the length of the line and add that to dstPtr->charIndex.
+ */
+
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ dstPtr->charIndex += segPtr->size;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StartEnd --
+ *
+ * This procedure handles modifiers like "wordstart" and "lineend"
+ * to adjust indices forwards or backwards.
+ *
+ * Results:
+ * If the modifier is successfully parsed then the return value
+ * is the address of the first character after the modifier, and
+ * *indexPtr is updated to reflect the modifier. If there is a
+ * syntax error in the modifier then NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+StartEnd(string, indexPtr)
+ char *string; /* String to parse for additional info
+ * about modifier (count and units).
+ * Points to first character of modifer
+ * word. */
+ TkTextIndex *indexPtr; /* Index to mdoify based on string. */
+{
+ char *p;
+ int c, offset;
+ size_t length;
+ register TkTextSegment *segPtr;
+
+ /*
+ * Find the end of the modifier word.
+ */
+
+ for (p = string; isalnum(UCHAR(*p)); p++) {
+ /* Empty loop body. */
+ }
+ length = p-string;
+ if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
+ && (length >= 5)) {
+ indexPtr->charIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ indexPtr->charIndex += segPtr->size;
+ }
+ indexPtr->charIndex -= 1;
+ } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
+ && (length >= 5)) {
+ indexPtr->charIndex = 0;
+ } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
+ && (length >= 5)) {
+ int firstChar = 1;
+
+ /*
+ * If the current character isn't part of a word then just move
+ * forward one character. Otherwise move forward until finding
+ * a character that isn't part of a word and stop there.
+ */
+
+ segPtr = TkTextIndexToSeg(indexPtr, &offset);
+ while (1) {
+ if (segPtr->typePtr == &tkTextCharType) {
+ c = segPtr->body.chars[offset];
+ if (!isalnum(UCHAR(c)) && (c != '_')) {
+ break;
+ }
+ firstChar = 0;
+ }
+ offset += 1;
+ indexPtr->charIndex += 1;
+ if (offset >= segPtr->size) {
+ segPtr = TkTextIndexToSeg(indexPtr, &offset);
+ }
+ }
+ if (firstChar) {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ }
+ } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
+ && (length >= 5)) {
+ int firstChar = 1;
+
+ /*
+ * Starting with the current character, look for one that's not
+ * part of a word and keep moving backward until you find one.
+ * Then if the character found wasn't the first one, move forward
+ * again one position.
+ */
+
+ segPtr = TkTextIndexToSeg(indexPtr, &offset);
+ while (1) {
+ if (segPtr->typePtr == &tkTextCharType) {
+ c = segPtr->body.chars[offset];
+ if (!isalnum(UCHAR(c)) && (c != '_')) {
+ break;
+ }
+ firstChar = 0;
+ }
+ offset -= 1;
+ indexPtr->charIndex -= 1;
+ if (offset < 0) {
+ if (indexPtr->charIndex < 0) {
+ indexPtr->charIndex = 0;
+ goto done;
+ }
+ segPtr = TkTextIndexToSeg(indexPtr, &offset);
+ }
+ }
+ if (!firstChar) {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ }
+ } else {
+ return NULL;
+ }
+ done:
+ return p;
+}