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