1273e1e22Sbostic /* 2273e1e22Sbostic * Copyright (c) 1988 Mark Nudleman 3273e1e22Sbostic * Copyright (c) 1988 Regents of the University of California. 4273e1e22Sbostic * All rights reserved. 5273e1e22Sbostic * 6273e1e22Sbostic * This code is derived from software contributed to Berkeley by 7273e1e22Sbostic * Mark Nudleman. 8273e1e22Sbostic * 9273e1e22Sbostic * Redistribution and use in source and binary forms are permitted 10273e1e22Sbostic * provided that the above copyright notice and this paragraph are 11273e1e22Sbostic * duplicated in all such forms and that any documentation, 12273e1e22Sbostic * advertising materials, and other materials related to such 13273e1e22Sbostic * distribution and use acknowledge that the software was developed 14273e1e22Sbostic * by the University of California, Berkeley. The name of the 15273e1e22Sbostic * University may not be used to endorse or promote products derived 16273e1e22Sbostic * from this software without specific prior written permission. 17273e1e22Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 18273e1e22Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 19273e1e22Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20273e1e22Sbostic */ 21273e1e22Sbostic 22273e1e22Sbostic #ifndef lint 23*8033092dSbostic static char sccsid[] = "@(#)prim.c 5.3 (Berkeley) 07/22/88"; 24273e1e22Sbostic #endif /* not lint */ 25273e1e22Sbostic 26273e1e22Sbostic /* 27273e1e22Sbostic * Primitives for displaying the file on the screen. 28273e1e22Sbostic */ 29273e1e22Sbostic 30273e1e22Sbostic #include "less.h" 31273e1e22Sbostic #include "position.h" 32273e1e22Sbostic 33273e1e22Sbostic public int hit_eof; /* Keeps track of how many times we hit end of file */ 34273e1e22Sbostic public int screen_trashed; 35273e1e22Sbostic 36273e1e22Sbostic static int squished; 37273e1e22Sbostic 38273e1e22Sbostic extern int quiet; 39273e1e22Sbostic extern int sigs; 40273e1e22Sbostic extern int how_search; 41273e1e22Sbostic extern int top_scroll; 42273e1e22Sbostic extern int back_scroll; 43273e1e22Sbostic extern int sc_width, sc_height; 44273e1e22Sbostic extern int quit_at_eof; 45273e1e22Sbostic extern int caseless; 46273e1e22Sbostic extern int linenums; 47273e1e22Sbostic extern int plusoption; 48273e1e22Sbostic extern char *line; 49273e1e22Sbostic extern char *first_cmd; 50273e1e22Sbostic extern int tagoption; 51273e1e22Sbostic 52273e1e22Sbostic /* 53273e1e22Sbostic * Sound the bell to indicate he is trying to move past end of file. 54273e1e22Sbostic */ 55273e1e22Sbostic static void 56273e1e22Sbostic eof_bell() 57273e1e22Sbostic { 58273e1e22Sbostic if (quiet == NOT_QUIET) 59273e1e22Sbostic bell(); 60273e1e22Sbostic else 61273e1e22Sbostic vbell(); 62273e1e22Sbostic } 63273e1e22Sbostic 64273e1e22Sbostic /* 65273e1e22Sbostic * Check to see if the end of file is currently "displayed". 66273e1e22Sbostic */ 67273e1e22Sbostic static void 68273e1e22Sbostic eof_check() 69273e1e22Sbostic { 70273e1e22Sbostic POSITION pos; 71273e1e22Sbostic 72273e1e22Sbostic if (sigs) 73273e1e22Sbostic return; 74273e1e22Sbostic /* 75273e1e22Sbostic * If the bottom line is empty, we are at EOF. 76273e1e22Sbostic * If the bottom line ends at the file length, 77273e1e22Sbostic * we must be just at EOF. 78273e1e22Sbostic */ 79273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE); 80273e1e22Sbostic if (pos == NULL_POSITION || pos == ch_length()) 81273e1e22Sbostic hit_eof++; 82273e1e22Sbostic } 83273e1e22Sbostic 84273e1e22Sbostic /* 85273e1e22Sbostic * If the screen is "squished", repaint it. 86273e1e22Sbostic * "Squished" means the first displayed line is not at the top 87273e1e22Sbostic * of the screen; this can happen when we display a short file 88273e1e22Sbostic * for the first time. 89273e1e22Sbostic */ 90273e1e22Sbostic static void 91273e1e22Sbostic squish_check() 92273e1e22Sbostic { 93273e1e22Sbostic if (!squished) 94273e1e22Sbostic return; 95273e1e22Sbostic squished = 0; 96273e1e22Sbostic repaint(); 97273e1e22Sbostic } 98273e1e22Sbostic 99273e1e22Sbostic /* 100273e1e22Sbostic * Display n lines, scrolling forward, 101273e1e22Sbostic * starting at position pos in the input file. 102273e1e22Sbostic * "force" means display the n lines even if we hit end of file. 103273e1e22Sbostic * "only_last" means display only the last screenful if n > screen size. 104273e1e22Sbostic */ 105273e1e22Sbostic static void 106273e1e22Sbostic forw(n, pos, force, only_last) 107273e1e22Sbostic register int n; 108273e1e22Sbostic POSITION pos; 109273e1e22Sbostic int force; 110273e1e22Sbostic int only_last; 111273e1e22Sbostic { 112273e1e22Sbostic int eof = 0; 113273e1e22Sbostic int nlines = 0; 114273e1e22Sbostic int do_repaint; 115273e1e22Sbostic static int first_time = 1; 116273e1e22Sbostic 117273e1e22Sbostic squish_check(); 118273e1e22Sbostic 119273e1e22Sbostic /* 120273e1e22Sbostic * do_repaint tells us not to display anything till the end, 121273e1e22Sbostic * then just repaint the entire screen. 122273e1e22Sbostic */ 123273e1e22Sbostic do_repaint = (only_last && n > sc_height-1); 124273e1e22Sbostic 125273e1e22Sbostic if (!do_repaint) 126273e1e22Sbostic { 127273e1e22Sbostic if (top_scroll && n >= sc_height - 1) 128273e1e22Sbostic { 129273e1e22Sbostic /* 130273e1e22Sbostic * Start a new screen. 131273e1e22Sbostic * {{ This is not really desirable if we happen 132273e1e22Sbostic * to hit eof in the middle of this screen, 133273e1e22Sbostic * but we don't yet know if that will happen. }} 134273e1e22Sbostic */ 135273e1e22Sbostic if (top_scroll == 2) 136273e1e22Sbostic clear(); 137273e1e22Sbostic home(); 138273e1e22Sbostic force = 1; 139273e1e22Sbostic } else 140273e1e22Sbostic { 141273e1e22Sbostic lower_left(); 142273e1e22Sbostic clear_eol(); 143273e1e22Sbostic } 144273e1e22Sbostic 145273e1e22Sbostic if (pos != position(BOTTOM_PLUS_ONE)) 146273e1e22Sbostic { 147273e1e22Sbostic /* 148273e1e22Sbostic * This is not contiguous with what is 149273e1e22Sbostic * currently displayed. Clear the screen image 150273e1e22Sbostic * (position table) and start a new screen. 151273e1e22Sbostic */ 152273e1e22Sbostic pos_clear(); 153273e1e22Sbostic add_forw_pos(pos); 154273e1e22Sbostic force = 1; 155273e1e22Sbostic if (top_scroll) 156273e1e22Sbostic { 157273e1e22Sbostic if (top_scroll == 2) 158273e1e22Sbostic clear(); 159273e1e22Sbostic home(); 160273e1e22Sbostic } else if (!first_time) 161273e1e22Sbostic { 162273e1e22Sbostic putstr("...skipping...\n"); 163273e1e22Sbostic } 164273e1e22Sbostic } 165273e1e22Sbostic } 166273e1e22Sbostic 167273e1e22Sbostic while (--n >= 0) 168273e1e22Sbostic { 169273e1e22Sbostic /* 170273e1e22Sbostic * Read the next line of input. 171273e1e22Sbostic */ 172273e1e22Sbostic pos = forw_line(pos); 173273e1e22Sbostic if (pos == NULL_POSITION) 174273e1e22Sbostic { 175273e1e22Sbostic /* 176273e1e22Sbostic * End of file: stop here unless the top line 177273e1e22Sbostic * is still empty, or "force" is true. 178273e1e22Sbostic */ 179273e1e22Sbostic eof = 1; 180273e1e22Sbostic if (!force && position(TOP) != NULL_POSITION) 181273e1e22Sbostic break; 182273e1e22Sbostic line = NULL; 183273e1e22Sbostic } 184273e1e22Sbostic /* 185273e1e22Sbostic * Add the position of the next line to the position table. 186273e1e22Sbostic * Display the current line on the screen. 187273e1e22Sbostic */ 188273e1e22Sbostic add_forw_pos(pos); 189273e1e22Sbostic nlines++; 190273e1e22Sbostic if (do_repaint) 191273e1e22Sbostic continue; 192273e1e22Sbostic /* 193273e1e22Sbostic * If this is the first screen displayed and 194273e1e22Sbostic * we hit an early EOF (i.e. before the requested 195273e1e22Sbostic * number of lines), we "squish" the display down 196273e1e22Sbostic * at the bottom of the screen. 197273e1e22Sbostic * But don't do this if a + option or a -t option 198273e1e22Sbostic * was given. These options can cause us to 199273e1e22Sbostic * start the display after the beginning of the file, 200273e1e22Sbostic * and it is not appropriate to squish in that case. 201273e1e22Sbostic */ 202273e1e22Sbostic if (first_time && line == NULL && !top_scroll && 2032cee6194Sbostic !tagoption && !plusoption) 204273e1e22Sbostic { 205273e1e22Sbostic squished = 1; 206273e1e22Sbostic continue; 207273e1e22Sbostic } 208273e1e22Sbostic if (top_scroll == 1) 209273e1e22Sbostic clear_eol(); 210273e1e22Sbostic put_line(); 211273e1e22Sbostic } 212273e1e22Sbostic 213273e1e22Sbostic if (eof && !sigs) 214273e1e22Sbostic hit_eof++; 215273e1e22Sbostic else 216273e1e22Sbostic eof_check(); 217273e1e22Sbostic if (nlines == 0) 218273e1e22Sbostic eof_bell(); 219273e1e22Sbostic else if (do_repaint) 220273e1e22Sbostic repaint(); 221273e1e22Sbostic first_time = 0; 222273e1e22Sbostic (void) currline(BOTTOM); 223273e1e22Sbostic } 224273e1e22Sbostic 225273e1e22Sbostic /* 226273e1e22Sbostic * Display n lines, scrolling backward. 227273e1e22Sbostic */ 228273e1e22Sbostic static void 229273e1e22Sbostic back(n, pos, force, only_last) 230273e1e22Sbostic register int n; 231273e1e22Sbostic POSITION pos; 232273e1e22Sbostic int force; 233273e1e22Sbostic int only_last; 234273e1e22Sbostic { 235273e1e22Sbostic int nlines = 0; 236273e1e22Sbostic int do_repaint; 237273e1e22Sbostic 238273e1e22Sbostic squish_check(); 239273e1e22Sbostic do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); 240273e1e22Sbostic hit_eof = 0; 241273e1e22Sbostic while (--n >= 0) 242273e1e22Sbostic { 243273e1e22Sbostic /* 244273e1e22Sbostic * Get the previous line of input. 245273e1e22Sbostic */ 246273e1e22Sbostic pos = back_line(pos); 247273e1e22Sbostic if (pos == NULL_POSITION) 248273e1e22Sbostic { 249273e1e22Sbostic /* 250273e1e22Sbostic * Beginning of file: stop here unless "force" is true. 251273e1e22Sbostic */ 252273e1e22Sbostic if (!force) 253273e1e22Sbostic break; 254273e1e22Sbostic line = NULL; 255273e1e22Sbostic } 256273e1e22Sbostic /* 257273e1e22Sbostic * Add the position of the previous line to the position table. 258273e1e22Sbostic * Display the line on the screen. 259273e1e22Sbostic */ 260273e1e22Sbostic add_back_pos(pos); 261273e1e22Sbostic nlines++; 262273e1e22Sbostic if (!do_repaint) 263273e1e22Sbostic { 264273e1e22Sbostic home(); 265273e1e22Sbostic add_line(); 266273e1e22Sbostic put_line(); 267273e1e22Sbostic } 268273e1e22Sbostic } 269273e1e22Sbostic 270273e1e22Sbostic eof_check(); 271273e1e22Sbostic if (nlines == 0) 272273e1e22Sbostic eof_bell(); 273273e1e22Sbostic else if (do_repaint) 274273e1e22Sbostic repaint(); 275273e1e22Sbostic (void) currline(BOTTOM); 276273e1e22Sbostic } 277273e1e22Sbostic 278273e1e22Sbostic /* 279273e1e22Sbostic * Display n more lines, forward. 280273e1e22Sbostic * Start just after the line currently displayed at the bottom of the screen. 281273e1e22Sbostic */ 282273e1e22Sbostic public void 283273e1e22Sbostic forward(n, only_last) 284273e1e22Sbostic int n; 285273e1e22Sbostic int only_last; 286273e1e22Sbostic { 287273e1e22Sbostic POSITION pos; 288273e1e22Sbostic 289273e1e22Sbostic if (quit_at_eof && hit_eof) 290273e1e22Sbostic { 291273e1e22Sbostic /* 292273e1e22Sbostic * If the -e flag is set and we're trying to go 293273e1e22Sbostic * forward from end-of-file, go on to the next file. 294273e1e22Sbostic */ 295273e1e22Sbostic next_file(1); 296273e1e22Sbostic return; 297273e1e22Sbostic } 298273e1e22Sbostic 299273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE); 300273e1e22Sbostic if (pos == NULL_POSITION) 301273e1e22Sbostic { 302273e1e22Sbostic eof_bell(); 303273e1e22Sbostic hit_eof++; 304273e1e22Sbostic return; 305273e1e22Sbostic } 306273e1e22Sbostic forw(n, pos, 0, only_last); 307273e1e22Sbostic } 308273e1e22Sbostic 309273e1e22Sbostic /* 310273e1e22Sbostic * Display n more lines, backward. 311273e1e22Sbostic * Start just before the line currently displayed at the top of the screen. 312273e1e22Sbostic */ 313273e1e22Sbostic public void 314273e1e22Sbostic backward(n, only_last) 315273e1e22Sbostic int n; 316273e1e22Sbostic int only_last; 317273e1e22Sbostic { 318273e1e22Sbostic POSITION pos; 319273e1e22Sbostic 320273e1e22Sbostic pos = position(TOP); 321273e1e22Sbostic if (pos == NULL_POSITION) 322273e1e22Sbostic { 323273e1e22Sbostic /* 324273e1e22Sbostic * This will almost never happen, 325273e1e22Sbostic * because the top line is almost never empty. 326273e1e22Sbostic */ 327273e1e22Sbostic eof_bell(); 328273e1e22Sbostic return; 329273e1e22Sbostic } 330273e1e22Sbostic back(n, pos, 0, only_last); 331273e1e22Sbostic } 332273e1e22Sbostic 333273e1e22Sbostic /* 334273e1e22Sbostic * Repaint the screen, starting from a specified position. 335273e1e22Sbostic */ 336273e1e22Sbostic static void 337273e1e22Sbostic prepaint(pos) 338273e1e22Sbostic POSITION pos; 339273e1e22Sbostic { 340273e1e22Sbostic hit_eof = 0; 341273e1e22Sbostic forw(sc_height-1, pos, 1, 0); 342273e1e22Sbostic screen_trashed = 0; 343273e1e22Sbostic } 344273e1e22Sbostic 345273e1e22Sbostic /* 346273e1e22Sbostic * Repaint the screen. 347273e1e22Sbostic */ 348273e1e22Sbostic public void 349273e1e22Sbostic repaint() 350273e1e22Sbostic { 351273e1e22Sbostic /* 352273e1e22Sbostic * Start at the line currently at the top of the screen 353273e1e22Sbostic * and redisplay the screen. 354273e1e22Sbostic */ 355273e1e22Sbostic prepaint(position(TOP)); 356273e1e22Sbostic } 357273e1e22Sbostic 358273e1e22Sbostic /* 359273e1e22Sbostic * Jump to the end of the file. 360273e1e22Sbostic * It is more convenient to paint the screen backward, 361273e1e22Sbostic * from the end of the file toward the beginning. 362273e1e22Sbostic */ 363273e1e22Sbostic public void 364273e1e22Sbostic jump_forw() 365273e1e22Sbostic { 366273e1e22Sbostic POSITION pos; 367273e1e22Sbostic 368273e1e22Sbostic if (ch_end_seek()) 369273e1e22Sbostic { 370273e1e22Sbostic error("Cannot seek to end of file"); 371273e1e22Sbostic return; 372273e1e22Sbostic } 373273e1e22Sbostic lastmark(); 374273e1e22Sbostic pos = ch_tell(); 375273e1e22Sbostic clear(); 376273e1e22Sbostic pos_clear(); 377273e1e22Sbostic add_back_pos(pos); 378273e1e22Sbostic back(sc_height - 1, pos, 0, 0); 379273e1e22Sbostic } 380273e1e22Sbostic 381273e1e22Sbostic /* 382273e1e22Sbostic * Jump to line n in the file. 383273e1e22Sbostic */ 384273e1e22Sbostic public void 385273e1e22Sbostic jump_back(n) 386273e1e22Sbostic register int n; 387273e1e22Sbostic { 388273e1e22Sbostic register int c; 389273e1e22Sbostic int nlines; 390273e1e22Sbostic 391273e1e22Sbostic /* 392273e1e22Sbostic * This is done the slow way, by starting at the beginning 393273e1e22Sbostic * of the file and counting newlines. 394273e1e22Sbostic * 395273e1e22Sbostic * {{ Now that we have line numbering (in linenum.c), 396273e1e22Sbostic * we could improve on this by starting at the 397273e1e22Sbostic * nearest known line rather than at the beginning. }} 398273e1e22Sbostic */ 399273e1e22Sbostic if (ch_seek((POSITION)0)) 400273e1e22Sbostic { 401273e1e22Sbostic /* 402273e1e22Sbostic * Probably a pipe with beginning of file no longer buffered. 403273e1e22Sbostic * If he wants to go to line 1, we do the best we can, 404273e1e22Sbostic * by going to the first line which is still buffered. 405273e1e22Sbostic */ 406273e1e22Sbostic if (n <= 1 && ch_beg_seek() == 0) 407273e1e22Sbostic jump_loc(ch_tell()); 408273e1e22Sbostic error("Cannot get to beginning of file"); 409273e1e22Sbostic return; 410273e1e22Sbostic } 411273e1e22Sbostic 412273e1e22Sbostic /* 413273e1e22Sbostic * Start counting lines. 414273e1e22Sbostic */ 415273e1e22Sbostic for (nlines = 1; nlines < n; nlines++) 416273e1e22Sbostic { 417273e1e22Sbostic while ((c = ch_forw_get()) != '\n') 418273e1e22Sbostic if (c == EOI) 419273e1e22Sbostic { 420273e1e22Sbostic char message[40]; 421*8033092dSbostic (void)sprintf(message, "File has only %d lines", 422273e1e22Sbostic nlines-1); 423273e1e22Sbostic error(message); 424273e1e22Sbostic return; 425273e1e22Sbostic } 426273e1e22Sbostic } 427273e1e22Sbostic 428273e1e22Sbostic jump_loc(ch_tell()); 429273e1e22Sbostic } 430273e1e22Sbostic 431273e1e22Sbostic /* 432273e1e22Sbostic * Jump to a specified percentage into the file. 433273e1e22Sbostic * This is a poor compensation for not being able to 434273e1e22Sbostic * quickly jump to a specific line number. 435273e1e22Sbostic */ 436273e1e22Sbostic public void 437273e1e22Sbostic jump_percent(percent) 438273e1e22Sbostic int percent; 439273e1e22Sbostic { 440273e1e22Sbostic POSITION pos, len; 441273e1e22Sbostic register int c; 442273e1e22Sbostic 443273e1e22Sbostic /* 444273e1e22Sbostic * Determine the position in the file 445273e1e22Sbostic * (the specified percentage of the file's length). 446273e1e22Sbostic */ 447273e1e22Sbostic if ((len = ch_length()) == NULL_POSITION) 448273e1e22Sbostic { 449273e1e22Sbostic error("Don't know length of file"); 450273e1e22Sbostic return; 451273e1e22Sbostic } 452273e1e22Sbostic pos = (percent * len) / 100; 453273e1e22Sbostic 454273e1e22Sbostic /* 455273e1e22Sbostic * Back up to the beginning of the line. 456273e1e22Sbostic */ 457273e1e22Sbostic if (ch_seek(pos) == 0) 458273e1e22Sbostic { 459273e1e22Sbostic while ((c = ch_back_get()) != '\n' && c != EOI) 460273e1e22Sbostic ; 461273e1e22Sbostic if (c == '\n') 462273e1e22Sbostic (void) ch_forw_get(); 463273e1e22Sbostic pos = ch_tell(); 464273e1e22Sbostic } 465273e1e22Sbostic jump_loc(pos); 466273e1e22Sbostic } 467273e1e22Sbostic 468273e1e22Sbostic /* 469273e1e22Sbostic * Jump to a specified position in the file. 470273e1e22Sbostic */ 471273e1e22Sbostic public void 472273e1e22Sbostic jump_loc(pos) 473273e1e22Sbostic POSITION pos; 474273e1e22Sbostic { 475273e1e22Sbostic register int nline; 476273e1e22Sbostic POSITION tpos; 477273e1e22Sbostic 478273e1e22Sbostic if ((nline = onscreen(pos)) >= 0) 479273e1e22Sbostic { 480273e1e22Sbostic /* 481273e1e22Sbostic * The line is currently displayed. 482273e1e22Sbostic * Just scroll there. 483273e1e22Sbostic */ 484273e1e22Sbostic forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); 485273e1e22Sbostic return; 486273e1e22Sbostic } 487273e1e22Sbostic 488273e1e22Sbostic /* 489273e1e22Sbostic * Line is not on screen. 490273e1e22Sbostic * Seek to the desired location. 491273e1e22Sbostic */ 492273e1e22Sbostic if (ch_seek(pos)) 493273e1e22Sbostic { 494273e1e22Sbostic error("Cannot seek to that position"); 495273e1e22Sbostic return; 496273e1e22Sbostic } 497273e1e22Sbostic 498273e1e22Sbostic /* 499273e1e22Sbostic * See if the desired line is BEFORE the currently 500273e1e22Sbostic * displayed screen. If so, then move forward far 501273e1e22Sbostic * enough so the line we're on will be at the bottom 502273e1e22Sbostic * of the screen, in order to be able to call back() 503273e1e22Sbostic * to make the screen scroll backwards & put the line 504273e1e22Sbostic * at the top of the screen. 505273e1e22Sbostic * {{ This seems inefficient, but it's not so bad, 506273e1e22Sbostic * since we can never move forward more than a 507273e1e22Sbostic * screenful before we stop to redraw the screen. }} 508273e1e22Sbostic */ 509273e1e22Sbostic tpos = position(TOP); 510273e1e22Sbostic if (tpos != NULL_POSITION && pos < tpos) 511273e1e22Sbostic { 512273e1e22Sbostic POSITION npos = pos; 513273e1e22Sbostic /* 514273e1e22Sbostic * Note that we can't forw_line() past tpos here, 515273e1e22Sbostic * so there should be no EOI at this stage. 516273e1e22Sbostic */ 517273e1e22Sbostic for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) 518273e1e22Sbostic npos = forw_line(npos); 519273e1e22Sbostic 520273e1e22Sbostic if (npos < tpos) 521273e1e22Sbostic { 522273e1e22Sbostic /* 523273e1e22Sbostic * More than a screenful back. 524273e1e22Sbostic */ 525273e1e22Sbostic lastmark(); 526273e1e22Sbostic clear(); 527273e1e22Sbostic pos_clear(); 528273e1e22Sbostic add_back_pos(npos); 529273e1e22Sbostic } 530273e1e22Sbostic 531273e1e22Sbostic /* 532273e1e22Sbostic * Note that back() will repaint() if nline > back_scroll. 533273e1e22Sbostic */ 534273e1e22Sbostic back(nline, npos, 1, 0); 535273e1e22Sbostic return; 536273e1e22Sbostic } 537273e1e22Sbostic /* 538273e1e22Sbostic * Remember where we were; clear and paint the screen. 539273e1e22Sbostic */ 540273e1e22Sbostic lastmark(); 541273e1e22Sbostic prepaint(pos); 542273e1e22Sbostic } 543273e1e22Sbostic 544273e1e22Sbostic /* 545273e1e22Sbostic * The table of marks. 546273e1e22Sbostic * A mark is simply a position in the file. 547273e1e22Sbostic */ 548273e1e22Sbostic #define NMARKS (27) /* 26 for a-z plus one for quote */ 549273e1e22Sbostic #define LASTMARK (NMARKS-1) /* For quote */ 550273e1e22Sbostic static POSITION marks[NMARKS]; 551273e1e22Sbostic 552273e1e22Sbostic /* 553273e1e22Sbostic * Initialize the mark table to show no marks are set. 554273e1e22Sbostic */ 555273e1e22Sbostic public void 556273e1e22Sbostic init_mark() 557273e1e22Sbostic { 558273e1e22Sbostic int i; 559273e1e22Sbostic 560273e1e22Sbostic for (i = 0; i < NMARKS; i++) 561273e1e22Sbostic marks[i] = NULL_POSITION; 562273e1e22Sbostic } 563273e1e22Sbostic 564273e1e22Sbostic /* 565273e1e22Sbostic * See if a mark letter is valid (between a and z). 566273e1e22Sbostic */ 567273e1e22Sbostic static int 568273e1e22Sbostic badmark(c) 569273e1e22Sbostic int c; 570273e1e22Sbostic { 571273e1e22Sbostic if (c < 'a' || c > 'z') 572273e1e22Sbostic { 573273e1e22Sbostic error("Choose a letter between 'a' and 'z'"); 574273e1e22Sbostic return (1); 575273e1e22Sbostic } 576273e1e22Sbostic return (0); 577273e1e22Sbostic } 578273e1e22Sbostic 579273e1e22Sbostic /* 580273e1e22Sbostic * Set a mark. 581273e1e22Sbostic */ 582273e1e22Sbostic public void 583273e1e22Sbostic setmark(c) 584273e1e22Sbostic int c; 585273e1e22Sbostic { 586273e1e22Sbostic if (badmark(c)) 587273e1e22Sbostic return; 588273e1e22Sbostic marks[c-'a'] = position(TOP); 589273e1e22Sbostic } 590273e1e22Sbostic 591273e1e22Sbostic public void 592273e1e22Sbostic lastmark() 593273e1e22Sbostic { 594273e1e22Sbostic marks[LASTMARK] = position(TOP); 595273e1e22Sbostic } 596273e1e22Sbostic 597273e1e22Sbostic /* 598273e1e22Sbostic * Go to a previously set mark. 599273e1e22Sbostic */ 600273e1e22Sbostic public void 601273e1e22Sbostic gomark(c) 602273e1e22Sbostic int c; 603273e1e22Sbostic { 604273e1e22Sbostic POSITION pos; 605273e1e22Sbostic 606273e1e22Sbostic if (c == '\'') 607273e1e22Sbostic pos = marks[LASTMARK]; 608273e1e22Sbostic else if (badmark(c)) 609273e1e22Sbostic return; 610273e1e22Sbostic else 611273e1e22Sbostic pos = marks[c-'a']; 612273e1e22Sbostic 613273e1e22Sbostic if (pos == NULL_POSITION) 614273e1e22Sbostic error("mark not set"); 615273e1e22Sbostic else 616273e1e22Sbostic jump_loc(pos); 617273e1e22Sbostic } 618273e1e22Sbostic 619273e1e22Sbostic /* 620273e1e22Sbostic * Get the backwards scroll limit. 621273e1e22Sbostic * Must call this function instead of just using the value of 622273e1e22Sbostic * back_scroll, because the default case depends on sc_height and 623273e1e22Sbostic * top_scroll, as well as back_scroll. 624273e1e22Sbostic */ 625273e1e22Sbostic public int 626273e1e22Sbostic get_back_scroll() 627273e1e22Sbostic { 628273e1e22Sbostic if (back_scroll >= 0) 629273e1e22Sbostic return (back_scroll); 630273e1e22Sbostic if (top_scroll) 631273e1e22Sbostic return (sc_height - 2); 632273e1e22Sbostic return (sc_height - 1); 633273e1e22Sbostic } 634273e1e22Sbostic 635273e1e22Sbostic /* 636273e1e22Sbostic * Search for the n-th occurence of a specified pattern, 637273e1e22Sbostic * either forward or backward. 638273e1e22Sbostic */ 639273e1e22Sbostic public void 640273e1e22Sbostic search(search_forward, pattern, n, wantmatch) 641273e1e22Sbostic register int search_forward; 642273e1e22Sbostic register char *pattern; 643273e1e22Sbostic register int n; 644273e1e22Sbostic int wantmatch; 645273e1e22Sbostic { 646273e1e22Sbostic POSITION pos, linepos; 647273e1e22Sbostic register char *p; 648273e1e22Sbostic register char *q; 649273e1e22Sbostic int linenum; 650273e1e22Sbostic int linematch; 651273e1e22Sbostic #if RECOMP 652273e1e22Sbostic char *re_comp(); 653273e1e22Sbostic char *errmsg; 654273e1e22Sbostic #else 655273e1e22Sbostic #if REGCMP 656273e1e22Sbostic char *regcmp(); 657273e1e22Sbostic static char *cpattern = NULL; 658273e1e22Sbostic #else 659273e1e22Sbostic static char lpbuf[100]; 660273e1e22Sbostic static char *last_pattern = NULL; 661*8033092dSbostic char *strcpy(); 662273e1e22Sbostic #endif 663273e1e22Sbostic #endif 664273e1e22Sbostic 665273e1e22Sbostic if (caseless && pattern != NULL) 666273e1e22Sbostic { 667273e1e22Sbostic /* 668273e1e22Sbostic * For a caseless search, convert any uppercase 669273e1e22Sbostic * in the pattern to lowercase. 670273e1e22Sbostic */ 671273e1e22Sbostic for (p = pattern; *p != '\0'; p++) 672273e1e22Sbostic if (*p >= 'A' && *p <= 'Z') 673273e1e22Sbostic *p += 'a' - 'A'; 674273e1e22Sbostic } 675273e1e22Sbostic #if RECOMP 676273e1e22Sbostic 677273e1e22Sbostic /* 678273e1e22Sbostic * (re_comp handles a null pattern internally, 679273e1e22Sbostic * so there is no need to check for a null pattern here.) 680273e1e22Sbostic */ 681273e1e22Sbostic if ((errmsg = re_comp(pattern)) != NULL) 682273e1e22Sbostic { 683273e1e22Sbostic error(errmsg); 684273e1e22Sbostic return; 685273e1e22Sbostic } 686273e1e22Sbostic #else 687273e1e22Sbostic #if REGCMP 688273e1e22Sbostic if (pattern == NULL || *pattern == '\0') 689273e1e22Sbostic { 690273e1e22Sbostic /* 691273e1e22Sbostic * A null pattern means use the previous pattern. 692273e1e22Sbostic * The compiled previous pattern is in cpattern, so just use it. 693273e1e22Sbostic */ 694273e1e22Sbostic if (cpattern == NULL) 695273e1e22Sbostic { 696273e1e22Sbostic error("No previous regular expression"); 697273e1e22Sbostic return; 698273e1e22Sbostic } 699273e1e22Sbostic } else 700273e1e22Sbostic { 701273e1e22Sbostic /* 702273e1e22Sbostic * Otherwise compile the given pattern. 703273e1e22Sbostic */ 704273e1e22Sbostic char *s; 705273e1e22Sbostic if ((s = regcmp(pattern, 0)) == NULL) 706273e1e22Sbostic { 707273e1e22Sbostic error("Invalid pattern"); 708273e1e22Sbostic return; 709273e1e22Sbostic } 710273e1e22Sbostic if (cpattern != NULL) 711273e1e22Sbostic free(cpattern); 712273e1e22Sbostic cpattern = s; 713273e1e22Sbostic } 714273e1e22Sbostic #else 715273e1e22Sbostic if (pattern == NULL || *pattern == '\0') 716273e1e22Sbostic { 717273e1e22Sbostic /* 718273e1e22Sbostic * Null pattern means use the previous pattern. 719273e1e22Sbostic */ 720273e1e22Sbostic if (last_pattern == NULL) 721273e1e22Sbostic { 722273e1e22Sbostic error("No previous regular expression"); 723273e1e22Sbostic return; 724273e1e22Sbostic } 725273e1e22Sbostic pattern = last_pattern; 726273e1e22Sbostic } else 727273e1e22Sbostic { 728*8033092dSbostic (void)strcpy(lpbuf, pattern); 729273e1e22Sbostic last_pattern = lpbuf; 730273e1e22Sbostic } 731273e1e22Sbostic #endif 732273e1e22Sbostic #endif 733273e1e22Sbostic 734273e1e22Sbostic /* 735273e1e22Sbostic * Figure out where to start the search. 736273e1e22Sbostic */ 737273e1e22Sbostic 738273e1e22Sbostic if (position(TOP) == NULL_POSITION) 739273e1e22Sbostic { 740273e1e22Sbostic /* 741273e1e22Sbostic * Nothing is currently displayed. 742273e1e22Sbostic * Start at the beginning of the file. 743273e1e22Sbostic * (This case is mainly for first_cmd searches, 744273e1e22Sbostic * for example, "+/xyz" on the command line.) 745273e1e22Sbostic */ 746273e1e22Sbostic pos = (POSITION)0; 747273e1e22Sbostic } else if (!search_forward) 748273e1e22Sbostic { 749273e1e22Sbostic /* 750273e1e22Sbostic * Backward search: start just before the top line 751273e1e22Sbostic * displayed on the screen. 752273e1e22Sbostic */ 753273e1e22Sbostic pos = position(TOP); 754273e1e22Sbostic } else if (how_search == 0) 755273e1e22Sbostic { 756273e1e22Sbostic /* 757273e1e22Sbostic * Start at the second real line displayed on the screen. 758273e1e22Sbostic */ 759273e1e22Sbostic pos = position(TOP); 760273e1e22Sbostic do 761273e1e22Sbostic pos = forw_raw_line(pos); 762273e1e22Sbostic while (pos < position(TOP+1)); 763273e1e22Sbostic } else if (how_search == 1) 764273e1e22Sbostic { 765273e1e22Sbostic /* 766273e1e22Sbostic * Start just after the bottom line displayed on the screen. 767273e1e22Sbostic */ 768273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE); 769273e1e22Sbostic } else 770273e1e22Sbostic { 771273e1e22Sbostic /* 772273e1e22Sbostic * Start at the second screen line displayed on the screen. 773273e1e22Sbostic */ 774273e1e22Sbostic pos = position(TOP_PLUS_ONE); 775273e1e22Sbostic } 776273e1e22Sbostic 777273e1e22Sbostic if (pos == NULL_POSITION) 778273e1e22Sbostic { 779273e1e22Sbostic /* 780273e1e22Sbostic * Can't find anyplace to start searching from. 781273e1e22Sbostic */ 782273e1e22Sbostic error("Nothing to search"); 783273e1e22Sbostic return; 784273e1e22Sbostic } 785273e1e22Sbostic 786273e1e22Sbostic linenum = find_linenum(pos); 787273e1e22Sbostic for (;;) 788273e1e22Sbostic { 789273e1e22Sbostic /* 790273e1e22Sbostic * Get lines until we find a matching one or 791273e1e22Sbostic * until we hit end-of-file (or beginning-of-file 792273e1e22Sbostic * if we're going backwards). 793273e1e22Sbostic */ 794273e1e22Sbostic if (sigs) 795273e1e22Sbostic /* 796273e1e22Sbostic * A signal aborts the search. 797273e1e22Sbostic */ 798273e1e22Sbostic return; 799273e1e22Sbostic 800273e1e22Sbostic if (search_forward) 801273e1e22Sbostic { 802273e1e22Sbostic /* 803273e1e22Sbostic * Read the next line, and save the 804273e1e22Sbostic * starting position of that line in linepos. 805273e1e22Sbostic */ 806273e1e22Sbostic linepos = pos; 807273e1e22Sbostic pos = forw_raw_line(pos); 808273e1e22Sbostic if (linenum != 0) 809273e1e22Sbostic linenum++; 810273e1e22Sbostic } else 811273e1e22Sbostic { 812273e1e22Sbostic /* 813273e1e22Sbostic * Read the previous line and save the 814273e1e22Sbostic * starting position of that line in linepos. 815273e1e22Sbostic */ 816273e1e22Sbostic pos = back_raw_line(pos); 817273e1e22Sbostic linepos = pos; 818273e1e22Sbostic if (linenum != 0) 819273e1e22Sbostic linenum--; 820273e1e22Sbostic } 821273e1e22Sbostic 822273e1e22Sbostic if (pos == NULL_POSITION) 823273e1e22Sbostic { 824273e1e22Sbostic /* 825273e1e22Sbostic * We hit EOF/BOF without a match. 826273e1e22Sbostic */ 827273e1e22Sbostic error("Pattern not found"); 828273e1e22Sbostic return; 829273e1e22Sbostic } 830273e1e22Sbostic 831273e1e22Sbostic /* 832273e1e22Sbostic * If we're using line numbers, we might as well 833273e1e22Sbostic * remember the information we have now (the position 834273e1e22Sbostic * and line number of the current line). 835273e1e22Sbostic */ 836273e1e22Sbostic if (linenums) 837273e1e22Sbostic add_lnum(linenum, pos); 838273e1e22Sbostic 839273e1e22Sbostic if (caseless) 840273e1e22Sbostic { 841273e1e22Sbostic /* 842273e1e22Sbostic * If this is a caseless search, convert 843273e1e22Sbostic * uppercase in the input line to lowercase. 844273e1e22Sbostic * While we're at it, remove any backspaces 845273e1e22Sbostic * along with the preceeding char. 846273e1e22Sbostic * This allows us to match text which is 847273e1e22Sbostic * underlined or overstruck. 848273e1e22Sbostic */ 849273e1e22Sbostic for (p = q = line; *p != '\0'; p++, q++) 850273e1e22Sbostic { 851273e1e22Sbostic if (*p >= 'A' && *p <= 'Z') 852273e1e22Sbostic /* Convert uppercase to lowercase. */ 853273e1e22Sbostic *q = *p + 'a' - 'A'; 854273e1e22Sbostic else if (q > line && *p == '\b') 855273e1e22Sbostic /* Delete BS and preceeding char. */ 856273e1e22Sbostic q -= 2; 857273e1e22Sbostic else 858273e1e22Sbostic /* Otherwise, just copy. */ 859273e1e22Sbostic *q = *p; 860273e1e22Sbostic } 861273e1e22Sbostic } 862273e1e22Sbostic 863273e1e22Sbostic /* 864273e1e22Sbostic * Test the next line to see if we have a match. 865273e1e22Sbostic * This is done in a variety of ways, depending 866273e1e22Sbostic * on what pattern matching functions are available. 867273e1e22Sbostic */ 868273e1e22Sbostic #if REGCMP 869273e1e22Sbostic linematch = (regex(cpattern, line) != NULL); 870273e1e22Sbostic #else 871273e1e22Sbostic #if RECOMP 872273e1e22Sbostic linematch = (re_exec(line) == 1); 873273e1e22Sbostic #else 874273e1e22Sbostic linematch = match(pattern, line); 875273e1e22Sbostic #endif 876273e1e22Sbostic #endif 877273e1e22Sbostic /* 878273e1e22Sbostic * We are successful if wantmatch and linematch are 879273e1e22Sbostic * both true (want a match and got it), 880273e1e22Sbostic * or both false (want a non-match and got it). 881273e1e22Sbostic */ 882273e1e22Sbostic if (((wantmatch && linematch) || (!wantmatch && !linematch)) && 883273e1e22Sbostic --n <= 0) 884273e1e22Sbostic /* 885273e1e22Sbostic * Found the line. 886273e1e22Sbostic */ 887273e1e22Sbostic break; 888273e1e22Sbostic } 889273e1e22Sbostic 890273e1e22Sbostic jump_loc(linepos); 891273e1e22Sbostic } 892273e1e22Sbostic 893273e1e22Sbostic #if (!REGCMP) && (!RECOMP) 894273e1e22Sbostic /* 895273e1e22Sbostic * We have neither regcmp() nor re_comp(). 896273e1e22Sbostic * We use this function to do simple pattern matching. 897273e1e22Sbostic * It supports no metacharacters like *, etc. 898273e1e22Sbostic */ 899273e1e22Sbostic static int 900273e1e22Sbostic match(pattern, buf) 901273e1e22Sbostic char *pattern, *buf; 902273e1e22Sbostic { 903273e1e22Sbostic register char *pp, *lp; 904273e1e22Sbostic 905273e1e22Sbostic for ( ; *buf != '\0'; buf++) 906273e1e22Sbostic { 907273e1e22Sbostic for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) 908273e1e22Sbostic if (*pp == '\0' || *lp == '\0') 909273e1e22Sbostic break; 910273e1e22Sbostic if (*pp == '\0') 911273e1e22Sbostic return (1); 912273e1e22Sbostic } 913273e1e22Sbostic return (0); 914273e1e22Sbostic } 915273e1e22Sbostic #endif 916