home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 100-199 / ff119.lzh / MicroEMACS / src / src.zoo / line.c < prev    next >
C/C++ Source or Header  |  1987-12-09  |  16KB  |  646 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "edef.h"
  17.  
  18. KILL *ykbuf;    /* ptr to current kill buffer chunk being yanked */
  19. int ykboff;    /* offset into that chunk */
  20.  
  21. /*
  22.  * This routine allocates a block of memory large enough to hold a LINE
  23.  * containing "used" characters. The block is always rounded up a bit. Return
  24.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  25.  * message in the message line if no space.
  26.  */
  27. LINE *lalloc(used)
  28.  
  29. register int    used;
  30.  
  31. {
  32.     register LINE    *lp;
  33.     register int    size;
  34.     char *malloc();
  35.  
  36.     size = (used+NBLOCK-1) & ~(NBLOCK-1);
  37.     if (size == 0)                /* Assume that an empty */
  38.         size = NBLOCK;            /* line is for type-in. */
  39.     if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  40.         mlwrite("[OUT OF MEMORY]");
  41.         return (NULL);
  42.     }
  43.     lp->l_size = size;
  44.     lp->l_used = used;
  45.     return (lp);
  46. }
  47.  
  48. /*
  49.  * Delete line "lp". Fix all of the links that might point at it (they are
  50.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  51.  * might be in. Release the memory. The buffers are updated too; the magic
  52.  * conditions described in the above comments don't hold here.
  53.  */
  54. lfree(lp)
  55. register LINE    *lp;
  56. {
  57.     register BUFFER *bp;
  58.     register WINDOW *wp;
  59.  
  60.     wp = wheadp;
  61.     while (wp != NULL) {
  62.         if (wp->w_linep == lp)
  63.             wp->w_linep = lp->l_fp;
  64.         if (wp->w_dotp    == lp) {
  65.             wp->w_dotp  = lp->l_fp;
  66.             wp->w_doto  = 0;
  67.         }
  68.         if (wp->w_markp == lp) {
  69.             wp->w_markp = lp->l_fp;
  70.             wp->w_marko = 0;
  71.         }
  72.         wp = wp->w_wndp;
  73.     }
  74.     bp = bheadp;
  75.     while (bp != NULL) {
  76.         if (bp->b_nwnd == 0) {
  77.             if (bp->b_dotp    == lp) {
  78.                 bp->b_dotp = lp->l_fp;
  79.                 bp->b_doto = 0;
  80.             }
  81.             if (bp->b_markp == lp) {
  82.                 bp->b_markp = lp->l_fp;
  83.                 bp->b_marko = 0;
  84.             }
  85.         }
  86.         bp = bp->b_bufp;
  87.     }
  88.     lp->l_bp->l_fp = lp->l_fp;
  89.     lp->l_fp->l_bp = lp->l_bp;
  90.     free((char *) lp);
  91. }
  92.  
  93. /*
  94.  * This routine gets called when a character is changed in place in the current
  95.  * buffer. It updates all of the required flags in the buffer and window
  96.  * system. The flag used is passed as an argument; if the buffer is being
  97.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  98.  * mode line needs to be updated (the "*" has to be set).
  99.  */
  100. lchange(flag)
  101. register int    flag;
  102. {
  103.     register WINDOW *wp;
  104.  
  105.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  106.         flag = WFHARD;
  107.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  108.         flag |= WFMODE;         /* update mode lines.    */
  109.         curbp->b_flag |= BFCHG;
  110.     }
  111.     wp = wheadp;
  112.     while (wp != NULL) {
  113.         if (wp->w_bufp == curbp)
  114.             wp->w_flag |= flag;
  115.         wp = wp->w_wndp;
  116.     }
  117. }
  118.  
  119. insspace(f, n)    /* insert spaces forward into text */
  120.  
  121. int f, n;    /* default flag and numeric argument */
  122.  
  123. {
  124.     linsert(n, ' ');
  125.     backchar(f, n);
  126. }
  127.  
  128. /*
  129.  * linstr -- Insert a string at the current point
  130.  */
  131.  
  132. linstr(instr)
  133. char    *instr;
  134. {
  135.     register int status = TRUE;
  136.     char tmpc;
  137.  
  138.     if (instr != NULL)
  139.         while ((tmpc = *instr) && status == TRUE) {
  140.             status = (tmpc == '\n'? lnewline(): linsert(1, tmpc));
  141.  
  142.             /* Insertion error? */
  143.             if (status != TRUE) {
  144.                 mlwrite("%%Out of memory while inserting");
  145.                 break;
  146.             }
  147.             instr++;
  148.         }
  149.     return(status);
  150. }
  151.  
  152. /*
  153.  * Insert "n" copies of the character "c" at the current location of dot. In
  154.  * the easy case all that happens is the text is stored in the line. In the
  155.  * hard case, the line has to be reallocated. When the window list is updated,
  156.  * take special care; I screwed it up once. You always update dot in the
  157.  * current window. You update mark, and a dot in another window, if it is
  158.  * greater than the place where you did the insert. Return TRUE if all is
  159.  * well, and FALSE on errors.
  160.  */
  161.  
  162. linsert(n, c)
  163. {
  164.     register char    *cp1;
  165.     register char    *cp2;
  166.     register LINE    *lp1;
  167.     register LINE    *lp2;
  168.     register LINE    *lp3;
  169.     register int    doto;
  170.     register int    i;
  171.     register WINDOW *wp;
  172.  
  173.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  174.         return(rdonly());    /* we are in read only mode    */
  175.     lchange(WFEDIT);
  176.     lp1 = curwp->w_dotp;            /* Current line     */
  177.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  178.         if (curwp->w_doto != 0) {
  179.             mlwrite("bug: linsert");
  180.             return (FALSE);
  181.         }
  182.         if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  183.             return (FALSE);
  184.         lp3 = lp1->l_bp;        /* Previous line    */
  185.         lp3->l_fp = lp2;        /* Link in        */
  186.         lp2->l_fp = lp1;
  187.         lp1->l_bp = lp2;
  188.         lp2->l_bp = lp3;
  189.         for (i=0; i<n; ++i)
  190.             lp2->l_text[i] = c;
  191.         curwp->w_dotp = lp2;
  192.         curwp->w_doto = n;
  193.         return (TRUE);
  194.     }
  195.     doto = curwp->w_doto;            /* Save for later.    */
  196.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  197.         if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  198.             return (FALSE);
  199.         cp1 = &lp1->l_text[0];
  200.         cp2 = &lp2->l_text[0];
  201.         while (cp1 != &lp1->l_text[doto])
  202.             *cp2++ = *cp1++;
  203.         cp2 += n;
  204.         while (cp1 != &lp1->l_text[lp1->l_used])
  205.             *cp2++ = *cp1++;
  206.         lp1->l_bp->l_fp = lp2;
  207.         lp2->l_fp = lp1->l_fp;
  208.         lp1->l_fp->l_bp = lp2;
  209.         lp2->l_bp = lp1->l_bp;
  210.         free((char *) lp1);
  211.     } else {                /* Easy: in place    */
  212.         lp2 = lp1;            /* Pretend new line    */
  213.         lp2->l_used += n;
  214.         cp2 = &lp1->l_text[lp1->l_used];
  215.         cp1 = cp2-n;
  216.         while (cp1 != &lp1->l_text[doto])
  217.             *--cp2 = *--cp1;
  218.     }
  219.     for (i=0; i<n; ++i)            /* Add the characters    */
  220.         lp2->l_text[doto+i] = c;
  221.     wp = wheadp;                /* Update windows    */
  222.     while (wp != NULL) {
  223.         if (wp->w_linep == lp1)
  224.             wp->w_linep = lp2;
  225.         if (wp->w_dotp == lp1) {
  226.             wp->w_dotp = lp2;
  227.             if (wp==curwp || wp->w_doto>doto)
  228.                 wp->w_doto += n;
  229.         }
  230.         if (wp->w_markp == lp1) {
  231.             wp->w_markp = lp2;
  232.             if (wp->w_marko > doto)
  233.                 wp->w_marko += n;
  234.         }
  235.         wp = wp->w_wndp;
  236.     }
  237.     return (TRUE);
  238. }
  239.  
  240. /*
  241.  * Overwrite a character into the current line at the current position
  242.  *
  243.  */
  244.  
  245. lowrite(c)
  246.  
  247. char c;        /* character to overwrite on current position */
  248.  
  249. {
  250.     if (curwp->w_doto < curwp->w_dotp->l_used &&
  251.         (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  252.          (curwp->w_doto) % 8 == 7))
  253.             ldelete(1L, FALSE);
  254.     return(linsert(1, c));
  255. }
  256.  
  257. /*
  258.  * lover -- Overwrite a string at the current point
  259.  */
  260.  
  261. lover(ostr)
  262.  
  263. char    *ostr;
  264.  
  265. {
  266.     register int status = TRUE;
  267.     char tmpc;
  268.  
  269.     if (ostr != NULL)
  270.         while ((tmpc = *ostr) && status == TRUE) {
  271.             status = (tmpc == '\n'? lnewline(): lowrite(tmpc));
  272.  
  273.             /* Insertion error? */
  274.             if (status != TRUE) {
  275.                 mlwrite("%%Out of memory while overwriting");
  276.                 break;
  277.             }
  278.             ostr++;
  279.         }
  280.     return(status);
  281. }
  282.  
  283. /*
  284.  * Insert a newline into the buffer at the current location of dot in the
  285.  * current window. The funny ass-backwards way it does things is not a botch;
  286.  * it just makes the last line in the file not a special case. Return TRUE if
  287.  * everything works out and FALSE on error (memory allocation failure). The
  288.  * update of dot and mark is a bit easier then in the above case, because the
  289.  * split forces more updating.
  290.  */
  291. lnewline()
  292. {
  293.     register char    *cp1;
  294.     register char    *cp2;
  295.     register LINE    *lp1;
  296.     register LINE    *lp2;
  297.     register int    doto;
  298.     register WINDOW *wp;
  299.  
  300.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  301.         return(rdonly());    /* we are in read only mode    */
  302.     lchange(WFHARD);
  303.     lp1  = curwp->w_dotp;            /* Get the address and    */
  304.     doto = curwp->w_doto;            /* offset of "."    */
  305.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  306.         return (FALSE);
  307.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  308.     cp2 = &lp2->l_text[0];
  309.     while (cp1 != &lp1->l_text[doto])
  310.         *cp2++ = *cp1++;
  311.     cp2 = &lp1->l_text[0];
  312.     while (cp1 != &lp1->l_text[lp1->l_used])
  313.         *cp2++ = *cp1++;
  314.     lp1->l_used -= doto;
  315.     lp2->l_bp = lp1->l_bp;
  316.     lp1->l_bp = lp2;
  317.     lp2->l_bp->l_fp = lp2;
  318.     lp2->l_fp = lp1;
  319.     wp = wheadp;                /* Windows        */
  320.     while (wp != NULL) {
  321.         if (wp->w_linep == lp1)
  322.             wp->w_linep = lp2;
  323.         if (wp->w_dotp == lp1) {
  324.             if (wp->w_doto < doto)
  325.                 wp->w_dotp = lp2;
  326.             else
  327.                 wp->w_doto -= doto;
  328.         }
  329.         if (wp->w_markp == lp1) {
  330.             if (wp->w_marko < doto)
  331.                 wp->w_markp = lp2;
  332.             else
  333.                 wp->w_marko -= doto;
  334.         }
  335.         wp = wp->w_wndp;
  336.     }
  337.     return (TRUE);
  338. }
  339.  
  340. /*
  341.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  342.  * with end of lines, etc. It returns TRUE if all of the characters were
  343.  * deleted, and FALSE if they were not (because dot ran into the end of the
  344.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  345.  */
  346. ldelete(n, kflag)
  347.  
  348. long n;     /* # of chars to delete */
  349. int kflag;    /* put killed text in kill buffer flag */
  350.  
  351. {
  352.     register char    *cp1;
  353.     register char    *cp2;
  354.     register LINE    *dotp;
  355.     register int    doto;
  356.     register int    chunk;
  357.     register WINDOW *wp;
  358.  
  359.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  360.         return(rdonly());    /* we are in read only mode    */
  361.     while (n != 0) {
  362.         dotp = curwp->w_dotp;
  363.         doto = curwp->w_doto;
  364.         if (dotp == curbp->b_linep)    /* Hit end of buffer.    */
  365.             return (FALSE);
  366.         chunk = dotp->l_used-doto;    /* Size of chunk.    */
  367.         if (chunk > n)
  368.             chunk = n;
  369.         if (chunk == 0) {        /* End of line, merge.    */
  370.             lchange(WFHARD);
  371.             if (ldelnewline() == FALSE
  372.             || (kflag!=FALSE && kinsert('\n')==FALSE))
  373.                 return (FALSE);
  374.             --n;
  375.             continue;
  376.         }
  377.         lchange(WFEDIT);
  378.         cp1 = &dotp->l_text[doto];    /* Scrunch text.    */
  379.         cp2 = cp1 + chunk;
  380.         if (kflag != FALSE) {        /* Kill?        */
  381.             while (cp1 != cp2) {
  382.                 if (kinsert(*cp1) == FALSE)
  383.                     return (FALSE);
  384.                 ++cp1;
  385.             }
  386.             cp1 = &dotp->l_text[doto];
  387.         }
  388.         while (cp2 != &dotp->l_text[dotp->l_used])
  389.             *cp1++ = *cp2++;
  390.         dotp->l_used -= chunk;
  391.         wp = wheadp;            /* Fix windows        */
  392.         while (wp != NULL) {
  393.             if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  394.                 wp->w_doto -= chunk;
  395.                 if (wp->w_doto < doto)
  396.                     wp->w_doto = doto;
  397.             }
  398.             if (wp->w_markp==dotp && wp->w_marko>=doto) {
  399.                 wp->w_marko -= chunk;
  400.                 if (wp->w_marko < doto)
  401.                     wp->w_marko = doto;
  402.             }
  403.             wp = wp->w_wndp;
  404.         }
  405.         n -= chunk;
  406.     }
  407.     return (TRUE);
  408. }
  409.  
  410. /* getctext:    grab and return a string with the text of
  411.         the current line
  412. */
  413.  
  414. char *getctext()
  415.  
  416. {
  417.     register LINE *lp;    /* line to copy */
  418.     register int size;    /* length of line to return */
  419.     register char *sp;    /* string pointer into line */
  420.     register char *dp;    /* string pointer into returned line */
  421.     char rline[NSTRING];    /* line to return */
  422.  
  423.     /* find the contents of the current line and its length */
  424.     lp = curwp->w_dotp;
  425.     sp = lp->l_text;
  426.     size = lp->l_used;
  427.     if (size >= NSTRING)
  428.         size = NSTRING - 1;
  429.  
  430.     /* copy it across */
  431.     dp = rline;
  432.     while (size--)
  433.         *dp++ = *sp++;
  434.     *dp = 0;
  435.     return(rline);
  436. }
  437.  
  438. /* putctext:    replace the current line with the passed in text    */
  439.  
  440. putctext(iline)
  441.  
  442. char *iline;    /* contents of new line */
  443.  
  444. {
  445.     register int status;
  446.  
  447.     /* delete the current line */
  448.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  449.     if ((status = killtext(TRUE, 1)) != TRUE)
  450.         return(status);
  451.  
  452.     /* insert the new line */
  453.     if ((status = linstr(iline)) != TRUE)
  454.         return(status);
  455.     status = lnewline();
  456.     backline(TRUE, 1);
  457.     return(status);
  458. }
  459.  
  460. /*
  461.  * Delete a newline. Join the current line with the next line. If the next line
  462.  * is the magic header line always return TRUE; merging the last line with the
  463.  * header line can be thought of as always being a successful operation, even
  464.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  465.  * can be done by shuffling data around. Hard cases require that lines be moved
  466.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  467.  * "ldelete" only.
  468.  */
  469. ldelnewline()
  470. {
  471.     register char    *cp1;
  472.     register char    *cp2;
  473.     register LINE    *lp1;
  474.     register LINE    *lp2;
  475.     register LINE    *lp3;
  476.     register WINDOW *wp;
  477.  
  478.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  479.         return(rdonly());    /* we are in read only mode    */
  480.     lp1 = curwp->w_dotp;
  481.     lp2 = lp1->l_fp;
  482.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  483.         if (lp1->l_used == 0)        /* Blank line.        */
  484.             lfree(lp1);
  485.         return (TRUE);
  486.     }
  487.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  488.         cp1 = &lp1->l_text[lp1->l_used];
  489.         cp2 = &lp2->l_text[0];
  490.         while (cp2 != &lp2->l_text[lp2->l_used])
  491.             *cp1++ = *cp2++;
  492.         wp = wheadp;
  493.         while (wp != NULL) {
  494.             if (wp->w_linep == lp2)
  495.                 wp->w_linep = lp1;
  496.             if (wp->w_dotp == lp2) {
  497.                 wp->w_dotp  = lp1;
  498.                 wp->w_doto += lp1->l_used;
  499.             }
  500.             if (wp->w_markp == lp2) {
  501.                 wp->w_markp  = lp1;
  502.                 wp->w_marko += lp1->l_used;
  503.             }
  504.             wp = wp->w_wndp;
  505.         }
  506.         lp1->l_used += lp2->l_used;
  507.         lp1->l_fp = lp2->l_fp;
  508.         lp2->l_fp->l_bp = lp1;
  509.         free((char *) lp2);
  510.         return (TRUE);
  511.     }
  512.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  513.         return (FALSE);
  514.     cp1 = &lp1->l_text[0];
  515.     cp2 = &lp3->l_text[0];
  516.     while (cp1 != &lp1->l_text[lp1->l_used])
  517.         *cp2++ = *cp1++;
  518.     cp1 = &lp2->l_text[0];
  519.     while (cp1 != &lp2->l_text[lp2->l_used])
  520.         *cp2++ = *cp1++;
  521.     lp1->l_bp->l_fp = lp3;
  522.     lp3->l_fp = lp2->l_fp;
  523.     lp2->l_fp->l_bp = lp3;
  524.     lp3->l_bp = lp1->l_bp;
  525.     wp = wheadp;
  526.     while (wp != NULL) {
  527.         if (wp->w_linep==lp1 || wp->w_linep==lp2)
  528.             wp->w_linep = lp3;
  529.         if (wp->w_dotp == lp1)
  530.             wp->w_dotp  = lp3;
  531.         else if (wp->w_dotp == lp2) {
  532.             wp->w_dotp  = lp3;
  533.             wp->w_doto += lp1->l_used;
  534.         }
  535.         if (wp->w_markp == lp1)
  536.             wp->w_markp  = lp3;
  537.         else if (wp->w_markp == lp2) {
  538.             wp->w_markp  = lp3;
  539.             wp->w_marko += lp1->l_used;
  540.         }
  541.         wp = wp->w_wndp;
  542.     }
  543.     free((char *) lp1);
  544.     free((char *) lp2);
  545.     return (TRUE);
  546. }
  547.  
  548. /*
  549.  * Delete all of the text saved in the kill buffer. Called by commands when a
  550.  * new kill context is being created. The kill buffer array is released, just
  551.  * in case the buffer has grown to immense size. No errors.
  552.  */
  553. kdelete()
  554. {
  555.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  556.  
  557.     if (kbufh != NULL) {
  558.  
  559.         /* first, delete all the chunks */
  560.         kbufp = kbufh;
  561.         while (kbufp != NULL) {
  562.             kp = kbufp->d_next;
  563.             free(kbufp);
  564.             kbufp = kp;
  565.         }
  566.  
  567.         /* and reset all the kill buffer pointers */
  568.         kbufh = kbufp = NULL;
  569.         kused = KBLOCK;             
  570.     }
  571. }
  572.  
  573. /*
  574.  * Insert a character to the kill buffer, allocating new chunks as needed.
  575.  * Return TRUE if all is well, and FALSE on errors.
  576.  */
  577.  
  578. kinsert(c)
  579.  
  580. int c;        /* character to insert in the kill buffer */
  581.  
  582. {
  583.     KILL *nchunk;    /* ptr to newly malloced chunk */
  584.  
  585.     /* check to see if we need a new chunk */
  586.     if (kused >= KBLOCK) {
  587.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  588.             return(FALSE);
  589.         if (kbufh == NULL)    /* set head ptr if first time */
  590.             kbufh = nchunk;
  591.         if (kbufp != NULL)    /* point the current to this new one */
  592.             kbufp->d_next = nchunk;
  593.         kbufp = nchunk;
  594.         kbufp->d_next = NULL;
  595.         kused = 0;
  596.     }
  597.  
  598.     /* and now insert the character */
  599.     kbufp->d_chunk[kused++] = c;
  600.     return(TRUE);
  601. }
  602.  
  603. /*
  604.  * Yank text back from the kill buffer. This is really easy. All of the work
  605.  * is done by the standard insert routines. All you do is run the loop, and
  606.  * check for errors. Bound to "C-Y".
  607.  */
  608. yank(f, n)
  609. {
  610.     register int    c;
  611.     register int    i;
  612.     register char    *sp;    /* pointer into string to insert */
  613.     KILL *kp;        /* pointer into kill buffer */
  614.  
  615.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  616.         return(rdonly());    /* we are in read only mode    */
  617.     if (n < 0)
  618.         return (FALSE);
  619.     /* make sure there is something to yank */
  620.     if (kbufh == NULL)
  621.         return(TRUE);        /* not an error, just nothing */
  622.  
  623.     /* for each time.... */
  624.     while (n--) {
  625.         kp = kbufh;
  626.         while (kp != NULL) {
  627.             if (kp->d_next == NULL)
  628.                 i = kused;
  629.             else
  630.                 i = KBLOCK;
  631.             sp = kp->d_chunk;
  632.             while (i--) {
  633.                 if ((c = *sp++) == '\n') {
  634.                     if (lnewline() == FALSE)
  635.                         return (FALSE);
  636.                 } else {
  637.                     if (linsert(1, c) == FALSE)
  638.                         return (FALSE);
  639.                 }
  640.             }
  641.             kp = kp->d_next;
  642.         }
  643.     }
  644.     return (TRUE);
  645. }
  646.