Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/miscutils/less.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/miscutils/less.c
r1765 r2725 5 5 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com> 6 6 * 7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 8 */ 9 9 … … 22 22 */ 23 23 24 #include <sched.h> 24 #include <sched.h> /* sched_yield() */ 25 25 26 26 #include "libbb.h" … … 29 29 #endif 30 30 31 /* FIXME: currently doesn't work right */ 32 #undef ENABLE_FEATURE_LESS_FLAGCS 33 #define ENABLE_FEATURE_LESS_FLAGCS 0 34 31 32 #define ESC "\033" 35 33 /* The escape codes for highlighted and normal text */ 36 #define HIGHLIGHT "\033[7m" 37 #define NORMAL "\033[0m" 38 /* The escape code to clear the screen */ 39 #define CLEAR "\033[H\033[J" 40 /* The escape code to clear to end of line */ 41 #define CLEAR_2_EOL "\033[K" 42 43 /* These are the escape sequences corresponding to special keys */ 34 #define HIGHLIGHT ESC"[7m" 35 #define NORMAL ESC"[0m" 36 /* The escape code to home and clear to the end of screen */ 37 #define CLEAR ESC"[H\033[J" 38 /* The escape code to clear to the end of line */ 39 #define CLEAR_2_EOL ESC"[K" 40 44 41 enum { 45 REAL_KEY_UP = 'A',46 REAL_KEY_DOWN = 'B',47 REAL_KEY_RIGHT = 'C',48 REAL_KEY_LEFT = 'D',49 REAL_PAGE_UP = '5',50 REAL_PAGE_DOWN = '6',51 REAL_KEY_HOME = '7', // vt100? linux vt? or what?52 REAL_KEY_END = '8',53 REAL_KEY_HOME_ALT = '1', // ESC [1~ (vt100? linux vt? or what?)54 REAL_KEY_END_ALT = '4', // ESC [4~55 REAL_KEY_HOME_XTERM = 'H',56 REAL_KEY_END_XTERM = 'F',57 58 /* These are the special codes assigned by this program to the special keys */59 KEY_UP = 20,60 KEY_DOWN = 21,61 KEY_RIGHT = 22,62 KEY_LEFT = 23,63 PAGE_UP = 24,64 PAGE_DOWN = 25,65 KEY_HOME = 26,66 KEY_END = 27,67 68 42 /* Absolute max of lines eaten */ 69 43 MAXLINES = CONFIG_FEATURE_LESS_MAXLINES, 70 71 44 /* This many "after the end" lines we will show (at max) */ 72 45 TILDES = 1, … … 75 48 /* Command line options */ 76 49 enum { 77 FLAG_E = 1 ,50 FLAG_E = 1 << 0, 78 51 FLAG_M = 1 << 1, 79 52 FLAG_m = 1 << 2, 80 53 FLAG_N = 1 << 3, 81 54 FLAG_TILDE = 1 << 4, 55 FLAG_I = 1 << 5, 56 FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD, 82 57 /* hijack command line options variable for internal state vars */ 83 58 LESS_STATE_MATCH_BACKWARDS = 1 << 15, … … 91 66 int cur_fline; /* signed */ 92 67 int kbd_fd; /* fd to get input from */ 68 int less_gets_pos; 93 69 /* last position in last line, taking into account tabs */ 94 size_t linepos; 95 unsigned max_displayed_line; 70 size_t last_line_pos; 96 71 unsigned max_fline; 97 72 unsigned max_lineno; /* this one tracks linewrap */ 73 unsigned max_displayed_line; 98 74 unsigned width; 75 #if ENABLE_FEATURE_LESS_WINCH 76 unsigned winch_counter; 77 #endif 99 78 ssize_t eof_error; /* eof if 0, error if < 0 */ 100 s ize_t readpos;101 s ize_t readeof;79 ssize_t readpos; 80 ssize_t readeof; /* must be signed */ 102 81 const char **buffer; 103 82 const char **flines; … … 114 93 unsigned *match_lines; 115 94 int match_pos; /* signed! */ 116 unsigned num_matches; 95 int wanted_match; /* signed! */ 96 int num_matches; 117 97 regex_t pattern; 118 98 smallint pattern_valid; … … 120 100 smallint terminated; 121 101 struct termios term_orig, term_less; 102 char kbd_input[KEYCODE_BUFFER_SIZE]; 122 103 }; 123 104 #define G (*ptr_to_globals) 124 105 #define cur_fline (G.cur_fline ) 125 106 #define kbd_fd (G.kbd_fd ) 126 #define l inepos (G.linepos)127 #define max_displayed_line (G.max_displayed_line)107 #define less_gets_pos (G.less_gets_pos ) 108 #define last_line_pos (G.last_line_pos ) 128 109 #define max_fline (G.max_fline ) 129 110 #define max_lineno (G.max_lineno ) 111 #define max_displayed_line (G.max_displayed_line) 130 112 #define width (G.width ) 113 #define winch_counter (G.winch_counter ) 114 /* This one is 100% not cached by compiler on read access */ 115 #define WINCH_COUNTER (*(volatile unsigned *)&winch_counter) 131 116 #define eof_error (G.eof_error ) 132 117 #define readpos (G.readpos ) … … 145 130 #define match_pos (G.match_pos ) 146 131 #define num_matches (G.num_matches ) 132 #define wanted_match (G.wanted_match ) 147 133 #define pattern (G.pattern ) 148 134 #define pattern_valid (G.pattern_valid ) … … 151 137 #define term_orig (G.term_orig ) 152 138 #define term_less (G.term_less ) 139 #define kbd_input (G.kbd_input ) 153 140 #define INIT_G() do { \ 154 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ 155 empty_line_marker = "~"; \ 156 num_files = 1; \ 157 current_file = 1; \ 158 eof_error = 1; \ 159 terminated = 1; \ 160 } while (0) 141 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 142 less_gets_pos = -1; \ 143 empty_line_marker = "~"; \ 144 num_files = 1; \ 145 current_file = 1; \ 146 eof_error = 1; \ 147 terminated = 1; \ 148 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \ 149 } while (0) 150 151 /* flines[] are lines read from stdin, each in malloc'ed buffer. 152 * Line numbers are stored as uint32_t prepended to each line. 153 * Pointer is adjusted so that flines[i] points directly past 154 * line number. Accesor: */ 155 #define MEMPTR(p) ((char*)(p) - 4) 156 #define LINENO(p) (*(uint32_t*)((p) - 4)) 157 161 158 162 159 /* Reset terminal input to normal */ 163 160 static void set_tty_cooked(void) 164 161 { 165 fflush (stdout);162 fflush_all(); 166 163 tcsetattr(kbd_fd, TCSANOW, &term_orig); 167 }168 169 /* Exit the program gracefully */170 static void less_exit(int code)171 {172 /* TODO: We really should save the terminal state when we start,173 * and restore it when we exit. Less does this with the174 * "ti" and "te" termcap commands; can this be done with175 * only termios.h? */176 putchar('\n');177 fflush_stdout_and_exit(code);178 164 } 179 165 … … 182 168 static void move_cursor(int line, int row) 183 169 { 184 printf( "\033[%u;%uH", line, row);170 printf(ESC"[%u;%uH", line, row); 185 171 } 186 172 187 173 static void clear_line(void) 188 174 { 189 printf( "\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);175 printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2); 190 176 } 191 177 … … 200 186 printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); 201 187 } 188 189 /* Exit the program gracefully */ 190 static void less_exit(int code) 191 { 192 set_tty_cooked(); 193 clear_line(); 194 if (code < 0) 195 kill_myself_with_sig(- code); /* does not return */ 196 exit(code); 197 } 198 199 #if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \ 200 || ENABLE_FEATURE_LESS_WINCH 201 static void re_wrap(void) 202 { 203 int w = width; 204 int new_line_pos; 205 int src_idx; 206 int dst_idx; 207 int new_cur_fline = 0; 208 uint32_t lineno; 209 char linebuf[w + 1]; 210 const char **old_flines = flines; 211 const char *s; 212 char **new_flines = NULL; 213 char *d; 214 215 if (option_mask32 & FLAG_N) 216 w -= 8; 217 218 src_idx = 0; 219 dst_idx = 0; 220 s = old_flines[0]; 221 lineno = LINENO(s); 222 d = linebuf; 223 new_line_pos = 0; 224 while (1) { 225 *d = *s; 226 if (*d != '\0') { 227 new_line_pos++; 228 if (*d == '\t') /* tab */ 229 new_line_pos += 7; 230 s++; 231 d++; 232 if (new_line_pos >= w) { 233 int sz; 234 /* new line is full, create next one */ 235 *d = '\0'; 236 next_new: 237 sz = (d - linebuf) + 1; /* + 1: NUL */ 238 d = ((char*)xmalloc(sz + 4)) + 4; 239 LINENO(d) = lineno; 240 memcpy(d, linebuf, sz); 241 new_flines = xrealloc_vector(new_flines, 8, dst_idx); 242 new_flines[dst_idx] = d; 243 dst_idx++; 244 if (new_line_pos < w) { 245 /* if we came here thru "goto next_new" */ 246 if (src_idx > max_fline) 247 break; 248 lineno = LINENO(s); 249 } 250 d = linebuf; 251 new_line_pos = 0; 252 } 253 continue; 254 } 255 /* *d == NUL: old line ended, go to next old one */ 256 free(MEMPTR(old_flines[src_idx])); 257 /* btw, convert cur_fline... */ 258 if (cur_fline == src_idx) 259 new_cur_fline = dst_idx; 260 src_idx++; 261 /* no more lines? finish last new line (and exit the loop) */ 262 if (src_idx > max_fline) 263 goto next_new; 264 s = old_flines[src_idx]; 265 if (lineno != LINENO(s)) { 266 /* this is not a continuation line! 267 * create next _new_ line too */ 268 goto next_new; 269 } 270 } 271 272 free(old_flines); 273 flines = (const char **)new_flines; 274 275 max_fline = dst_idx - 1; 276 last_line_pos = new_line_pos; 277 cur_fline = new_cur_fline; 278 /* max_lineno is screen-size independent */ 279 #if ENABLE_FEATURE_LESS_REGEXP 280 pattern_valid = 0; 281 #endif 282 } 283 #endif 202 284 203 285 #if ENABLE_FEATURE_LESS_REGEXP … … 226 308 * readbuf[0..readeof-1] - small preliminary buffer. 227 309 * readbuf[readpos] - next character to add to current line. 228 * l inepos - screen line position of next char to be read310 * last_line_pos - screen line position of next char to be read 229 311 * (takes into account tabs and backspaces) 230 312 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error … … 234 316 #define readbuf bb_common_bufsiz1 235 317 char *current_line, *p; 236 USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;)237 318 int w = width; 238 319 char last_terminated = terminated; 320 #if ENABLE_FEATURE_LESS_REGEXP 321 unsigned old_max_fline = max_fline; 322 time_t last_time = 0; 323 unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ 324 #endif 239 325 240 326 if (option_mask32 & FLAG_N) 241 327 w -= 8; 242 328 243 current_line = xmalloc(w); 244 p = current_line; 329 IF_FEATURE_LESS_REGEXP(again0:) 330 331 p = current_line = ((char*)xmalloc(w + 4)) + 4; 245 332 max_fline += last_terminated; 246 333 if (!last_terminated) { 247 334 const char *cp = flines[max_fline]; 248 if (option_mask32 & FLAG_N) 249 cp += 8; 250 strcpy(current_line, cp); 335 strcpy(p, cp); 251 336 p += strlen(current_line); 252 /* linepos is still valid from previous read_lines() */ 337 free(MEMPTR(flines[max_fline])); 338 /* last_line_pos is still valid from previous read_lines() */ 253 339 } else { 254 linepos = 0; 255 } 256 257 while (1) { 258 again: 340 last_line_pos = 0; 341 } 342 343 while (1) { /* read lines until we reach cur_fline or wanted_match */ 259 344 *p = '\0'; 260 345 terminated = 0; 261 while (1) { 346 while (1) { /* read chars until we have a line */ 262 347 char c; 263 348 /* if no unprocessed chars left, eat more */ 264 349 if (readpos >= readeof) { 265 smallint yielded = 0;266 267 350 ndelay_on(0); 268 read_again: 269 eof_error = safe_read(0, readbuf, sizeof(readbuf));351 eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf)); 352 ndelay_off(0); 270 353 readpos = 0; 271 354 readeof = eof_error; 272 if (eof_error < 0) { 273 if (errno == EAGAIN && !yielded) { 274 /* We can hit EAGAIN while searching for regexp match. 275 * Yield is not 100% reliable solution in general, 276 * but for less it should be good enough - 277 * we give stdin supplier some CPU time to produce 278 * more input. We do it just once. 279 * Currently, we do not stop when we found the Nth 280 * occurrence we were looking for. We read till end 281 * (or double EAGAIN). TODO? */ 282 sched_yield(); 283 yielded = 1; 284 goto read_again; 285 } 286 readeof = 0; 287 if (errno != EAGAIN) 288 print_statusline("read error"); 289 } 290 ndelay_off(0); 291 292 if (eof_error <= 0) { 355 if (eof_error <= 0) 293 356 goto reached_eof; 294 }295 357 } 296 358 c = readbuf[readpos]; … … 298 360 /* <tab><bs> is (a) insane and */ 299 361 /* (b) harder to do correctly, so we refuse to do it */ 300 if (c == '\x8' && l inepos && p[-1] != '\t') {362 if (c == '\x8' && last_line_pos && p[-1] != '\t') { 301 363 readpos++; /* eat it */ 302 l inepos--;364 last_line_pos--; 303 365 /* was buggy (p could end up <= current_line)... */ 304 366 *--p = '\0'; … … 306 368 } 307 369 { 308 size_t new_l inepos = linepos + 1;370 size_t new_last_line_pos = last_line_pos + 1; 309 371 if (c == '\t') { 310 new_l inepos += 7;311 new_l inepos &= (~7);372 new_last_line_pos += 7; 373 new_last_line_pos &= (~7); 312 374 } 313 if ( new_linepos >= w)375 if ((int)new_last_line_pos >= w) 314 376 break; 315 l inepos = new_linepos;377 last_line_pos = new_last_line_pos; 316 378 } 317 379 /* ok, we will eat this char */ … … 319 381 if (c == '\n') { 320 382 terminated = 1; 321 l inepos = 0;383 last_line_pos = 0; 322 384 break; 323 385 } … … 326 388 *p++ = c; 327 389 *p = '\0'; 328 } 390 } /* end of "read chars until we have a line" loop */ 329 391 /* Corner case: linewrap with only "" wrapping to next line */ 330 392 /* Looks ugly on screen, so we do not store this empty line */ … … 332 394 last_terminated = 1; 333 395 max_lineno++; 334 goto again;396 continue; 335 397 } 336 398 reached_eof: 337 399 last_terminated = terminated; 338 flines = xrealloc(flines, (max_fline+1) * sizeof(char *)); 339 if (option_mask32 & FLAG_N) { 340 /* Width of 7 preserves tab spacing in the text */ 341 flines[max_fline] = xasprintf( 342 (max_lineno <= 9999999) ? "%7u %s" : "%07u %s", 343 max_lineno % 10000000, current_line); 344 free(current_line); 345 if (terminated) 346 max_lineno++; 347 } else { 348 flines[max_fline] = xrealloc(current_line, strlen(current_line)+1); 349 } 400 flines = xrealloc_vector(flines, 8, max_fline); 401 402 flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4; 403 LINENO(flines[max_fline]) = max_lineno; 404 if (terminated) 405 max_lineno++; 406 350 407 if (max_fline >= MAXLINES) { 351 408 eof_error = 0; /* Pretend we saw EOF */ 352 409 break; 353 410 } 354 if (max_fline > cur_fline + max_displayed_line) 411 if (!(option_mask32 & FLAG_S) 412 ? (max_fline > cur_fline + max_displayed_line) 413 : (max_fline >= cur_fline 414 && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line) 415 ) { 416 #if !ENABLE_FEATURE_LESS_REGEXP 355 417 break; 418 #else 419 if (wanted_match >= num_matches) { /* goto_match called us */ 420 fill_match_lines(old_max_fline); 421 old_max_fline = max_fline; 422 } 423 if (wanted_match < num_matches) 424 break; 425 #endif 426 } 356 427 if (eof_error <= 0) { 357 if (eof_error < 0 && errno == EAGAIN) { 358 /* not yet eof or error, reset flag (or else 359 * we will hog CPU - select() will return 360 * immediately */ 361 eof_error = 1; 428 if (eof_error < 0) { 429 if (errno == EAGAIN) { 430 /* not yet eof or error, reset flag (or else 431 * we will hog CPU - select() will return 432 * immediately */ 433 eof_error = 1; 434 } else { 435 print_statusline(bb_msg_read_error); 436 } 362 437 } 438 #if !ENABLE_FEATURE_LESS_REGEXP 363 439 break; 440 #else 441 if (wanted_match < num_matches) { 442 break; 443 } else { /* goto_match called us */ 444 time_t t = time(NULL); 445 if (t != last_time) { 446 last_time = t; 447 if (--seconds_p1 == 0) 448 break; 449 } 450 sched_yield(); 451 goto again0; /* go loop again (max 2 seconds) */ 452 } 453 #endif 364 454 } 365 455 max_fline++; 366 current_line = xmalloc(w);456 current_line = ((char*)xmalloc(w + 4)) + 4; 367 457 p = current_line; 368 l inepos = 0;369 } 458 last_line_pos = 0; 459 } /* end of "read lines until we reach cur_fline" loop */ 370 460 fill_match_lines(old_max_fline); 461 #if ENABLE_FEATURE_LESS_REGEXP 462 /* prevent us from being stuck in search for a match */ 463 wanted_match = -1; 464 #endif 371 465 #undef readbuf 372 466 } … … 385 479 { 386 480 int percentage; 481 482 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ 483 return; 387 484 388 485 clear_line(); … … 393 490 cur_fline + 1, cur_fline + max_displayed_line + 1, 394 491 max_fline + 1); 395 if (cur_fline >= max_fline - max_displayed_line) {492 if (cur_fline >= (int)(max_fline - max_displayed_line)) { 396 493 printf("(END)"NORMAL); 397 494 if (num_files > 1 && current_file != num_files) … … 408 505 { 409 506 const char *p; 507 508 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ 509 return; 410 510 411 511 /* Change the status if flags have been set */ … … 419 519 420 520 clear_line(); 421 if (cur_fline && cur_fline < max_fline - max_displayed_line) {422 putchar(':');521 if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) { 522 bb_putchar(':'); 423 523 return; 424 524 } … … 445 545 diff = max_fline - (cur_fline + max_displayed_line) + TILDES; 446 546 /* As the number of lines requested was too large, we just move 447 to the end of the file */547 * to the end of the file */ 448 548 if (diff > 0) 449 549 cur_fline += diff; … … 457 557 "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */ 458 558 static const char ctrlconv[] ALIGN1 = 459 /* '\n': it's a former NUL - subst with '@', not 'J' */ 559 /* why 40 instead of 4a below? - it is a replacement for '\n'. 560 * '\n' is a former NUL - we subst it with @, not J */ 460 561 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f" 461 562 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"; 563 564 static void lineno_str(char *nbuf9, const char *line) 565 { 566 nbuf9[0] = '\0'; 567 if (option_mask32 & FLAG_N) { 568 const char *fmt; 569 unsigned n; 570 571 if (line == empty_line_marker) { 572 memset(nbuf9, ' ', 8); 573 nbuf9[8] = '\0'; 574 return; 575 } 576 /* Width of 7 preserves tab spacing in the text */ 577 fmt = "%7u "; 578 n = LINENO(line) + 1; 579 if (n > 9999999) { 580 n %= 10000000; 581 fmt = "%07u "; 582 } 583 sprintf(nbuf9, fmt, n); 584 } 585 } 586 462 587 463 588 #if ENABLE_FEATURE_LESS_REGEXP … … 470 595 471 596 char buf[width]; 597 char nbuf9[9]; 472 598 const char *str = line; 473 599 char *p = buf; … … 502 628 while (match_status == 0) { 503 629 char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL, 504 growline ? : "",630 growline ? growline : "", 505 631 match_structs.rm_so, str, 506 632 match_structs.rm_eo - match_structs.rm_so, 507 633 str + match_structs.rm_so); 508 free(growline); growline = new; 634 free(growline); 635 growline = new; 509 636 str += match_structs.rm_eo; 510 637 line += match_structs.rm_eo; … … 513 640 /* Most of the time doesn't find the regex, optimize for that */ 514 641 match_status = regexec(&pattern, line, 1, &match_structs, eflags); 515 } 516 642 /* if even "" matches, treat it as "not a match" */ 643 if (match_structs.rm_so >= match_structs.rm_eo) 644 match_status = 1; 645 } 646 647 lineno_str(nbuf9, line); 517 648 if (!growline) { 518 printf(CLEAR_2_EOL"%s \n", str);649 printf(CLEAR_2_EOL"%s%s\n", nbuf9, str); 519 650 return; 520 651 } 521 printf(CLEAR_2_EOL"%s%s \n", growline, str);652 printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str); 522 653 free(growline); 523 654 } … … 529 660 { 530 661 char buf[width]; 662 char nbuf9[9]; 531 663 char *p; 532 664 size_t n; 533 665 534 printf(CLEAR_2_EOL); 666 lineno_str(nbuf9, str); 667 printf(CLEAR_2_EOL"%s", nbuf9); 668 535 669 while (*str) { 536 670 n = strcspn(str, controls); … … 562 696 static void buffer_print(void) 563 697 { 564 inti;698 unsigned i; 565 699 566 700 move_cursor(0, 0); … … 575 709 static void buffer_fill_and_print(void) 576 710 { 577 int i; 711 unsigned i; 712 #if ENABLE_FEATURE_LESS_DASHCMD 713 int fpos = cur_fline; 714 715 if (option_mask32 & FLAG_S) { 716 /* Go back to the beginning of this line */ 717 while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1])) 718 fpos--; 719 } 720 721 i = 0; 722 while (i <= max_displayed_line && fpos <= max_fline) { 723 int lineno = LINENO(flines[fpos]); 724 buffer[i] = flines[fpos]; 725 i++; 726 do { 727 fpos++; 728 } while ((fpos <= max_fline) 729 && (option_mask32 & FLAG_S) 730 && lineno == LINENO(flines[fpos]) 731 ); 732 } 733 #else 578 734 for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) { 579 735 buffer[i] = flines[cur_fline + i]; 580 736 } 737 #endif 581 738 for (; i <= max_displayed_line; i++) { 582 739 buffer[i] = empty_line_marker; … … 619 776 { 620 777 if (filename) { 621 int fd = xopen(filename, O_RDONLY); 622 dup2(fd, 0); 623 if (fd) close(fd); 778 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO); 624 779 } else { 625 780 /* "less" with no arguments in argv[] */ … … 629 784 readpos = 0; 630 785 readeof = 0; 631 l inepos = 0;786 last_line_pos = 0; 632 787 terminated = 1; 633 788 read_lines(); … … 637 792 static void reinitialize(void) 638 793 { 639 inti;794 unsigned i; 640 795 641 796 if (flines) { 642 797 for (i = 0; i <= max_fline; i++) 643 free( (void*)(flines[i]));798 free(MEMPTR(flines[i])); 644 799 free(flines); 645 800 flines = NULL; … … 653 808 } 654 809 655 static void getch_nowait(char* input, int sz) 656 { 657 ssize_t rd; 658 fd_set readfds; 810 static int getch_nowait(void) 811 { 812 int rd; 813 struct pollfd pfd[2]; 814 815 pfd[0].fd = STDIN_FILENO; 816 pfd[0].events = POLLIN; 817 pfd[1].fd = kbd_fd; 818 pfd[1].events = POLLIN; 659 819 again: 660 fflush(stdout); 661 662 /* NB: select returns whenever read will not block. Therefore: 663 * (a) with O_NONBLOCK'ed fds select will return immediately 664 * (b) if eof is reached, select will also return 665 * because read will immediately return 0 bytes. 666 * Even if select says that input is available, read CAN block 820 tcsetattr(kbd_fd, TCSANOW, &term_less); 821 /* NB: select/poll returns whenever read will not block. Therefore: 822 * if eof is reached, select/poll will return immediately 823 * because read will immediately return 0 bytes. 824 * Even if select/poll says that input is available, read CAN block 667 825 * (switch fd into O_NONBLOCK'ed mode to avoid it) 668 826 */ 669 FD_ZERO(&readfds); 670 if (max_fline <= cur_fline + max_displayed_line 671 && eof_error > 0 /* did NOT reach eof yet */ 827 rd = 1; 828 /* Are we interested in stdin? */ 829 //TODO: reuse code for determining this 830 if (!(option_mask32 & FLAG_S) 831 ? !(max_fline > cur_fline + max_displayed_line) 832 : !(max_fline >= cur_fline 833 && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line) 672 834 ) { 673 /* We are interested in stdin */ 674 FD_SET(0, &readfds); 675 } 676 FD_SET(kbd_fd, &readfds); 677 tcsetattr(kbd_fd, TCSANOW, &term_less); 678 select(kbd_fd + 1, &readfds, NULL, NULL, NULL); 679 680 input[0] = '\0'; 681 ndelay_on(kbd_fd); 682 rd = read(kbd_fd, input, sz); 683 ndelay_off(kbd_fd); 684 if (rd < 0) { 685 /* No keyboard input, but we have input on stdin! */ 686 if (errno != EAGAIN) /* Huh?? */ 687 return; 688 read_lines(); 689 buffer_fill_and_print(); 835 if (eof_error > 0) /* did NOT reach eof yet */ 836 rd = 0; /* yes, we are interested in stdin */ 837 } 838 /* Position cursor if line input is done */ 839 if (less_gets_pos >= 0) 840 move_cursor(max_displayed_line + 2, less_gets_pos + 1); 841 fflush_all(); 842 843 if (kbd_input[0] == 0) { /* if nothing is buffered */ 844 #if ENABLE_FEATURE_LESS_WINCH 845 while (1) { 846 int r; 847 /* NB: SIGWINCH interrupts poll() */ 848 r = poll(pfd + rd, 2 - rd, -1); 849 if (/*r < 0 && errno == EINTR &&*/ winch_counter) 850 return '\\'; /* anything which has no defined function */ 851 if (r) break; 852 } 853 #else 854 safe_poll(pfd + rd, 2 - rd, -1); 855 #endif 856 } 857 858 /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() 859 * would not block even if there is no input available */ 860 rd = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); 861 if (rd == -1) { 862 if (errno == EAGAIN) { 863 /* No keyboard input available. Since poll() did return, 864 * we should have input on stdin */ 865 read_lines(); 866 buffer_fill_and_print(); 867 goto again; 868 } 869 /* EOF/error (ssh session got killed etc) */ 870 less_exit(0); 871 } 872 set_tty_cooked(); 873 return rd; 874 } 875 876 /* Grab a character from input without requiring the return key. 877 * May return KEYCODE_xxx values. 878 * Note that this function works best with raw input. */ 879 static int less_getch(int pos) 880 { 881 int i; 882 883 again: 884 less_gets_pos = pos; 885 i = getch_nowait(); 886 less_gets_pos = -1; 887 888 /* Discard Ctrl-something chars */ 889 if (i >= 0 && i < ' ' && i != 0x0d && i != 8) 690 890 goto again; 691 }692 }693 694 /* Grab a character from input without requiring the return key. If the695 * character is ASCII \033, get more characters and assign certain sequences696 * special return codes. Note that this function works best with raw input. */697 static int less_getch(void)698 {699 char input[16];700 unsigned i;701 again:702 memset(input, 0, sizeof(input));703 getch_nowait(input, sizeof(input));704 705 /* Detect escape sequences (i.e. arrow keys) and handle706 * them accordingly */707 if (input[0] == '\033' && input[1] == '[') {708 set_tty_cooked();709 i = input[2] - REAL_KEY_UP;710 if (i < 4)711 return 20 + i;712 i = input[2] - REAL_PAGE_UP;713 if (i < 4)714 return 24 + i;715 if (input[2] == REAL_KEY_HOME_XTERM)716 return KEY_HOME;717 if (input[2] == REAL_KEY_HOME_ALT)718 return KEY_HOME;719 if (input[2] == REAL_KEY_END_XTERM)720 return KEY_END;721 if (input[2] == REAL_KEY_END_ALT)722 return KEY_END;723 return 0;724 }725 /* Reject almost all control chars */726 i = input[0];727 if (i < ' ' && i != 0x0d && i != 8) goto again;728 set_tty_cooked();729 891 return i; 730 892 } … … 732 894 static char* less_gets(int sz) 733 895 { 734 charc;735 inti = 0;896 int c; 897 unsigned i = 0; 736 898 char *result = xzalloc(1); 899 737 900 while (1) { 738 fflush(stdout);739 740 /* I be damned if I know why is it needed *repeatedly*,741 * but it is needed. Is it because of stdio? */742 tcsetattr(kbd_fd, TCSANOW, &term_less);743 744 901 c = '\0'; 745 read(kbd_fd, &c, 1); 746 if (c == 0x0d) 902 less_gets_pos = sz + i; 903 c = getch_nowait(); 904 if (c == 0x0d) { 905 result[i] = '\0'; 906 less_gets_pos = -1; 747 907 return result; 908 } 748 909 if (c == 0x7f) 749 910 c = 8; … … 752 913 i--; 753 914 } 754 if (c < ' ') 915 if (c < ' ') /* filters out KEYCODE_xxx too (<0) */ 755 916 continue; 756 917 if (i >= width - sz - 1) 757 918 continue; /* len limit */ 758 putchar(c);919 bb_putchar(c); 759 920 result[i++] = c; 760 921 result = xrealloc(result, i+1); 761 result[i] = '\0';762 922 } 763 923 } … … 765 925 static void examine_file(void) 766 926 { 927 char *new_fname; 928 767 929 print_statusline("Examine: "); 930 new_fname = less_gets(sizeof("Examine: ") - 1); 931 if (!new_fname[0]) { 932 status_print(); 933 err: 934 free(new_fname); 935 return; 936 } 937 if (access(new_fname, R_OK) != 0) { 938 print_statusline("Cannot read this file"); 939 goto err; 940 } 768 941 free(filename); 769 filename = less_gets(sizeof("Examine: ")-1);942 filename = new_fname; 770 943 /* files start by = argv. why we assume that argv is infinitely long?? 771 944 files[num_files] = filename; … … 795 968 static void remove_current_file(void) 796 969 { 797 inti;970 unsigned i; 798 971 799 972 if (num_files < 2) … … 821 994 print_statusline(" :"); 822 995 823 keypress = less_getch( );996 keypress = less_getch(2); 824 997 switch (keypress) { 825 998 case 'd': … … 841 1014 break; 842 1015 case 'q': 843 less_exit( 0);1016 less_exit(EXIT_SUCCESS); 844 1017 break; 845 1018 case 'x': … … 861 1034 static void goto_match(int match) 862 1035 { 863 int sv;864 865 1036 if (!pattern_valid) 866 1037 return; 867 1038 if (match < 0) 868 1039 match = 0; 869 sv = cur_fline;870 1040 /* Try to find next match if eof isn't reached yet */ 871 1041 if (match >= num_matches && eof_error > 0) { 872 cur_fline = MAXLINES; /* look as far as needed*/1042 wanted_match = match; /* "I want to read until I see N'th match" */ 873 1043 read_lines(); 874 1044 } 875 1045 if (num_matches) { 876 cap_cur_fline(cur_fline);877 1046 normalize_match_pos(match); 878 1047 buffer_line(match_lines[match_pos]); 879 1048 } else { 880 cur_fline = sv;881 1049 print_statusline("No matches found"); 882 1050 } … … 894 1062 && !(num_matches && match_lines[num_matches-1] == pos) 895 1063 ) { 896 match_lines = xrealloc (match_lines, (num_matches+1) * sizeof(int));1064 match_lines = xrealloc_vector(match_lines, 4, num_matches); 897 1065 match_lines[num_matches++] = pos; 898 1066 } … … 917 1085 /* Get the uncompiled regular expression from the user */ 918 1086 clear_line(); 919 putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');1087 bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/'); 920 1088 uncomp_regex = less_gets(1); 921 1089 if (!uncomp_regex[0]) { … … 926 1094 927 1095 /* Compile the regex and check for errors */ 928 err = regcomp_or_errmsg(&pattern, uncomp_regex, 0); 1096 err = regcomp_or_errmsg(&pattern, uncomp_regex, 1097 (option_mask32 & FLAG_I) ? REG_ICASE : 0); 929 1098 free(uncomp_regex); 930 1099 if (err) { … … 938 1107 fill_match_lines(0); 939 1108 while (match_pos < num_matches) { 940 if ( match_lines[match_pos] > cur_fline)1109 if ((int)match_lines[match_pos] > cur_fline) 941 1110 break; 942 1111 match_pos++; … … 954 1123 static void number_process(int first_digit) 955 1124 { 956 int i = 1;1125 unsigned i; 957 1126 int num; 1127 int keypress; 958 1128 char num_input[sizeof(int)*4]; /* more than enough */ 959 char keypress;960 1129 961 1130 num_input[0] = first_digit; … … 966 1135 967 1136 /* Receive input until a letter is given */ 1137 i = 1; 968 1138 while (i < sizeof(num_input)-1) { 969 num_input[i] = less_getch();970 if ( !num_input[i]|| !isdigit(num_input[i]))1139 keypress = less_getch(i + 1); 1140 if ((unsigned)keypress > 255 || !isdigit(num_input[i])) 971 1141 break; 972 putchar(num_input[i]); 1142 num_input[i] = keypress; 1143 bb_putchar(keypress); 973 1144 i++; 974 1145 } 975 1146 976 /* Take the final letter out of the digits string */977 keypress = num_input[i];978 1147 num_input[i] = '\0'; 979 1148 num = bb_strtou(num_input, NULL, 10); … … 986 1155 /* We now know the number and the letter entered, so we process them */ 987 1156 switch (keypress) { 988 case KEY _DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':1157 case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': 989 1158 buffer_down(num); 990 1159 break; 991 case KEY _UP: case 'b': case 'w': case 'y': case 'u':1160 case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u': 992 1161 buffer_up(num); 993 1162 break; … … 1019 1188 } 1020 1189 1021 #if ENABLE_FEATURE_LESS_ FLAGCS1190 #if ENABLE_FEATURE_LESS_DASHCMD 1022 1191 static void flag_change(void) 1023 1192 { … … 1025 1194 1026 1195 clear_line(); 1027 putchar('-');1028 keypress = less_getch( );1196 bb_putchar('-'); 1197 keypress = less_getch(1); 1029 1198 1030 1199 switch (keypress) { … … 1041 1210 option_mask32 ^= FLAG_TILDE; 1042 1211 break; 1043 } 1044 } 1045 1212 case 'S': 1213 option_mask32 ^= FLAG_S; 1214 buffer_fill_and_print(); 1215 break; 1216 #if ENABLE_FEATURE_LESS_LINENUMS 1217 case 'N': 1218 option_mask32 ^= FLAG_N; 1219 re_wrap(); 1220 buffer_fill_and_print(); 1221 break; 1222 #endif 1223 } 1224 } 1225 1226 #ifdef BLOAT 1046 1227 static void show_flag_status(void) 1047 1228 { … … 1050 1231 1051 1232 clear_line(); 1052 putchar('_');1053 keypress = less_getch( );1233 bb_putchar('_'); 1234 keypress = less_getch(1); 1054 1235 1055 1236 switch (keypress) { … … 1079 1260 #endif 1080 1261 1262 #endif /* ENABLE_FEATURE_LESS_DASHCMD */ 1263 1081 1264 static void save_input_to_file(void) 1082 1265 { 1083 1266 const char *msg = ""; 1084 1267 char *current_line; 1085 inti;1268 unsigned i; 1086 1269 FILE *fp; 1087 1270 1088 1271 print_statusline("Log file: "); 1089 1272 current_line = less_gets(sizeof("Log file: ")-1); 1090 if ( strlen(current_line) > 0) {1091 fp = fopen (current_line, "w");1273 if (current_line[0]) { 1274 fp = fopen_for_write(current_line); 1092 1275 if (!fp) { 1093 1276 msg = "Error opening log file"; … … 1110 1293 1111 1294 print_statusline("Mark: "); 1112 letter = less_getch( );1295 letter = less_getch(sizeof("Mark: ") - 1); 1113 1296 1114 1297 if (isalpha(letter)) { … … 1131 1314 1132 1315 print_statusline("Go to mark: "); 1133 letter = less_getch( );1316 letter = less_getch(sizeof("Go to mark: ") - 1); 1134 1317 clear_line(); 1135 1318 … … 1151 1334 { 1152 1335 switch (bracket) { 1153 case '{': case '[': 1154 return bracket + 2; 1155 case '(': 1156 return ')'; 1157 case '}': case ']': 1158 return bracket - 2; 1159 case ')': 1160 return '('; 1161 } 1162 return 0; 1336 case '{': case '[': /* '}' == '{' + 2. Same for '[' */ 1337 bracket++; 1338 case '(': /* ')' == '(' + 1 */ 1339 bracket++; 1340 break; 1341 case '}': case ']': 1342 bracket--; 1343 case ')': 1344 bracket--; 1345 break; 1346 }; 1347 return bracket; 1163 1348 } 1164 1349 1165 1350 static void match_right_bracket(char bracket) 1166 1351 { 1167 int bracket_line = -1; 1168 int i; 1352 unsigned i; 1169 1353 1170 1354 if (strchr(flines[cur_fline], bracket) == NULL) { … … 1172 1356 return; 1173 1357 } 1358 bracket = opp_bracket(bracket); 1174 1359 for (i = cur_fline + 1; i < max_fline; i++) { 1175 if (strchr(flines[i], opp_bracket(bracket)) != NULL) { 1176 bracket_line = i; 1177 break; 1178 } 1179 } 1180 if (bracket_line == -1) 1181 print_statusline("No matching bracket found"); 1182 buffer_line(bracket_line - max_displayed_line); 1360 if (strchr(flines[i], bracket) != NULL) { 1361 buffer_line(i); 1362 return; 1363 } 1364 } 1365 print_statusline("No matching bracket found"); 1183 1366 } 1184 1367 1185 1368 static void match_left_bracket(char bracket) 1186 1369 { 1187 int bracket_line = -1;1188 1370 int i; 1189 1371 … … 1193 1375 } 1194 1376 1377 bracket = opp_bracket(bracket); 1195 1378 for (i = cur_fline + max_displayed_line; i >= 0; i--) { 1196 if (strchr(flines[i], opp_bracket(bracket)) != NULL) { 1197 bracket_line = i; 1198 break; 1199 } 1200 } 1201 if (bracket_line == -1) 1202 print_statusline("No matching bracket found"); 1203 buffer_line(bracket_line); 1379 if (strchr(flines[i], bracket) != NULL) { 1380 buffer_line(i); 1381 return; 1382 } 1383 } 1384 print_statusline("No matching bracket found"); 1204 1385 } 1205 1386 #endif /* FEATURE_LESS_BRACKETS */ … … 1208 1389 { 1209 1390 switch (keypress) { 1210 case KEY _DOWN: case 'e': case 'j': case 0x0d:1391 case KEYCODE_DOWN: case 'e': case 'j': case 0x0d: 1211 1392 buffer_down(1); 1212 1393 break; 1213 case KEY _UP: case 'y': case 'k':1394 case KEYCODE_UP: case 'y': case 'k': 1214 1395 buffer_up(1); 1215 1396 break; 1216 case PAGE_DOWN: case ' ': case 'z':1397 case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f': 1217 1398 buffer_down(max_displayed_line + 1); 1218 1399 break; 1219 case PAGE_UP: case 'w': case 'b':1400 case KEYCODE_PAGEUP: case 'w': case 'b': 1220 1401 buffer_up(max_displayed_line + 1); 1221 1402 break; … … 1226 1407 buffer_up((max_displayed_line + 1) / 2); 1227 1408 break; 1228 case KEY _HOME: case 'g': case 'p': case '<': case '%':1409 case KEYCODE_HOME: case 'g': case 'p': case '<': case '%': 1229 1410 buffer_line(0); 1230 1411 break; 1231 case KEY _END: case 'G': case '>':1412 case KEYCODE_END: case 'G': case '>': 1232 1413 cur_fline = MAXLINES; 1233 1414 read_lines(); … … 1235 1416 break; 1236 1417 case 'q': case 'Q': 1237 less_exit( 0);1418 less_exit(EXIT_SUCCESS); 1238 1419 break; 1239 1420 #if ENABLE_FEATURE_LESS_MARKS … … 1280 1461 break; 1281 1462 #endif 1282 #if ENABLE_FEATURE_LESS_ FLAGCS1463 #if ENABLE_FEATURE_LESS_DASHCMD 1283 1464 case '-': 1284 1465 flag_change(); 1285 1466 buffer_print(); 1286 1467 break; 1468 #ifdef BLOAT 1287 1469 case '_': 1288 1470 show_flag_status(); 1289 1471 break; 1472 #endif 1290 1473 #endif 1291 1474 #if ENABLE_FEATURE_LESS_BRACKETS … … 1306 1489 } 1307 1490 1308 static void sig_catcher(int sig ATTRIBUTE_UNUSED) 1309 { 1310 set_tty_cooked(); 1311 exit(1); 1312 } 1313 1314 int less_main(int argc, char **argv); 1491 static void sig_catcher(int sig) 1492 { 1493 less_exit(- sig); 1494 } 1495 1496 #if ENABLE_FEATURE_LESS_WINCH 1497 static void sigwinch_handler(int sig UNUSED_PARAM) 1498 { 1499 winch_counter++; 1500 } 1501 #endif 1502 1503 int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1315 1504 int less_main(int argc, char **argv) 1316 1505 { … … 1322 1511 /* -xxx: newline also */ 1323 1512 /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */ 1324 getopt32(argv, "EMmN~ ");1513 getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S")); 1325 1514 argc -= optind; 1326 1515 argv += optind; … … 1331 1520 * is not a tty and turns into cat. This makes sense. */ 1332 1521 if (!isatty(STDOUT_FILENO)) 1333 return bb_cat(argv);1334 kbd_fd = open(CURRENT_TTY, O_RDONLY);1335 if (kbd_fd < 0)1336 1522 return bb_cat(argv); 1337 1523 … … 1342 1528 bb_show_usage(); 1343 1529 } 1344 } else 1530 } else { 1345 1531 filename = xstrdup(files[0]); 1346 1347 get_terminal_width_height(kbd_fd, &width, &max_displayed_line); 1348 /* 20: two tabstops + 4 */ 1349 if (width < 20 || max_displayed_line < 3) 1350 bb_error_msg_and_die("too narrow here"); 1351 max_displayed_line -= 2; 1352 1353 buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); 1532 } 1533 1354 1534 if (option_mask32 & FLAG_TILDE) 1355 1535 empty_line_marker = ""; 1356 1536 1537 kbd_fd = open(CURRENT_TTY, O_RDONLY); 1538 if (kbd_fd < 0) 1539 return bb_cat(argv); 1540 ndelay_on(kbd_fd); 1541 1357 1542 tcgetattr(kbd_fd, &term_orig); 1358 signal(SIGTERM, sig_catcher);1359 signal(SIGINT, sig_catcher);1360 1543 term_less = term_orig; 1361 1544 term_less.c_lflag &= ~(ICANON | ECHO); … … 1365 1548 term_less.c_cc[VTIME] = 0; 1366 1549 1367 /* Want to do it just once, but it doesn't work, */ 1368 /* so we are redoing it (see code above). Mystery... */ 1369 /*tcsetattr(kbd_fd, TCSANOW, &term_less);*/ 1370 1550 get_terminal_width_height(kbd_fd, &width, &max_displayed_line); 1551 /* 20: two tabstops + 4 */ 1552 if (width < 20 || max_displayed_line < 3) 1553 return bb_cat(argv); 1554 max_displayed_line -= 2; 1555 1556 /* We want to restore term_orig on exit */ 1557 bb_signals(BB_FATAL_SIGS, sig_catcher); 1558 #if ENABLE_FEATURE_LESS_WINCH 1559 signal(SIGWINCH, sigwinch_handler); 1560 #endif 1561 1562 buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); 1371 1563 reinitialize(); 1372 1564 while (1) { 1373 keypress = less_getch(); 1565 #if ENABLE_FEATURE_LESS_WINCH 1566 while (WINCH_COUNTER) { 1567 again: 1568 winch_counter--; 1569 get_terminal_width_height(kbd_fd, &width, &max_displayed_line); 1570 /* 20: two tabstops + 4 */ 1571 if (width < 20) 1572 width = 20; 1573 if (max_displayed_line < 3) 1574 max_displayed_line = 3; 1575 max_displayed_line -= 2; 1576 free(buffer); 1577 buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); 1578 /* Avoid re-wrap and/or redraw if we already know 1579 * we need to do it again. These ops are expensive */ 1580 if (WINCH_COUNTER) 1581 goto again; 1582 re_wrap(); 1583 if (WINCH_COUNTER) 1584 goto again; 1585 buffer_fill_and_print(); 1586 /* This took some time. Loop back and check, 1587 * were there another SIGWINCH? */ 1588 } 1589 #endif 1590 keypress = less_getch(-1); /* -1: do not position cursor */ 1374 1591 keypress_process(keypress); 1375 1592 } 1376 1593 } 1594 1595 /* 1596 Help text of less version 418 is below. 1597 If you are implementing something, keeping 1598 key and/or command line switch compatibility is a good idea: 1599 1600 1601 SUMMARY OF LESS COMMANDS 1602 1603 Commands marked with * may be preceded by a number, N. 1604 Notes in parentheses indicate the behavior if N is given. 1605 h H Display this help. 1606 q :q Q :Q ZZ Exit. 1607 --------------------------------------------------------------------------- 1608 MOVING 1609 e ^E j ^N CR * Forward one line (or N lines). 1610 y ^Y k ^K ^P * Backward one line (or N lines). 1611 f ^F ^V SPACE * Forward one window (or N lines). 1612 b ^B ESC-v * Backward one window (or N lines). 1613 z * Forward one window (and set window to N). 1614 w * Backward one window (and set window to N). 1615 ESC-SPACE * Forward one window, but don't stop at end-of-file. 1616 d ^D * Forward one half-window (and set half-window to N). 1617 u ^U * Backward one half-window (and set half-window to N). 1618 ESC-) RightArrow * Left one half screen width (or N positions). 1619 ESC-( LeftArrow * Right one half screen width (or N positions). 1620 F Forward forever; like "tail -f". 1621 r ^R ^L Repaint screen. 1622 R Repaint screen, discarding buffered input. 1623 --------------------------------------------------- 1624 Default "window" is the screen height. 1625 Default "half-window" is half of the screen height. 1626 --------------------------------------------------------------------------- 1627 SEARCHING 1628 /pattern * Search forward for (N-th) matching line. 1629 ?pattern * Search backward for (N-th) matching line. 1630 n * Repeat previous search (for N-th occurrence). 1631 N * Repeat previous search in reverse direction. 1632 ESC-n * Repeat previous search, spanning files. 1633 ESC-N * Repeat previous search, reverse dir. & spanning files. 1634 ESC-u Undo (toggle) search highlighting. 1635 --------------------------------------------------- 1636 Search patterns may be modified by one or more of: 1637 ^N or ! Search for NON-matching lines. 1638 ^E or * Search multiple files (pass thru END OF FILE). 1639 ^F or @ Start search at FIRST file (for /) or last file (for ?). 1640 ^K Highlight matches, but don't move (KEEP position). 1641 ^R Don't use REGULAR EXPRESSIONS. 1642 --------------------------------------------------------------------------- 1643 JUMPING 1644 g < ESC-< * Go to first line in file (or line N). 1645 G > ESC-> * Go to last line in file (or line N). 1646 p % * Go to beginning of file (or N percent into file). 1647 t * Go to the (N-th) next tag. 1648 T * Go to the (N-th) previous tag. 1649 { ( [ * Find close bracket } ) ]. 1650 } ) ] * Find open bracket { ( [. 1651 ESC-^F <c1> <c2> * Find close bracket <c2>. 1652 ESC-^B <c1> <c2> * Find open bracket <c1> 1653 --------------------------------------------------- 1654 Each "find close bracket" command goes forward to the close bracket 1655 matching the (N-th) open bracket in the top line. 1656 Each "find open bracket" command goes backward to the open bracket 1657 matching the (N-th) close bracket in the bottom line. 1658 m<letter> Mark the current position with <letter>. 1659 '<letter> Go to a previously marked position. 1660 '' Go to the previous position. 1661 ^X^X Same as '. 1662 --------------------------------------------------- 1663 A mark is any upper-case or lower-case letter. 1664 Certain marks are predefined: 1665 ^ means beginning of the file 1666 $ means end of the file 1667 --------------------------------------------------------------------------- 1668 CHANGING FILES 1669 :e [file] Examine a new file. 1670 ^X^V Same as :e. 1671 :n * Examine the (N-th) next file from the command line. 1672 :p * Examine the (N-th) previous file from the command line. 1673 :x * Examine the first (or N-th) file from the command line. 1674 :d Delete the current file from the command line list. 1675 = ^G :f Print current file name. 1676 --------------------------------------------------------------------------- 1677 MISCELLANEOUS COMMANDS 1678 -<flag> Toggle a command line option [see OPTIONS below]. 1679 --<name> Toggle a command line option, by name. 1680 _<flag> Display the setting of a command line option. 1681 __<name> Display the setting of an option, by name. 1682 +cmd Execute the less cmd each time a new file is examined. 1683 !command Execute the shell command with $SHELL. 1684 |Xcommand Pipe file between current pos & mark X to shell command. 1685 v Edit the current file with $VISUAL or $EDITOR. 1686 V Print version number of "less". 1687 --------------------------------------------------------------------------- 1688 OPTIONS 1689 Most options may be changed either on the command line, 1690 or from within less by using the - or -- command. 1691 Options may be given in one of two forms: either a single 1692 character preceded by a -, or a name preceeded by --. 1693 -? ........ --help 1694 Display help (from command line). 1695 -a ........ --search-skip-screen 1696 Forward search skips current screen. 1697 -b [N] .... --buffers=[N] 1698 Number of buffers. 1699 -B ........ --auto-buffers 1700 Don't automatically allocate buffers for pipes. 1701 -c ........ --clear-screen 1702 Repaint by clearing rather than scrolling. 1703 -d ........ --dumb 1704 Dumb terminal. 1705 -D [xn.n] . --color=xn.n 1706 Set screen colors. (MS-DOS only) 1707 -e -E .... --quit-at-eof --QUIT-AT-EOF 1708 Quit at end of file. 1709 -f ........ --force 1710 Force open non-regular files. 1711 -F ........ --quit-if-one-screen 1712 Quit if entire file fits on first screen. 1713 -g ........ --hilite-search 1714 Highlight only last match for searches. 1715 -G ........ --HILITE-SEARCH 1716 Don't highlight any matches for searches. 1717 -h [N] .... --max-back-scroll=[N] 1718 Backward scroll limit. 1719 -i ........ --ignore-case 1720 Ignore case in searches that do not contain uppercase. 1721 -I ........ --IGNORE-CASE 1722 Ignore case in all searches. 1723 -j [N] .... --jump-target=[N] 1724 Screen position of target lines. 1725 -J ........ --status-column 1726 Display a status column at left edge of screen. 1727 -k [file] . --lesskey-file=[file] 1728 Use a lesskey file. 1729 -L ........ --no-lessopen 1730 Ignore the LESSOPEN environment variable. 1731 -m -M .... --long-prompt --LONG-PROMPT 1732 Set prompt style. 1733 -n -N .... --line-numbers --LINE-NUMBERS 1734 Don't use line numbers. 1735 -o [file] . --log-file=[file] 1736 Copy to log file (standard input only). 1737 -O [file] . --LOG-FILE=[file] 1738 Copy to log file (unconditionally overwrite). 1739 -p [pattern] --pattern=[pattern] 1740 Start at pattern (from command line). 1741 -P [prompt] --prompt=[prompt] 1742 Define new prompt. 1743 -q -Q .... --quiet --QUIET --silent --SILENT 1744 Quiet the terminal bell. 1745 -r -R .... --raw-control-chars --RAW-CONTROL-CHARS 1746 Output "raw" control characters. 1747 -s ........ --squeeze-blank-lines 1748 Squeeze multiple blank lines. 1749 -S ........ --chop-long-lines 1750 Chop long lines. 1751 -t [tag] .. --tag=[tag] 1752 Find a tag. 1753 -T [tagsfile] --tag-file=[tagsfile] 1754 Use an alternate tags file. 1755 -u -U .... --underline-special --UNDERLINE-SPECIAL 1756 Change handling of backspaces. 1757 -V ........ --version 1758 Display the version number of "less". 1759 -w ........ --hilite-unread 1760 Highlight first new line after forward-screen. 1761 -W ........ --HILITE-UNREAD 1762 Highlight first new line after any forward movement. 1763 -x [N[,...]] --tabs=[N[,...]] 1764 Set tab stops. 1765 -X ........ --no-init 1766 Don't use termcap init/deinit strings. 1767 --no-keypad 1768 Don't use termcap keypad init/deinit strings. 1769 -y [N] .... --max-forw-scroll=[N] 1770 Forward scroll limit. 1771 -z [N] .... --window=[N] 1772 Set size of window. 1773 -" [c[c]] . --quotes=[c[c]] 1774 Set shell quote characters. 1775 -~ ........ --tilde 1776 Don't display tildes after end of file. 1777 -# [N] .... --shift=[N] 1778 Horizontal scroll amount (0 = one half screen width) 1779 1780 --------------------------------------------------------------------------- 1781 LINE EDITING 1782 These keys can be used to edit text being entered 1783 on the "command line" at the bottom of the screen. 1784 RightArrow ESC-l Move cursor right one character. 1785 LeftArrow ESC-h Move cursor left one character. 1786 CNTL-RightArrow ESC-RightArrow ESC-w Move cursor right one word. 1787 CNTL-LeftArrow ESC-LeftArrow ESC-b Move cursor left one word. 1788 HOME ESC-0 Move cursor to start of line. 1789 END ESC-$ Move cursor to end of line. 1790 BACKSPACE Delete char to left of cursor. 1791 DELETE ESC-x Delete char under cursor. 1792 CNTL-BACKSPACE ESC-BACKSPACE Delete word to left of cursor. 1793 CNTL-DELETE ESC-DELETE ESC-X Delete word under cursor. 1794 CNTL-U ESC (MS-DOS only) Delete entire line. 1795 UpArrow ESC-k Retrieve previous command line. 1796 DownArrow ESC-j Retrieve next command line. 1797 TAB Complete filename & cycle. 1798 SHIFT-TAB ESC-TAB Complete filename & reverse cycle. 1799 CNTL-L Complete filename, list all. 1800 */
Note:
See TracChangeset
for help on using the changeset viewer.