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