4 * This module provides procedures that manipulate indices for
7 * Copyright (c) 1992-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9 * Copyright (c) 1995-2000 Christian Werner
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20 * Index to use to select last character in line (very large integer):
23 #define LAST_CHAR 1000000
26 * Forward declarations for procedures defined later in this file:
29 static char * ForwBack _ANSI_ARGS_((char *string,
30 CkTextIndex *indexPtr));
31 static char * StartEnd _ANSI_ARGS_(( char *string,
32 CkTextIndex *indexPtr));
36 *---------------------------------------------------------------------------
38 * CkTextMakeByteIndex --
40 * Given a line index and a byte index, look things up in the B-tree
41 * and fill in a CkTextIndex structure.
44 * The structure at *indexPtr is filled in with information about the
45 * character at lineIndex and byteIndex (or the closest existing
46 * character, if the specified one doesn't exist), and indexPtr is
52 *---------------------------------------------------------------------------
56 CkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
57 CkTextBTree tree; /* Tree that lineIndex and charIndex refer
59 int lineIndex; /* Index of desired line (0 means first
61 int byteIndex; /* Byte index of desired character. */
62 CkTextIndex *indexPtr; /* Structure to fill in. */
64 CkTextSegment *segPtr;
69 indexPtr->tree = tree;
77 indexPtr->linePtr = CkBTreeFindLine(tree, lineIndex);
78 if (indexPtr->linePtr == NULL) {
79 indexPtr->linePtr = CkBTreeFindLine(tree, CkBTreeNumLines(tree));
83 indexPtr->charIndex = byteIndex;
88 * Verify that the index is within the range of the line and points
89 * to a valid character boundary.
93 for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
96 * Use the index of the last character in the line. Since
97 * the last character on the line is guaranteed to be a '\n',
98 * we can back up a constant sizeof(char) bytes.
101 indexPtr->charIndex = index - sizeof(char);
104 if (index + segPtr->size > byteIndex) {
105 indexPtr->charIndex = byteIndex;
106 if ((byteIndex > index) && (segPtr->typePtr == &ckTextCharType)) {
108 * Prevent UTF-8 character from being split up by ensuring
109 * that byteIndex falls on a character boundary. If index
110 * falls in the middle of a UTF-8 character, it will be
111 * adjusted to the end of that UTF-8 character.
114 start = segPtr->body.chars + (byteIndex - index);
115 p = Tcl_UtfPrev(start, segPtr->body.chars);
116 p += Tcl_UtfToUniChar(p, &ch);
117 indexPtr->charIndex += p - start;
121 index += segPtr->size;
128 *--------------------------------------------------------------
132 * Given a line index and a character index, look things up
133 * in the B-tree and fill in a CkTextIndex structure.
136 * The structure at *indexPtr is filled in with information
137 * about the character at lineIndex and charIndex (or the
138 * closest existing character, if the specified one doesn't
139 * exist), and indexPtr is returned as result.
144 *--------------------------------------------------------------
148 CkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
149 CkTextBTree tree; /* Tree that lineIndex and charIndex refer
151 int lineIndex; /* Index of desired line (0 means first
153 int charIndex; /* Index of desired character. */
154 CkTextIndex *indexPtr; /* Structure to fill in. */
156 register CkTextSegment *segPtr;
159 char *p, *start, *end;
164 indexPtr->tree = tree;
172 indexPtr->linePtr = CkBTreeFindLine(tree, lineIndex);
173 if (indexPtr->linePtr == NULL) {
174 indexPtr->linePtr = CkBTreeFindLine(tree, CkBTreeNumLines(tree));
179 * Verify that the index is within the range of the line.
180 * If not, just use the index of the last character in the line.
183 for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
184 segPtr = segPtr->nextPtr) {
185 if (segPtr == NULL) {
186 indexPtr->charIndex = index-1;
190 if (segPtr->typePtr == &ckTextCharType) {
192 * Turn character offset into a byte offset.
195 start = segPtr->body.chars;
196 end = start + segPtr->size;
197 for (p = start; p < end; p += offset) {
198 if (charIndex == 0) {
199 indexPtr->charIndex = index;
203 offset = Tcl_UtfToUniChar(p, &ch);
207 if (charIndex < segPtr->size) {
208 indexPtr->charIndex = index;
211 charIndex -= segPtr->size;
212 index += segPtr->size;
215 index += segPtr->size;
216 if (index > charIndex) {
217 indexPtr->charIndex = charIndex;
226 *--------------------------------------------------------------
228 * CkTextIndexToSeg --
230 * Given an index, this procedure returns the segment and
231 * offset within segment for the index.
234 * The return value is a pointer to the segment referred to
235 * by indexPtr; this will always be a segment with non-zero
236 * size. The variable at *offsetPtr is set to hold the
237 * integer offset within the segment of the character
243 *--------------------------------------------------------------
247 CkTextIndexToSeg(indexPtr, offsetPtr)
248 CkTextIndex *indexPtr; /* Text index. */
249 int *offsetPtr; /* Where to store offset within
250 * segment, or NULL if offset isn't
253 register CkTextSegment *segPtr;
256 for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
257 offset >= segPtr->size;
258 offset -= segPtr->size, segPtr = segPtr->nextPtr) {
259 /* Empty loop body. */
261 if (offsetPtr != NULL) {
268 *--------------------------------------------------------------
270 * CkTextSegToOffset --
272 * Given a segment pointer and the line containing it, this
273 * procedure returns the offset of the segment within its
277 * The return value is the offset (within its line) of the
278 * first character in segPtr.
283 *--------------------------------------------------------------
287 CkTextSegToOffset(segPtr, linePtr)
288 CkTextSegment *segPtr; /* Segment whose offset is desired. */
289 CkTextLine *linePtr; /* Line containing segPtr. */
291 CkTextSegment *segPtr2;
295 for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
296 segPtr2 = segPtr2->nextPtr) {
297 offset += segPtr2->size;
303 *----------------------------------------------------------------------
307 * Given a string, return the line and character indices that
311 * The return value is a standard Tcl return result. If
312 * TCL_OK is returned, then everything went well and the index
313 * at *indexPtr is filled in; otherwise TCL_ERROR is returned
314 * and an error message is left in interp->result.
319 *----------------------------------------------------------------------
323 CkTextGetIndex(interp, textPtr, string, indexPtr)
324 Tcl_Interp *interp; /* Use this for error reporting. */
325 CkText *textPtr; /* Information about text widget. */
326 char *string; /* Textual description of position. */
327 CkTextIndex *indexPtr; /* Index structure to fill in. */
330 char *end, *endOfBase;
334 CkTextIndex first, last;
335 int wantLast, result;
339 *---------------------------------------------------------------------
340 * Stage 1: check to see if the index consists of nothing but a mar
341 * name. We do this check now even though it's also done later, in
342 * order to allow mark names that include funny characters such as
344 *---------------------------------------------------------------------
347 if (CkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
352 *------------------------------------------------
353 * Stage 2: start again by parsing the base index.
354 *------------------------------------------------
357 indexPtr->tree = textPtr->tree;
360 * First look for the form "tag.first" or "tag.last" where "tag"
361 * is the name of a valid tag. Try to use up as much as possible
362 * of the string in this check (strrchr instead of strchr below).
363 * Doing the check now, and in this way, allows tag names to include
364 * funny characters like "@" or "+1c".
367 p = strrchr(string, '.');
369 if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
372 } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
379 hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
384 tagPtr = (CkTextTag *) Tcl_GetHashValue(hPtr);
386 CkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
387 CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree), 0,
390 CkTextMakeIndex(textPtr->tree, 0, 0, &first);
391 CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree), 0,
394 CkBTreeStartSearch(&first, &last, tagPtr, &search);
395 if (!CkBTreeCharTagged(&first, tagPtr) && !CkBTreeNextTag(&search)) {
396 Tcl_AppendResult(interp,
397 "text doesn't contain any characters tagged with \"",
398 Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
402 *indexPtr = search.curIndex;
404 while (CkBTreeNextTag(&search)) {
405 *indexPtr = search.curIndex;
412 if (string[0] == '@') {
414 * Find character at a given x,y location in the window.
420 x = strtol(p, &end, 0);
421 if ((end == p) || (*end != ',')) {
425 y = strtol(p, &end, 0);
429 CkTextPixelIndex(textPtr, x, y, indexPtr);
434 if (isdigit((unsigned char) string[0]) || (string[0] == '-')) {
435 int lineIndex, charIndex;
438 * Base is identified with line and character indices.
441 lineIndex = strtol(string, &end, 0) - 1;
442 if ((end == string) || (*end != '.')) {
446 if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
447 charIndex = LAST_CHAR;
450 charIndex = strtol(p, &end, 0);
456 CkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
460 for (p = string; *p != 0; p++) {
461 if (isspace((unsigned char) *p) || (*p == '+') || (*p == '-')) {
467 if (string[0] == '.') {
469 * See if the base position is the name of an embedded window.
474 result = CkTextWindowIndex(textPtr, string, indexPtr);
481 if ((string[0] == 'e')
482 && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
484 * Base position is end of text.
488 CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
491 CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
497 * See if the base position is the name of a mark.
502 result = CkTextMarkNameToIndex(textPtr, string, indexPtr);
504 if (result == TCL_OK) {
511 *-------------------------------------------------------------------
512 * Stage 3: process zero or more modifiers. Each modifier is either
513 * a keyword like "wordend" or "linestart", or it has the form
514 * "op count units" where op is + or -, count is a number, and units
515 * is "chars" or "lines".
516 *-------------------------------------------------------------------
522 while (isspace((unsigned char) *p)) {
529 if ((*p == '+') || (*p == '-')) {
530 p = ForwBack(p, indexPtr);
532 p = StartEnd(p, indexPtr);
541 Tcl_AppendResult(interp, "bad text index \"", string, "\"",
547 *----------------------------------------------------------------------
549 * CkTextPrintIndex --
552 * This procedure generates a string description of an index,
553 * suitable for reading in again later.
556 * The characters pointed to by string are modified.
561 *----------------------------------------------------------------------
565 CkTextPrintIndex(indexPtr, string)
566 CkTextIndex *indexPtr; /* Pointer to index. */
567 char *string; /* Place to store the position. Must have
568 * at least TK_POS_CHARS characters. */
571 CkTextSegment *segPtr;
572 int numBytes, charIndex;
574 numBytes = indexPtr->charIndex;
576 for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
577 if (numBytes <= segPtr->size) {
580 if (segPtr->typePtr == &ckTextCharType) {
581 charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
583 charIndex += segPtr->size;
585 numBytes -= segPtr->size;
587 if (segPtr->typePtr == &ckTextCharType) {
588 charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
590 charIndex += numBytes;
592 sprintf(string, "%d.%d", CkBTreeLineIndex(indexPtr->linePtr) + 1,
595 sprintf(string, "%d.%d", CkBTreeLineIndex(indexPtr->linePtr) + 1,
596 indexPtr->charIndex);
601 *--------------------------------------------------------------
605 * Compare two indices to see which one is earlier in
609 * The return value is 0 if index1Ptr and index2Ptr refer
610 * to the same position in the file, -1 if index1Ptr refers
611 * to an earlier position than index2Ptr, and 1 otherwise.
616 *--------------------------------------------------------------
620 CkTextIndexCmp(index1Ptr, index2Ptr)
621 CkTextIndex *index1Ptr; /* First index. */
622 CkTextIndex *index2Ptr; /* Second index. */
626 if (index1Ptr->linePtr == index2Ptr->linePtr) {
627 if (index1Ptr->charIndex < index2Ptr->charIndex) {
629 } else if (index1Ptr->charIndex > index2Ptr->charIndex) {
635 line1 = CkBTreeLineIndex(index1Ptr->linePtr);
636 line2 = CkBTreeLineIndex(index2Ptr->linePtr);
647 *----------------------------------------------------------------------
651 * This procedure handles +/- modifiers for indices to adjust
652 * the index forwards or backwards.
655 * If the modifier in string is successfully parsed then the
656 * return value is the address of the first character after the
657 * modifier, and *indexPtr is updated to reflect the modifier.
658 * If there is a syntax error in the modifier then NULL is returned.
663 *----------------------------------------------------------------------
667 ForwBack(string, indexPtr)
668 char *string; /* String to parse for additional info
669 * about modifier (count and units).
670 * Points to "+" or "-" that starts
672 CkTextIndex *indexPtr; /* Index to update as specified in string. */
676 int count, lineIndex;
680 * Get the count (how many units forward or backward).
684 while (isspace((unsigned char) *p)) {
687 count = strtol(p, &end, 0);
692 while (isspace((unsigned char) *p)) {
697 * Find the end of this modifier (next space or + or - character),
698 * then parse the unit specifier and update the position
703 while ((*p != 0) && !isspace((unsigned char) *p)
704 && (*p != '+') && (*p != '-')) {
708 if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
709 if (*string == '+') {
710 CkTextIndexForwChars(indexPtr, count, indexPtr);
712 CkTextIndexBackChars(indexPtr, count, indexPtr);
714 } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
715 lineIndex = CkBTreeLineIndex(indexPtr->linePtr);
716 if (*string == '+') {
722 * The check below retains the character position, even
723 * if the line runs off the start of the file. Without
724 * it, the character position will get reset to 0 by
733 CkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
736 CkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
747 *---------------------------------------------------------------------------
749 * CkTextIndexForwBytes --
751 * Given an index for a text widget, this procedure creates a new
752 * index that points "count" bytes ahead of the source index.
755 * *dstPtr is modified to refer to the character "count" bytes after
756 * srcPtr, or to the last character in the CkText if there aren't
757 * "count" bytes left.
762 *---------------------------------------------------------------------------
766 CkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
767 CkTextIndex *srcPtr; /* Source index. */
768 int byteCount; /* How many bytes forward to move. May be
770 CkTextIndex *dstPtr; /* Destination index: gets modified. */
773 CkTextSegment *segPtr;
777 CkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
782 dstPtr->charIndex += byteCount;
785 * Compute the length of the current line.
789 for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
790 segPtr = segPtr->nextPtr) {
791 lineLength += segPtr->size;
795 * If the new index is in the same line then we're done.
796 * Otherwise go on to the next line.
799 if (dstPtr->charIndex < lineLength) {
802 dstPtr->charIndex -= lineLength;
803 linePtr = CkBTreeNextLine(dstPtr->linePtr);
804 if (linePtr == NULL) {
805 dstPtr->charIndex = lineLength - 1;
808 dstPtr->linePtr = linePtr;
814 *----------------------------------------------------------------------
816 * CkTextIndexForwChars --
818 * Given an index for a text widget, this procedure creates a
819 * new index that points "count" characters ahead of the source
823 * *dstPtr is modified to refer to the character "count" characters
824 * after srcPtr, or to the last character in the file if there aren't
825 * "count" characters left in the file.
830 *----------------------------------------------------------------------
834 CkTextIndexForwChars(srcPtr, count, dstPtr)
835 CkTextIndex *srcPtr; /* Source index. */
836 int count; /* How many characters forward to
837 * move. May be negative. */
838 CkTextIndex *dstPtr; /* Destination index: gets modified. */
841 CkTextSegment *segPtr;
845 char *p, *start, *end;
850 CkTextIndexBackChars(srcPtr, -count, dstPtr);
857 segPtr = CkTextIndexToSeg(dstPtr, &byteOffset);
861 * Go through each segment in line looking for specified character
865 for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
866 if (segPtr->typePtr == &ckTextCharType) {
867 start = segPtr->body.chars + byteOffset;
868 end = segPtr->body.chars + segPtr->size;
869 for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
871 dstPtr->charIndex += (p - start);
877 if (count < segPtr->size - byteOffset) {
878 dstPtr->charIndex += count;
881 count -= segPtr->size - byteOffset;
883 dstPtr->charIndex += segPtr->size - byteOffset;
888 * Go to the next line. If we are at the end of the text item,
889 * back up one byte (for the terminal '\n' character) and return
893 linePtr = CkBTreeNextLine(dstPtr->linePtr);
894 if (linePtr == NULL) {
895 dstPtr->charIndex -= sizeof(char);
898 dstPtr->linePtr = linePtr;
899 dstPtr->charIndex = 0;
900 segPtr = dstPtr->linePtr->segPtr;
903 dstPtr->charIndex += count;
906 * Compute the length of the current line.
910 for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
911 segPtr = segPtr->nextPtr) {
912 lineLength += segPtr->size;
916 * If the new index is in the same line then we're done.
917 * Otherwise go on to the next line.
920 if (dstPtr->charIndex < lineLength) {
923 dstPtr->charIndex -= lineLength;
924 linePtr = CkBTreeNextLine(dstPtr->linePtr);
925 if (linePtr == NULL) {
926 dstPtr->charIndex = lineLength - 1;
929 dstPtr->linePtr = linePtr;
936 *---------------------------------------------------------------------------
938 * CkTextIndexBackBytes --
940 * Given an index for a text widget, this procedure creates a new
941 * index that points "count" bytes earlier than the source index.
944 * *dstPtr is modified to refer to the character "count" bytes before
945 * srcPtr, or to the first character in the CkText if there aren't
946 * "count" bytes earlier than srcPtr.
951 *---------------------------------------------------------------------------
955 CkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
956 CkTextIndex *srcPtr; /* Source index. */
957 int byteCount; /* How many bytes backward to move. May be
959 CkTextIndex *dstPtr; /* Destination index: gets modified. */
961 CkTextSegment *segPtr;
965 CkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
970 dstPtr->charIndex -= byteCount;
972 while (dstPtr->charIndex < 0) {
974 * Move back one line in the text. If we run off the beginning
975 * of the file then just return the first character in the text.
979 lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
981 if (lineIndex == 0) {
982 dstPtr->charIndex = 0;
986 dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
989 * Compute the length of the line and add that to dstPtr->charIndex.
992 for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
993 segPtr = segPtr->nextPtr) {
994 dstPtr->charIndex += segPtr->size;
1001 *----------------------------------------------------------------------
1003 * CkTextIndexBackChars --
1005 * Given an index for a text widget, this procedure creates a
1006 * new index that points "count" characters earlier than the
1010 * *dstPtr is modified to refer to the character "count" characters
1011 * before srcPtr, or to the first character in the file if there aren't
1012 * "count" characters earlier than srcPtr.
1017 *----------------------------------------------------------------------
1021 CkTextIndexBackChars(srcPtr, count, dstPtr)
1022 CkTextIndex *srcPtr; /* Source index. */
1023 int count; /* How many characters backward to
1024 * move. May be negative. */
1025 CkTextIndex *dstPtr; /* Destination index: gets modified. */
1027 CkTextSegment *segPtr;
1030 CkTextSegment *oldPtr;
1032 char *p, *start, *end;
1036 CkTextIndexForwChars(srcPtr, -count, dstPtr);
1044 * Find offset within seg that contains byteIndex.
1045 * Move backward specified number of chars.
1050 segSize = dstPtr->charIndex;
1051 for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
1052 if (segSize <= segPtr->size) {
1055 segSize -= segPtr->size;
1058 if (segPtr->typePtr == &ckTextCharType) {
1059 start = segPtr->body.chars;
1060 end = segPtr->body.chars + segSize;
1061 for (p = end; ; p = Tcl_UtfPrev(p, start)) {
1063 dstPtr->charIndex -= (end - p);
1072 if (count <= segSize) {
1073 dstPtr->charIndex -= count;
1078 dstPtr->charIndex -= segSize;
1081 * Move back into previous segment.
1085 segPtr = dstPtr->linePtr->segPtr;
1086 if (segPtr != oldPtr) {
1087 for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
1090 segSize = segPtr->size;
1095 * Move back to previous line.
1098 if (lineIndex < 0) {
1099 lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
1101 if (lineIndex == 0) {
1102 dstPtr->charIndex = 0;
1106 dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
1109 * Compute the length of the line and add that to dstPtr->byteIndex.
1112 oldPtr = dstPtr->linePtr->segPtr;
1113 for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
1114 dstPtr->charIndex += segPtr->size;
1118 segSize = segPtr->size;
1121 dstPtr->charIndex -= count;
1123 while (dstPtr->charIndex < 0) {
1125 * Move back one line in the text. If we run off the beginning
1126 * of the file then just return the first character in the text.
1129 if (lineIndex < 0) {
1130 lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
1132 if (lineIndex == 0) {
1133 dstPtr->charIndex = 0;
1137 dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
1140 * Compute the length of the line and add that to dstPtr->charIndex.
1143 for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
1144 segPtr = segPtr->nextPtr) {
1145 dstPtr->charIndex += segPtr->size;
1152 *----------------------------------------------------------------------
1156 * This procedure handles modifiers like "wordstart" and "lineend"
1157 * to adjust indices forwards or backwards.
1160 * If the modifier is successfully parsed then the return value
1161 * is the address of the first character after the modifier, and
1162 * *indexPtr is updated to reflect the modifier. If there is a
1163 * syntax error in the modifier then NULL is returned.
1168 *----------------------------------------------------------------------
1172 StartEnd(string, indexPtr)
1173 char *string; /* String to parse for additional info
1174 * about modifier (count and units).
1175 * Points to first character of modifer
1177 CkTextIndex *indexPtr; /* Index to mdoify based on string. */
1182 register CkTextSegment *segPtr;
1185 * Find the end of the modifier word.
1188 for (p = string; isalnum((unsigned char) *p); p++) {
1189 /* Empty loop body. */
1192 if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
1194 indexPtr->charIndex = 0;
1195 for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
1196 segPtr = segPtr->nextPtr) {
1197 indexPtr->charIndex += segPtr->size;
1199 indexPtr->charIndex -= 1;
1200 } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
1202 indexPtr->charIndex = 0;
1203 } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
1208 * If the current character isn't part of a word then just move
1209 * forward one character. Otherwise move forward until finding
1210 * a character that isn't part of a word and stop there.
1213 segPtr = CkTextIndexToSeg(indexPtr, &offset);
1215 if (segPtr->typePtr == &ckTextCharType) {
1216 c = segPtr->body.chars[offset];
1217 if (!isalnum((unsigned char) c) && (c != '_')) {
1223 indexPtr->charIndex += 1;
1224 if (offset >= segPtr->size) {
1225 segPtr = CkTextIndexToSeg(indexPtr, &offset);
1229 CkTextIndexForwChars(indexPtr, 1, indexPtr);
1231 } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
1236 * Starting with the current character, look for one that's not
1237 * part of a word and keep moving backward until you find one.
1238 * Then if the character found wasn't the first one, move forward
1239 * again one position.
1242 segPtr = CkTextIndexToSeg(indexPtr, &offset);
1244 if (segPtr->typePtr == &ckTextCharType) {
1245 c = segPtr->body.chars[offset];
1246 if (!isalnum((unsigned char) c) && (c != '_')) {
1252 indexPtr->charIndex -= 1;
1254 if (indexPtr->charIndex < 0) {
1255 indexPtr->charIndex = 0;
1258 segPtr = CkTextIndexToSeg(indexPtr, &offset);
1262 CkTextIndexForwChars(indexPtr, 1, indexPtr);