Commit | Line | Data |
---|---|---|
bfe13c81 KB |
1 | /* |
2 | * Copyright (c) 1988 Mark Nudleman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Mark Nudleman. | |
8 | * | |
9 | * Redistribution and use in source and binary forms are permitted | |
10 | * provided that the above copyright notice and this paragraph are | |
11 | * duplicated in all such forms and that any documentation, | |
12 | * advertising materials, and other materials related to such | |
13 | * distribution and use acknowledge that the software was developed | |
14 | * by the University of California, Berkeley. The name of the | |
15 | * University may not be used to endorse or promote products derived | |
16 | * from this software without specific prior written permission. | |
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
18 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
19 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
20 | */ | |
21 | ||
22 | #ifndef lint | |
23 | static char sccsid[] = "@(#)prim.c 5.1 (Berkeley) %G%"; | |
24 | #endif /* not lint */ | |
25 | ||
26 | /* | |
27 | * Primitives for displaying the file on the screen. | |
28 | */ | |
29 | ||
30 | #include "less.h" | |
31 | #include "position.h" | |
32 | ||
33 | public int hit_eof; /* Keeps track of how many times we hit end of file */ | |
34 | public int screen_trashed; | |
35 | ||
36 | static int squished; | |
37 | ||
38 | extern int quiet; | |
39 | extern int sigs; | |
40 | extern int how_search; | |
41 | extern int top_scroll; | |
42 | extern int back_scroll; | |
43 | extern int sc_width, sc_height; | |
44 | extern int quit_at_eof; | |
45 | extern int caseless; | |
46 | extern int linenums; | |
47 | extern int plusoption; | |
48 | extern char *line; | |
49 | extern char *first_cmd; | |
50 | #if TAGS | |
51 | extern int tagoption; | |
52 | #endif | |
53 | ||
54 | /* | |
55 | * Sound the bell to indicate he is trying to move past end of file. | |
56 | */ | |
57 | static void | |
58 | eof_bell() | |
59 | { | |
60 | if (quiet == NOT_QUIET) | |
61 | bell(); | |
62 | else | |
63 | vbell(); | |
64 | } | |
65 | ||
66 | /* | |
67 | * Check to see if the end of file is currently "displayed". | |
68 | */ | |
69 | static void | |
70 | eof_check() | |
71 | { | |
72 | POSITION pos; | |
73 | ||
74 | if (sigs) | |
75 | return; | |
76 | /* | |
77 | * If the bottom line is empty, we are at EOF. | |
78 | * If the bottom line ends at the file length, | |
79 | * we must be just at EOF. | |
80 | */ | |
81 | pos = position(BOTTOM_PLUS_ONE); | |
82 | if (pos == NULL_POSITION || pos == ch_length()) | |
83 | hit_eof++; | |
84 | } | |
85 | ||
86 | /* | |
87 | * If the screen is "squished", repaint it. | |
88 | * "Squished" means the first displayed line is not at the top | |
89 | * of the screen; this can happen when we display a short file | |
90 | * for the first time. | |
91 | */ | |
92 | static void | |
93 | squish_check() | |
94 | { | |
95 | if (!squished) | |
96 | return; | |
97 | squished = 0; | |
98 | repaint(); | |
99 | } | |
100 | ||
101 | /* | |
102 | * Display n lines, scrolling forward, | |
103 | * starting at position pos in the input file. | |
104 | * "force" means display the n lines even if we hit end of file. | |
105 | * "only_last" means display only the last screenful if n > screen size. | |
106 | */ | |
107 | static void | |
108 | forw(n, pos, force, only_last) | |
109 | register int n; | |
110 | POSITION pos; | |
111 | int force; | |
112 | int only_last; | |
113 | { | |
114 | int eof = 0; | |
115 | int nlines = 0; | |
116 | int do_repaint; | |
117 | static int first_time = 1; | |
118 | ||
119 | squish_check(); | |
120 | ||
121 | /* | |
122 | * do_repaint tells us not to display anything till the end, | |
123 | * then just repaint the entire screen. | |
124 | */ | |
125 | do_repaint = (only_last && n > sc_height-1); | |
126 | ||
127 | if (!do_repaint) | |
128 | { | |
129 | if (top_scroll && n >= sc_height - 1) | |
130 | { | |
131 | /* | |
132 | * Start a new screen. | |
133 | * {{ This is not really desirable if we happen | |
134 | * to hit eof in the middle of this screen, | |
135 | * but we don't yet know if that will happen. }} | |
136 | */ | |
137 | if (top_scroll == 2) | |
138 | clear(); | |
139 | home(); | |
140 | force = 1; | |
141 | } else | |
142 | { | |
143 | lower_left(); | |
144 | clear_eol(); | |
145 | } | |
146 | ||
147 | if (pos != position(BOTTOM_PLUS_ONE)) | |
148 | { | |
149 | /* | |
150 | * This is not contiguous with what is | |
151 | * currently displayed. Clear the screen image | |
152 | * (position table) and start a new screen. | |
153 | */ | |
154 | pos_clear(); | |
155 | add_forw_pos(pos); | |
156 | force = 1; | |
157 | if (top_scroll) | |
158 | { | |
159 | if (top_scroll == 2) | |
160 | clear(); | |
161 | home(); | |
162 | } else if (!first_time) | |
163 | { | |
164 | putstr("...skipping...\n"); | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | while (--n >= 0) | |
170 | { | |
171 | /* | |
172 | * Read the next line of input. | |
173 | */ | |
174 | pos = forw_line(pos); | |
175 | if (pos == NULL_POSITION) | |
176 | { | |
177 | /* | |
178 | * End of file: stop here unless the top line | |
179 | * is still empty, or "force" is true. | |
180 | */ | |
181 | eof = 1; | |
182 | if (!force && position(TOP) != NULL_POSITION) | |
183 | break; | |
184 | line = NULL; | |
185 | } | |
186 | /* | |
187 | * Add the position of the next line to the position table. | |
188 | * Display the current line on the screen. | |
189 | */ | |
190 | add_forw_pos(pos); | |
191 | nlines++; | |
192 | if (do_repaint) | |
193 | continue; | |
194 | /* | |
195 | * If this is the first screen displayed and | |
196 | * we hit an early EOF (i.e. before the requested | |
197 | * number of lines), we "squish" the display down | |
198 | * at the bottom of the screen. | |
199 | * But don't do this if a + option or a -t option | |
200 | * was given. These options can cause us to | |
201 | * start the display after the beginning of the file, | |
202 | * and it is not appropriate to squish in that case. | |
203 | */ | |
204 | if (first_time && line == NULL && !top_scroll && | |
205 | #if TAGS | |
206 | !tagoption && | |
207 | #endif | |
208 | !plusoption) | |
209 | { | |
210 | squished = 1; | |
211 | continue; | |
212 | } | |
213 | if (top_scroll == 1) | |
214 | clear_eol(); | |
215 | put_line(); | |
216 | } | |
217 | ||
218 | if (eof && !sigs) | |
219 | hit_eof++; | |
220 | else | |
221 | eof_check(); | |
222 | if (nlines == 0) | |
223 | eof_bell(); | |
224 | else if (do_repaint) | |
225 | repaint(); | |
226 | first_time = 0; | |
227 | (void) currline(BOTTOM); | |
228 | } | |
229 | ||
230 | /* | |
231 | * Display n lines, scrolling backward. | |
232 | */ | |
233 | static void | |
234 | back(n, pos, force, only_last) | |
235 | register int n; | |
236 | POSITION pos; | |
237 | int force; | |
238 | int only_last; | |
239 | { | |
240 | int nlines = 0; | |
241 | int do_repaint; | |
242 | ||
243 | squish_check(); | |
244 | do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); | |
245 | hit_eof = 0; | |
246 | while (--n >= 0) | |
247 | { | |
248 | /* | |
249 | * Get the previous line of input. | |
250 | */ | |
251 | pos = back_line(pos); | |
252 | if (pos == NULL_POSITION) | |
253 | { | |
254 | /* | |
255 | * Beginning of file: stop here unless "force" is true. | |
256 | */ | |
257 | if (!force) | |
258 | break; | |
259 | line = NULL; | |
260 | } | |
261 | /* | |
262 | * Add the position of the previous line to the position table. | |
263 | * Display the line on the screen. | |
264 | */ | |
265 | add_back_pos(pos); | |
266 | nlines++; | |
267 | if (!do_repaint) | |
268 | { | |
269 | home(); | |
270 | add_line(); | |
271 | put_line(); | |
272 | } | |
273 | } | |
274 | ||
275 | eof_check(); | |
276 | if (nlines == 0) | |
277 | eof_bell(); | |
278 | else if (do_repaint) | |
279 | repaint(); | |
280 | (void) currline(BOTTOM); | |
281 | } | |
282 | ||
283 | /* | |
284 | * Display n more lines, forward. | |
285 | * Start just after the line currently displayed at the bottom of the screen. | |
286 | */ | |
287 | public void | |
288 | forward(n, only_last) | |
289 | int n; | |
290 | int only_last; | |
291 | { | |
292 | POSITION pos; | |
293 | ||
294 | if (quit_at_eof && hit_eof) | |
295 | { | |
296 | /* | |
297 | * If the -e flag is set and we're trying to go | |
298 | * forward from end-of-file, go on to the next file. | |
299 | */ | |
300 | next_file(1); | |
301 | return; | |
302 | } | |
303 | ||
304 | pos = position(BOTTOM_PLUS_ONE); | |
305 | if (pos == NULL_POSITION) | |
306 | { | |
307 | eof_bell(); | |
308 | hit_eof++; | |
309 | return; | |
310 | } | |
311 | forw(n, pos, 0, only_last); | |
312 | } | |
313 | ||
314 | /* | |
315 | * Display n more lines, backward. | |
316 | * Start just before the line currently displayed at the top of the screen. | |
317 | */ | |
318 | public void | |
319 | backward(n, only_last) | |
320 | int n; | |
321 | int only_last; | |
322 | { | |
323 | POSITION pos; | |
324 | ||
325 | pos = position(TOP); | |
326 | if (pos == NULL_POSITION) | |
327 | { | |
328 | /* | |
329 | * This will almost never happen, | |
330 | * because the top line is almost never empty. | |
331 | */ | |
332 | eof_bell(); | |
333 | return; | |
334 | } | |
335 | back(n, pos, 0, only_last); | |
336 | } | |
337 | ||
338 | /* | |
339 | * Repaint the screen, starting from a specified position. | |
340 | */ | |
341 | static void | |
342 | prepaint(pos) | |
343 | POSITION pos; | |
344 | { | |
345 | hit_eof = 0; | |
346 | forw(sc_height-1, pos, 1, 0); | |
347 | screen_trashed = 0; | |
348 | } | |
349 | ||
350 | /* | |
351 | * Repaint the screen. | |
352 | */ | |
353 | public void | |
354 | repaint() | |
355 | { | |
356 | /* | |
357 | * Start at the line currently at the top of the screen | |
358 | * and redisplay the screen. | |
359 | */ | |
360 | prepaint(position(TOP)); | |
361 | } | |
362 | ||
363 | /* | |
364 | * Jump to the end of the file. | |
365 | * It is more convenient to paint the screen backward, | |
366 | * from the end of the file toward the beginning. | |
367 | */ | |
368 | public void | |
369 | jump_forw() | |
370 | { | |
371 | POSITION pos; | |
372 | ||
373 | if (ch_end_seek()) | |
374 | { | |
375 | error("Cannot seek to end of file"); | |
376 | return; | |
377 | } | |
378 | lastmark(); | |
379 | pos = ch_tell(); | |
380 | clear(); | |
381 | pos_clear(); | |
382 | add_back_pos(pos); | |
383 | back(sc_height - 1, pos, 0, 0); | |
384 | } | |
385 | ||
386 | /* | |
387 | * Jump to line n in the file. | |
388 | */ | |
389 | public void | |
390 | jump_back(n) | |
391 | register int n; | |
392 | { | |
393 | register int c; | |
394 | int nlines; | |
395 | ||
396 | /* | |
397 | * This is done the slow way, by starting at the beginning | |
398 | * of the file and counting newlines. | |
399 | * | |
400 | * {{ Now that we have line numbering (in linenum.c), | |
401 | * we could improve on this by starting at the | |
402 | * nearest known line rather than at the beginning. }} | |
403 | */ | |
404 | if (ch_seek((POSITION)0)) | |
405 | { | |
406 | /* | |
407 | * Probably a pipe with beginning of file no longer buffered. | |
408 | * If he wants to go to line 1, we do the best we can, | |
409 | * by going to the first line which is still buffered. | |
410 | */ | |
411 | if (n <= 1 && ch_beg_seek() == 0) | |
412 | jump_loc(ch_tell()); | |
413 | error("Cannot get to beginning of file"); | |
414 | return; | |
415 | } | |
416 | ||
417 | /* | |
418 | * Start counting lines. | |
419 | */ | |
420 | for (nlines = 1; nlines < n; nlines++) | |
421 | { | |
422 | while ((c = ch_forw_get()) != '\n') | |
423 | if (c == EOI) | |
424 | { | |
425 | char message[40]; | |
426 | sprintf(message, "File has only %d lines", | |
427 | nlines-1); | |
428 | error(message); | |
429 | return; | |
430 | } | |
431 | } | |
432 | ||
433 | jump_loc(ch_tell()); | |
434 | } | |
435 | ||
436 | /* | |
437 | * Jump to a specified percentage into the file. | |
438 | * This is a poor compensation for not being able to | |
439 | * quickly jump to a specific line number. | |
440 | */ | |
441 | public void | |
442 | jump_percent(percent) | |
443 | int percent; | |
444 | { | |
445 | POSITION pos, len; | |
446 | register int c; | |
447 | ||
448 | /* | |
449 | * Determine the position in the file | |
450 | * (the specified percentage of the file's length). | |
451 | */ | |
452 | if ((len = ch_length()) == NULL_POSITION) | |
453 | { | |
454 | error("Don't know length of file"); | |
455 | return; | |
456 | } | |
457 | pos = (percent * len) / 100; | |
458 | ||
459 | /* | |
460 | * Back up to the beginning of the line. | |
461 | */ | |
462 | if (ch_seek(pos) == 0) | |
463 | { | |
464 | while ((c = ch_back_get()) != '\n' && c != EOI) | |
465 | ; | |
466 | if (c == '\n') | |
467 | (void) ch_forw_get(); | |
468 | pos = ch_tell(); | |
469 | } | |
470 | jump_loc(pos); | |
471 | } | |
472 | ||
473 | /* | |
474 | * Jump to a specified position in the file. | |
475 | */ | |
476 | public void | |
477 | jump_loc(pos) | |
478 | POSITION pos; | |
479 | { | |
480 | register int nline; | |
481 | POSITION tpos; | |
482 | ||
483 | if ((nline = onscreen(pos)) >= 0) | |
484 | { | |
485 | /* | |
486 | * The line is currently displayed. | |
487 | * Just scroll there. | |
488 | */ | |
489 | forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); | |
490 | return; | |
491 | } | |
492 | ||
493 | /* | |
494 | * Line is not on screen. | |
495 | * Seek to the desired location. | |
496 | */ | |
497 | if (ch_seek(pos)) | |
498 | { | |
499 | error("Cannot seek to that position"); | |
500 | return; | |
501 | } | |
502 | ||
503 | /* | |
504 | * See if the desired line is BEFORE the currently | |
505 | * displayed screen. If so, then move forward far | |
506 | * enough so the line we're on will be at the bottom | |
507 | * of the screen, in order to be able to call back() | |
508 | * to make the screen scroll backwards & put the line | |
509 | * at the top of the screen. | |
510 | * {{ This seems inefficient, but it's not so bad, | |
511 | * since we can never move forward more than a | |
512 | * screenful before we stop to redraw the screen. }} | |
513 | */ | |
514 | tpos = position(TOP); | |
515 | if (tpos != NULL_POSITION && pos < tpos) | |
516 | { | |
517 | POSITION npos = pos; | |
518 | /* | |
519 | * Note that we can't forw_line() past tpos here, | |
520 | * so there should be no EOI at this stage. | |
521 | */ | |
522 | for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) | |
523 | npos = forw_line(npos); | |
524 | ||
525 | if (npos < tpos) | |
526 | { | |
527 | /* | |
528 | * More than a screenful back. | |
529 | */ | |
530 | lastmark(); | |
531 | clear(); | |
532 | pos_clear(); | |
533 | add_back_pos(npos); | |
534 | } | |
535 | ||
536 | /* | |
537 | * Note that back() will repaint() if nline > back_scroll. | |
538 | */ | |
539 | back(nline, npos, 1, 0); | |
540 | return; | |
541 | } | |
542 | /* | |
543 | * Remember where we were; clear and paint the screen. | |
544 | */ | |
545 | lastmark(); | |
546 | prepaint(pos); | |
547 | } | |
548 | ||
549 | /* | |
550 | * The table of marks. | |
551 | * A mark is simply a position in the file. | |
552 | */ | |
553 | #define NMARKS (27) /* 26 for a-z plus one for quote */ | |
554 | #define LASTMARK (NMARKS-1) /* For quote */ | |
555 | static POSITION marks[NMARKS]; | |
556 | ||
557 | /* | |
558 | * Initialize the mark table to show no marks are set. | |
559 | */ | |
560 | public void | |
561 | init_mark() | |
562 | { | |
563 | int i; | |
564 | ||
565 | for (i = 0; i < NMARKS; i++) | |
566 | marks[i] = NULL_POSITION; | |
567 | } | |
568 | ||
569 | /* | |
570 | * See if a mark letter is valid (between a and z). | |
571 | */ | |
572 | static int | |
573 | badmark(c) | |
574 | int c; | |
575 | { | |
576 | if (c < 'a' || c > 'z') | |
577 | { | |
578 | error("Choose a letter between 'a' and 'z'"); | |
579 | return (1); | |
580 | } | |
581 | return (0); | |
582 | } | |
583 | ||
584 | /* | |
585 | * Set a mark. | |
586 | */ | |
587 | public void | |
588 | setmark(c) | |
589 | int c; | |
590 | { | |
591 | if (badmark(c)) | |
592 | return; | |
593 | marks[c-'a'] = position(TOP); | |
594 | } | |
595 | ||
596 | public void | |
597 | lastmark() | |
598 | { | |
599 | marks[LASTMARK] = position(TOP); | |
600 | } | |
601 | ||
602 | /* | |
603 | * Go to a previously set mark. | |
604 | */ | |
605 | public void | |
606 | gomark(c) | |
607 | int c; | |
608 | { | |
609 | POSITION pos; | |
610 | ||
611 | if (c == '\'') | |
612 | pos = marks[LASTMARK]; | |
613 | else if (badmark(c)) | |
614 | return; | |
615 | else | |
616 | pos = marks[c-'a']; | |
617 | ||
618 | if (pos == NULL_POSITION) | |
619 | error("mark not set"); | |
620 | else | |
621 | jump_loc(pos); | |
622 | } | |
623 | ||
624 | /* | |
625 | * Get the backwards scroll limit. | |
626 | * Must call this function instead of just using the value of | |
627 | * back_scroll, because the default case depends on sc_height and | |
628 | * top_scroll, as well as back_scroll. | |
629 | */ | |
630 | public int | |
631 | get_back_scroll() | |
632 | { | |
633 | if (back_scroll >= 0) | |
634 | return (back_scroll); | |
635 | if (top_scroll) | |
636 | return (sc_height - 2); | |
637 | return (sc_height - 1); | |
638 | } | |
639 | ||
640 | /* | |
641 | * Search for the n-th occurence of a specified pattern, | |
642 | * either forward or backward. | |
643 | */ | |
644 | public void | |
645 | search(search_forward, pattern, n, wantmatch) | |
646 | register int search_forward; | |
647 | register char *pattern; | |
648 | register int n; | |
649 | int wantmatch; | |
650 | { | |
651 | POSITION pos, linepos; | |
652 | register char *p; | |
653 | register char *q; | |
654 | int linenum; | |
655 | int linematch; | |
656 | #if RECOMP | |
657 | char *re_comp(); | |
658 | char *errmsg; | |
659 | #else | |
660 | #if REGCMP | |
661 | char *regcmp(); | |
662 | static char *cpattern = NULL; | |
663 | #else | |
664 | static char lpbuf[100]; | |
665 | static char *last_pattern = NULL; | |
666 | #endif | |
667 | #endif | |
668 | ||
669 | if (caseless && pattern != NULL) | |
670 | { | |
671 | /* | |
672 | * For a caseless search, convert any uppercase | |
673 | * in the pattern to lowercase. | |
674 | */ | |
675 | for (p = pattern; *p != '\0'; p++) | |
676 | if (*p >= 'A' && *p <= 'Z') | |
677 | *p += 'a' - 'A'; | |
678 | } | |
679 | #if RECOMP | |
680 | ||
681 | /* | |
682 | * (re_comp handles a null pattern internally, | |
683 | * so there is no need to check for a null pattern here.) | |
684 | */ | |
685 | if ((errmsg = re_comp(pattern)) != NULL) | |
686 | { | |
687 | error(errmsg); | |
688 | return; | |
689 | } | |
690 | #else | |
691 | #if REGCMP | |
692 | if (pattern == NULL || *pattern == '\0') | |
693 | { | |
694 | /* | |
695 | * A null pattern means use the previous pattern. | |
696 | * The compiled previous pattern is in cpattern, so just use it. | |
697 | */ | |
698 | if (cpattern == NULL) | |
699 | { | |
700 | error("No previous regular expression"); | |
701 | return; | |
702 | } | |
703 | } else | |
704 | { | |
705 | /* | |
706 | * Otherwise compile the given pattern. | |
707 | */ | |
708 | char *s; | |
709 | if ((s = regcmp(pattern, 0)) == NULL) | |
710 | { | |
711 | error("Invalid pattern"); | |
712 | return; | |
713 | } | |
714 | if (cpattern != NULL) | |
715 | free(cpattern); | |
716 | cpattern = s; | |
717 | } | |
718 | #else | |
719 | if (pattern == NULL || *pattern == '\0') | |
720 | { | |
721 | /* | |
722 | * Null pattern means use the previous pattern. | |
723 | */ | |
724 | if (last_pattern == NULL) | |
725 | { | |
726 | error("No previous regular expression"); | |
727 | return; | |
728 | } | |
729 | pattern = last_pattern; | |
730 | } else | |
731 | { | |
732 | strcpy(lpbuf, pattern); | |
733 | last_pattern = lpbuf; | |
734 | } | |
735 | #endif | |
736 | #endif | |
737 | ||
738 | /* | |
739 | * Figure out where to start the search. | |
740 | */ | |
741 | ||
742 | if (position(TOP) == NULL_POSITION) | |
743 | { | |
744 | /* | |
745 | * Nothing is currently displayed. | |
746 | * Start at the beginning of the file. | |
747 | * (This case is mainly for first_cmd searches, | |
748 | * for example, "+/xyz" on the command line.) | |
749 | */ | |
750 | pos = (POSITION)0; | |
751 | } else if (!search_forward) | |
752 | { | |
753 | /* | |
754 | * Backward search: start just before the top line | |
755 | * displayed on the screen. | |
756 | */ | |
757 | pos = position(TOP); | |
758 | } else if (how_search == 0) | |
759 | { | |
760 | /* | |
761 | * Start at the second real line displayed on the screen. | |
762 | */ | |
763 | pos = position(TOP); | |
764 | do | |
765 | pos = forw_raw_line(pos); | |
766 | while (pos < position(TOP+1)); | |
767 | } else if (how_search == 1) | |
768 | { | |
769 | /* | |
770 | * Start just after the bottom line displayed on the screen. | |
771 | */ | |
772 | pos = position(BOTTOM_PLUS_ONE); | |
773 | } else | |
774 | { | |
775 | /* | |
776 | * Start at the second screen line displayed on the screen. | |
777 | */ | |
778 | pos = position(TOP_PLUS_ONE); | |
779 | } | |
780 | ||
781 | if (pos == NULL_POSITION) | |
782 | { | |
783 | /* | |
784 | * Can't find anyplace to start searching from. | |
785 | */ | |
786 | error("Nothing to search"); | |
787 | return; | |
788 | } | |
789 | ||
790 | linenum = find_linenum(pos); | |
791 | for (;;) | |
792 | { | |
793 | /* | |
794 | * Get lines until we find a matching one or | |
795 | * until we hit end-of-file (or beginning-of-file | |
796 | * if we're going backwards). | |
797 | */ | |
798 | if (sigs) | |
799 | /* | |
800 | * A signal aborts the search. | |
801 | */ | |
802 | return; | |
803 | ||
804 | if (search_forward) | |
805 | { | |
806 | /* | |
807 | * Read the next line, and save the | |
808 | * starting position of that line in linepos. | |
809 | */ | |
810 | linepos = pos; | |
811 | pos = forw_raw_line(pos); | |
812 | if (linenum != 0) | |
813 | linenum++; | |
814 | } else | |
815 | { | |
816 | /* | |
817 | * Read the previous line and save the | |
818 | * starting position of that line in linepos. | |
819 | */ | |
820 | pos = back_raw_line(pos); | |
821 | linepos = pos; | |
822 | if (linenum != 0) | |
823 | linenum--; | |
824 | } | |
825 | ||
826 | if (pos == NULL_POSITION) | |
827 | { | |
828 | /* | |
829 | * We hit EOF/BOF without a match. | |
830 | */ | |
831 | error("Pattern not found"); | |
832 | return; | |
833 | } | |
834 | ||
835 | /* | |
836 | * If we're using line numbers, we might as well | |
837 | * remember the information we have now (the position | |
838 | * and line number of the current line). | |
839 | */ | |
840 | if (linenums) | |
841 | add_lnum(linenum, pos); | |
842 | ||
843 | if (caseless) | |
844 | { | |
845 | /* | |
846 | * If this is a caseless search, convert | |
847 | * uppercase in the input line to lowercase. | |
848 | * While we're at it, remove any backspaces | |
849 | * along with the preceeding char. | |
850 | * This allows us to match text which is | |
851 | * underlined or overstruck. | |
852 | */ | |
853 | for (p = q = line; *p != '\0'; p++, q++) | |
854 | { | |
855 | if (*p >= 'A' && *p <= 'Z') | |
856 | /* Convert uppercase to lowercase. */ | |
857 | *q = *p + 'a' - 'A'; | |
858 | else if (q > line && *p == '\b') | |
859 | /* Delete BS and preceeding char. */ | |
860 | q -= 2; | |
861 | else | |
862 | /* Otherwise, just copy. */ | |
863 | *q = *p; | |
864 | } | |
865 | } | |
866 | ||
867 | /* | |
868 | * Test the next line to see if we have a match. | |
869 | * This is done in a variety of ways, depending | |
870 | * on what pattern matching functions are available. | |
871 | */ | |
872 | #if REGCMP | |
873 | linematch = (regex(cpattern, line) != NULL); | |
874 | #else | |
875 | #if RECOMP | |
876 | linematch = (re_exec(line) == 1); | |
877 | #else | |
878 | linematch = match(pattern, line); | |
879 | #endif | |
880 | #endif | |
881 | /* | |
882 | * We are successful if wantmatch and linematch are | |
883 | * both true (want a match and got it), | |
884 | * or both false (want a non-match and got it). | |
885 | */ | |
886 | if (((wantmatch && linematch) || (!wantmatch && !linematch)) && | |
887 | --n <= 0) | |
888 | /* | |
889 | * Found the line. | |
890 | */ | |
891 | break; | |
892 | } | |
893 | ||
894 | jump_loc(linepos); | |
895 | } | |
896 | ||
897 | #if (!REGCMP) && (!RECOMP) | |
898 | /* | |
899 | * We have neither regcmp() nor re_comp(). | |
900 | * We use this function to do simple pattern matching. | |
901 | * It supports no metacharacters like *, etc. | |
902 | */ | |
903 | static int | |
904 | match(pattern, buf) | |
905 | char *pattern, *buf; | |
906 | { | |
907 | register char *pp, *lp; | |
908 | ||
909 | for ( ; *buf != '\0'; buf++) | |
910 | { | |
911 | for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) | |
912 | if (*pp == '\0' || *lp == '\0') | |
913 | break; | |
914 | if (*pp == '\0') | |
915 | return (1); | |
916 | } | |
917 | return (0); | |
918 | } | |
919 | #endif |