Annotation of src/usr.bin/mg/line.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * Text line handling.
! 3: * The functions in this file
! 4: * are a general set of line management
! 5: * utilities. They are the only routines that
! 6: * touch the text. They also touch the buffer
! 7: * and window structures, to make sure that the
! 8: * necessary updating gets done. There are routines
! 9: * in this file that handle the kill buffer too.
! 10: * It isn't here for any good reason.
! 11: *
! 12: * Note that this code only updates the dot and
! 13: * mark values in the window list. Since all the code
! 14: * acts on the current window, the buffer that we
! 15: * are editing must be being displayed, which means
! 16: * that "b_nwnd" is non zero, which means that the
! 17: * dot and mark values in the buffer headers are
! 18: * nonsense.
! 19: */
! 20: #include "def.h"
! 21:
! 22: /* number of bytes member is from start of structure type */
! 23: /* should be computed at compile time */
! 24:
! 25: #ifndef OFFSET
! 26: #define OFFSET(type,member) ((char *)&(((type *)0)->member)-(char *)((type *)0))
! 27: #endif
! 28:
! 29: #ifndef NBLOCK
! 30: #define NBLOCK 16 /* Line block chunk size */
! 31: #endif
! 32:
! 33: #ifndef KBLOCK
! 34: #define KBLOCK 256 /* Kill buffer block size. */
! 35: #endif
! 36:
! 37: static char *kbufp = NULL; /* Kill buffer data. */
! 38: static RSIZE kused = 0; /* # of bytes used in KB. */
! 39: static RSIZE ksize = 0; /* # of bytes allocated in KB. */
! 40: static RSIZE kstart = 0; /* # of first used byte in KB. */
! 41:
! 42: /*
! 43: * This routine allocates a block of memory large enough to hold a LINE
! 44: * containing "used" characters. The block is rounded up to whatever
! 45: * needs to be allocated. (use lallocx for lines likely to grow.)
! 46: * Return a pointer to the new block, or NULL if there isn't
! 47: * any memory left. Print a message in the message line if no space.
! 48: */
! 49: LINE *
! 50: lalloc(used) register int used; {
! 51: register LINE *lp;
! 52: register int size;
! 53:
! 54: /* any padding at the end of the structure is used */
! 55: if((size = used + OFFSET(LINE, l_text[0])) < sizeof(LINE))
! 56: size = sizeof(LINE);
! 57: #ifdef MALLOCROUND
! 58: MALLOCROUND(size); /* round up to a size optimal to malloc */
! 59: #endif
! 60: if((lp = (LINE *)malloc((unsigned)size)) == NULL) {
! 61: ewprintf("Can't get %d bytes", size);
! 62: return (LINE *)NULL;
! 63: }
! 64: lp->l_size = size - OFFSET(LINE, l_text[0]);
! 65: lp->l_used = used;
! 66: return lp;
! 67: }
! 68:
! 69: /*
! 70: * Like lalloc, only round amount desired up because this line will
! 71: * probably grow. We always make room for at least one more char.
! 72: * (thus making 0 not a special case anymore.)
! 73: */
! 74: LINE *
! 75: lallocx(used)
! 76: int used;
! 77: {
! 78: register int size;
! 79: register LINE *lp;
! 80:
! 81: size = (NBLOCK+used) & ~(NBLOCK-1);
! 82: if((lp = lalloc(size)) != NULL) lp->l_used = used;
! 83: return lp;
! 84: }
! 85:
! 86: /*
! 87: * Delete line "lp". Fix all of the
! 88: * links that might point at it (they are
! 89: * moved to offset 0 of the next line.
! 90: * Unlink the line from whatever buffer it
! 91: * might be in. Release the memory. The
! 92: * buffers are updated too; the magic conditions
! 93: * described in the above comments don't hold
! 94: * here.
! 95: */
! 96: VOID
! 97: lfree(lp) register LINE *lp; {
! 98: register BUFFER *bp;
! 99: register WINDOW *wp;
! 100:
! 101: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 102: if (wp->w_linep == lp)
! 103: wp->w_linep = lp->l_fp;
! 104: if (wp->w_dotp == lp) {
! 105: wp->w_dotp = lp->l_fp;
! 106: wp->w_doto = 0;
! 107: }
! 108: if (wp->w_markp == lp) {
! 109: wp->w_markp = lp->l_fp;
! 110: wp->w_marko = 0;
! 111: }
! 112: }
! 113: for(bp = bheadp; bp != NULL; bp = bp->b_bufp) {
! 114: if (bp->b_nwnd == 0) {
! 115: if (bp->b_dotp == lp) {
! 116: bp->b_dotp = lp->l_fp;
! 117: bp->b_doto = 0;
! 118: }
! 119: if (bp->b_markp == lp) {
! 120: bp->b_markp = lp->l_fp;
! 121: bp->b_marko = 0;
! 122: }
! 123: }
! 124: }
! 125: lp->l_bp->l_fp = lp->l_fp;
! 126: lp->l_fp->l_bp = lp->l_bp;
! 127: free((char *) lp);
! 128: }
! 129:
! 130: /*
! 131: * This routine gets called when
! 132: * a character is changed in place in the
! 133: * current buffer. It updates all of the required
! 134: * flags in the buffer and window system. The flag
! 135: * used is passed as an argument; if the buffer is being
! 136: * displayed in more than 1 window we change EDIT to
! 137: * HARD. Set MODE if the mode line needs to be
! 138: * updated (the "*" has to be set).
! 139: */
! 140: VOID
! 141: lchange(flag) register int flag; {
! 142: register WINDOW *wp;
! 143:
! 144: if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
! 145: flag |= WFMODE; /* update mode lines. */
! 146: curbp->b_flag |= BFCHG;
! 147: }
! 148: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 149: if (wp->w_bufp == curbp) {
! 150: wp->w_flag |= flag;
! 151: if(wp != curwp) wp->w_flag |= WFHARD;
! 152: }
! 153: }
! 154: }
! 155:
! 156: /*
! 157: * Insert "n" copies of the character "c"
! 158: * at the current location of dot. In the easy case
! 159: * all that happens is the text is stored in the line.
! 160: * In the hard case, the line has to be reallocated.
! 161: * When the window list is updated, take special
! 162: * care; I screwed it up once. You always update dot
! 163: * in the current window. You update mark, and a
! 164: * dot in another window, if it is greater than
! 165: * the place where you did the insert. Return TRUE
! 166: * if all is well, and FALSE on errors.
! 167: */
! 168: linsert(n, c)
! 169: int n;
! 170: {
! 171: register char *cp1;
! 172: register char *cp2;
! 173: register LINE *lp1;
! 174: LINE *lp2;
! 175: LINE *lp3;
! 176: register int doto;
! 177: register RSIZE i;
! 178: WINDOW *wp;
! 179:
! 180: lchange(WFEDIT);
! 181: lp1 = curwp->w_dotp; /* Current line */
! 182: if (lp1 == curbp->b_linep) { /* At the end: special */
! 183: /* (now should only happen in empty buffer */
! 184: if (curwp->w_doto != 0) {
! 185: ewprintf("bug: linsert");
! 186: return FALSE;
! 187: }
! 188: if ((lp2=lallocx(n)) == NULL) /* Allocate new line */
! 189: return FALSE;
! 190: lp3 = lp1->l_bp; /* Previous line */
! 191: lp3->l_fp = lp2; /* Link in */
! 192: lp2->l_fp = lp1;
! 193: lp1->l_bp = lp2;
! 194: lp2->l_bp = lp3;
! 195: for (i=0; i<n; ++i)
! 196: lp2->l_text[i] = c;
! 197: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 198: if (wp->w_linep == lp1)
! 199: wp->w_linep = lp2;
! 200: if (wp->w_dotp == lp1)
! 201: wp->w_dotp = lp2;
! 202: if (wp->w_markp == lp1)
! 203: wp->w_markp = lp2;
! 204: }
! 205: /*NOSTRICT*/
! 206: curwp->w_doto = n;
! 207: return TRUE;
! 208: }
! 209: doto = curwp->w_doto; /* Save for later. */
! 210: /*NOSTRICT (2) */
! 211: if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
! 212: if ((lp2=lallocx(lp1->l_used+n)) == NULL)
! 213: return FALSE;
! 214: cp1 = &lp1->l_text[0];
! 215: cp2 = &lp2->l_text[0];
! 216: while (cp1 != &lp1->l_text[doto])
! 217: *cp2++ = *cp1++;
! 218: /*NOSTRICT*/
! 219: cp2 += n;
! 220: while (cp1 != &lp1->l_text[lp1->l_used])
! 221: *cp2++ = *cp1++;
! 222: lp1->l_bp->l_fp = lp2;
! 223: lp2->l_fp = lp1->l_fp;
! 224: lp1->l_fp->l_bp = lp2;
! 225: lp2->l_bp = lp1->l_bp;
! 226: free((char *) lp1);
! 227: } else { /* Easy: in place */
! 228: lp2 = lp1; /* Pretend new line */
! 229: /*NOSTRICT*/
! 230: lp2->l_used += n;
! 231: cp2 = &lp1->l_text[lp1->l_used];
! 232:
! 233: cp1 = cp2-n;
! 234: while (cp1 != &lp1->l_text[doto])
! 235: *--cp2 = *--cp1;
! 236: }
! 237: for (i=0; i<n; ++i) /* Add the characters */
! 238: lp2->l_text[doto+i] = c;
! 239:
! 240: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 241: if (wp->w_linep == lp1)
! 242: wp->w_linep = lp2;
! 243: if (wp->w_dotp == lp1) {
! 244: wp->w_dotp = lp2;
! 245: if (wp==curwp || wp->w_doto>doto)
! 246: /*NOSTRICT*/
! 247: wp->w_doto += n;
! 248: }
! 249: if (wp->w_markp == lp1) {
! 250: wp->w_markp = lp2;
! 251: if (wp->w_marko > doto)
! 252: /*NOSTRICT*/
! 253: wp->w_marko += n;
! 254: }
! 255: }
! 256: return TRUE;
! 257: }
! 258:
! 259: /*
! 260: * Insert a newline into the buffer
! 261: * at the current location of dot in the current
! 262: * window. The funny ass-backwards way is no longer used.
! 263: */
! 264: lnewline()
! 265: {
! 266: register LINE *lp1;
! 267: register LINE *lp2;
! 268: register int doto;
! 269: register int nlen;
! 270: WINDOW *wp;
! 271:
! 272: lchange(WFHARD);
! 273: lp1 = curwp->w_dotp; /* Get the address and */
! 274: doto = curwp->w_doto; /* offset of "." */
! 275: if(doto == 0) { /* avoid unnessisary copying */
! 276: if((lp2 = lallocx(0)) == NULL) /* new first part */
! 277: return FALSE;
! 278: lp2->l_bp = lp1->l_bp;
! 279: lp1->l_bp->l_fp = lp2;
! 280: lp2->l_fp = lp1;
! 281: lp1->l_bp = lp2;
! 282: for(wp = wheadp; wp!=NULL; wp = wp->w_wndp)
! 283: if(wp->w_linep == lp1) wp->w_linep = lp2;
! 284: return TRUE;
! 285: }
! 286: nlen = llength(lp1) - doto; /* length of new part */
! 287: if((lp2=lallocx(nlen)) == NULL) /* New second half line */
! 288: return FALSE;
! 289: if(nlen!=0) bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
! 290: lp1->l_used = doto;
! 291: lp2->l_bp = lp1;
! 292: lp2->l_fp = lp1->l_fp;
! 293: lp1->l_fp = lp2;
! 294: lp2->l_fp->l_bp = lp2;
! 295: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) { /* Windows */
! 296: if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
! 297: wp->w_dotp = lp2;
! 298: wp->w_doto -= doto;
! 299: }
! 300: if (wp->w_markp == lp1 && wp->w_marko >= doto) {
! 301: wp->w_markp = lp2;
! 302: wp->w_marko -= doto;
! 303: }
! 304: }
! 305: return TRUE;
! 306: }
! 307:
! 308: /*
! 309: * This function deletes "n" bytes,
! 310: * starting at dot. It understands how do deal
! 311: * with end of lines, etc. It returns TRUE if all
! 312: * of the characters were deleted, and FALSE if
! 313: * they were not (because dot ran into the end of
! 314: * the buffer. The "kflag" indicates either no insertion,
! 315: * or direction of insertion into the kill buffer.
! 316: */
! 317: ldelete(n, kflag) RSIZE n; {
! 318: register char *cp1;
! 319: register char *cp2;
! 320: register LINE *dotp;
! 321: register int doto;
! 322: register RSIZE chunk;
! 323: WINDOW *wp;
! 324:
! 325: /*
! 326: * HACK - doesn't matter, and fixes back-over-nl bug for empty
! 327: * kill buffers.
! 328: */
! 329: if (kused == kstart) kflag = KFORW;
! 330:
! 331: while (n != 0) {
! 332: dotp = curwp->w_dotp;
! 333: doto = curwp->w_doto;
! 334: if (dotp == curbp->b_linep) /* Hit end of buffer. */
! 335: return FALSE;
! 336: chunk = dotp->l_used-doto; /* Size of chunk. */
! 337: if (chunk > n)
! 338: chunk = n;
! 339: if (chunk == 0) { /* End of line, merge. */
! 340: if(dotp == lback(curbp->b_linep))
! 341: return FALSE; /* End of buffer. */
! 342: lchange(WFHARD);
! 343: if (ldelnewline() == FALSE
! 344: || (kflag!=KNONE && kinsert('\n', kflag)==FALSE))
! 345: return FALSE;
! 346: --n;
! 347: continue;
! 348: }
! 349: lchange(WFEDIT);
! 350: cp1 = &dotp->l_text[doto]; /* Scrunch text. */
! 351: cp2 = cp1 + chunk;
! 352: if (kflag == KFORW) {
! 353: while (ksize - kused < chunk)
! 354: if (kgrow(FALSE) == FALSE) return FALSE;
! 355: bcopy(cp1, &(kbufp[kused]), (int) chunk);
! 356: kused += chunk;
! 357: } else if (kflag == KBACK) {
! 358: while (kstart < chunk)
! 359: if (kgrow(TRUE) == FALSE) return FALSE;
! 360: bcopy(cp1, &(kbufp[kstart-chunk]), (int) chunk);
! 361: kstart -= chunk;
! 362: } else if (kflag != KNONE) panic("broken ldelete call");
! 363: while (cp2 != &dotp->l_text[dotp->l_used])
! 364: *cp1++ = *cp2++;
! 365: dotp->l_used -= (int) chunk;
! 366: for(wp = wheadp; wp != NULL; wp = wp->w_wndp ) {
! 367: if (wp->w_dotp==dotp && wp->w_doto>=doto) {
! 368: /*NOSTRICT*/
! 369: wp->w_doto -= chunk;
! 370: if (wp->w_doto < doto)
! 371: wp->w_doto = doto;
! 372: }
! 373: if (wp->w_markp==dotp && wp->w_marko>=doto) {
! 374: /*NOSTRICT*/
! 375: wp->w_marko -= chunk;
! 376: if (wp->w_marko < doto)
! 377: wp->w_marko = doto;
! 378: }
! 379: }
! 380: n -= chunk;
! 381: }
! 382: return TRUE;
! 383: }
! 384:
! 385: /*
! 386: * Delete a newline. Join the current line
! 387: * with the next line. If the next line is the magic
! 388: * header line always return TRUE; merging the last line
! 389: * with the header line can be thought of as always being a
! 390: * successful operation, even if nothing is done, and this makes
! 391: * the kill buffer work "right". Easy cases can be done by
! 392: * shuffling data around. Hard cases require that lines be moved
! 393: * about in memory. Return FALSE on error and TRUE if all
! 394: * looks ok.
! 395: */
! 396: ldelnewline() {
! 397: register LINE *lp1;
! 398: register LINE *lp2;
! 399: register WINDOW *wp;
! 400: LINE *lp3;
! 401:
! 402: lp1 = curwp->w_dotp;
! 403: lp2 = lp1->l_fp;
! 404: if (lp2 == curbp->b_linep) /* At the buffer end. */
! 405: return TRUE;
! 406: if (lp2->l_used <= lp1->l_size - lp1->l_used) {
! 407: bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used);
! 408: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 409: if (wp->w_linep == lp2)
! 410: wp->w_linep = lp1;
! 411: if (wp->w_dotp == lp2) {
! 412: wp->w_dotp = lp1;
! 413: wp->w_doto += lp1->l_used;
! 414: }
! 415: if (wp->w_markp == lp2) {
! 416: wp->w_markp = lp1;
! 417: wp->w_marko += lp1->l_used;
! 418: }
! 419: }
! 420: lp1->l_used += lp2->l_used;
! 421: lp1->l_fp = lp2->l_fp;
! 422: lp2->l_fp->l_bp = lp1;
! 423: free((char *) lp2);
! 424: return TRUE;
! 425: }
! 426: if ((lp3=lalloc(lp1->l_used + lp2->l_used)) == NULL)
! 427: return FALSE;
! 428: bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used);
! 429: bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used);
! 430: lp1->l_bp->l_fp = lp3;
! 431: lp3->l_fp = lp2->l_fp;
! 432: lp2->l_fp->l_bp = lp3;
! 433: lp3->l_bp = lp1->l_bp;
! 434: for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
! 435: if (wp->w_linep==lp1 || wp->w_linep==lp2)
! 436: wp->w_linep = lp3;
! 437: if (wp->w_dotp == lp1)
! 438: wp->w_dotp = lp3;
! 439: else if (wp->w_dotp == lp2) {
! 440: wp->w_dotp = lp3;
! 441: wp->w_doto += lp1->l_used;
! 442: }
! 443: if (wp->w_markp == lp1)
! 444: wp->w_markp = lp3;
! 445: else if (wp->w_markp == lp2) {
! 446: wp->w_markp = lp3;
! 447: wp->w_marko += lp1->l_used;
! 448: }
! 449: }
! 450: free((char *) lp1);
! 451: free((char *) lp2);
! 452: return TRUE;
! 453: }
! 454:
! 455: /*
! 456: * Replace plen characters before dot with argument string.
! 457: * Control-J characters in st are interpreted as newlines.
! 458: * There is a casehack disable flag (normally it likes to match
! 459: * case of replacement to what was there).
! 460: */
! 461: lreplace(plen, st, f)
! 462: register RSIZE plen; /* length to remove */
! 463: char *st; /* replacement string */
! 464: int f; /* case hack disable */
! 465: {
! 466: register RSIZE rlen; /* replacement length */
! 467: register int rtype; /* capitalization */
! 468: register int c; /* used for random characters */
! 469: register int doto; /* offset into line */
! 470:
! 471: /*
! 472: * Find the capitalization of the word that was found.
! 473: * f says use exact case of replacement string (same thing that
! 474: * happens with lowercase found), so bypass check.
! 475: */
! 476: /*NOSTRICT*/
! 477: (VOID) backchar(FFARG | FFRAND, (int) plen);
! 478: rtype = _L;
! 479: c = lgetc(curwp->w_dotp, curwp->w_doto);
! 480: if (ISUPPER(c)!=FALSE && f==FALSE) {
! 481: rtype = _U|_L;
! 482: if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
! 483: c = lgetc(curwp->w_dotp, curwp->w_doto+1);
! 484: if (ISUPPER(c) != FALSE) {
! 485: rtype = _U;
! 486: }
! 487: }
! 488: }
! 489:
! 490: /*
! 491: * make the string lengths match (either pad the line
! 492: * so that it will fit, or scrunch out the excess).
! 493: * be careful with dot's offset.
! 494: */
! 495: rlen = strlen(st);
! 496: doto = curwp->w_doto;
! 497: if (plen > rlen)
! 498: (VOID) ldelete((RSIZE) (plen-rlen), KNONE);
! 499: else if (plen < rlen) {
! 500: if (linsert((int)(rlen-plen), ' ') == FALSE)
! 501: return FALSE;
! 502: }
! 503: curwp->w_doto = doto;
! 504:
! 505: /*
! 506: * do the replacement: If was capital, then place first
! 507: * char as if upper, and subsequent chars as if lower.
! 508: * If inserting upper, check replacement for case.
! 509: */
! 510: while ((c = CHARMASK(*st++)) != '\0') {
! 511: if ((rtype&_U)!=0 && ISLOWER(c)!=0)
! 512: c = TOUPPER(c);
! 513: if (rtype == (_U|_L))
! 514: rtype = _L;
! 515: if (c == CCHR('J')) {
! 516: if (curwp->w_doto == llength(curwp->w_dotp))
! 517: (VOID) forwchar(FFRAND, 1);
! 518: else {
! 519: if (ldelete((RSIZE) 1, KNONE) != FALSE)
! 520: (VOID) lnewline();
! 521: }
! 522: } else if (curwp->w_dotp == curbp->b_linep) {
! 523: (VOID) linsert(1, c);
! 524: } else if (curwp->w_doto == llength(curwp->w_dotp)) {
! 525: if (ldelete((RSIZE) 1, KNONE) != FALSE)
! 526: (VOID) linsert(1, c);
! 527: } else
! 528: lputc(curwp->w_dotp, curwp->w_doto++, c);
! 529: }
! 530: lchange(WFHARD);
! 531: return (TRUE);
! 532: }
! 533:
! 534: /*
! 535: * Delete all of the text
! 536: * saved in the kill buffer. Called by commands
! 537: * when a new kill context is being created. The kill
! 538: * buffer array is released, just in case the buffer has
! 539: * grown to immense size. No errors.
! 540: */
! 541: VOID
! 542: kdelete() {
! 543: if (kbufp != NULL) {
! 544: free((char *) kbufp);
! 545: kbufp = NULL;
! 546: kstart = kused = ksize = 0;
! 547: }
! 548: }
! 549:
! 550: /*
! 551: * Insert a character to the kill buffer,
! 552: * enlarging the buffer if there isn't any room. Always
! 553: * grow the buffer in chunks, on the assumption that if you
! 554: * put something in the kill buffer you are going to put
! 555: * more stuff there too later. Return TRUE if all is
! 556: * well, and FALSE on errors. Print a message on
! 557: * errors. Dir says whether to put it at back or front.
! 558: */
! 559: kinsert(c, dir) {
! 560:
! 561: if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
! 562: return FALSE;
! 563: if (kstart == 0 && dir == KBACK && kgrow(TRUE) == FALSE)
! 564: return FALSE;
! 565: if (dir == KFORW) kbufp[kused++] = c;
! 566: else if (dir == KBACK) kbufp[--kstart] = c;
! 567: else panic("broken kinsert call"); /* Oh shit! */
! 568: return (TRUE);
! 569: }
! 570:
! 571: /*
! 572: * kgrow - just get more kill buffer for the callee. back is true if
! 573: * we are trying to get space at the beginning of the kill buffer.
! 574: */
! 575: kgrow(back) {
! 576: register int nstart;
! 577: register char *nbufp;
! 578:
! 579: if ((unsigned)(ksize+KBLOCK) <= (unsigned)ksize) {
! 580: /* probably 16 bit unsigned */
! 581: ewprintf("Kill buffer size at maximum");
! 582: return FALSE;
! 583: }
! 584: if ((nbufp=malloc((unsigned)(ksize+KBLOCK))) == NULL) {
! 585: ewprintf("Can't get %ld bytes", (long)(ksize+KBLOCK));
! 586: return FALSE;
! 587: }
! 588: nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4) ;
! 589: bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int) (kused-kstart));
! 590: if (kbufp != NULL)
! 591: free((char *) kbufp);
! 592: kbufp = nbufp;
! 593: ksize += KBLOCK;
! 594: kused = kused - kstart + nstart;
! 595: kstart = nstart;
! 596: return TRUE;
! 597: }
! 598:
! 599: /*
! 600: * This function gets characters from
! 601: * the kill buffer. If the character index "n" is
! 602: * off the end, it returns "-1". This lets the caller
! 603: * just scan along until it gets a "-1" back.
! 604: */
! 605: kremove(n) {
! 606: if (n < 0 || n + kstart >= kused)
! 607: return -1;
! 608: return CHARMASK(kbufp[n + kstart]);
! 609: }