1: /*************************************************************************** 2: * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE * 3: * is provided to you without charge, and with no warranty. You may give * 4: * away copies of JOVE, including sources, provided that this notice is * 5: * included in all the files. * 6: ***************************************************************************/ 7: 8: #include "jove.h" 9: #include "ctype.h" 10: #include "table.h" 11: 12: #ifdef MAC 13: # undef private 14: # define private 15: #endif 16: 17: #ifdef LINT_ARGS 18: private int 19: newchunk(void); 20: private void 21: init_specials(void), 22: remfreelines(struct chunk *); 23: #else 24: private int 25: newchunk(); 26: private void 27: init_specials(), 28: remfreelines(); 29: #endif /* LINT_ARGS */ 30: 31: #ifdef MAC 32: # undef private 33: # define private static 34: #endif 35: 36: /* Make a newline after AFTER in buffer BUF, UNLESS after is 0, 37: in which case we insert the newline before after. */ 38: 39: Line * 40: listput(buf, after) 41: register Buffer *buf; 42: register Line *after; 43: { 44: register Line *newline = nbufline(); 45: 46: if (after == 0) { /* Before the first line */ 47: newline->l_next = buf->b_first; 48: newline->l_prev = 0; 49: buf->b_first = newline; 50: } else { 51: newline->l_prev = after; 52: newline->l_next = after->l_next; 53: after->l_next = newline; 54: } 55: if (newline->l_next) 56: newline->l_next->l_prev = newline; 57: else 58: if (buf) 59: buf->b_last = newline; 60: if (buf && buf->b_dot == 0) 61: buf->b_dot = newline; 62: return newline; 63: } 64: 65: /* Divide the current line and move the current line to the next one */ 66: 67: void 68: LineInsert(num) 69: register int num; 70: { 71: char newline[LBSIZE]; 72: register Line *newdot, 73: *olddot; 74: int oldchar; 75: 76: olddot = curline; 77: oldchar = curchar; 78: 79: newdot = curline; 80: while (--num >= 0) { 81: newdot = listput(curbuf, newdot); 82: SavLine(newdot, NullStr); 83: } 84: 85: modify(); 86: if (curchar != 0) { 87: strcpy(newline, &linebuf[curchar]); 88: linebuf[curchar] = '\0'; /* Shorten this line */ 89: SavLine(curline, linebuf); 90: strcpy(linebuf, newline); 91: } else { /* Redisplay optimization */ 92: newdot->l_dline = curline->l_dline; 93: SavLine(curline, NullStr); 94: } 95: 96: makedirty(curline); 97: curline = newdot; 98: curchar = 0; 99: makedirty(curline); 100: IFixMarks(olddot, oldchar, curline, curchar); 101: } 102: 103: /* Makes the indent of the current line == goal. If the current indent 104: is greater than GOAL it deletes. If more indent is needed, it uses 105: tabs and spaces to get to where it's going. */ 106: 107: void 108: n_indent(goal) 109: register int goal; 110: { 111: int dotcol, 112: incrmt; 113: 114: ToIndent(); 115: dotcol = calc_pos(linebuf, curchar); 116: if (goal < dotcol) { 117: DelWtSpace(); 118: dotcol = 0; 119: } 120: 121: for (;;) { 122: incrmt = (tabstop - (dotcol % tabstop)); 123: if (dotcol + incrmt > goal) 124: break; 125: insert_c('\t', 1); 126: dotcol += incrmt; 127: } 128: if (dotcol != goal) 129: insert_c(' ', (goal - dotcol)); 130: } 131: 132: #ifdef ABBREV 133: void 134: MaybeAbbrevExpand() 135: { 136: if (MinorMode(Abbrev) && !ismword(LastKeyStruck) && 137: !bolp() && ismword(linebuf[curchar - 1])) 138: AbbrevExpand(); 139: } 140: #endif 141: 142: void 143: SelfInsert() 144: { 145: #ifdef ABBREV 146: MaybeAbbrevExpand(); 147: #endif 148: if (LastKeyStruck != CTL('J') && MinorMode(OverWrite)) { 149: register int num, 150: i; 151: 152: for (i = 0, num = arg_value(); i < num; i++) { 153: int pos = calc_pos(linebuf, curchar); 154: 155: if (!eolp()) { 156: if (linebuf[curchar] == '\t') { 157: if ((pos + 1) == ((pos + tabstop) - (pos % tabstop))) 158: del_char(FORWARD, 1); 159: } else 160: del_char(FORWARD, 1); 161: } 162: insert_c(LastKeyStruck, 1); 163: } 164: } else 165: Insert(LastKeyStruck); 166: 167: if (MinorMode(Fill) && (curchar >= RMargin || 168: (calc_pos(linebuf, curchar) >= RMargin))) { 169: int margin; 170: Bufpos save; 171: 172: if (MinorMode(Indent)) { 173: DOTsave(&save); 174: ToIndent(); 175: margin = calc_pos(linebuf, curchar); 176: SetDot(&save); 177: } else 178: margin = LMargin; 179: DoJustify(curline, 0, curline, 180: curchar + strlen(&linebuf[curchar]), 1, margin); 181: } 182: } 183: 184: void 185: Insert(c) 186: { 187: if (c == CTL('J')) 188: LineInsert(arg_value()); 189: else 190: insert_c(c, arg_value()); 191: } 192: 193: /* insert character C N times at point */ 194: void 195: insert_c(c, n) 196: { 197: if (n <= 0) 198: return; 199: modify(); 200: makedirty(curline); 201: ins_c(c, linebuf, curchar, n, LBSIZE); 202: IFixMarks(curline, curchar, curline, curchar + n); 203: curchar += n; 204: } 205: 206: /* Tab in to the right place for C mode */ 207: 208: void 209: Tab() 210: { 211: #ifdef LISP 212: if (MajorMode(LISPMODE) && (bolp() || !eolp())) { 213: int dotchar = curchar; 214: Mark *m = 0; 215: 216: ToIndent(); 217: if (dotchar > curchar) 218: m = MakeMark(curline, dotchar, M_FLOATER); 219: (void) lisp_indent(); 220: if (m) { 221: ToMark(m); 222: DelMark(m); 223: } else 224: ToIndent(); 225: return; 226: } 227: #endif 228: if (MajorMode(CMODE) && strlen(linebuf) == 0) 229: (void) c_indent(CIndIncrmt); 230: else 231: SelfInsert(); 232: } 233: 234: void 235: QuotChar() 236: { 237: int c, 238: slow; 239: 240: c = waitchar(&slow); 241: if (slow) 242: message(key_strokes); 243: if (c != CTL('@')) 244: Insert(c); 245: } 246: 247: /* Insert the paren. If in C mode and c is a '}' then insert the 248: '}' in the "right" place for C indentation; that is indented 249: the same amount as the matching '{' is indented. */ 250: 251: int PDelay = 5, /* 1/2 a second */ 252: CIndIncrmt = 8; 253: 254: void 255: DoParen() 256: { 257: Bufpos *bp = (Bufpos *) -1; 258: int nx, 259: c = LastKeyStruck; 260: 261: if (!isclosep(c)) { 262: SelfInsert(); 263: return; 264: } 265: 266: if (MajorMode(CMODE) && c == '}' && blnkp(linebuf)) 267: bp = c_indent(0); 268: #ifdef LISP 269: if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf)) 270: bp = lisp_indent(); 271: #endif 272: SelfInsert(); 273: #ifdef MAC 274: if (MinorMode(ShowMatch) && !in_macro()) { 275: #else 276: if (MinorMode(ShowMatch) && !charp() && !in_macro()) { 277: #endif 278: b_char(1); /* Back onto the ')' */ 279: if ((int) bp == -1) 280: bp = m_paren(c, BACKWARD, NO, YES); 281: f_char(1); 282: if (bp != 0) { 283: nx = in_window(curwind, bp->p_line); 284: if (nx != -1) { /* is visible */ 285: Bufpos b; 286: 287: DOTsave(&b); 288: SetDot(bp); 289: SitFor(PDelay); 290: SetDot(&b); 291: } else 292: s_mess("%s", lcontents(bp->p_line)); 293: } 294: mp_error(); /* display error message */ 295: } 296: } 297: 298: void 299: LineAI() 300: { 301: DoNewline(TRUE); 302: } 303: 304: void 305: Newline() 306: { 307: DoNewline(MinorMode(Indent)); 308: } 309: 310: void 311: DoNewline(indentp) 312: { 313: Bufpos save; 314: int indent; 315: 316: /* first we calculate the indent of the current line */ 317: DOTsave(&save); 318: ToIndent(); 319: indent = calc_pos(linebuf, curchar); 320: SetDot(&save); 321: 322: #ifdef ABBREV 323: MaybeAbbrevExpand(); 324: #endif 325: #ifdef LISP 326: if (MajorMode(LISPMODE)) 327: DelWtSpace(); 328: else 329: #endif 330: if (indentp || blnkp(linebuf)) 331: DelWtSpace(); 332: 333: /* If there is more than 2 blank lines in a row then don't make 334: a newline, just move down one. */ 335: if (arg_value() == 1 && eolp() && TwoBlank()) 336: SetLine(curline->l_next); 337: else 338: LineInsert(arg_value()); 339: 340: if (indentp) 341: #ifdef LISP 342: if (MajorMode(LISPMODE)) 343: (void) lisp_indent(); 344: else 345: #endif 346: n_indent((LMargin == 0) ? indent : LMargin); 347: } 348: 349: void 350: ins_str(str, ok_nl) 351: register char *str; 352: { 353: register char c; 354: Bufpos save; 355: int llen; 356: 357: if (*str == 0) 358: return; /* ain't nothing to insert! */ 359: DOTsave(&save); 360: llen = strlen(linebuf); 361: while (c = *str++) { 362: if (c == '\n' || (ok_nl && llen >= LBSIZE - 2)) { 363: IFixMarks(save.p_line, save.p_char, curline, curchar); 364: modify(); 365: makedirty(curline); 366: LineInsert(1); 367: DOTsave(&save); 368: llen = strlen(linebuf); 369: } 370: if (c != '\n') { 371: ins_c(c, linebuf, curchar++, 1, LBSIZE); 372: llen += 1; 373: } 374: } 375: IFixMarks(save.p_line, save.p_char, curline, curchar); 376: modify(); 377: makedirty(curline); 378: } 379: 380: void 381: open_lines(n) 382: { 383: Bufpos dot; 384: 385: DOTsave(&dot); 386: LineInsert(n); /* Open the lines... */ 387: SetDot(&dot); 388: } 389: 390: void 391: OpenLine() 392: { 393: open_lines(arg_value()); 394: } 395: 396: /* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at 397: ATLINE/ATCHAR in WHATBUF. */ 398: 399: Bufpos * 400: DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf) 401: Line *fline, 402: *tline, 403: *atline; 404: Buffer *whatbuf; 405: { 406: register Line *newline; 407: static Bufpos bp; 408: char save[LBSIZE], 409: buf[LBSIZE]; 410: Line *startline = atline; 411: int startchar = atchar; 412: 413: lsave(); 414: if (whatbuf) 415: modify(); 416: (void) ltobuf(atline, genbuf); 417: strcpy(save, &genbuf[atchar]); 418: 419: (void) ltobuf(fline, buf); 420: if (fline == tline) 421: buf[tchar] = '\0'; 422: 423: linecopy(genbuf, atchar, &buf[fchar]); 424: atline->l_dline = putline(genbuf); 425: makedirty(atline); 426: 427: fline = fline->l_next; 428: while (fline != tline->l_next) { 429: newline = listput(whatbuf, atline); 430: newline->l_dline = fline->l_dline; 431: makedirty(newline); 432: fline = fline->l_next; 433: atline = newline; 434: atchar = 0; 435: } 436: 437: getline(atline->l_dline, genbuf); 438: atchar += tchar; 439: linecopy(genbuf, atchar, save); 440: atline->l_dline = putline(genbuf); 441: makedirty(atline); 442: IFixMarks(startline, startchar, atline, atchar); 443: bp.p_line = atline; 444: bp.p_char = atchar; 445: this_cmd = YANKCMD; 446: getDOT(); /* Whatever used to be in linebuf */ 447: return &bp; 448: } 449: 450: void 451: YankPop() 452: { 453: Line *line, 454: *last; 455: Mark *mp = CurMark(); 456: Bufpos *dot; 457: int dir = -1; /* Direction to rotate the ring */ 458: 459: if (last_cmd != YANKCMD) 460: complain("Yank something first!"); 461: 462: lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar)); 463: 464: /* Now must find a recently killed region. */ 465: 466: if (arg_value() < 0) 467: dir = 1; 468: 469: killptr += dir; 470: for (;;) { 471: if (killptr < 0) 472: killptr = NUMKILLS - 1; 473: else if (killptr >= NUMKILLS) 474: killptr = 0; 475: if (killbuf[killptr]) 476: break; 477: killptr += dir; 478: } 479: 480: this_cmd = YANKCMD; 481: 482: line = killbuf[killptr]; 483: last = lastline(line); 484: dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf); 485: MarkSet(CurMark(), curline, curchar); 486: SetDot(dot); 487: } 488: 489: /* This is an attempt to reduce the amount of memory taken up by each line. 490: Without this each malloc of a line uses sizeof (line) + sizeof(HEADER) 491: where line is 3 words and HEADER is 1 word. 492: This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line) 493: and divide each chuck into lineS. A line is free in a chunk when its 494: line->l_dline == 0, so freeline sets dline to 0. */ 495: 496: #define CHUNKSIZE 300 497: 498: struct chunk { 499: int c_nlines; /* Number of lines in this chunk (so they 500: don't all have to be CHUNKSIZE long). */ 501: Line *c_block; /* Chunk of memory */ 502: struct chunk *c_nextfree; /* Next chunk of lines */ 503: }; 504: 505: private struct chunk *fchunk = 0; 506: private Line *ffline = 0; /* First free line */ 507: 508: void 509: freeline(line) 510: register Line *line; 511: { 512: line->l_dline = 0; 513: line->l_next = ffline; 514: if (ffline) 515: ffline->l_prev = line; 516: line->l_prev = 0; 517: ffline = line; 518: } 519: 520: void 521: lfreelist(first) 522: register Line *first; 523: { 524: if (first) 525: lfreereg(first, lastline(first)); 526: } 527: 528: /* Append region from line1 to line2 onto the free list of lines */ 529: 530: void 531: lfreereg(line1, line2) 532: register Line *line1, 533: *line2; 534: { 535: register Line *next, 536: *last = line2->l_next; 537: 538: while (line1 != last) { 539: next = line1->l_next; 540: freeline(line1); 541: line1 = next; 542: } 543: } 544: 545: private int 546: newchunk() 547: { 548: register Line *newline; 549: register int i; 550: struct chunk *f; 551: int nlines = CHUNKSIZE; 552: 553: f = (struct chunk *) emalloc(sizeof (struct chunk)); 554: if (f == 0) 555: return 0; 556: 557: if ((f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines))) == 0) { 558: while (nlines > 0) { 559: f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines)); 560: if (f->c_block != 0) 561: break; 562: nlines /= 2; 563: } 564: } 565: 566: if (nlines <= 0) 567: return 0; 568: 569: f->c_nlines = nlines; 570: for (i = 0, newline = f->c_block; i < nlines; newline++, i++) 571: freeline(newline); 572: f->c_nextfree = fchunk; 573: fchunk = f; 574: return 1; 575: } 576: 577: /* New BUFfer LINE */ 578: 579: Line * 580: nbufline() 581: { 582: register Line *newline; 583: 584: if (ffline == 0) /* No free list */ 585: if (newchunk() == 0) 586: complain("[Out of lines] "); 587: newline = ffline; 588: ffline = ffline->l_next; 589: if (ffline) 590: ffline->l_prev = 0; 591: return newline; 592: } 593: 594: /* Remove the free lines, in chunk c, from the free list because they are 595: no longer free. */ 596: 597: private void 598: remfreelines(c) 599: register struct chunk *c; 600: { 601: register Line *lp; 602: register int i; 603: 604: for (lp = c->c_block, i = 0; i < c->c_nlines; i++, lp++) { 605: if (lp->l_prev) 606: lp->l_prev->l_next = lp->l_next; 607: else 608: ffline = lp->l_next; 609: if (lp->l_next) 610: lp->l_next->l_prev = lp->l_prev; 611: } 612: } 613: 614: /* This is used to garbage collect the chunks of lines when malloc fails 615: and we are NOT looking for a new buffer line. This goes through each 616: chunk, and if every line in a given chunk is not allocated, the entire 617: chunk is `free'd by "free()". */ 618: 619: void 620: GCchunks() 621: { 622: register struct chunk *cp; 623: struct chunk *prev = 0, 624: *next = 0; 625: register int i; 626: register Line *newline; 627: 628: for (cp = fchunk; cp != 0; cp = next) { 629: for (i = 0, newline = cp->c_block; i < cp->c_nlines; newline++, i++) 630: if (newline->l_dline != 0) 631: break; 632: 633: next = cp->c_nextfree; 634: 635: if (i == cp->c_nlines) { /* Unlink it!!! */ 636: if (prev) 637: prev->c_nextfree = cp->c_nextfree; 638: else 639: fchunk = cp->c_nextfree; 640: remfreelines(cp); 641: free((char *) cp->c_block); 642: free((char *) cp); 643: } else 644: prev = cp; 645: } 646: } 647: 648: #ifdef LISP 649: 650: /* Grind S-Expr */ 651: 652: void 653: GSexpr() 654: { 655: Bufpos dot, 656: end; 657: 658: if (linebuf[curchar] != '(') 659: complain((char *) 0); 660: DOTsave(&dot); 661: FSexpr(); 662: DOTsave(&end); 663: SetDot(&dot); 664: for (;;) { 665: if (curline == end.p_line) 666: break; 667: line_move(FORWARD, 1, NO); 668: if (!blnkp(linebuf)) 669: (void) lisp_indent(); 670: } 671: SetDot(&dot); 672: } 673: 674: /* lisp_indent() indents a new line in Lisp Mode, according to where 675: the matching close-paren would go if we typed that (sort of). */ 676: 677: private Table *specials = NIL; 678: 679: private void 680: init_specials() 681: { 682: static char *words[] = { 683: "case", 684: "def", 685: "dolist", 686: "fluid-let", 687: "lambda", 688: "let", 689: "lexpr", 690: "macro", 691: "named-l", /* named-let and named-lambda */ 692: "nlambda", 693: "prog", 694: "selectq", 695: 0 696: }; 697: char **wordp = words; 698: 699: specials = make_table(); 700: while (*wordp) 701: add_word(*wordp++, specials); 702: } 703: 704: void 705: AddSpecial() 706: { 707: char *word; 708: 709: word = ask((char *) 0, ProcFmt); 710: if (specials == NIL) 711: init_specials(); 712: add_word(copystr(word), specials); 713: } 714: 715: Bufpos * 716: lisp_indent() 717: { 718: Bufpos *bp, 719: savedot; 720: int goal; 721: 722: bp = m_paren(')', BACKWARD, NO, YES); 723: 724: if (bp == 0) 725: return 0; 726: 727: /* We want to end up 728: 729: (atom atom atom ... 730: ^ here. 731: */ 732: 733: DOTsave(&savedot); 734: SetDot(bp); 735: f_char(1); 736: if (linebuf[curchar] != '(') { 737: register Word *wp; 738: 739: if (specials == NIL) 740: init_specials(); 741: for (wp = table_top(specials); wp != NIL; wp = next_word(wp)) 742: if (casencmp(word_text(wp), &linebuf[curchar], word_length(wp)) == 0) 743: break; 744: if (wp == NIL) { /* not special */ 745: int c_char = curchar; 746: 747: WITH_TABLE(curbuf->b_major) 748: f_word(1); 749: END_TABLE(); 750: if (LookingAt("[ \t]*;\\|[ \t]*$", linebuf, curchar)) 751: curchar = c_char; 752: else while (linebuf[curchar] == ' ') 753: curchar += 1; 754: } else 755: curchar += 1; 756: } 757: goal = calc_pos(linebuf, curchar); 758: SetDot(&savedot); 759: n_indent(goal); 760: 761: return bp; 762: } 763: #endif /* LISP */