|
|
1.1 ! root 1: /* Copyright (c) 1979 Regents of the University of California */ ! 2: #include "ex.h" ! 3: #include "ex_tty.h" ! 4: #include "ex_vis.h" ! 5: ! 6: /* ! 7: * Low level routines for operations sequences, ! 8: * and mostly, insert mode (and a subroutine ! 9: * to read an input line, including in the echo area.) ! 10: */ ! 11: char *vUA1, *vUA2; ! 12: char *vUD1, *vUD2; ! 13: ! 14: /* ! 15: * Obleeperate characters in hardcopy ! 16: * open with \'s. ! 17: */ ! 18: bleep(i, cp) ! 19: register int i; ! 20: char *cp; ! 21: { ! 22: ! 23: i -= column(cp); ! 24: do ! 25: putchar('\\' | QUOTE); ! 26: while (--i >= 0); ! 27: rubble = 1; ! 28: } ! 29: ! 30: /* ! 31: * Common code for middle part of delete ! 32: * and change operating on parts of lines. ! 33: */ ! 34: vdcMID() ! 35: { ! 36: register char *cp; ! 37: ! 38: squish(); ! 39: setLAST(); ! 40: vundkind = VCHNG, CP(vutmp, linebuf); ! 41: if (wcursor < cursor) ! 42: cp = wcursor, wcursor = cursor, cursor = cp; ! 43: vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor; ! 44: return (column(wcursor - 1)); ! 45: } ! 46: ! 47: /* ! 48: * Take text from linebuf and stick it ! 49: * in the VBSIZE buffer BUF. Used to save ! 50: * deleted text of part of line. ! 51: */ ! 52: takeout(BUF) ! 53: char *BUF; ! 54: { ! 55: register char *cp; ! 56: ! 57: if (wcursor < linebuf) ! 58: wcursor = linebuf; ! 59: if (cursor == wcursor) { ! 60: beep(); ! 61: return; ! 62: } ! 63: if (wcursor < cursor) { ! 64: cp = wcursor; ! 65: wcursor = cursor; ! 66: cursor = cp; ! 67: } ! 68: setBUF(BUF); ! 69: if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF) ! 70: beep(); ! 71: } ! 72: ! 73: /* ! 74: * Are we at the end of the printed representation of the ! 75: * line? Used internally in hardcopy open. ! 76: */ ! 77: ateopr() ! 78: { ! 79: register int i, c; ! 80: register char *cp = vtube[destline] + destcol; ! 81: ! 82: for (i = WCOLS - destcol; i > 0; i--) { ! 83: c = *cp++; ! 84: if (c == 0) ! 85: return (1); ! 86: if (c != ' ' && (c & QUOTE) == 0) ! 87: return (0); ! 88: } ! 89: return (1); ! 90: } ! 91: ! 92: /* ! 93: * Append. ! 94: * ! 95: * This routine handles the top level append, doing work ! 96: * as each new line comes in, and arranging repeatability. ! 97: * It also handles append with repeat counts, and calculation ! 98: * of autoindents for new lines. ! 99: */ ! 100: bool vaifirst; ! 101: bool gobbled; ! 102: char *ogcursor; ! 103: ! 104: vappend(ch, cnt, indent) ! 105: char ch; ! 106: int cnt, indent; ! 107: { ! 108: register int i; ! 109: register char *gcursor; ! 110: bool escape; ! 111: int repcnt; ! 112: short oldhold = hold; ! 113: ! 114: /* ! 115: * Before a move in hardopen when the line is dirty ! 116: * or we are in the middle of the printed representation, ! 117: * we retype the line to the left of the cursor so the ! 118: * insert looks clean. ! 119: */ ! 120: if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { ! 121: rubble = 1; ! 122: gcursor = cursor; ! 123: i = *gcursor; ! 124: *gcursor = ' '; ! 125: wcursor = gcursor; ! 126: vmove(); ! 127: *gcursor = i; ! 128: } ! 129: vaifirst = indent == 0; ! 130: ! 131: /* ! 132: * Handle replace character by (eventually) ! 133: * limiting the number of input characters allowed ! 134: * in the vgetline routine. ! 135: */ ! 136: if (ch == 'r') ! 137: repcnt = 2; ! 138: else ! 139: repcnt = 0; ! 140: ! 141: /* ! 142: * If an autoindent is specified, then ! 143: * generate a mixture of blanks to tabs to implement ! 144: * it and place the cursor after the indent. ! 145: * Text read by the vgetline routine will be placed in genbuf, ! 146: * so the indent is generated there. ! 147: */ ! 148: if (value(AUTOINDENT) && indent != 0) { ! 149: gcursor = genindent(indent); ! 150: *gcursor = 0; ! 151: vgotoCL(qcolumn(cursor - 1, genbuf)); ! 152: } else { ! 153: gcursor = genbuf; ! 154: *gcursor = 0; ! 155: if (ch == 'o') ! 156: vfixcurs(); ! 157: } ! 158: ! 159: /* ! 160: * Prepare for undo. Pointers delimit inserted portion of line. ! 161: */ ! 162: vUA1 = vUA2 = cursor; ! 163: ! 164: /* ! 165: * If we are not in a repeated command and a ^@ comes in ! 166: * then this means the previous inserted text. ! 167: * If there is none or it was too long to be saved, ! 168: * then beep() and also arrange to undo any damage done ! 169: * so far (e.g. if we are a change.) ! 170: */ ! 171: if ((vglobp && *vglobp == 0) || peekbr()) { ! 172: if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) { ! 173: beep(); ! 174: if (!splitw) ! 175: ungetkey('u'); ! 176: doomed = 0; ! 177: hold = oldhold; ! 178: return; ! 179: } ! 180: /* ! 181: * Unread input from INS. ! 182: * An escape will be generated at end of string. ! 183: * Hold off n^^2 type update on dumb terminals. ! 184: */ ! 185: vglobp = INS; ! 186: hold |= HOLDQIK; ! 187: } else if (vglobp == 0) ! 188: /* ! 189: * Not a repeated command, get ! 190: * a new inserted text for repeat. ! 191: */ ! 192: INS[0] = 0; ! 193: ! 194: /* ! 195: * For wrapmargin to hack away second space after a '.' ! 196: * when the first space caused a line break we keep ! 197: * track that this happened in gobblebl, which says ! 198: * to gobble up a blank silently. ! 199: */ ! 200: gobblebl = 0; ! 201: ! 202: /* ! 203: * Text gathering loop. ! 204: * New text goes into genbuf starting at gcursor. ! 205: * cursor preserves place in linebuf where text will eventually go. ! 206: */ ! 207: if (*cursor == 0 || state == CRTOPEN) ! 208: hold |= HOLDROL; ! 209: for (;;) { ! 210: if (ch == 'r' && repcnt == 0) ! 211: escape = 0; ! 212: else { ! 213: gcursor = vgetline(repcnt, gcursor, &escape); ! 214: ! 215: /* ! 216: * After an append, stick information ! 217: * about the ^D's and ^^D's and 0^D's in ! 218: * the repeated text buffer so repeated ! 219: * inserts of stuff indented with ^D as backtab's ! 220: * can work. ! 221: */ ! 222: if (HADUP) ! 223: addtext("^"); ! 224: else if (HADZERO) ! 225: addtext("0"); ! 226: while (CDCNT > 0) ! 227: addtext("\204"), CDCNT--; ! 228: if (gobbled) ! 229: addtext(" "); ! 230: addtext(ogcursor); ! 231: } ! 232: repcnt = 0; ! 233: ! 234: /* ! 235: * Smash the generated and preexisting indents together ! 236: * and generate one cleanly made out of tabs and spaces ! 237: * if we are using autoindent. ! 238: */ ! 239: if (!vaifirst && value(AUTOINDENT)) { ! 240: i = fixindent(indent); ! 241: if (!HADUP) ! 242: indent = i; ! 243: gcursor = strend(genbuf); ! 244: } ! 245: ! 246: /* ! 247: * Limit the repetition count based on maximum ! 248: * possible line length; do output implied ! 249: * by further count (> 1) and cons up the new line ! 250: * in linebuf. ! 251: */ ! 252: cnt = vmaxrep(ch, cnt); ! 253: CP(gcursor + 1, cursor); ! 254: do { ! 255: CP(cursor, genbuf); ! 256: if (cnt > 1) { ! 257: int oldhold = hold; ! 258: ! 259: Outchar = vinschar; ! 260: hold |= HOLDQIK; ! 261: printf("%s", genbuf); ! 262: hold = oldhold; ! 263: Outchar = vputchar; ! 264: } ! 265: cursor += gcursor - genbuf; ! 266: } while (--cnt > 0); ! 267: endim(); ! 268: vUA2 = cursor; ! 269: if (escape != '\n') ! 270: CP(cursor, gcursor + 1); ! 271: ! 272: /* ! 273: * If doomed characters remain, clobber them, ! 274: * and reopen the line to get the display exact. ! 275: */ ! 276: if (state != HARDOPEN) { ! 277: DEPTH(vcline) = 0; ! 278: if (doomed > 0) { ! 279: register int cind = cindent(); ! 280: ! 281: physdc(cind, cind + doomed); ! 282: doomed = 0; ! 283: } ! 284: i = vreopen(LINE(vcline), lineDOT(), vcline); ! 285: } ! 286: ! 287: /* ! 288: * All done unless we are continuing on to another line. ! 289: */ ! 290: if (escape != '\n') ! 291: break; ! 292: ! 293: /* ! 294: * Set up for the new line. ! 295: * First save the current line, then construct a new ! 296: * first image for the continuation line consisting ! 297: * of any new autoindent plus the pushed ahead text. ! 298: */ ! 299: killU(); ! 300: addtext(gobblebl ? " " : "\n"); ! 301: vsave(); ! 302: cnt = 1; ! 303: if (value(AUTOINDENT)) { ! 304: #ifdef LISPCODE ! 305: if (value(LISP)) ! 306: indent = lindent(dot + 1); ! 307: else ! 308: #endif ! 309: if (!HADUP && vaifirst) ! 310: indent = whitecnt(linebuf); ! 311: vaifirst = 0; ! 312: strcLIN(vpastwh(gcursor + 1)); ! 313: gcursor = genindent(indent); ! 314: *gcursor = 0; ! 315: if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) ! 316: gcursor = genbuf; ! 317: CP(gcursor, linebuf); ! 318: } else { ! 319: CP(genbuf, gcursor + 1); ! 320: gcursor = genbuf; ! 321: } ! 322: ! 323: /* ! 324: * If we started out as a single line operation and are now ! 325: * turning into a multi-line change, then we had better yank ! 326: * out dot before it changes so that undo will work ! 327: * correctly later. ! 328: */ ! 329: if (vundkind == VCHNG) { ! 330: vremote(1, yank, 0); ! 331: undap1--; ! 332: } ! 333: ! 334: /* ! 335: * Now do the append of the new line in the buffer, ! 336: * and update the display. If slowopen ! 337: * we don't do very much. ! 338: */ ! 339: vdoappend(genbuf); ! 340: vundkind = VMANYINS; ! 341: vcline++; ! 342: if (state != VISUAL) ! 343: vshow(dot, NOLINE); ! 344: else { ! 345: i += LINE(vcline - 1); ! 346: vopen(dot, i); ! 347: if (value(SLOWOPEN)) ! 348: vscrap(); ! 349: else ! 350: vsync1(LINE(vcline)); ! 351: } ! 352: strcLIN(gcursor); ! 353: *gcursor = 0; ! 354: cursor = linebuf; ! 355: vgotoCL(qcolumn(cursor - 1, genbuf)); ! 356: } ! 357: ! 358: /* ! 359: * All done with insertion, position the cursor ! 360: * and sync the screen. ! 361: */ ! 362: hold = oldhold; ! 363: if (cursor > linebuf) ! 364: cursor--; ! 365: if (state != HARDOPEN) ! 366: vsyncCL(); ! 367: else if (cursor > linebuf) ! 368: back1(); ! 369: doomed = 0; ! 370: wcursor = cursor; ! 371: vmove(); ! 372: } ! 373: ! 374: /* ! 375: * Subroutine for vgetline to back up a single character position, ! 376: * backwards around end of lines (vgoto can't hack columns which are ! 377: * less than 0 in general). ! 378: */ ! 379: back1() ! 380: { ! 381: ! 382: vgoto(destline - 1, WCOLS + destcol - 1); ! 383: } ! 384: ! 385: /* ! 386: * Get a line into genbuf after gcursor. ! 387: * Cnt limits the number of input characters ! 388: * accepted and is used for handling the replace ! 389: * single character command. Aescaped is the location ! 390: * where we stick a termination indicator (whether we ! 391: * ended with an ESCAPE or a newline/return. ! 392: * ! 393: * We do erase-kill type processing here and also ! 394: * are careful about the way we do this so that it is ! 395: * repeatable. (I.e. so that your kill doesn't happen, ! 396: * when you repeat an insert if it was escaped with \ the ! 397: * first time you did it. ! 398: */ ! 399: char * ! 400: vgetline(cnt, gcursor, aescaped) ! 401: int cnt; ! 402: register char *gcursor; ! 403: bool *aescaped; ! 404: { ! 405: register int c, ch; ! 406: register char *cp; ! 407: int x, y, iwhite; ! 408: char *iglobp; ! 409: int (*OO)() = Outchar; ! 410: ! 411: /* ! 412: * Clear the output state and counters ! 413: * for autoindent backwards motion (counts of ^D, etc.) ! 414: * Remember how much white space at beginning of line so ! 415: * as not to allow backspace over autoindent. ! 416: */ ! 417: *aescaped = 0; ! 418: ogcursor = gcursor; ! 419: flusho(); ! 420: CDCNT = 0; ! 421: HADUP = 0; ! 422: HADZERO = 0; ! 423: gobbled = 0; ! 424: iwhite = whitecnt(genbuf); ! 425: iglobp = vglobp; ! 426: ! 427: /* ! 428: * Carefully avoid using vinschar in the echo area. ! 429: */ ! 430: if (splitw) ! 431: Outchar = vputchar; ! 432: else { ! 433: Outchar = vinschar; ! 434: vprepins(); ! 435: } ! 436: for (;;) { ! 437: if (gobblebl) ! 438: gobblebl--; ! 439: if (cnt != 0) { ! 440: cnt--; ! 441: if (cnt == 0) ! 442: goto vadone; ! 443: } ! 444: ch = c = getkey() & (QUOTE|TRIM); ! 445: if (value(MAPINPUT)) ! 446: while ((ch = map(c, arrows)) != c) ! 447: c = ch; ! 448: if (!iglobp) { ! 449: ! 450: /* ! 451: * Erase-kill type processing. ! 452: * Only happens if we were not reading ! 453: * from untyped input when we started. ! 454: * Map users erase to ^H, kill to -1 for switch. ! 455: */ ! 456: if (c == tty.sg_erase) ! 457: c = CTRL(h); ! 458: else if (c == tty.sg_kill) ! 459: c = -1; ! 460: switch (c) { ! 461: ! 462: /* ! 463: * ^? Interrupt drops you back to visual ! 464: * command mode with an unread interrupt ! 465: * still in the input buffer. ! 466: * ! 467: * ^\ Quit does the same as interrupt. ! 468: * If you are a ex command rather than ! 469: * a vi command this will drop you ! 470: * back to command mode for sure. ! 471: */ ! 472: case ATTN: ! 473: case QUIT: ! 474: ungetkey(c); ! 475: goto vadone; ! 476: ! 477: /* ! 478: * ^H Backs up a character in the input. ! 479: * ! 480: * BUG: Can't back around line boundaries. ! 481: * This is hard because stuff has ! 482: * already been saved for repeat. ! 483: */ ! 484: case CTRL(h): ! 485: bakchar: ! 486: cp = gcursor - 1; ! 487: if (cp < ogcursor) { ! 488: if (splitw) { ! 489: /* ! 490: * Backspacing over readecho ! 491: * prompt. Pretend delete but ! 492: * don't beep. ! 493: */ ! 494: ungetkey(c); ! 495: goto vadone; ! 496: } ! 497: beep(); ! 498: continue; ! 499: } ! 500: goto vbackup; ! 501: ! 502: /* ! 503: * ^W Back up a white/non-white word. ! 504: */ ! 505: case CTRL(w): ! 506: wdkind = 1; ! 507: for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) ! 508: continue; ! 509: for (c = wordch(cp - 1); ! 510: cp > ogcursor && wordof(c, cp - 1); cp--) ! 511: continue; ! 512: goto vbackup; ! 513: ! 514: /* ! 515: * users kill Kill input on this line, back to ! 516: * the autoindent. ! 517: */ ! 518: case -1: ! 519: cp = ogcursor; ! 520: vbackup: ! 521: if (cp == gcursor) { ! 522: beep(); ! 523: continue; ! 524: } ! 525: endim(); ! 526: *cp = 0; ! 527: c = cindent(); ! 528: vgotoCL(qcolumn(cursor - 1, genbuf)); ! 529: if (doomed >= 0) ! 530: doomed += c - cindent(); ! 531: gcursor = cp; ! 532: continue; ! 533: ! 534: /* ! 535: * \ Followed by erase or kill ! 536: * maps to just the erase or kill. ! 537: */ ! 538: case '\\': ! 539: x = destcol, y = destline; ! 540: putchar('\\'); ! 541: vcsync(); ! 542: c = getkey(); ! 543: if (c == tty.sg_erase || c == tty.sg_kill) { ! 544: vgoto(y, x); ! 545: if (doomed >= 0) ! 546: doomed++; ! 547: goto def; ! 548: } ! 549: ungetkey(c), c = '\\'; ! 550: goto noput; ! 551: ! 552: /* ! 553: * ^Q Super quote following character ! 554: * Only ^@ is verboten (trapped at ! 555: * a lower level) and \n forces a line ! 556: * split so doesn't really go in. ! 557: * ! 558: * ^V Synonym for ^Q ! 559: */ ! 560: case CTRL(q): ! 561: case CTRL(v): ! 562: x = destcol, y = destline; ! 563: putchar('^'); ! 564: vgoto(y, x); ! 565: c = getkey(); ! 566: #ifdef TIOCSETC ! 567: if (c == ATTN) ! 568: c = nttyc.t_intrc; ! 569: #endif ! 570: if (c != NL) { ! 571: if (doomed >= 0) ! 572: doomed++; ! 573: goto def; ! 574: } ! 575: break; ! 576: } ! 577: } ! 578: ! 579: /* ! 580: * If we get a blank not in the echo area ! 581: * consider splitting the window in the wrapmargin. ! 582: */ ! 583: if (c == ' ' && !splitw) { ! 584: if (gobblebl) { ! 585: gobbled = 1; ! 586: continue; ! 587: } ! 588: if (value(WRAPMARGIN) && outcol >= OCOLUMNS - value(WRAPMARGIN)) { ! 589: c = NL; ! 590: gobblebl = 2; ! 591: } ! 592: } ! 593: switch (c) { ! 594: ! 595: /* ! 596: * ^M Except in repeat maps to \n. ! 597: */ ! 598: case CR: ! 599: if (vglobp) ! 600: goto def; ! 601: c = '\n'; ! 602: /* presto chango ... */ ! 603: ! 604: /* ! 605: * \n Start new line. ! 606: */ ! 607: case NL: ! 608: *aescaped = c; ! 609: goto vadone; ! 610: ! 611: /* ! 612: * escape End insert unless repeat and more to repeat. ! 613: */ ! 614: case ESCAPE: ! 615: if (lastvgk) ! 616: goto def; ! 617: goto vadone; ! 618: ! 619: /* ! 620: * ^D Backtab. ! 621: * ^T Software forward tab. ! 622: * ! 623: * Unless in repeat where this means these ! 624: * were superquoted in. ! 625: */ ! 626: case CTRL(d): ! 627: case CTRL(t): ! 628: if (vglobp) ! 629: goto def; ! 630: /* fall into ... */ ! 631: ! 632: /* ! 633: * ^D|QUOTE Is a backtab (in a repeated command). ! 634: */ ! 635: case CTRL(d) | QUOTE: ! 636: *gcursor = 0; ! 637: cp = vpastwh(genbuf); ! 638: c = whitecnt(genbuf); ! 639: if (ch == CTRL(t)) { ! 640: /* ! 641: * ^t just generates new indent replacing ! 642: * current white space rounded up to soft ! 643: * tab stop increment. ! 644: */ ! 645: if (cp != gcursor) ! 646: /* ! 647: * BUG: Don't hack ^T except ! 648: * right after initial ! 649: * white space. ! 650: */ ! 651: continue; ! 652: cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); ! 653: ogcursor = cp; ! 654: goto vbackup; ! 655: } ! 656: /* ! 657: * ^D works only if we are at the (end of) the ! 658: * generated autoindent. We count the ^D for repeat ! 659: * purposes. ! 660: */ ! 661: if (c == iwhite && c != 0) ! 662: if (cp == gcursor) { ! 663: iwhite = backtab(c); ! 664: CDCNT++; ! 665: ogcursor = cp = genindent(iwhite); ! 666: goto vbackup; ! 667: } else if (&cp[1] == gcursor && ! 668: (*cp == '^' || *cp == '0')) { ! 669: /* ! 670: * ^^D moves to margin, then back ! 671: * to current indent on next line. ! 672: * ! 673: * 0^D moves to margin and then ! 674: * stays there. ! 675: */ ! 676: HADZERO = *cp == '0'; ! 677: ogcursor = cp = genbuf; ! 678: HADUP = 1 - HADZERO; ! 679: CDCNT = 1; ! 680: endim(); ! 681: back1(); ! 682: vputc(' '); ! 683: goto vbackup; ! 684: } ! 685: if (vglobp && vglobp - iglobp >= 2 && ! 686: (vglobp[-2] == '^' || vglobp[-2] == '0') ! 687: && gcursor == ogcursor + 1) ! 688: goto bakchar; ! 689: continue; ! 690: ! 691: default: ! 692: /* ! 693: * Possibly discard control inputs. ! 694: */ ! 695: if (!vglobp && junk(c)) { ! 696: beep(); ! 697: continue; ! 698: } ! 699: def: ! 700: putchar(c); ! 701: noput: ! 702: if (gcursor > &genbuf[LBSIZE - 2]) ! 703: error("Line too long"); ! 704: *gcursor++ = c & TRIM; ! 705: vcsync(); ! 706: #ifdef LISPCODE ! 707: if (value(SHOWMATCH) && !iglobp) ! 708: if (c == ')' || c == '}') ! 709: lsmatch(gcursor); ! 710: #endif ! 711: continue; ! 712: } ! 713: } ! 714: vadone: ! 715: *gcursor = 0; ! 716: Outchar = OO; ! 717: endim(); ! 718: return (gcursor); ! 719: } ! 720: ! 721: int vgetsplit(); ! 722: char *vsplitpt; ! 723: ! 724: /* ! 725: * Append the line in buffer at lp ! 726: * to the buffer after dot. ! 727: */ ! 728: vdoappend(lp) ! 729: char *lp; ! 730: { ! 731: register int oing = inglobal; ! 732: ! 733: vsplitpt = lp; ! 734: inglobal = 1; ! 735: ignore(append(vgetsplit, dot)); ! 736: inglobal = oing; ! 737: } ! 738: ! 739: /* ! 740: * Subroutine for vdoappend to pass to append. ! 741: */ ! 742: vgetsplit() ! 743: { ! 744: ! 745: if (vsplitpt == 0) ! 746: return (EOF); ! 747: strcLIN(vsplitpt); ! 748: vsplitpt = 0; ! 749: return (0); ! 750: } ! 751: ! 752: /* ! 753: * Vmaxrep determines the maximum repetitition factor ! 754: * allowed that will yield total line length less than ! 755: * LBSIZE characters and also does hacks for the R command. ! 756: */ ! 757: vmaxrep(ch, cnt) ! 758: char ch; ! 759: register int cnt; ! 760: { ! 761: register int len, replen; ! 762: ! 763: if (cnt > LBSIZE - 2) ! 764: cnt = LBSIZE - 2; ! 765: replen = strlen(genbuf); ! 766: if (ch == 'R') { ! 767: len = strlen(cursor); ! 768: if (replen < len) ! 769: len = replen; ! 770: CP(cursor, cursor + len); ! 771: vUD2 += len; ! 772: } ! 773: len = strlen(linebuf); ! 774: if (len + cnt * replen <= LBSIZE - 2) ! 775: return (cnt); ! 776: cnt = (LBSIZE - 2 - len) / replen; ! 777: if (cnt == 0) { ! 778: vsave(); ! 779: error("Line too long"); ! 780: } ! 781: return (cnt); ! 782: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.