1: /*
   2:  * Primitives for displaying the file on the screen.
   3:  */
   4: 
   5: #include "less.h"
   6: #include "position.h"
   7: 
   8: public int hit_eof; /* Keeps track of how many times we hit end of file */
   9: 
  10: extern int quiet;
  11: extern int top_search;
  12: extern int top_scroll;
  13: extern int back_scroll;
  14: extern int sc_width, sc_height;
  15: extern int sigs;
  16: extern char *line;
  17: extern char *first_cmd;
  18: 
  19: /*
  20:  * Sound the bell to indicate he is trying to move past end of file.
  21:  */
  22:     static void
  23: eof_bell()
  24: {
  25:     if (quiet == NOT_QUIET)
  26:         bell();
  27:     else
  28:         vbell();
  29: }
  30: 
  31: /*
  32:  * Check to see if the end of file is currently "displayed".
  33:  */
  34:     static void
  35: eof_check()
  36: {
  37:     POSITION pos;
  38: 
  39:     /*
  40: 	 * If the bottom line is empty, we are at EOF.
  41: 	 * If the bottom line ends at the file length,
  42: 	 * we must be just at EOF.
  43: 	 */
  44:     pos = position(BOTTOM_PLUS_ONE);
  45:     if (pos == NULL_POSITION || pos == ch_length())
  46:         hit_eof++;
  47: }
  48: 
  49: /*
  50:  * Display n lines, scrolling forward,
  51:  * starting at position pos in the input file.
  52:  * "force" means display the n lines even if we hit end of file.
  53:  * "only_last" means display only the last screenful if n > screen size.
  54:  */
  55:     static void
  56: forw(n, pos, force, only_last)
  57:     register int n;
  58:     POSITION pos;
  59:     int force;
  60:     int only_last;
  61: {
  62:     int eof = 0;
  63:     int nlines = 0;
  64:     int repaint_flag;
  65:     static int first_time = 1;
  66: 
  67:     /*
  68: 	 * repaint_flag tells us not to display anything till the end,
  69: 	 * then just repaint the entire screen.
  70: 	 */
  71:     repaint_flag = (only_last && n > sc_height-1);
  72: 
  73:     if (!repaint_flag)
  74:     {
  75:         if (top_scroll && n >= sc_height - 1)
  76:         {
  77:             /*
  78: 			 * Start a new screen.
  79: 			 * {{ This is not really desirable if we happen
  80: 			 *    to hit eof in the middle of this screen,
  81: 			 *    but we don't yet know if that will happen. }}
  82: 			 */
  83:             clear();
  84:             home();
  85:             force = 1;
  86:         } else
  87:         {
  88:             lower_left();
  89:             clear_eol();
  90:         }
  91: 
  92:         if (pos != position(BOTTOM_PLUS_ONE))
  93:         {
  94:             /*
  95: 			 * This is not contiguous with what is
  96: 			 * currently displayed.  Clear the screen image
  97: 			 * (position table) and start a new screen.
  98: 			 */
  99:             pos_clear();
 100:             add_forw_pos(pos);
 101:             force = 1;
 102:             if (top_scroll)
 103:             {
 104:                 clear();
 105:                 home();
 106:             } else if (!first_time)
 107:             {
 108:                 puts("...skipping...\n");
 109:             }
 110:         }
 111:     }
 112: 
 113:     while (--n >= 0)
 114:     {
 115:         /*
 116: 		 * Read the next line of input.
 117: 		 */
 118:         pos = forw_line(pos);
 119:         if (pos == NULL_POSITION)
 120:         {
 121:             /*
 122: 			 * End of file: stop here unless the top line
 123: 			 * is still empty, or "force" is true.
 124: 			 */
 125:             eof = 1;
 126:             if (!force && position(TOP) != NULL_POSITION)
 127:                 break;
 128:             line = NULL;
 129:         }
 130:         /*
 131: 		 * Add the position of the next line to the position table.
 132: 		 * Display the current line on the screen.
 133: 		 */
 134:         add_forw_pos(pos);
 135:         nlines++;
 136:         if (repaint_flag ||
 137:             (first_time && line == NULL && !top_scroll))
 138:             continue;
 139:         put_line();
 140:     }
 141: 
 142:     if (eof)
 143:         hit_eof++;
 144:     else
 145:         eof_check();
 146:     if (nlines == 0)
 147:         eof_bell();
 148:     else if (repaint_flag)
 149:         repaint();
 150:     first_time = 0;
 151: }
 152: 
 153: /*
 154:  * Display n lines, scrolling backward.
 155:  */
 156:     static void
 157: back(n, pos, force, only_last)
 158:     register int n;
 159:     POSITION pos;
 160:     int force;
 161:     int only_last;
 162: {
 163:     int nlines = 0;
 164:     int repaint_flag;
 165: 
 166:     repaint_flag = (n > get_back_scroll() || (only_last && n > sc_height-1));
 167:     hit_eof = 0;
 168:     while (--n >= 0)
 169:     {
 170:         /*
 171: 		 * Get the previous line of input.
 172: 		 */
 173:         pos = back_line(pos);
 174:         if (pos == NULL_POSITION)
 175:         {
 176:             /*
 177: 			 * Beginning of file: stop here unless "force" is true.
 178: 			 */
 179:             if (!force)
 180:                 break;
 181:             line = NULL;
 182:         }
 183:         /*
 184: 		 * Add the position of the previous line to the position table.
 185: 		 * Display the line on the screen.
 186: 		 */
 187:         add_back_pos(pos);
 188:         nlines++;
 189:         if (!repaint_flag)
 190:         {
 191:             home();
 192:             add_line();
 193:             put_line();
 194:         }
 195:     }
 196: 
 197:     eof_check();
 198:     if (nlines == 0)
 199:         eof_bell();
 200:     else if (repaint_flag)
 201:         repaint();
 202: }
 203: 
 204: /*
 205:  * Display n more lines, forward.
 206:  * Start just after the line currently displayed at the bottom of the screen.
 207:  */
 208:     public void
 209: forward(n, only_last)
 210:     int n;
 211:     int only_last;
 212: {
 213:     POSITION pos;
 214: 
 215:     pos = position(BOTTOM_PLUS_ONE);
 216:     if (pos == NULL_POSITION)
 217:     {
 218:         eof_bell();
 219:         hit_eof++;
 220:         return;
 221:     }
 222:     forw(n, pos, 0, only_last);
 223: }
 224: 
 225: /*
 226:  * Display n more lines, backward.
 227:  * Start just before the line currently displayed at the top of the screen.
 228:  */
 229:     public void
 230: backward(n, only_last)
 231:     int n;
 232:     int only_last;
 233: {
 234:     POSITION pos;
 235: 
 236:     pos = position(TOP);
 237:     if (pos == NULL_POSITION)
 238:     {
 239:         /*
 240: 		 * This will almost never happen,
 241: 		 * because the top line is almost never empty.
 242: 		 */
 243:         eof_bell();
 244:         return;
 245:     }
 246:     back(n, pos, 0, only_last);
 247: }
 248: 
 249: /*
 250:  * Repaint the screen, starting from a specified position.
 251:  */
 252:     static void
 253: prepaint(pos)
 254:     POSITION pos;
 255: {
 256:     hit_eof = 0;
 257:     forw(sc_height-1, pos, 0, 0);
 258: }
 259: 
 260: /*
 261:  * Repaint the screen.
 262:  */
 263:     public void
 264: repaint()
 265: {
 266:     /*
 267: 	 * Start at the line currently at the top of the screen
 268: 	 * and redisplay the screen.
 269: 	 */
 270:     prepaint(position(TOP));
 271: }
 272: 
 273: /*
 274:  * Jump to the end of the file.
 275:  * It is more convenient to paint the screen backward,
 276:  * from the end of the file toward the beginning.
 277:  */
 278:     public void
 279: jump_forw()
 280: {
 281:     POSITION pos;
 282: 
 283:     if (ch_end_seek())
 284:     {
 285:         error("Cannot seek to end of file");
 286:         return;
 287:     }
 288:     lastmark();
 289:     pos = ch_tell();
 290:     clear();
 291:     pos_clear();
 292:     add_back_pos(pos);
 293:     back(sc_height - 1, pos, 0, 0);
 294: }
 295: 
 296: /*
 297:  * Jump to line n in the file.
 298:  */
 299:     public void
 300: jump_back(n)
 301:     register int n;
 302: {
 303:     register int c;
 304:     int nlines;
 305: 
 306:     /*
 307: 	 * This is done the slow way, by starting at the beginning
 308: 	 * of the file and counting newlines.
 309: 	 */
 310:     if (ch_seek((POSITION)0))
 311:     {
 312:         /*
 313: 		 * Probably a pipe with beginning of file no longer buffered.
 314: 		 * If he wants to go to line 1, we do the best we can,
 315: 		 * by going to the first line which is still buffered.
 316: 		 */
 317:         if (n <= 1 && ch_beg_seek() == 0)
 318:             jump_loc(ch_tell());
 319:         error("Cannot get to beginning of file");
 320:         return;
 321:     }
 322: 
 323:     /*
 324: 	 * Start counting lines.
 325: 	 */
 326:     for (nlines = 1;  nlines < n;  nlines++)
 327:     {
 328:         while ((c = ch_forw_get()) != '\n')
 329:             if (c == EOF)
 330:             {
 331:                 char message[40];
 332:                 sprintf(message, "File has only %d lines",
 333:                     nlines-1);
 334:                 error(message);
 335:                 return;
 336:             }
 337:     }
 338: 
 339:     jump_loc(ch_tell());
 340: }
 341: 
 342: /*
 343:  * Jump to a specified percentage into the file.
 344:  * This is a poor compensation for not being able to
 345:  * quickly jump to a specific line number.
 346:  */
 347:     public void
 348: jump_percent(percent)
 349:     int percent;
 350: {
 351:     POSITION pos, len;
 352:     register int c;
 353: 
 354:     /*
 355: 	 * Determine the position in the file
 356: 	 * (the specified percentage of the file's length).
 357: 	 */
 358:     if ((len = ch_length()) == NULL_POSITION)
 359:     {
 360:         error("Don't know length of file");
 361:         return;
 362:     }
 363:     pos = (percent * len) / 100;
 364: 
 365:     /*
 366: 	 * Back up to the beginning of the line.
 367: 	 */
 368:     if (ch_seek(pos) == 0)
 369:     {
 370:         while ((c = ch_back_get()) != '\n' && c != EOF)
 371:             ;
 372:         if (c == '\n')
 373:             (void) ch_forw_get();
 374:         pos = ch_tell();
 375:     }
 376:     jump_loc(pos);
 377: }
 378: 
 379: /*
 380:  * Jump to a specified position in the file.
 381:  */
 382:     public void
 383: jump_loc(pos)
 384:     POSITION pos;
 385: {
 386:     register int nline;
 387:     POSITION tpos;
 388: 
 389:     /*
 390: 	 * See if the desired line is BEFORE the currently
 391: 	 * displayed screen.  If so, see if it is close enough
 392: 	 * to scroll backwards to it.
 393: 	 * {{ This can be expensive if he has specified a very
 394: 	 *    large back_scroll count.  Perhaps we should put
 395: 	 *    some sanity limit on the loop count here. }}
 396: 	 */
 397:     tpos = position(TOP);
 398:     if (tpos != NULL_POSITION && pos < tpos)
 399:     {
 400:         int bs = get_back_scroll();
 401:         for (nline = 1;  nline <= bs;  nline++)
 402:         {
 403:             tpos = back_line(tpos);
 404:             if (tpos == NULL_POSITION)
 405:                 break;
 406:             if (tpos <= pos)
 407:             {
 408:                 back(nline, position(TOP), 1, 0);
 409:                 return;
 410:             }
 411:         }
 412:     } else if ((nline = onscreen(pos)) >= 0)
 413:     {
 414:         /*
 415: 		 * The line is currently displayed.
 416: 		 * Just scroll there.
 417: 		 */
 418:         forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
 419:         return;
 420:     }
 421: 
 422:     /*
 423: 	 * Line is not on screen.
 424: 	 * Remember where we were; clear and paint the screen.
 425: 	 */
 426:     if (ch_seek(pos))
 427:     {
 428:         error("Cannot seek to that position");
 429:         return;
 430:     }
 431:     lastmark();
 432:     prepaint(pos);
 433: }
 434: 
 435: /*
 436:  * The table of marks.
 437:  * A mark is simply a position in the file.
 438:  */
 439: #define NMARKS      (27)        /* 26 for a-z plus one for quote */
 440: #define LASTMARK    (NMARKS-1)  /* For quote */
 441: static POSITION marks[NMARKS];
 442: 
 443: /*
 444:  * Initialize the mark table to show no marks are set.
 445:  */
 446:     public void
 447: init_mark()
 448: {
 449:     int i;
 450: 
 451:     for (i = 0;  i < NMARKS;  i++)
 452:         marks[i] = NULL_POSITION;
 453: }
 454: 
 455: /*
 456:  * See if a mark letter is valid (between a and z).
 457:  */
 458:     static int
 459: badmark(c)
 460:     int c;
 461: {
 462:     if (c < 'a' || c > 'z')
 463:     {
 464:         error("Choose a letter between 'a' and 'z'");
 465:         return (1);
 466:     }
 467:     return (0);
 468: }
 469: 
 470: /*
 471:  * Set a mark.
 472:  */
 473:     public void
 474: setmark(c)
 475:     int c;
 476: {
 477:     if (badmark(c))
 478:         return;
 479:     marks[c-'a'] = position(TOP);
 480: }
 481: 
 482:     public void
 483: lastmark()
 484: {
 485:     marks[LASTMARK] = position(TOP);
 486: }
 487: 
 488: /*
 489:  * Go to a previously set mark.
 490:  */
 491:     public void
 492: gomark(c)
 493:     int c;
 494: {
 495:     POSITION pos;
 496: 
 497:     if (c == '\'')
 498:         pos = marks[LASTMARK];
 499:     else if (badmark(c))
 500:         return;
 501:     else
 502:         pos = marks[c-'a'];
 503: 
 504:     if (pos == NULL_POSITION)
 505:         error("mark not set");
 506:     else
 507:         jump_loc(pos);
 508: }
 509: 
 510: /*
 511:  * Get the backwards scroll limit.
 512:  * Must call this function instead of just using the value of
 513:  * back_scroll, because the default case depends on sc_height and
 514:  * top_scroll, as well as back_scroll.
 515:  */
 516:     public int
 517: get_back_scroll()
 518: {
 519:     if (back_scroll < 0)
 520:         return (sc_height - 1 - top_scroll);
 521:     return (back_scroll);
 522: }
 523: 
 524: /*
 525:  * Search for the n-th occurence of a specified pattern,
 526:  * either forward (direction == '/'), or backwards (direction == '?').
 527:  */
 528:     public void
 529: search(direction, pattern, n)
 530:     int direction;
 531:     char *pattern;
 532:     register int n;
 533: {
 534:     register int search_forward = (direction == '/');
 535:     POSITION pos, linepos;
 536: 
 537: #if RECOMP
 538:     char *re_comp();
 539:     char *errmsg;
 540: 
 541:     /*
 542: 	 * (re_comp handles a null pattern internally,
 543: 	 *  so there is no need to check for a null pattern here.)
 544: 	 */
 545:     if ((errmsg = re_comp(pattern)) != NULL)
 546:     {
 547:         error(errmsg);
 548:         return;
 549:     }
 550: #else
 551: #if REGCMP
 552:     char *regcmp();
 553:     static char *cpattern = NULL;
 554: 
 555:     if (pattern == NULL || *pattern == '\0')
 556:     {
 557:         /*
 558: 		 * A null pattern means use the previous pattern.
 559: 		 * The compiled previous pattern is in cpattern, so just use it.
 560: 		 */
 561:         if (cpattern == NULL)
 562:         {
 563:             error("No previous regular expression");
 564:             return;
 565:         }
 566:     } else
 567:     {
 568:         /*
 569: 		 * Otherwise compile the given pattern.
 570: 		 */
 571:         char *s;
 572:         if ((s = regcmp(pattern, 0)) == NULL)
 573:         {
 574:             error("Invalid pattern");
 575:             return;
 576:         }
 577:         if (cpattern != NULL)
 578:             free(cpattern);
 579:         cpattern = s;
 580:     }
 581: #else
 582:     static char lpbuf[100];
 583:     static char *last_pattern = NULL;
 584: 
 585:     if (pattern == NULL || *pattern == '\0')
 586:     {
 587:         /*
 588: 		 * Null pattern means use the previous pattern.
 589: 		 */
 590:         if (last_pattern == NULL)
 591:         {
 592:             error("No previous regular expression");
 593:             return;
 594:         }
 595:         pattern = last_pattern;
 596:     } else
 597:     {
 598:         strcpy(lpbuf, pattern);
 599:         last_pattern = lpbuf;
 600:     }
 601: #endif
 602: #endif
 603: 
 604:     /*
 605: 	 * Figure out where to start the search.
 606: 	 */
 607: 
 608:     if (position(TOP) == NULL_POSITION)
 609:     {
 610:         /*
 611: 		 * Nothing is currently displayed.
 612: 		 * Start at the beginning of the file.
 613: 		 * (This case is mainly for first_cmd searches,
 614: 		 * for example, "+/xyz" on the command line.)
 615: 		 */
 616:         pos = (POSITION)0;
 617:     } else if (!search_forward)
 618:     {
 619:         /*
 620: 		 * Backward search: start just before the top line
 621: 		 * displayed on the screen.
 622: 		 */
 623:         pos = position(TOP);
 624:     } else if (top_search)
 625:     {
 626:         /*
 627: 		 * Forward search and "start from top".
 628: 		 * Start at the second line displayed on the screen.
 629: 		 */
 630:         pos = position(TOP_PLUS_ONE);
 631:     } else
 632:     {
 633:         /*
 634: 		 * Forward search but don't "start from top".
 635: 		 * Start just after the bottom line displayed on the screen.
 636: 		 */
 637:         pos = position(BOTTOM_PLUS_ONE);
 638:     }
 639: 
 640:     if (pos == NULL_POSITION)
 641:     {
 642:         /*
 643: 		 * Can't find anyplace to start searching from.
 644: 		 */
 645:         error("Nothing to search");
 646:         return;
 647:     }
 648: 
 649:     for (;;)
 650:     {
 651:         /*
 652: 		 * Get lines until we find a matching one or
 653: 		 * until we hit end-of-file (or beginning-of-file
 654: 		 * if we're going backwards).
 655: 		 */
 656:         if (sigs)
 657:             /*
 658: 			 * A signal aborts the search.
 659: 			 */
 660:             return;
 661: 
 662:         if (search_forward)
 663:         {
 664:             /*
 665: 			 * Read the next line, and save the
 666: 			 * starting position of that line in linepos.
 667: 			 */
 668:             linepos = pos;
 669:             pos = forw_raw_line(pos);
 670:         } else
 671:         {
 672:             /*
 673: 			 * Read the previous line and save the
 674: 			 * starting position of that line in linepos.
 675: 			 */
 676:             pos = back_raw_line(pos);
 677:             linepos = pos;
 678:         }
 679: 
 680:         if (pos == NULL_POSITION)
 681:         {
 682:             /*
 683: 			 * We hit EOF/BOF without a match.
 684: 			 */
 685:             error("Pattern not found");
 686:             return;
 687:         }
 688: 
 689:         /*
 690: 		 * Test the next line to see if we have a match.
 691: 		 * This is done in a variety of ways, depending
 692: 		 * on what pattern matching functions are available.
 693: 		 */
 694: #if REGCMP
 695:         if ( (regex(cpattern, line) != NULL)
 696: #else
 697: #if RECOMP
 698:         if ( (re_exec(line) == 1)
 699: #else
 700:         if ( (match(pattern, line))
 701: #endif
 702: #endif
 703:                 && (--n <= 0) )
 704:             /*
 705: 			 * Found the matching line.
 706: 			 */
 707:             break;
 708:     }
 709: 
 710:     jump_loc(linepos);
 711: }
 712: 
 713: #if (!REGCMP) && (!RECOMP)
 714: /*
 715:  * We have neither regcmp() nor re_comp().
 716:  * We use this function to do simple pattern matching.
 717:  * It supports no metacharacters like *, etc.
 718:  */
 719:     static int
 720: match(pattern, buf)
 721:     char *pattern, *buf;
 722: {
 723:     register char *pp, *lp;
 724: 
 725:     for ( ;  *buf != '\0';  buf++)
 726:     {
 727:         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
 728:             if (*pp == '\0' || *lp == '\0')
 729:                 break;
 730:         if (*pp == '\0')
 731:             return (1);
 732:     }
 733:     return (0);
 734: }
 735: #endif

Defined functions

back defined in line 156; used 3 times
backward defined in line 229; used 4 times
badmark defined in line 458; used 2 times
eof_bell defined in line 22; used 4 times
eof_check defined in line 34; used 2 times
forw defined in line 55; used 3 times
forward defined in line 208; used 4 times
get_back_scroll defined in line 516; used 4 times
gomark defined in line 491; used 2 times
init_mark defined in line 446; used 2 times
jump_back defined in line 299; used 4 times
jump_forw defined in line 278; used 2 times
jump_loc defined in line 382; used 6 times
jump_percent defined in line 347; used 2 times
lastmark defined in line 482; used 3 times
match defined in line 719; used 1 times
prepaint defined in line 252; used 2 times
search defined in line 528; used 3 times
setmark defined in line 473; used 2 times

Defined variables

hit_eof defined in line 8; used 9 times
marks defined in line 441; used 5 times
public defined in line 528; never used

Defined macros

LASTMARK defined in line 440; used 2 times
NMARKS defined in line 439; used 3 times
Last modified: 1990-07-10
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3430
Valid CSS Valid XHTML 1.0 Strict