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