home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkTextIndex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  20.5 KB  |  841 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkTextIndex.c --
  3.  *
  4.  *    This module provides procedures that manipulate indices for
  5.  *    text widgets.
  6.  *
  7.  * Copyright (c) 1992-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkTextIndex.c 1.15 97/06/17 17:49:24
  14.  */
  15.  
  16. #include "default.h"
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19. #include "tkText.h"
  20.  
  21. /*
  22.  * Index to use to select last character in line (very large integer):
  23.  */
  24.  
  25. #define LAST_CHAR 1000000
  26.  
  27. /*
  28.  * Forward declarations for procedures defined later in this file:
  29.  */
  30.  
  31. static char *        ForwBack _ANSI_ARGS_((char *string,
  32.                 TkTextIndex *indexPtr));
  33. static char *        StartEnd _ANSI_ARGS_(( char *string,
  34.                 TkTextIndex *indexPtr));
  35.  
  36. /*
  37.  *--------------------------------------------------------------
  38.  *
  39.  * TkTextMakeIndex --
  40.  *
  41.  *    Given a line index and a character index, look things up
  42.  *    in the B-tree and fill in a TkTextIndex structure.
  43.  *
  44.  * Results:
  45.  *    The structure at *indexPtr is filled in with information
  46.  *    about the character at lineIndex and charIndex (or the
  47.  *    closest existing character, if the specified one doesn't
  48.  *    exist), and indexPtr is returned as result.
  49.  *
  50.  * Side effects:
  51.  *    None.
  52.  *
  53.  *--------------------------------------------------------------
  54.  */
  55.  
  56. TkTextIndex *
  57. TkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
  58.     TkTextBTree tree;        /* Tree that lineIndex and charIndex refer
  59.                  * to. */
  60.     int lineIndex;        /* Index of desired line (0 means first
  61.                  * line of text). */
  62.     int charIndex;        /* Index of desired character. */
  63.     TkTextIndex *indexPtr;    /* Structure to fill in. */
  64. {
  65.     register TkTextSegment *segPtr;
  66.     int index;
  67.  
  68.     indexPtr->tree = tree;
  69.     if (lineIndex < 0) {
  70.     lineIndex = 0;
  71.     charIndex = 0;
  72.     }
  73.     if (charIndex < 0) {
  74.     charIndex = 0;
  75.     }
  76.     indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
  77.     if (indexPtr->linePtr == NULL) {
  78.     indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
  79.     charIndex = 0;
  80.     }
  81.  
  82.     /*
  83.      * Verify that the index is within the range of the line.
  84.      * If not, just use the index of the last character in the line.
  85.      */
  86.  
  87.     for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
  88.         segPtr = segPtr->nextPtr) {
  89.     if (segPtr == NULL) {
  90.         indexPtr->charIndex = index-1;
  91.         break;
  92.     }
  93.     index += segPtr->size;
  94.     if (index > charIndex) {
  95.         indexPtr->charIndex = charIndex;
  96.         break;
  97.     }
  98.     }
  99.     return indexPtr;
  100. }
  101.  
  102. /*
  103.  *--------------------------------------------------------------
  104.  *
  105.  * TkTextIndexToSeg --
  106.  *
  107.  *    Given an index, this procedure returns the segment and
  108.  *    offset within segment for the index.
  109.  *
  110.  * Results:
  111.  *    The return value is a pointer to the segment referred to
  112.  *    by indexPtr;  this will always be a segment with non-zero
  113.  *    size.  The variable at *offsetPtr is set to hold the
  114.  *    integer offset within the segment of the character
  115.  *    given by indexPtr.
  116.  *
  117.  * Side effects:
  118.  *    None.
  119.  *
  120.  *--------------------------------------------------------------
  121.  */
  122.  
  123. TkTextSegment *
  124. TkTextIndexToSeg(indexPtr, offsetPtr)
  125.     TkTextIndex *indexPtr;        /* Text index. */
  126.     int *offsetPtr;            /* Where to store offset within
  127.                      * segment, or NULL if offset isn't
  128.                      * wanted. */
  129. {
  130.     register TkTextSegment *segPtr;
  131.     int offset;
  132.  
  133.     for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
  134.         offset >= segPtr->size;
  135.         offset -= segPtr->size, segPtr = segPtr->nextPtr) {
  136.     /* Empty loop body. */
  137.     }
  138.     if (offsetPtr != NULL) {
  139.     *offsetPtr = offset;
  140.     }
  141.     return segPtr;
  142. }
  143.  
  144. /*
  145.  *--------------------------------------------------------------
  146.  *
  147.  * TkTextSegToOffset --
  148.  *
  149.  *    Given a segment pointer and the line containing it, this
  150.  *    procedure returns the offset of the segment within its
  151.  *    line.
  152.  *
  153.  * Results:
  154.  *    The return value is the offset (within its line) of the
  155.  *    first character in segPtr.
  156.  *
  157.  * Side effects:
  158.  *    None.
  159.  *
  160.  *--------------------------------------------------------------
  161.  */
  162.  
  163. int
  164. TkTextSegToOffset(segPtr, linePtr)
  165.     TkTextSegment *segPtr;        /* Segment whose offset is desired. */
  166.     TkTextLine *linePtr;        /* Line containing segPtr. */
  167. {
  168.     TkTextSegment *segPtr2;
  169.     int offset;
  170.  
  171.     offset = 0;
  172.     for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
  173.         segPtr2 = segPtr2->nextPtr) {
  174.     offset += segPtr2->size;
  175.     }
  176.     return offset;
  177. }
  178.  
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * TkTextGetIndex --
  183.  *
  184.  *    Given a string, return the line and character indices that
  185.  *    it describes.
  186.  *
  187.  * Results:
  188.  *    The return value is a standard Tcl return result.  If
  189.  *    TCL_OK is returned, then everything went well and the index
  190.  *    at *indexPtr is filled in;  otherwise TCL_ERROR is returned
  191.  *    and an error message is left in interp->result.
  192.  *
  193.  * Side effects:
  194.  *    None.
  195.  *
  196.  *----------------------------------------------------------------------
  197.  */
  198.  
  199. int
  200. TkTextGetIndex(interp, textPtr, string, indexPtr)
  201.     Tcl_Interp *interp;        /* Use this for error reporting. */
  202.     TkText *textPtr;        /* Information about text widget. */
  203.     char *string;        /* Textual description of position. */
  204.     TkTextIndex *indexPtr;    /* Index structure to fill in. */
  205. {
  206.     register char *p;
  207.     char *end, *endOfBase;
  208.     Tcl_HashEntry *hPtr;
  209.     TkTextTag *tagPtr;
  210.     TkTextSearch search;
  211.     TkTextIndex first, last;
  212.     int wantLast, result;
  213.     char c;
  214.  
  215.     /*
  216.      *---------------------------------------------------------------------
  217.      * Stage 1: check to see if the index consists of nothing but a mark
  218.      * name.  We do this check now even though it's also done later, in
  219.      * order to allow mark names that include funny characters such as
  220.      * spaces or "+1c".
  221.      *---------------------------------------------------------------------
  222.      */
  223.  
  224.     if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
  225.     return TCL_OK;
  226.     }
  227.  
  228.     /*
  229.      *------------------------------------------------
  230.      * Stage 2: start again by parsing the base index.
  231.      *------------------------------------------------
  232.      */
  233.  
  234.     indexPtr->tree = textPtr->tree;
  235.  
  236.     /*
  237.      * First look for the form "tag.first" or "tag.last" where "tag"
  238.      * is the name of a valid tag.  Try to use up as much as possible
  239.      * of the string in this check (strrchr instead of strchr below).
  240.      * Doing the check now, and in this way, allows tag names to include
  241.      * funny characters like "@" or "+1c".
  242.      */
  243.  
  244.     p = strrchr(string, '.');
  245.     if (p != NULL) {
  246.     if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
  247.         wantLast = 0;
  248.         endOfBase = p+6;
  249.     } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
  250.         wantLast = 1;
  251.         endOfBase = p+5;
  252.     } else {
  253.         goto tryxy;
  254.     }
  255.     *p = 0;
  256.     hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
  257.     *p = '.';
  258.     if (hPtr == NULL) {
  259.         goto tryxy;
  260.     }
  261.     tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
  262.     TkTextMakeIndex(textPtr->tree, 0, 0, &first);
  263.     TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
  264.         &last);
  265.     TkBTreeStartSearch(&first, &last, tagPtr, &search);
  266.     if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
  267.         Tcl_AppendResult(interp,
  268.             "text doesn't contain any characters tagged with \"",
  269.             Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
  270.                 (char *) NULL);
  271.         return TCL_ERROR;
  272.     }
  273.     *indexPtr = search.curIndex;
  274.     if (wantLast) {
  275.         while (TkBTreeNextTag(&search)) {
  276.         *indexPtr = search.curIndex;
  277.         }
  278.     }
  279.     goto gotBase;
  280.     }
  281.  
  282.     tryxy:
  283.     if (string[0] == '@') {
  284.     /*
  285.      * Find character at a given x,y location in the window.
  286.      */
  287.  
  288.     int x, y;
  289.  
  290.     p = string+1;
  291.     x = strtol(p, &end, 0);
  292.     if ((end == p) || (*end != ',')) {
  293.         goto error;
  294.     }
  295.     p = end+1;
  296.     y = strtol(p, &end, 0);
  297.     if (end == p) {
  298.         goto error;
  299.     }
  300.     TkTextPixelIndex(textPtr, x, y, indexPtr);
  301.     endOfBase = end;
  302.     goto gotBase; 
  303.     }
  304.  
  305.     if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
  306.     int lineIndex, charIndex;
  307.  
  308.     /*
  309.      * Base is identified with line and character indices.
  310.      */
  311.  
  312.     lineIndex = strtol(string, &end, 0) - 1;
  313.     if ((end == string) || (*end != '.')) {
  314.         goto error;
  315.     }
  316.     p = end+1;
  317.     if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
  318.         charIndex = LAST_CHAR;
  319.         endOfBase = p+3;
  320.     } else {
  321.         charIndex = strtol(p, &end, 0);
  322.         if (end == p) {
  323.         goto error;
  324.         }
  325.         endOfBase = end;
  326.     }
  327.     TkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
  328.     goto gotBase;
  329.     }
  330.  
  331.     for (p = string; *p != 0; p++) {
  332.     if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
  333.         break;
  334.     }
  335.     }
  336.     endOfBase = p;
  337.     if (string[0] == '.') {
  338.     /*
  339.      * See if the base position is the name of an embedded window.
  340.      */
  341.  
  342.     c = *endOfBase;
  343.     *endOfBase = 0;
  344.     result = TkTextWindowIndex(textPtr, string, indexPtr);
  345.     *endOfBase = c;
  346.     if (result != 0) {
  347.         goto gotBase;
  348.     }
  349.     }
  350.     if ((string[0] == 'e')
  351.         && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
  352.     /*
  353.      * Base position is end of text.
  354.      */
  355.  
  356.     TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
  357.         0, indexPtr);
  358.     goto gotBase;
  359.     } else {
  360.     /*
  361.      * See if the base position is the name of a mark.
  362.      */
  363.  
  364.     c = *endOfBase;
  365.     *endOfBase = 0;
  366.     result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
  367.     *endOfBase = c;
  368.     if (result == TCL_OK) {
  369.         goto gotBase;
  370.     }
  371.  
  372.     /*
  373.      * See if the base position is the name of an embedded image
  374.      */
  375.  
  376.     c = *endOfBase;
  377.     *endOfBase = 0;
  378.     result = TkTextImageIndex(textPtr, string, indexPtr);
  379.     *endOfBase = c;
  380.     if (result != 0) {
  381.         goto gotBase;
  382.     }
  383.     }
  384.     goto error;
  385.  
  386.     /*
  387.      *-------------------------------------------------------------------
  388.      * Stage 3: process zero or more modifiers.  Each modifier is either
  389.      * a keyword like "wordend" or "linestart", or it has the form
  390.      * "op count units" where op is + or -, count is a number, and units
  391.      * is "chars" or "lines".
  392.      *-------------------------------------------------------------------
  393.      */
  394.  
  395.     gotBase:
  396.     p = endOfBase;
  397.     while (1) {
  398.     while (isspace(UCHAR(*p))) {
  399.         p++;
  400.     }
  401.     if (*p == 0) {
  402.         break;
  403.     }
  404.     
  405.     if ((*p == '+') || (*p == '-')) {
  406.         p = ForwBack(p, indexPtr);
  407.     } else {
  408.         p = StartEnd(p, indexPtr);
  409.     }
  410.     if (p == NULL) {
  411.         goto error;
  412.     }
  413.     }
  414.     return TCL_OK;
  415.  
  416.     error:
  417.     Tcl_AppendResult(interp, "bad text index \"", string, "\"",
  418.         (char *) NULL);
  419.     return TCL_ERROR;
  420. }
  421.  
  422. /*
  423.  *----------------------------------------------------------------------
  424.  *
  425.  * TkTextPrintIndex --
  426.  *
  427.  *    
  428.  *    This procedure generates a string description of an index,
  429.  *    suitable for reading in again later.
  430.  *
  431.  * Results:
  432.  *    The characters pointed to by string are modified.
  433.  *
  434.  * Side effects:
  435.  *    None.
  436.  *
  437.  *----------------------------------------------------------------------
  438.  */
  439.  
  440. void
  441. TkTextPrintIndex(indexPtr, string)
  442.     TkTextIndex *indexPtr;    /* Pointer to index. */
  443.     char *string;        /* Place to store the position.  Must have
  444.                  * at least TK_POS_CHARS characters. */
  445. {
  446.     sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
  447.         indexPtr->charIndex);
  448. }
  449.  
  450. /*
  451.  *--------------------------------------------------------------
  452.  *
  453.  * TkTextIndexCmp --
  454.  *
  455.  *    Compare two indices to see which one is earlier in
  456.  *    the text.
  457.  *
  458.  * Results:
  459.  *    The return value is 0 if index1Ptr and index2Ptr refer
  460.  *    to the same position in the file, -1 if index1Ptr refers
  461.  *    to an earlier position than index2Ptr, and 1 otherwise.
  462.  *
  463.  * Side effects:
  464.  *    None.
  465.  *
  466.  *--------------------------------------------------------------
  467.  */
  468.  
  469. int
  470. TkTextIndexCmp(index1Ptr, index2Ptr)
  471.     TkTextIndex *index1Ptr;        /* First index. */
  472.     TkTextIndex *index2Ptr;        /* Second index. */
  473. {
  474.     int line1, line2;
  475.  
  476.     if (index1Ptr->linePtr == index2Ptr->linePtr) {
  477.     if (index1Ptr->charIndex < index2Ptr->charIndex) {
  478.         return -1;
  479.     } else if (index1Ptr->charIndex > index2Ptr->charIndex) {
  480.         return 1;
  481.     } else {
  482.         return 0;
  483.     }
  484.     }
  485.     line1 = TkBTreeLineIndex(index1Ptr->linePtr);
  486.     line2 = TkBTreeLineIndex(index2Ptr->linePtr);
  487.     if (line1 < line2) {
  488.     return -1;
  489.     }
  490.     if (line1 > line2) {
  491.     return 1;
  492.     }
  493.     return 0;
  494. }
  495.  
  496. /*
  497.  *----------------------------------------------------------------------
  498.  *
  499.  * ForwBack --
  500.  *
  501.  *    This procedure handles +/- modifiers for indices to adjust
  502.  *    the index forwards or backwards.
  503.  *
  504.  * Results:
  505.  *    If the modifier in string is successfully parsed then the
  506.  *    return value is the address of the first character after the
  507.  *    modifier, and *indexPtr is updated to reflect the modifier.
  508.  *    If there is a syntax error in the modifier then NULL is returned.
  509.  *
  510.  * Side effects:
  511.  *    None.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515.  
  516. static char *
  517. ForwBack(string, indexPtr)
  518.     char *string;        /* String to parse for additional info
  519.                  * about modifier (count and units). 
  520.                  * Points to "+" or "-" that starts
  521.                  * modifier. */
  522.     TkTextIndex *indexPtr;    /* Index to update as specified in string. */
  523. {
  524.     register char *p;
  525.     char *end, *units;
  526.     int count, lineIndex;
  527.     size_t length;
  528.  
  529.     /*
  530.      * Get the count (how many units forward or backward).
  531.      */
  532.  
  533.     p = string+1;
  534.     while (isspace(UCHAR(*p))) {
  535.     p++;
  536.     }
  537.     count = strtol(p, &end, 0);
  538.     if (end == p) {
  539.     return NULL;
  540.     }
  541.     p = end;
  542.     while (isspace(UCHAR(*p))) {
  543.     p++;
  544.     }
  545.  
  546.     /*
  547.      * Find the end of this modifier (next space or + or - character),
  548.      * then parse the unit specifier and update the position
  549.      * accordingly.
  550.      */
  551.  
  552.     units = p; 
  553.     while ((*p != 0) && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
  554.     p++;
  555.     }
  556.     length = p - units;
  557.     if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
  558.     if (*string == '+') {
  559.         TkTextIndexForwChars(indexPtr, count, indexPtr);
  560.     } else {
  561.         TkTextIndexBackChars(indexPtr, count, indexPtr);
  562.     }
  563.     } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
  564.     lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
  565.     if (*string == '+') {
  566.         lineIndex += count;
  567.     } else {
  568.         lineIndex -= count;
  569.  
  570.         /*
  571.          * The check below retains the character position, even
  572.          * if the line runs off the start of the file.  Without
  573.          * it, the character position will get reset to 0 by
  574.          * TkTextMakeIndex.
  575.          */
  576.  
  577.         if (lineIndex < 0) {
  578.         lineIndex = 0;
  579.         }
  580.     }
  581.     TkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
  582.         indexPtr);
  583.     } else {
  584.     return NULL;
  585.     }
  586.     return p;
  587. }
  588.  
  589. /*
  590.  *----------------------------------------------------------------------
  591.  *
  592.  * TkTextIndexForwChars --
  593.  *
  594.  *    Given an index for a text widget, this procedure creates a
  595.  *    new index that points "count" characters ahead of the source
  596.  *    index.
  597.  *
  598.  * Results:
  599.  *    *dstPtr is modified to refer to the character "count" characters
  600.  *    after srcPtr, or to the last character in the file if there aren't
  601.  *    "count" characters left in the file.
  602.  *
  603.  * Side effects:
  604.  *    None.
  605.  *
  606.  *----------------------------------------------------------------------
  607.  */
  608.  
  609.     /* ARGSUSED */
  610. void
  611. TkTextIndexForwChars(srcPtr, count, dstPtr)
  612.     TkTextIndex *srcPtr;        /* Source index. */
  613.     int count;                /* How many characters forward to
  614.                      * move.  May be negative. */
  615.     TkTextIndex *dstPtr;        /* Destination index: gets modified. */
  616. {
  617.     TkTextLine *linePtr;
  618.     TkTextSegment *segPtr;
  619.     int lineLength;
  620.  
  621.     if (count < 0) {
  622.     TkTextIndexBackChars(srcPtr, -count, dstPtr);
  623.     return;
  624.     }
  625.  
  626.     *dstPtr = *srcPtr;
  627.     dstPtr->charIndex += count;
  628.     while (1) {
  629.     /*
  630.      * Compute the length of the current line.
  631.      */
  632.  
  633.     lineLength = 0;
  634.     for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  635.         segPtr = segPtr->nextPtr) {
  636.         lineLength += segPtr->size;
  637.     }
  638.  
  639.     /*
  640.      * If the new index is in the same line then we're done.
  641.      * Otherwise go on to the next line.
  642.      */
  643.  
  644.     if (dstPtr->charIndex < lineLength) {
  645.         return;
  646.     }
  647.     dstPtr->charIndex -= lineLength;
  648.     linePtr = TkBTreeNextLine(dstPtr->linePtr);
  649.     if (linePtr == NULL) {
  650.         dstPtr->charIndex = lineLength - 1;
  651.         return;
  652.     }
  653.     dstPtr->linePtr = linePtr;
  654.     }
  655. }
  656.  
  657. /*
  658.  *----------------------------------------------------------------------
  659.  *
  660.  * TkTextIndexBackChars --
  661.  *
  662.  *    Given an index for a text widget, this procedure creates a
  663.  *    new index that points "count" characters earlier than the
  664.  *    source index.
  665.  *
  666.  * Results:
  667.  *    *dstPtr is modified to refer to the character "count" characters
  668.  *    before srcPtr, or to the first character in the file if there aren't
  669.  *    "count" characters earlier than srcPtr.
  670.  *
  671.  * Side effects:
  672.  *    None.
  673.  *
  674.  *----------------------------------------------------------------------
  675.  */
  676.  
  677. void
  678. TkTextIndexBackChars(srcPtr, count, dstPtr)
  679.     TkTextIndex *srcPtr;        /* Source index. */
  680.     int count;                /* How many characters backward to
  681.                      * move.  May be negative. */
  682.     TkTextIndex *dstPtr;        /* Destination index: gets modified. */
  683. {
  684.     TkTextSegment *segPtr;
  685.     int lineIndex;
  686.  
  687.     if (count < 0) {
  688.     TkTextIndexForwChars(srcPtr, -count, dstPtr);
  689.     return;
  690.     }
  691.  
  692.     *dstPtr = *srcPtr;
  693.     dstPtr->charIndex -= count;
  694.     lineIndex = -1;
  695.     while (dstPtr->charIndex < 0) {
  696.     /*
  697.      * Move back one line in the text.  If we run off the beginning
  698.      * of the file then just return the first character in the text.
  699.      */
  700.  
  701.     if (lineIndex < 0) {
  702.         lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
  703.     }
  704.     if (lineIndex == 0) {
  705.         dstPtr->charIndex = 0;
  706.         return;
  707.     }
  708.     lineIndex--;
  709.     dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
  710.  
  711.     /*
  712.      * Compute the length of the line and add that to dstPtr->charIndex.
  713.      */
  714.  
  715.     for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  716.         segPtr = segPtr->nextPtr) {
  717.         dstPtr->charIndex += segPtr->size;
  718.     }
  719.     }
  720. }
  721.  
  722. /*
  723.  *----------------------------------------------------------------------
  724.  *
  725.  * StartEnd --
  726.  *
  727.  *    This procedure handles modifiers like "wordstart" and "lineend"
  728.  *    to adjust indices forwards or backwards.
  729.  *
  730.  * Results:
  731.  *    If the modifier is successfully parsed then the return value
  732.  *    is the address of the first character after the modifier, and
  733.  *    *indexPtr is updated to reflect the modifier. If there is a
  734.  *    syntax error in the modifier then NULL is returned.
  735.  *
  736.  * Side effects:
  737.  *    None.
  738.  *
  739.  *----------------------------------------------------------------------
  740.  */
  741.  
  742. static char *
  743. StartEnd(string, indexPtr)
  744.     char *string;        /* String to parse for additional info
  745.                  * about modifier (count and units). 
  746.                  * Points to first character of modifer
  747.                  * word. */
  748.     TkTextIndex *indexPtr;    /* Index to mdoify based on string. */
  749. {
  750.     char *p;
  751.     int c, offset;
  752.     size_t length;
  753.     register TkTextSegment *segPtr;
  754.  
  755.     /*
  756.      * Find the end of the modifier word.
  757.      */
  758.  
  759.     for (p = string; isalnum(UCHAR(*p)); p++) {
  760.     /* Empty loop body. */
  761.     }
  762.     length = p-string;
  763.     if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
  764.         && (length >= 5)) {
  765.     indexPtr->charIndex = 0;
  766.     for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
  767.         segPtr = segPtr->nextPtr) {
  768.         indexPtr->charIndex += segPtr->size;
  769.     }
  770.     indexPtr->charIndex -= 1;
  771.     } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
  772.         && (length >= 5)) {
  773.     indexPtr->charIndex = 0;
  774.     } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
  775.         && (length >= 5)) {
  776.     int firstChar = 1;
  777.  
  778.     /*
  779.      * If the current character isn't part of a word then just move
  780.      * forward one character.  Otherwise move forward until finding
  781.      * a character that isn't part of a word and stop there.
  782.      */
  783.  
  784.     segPtr = TkTextIndexToSeg(indexPtr, &offset);
  785.     while (1) {
  786.         if (segPtr->typePtr == &tkTextCharType) {
  787.         c = segPtr->body.chars[offset];
  788.         if (!isalnum(UCHAR(c)) && (c != '_')) {
  789.             break;
  790.         }
  791.         firstChar = 0;
  792.         }
  793.         offset += 1;
  794.         indexPtr->charIndex += 1;
  795.         if (offset >= segPtr->size) {
  796.         segPtr = TkTextIndexToSeg(indexPtr, &offset);
  797.         }
  798.     }
  799.     if (firstChar) {
  800.         TkTextIndexForwChars(indexPtr, 1, indexPtr);
  801.     }
  802.     } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
  803.         && (length >= 5)) {
  804.     int firstChar = 1;
  805.  
  806.     /*
  807.      * Starting with the current character, look for one that's not
  808.      * part of a word and keep moving backward until you find one.
  809.      * Then if the character found wasn't the first one, move forward
  810.      * again one position.
  811.      */
  812.  
  813.     segPtr = TkTextIndexToSeg(indexPtr, &offset);
  814.     while (1) {
  815.         if (segPtr->typePtr == &tkTextCharType) {
  816.         c = segPtr->body.chars[offset];
  817.         if (!isalnum(UCHAR(c)) && (c != '_')) {
  818.             break;
  819.         }
  820.         firstChar = 0;
  821.         }
  822.         offset -= 1;
  823.         indexPtr->charIndex -= 1;
  824.         if (offset < 0) {
  825.         if (indexPtr->charIndex < 0) {
  826.             indexPtr->charIndex = 0;
  827.             goto done;
  828.         }
  829.         segPtr = TkTextIndexToSeg(indexPtr, &offset);
  830.         }
  831.     }
  832.     if (!firstChar) {
  833.         TkTextIndexForwChars(indexPtr, 1, indexPtr);
  834.     }
  835.     } else {
  836.     return NULL;
  837.     }
  838.     done:
  839.     return p;
  840. }
  841.