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