1273e1e22Sbostic /* 2273e1e22Sbostic * Copyright (c) 1988 Mark Nudleman 3273e1e22Sbostic * Copyright (c) 1988 Regents of the University of California. 4273e1e22Sbostic * All rights reserved. 5273e1e22Sbostic * 6273e1e22Sbostic * Redistribution and use in source and binary forms are permitted 7273e1e22Sbostic * provided that the above copyright notice and this paragraph are 8273e1e22Sbostic * duplicated in all such forms and that any documentation, 9273e1e22Sbostic * advertising materials, and other materials related to such 10273e1e22Sbostic * distribution and use acknowledge that the software was developed 1194cb6cb2Sbostic * by Mark Nudleman and the University of California, Berkeley. The 1294cb6cb2Sbostic * name of Mark Nudleman or the 13273e1e22Sbostic * University may not be used to endorse or promote products derived 14273e1e22Sbostic * from this software without specific prior written permission. 15273e1e22Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16273e1e22Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17273e1e22Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18273e1e22Sbostic */ 19273e1e22Sbostic 20273e1e22Sbostic #ifndef lint 21*427e68f0Sbostic static char sccsid[] = "@(#)prim.c 5.5 (Berkeley) 11/22/88"; 22273e1e22Sbostic #endif /* not lint */ 23273e1e22Sbostic 24273e1e22Sbostic /* 25273e1e22Sbostic * Primitives for displaying the file on the screen. 26273e1e22Sbostic */ 27273e1e22Sbostic 28*427e68f0Sbostic #include <sys/types.h> 29*427e68f0Sbostic #include <stdio.h> 30*427e68f0Sbostic #include <ctype.h> 31*427e68f0Sbostic #include <less.h> 32273e1e22Sbostic 33*427e68f0Sbostic int back_scroll = -1; 34*427e68f0Sbostic int hit_eof; /* keeps track of how many times we hit end of file */ 35*427e68f0Sbostic int screen_trashed; 36273e1e22Sbostic 37273e1e22Sbostic static int squished; 38273e1e22Sbostic 39273e1e22Sbostic extern int sigs; 40273e1e22Sbostic extern int top_scroll; 41273e1e22Sbostic extern int sc_width, sc_height; 42273e1e22Sbostic extern int caseless; 43273e1e22Sbostic extern int linenums; 44273e1e22Sbostic extern int tagoption; 45*427e68f0Sbostic extern char *line; 46273e1e22Sbostic 47*427e68f0Sbostic off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line(); 48*427e68f0Sbostic off_t ch_length(), ch_tell(); 49273e1e22Sbostic 50273e1e22Sbostic /* 51273e1e22Sbostic * Check to see if the end of file is currently "displayed". 52273e1e22Sbostic */ 53273e1e22Sbostic eof_check() 54273e1e22Sbostic { 55*427e68f0Sbostic off_t pos; 56273e1e22Sbostic 57273e1e22Sbostic if (sigs) 58273e1e22Sbostic return; 59273e1e22Sbostic /* 60273e1e22Sbostic * If the bottom line is empty, we are at EOF. 61273e1e22Sbostic * If the bottom line ends at the file length, 62273e1e22Sbostic * we must be just at EOF. 63273e1e22Sbostic */ 64273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE); 65273e1e22Sbostic if (pos == NULL_POSITION || pos == ch_length()) 66273e1e22Sbostic hit_eof++; 67273e1e22Sbostic } 68273e1e22Sbostic 69273e1e22Sbostic /* 70273e1e22Sbostic * If the screen is "squished", repaint it. 71273e1e22Sbostic * "Squished" means the first displayed line is not at the top 72273e1e22Sbostic * of the screen; this can happen when we display a short file 73273e1e22Sbostic * for the first time. 74273e1e22Sbostic */ 75273e1e22Sbostic squish_check() 76273e1e22Sbostic { 77*427e68f0Sbostic if (squished) { 78273e1e22Sbostic squished = 0; 79273e1e22Sbostic repaint(); 80273e1e22Sbostic } 81*427e68f0Sbostic } 82273e1e22Sbostic 83273e1e22Sbostic /* 84*427e68f0Sbostic * Display n lines, scrolling forward, starting at position pos in the 85*427e68f0Sbostic * input file. "force" means display the n lines even if we hit end of 86*427e68f0Sbostic * file. "only_last" means display only the last screenful if n > screen 87*427e68f0Sbostic * size. 88273e1e22Sbostic */ 89273e1e22Sbostic forw(n, pos, force, only_last) 90273e1e22Sbostic register int n; 91*427e68f0Sbostic off_t pos; 92273e1e22Sbostic int force; 93273e1e22Sbostic int only_last; 94273e1e22Sbostic { 95273e1e22Sbostic static int first_time = 1; 96*427e68f0Sbostic int eof = 0, do_repaint; 97273e1e22Sbostic 98273e1e22Sbostic squish_check(); 99273e1e22Sbostic 100273e1e22Sbostic /* 101273e1e22Sbostic * do_repaint tells us not to display anything till the end, 102273e1e22Sbostic * then just repaint the entire screen. 103273e1e22Sbostic */ 104273e1e22Sbostic do_repaint = (only_last && n > sc_height-1); 105273e1e22Sbostic 106*427e68f0Sbostic if (!do_repaint) { 107*427e68f0Sbostic if (top_scroll && n >= sc_height - 1) { 108273e1e22Sbostic /* 109273e1e22Sbostic * Start a new screen. 110273e1e22Sbostic * {{ This is not really desirable if we happen 111273e1e22Sbostic * to hit eof in the middle of this screen, 112273e1e22Sbostic * but we don't yet know if that will happen. }} 113273e1e22Sbostic */ 114273e1e22Sbostic clear(); 115273e1e22Sbostic home(); 116273e1e22Sbostic force = 1; 117*427e68f0Sbostic } else { 118273e1e22Sbostic lower_left(); 119273e1e22Sbostic clear_eol(); 120273e1e22Sbostic } 121273e1e22Sbostic 122273e1e22Sbostic /* 123*427e68f0Sbostic * This is not contiguous with what is currently displayed. 124*427e68f0Sbostic * Clear the screen image (position table) and start a new 125*427e68f0Sbostic * screen. 126273e1e22Sbostic */ 127*427e68f0Sbostic if (pos != position(BOTTOM_PLUS_ONE)) { 128273e1e22Sbostic pos_clear(); 129273e1e22Sbostic add_forw_pos(pos); 130273e1e22Sbostic force = 1; 131*427e68f0Sbostic if (top_scroll) { 132273e1e22Sbostic clear(); 133273e1e22Sbostic home(); 134273e1e22Sbostic } else if (!first_time) 135273e1e22Sbostic putstr("...skipping...\n"); 136273e1e22Sbostic } 137273e1e22Sbostic } 138273e1e22Sbostic 139*427e68f0Sbostic while (--n >= 0) { 140273e1e22Sbostic /* 141273e1e22Sbostic * Read the next line of input. 142273e1e22Sbostic */ 143273e1e22Sbostic pos = forw_line(pos); 144*427e68f0Sbostic if (pos == NULL_POSITION) { 145273e1e22Sbostic /* 146273e1e22Sbostic * End of file: stop here unless the top line 147273e1e22Sbostic * is still empty, or "force" is true. 148273e1e22Sbostic */ 149273e1e22Sbostic eof = 1; 150273e1e22Sbostic if (!force && position(TOP) != NULL_POSITION) 151273e1e22Sbostic break; 152273e1e22Sbostic line = NULL; 153273e1e22Sbostic } 154273e1e22Sbostic /* 155273e1e22Sbostic * Add the position of the next line to the position table. 156273e1e22Sbostic * Display the current line on the screen. 157273e1e22Sbostic */ 158273e1e22Sbostic add_forw_pos(pos); 159273e1e22Sbostic if (do_repaint) 160273e1e22Sbostic continue; 161273e1e22Sbostic /* 162*427e68f0Sbostic * If this is the first screen displayed and we hit an early 163*427e68f0Sbostic * EOF (i.e. before the requested number of lines), we 164*427e68f0Sbostic * "squish" the display down at the bottom of the screen. 165*427e68f0Sbostic * But don't do this if a -t option was given; it can cause 166*427e68f0Sbostic * us to start the display after the beginning of the file, 167273e1e22Sbostic * and it is not appropriate to squish in that case. 168273e1e22Sbostic */ 169*427e68f0Sbostic if (first_time && line == NULL && !top_scroll && !tagoption) { 170273e1e22Sbostic squished = 1; 171273e1e22Sbostic continue; 172273e1e22Sbostic } 173273e1e22Sbostic put_line(); 174273e1e22Sbostic } 175273e1e22Sbostic 176273e1e22Sbostic if (eof && !sigs) 177273e1e22Sbostic hit_eof++; 178273e1e22Sbostic else 179273e1e22Sbostic eof_check(); 180*427e68f0Sbostic if (do_repaint) 181273e1e22Sbostic repaint(); 182273e1e22Sbostic first_time = 0; 183273e1e22Sbostic (void) currline(BOTTOM); 184273e1e22Sbostic } 185273e1e22Sbostic 186273e1e22Sbostic /* 187273e1e22Sbostic * Display n lines, scrolling backward. 188273e1e22Sbostic */ 189273e1e22Sbostic back(n, pos, force, only_last) 190273e1e22Sbostic register int n; 191*427e68f0Sbostic off_t pos; 192273e1e22Sbostic int force; 193273e1e22Sbostic int only_last; 194273e1e22Sbostic { 195273e1e22Sbostic int do_repaint; 196273e1e22Sbostic 197273e1e22Sbostic squish_check(); 198273e1e22Sbostic do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); 199273e1e22Sbostic hit_eof = 0; 200273e1e22Sbostic while (--n >= 0) 201273e1e22Sbostic { 202273e1e22Sbostic /* 203273e1e22Sbostic * Get the previous line of input. 204273e1e22Sbostic */ 205273e1e22Sbostic pos = back_line(pos); 206273e1e22Sbostic if (pos == NULL_POSITION) 207273e1e22Sbostic { 208273e1e22Sbostic /* 209273e1e22Sbostic * Beginning of file: stop here unless "force" is true. 210273e1e22Sbostic */ 211273e1e22Sbostic if (!force) 212273e1e22Sbostic break; 213273e1e22Sbostic line = NULL; 214273e1e22Sbostic } 215273e1e22Sbostic /* 216273e1e22Sbostic * Add the position of the previous line to the position table. 217273e1e22Sbostic * Display the line on the screen. 218273e1e22Sbostic */ 219273e1e22Sbostic add_back_pos(pos); 220273e1e22Sbostic if (!do_repaint) 221273e1e22Sbostic { 222273e1e22Sbostic home(); 223273e1e22Sbostic add_line(); 224273e1e22Sbostic put_line(); 225273e1e22Sbostic } 226273e1e22Sbostic } 227273e1e22Sbostic 228273e1e22Sbostic eof_check(); 229*427e68f0Sbostic if (do_repaint) 230273e1e22Sbostic repaint(); 231273e1e22Sbostic (void) currline(BOTTOM); 232273e1e22Sbostic } 233273e1e22Sbostic 234273e1e22Sbostic /* 235273e1e22Sbostic * Display n more lines, forward. 236273e1e22Sbostic * Start just after the line currently displayed at the bottom of the screen. 237273e1e22Sbostic */ 238273e1e22Sbostic forward(n, only_last) 239273e1e22Sbostic int n; 240273e1e22Sbostic int only_last; 241273e1e22Sbostic { 242*427e68f0Sbostic off_t pos; 243273e1e22Sbostic 244*427e68f0Sbostic if (hit_eof) { 245273e1e22Sbostic /* 246*427e68f0Sbostic * If we're trying to go forward from end-of-file, 247*427e68f0Sbostic * go on to the next file. 248273e1e22Sbostic */ 249273e1e22Sbostic next_file(1); 250273e1e22Sbostic return; 251273e1e22Sbostic } 252273e1e22Sbostic 253273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE); 254273e1e22Sbostic if (pos == NULL_POSITION) 255273e1e22Sbostic { 256273e1e22Sbostic hit_eof++; 257273e1e22Sbostic return; 258273e1e22Sbostic } 259273e1e22Sbostic forw(n, pos, 0, only_last); 260273e1e22Sbostic } 261273e1e22Sbostic 262273e1e22Sbostic /* 263273e1e22Sbostic * Display n more lines, backward. 264273e1e22Sbostic * Start just before the line currently displayed at the top of the screen. 265273e1e22Sbostic */ 266273e1e22Sbostic backward(n, only_last) 267273e1e22Sbostic int n; 268273e1e22Sbostic int only_last; 269273e1e22Sbostic { 270*427e68f0Sbostic off_t pos; 271273e1e22Sbostic 272273e1e22Sbostic pos = position(TOP); 273273e1e22Sbostic /* 274*427e68f0Sbostic * This will almost never happen, because the top line is almost 275*427e68f0Sbostic * never empty. 276273e1e22Sbostic */ 277*427e68f0Sbostic if (pos == NULL_POSITION) 278273e1e22Sbostic return; 279273e1e22Sbostic back(n, pos, 0, only_last); 280273e1e22Sbostic } 281273e1e22Sbostic 282273e1e22Sbostic /* 283273e1e22Sbostic * Repaint the screen, starting from a specified position. 284273e1e22Sbostic */ 285273e1e22Sbostic prepaint(pos) 286*427e68f0Sbostic off_t pos; 287273e1e22Sbostic { 288273e1e22Sbostic hit_eof = 0; 289273e1e22Sbostic forw(sc_height-1, pos, 1, 0); 290273e1e22Sbostic screen_trashed = 0; 291273e1e22Sbostic } 292273e1e22Sbostic 293273e1e22Sbostic /* 294273e1e22Sbostic * Repaint the screen. 295273e1e22Sbostic */ 296273e1e22Sbostic repaint() 297273e1e22Sbostic { 298273e1e22Sbostic /* 299273e1e22Sbostic * Start at the line currently at the top of the screen 300273e1e22Sbostic * and redisplay the screen. 301273e1e22Sbostic */ 302273e1e22Sbostic prepaint(position(TOP)); 303273e1e22Sbostic } 304273e1e22Sbostic 305273e1e22Sbostic /* 306273e1e22Sbostic * Jump to the end of the file. 307273e1e22Sbostic * It is more convenient to paint the screen backward, 308273e1e22Sbostic * from the end of the file toward the beginning. 309273e1e22Sbostic */ 310273e1e22Sbostic jump_forw() 311273e1e22Sbostic { 312*427e68f0Sbostic off_t pos; 313273e1e22Sbostic 314273e1e22Sbostic if (ch_end_seek()) 315273e1e22Sbostic { 316273e1e22Sbostic error("Cannot seek to end of file"); 317273e1e22Sbostic return; 318273e1e22Sbostic } 319273e1e22Sbostic lastmark(); 320273e1e22Sbostic pos = ch_tell(); 321273e1e22Sbostic clear(); 322273e1e22Sbostic pos_clear(); 323273e1e22Sbostic add_back_pos(pos); 324273e1e22Sbostic back(sc_height - 1, pos, 0, 0); 325273e1e22Sbostic } 326273e1e22Sbostic 327273e1e22Sbostic /* 328273e1e22Sbostic * Jump to line n in the file. 329273e1e22Sbostic */ 330273e1e22Sbostic jump_back(n) 331273e1e22Sbostic register int n; 332273e1e22Sbostic { 333*427e68f0Sbostic register int c, nlines; 334273e1e22Sbostic 335273e1e22Sbostic /* 336273e1e22Sbostic * This is done the slow way, by starting at the beginning 337273e1e22Sbostic * of the file and counting newlines. 338273e1e22Sbostic * 339273e1e22Sbostic * {{ Now that we have line numbering (in linenum.c), 340273e1e22Sbostic * we could improve on this by starting at the 341273e1e22Sbostic * nearest known line rather than at the beginning. }} 342273e1e22Sbostic */ 343*427e68f0Sbostic if (ch_seek((off_t)0)) { 344273e1e22Sbostic /* 345273e1e22Sbostic * Probably a pipe with beginning of file no longer buffered. 346273e1e22Sbostic * If he wants to go to line 1, we do the best we can, 347273e1e22Sbostic * by going to the first line which is still buffered. 348273e1e22Sbostic */ 349273e1e22Sbostic if (n <= 1 && ch_beg_seek() == 0) 350273e1e22Sbostic jump_loc(ch_tell()); 351273e1e22Sbostic error("Cannot get to beginning of file"); 352273e1e22Sbostic return; 353273e1e22Sbostic } 354273e1e22Sbostic 355273e1e22Sbostic /* 356273e1e22Sbostic * Start counting lines. 357273e1e22Sbostic */ 358273e1e22Sbostic for (nlines = 1; nlines < n; nlines++) 359273e1e22Sbostic while ((c = ch_forw_get()) != '\n') 360*427e68f0Sbostic if (c == EOI) { 361273e1e22Sbostic char message[40]; 3628033092dSbostic (void)sprintf(message, "File has only %d lines", 363273e1e22Sbostic nlines - 1); 364273e1e22Sbostic error(message); 365273e1e22Sbostic return; 366273e1e22Sbostic } 367273e1e22Sbostic jump_loc(ch_tell()); 368273e1e22Sbostic } 369273e1e22Sbostic 370273e1e22Sbostic /* 371273e1e22Sbostic * Jump to a specified percentage into the file. 372273e1e22Sbostic * This is a poor compensation for not being able to 373273e1e22Sbostic * quickly jump to a specific line number. 374273e1e22Sbostic */ 375273e1e22Sbostic jump_percent(percent) 376273e1e22Sbostic int percent; 377273e1e22Sbostic { 378*427e68f0Sbostic off_t pos, len, ch_length(); 379273e1e22Sbostic register int c; 380273e1e22Sbostic 381273e1e22Sbostic /* 382273e1e22Sbostic * Determine the position in the file 383273e1e22Sbostic * (the specified percentage of the file's length). 384273e1e22Sbostic */ 385273e1e22Sbostic if ((len = ch_length()) == NULL_POSITION) 386273e1e22Sbostic { 387273e1e22Sbostic error("Don't know length of file"); 388273e1e22Sbostic return; 389273e1e22Sbostic } 390273e1e22Sbostic pos = (percent * len) / 100; 391273e1e22Sbostic 392273e1e22Sbostic /* 393273e1e22Sbostic * Back up to the beginning of the line. 394273e1e22Sbostic */ 395273e1e22Sbostic if (ch_seek(pos) == 0) 396273e1e22Sbostic { 397273e1e22Sbostic while ((c = ch_back_get()) != '\n' && c != EOI) 398273e1e22Sbostic ; 399273e1e22Sbostic if (c == '\n') 400273e1e22Sbostic (void) ch_forw_get(); 401273e1e22Sbostic pos = ch_tell(); 402273e1e22Sbostic } 403273e1e22Sbostic jump_loc(pos); 404273e1e22Sbostic } 405273e1e22Sbostic 406273e1e22Sbostic /* 407273e1e22Sbostic * Jump to a specified position in the file. 408273e1e22Sbostic */ 409273e1e22Sbostic jump_loc(pos) 410*427e68f0Sbostic off_t pos; 411273e1e22Sbostic { 412273e1e22Sbostic register int nline; 413*427e68f0Sbostic off_t tpos; 414273e1e22Sbostic 415*427e68f0Sbostic if ((nline = onscreen(pos)) >= 0) { 416273e1e22Sbostic /* 417273e1e22Sbostic * The line is currently displayed. 418273e1e22Sbostic * Just scroll there. 419273e1e22Sbostic */ 420273e1e22Sbostic forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); 421273e1e22Sbostic return; 422273e1e22Sbostic } 423273e1e22Sbostic 424273e1e22Sbostic /* 425273e1e22Sbostic * Line is not on screen. 426273e1e22Sbostic * Seek to the desired location. 427273e1e22Sbostic */ 428*427e68f0Sbostic if (ch_seek(pos)) { 429273e1e22Sbostic error("Cannot seek to that position"); 430273e1e22Sbostic return; 431273e1e22Sbostic } 432273e1e22Sbostic 433273e1e22Sbostic /* 434*427e68f0Sbostic * See if the desired line is BEFORE the currently displayed screen. 435*427e68f0Sbostic * If so, then move forward far enough so the line we're on will be 436*427e68f0Sbostic * at the bottom of the screen, in order to be able to call back() 437*427e68f0Sbostic * to make the screen scroll backwards & put the line at the top of 438*427e68f0Sbostic * the screen. 439273e1e22Sbostic * {{ This seems inefficient, but it's not so bad, 440273e1e22Sbostic * since we can never move forward more than a 441273e1e22Sbostic * screenful before we stop to redraw the screen. }} 442273e1e22Sbostic */ 443273e1e22Sbostic tpos = position(TOP); 444*427e68f0Sbostic if (tpos != NULL_POSITION && pos < tpos) { 445*427e68f0Sbostic off_t npos = pos; 446273e1e22Sbostic /* 447273e1e22Sbostic * Note that we can't forw_line() past tpos here, 448273e1e22Sbostic * so there should be no EOI at this stage. 449273e1e22Sbostic */ 450273e1e22Sbostic for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) 451273e1e22Sbostic npos = forw_line(npos); 452273e1e22Sbostic 453*427e68f0Sbostic if (npos < tpos) { 454273e1e22Sbostic /* 455273e1e22Sbostic * More than a screenful back. 456273e1e22Sbostic */ 457273e1e22Sbostic lastmark(); 458273e1e22Sbostic clear(); 459273e1e22Sbostic pos_clear(); 460273e1e22Sbostic add_back_pos(npos); 461273e1e22Sbostic } 462273e1e22Sbostic 463273e1e22Sbostic /* 464273e1e22Sbostic * Note that back() will repaint() if nline > back_scroll. 465273e1e22Sbostic */ 466273e1e22Sbostic back(nline, npos, 1, 0); 467273e1e22Sbostic return; 468273e1e22Sbostic } 469273e1e22Sbostic /* 470273e1e22Sbostic * Remember where we were; clear and paint the screen. 471273e1e22Sbostic */ 472273e1e22Sbostic lastmark(); 473273e1e22Sbostic prepaint(pos); 474273e1e22Sbostic } 475273e1e22Sbostic 476273e1e22Sbostic /* 477273e1e22Sbostic * The table of marks. 478273e1e22Sbostic * A mark is simply a position in the file. 479273e1e22Sbostic */ 480273e1e22Sbostic #define NMARKS (27) /* 26 for a-z plus one for quote */ 481273e1e22Sbostic #define LASTMARK (NMARKS-1) /* For quote */ 482*427e68f0Sbostic static off_t marks[NMARKS]; 483273e1e22Sbostic 484273e1e22Sbostic /* 485273e1e22Sbostic * Initialize the mark table to show no marks are set. 486273e1e22Sbostic */ 487273e1e22Sbostic init_mark() 488273e1e22Sbostic { 489273e1e22Sbostic int i; 490273e1e22Sbostic 491273e1e22Sbostic for (i = 0; i < NMARKS; i++) 492273e1e22Sbostic marks[i] = NULL_POSITION; 493273e1e22Sbostic } 494273e1e22Sbostic 495273e1e22Sbostic /* 496273e1e22Sbostic * See if a mark letter is valid (between a and z). 497273e1e22Sbostic */ 498273e1e22Sbostic static int 499273e1e22Sbostic badmark(c) 500273e1e22Sbostic int c; 501273e1e22Sbostic { 502273e1e22Sbostic if (c < 'a' || c > 'z') 503273e1e22Sbostic { 504273e1e22Sbostic error("Choose a letter between 'a' and 'z'"); 505273e1e22Sbostic return (1); 506273e1e22Sbostic } 507273e1e22Sbostic return (0); 508273e1e22Sbostic } 509273e1e22Sbostic 510273e1e22Sbostic /* 511273e1e22Sbostic * Set a mark. 512273e1e22Sbostic */ 513273e1e22Sbostic setmark(c) 514273e1e22Sbostic int c; 515273e1e22Sbostic { 516273e1e22Sbostic if (badmark(c)) 517273e1e22Sbostic return; 518273e1e22Sbostic marks[c-'a'] = position(TOP); 519273e1e22Sbostic } 520273e1e22Sbostic 521273e1e22Sbostic lastmark() 522273e1e22Sbostic { 523273e1e22Sbostic marks[LASTMARK] = position(TOP); 524273e1e22Sbostic } 525273e1e22Sbostic 526273e1e22Sbostic /* 527273e1e22Sbostic * Go to a previously set mark. 528273e1e22Sbostic */ 529273e1e22Sbostic gomark(c) 530273e1e22Sbostic int c; 531273e1e22Sbostic { 532*427e68f0Sbostic off_t pos; 533273e1e22Sbostic 534273e1e22Sbostic if (c == '\'') 535273e1e22Sbostic pos = marks[LASTMARK]; 536273e1e22Sbostic else if (badmark(c)) 537273e1e22Sbostic return; 538273e1e22Sbostic else 539273e1e22Sbostic pos = marks[c-'a']; 540273e1e22Sbostic 541273e1e22Sbostic if (pos == NULL_POSITION) 542273e1e22Sbostic error("mark not set"); 543273e1e22Sbostic else 544273e1e22Sbostic jump_loc(pos); 545273e1e22Sbostic } 546273e1e22Sbostic 547273e1e22Sbostic /* 548273e1e22Sbostic * Get the backwards scroll limit. 549273e1e22Sbostic * Must call this function instead of just using the value of 550273e1e22Sbostic * back_scroll, because the default case depends on sc_height and 551273e1e22Sbostic * top_scroll, as well as back_scroll. 552273e1e22Sbostic */ 553273e1e22Sbostic get_back_scroll() 554273e1e22Sbostic { 555273e1e22Sbostic if (back_scroll >= 0) 556273e1e22Sbostic return (back_scroll); 557273e1e22Sbostic if (top_scroll) 558273e1e22Sbostic return (sc_height - 2); 559273e1e22Sbostic return (sc_height - 1); 560273e1e22Sbostic } 561273e1e22Sbostic 562273e1e22Sbostic /* 563273e1e22Sbostic * Search for the n-th occurence of a specified pattern, 564273e1e22Sbostic * either forward or backward. 565273e1e22Sbostic */ 566273e1e22Sbostic search(search_forward, pattern, n, wantmatch) 567273e1e22Sbostic register int search_forward; 568273e1e22Sbostic register char *pattern; 569273e1e22Sbostic register int n; 570273e1e22Sbostic int wantmatch; 571273e1e22Sbostic { 572*427e68f0Sbostic off_t pos, linepos; 573273e1e22Sbostic register char *p; 574273e1e22Sbostic register char *q; 575273e1e22Sbostic int linenum; 576273e1e22Sbostic int linematch; 577*427e68f0Sbostic #ifdef RECOMP 578273e1e22Sbostic char *re_comp(); 579273e1e22Sbostic char *errmsg; 580273e1e22Sbostic #else 581*427e68f0Sbostic #ifdef REGCMP 582273e1e22Sbostic char *regcmp(); 583273e1e22Sbostic static char *cpattern = NULL; 584273e1e22Sbostic #else 585273e1e22Sbostic static char lpbuf[100]; 586273e1e22Sbostic static char *last_pattern = NULL; 5878033092dSbostic char *strcpy(); 588273e1e22Sbostic #endif 589273e1e22Sbostic #endif 590273e1e22Sbostic 591273e1e22Sbostic /* 592*427e68f0Sbostic * For a caseless search, convert any uppercase in the pattern to 593*427e68f0Sbostic * lowercase. 594273e1e22Sbostic */ 595*427e68f0Sbostic if (caseless && pattern != NULL) 596*427e68f0Sbostic for (p = pattern; *p; p++) 597*427e68f0Sbostic if (isupper(*p)) 598*427e68f0Sbostic *p = tolower(*p); 599*427e68f0Sbostic #ifdef RECOMP 600273e1e22Sbostic 601273e1e22Sbostic /* 602273e1e22Sbostic * (re_comp handles a null pattern internally, 603273e1e22Sbostic * so there is no need to check for a null pattern here.) 604273e1e22Sbostic */ 605273e1e22Sbostic if ((errmsg = re_comp(pattern)) != NULL) 606273e1e22Sbostic { 607273e1e22Sbostic error(errmsg); 608*427e68f0Sbostic return(0); 609273e1e22Sbostic } 610273e1e22Sbostic #else 611*427e68f0Sbostic #ifdef REGCMP 612273e1e22Sbostic if (pattern == NULL || *pattern == '\0') 613273e1e22Sbostic { 614273e1e22Sbostic /* 615273e1e22Sbostic * A null pattern means use the previous pattern. 616273e1e22Sbostic * The compiled previous pattern is in cpattern, so just use it. 617273e1e22Sbostic */ 618273e1e22Sbostic if (cpattern == NULL) 619273e1e22Sbostic { 620273e1e22Sbostic error("No previous regular expression"); 621*427e68f0Sbostic return(0); 622273e1e22Sbostic } 623273e1e22Sbostic } else 624273e1e22Sbostic { 625273e1e22Sbostic /* 626273e1e22Sbostic * Otherwise compile the given pattern. 627273e1e22Sbostic */ 628273e1e22Sbostic char *s; 629273e1e22Sbostic if ((s = regcmp(pattern, 0)) == NULL) 630273e1e22Sbostic { 631273e1e22Sbostic error("Invalid pattern"); 632*427e68f0Sbostic return(0); 633273e1e22Sbostic } 634273e1e22Sbostic if (cpattern != NULL) 635273e1e22Sbostic free(cpattern); 636273e1e22Sbostic cpattern = s; 637273e1e22Sbostic } 638273e1e22Sbostic #else 639273e1e22Sbostic if (pattern == NULL || *pattern == '\0') 640273e1e22Sbostic { 641273e1e22Sbostic /* 642273e1e22Sbostic * Null pattern means use the previous pattern. 643273e1e22Sbostic */ 644273e1e22Sbostic if (last_pattern == NULL) 645273e1e22Sbostic { 646273e1e22Sbostic error("No previous regular expression"); 647*427e68f0Sbostic return(0); 648273e1e22Sbostic } 649273e1e22Sbostic pattern = last_pattern; 650273e1e22Sbostic } else 651273e1e22Sbostic { 6528033092dSbostic (void)strcpy(lpbuf, pattern); 653273e1e22Sbostic last_pattern = lpbuf; 654273e1e22Sbostic } 655273e1e22Sbostic #endif 656273e1e22Sbostic #endif 657273e1e22Sbostic 658273e1e22Sbostic /* 659273e1e22Sbostic * Figure out where to start the search. 660273e1e22Sbostic */ 661273e1e22Sbostic 662*427e68f0Sbostic if (position(TOP) == NULL_POSITION) { 663273e1e22Sbostic /* 664*427e68f0Sbostic * Nothing is currently displayed. Start at the beginning 665*427e68f0Sbostic * of the file. (This case is mainly for searches from the 666*427e68f0Sbostic * command line. 667273e1e22Sbostic */ 668*427e68f0Sbostic pos = (off_t)0; 669*427e68f0Sbostic } else if (!search_forward) { 670273e1e22Sbostic /* 671273e1e22Sbostic * Backward search: start just before the top line 672273e1e22Sbostic * displayed on the screen. 673273e1e22Sbostic */ 674273e1e22Sbostic pos = position(TOP); 675*427e68f0Sbostic } else { 676273e1e22Sbostic /* 677273e1e22Sbostic * Start at the second screen line displayed on the screen. 678273e1e22Sbostic */ 679273e1e22Sbostic pos = position(TOP_PLUS_ONE); 680273e1e22Sbostic } 681273e1e22Sbostic 682273e1e22Sbostic if (pos == NULL_POSITION) 683273e1e22Sbostic { 684273e1e22Sbostic /* 685273e1e22Sbostic * Can't find anyplace to start searching from. 686273e1e22Sbostic */ 687273e1e22Sbostic error("Nothing to search"); 688*427e68f0Sbostic return(0); 689273e1e22Sbostic } 690273e1e22Sbostic 691273e1e22Sbostic linenum = find_linenum(pos); 692273e1e22Sbostic for (;;) 693273e1e22Sbostic { 694273e1e22Sbostic /* 695273e1e22Sbostic * Get lines until we find a matching one or 696273e1e22Sbostic * until we hit end-of-file (or beginning-of-file 697273e1e22Sbostic * if we're going backwards). 698273e1e22Sbostic */ 699273e1e22Sbostic if (sigs) 700273e1e22Sbostic /* 701273e1e22Sbostic * A signal aborts the search. 702273e1e22Sbostic */ 703*427e68f0Sbostic return(0); 704273e1e22Sbostic 705273e1e22Sbostic if (search_forward) 706273e1e22Sbostic { 707273e1e22Sbostic /* 708273e1e22Sbostic * Read the next line, and save the 709273e1e22Sbostic * starting position of that line in linepos. 710273e1e22Sbostic */ 711273e1e22Sbostic linepos = pos; 712273e1e22Sbostic pos = forw_raw_line(pos); 713273e1e22Sbostic if (linenum != 0) 714273e1e22Sbostic linenum++; 715273e1e22Sbostic } else 716273e1e22Sbostic { 717273e1e22Sbostic /* 718273e1e22Sbostic * Read the previous line and save the 719273e1e22Sbostic * starting position of that line in linepos. 720273e1e22Sbostic */ 721273e1e22Sbostic pos = back_raw_line(pos); 722273e1e22Sbostic linepos = pos; 723273e1e22Sbostic if (linenum != 0) 724273e1e22Sbostic linenum--; 725273e1e22Sbostic } 726273e1e22Sbostic 727273e1e22Sbostic if (pos == NULL_POSITION) 728273e1e22Sbostic { 729273e1e22Sbostic /* 730273e1e22Sbostic * We hit EOF/BOF without a match. 731273e1e22Sbostic */ 732273e1e22Sbostic error("Pattern not found"); 733*427e68f0Sbostic return(0); 734273e1e22Sbostic } 735273e1e22Sbostic 736273e1e22Sbostic /* 737273e1e22Sbostic * If we're using line numbers, we might as well 738273e1e22Sbostic * remember the information we have now (the position 739273e1e22Sbostic * and line number of the current line). 740273e1e22Sbostic */ 741273e1e22Sbostic if (linenums) 742273e1e22Sbostic add_lnum(linenum, pos); 743273e1e22Sbostic 744273e1e22Sbostic /* 745*427e68f0Sbostic * If this is a caseless search, convert uppercase in the 746*427e68f0Sbostic * input line to lowercase. 747273e1e22Sbostic */ 748*427e68f0Sbostic if (caseless) 749*427e68f0Sbostic for (p = q = line; *p; p++, q++) 750*427e68f0Sbostic *q = isupper(*p) ? tolower(*p) : *p; 751*427e68f0Sbostic 752*427e68f0Sbostic /* 753*427e68f0Sbostic * Remove any backspaces along with the preceeding char. 754*427e68f0Sbostic * This allows us to match text which is underlined or 755*427e68f0Sbostic * overstruck. 756*427e68f0Sbostic */ 757*427e68f0Sbostic for (p = q = line; *p; p++, q++) 758*427e68f0Sbostic if (q > line && *p == '\b') 759273e1e22Sbostic /* Delete BS and preceeding char. */ 760273e1e22Sbostic q -= 2; 761273e1e22Sbostic else 762273e1e22Sbostic /* Otherwise, just copy. */ 763273e1e22Sbostic *q = *p; 764273e1e22Sbostic 765273e1e22Sbostic /* 766273e1e22Sbostic * Test the next line to see if we have a match. 767273e1e22Sbostic * This is done in a variety of ways, depending 768273e1e22Sbostic * on what pattern matching functions are available. 769273e1e22Sbostic */ 770*427e68f0Sbostic #ifdef REGCMP 771273e1e22Sbostic linematch = (regex(cpattern, line) != NULL); 772273e1e22Sbostic #else 773*427e68f0Sbostic #ifdef RECOMP 774273e1e22Sbostic linematch = (re_exec(line) == 1); 775273e1e22Sbostic #else 776273e1e22Sbostic linematch = match(pattern, line); 777273e1e22Sbostic #endif 778273e1e22Sbostic #endif 779273e1e22Sbostic /* 780273e1e22Sbostic * We are successful if wantmatch and linematch are 781273e1e22Sbostic * both true (want a match and got it), 782273e1e22Sbostic * or both false (want a non-match and got it). 783273e1e22Sbostic */ 784273e1e22Sbostic if (((wantmatch && linematch) || (!wantmatch && !linematch)) && 785273e1e22Sbostic --n <= 0) 786273e1e22Sbostic /* 787273e1e22Sbostic * Found the line. 788273e1e22Sbostic */ 789273e1e22Sbostic break; 790273e1e22Sbostic } 791273e1e22Sbostic jump_loc(linepos); 792*427e68f0Sbostic return(1); 793273e1e22Sbostic } 794273e1e22Sbostic 795*427e68f0Sbostic #if !defined(REGCMP) && !defined(RECOMP) 796273e1e22Sbostic /* 797273e1e22Sbostic * We have neither regcmp() nor re_comp(). 798273e1e22Sbostic * We use this function to do simple pattern matching. 799273e1e22Sbostic * It supports no metacharacters like *, etc. 800273e1e22Sbostic */ 801*427e68f0Sbostic static 802273e1e22Sbostic match(pattern, buf) 803273e1e22Sbostic char *pattern, *buf; 804273e1e22Sbostic { 805273e1e22Sbostic register char *pp, *lp; 806273e1e22Sbostic 807273e1e22Sbostic for ( ; *buf != '\0'; buf++) 808273e1e22Sbostic { 809273e1e22Sbostic for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) 810273e1e22Sbostic if (*pp == '\0' || *lp == '\0') 811273e1e22Sbostic break; 812273e1e22Sbostic if (*pp == '\0') 813273e1e22Sbostic return (1); 814273e1e22Sbostic } 815273e1e22Sbostic return (0); 816273e1e22Sbostic } 817273e1e22Sbostic #endif 818