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: }

Defined functions

BackPara defined in line 542; used 4 times
DoJustify defined in line 439; used 6 times
DoPara defined in line 511; used 4 times
ForPara defined in line 548; used 4 times
Justify defined in line 323; used 4 times
RegJustify defined in line 352; used 4 times
do_rfill defined in line 381; used 3 times
do_space defined in line 395; used 3 times
find_para defined in line 216; used 5 times
get_indent defined in line 158; used 17 times
i_blank defined in line 151; used 8 times
i_bsblank defined in line 142; used 6 times
max_line defined in line 332; used 3 times
min_line defined in line 342; used 3 times
tailrule defined in line 192; used 3 times

Defined variables

RMargin defined in line 126; used 4 times
body_indent defined in line 131; used 4 times
bslash defined in line 139; used 5 times
head_indent defined in line 130; used 2 times
para_head declared in line 508; defined in line 128; used 5 times
para_tail declared in line 509; defined in line 129; used 6 times
use_lmargin defined in line 132; used 8 times

Defined macros

I_BUFEDGE defined in line 137; used 1 times
I_EMPTY defined in line 135; used 1 times
I_PERIOD defined in line 136; used 2 times
private defined in line 25; used 8 times
Last modified: 1988-03-15
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3733
Valid CSS Valid XHTML 1.0 Strict