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