|
|
1.1 ! root 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: ! 10: #ifdef MAC ! 11: # undef private ! 12: # define private ! 13: #endif ! 14: ! 15: #ifdef LINT_ARGS ! 16: private int get_indent(Line *); ! 17: private Line * tailrule(Line *); ! 18: #else ! 19: private int get_indent(); ! 20: private Line * tailrule(); ! 21: #endif ! 22: ! 23: #ifdef MAC ! 24: # undef private ! 25: # define private static ! 26: #endif ! 27: ! 28: /* Thanks to Brian Harvey for this paragraph boundery finding algorithm. ! 29: It's really quite hairy figuring it out. This deals with paragraphs that ! 30: are seperated by blank lines, lines beginning with a Period (assumed to ! 31: be an nroff command), lines beginning with BackSlash (assumed to be Tex ! 32: commands). Also handles paragraphs that are separated by lines of ! 33: different indent; and it deals with outdented paragraphs, too. It's ! 34: really quite nice. Here's Brian's algorithm. ! 35: ! 36: Definitions: ! 37: ! 38: THIS means the line containing the cursor. ! 39: PREV means the line above THIS. ! 40: NEXT means the line below THIS. ! 41: ! 42: BLANK means empty, empty except for spaces and tabs, starts with a period ! 43: or a backslash, or nonexistent (because the edge of the buffer is ! 44: reached). ((BH 12/24/85 A line starting with backslash is blank only if ! 45: the following line also starts with backslash. This is so that \noindent ! 46: is part of a paragraph, but long strings of TeX commands don't get ! 47: rearranged. It still isn't perfect but it's better.)) ! 48: ! 49: BSBLANK means BLANK or starts with a backslash. (BH 12/24/85) ! 50: ! 51: HEAD means the first (nonblank) line of the paragraph containing THIS. ! 52: BODY means all other (nonblank) lines of the paragraph. ! 53: TAIL means the last (nb) line of the paragraph. (TAIL is part of BODY.) ! 54: ! 55: HEAD INDENT means the indentation of HEAD. M-J should preserve this. ! 56: BODY INDENT means the indentation of BODY. Ditto. ! 57: ! 58: Subprocedures: ! 59: ! 60: TAILRULE(BODYLINE) ! 61: If BODYLINE is BLANK, the paragraph has only one line, and there is no ! 62: BODY and therefore no TAIL. Return. Otherwise, starting from BODYLINE, ! 63: move down until you find a line that either is BSBLANK or has a different ! 64: indentation from BODYLINE. The line above that different line is TAIL. ! 65: Return. ! 66: ! 67: Rules: ! 68: ! 69: 1. If THIS is BLANK, which command are you doing? If M-J or M-[, then go ! 70: up to the first non-BLANK line and start over. (If there is no non-BLANK ! 71: line before THIS, ring the bell.) If M-], then the first non-BLANK line ! 72: below THIS is HEAD, and the second consecutive non-BSBLANK line (if any) is ! 73: the beginning of BODY. (If there is no non-BLANK line after THIS, ring ! 74: the bell.) Do TAILRULE(beginning-of-BODY). Go to rule A. ! 75: ! 76: 2. If PREV is BLANK or THIS is BSBLANK, then THIS is HEAD, and NEXT (if ! 77: not BSBLANK) is in BODY. Do TAILRULE(NEXT). Go to rule A. ! 78: ! 79: 3. If NEXT is BSBLANK, then THIS is TAIL, therefore part of BODY. Go to ! 80: rule 5 to find HEAD. ! 81: ! 82: 4. If either NEXT or PREV has the same indentation as THIS, then THIS is ! 83: part of BODY. Do TAILRULE(THIS). Go to rule 5 to find HEAD. Otherwise, ! 84: go to rule 6. ! 85: ! 86: 5. Go up until you find a line that is either BSBLANK or has a different ! 87: indentation from THIS. If that line is BLANK, the line below it is HEAD. ! 88: If that line is non-BLANK, then call that new line THIS for what follows. ! 89: If (the new) PREV has the same indent as THIS, then (the new) NEXT is ! 90: HEAD. If PREV has a different indent from THIS, then THIS is HEAD. Go to ! 91: rule A. ! 92: ! 93: 6. If you got here, then both NEXT and PREV are nonblank and are ! 94: differently indented from THIS. This is a tricky case and there is no ! 95: guarantee that you're going to win. The most straightforward thing to do ! 96: is assume that we are not using hanging indentation. In that case: ! 97: whichever of PREV and THIS is indented further is HEAD. Do ! 98: TAILRULE(HEAD+1). Go to rule A. ! 99: ! 100: 6+. A more complicated variant would be this: if THIS is indented further ! 101: than PREV, we are using regular indentation and rule 6 applies. If PREV ! 102: is indented further than THIS, look at both NEXT and the line after NEXT. ! 103: If those two lines are indented equally, and more than THIS, then we are ! 104: using hanging indent, THIS is HEAD, and NEXT is the first line of BODY. ! 105: Do TAILRULE(NEXT). Otherwise, rule 6 applies. ! 106: ! 107: A. You now know where HEAD and TAIL are. The indentation of HEAD is HEAD ! 108: INDENT; the indentation of TAIL is BODY INDENT. ! 109: ! 110: B. If you are trying to M-J, you are now ready to do it. ! 111: ! 112: C. If you are trying to M-], leave point after the newline that ends ! 113: TAIL. In other words, leave the cursor at the beginning of the line ! 114: after TAIL. It is not possible for this to leave point where it started ! 115: unless it was already at the end of the buffer. ! 116: ! 117: D. If you are trying to M-[, if the line before HEAD is not BLANK, then ! 118: leave point just before HEAD. That is, leave the cursor at the beginning ! 119: of HEAD. If the line before HEAD is BLANK, then leave the cursor at the ! 120: beginning of that line. If the cursor didn't move, go up to the first ! 121: earlier non-BLANK line and start over. ! 122: ! 123: ! 124: End of Algorithm. I implemented rule 6+ because it seemed nicer. */ ! 125: ! 126: int RMargin = 78, ! 127: LMargin = 0; ! 128: Line *para_head, ! 129: *para_tail; ! 130: int head_indent, ! 131: body_indent; ! 132: static int use_lmargin; ! 133: ! 134: /* some defines for paragraph boundery checking */ ! 135: #define I_EMPTY -1 /* line "looks" empty (spaces and tabs) */ ! 136: #define I_PERIOD -2 /* line begins with "." or "\" */ ! 137: #define I_BUFEDGE -3 /* line is nonexistent (edge of buffer) */ ! 138: ! 139: static int bslash; /* Nonzero if get_indent finds line starting ! 140: with backslash */ ! 141: ! 142: int ! 143: i_bsblank(lp) ! 144: Line *lp; ! 145: { ! 146: if (i_blank(lp)) ! 147: return 1; ! 148: return bslash; ! 149: } ! 150: ! 151: int ! 152: i_blank(lp) ! 153: Line *lp; ! 154: { ! 155: return (get_indent(lp) < 0); ! 156: } ! 157: ! 158: private int ! 159: get_indent(lp) ! 160: register Line *lp; ! 161: { ! 162: Bufpos save; ! 163: register int indent; ! 164: ! 165: bslash = 0; ! 166: if (lp == 0) ! 167: return I_BUFEDGE; ! 168: DOTsave(&save); ! 169: SetLine(lp); ! 170: if (blnkp(linebuf)) ! 171: indent = I_EMPTY; ! 172: else if (linebuf[0] == '.') ! 173: indent = I_PERIOD; ! 174: else if (linebuf[0] == '\\') { ! 175: /* BH 12/24/85. Backslash is BLANK only if next line ! 176: also starts with Backslash. */ ! 177: bslash += 1; ! 178: SetLine(lp->l_next); ! 179: if (linebuf[0] == '\\') ! 180: indent = I_PERIOD; ! 181: else ! 182: indent = 0; ! 183: } else { ! 184: ToIndent(); ! 185: indent = calc_pos(linebuf, curchar); ! 186: } ! 187: SetDot(&save); ! 188: ! 189: return indent; ! 190: } ! 191: ! 192: private Line * ! 193: tailrule(lp) ! 194: register Line *lp; ! 195: { ! 196: int i; ! 197: ! 198: i = get_indent(lp); ! 199: if (i < 0) ! 200: return lp; /* one line paragraph */ ! 201: do { ! 202: if ((get_indent(lp->l_next) != i) || bslash) ! 203: /* BH line with backslash is head of next para */ ! 204: break; ! 205: } while ((lp = lp->l_next) != 0); ! 206: if (lp == 0) ! 207: complain((char *) 0); ! 208: return lp; ! 209: } ! 210: ! 211: /* Finds the beginning, end and indent of the current paragraph, and sets ! 212: the above global variables. HOW says how to behave when we're between ! 213: paragraphs. That is, it's either FORWARD or BACKWARD depending on which ! 214: way we're favoring. */ ! 215: ! 216: void ! 217: find_para(how) ! 218: { ! 219: Line *this, ! 220: *prev, ! 221: *next, ! 222: *head = 0, ! 223: *body = 0, ! 224: *tail = 0; ! 225: int this_indent; ! 226: Bufpos orig; /* remember where we were when we started */ ! 227: ! 228: DOTsave(&orig); ! 229: strt: ! 230: this = curline; ! 231: prev = curline->l_prev; ! 232: next = curline->l_next; ! 233: this_indent = get_indent(this); ! 234: ! 235: if (i_blank(this)) { /* rule 1 */ ! 236: if (how == BACKWARD) { ! 237: while (i_blank(curline)) ! 238: if (firstp(curline)) ! 239: complain((char *) 0); ! 240: else ! 241: line_move(BACKWARD, 1, NO); ! 242: goto strt; ! 243: } else { ! 244: while (i_blank(curline)) ! 245: if (lastp(curline)) ! 246: complain((char *) 0); ! 247: else ! 248: line_move(FORWARD, 1, NO); ! 249: head = curline; ! 250: next = curline->l_next; ! 251: if (!i_bsblank(next)) ! 252: body = next; ! 253: else ! 254: body = head; ! 255: } ! 256: } else if (i_bsblank(this) || i_blank(prev)) { /* rule 2 */ ! 257: head = this; ! 258: if (!i_bsblank(next)) ! 259: body = next; ! 260: } else if (i_bsblank(next)) { /* rule 3 */ ! 261: tail = this; ! 262: body = this; ! 263: } else if ((get_indent(next) == this_indent) || /* rule 4 */ ! 264: (get_indent(prev) == this_indent)) ! 265: body = this; ! 266: else { /* rule 6+ */ ! 267: if (get_indent(prev) > this_indent) { ! 268: /* hanging indent maybe? */ ! 269: if ((next != 0) && ! 270: (get_indent(next) == get_indent(next->l_next))) { ! 271: head = this; ! 272: body = next; ! 273: } ! 274: } ! 275: /* Now we handle hanging indent else and the other ! 276: case of this_indent > get_indent(prev). That is, ! 277: if we didn't resolve HEAD in the above if, then ! 278: we are not a hanging indent. */ ! 279: if (head == 0) { /* still don't know */ ! 280: if (this_indent > get_indent(prev)) ! 281: head = this; ! 282: else ! 283: head = prev; ! 284: body = head->l_next; ! 285: } ! 286: } ! 287: /* rule 5 -- find the missing parts */ ! 288: if (head == 0) { /* haven't found head of paragraph so do so now */ ! 289: Line *lp; ! 290: int i; ! 291: ! 292: lp = this; ! 293: do { ! 294: i = get_indent(lp->l_prev); ! 295: if (i < 0) /* is blank */ ! 296: head = lp; ! 297: else if (i != this_indent || bslash) { ! 298: Line *this = lp->l_prev; ! 299: ! 300: if (get_indent(this->l_prev) == i) ! 301: head = this->l_next; ! 302: else ! 303: head = this; ! 304: } ! 305: } while (head == 0 && (lp = lp->l_prev) != 0); ! 306: if (lp == 0) ! 307: complain((char *) 0); ! 308: } ! 309: if (body == 0) /* this must be a one line paragraph */ ! 310: body = head; ! 311: if (tail == 0) ! 312: tail = tailrule(body); ! 313: if (tail == 0 || head == 0 || body == 0) ! 314: complain("BUG! tail(%d),head(%d),body(%d)!", tail, head, body); ! 315: para_head = head; ! 316: para_tail = tail; ! 317: head_indent = get_indent(head); ! 318: body_indent = get_indent(body); ! 319: ! 320: SetDot(&orig); ! 321: } ! 322: ! 323: void ! 324: Justify() ! 325: { ! 326: use_lmargin = is_an_arg(); ! 327: find_para(BACKWARD); ! 328: DoJustify(para_head, 0, para_tail, length(para_tail), NO, ! 329: use_lmargin ? LMargin : body_indent); ! 330: } ! 331: ! 332: Line * ! 333: max_line(l1, l2) ! 334: Line *l1, ! 335: *l2; ! 336: { ! 337: if (inorder(l1, 0, l2, 0)) ! 338: return l2; ! 339: return l1; ! 340: } ! 341: ! 342: Line * ! 343: min_line(l1, l2) ! 344: Line *l1, ! 345: *l2; ! 346: { ! 347: if (inorder(l1, 0, l2, 0)) ! 348: return l1; ! 349: return l2; ! 350: } ! 351: ! 352: void ! 353: RegJustify() ! 354: { ! 355: Mark *mp = CurMark(), ! 356: *tailmark; ! 357: Line *l1 = curline, ! 358: *l2 = mp->m_line; ! 359: int c1 = curchar, ! 360: c2 = mp->m_char; ! 361: Line *rl1, ! 362: *rl2; ! 363: ! 364: use_lmargin = is_an_arg(); ! 365: (void) fixorder(&l1, &c1, &l2, &c2); ! 366: do { ! 367: DotTo(l1, c1); ! 368: find_para(FORWARD); ! 369: rl1 = max_line(l1, para_head); ! 370: rl2 = min_line(l2, para_tail); ! 371: tailmark = MakeMark(para_tail, 0, M_FLOATER); ! 372: DoJustify(rl1, (rl1 == l1) ? c1 : 0, rl2, ! 373: (rl2 == l2) ? c2 : length(rl2), ! 374: NO, use_lmargin ? LMargin : body_indent); ! 375: l1 = tailmark->m_line->l_next; ! 376: DelMark(tailmark); ! 377: c1 = 0; ! 378: } while (l1 != 0 && l2 != rl2); ! 379: } ! 380: ! 381: void ! 382: do_rfill(ulm) ! 383: { ! 384: Mark *mp = CurMark(); ! 385: Line *l1 = curline, ! 386: *l2 = mp->m_line; ! 387: int c1 = curchar, ! 388: c2 = mp->m_char; ! 389: ! 390: use_lmargin = ulm; ! 391: (void) fixorder(&l1, &c1, &l2, &c2); ! 392: DoJustify(l1, c1, l2, c2, NO, use_lmargin ? LMargin : 0); ! 393: } ! 394: ! 395: void ! 396: do_space() ! 397: { ! 398: int c1 = curchar, ! 399: c2 = c1, ! 400: diff, ! 401: nspace; ! 402: char ch; ! 403: ! 404: while (c1 > 0 && ((ch = linebuf[c1 - 1]) == ' ' || ch == '\t')) ! 405: c1 -= 1; ! 406: while ((ch = linebuf[c2]) == ' ' || ch == '\t') ! 407: c2 += 1; ! 408: diff = (c2 - c1); ! 409: curchar = c2; ! 410: ! 411: if (diff == 0) ! 412: return; ! 413: if (c1 > 0) { ! 414: int topunct = c1 - 1; ! 415: ! 416: nspace = 1; ! 417: if (diff >= 2) { ! 418: while (index("\")]", linebuf[topunct])) { ! 419: if (topunct == 0) ! 420: break; ! 421: topunct -= 1; ! 422: } ! 423: if (index("?!.:", linebuf[topunct])) ! 424: nspace = 2; ! 425: } ! 426: } else ! 427: nspace = 0; ! 428: ! 429: if (diff > nspace) ! 430: del_char(BACKWARD, (diff - nspace)); ! 431: else if (diff < nspace) ! 432: insert_c(' ', (nspace - diff)); ! 433: } ! 434: ! 435: #ifdef MSDOS ! 436: /*#pragma loop_opt(off) */ ! 437: #endif ! 438: ! 439: void ! 440: DoJustify(l1, c1, l2, c2, scrunch, indent) ! 441: Line *l1, ! 442: *l2; ! 443: { ! 444: int okay_char = -1; ! 445: char *cp; ! 446: Mark *savedot = MakeMark(curline, curchar, M_FLOATER), ! 447: *endmark; ! 448: ! 449: (void) fixorder(&l1, &c1, &l2, &c2); /* l1/c1 will be before l2/c2 */ ! 450: DotTo(l1, c1); ! 451: if (get_indent(l1) >= c1) { ! 452: if (use_lmargin) { ! 453: n_indent(indent + (head_indent - body_indent)); ! 454: use_lmargin = 0; /* turn this off now */ ! 455: } ! 456: ToIndent(); ! 457: } ! 458: endmark = MakeMark(l2, c2, M_FLOATER); ! 459: ! 460: for (;;) { ! 461: while (calc_pos(linebuf, curchar) < RMargin) { ! 462: if (curline == endmark->m_line && curchar >= endmark->m_char) ! 463: goto outahere; ! 464: okay_char = curchar; ! 465: if (eolp()) { ! 466: del_char(FORWARD, 1); /* Delete line separator. */ ! 467: ins_str(" ", NO); ! 468: } else { ! 469: cp = StrIndex(1, linebuf, curchar + 1, ' '); ! 470: if (cp == 0) ! 471: Eol(); ! 472: else ! 473: curchar = (cp - linebuf); ! 474: } ! 475: do_space(); ! 476: } ! 477: if (okay_char > 0) ! 478: curchar = okay_char; ! 479: if (curline == endmark->m_line && curchar >= endmark->m_char) ! 480: goto outahere; ! 481: ! 482: /* Can't fit in small margin, so we do the best we can. */ ! 483: if (eolp()) { ! 484: line_move(FORWARD, 1, NO); ! 485: n_indent(indent); ! 486: } else { ! 487: DelWtSpace(); ! 488: LineInsert(1); ! 489: if (scrunch && TwoBlank()) { ! 490: Eol(); ! 491: del_char(FORWARD, 1); ! 492: } ! 493: n_indent(indent); ! 494: } ! 495: } ! 496: outahere: ! 497: ToMark(savedot); /* Back to where we were */ ! 498: DelMark(endmark); /* Free up marks */ ! 499: DelMark(savedot); ! 500: this_cmd = last_cmd = 0; /* So everything is under control */ ! 501: f_mess(""); ! 502: } ! 503: ! 504: #ifdef MSDOS ! 505: /*#pragma loop_opt() */ ! 506: #endif ! 507: ! 508: extern Line *para_head, ! 509: *para_tail; ! 510: ! 511: void ! 512: DoPara(dir) ! 513: { ! 514: register int num = arg_value(), ! 515: first_time = TRUE; ! 516: ! 517: while (--num >= 0) { ! 518: tryagain: find_para(dir); /* find paragraph bounderies */ ! 519: if ((dir == BACKWARD) && ! 520: ((!first_time) || ((para_head == curline) && bolp()))) { ! 521: if (bobp()) ! 522: complain((char *) 0); ! 523: b_char(1); ! 524: first_time = !first_time; ! 525: goto tryagain; ! 526: } ! 527: SetLine((dir == BACKWARD) ? para_head : para_tail); ! 528: if (dir == BACKWARD && !firstp(curline) && ! 529: i_blank(curline->l_prev)) ! 530: line_move(BACKWARD, 1, NO); ! 531: else if (dir == FORWARD) { ! 532: if (lastp(curline)) { ! 533: Eol(); ! 534: break; ! 535: } ! 536: /* otherwise */ ! 537: line_move(FORWARD, 1, NO); ! 538: } ! 539: } ! 540: } ! 541: ! 542: void ! 543: BackPara() ! 544: { ! 545: DoPara(BACKWARD); ! 546: } ! 547: ! 548: void ! 549: ForPara() ! 550: { ! 551: DoPara(FORWARD); ! 552: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.