diff options
Diffstat (limited to 'miscutils/less.c')
| -rw-r--r-- | miscutils/less.c | 484 |
1 files changed, 236 insertions, 248 deletions
diff --git a/miscutils/less.c b/miscutils/less.c index 73500671d..6843dddcb 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
| @@ -8,30 +8,20 @@ | |||
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | /* | 10 | /* |
| 11 | * This program needs a lot of development, so consider it in a beta stage | 11 | * TODO: |
| 12 | * at best. | 12 | * - Add more regular expression support - search modifiers, certain matches, etc. |
| 13 | * - Add more complex bracket searching - currently, nested brackets are | ||
| 14 | * not considered. | ||
| 15 | * - Add support for "F" as an input. This causes less to act in | ||
| 16 | * a similar way to tail -f. | ||
| 17 | * - Allow horizontal scrolling. | ||
| 13 | * | 18 | * |
| 14 | * TODO: | 19 | * Notes: |
| 15 | * - Add more regular expression support - search modifiers, certain matches, etc. | 20 | * - the inp file pointer is used so that keyboard input works after |
| 16 | * - Add more complex bracket searching - currently, nested brackets are | 21 | * redirected input has been read from stdin |
| 17 | * not considered. | 22 | */ |
| 18 | * - Add support for "F" as an input. This causes less to act in | ||
| 19 | * a similar way to tail -f. | ||
| 20 | * - Check for binary files, and prompt the user if a binary file | ||
| 21 | * is detected. | ||
| 22 | * - Allow horizontal scrolling. Currently, lines simply continue onto | ||
| 23 | * the next line, per the terminal's discretion | ||
| 24 | * | ||
| 25 | * Notes: | ||
| 26 | * - filename is an array and not a pointer because that avoids all sorts | ||
| 27 | * of complications involving the fact that something that is pointed to | ||
| 28 | * will be changed if the pointer is changed. | ||
| 29 | * - the inp file pointer is used so that keyboard input works after | ||
| 30 | * redirected input has been read from stdin | ||
| 31 | */ | ||
| 32 | 23 | ||
| 33 | #include "busybox.h" | 24 | #include "busybox.h" |
| 34 | |||
| 35 | #if ENABLE_FEATURE_LESS_REGEXP | 25 | #if ENABLE_FEATURE_LESS_REGEXP |
| 36 | #include "xregex.h" | 26 | #include "xregex.h" |
| 37 | #endif | 27 | #endif |
| @@ -70,12 +60,13 @@ static int height; | |||
| 70 | static int width; | 60 | static int width; |
| 71 | static char **files; | 61 | static char **files; |
| 72 | static char *filename; | 62 | static char *filename; |
| 73 | static char **buffer; | 63 | static const char **buffer; |
| 74 | static char **flines; | 64 | static char **flines; |
| 75 | static int current_file = 1; | 65 | static int current_file = 1; |
| 76 | static int line_pos; | 66 | static int line_pos; |
| 77 | static int num_flines; | 67 | static int num_flines; |
| 78 | static int num_files = 1; | 68 | static int num_files = 1; |
| 69 | static const char *empty_line_marker = "~"; | ||
| 79 | 70 | ||
| 80 | /* Command line options */ | 71 | /* Command line options */ |
| 81 | #define FLAG_E 1 | 72 | #define FLAG_E 1 |
| @@ -84,7 +75,6 @@ static int num_files = 1; | |||
| 84 | #define FLAG_N (1<<3) | 75 | #define FLAG_N (1<<3) |
| 85 | #define FLAG_TILDE (1<<4) | 76 | #define FLAG_TILDE (1<<4) |
| 86 | /* hijack command line options variable for internal state vars */ | 77 | /* hijack command line options variable for internal state vars */ |
| 87 | #define LESS_STATE_PAST_EOF (1<<5) | ||
| 88 | #define LESS_STATE_MATCH_BACKWARDS (1<<6) | 78 | #define LESS_STATE_MATCH_BACKWARDS (1<<6) |
| 89 | 79 | ||
| 90 | #if ENABLE_FEATURE_LESS_MARKS | 80 | #if ENABLE_FEATURE_LESS_MARKS |
| @@ -93,11 +83,11 @@ static int num_marks; | |||
| 93 | #endif | 83 | #endif |
| 94 | 84 | ||
| 95 | #if ENABLE_FEATURE_LESS_REGEXP | 85 | #if ENABLE_FEATURE_LESS_REGEXP |
| 96 | static int match_found; | ||
| 97 | static int *match_lines; | 86 | static int *match_lines; |
| 98 | static int match_pos; | 87 | static int match_pos; |
| 99 | static int num_matches; | 88 | static int num_matches; |
| 100 | static regex_t old_pattern; | 89 | static regex_t pattern; |
| 90 | static int pattern_valid; | ||
| 101 | #endif | 91 | #endif |
| 102 | 92 | ||
| 103 | /* Needed termios structures */ | 93 | /* Needed termios structures */ |
| @@ -174,52 +164,57 @@ static void data_readlines(void) | |||
| 174 | unsigned i; | 164 | unsigned i; |
| 175 | unsigned n = 1; | 165 | unsigned n = 1; |
| 176 | int w = width; | 166 | int w = width; |
| 177 | char *last_nl = (char*)1; /* "not NULL" */ | 167 | /* "remained unused space" in a line (0 if line fills entire width) */ |
| 178 | char *current_line; | 168 | int rem, last_rem = 1; /* "not 0" */ |
| 169 | char *current_line, *p; | ||
| 179 | FILE *fp; | 170 | FILE *fp; |
| 180 | 171 | ||
| 181 | fp = filename ? xfopen(filename, "r") : stdin; | 172 | fp = filename ? xfopen(filename, "r") : stdin; |
| 182 | flines = NULL; | 173 | flines = NULL; |
| 183 | if (option_mask32 & FLAG_N) { | 174 | if (option_mask32 & FLAG_N) { |
| 184 | w -= 6; | 175 | w -= 6; |
| 185 | if (w < 1) w = 1; /* paranoia */ | ||
| 186 | } | 176 | } |
| 187 | for (i = 0; !feof(fp) && i <= MAXLINES; i++) { | 177 | for (i = 0; !feof(fp) && i <= MAXLINES; i++) { |
| 188 | flines = xrealloc(flines, (i+1) * sizeof(char *)); | 178 | flines = xrealloc(flines, (i+1) * sizeof(char *)); |
| 189 | |||
| 190 | current_line = xmalloc(w); | 179 | current_line = xmalloc(w); |
| 191 | again: | 180 | again: |
| 192 | current_line[0] = '\0'; | 181 | p = current_line; |
| 193 | fgets(current_line, w, fp); | 182 | rem = w - 1; |
| 183 | do { | ||
| 184 | int c = getc(fp); | ||
| 185 | if (c == EOF) break; | ||
| 186 | if (c == '\n') break; | ||
| 187 | /* NUL is substituted by '\n'! */ | ||
| 188 | if (c == '\0') c = '\n'; | ||
| 189 | *p++ = c; | ||
| 190 | } while (--rem); | ||
| 191 | *p = '\0'; | ||
| 194 | if (fp != stdin) | 192 | if (fp != stdin) |
| 195 | die_if_ferror(fp, filename); | 193 | die_if_ferror(fp, filename); |
| 196 | 194 | ||
| 197 | /* Corner case: linewrap with only '\n' wrapping */ | 195 | /* Corner case: linewrap with only "" wrapping to next line */ |
| 198 | /* Looks ugly on screen, so we handle it specially */ | 196 | /* Looks ugly on screen, so we do not store this empty line */ |
| 199 | if (!last_nl && current_line[0] == '\n') { | 197 | if (!last_rem && !current_line[0]) { |
| 200 | last_nl = (char*)1; /* "not NULL" */ | 198 | last_rem = 1; /* "not 0" */ |
| 201 | n++; | 199 | n++; |
| 202 | goto again; | 200 | goto again; |
| 203 | } | 201 | } |
| 204 | last_nl = last_char_is(current_line, '\n'); | 202 | last_rem = rem; |
| 205 | if (last_nl) | ||
| 206 | *last_nl = '\0'; | ||
| 207 | if (option_mask32 & FLAG_N) { | 203 | if (option_mask32 & FLAG_N) { |
| 208 | flines[i] = xasprintf((n <= 99999) ? "%5u %s" : "%05u %s", | 204 | flines[i] = xasprintf((n <= 99999) ? "%5u %s" : "%05u %s", |
| 209 | n % 100000, current_line); | 205 | n % 100000, current_line); |
| 210 | free(current_line); | 206 | free(current_line); |
| 211 | if (last_nl) | 207 | if (rem) |
| 212 | n++; | 208 | n++; |
| 213 | } else { | 209 | } else { |
| 214 | flines[i] = xrealloc(current_line, strlen(current_line)+1); | 210 | flines[i] = xrealloc(current_line, strlen(current_line)+1); |
| 215 | } | 211 | } |
| 216 | } | 212 | } |
| 217 | num_flines = i - 2; | 213 | num_flines = i - 1; /* buggie: 'num_flines' must be 'max_fline' */ |
| 218 | 214 | ||
| 219 | /* Reset variables for a new file */ | 215 | /* Reset variables for a new file */ |
| 220 | 216 | ||
| 221 | line_pos = 0; | 217 | line_pos = 0; |
| 222 | option_mask32 &= ~LESS_STATE_PAST_EOF; | ||
| 223 | 218 | ||
| 224 | fclose(fp); | 219 | fclose(fp); |
| 225 | } | 220 | } |
| @@ -238,36 +233,28 @@ static void m_status_print(void) | |||
| 238 | { | 233 | { |
| 239 | int percentage; | 234 | int percentage; |
| 240 | 235 | ||
| 241 | if (!(option_mask32 & LESS_STATE_PAST_EOF)) { | 236 | if (!line_pos) { |
| 242 | if (!line_pos) { | 237 | if (num_files > 1) { |
| 243 | if (num_files > 1) { | 238 | printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, |
| 244 | printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, | 239 | filename, "(file ", current_file, " of ", num_files, ") lines ", |
| 245 | filename, "(file ", current_file, " of ", num_files, ") lines ", | ||
| 246 | line_pos + 1, line_pos + height - 1, num_flines + 1); | ||
| 247 | } else { | ||
| 248 | printf("%s%s lines %i-%i/%i ", HIGHLIGHT, | ||
| 249 | filename, line_pos + 1, line_pos + height - 1, | ||
| 250 | num_flines + 1); | ||
| 251 | } | ||
| 252 | } else { | ||
| 253 | printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, | ||
| 254 | line_pos + 1, line_pos + height - 1, num_flines + 1); | 240 | line_pos + 1, line_pos + height - 1, num_flines + 1); |
| 255 | } | ||
| 256 | |||
| 257 | if (line_pos == num_flines - height + 2) { | ||
| 258 | printf("(END) %s", NORMAL); | ||
| 259 | if ((num_files > 1) && (current_file != num_files)) | ||
| 260 | printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL); | ||
| 261 | } else { | 241 | } else { |
| 262 | percentage = calc_percent(); | 242 | printf("%s%s lines %i-%i/%i ", HIGHLIGHT, |
| 263 | printf("%i%% %s", percentage, NORMAL); | 243 | filename, line_pos + 1, line_pos + height - 1, |
| 244 | num_flines + 1); | ||
| 264 | } | 245 | } |
| 265 | } else { | 246 | } else { |
| 266 | printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, | 247 | printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, |
| 267 | line_pos + 1, num_flines + 1, num_flines + 1); | 248 | line_pos + 1, line_pos + height - 1, num_flines + 1); |
| 249 | } | ||
| 250 | |||
| 251 | if (line_pos >= num_flines - height + 2) { | ||
| 252 | printf("(END) %s", NORMAL); | ||
| 268 | if ((num_files > 1) && (current_file != num_files)) | 253 | if ((num_files > 1) && (current_file != num_files)) |
| 269 | printf("- Next: %s", files[current_file]); | 254 | printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL); |
| 270 | printf("%s", NORMAL); | 255 | } else { |
| 256 | percentage = calc_percent(); | ||
| 257 | printf("%i%% %s", percentage, NORMAL); | ||
| 271 | } | 258 | } |
| 272 | } | 259 | } |
| 273 | 260 | ||
| @@ -315,22 +302,123 @@ static void status_print(void) | |||
| 315 | #endif | 302 | #endif |
| 316 | } | 303 | } |
| 317 | 304 | ||
| 305 | static char controls[] = | ||
| 306 | /**/"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" | ||
| 307 | "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" | ||
| 308 | "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */ | ||
| 309 | static char ctrlconv[] = | ||
| 310 | /* Note that on input NUL is converted to '\n' ('\x0a') */ | ||
| 311 | /* Therefore we subst '\n' with '@', not 'J' */ | ||
| 312 | "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f" | ||
| 313 | "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"; | ||
| 314 | |||
| 315 | static void print_found(const char *line) | ||
| 316 | { | ||
| 317 | int match_status; | ||
| 318 | int eflags; | ||
| 319 | char *growline; | ||
| 320 | regmatch_t match_structs; | ||
| 321 | |||
| 322 | char buf[width]; | ||
| 323 | const char *str = line; | ||
| 324 | char *p = buf; | ||
| 325 | size_t n; | ||
| 326 | |||
| 327 | while (*str) { | ||
| 328 | n = strcspn(str, controls); | ||
| 329 | if (n) { | ||
| 330 | if (!str[n]) break; | ||
| 331 | memcpy(p, str, n); | ||
| 332 | p += n; | ||
| 333 | str += n; | ||
| 334 | } | ||
| 335 | n = strspn(str, controls); | ||
| 336 | memset(p, '.', n); | ||
| 337 | p += n; | ||
| 338 | str += n; | ||
| 339 | /* | ||
| 340 | do { | ||
| 341 | if (*str == '\x7f') { *p++ = '?'; str++; } | ||
| 342 | else if (*str == '\x9b') { *p++ = '{'; str++; } | ||
| 343 | else *p++ = ctrlconv[(unsigned char)*str++]; | ||
| 344 | } while (--n); | ||
| 345 | */ | ||
| 346 | } | ||
| 347 | strcpy(p, str); | ||
| 348 | |||
| 349 | /* buf[] holds quarantined version of str */ | ||
| 350 | |||
| 351 | /* Each part of the line that matches has the HIGHLIGHT | ||
| 352 | and NORMAL escape sequences placed around it. | ||
| 353 | NB: we regex against line, but insert text | ||
| 354 | from quarantined copy (buf[]) */ | ||
| 355 | str = buf; | ||
| 356 | growline = NULL; | ||
| 357 | eflags = 0; | ||
| 358 | goto start; | ||
| 359 | |||
| 360 | while (match_status == 0) { | ||
| 361 | char *new = xasprintf("%s" "%.*s" "%s" "%.*s" "%s", | ||
| 362 | growline ? : "", | ||
| 363 | match_structs.rm_so, str, | ||
| 364 | HIGHLIGHT, | ||
| 365 | match_structs.rm_eo - match_structs.rm_so, | ||
| 366 | str + match_structs.rm_so, | ||
| 367 | NORMAL); | ||
| 368 | free(growline); growline = new; | ||
| 369 | str += match_structs.rm_eo; | ||
| 370 | line += match_structs.rm_eo; | ||
| 371 | eflags = REG_NOTBOL; | ||
| 372 | start: | ||
| 373 | /* Most of the time doesn't find the regex, optimize for that */ | ||
| 374 | match_status = regexec(&pattern, line, 1, &match_structs, eflags); | ||
| 375 | } | ||
| 376 | |||
| 377 | if (!growline) { | ||
| 378 | puts(str); | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | printf("%s%s\n", growline, str); | ||
| 382 | free(growline); | ||
| 383 | } | ||
| 384 | |||
| 385 | static void print_ascii(const char *str) | ||
| 386 | { | ||
| 387 | char buf[width]; | ||
| 388 | char *p; | ||
| 389 | size_t n; | ||
| 390 | |||
| 391 | while (*str) { | ||
| 392 | n = strcspn(str, controls); | ||
| 393 | if (n) { | ||
| 394 | if (!str[n]) break; | ||
| 395 | printf("%.*s", n, str); | ||
| 396 | str += n; | ||
| 397 | } | ||
| 398 | n = strspn(str, controls); | ||
| 399 | p = buf; | ||
| 400 | do { | ||
| 401 | if (*str == '\x7f') { *p++ = '?'; str++; } | ||
| 402 | else if (*str == '\x9b') { *p++ = '{'; str++; } | ||
| 403 | else *p++ = ctrlconv[(unsigned char)*str++]; | ||
| 404 | } while (--n); | ||
| 405 | *p = '\0'; | ||
| 406 | printf("%s%s%s", HIGHLIGHT, buf, NORMAL); | ||
| 407 | } | ||
| 408 | puts(str); | ||
| 409 | } | ||
| 410 | |||
| 318 | /* Print the buffer */ | 411 | /* Print the buffer */ |
| 319 | static void buffer_print(void) | 412 | static void buffer_print(void) |
| 320 | { | 413 | { |
| 321 | int i; | 414 | int i; |
| 322 | 415 | ||
| 323 | printf("%s", CLEAR); | 416 | printf("%s", CLEAR); |
| 324 | if (num_flines >= height - 2) { | 417 | for (i = 0; i < height - 1; i++) |
| 325 | for (i = 0; i < height - 1; i++) | 418 | if (pattern_valid) |
| 326 | printf("%.*s\n", width, buffer[i]); | 419 | print_found(buffer[i]); |
| 327 | } else { | 420 | else |
| 328 | for (i = 1; i < (height - 1 - num_flines); i++) | 421 | print_ascii(buffer[i]); |
| 329 | putchar('\n'); | ||
| 330 | for (i = 0; i < height - 1; i++) | ||
| 331 | printf("%.*s\n", width, buffer[i]); | ||
| 332 | } | ||
| 333 | |||
| 334 | status_print(); | 422 | status_print(); |
| 335 | } | 423 | } |
| 336 | 424 | ||
| @@ -339,20 +427,20 @@ static void buffer_init(void) | |||
| 339 | { | 427 | { |
| 340 | int i; | 428 | int i; |
| 341 | 429 | ||
| 342 | if (buffer == NULL) { | 430 | if (!buffer) { |
| 343 | /* malloc the number of lines needed for the buffer */ | 431 | /* malloc the number of lines needed for the buffer */ |
| 344 | buffer = xmalloc(height * sizeof(char *)); | 432 | buffer = xmalloc(height * sizeof(char *)); |
| 345 | } | 433 | } |
| 346 | 434 | ||
| 347 | /* Fill the buffer until the end of the file or the | 435 | /* Fill the buffer until the end of the file or the |
| 348 | end of the buffer is reached */ | 436 | end of the buffer is reached */ |
| 349 | for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) { | 437 | for (i = 0; i < height - 1 && i <= num_flines; i++) { |
| 350 | buffer[i] = flines[i]; | 438 | buffer[i] = flines[i]; |
| 351 | } | 439 | } |
| 352 | 440 | ||
| 353 | /* If the buffer still isn't full, fill it with blank lines */ | 441 | /* If the buffer still isn't full, fill it with blank lines */ |
| 354 | for (; i < (height - 1); i++) { | 442 | for (; i < height - 1; i++) { |
| 355 | buffer[i] = ""; | 443 | buffer[i] = empty_line_marker; |
| 356 | } | 444 | } |
| 357 | } | 445 | } |
| 358 | 446 | ||
| @@ -361,72 +449,38 @@ static void buffer_down(int nlines) | |||
| 361 | { | 449 | { |
| 362 | int i; | 450 | int i; |
| 363 | 451 | ||
| 364 | if (!(option_mask32 & LESS_STATE_PAST_EOF)) { | 452 | if (line_pos + (height - 3) + nlines < num_flines) { |
| 365 | if (line_pos + (height - 3) + nlines < num_flines) { | 453 | line_pos += nlines; |
| 366 | line_pos += nlines; | 454 | for (i = 0; i < (height - 1); i++) { |
| 455 | buffer[i] = flines[line_pos + i]; | ||
| 456 | } | ||
| 457 | } else { | ||
| 458 | /* As the number of lines requested was too large, we just move | ||
| 459 | to the end of the file */ | ||
| 460 | while (line_pos + (height - 3) + 1 < num_flines) { | ||
| 461 | line_pos += 1; | ||
| 367 | for (i = 0; i < (height - 1); i++) { | 462 | for (i = 0; i < (height - 1); i++) { |
| 368 | buffer[i] = flines[line_pos + i]; | 463 | buffer[i] = flines[line_pos + i]; |
| 369 | } | 464 | } |
| 370 | } else { | ||
| 371 | /* As the number of lines requested was too large, we just move | ||
| 372 | to the end of the file */ | ||
| 373 | while (line_pos + (height - 3) + 1 < num_flines) { | ||
| 374 | line_pos += 1; | ||
| 375 | for (i = 0; i < (height - 1); i++) { | ||
| 376 | buffer[i] = flines[line_pos + i]; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | 465 | } |
| 380 | |||
| 381 | /* We exit if the -E flag has been set */ | ||
| 382 | if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines)) | ||
| 383 | tless_exit(0); | ||
| 384 | } | 466 | } |
| 467 | |||
| 468 | /* We exit if the -E flag has been set */ | ||
| 469 | if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines)) | ||
| 470 | tless_exit(0); | ||
| 385 | } | 471 | } |
| 386 | 472 | ||
| 387 | static void buffer_up(int nlines) | 473 | static void buffer_up(int nlines) |
| 388 | { | 474 | { |
| 389 | int i; | 475 | int i; |
| 390 | int tilde_line; | ||
| 391 | 476 | ||
| 392 | if (!(option_mask32 & LESS_STATE_PAST_EOF)) { | 477 | line_pos -= nlines; |
| 393 | if (line_pos - nlines >= 0) { | 478 | if (line_pos < 0) line_pos = 0; |
| 394 | line_pos -= nlines; | 479 | for (i = 0; i < height - 1; i++) { |
| 395 | for (i = 0; i < (height - 1); i++) { | 480 | if (line_pos + i <= num_flines) { |
| 396 | buffer[i] = flines[line_pos + i]; | 481 | buffer[i] = flines[line_pos + i]; |
| 397 | } | ||
| 398 | } else { | ||
| 399 | /* As the requested number of lines to move was too large, we | ||
| 400 | move one line up at a time until we can't. */ | ||
| 401 | while (line_pos != 0) { | ||
| 402 | line_pos -= 1; | ||
| 403 | for (i = 0; i < (height - 1); i++) { | ||
| 404 | buffer[i] = flines[line_pos + i]; | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 408 | } | ||
| 409 | else { | ||
| 410 | /* Work out where the tildes start */ | ||
| 411 | tilde_line = num_flines - line_pos + 3; | ||
| 412 | |||
| 413 | line_pos -= nlines; | ||
| 414 | /* Going backwards nlines lines has taken us to a point where | ||
| 415 | nothing is past the EOF, so we revert to normal. */ | ||
| 416 | if (line_pos < num_flines - height + 3) { | ||
| 417 | option_mask32 &= ~LESS_STATE_PAST_EOF; | ||
| 418 | buffer_up(nlines); | ||
| 419 | } else { | 482 | } else { |
| 420 | /* We only move part of the buffer, as the rest | 483 | buffer[i] = empty_line_marker; |
| 421 | is past the EOF */ | ||
| 422 | for (i = 0; i < (height - 1); i++) { | ||
| 423 | if (i < tilde_line - nlines + 1) { | ||
| 424 | buffer[i] = flines[line_pos + i]; | ||
| 425 | } else { | ||
| 426 | if (line_pos >= num_flines - height + 2) | ||
| 427 | buffer[i] = "~"; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | 484 | } |
| 431 | } | 485 | } |
| 432 | } | 486 | } |
| @@ -434,28 +488,19 @@ static void buffer_up(int nlines) | |||
| 434 | static void buffer_line(int linenum) | 488 | static void buffer_line(int linenum) |
| 435 | { | 489 | { |
| 436 | int i; | 490 | int i; |
| 437 | option_mask32 &= ~LESS_STATE_PAST_EOF; | ||
| 438 | 491 | ||
| 439 | if (linenum < 0 || linenum > num_flines) { | 492 | if (linenum < 0 || linenum > num_flines) { |
| 440 | clear_line(); | 493 | clear_line(); |
| 441 | printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL); | 494 | printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL); |
| 442 | } | ||
| 443 | else if (linenum < (num_flines - height - 2)) { | ||
| 444 | for (i = 0; i < (height - 1); i++) { | ||
| 445 | buffer[i] = flines[linenum + i]; | ||
| 446 | } | ||
| 447 | line_pos = linenum; | ||
| 448 | buffer_print(); | ||
| 449 | } else { | 495 | } else { |
| 450 | for (i = 0; i < (height - 1); i++) { | 496 | for (i = 0; i < height - 1; i++) { |
| 451 | if (linenum + i < num_flines + 2) | 497 | if (linenum + i <= num_flines) |
| 452 | buffer[i] = flines[linenum + i]; | 498 | buffer[i] = flines[linenum + i]; |
| 453 | else | 499 | else { |
| 454 | buffer[i] = (option_mask32 & FLAG_TILDE) ? "" : "~"; | 500 | buffer[i] = empty_line_marker; |
| 501 | } | ||
| 455 | } | 502 | } |
| 456 | line_pos = linenum; | 503 | line_pos = linenum; |
| 457 | /* Set past_eof so buffer_down and buffer_up act differently */ | ||
| 458 | option_mask32 |= LESS_STATE_PAST_EOF; | ||
| 459 | buffer_print(); | 504 | buffer_print(); |
| 460 | } | 505 | } |
| 461 | } | 506 | } |
| @@ -563,76 +608,37 @@ static void colon_process(void) | |||
| 563 | } | 608 | } |
| 564 | } | 609 | } |
| 565 | 610 | ||
| 566 | #if ENABLE_FEATURE_LESS_REGEXP | 611 | static int normalize_match_pos(int match) |
| 567 | /* The below two regular expression handler functions NEED development. */ | ||
| 568 | |||
| 569 | /* Get a regular expression from the user, and then go through the current | ||
| 570 | file line by line, running a processing regex function on each one. */ | ||
| 571 | |||
| 572 | static char *process_regex_on_line(char *line, regex_t *pattern, int action) | ||
| 573 | { | 612 | { |
| 574 | /* UNTESTED. LOOKED BUGGY AND LEAKY AS HELL. */ | 613 | match_pos = match; |
| 575 | /* FIXED. NEED TESTING. */ | 614 | if (match < 0) |
| 576 | /* 'line' should be either returned or free()ed */ | 615 | return (match_pos = 0); |
| 577 | 616 | if (match >= num_matches) | |
| 578 | /* This function takes the regex and applies it to the line. | 617 | { |
| 579 | Each part of the line that matches has the HIGHLIGHT | 618 | match_pos = num_matches - 1; |
| 580 | and NORMAL escape sequences placed around it by | 619 | } |
| 581 | insert_highlights if action = 1, or has the escape sequences | 620 | return match_pos; |
| 582 | removed if action = 0, and then the line is returned. */ | ||
| 583 | int match_status; | ||
| 584 | char *line2 = line; | ||
| 585 | char *growline = xstrdup(""); | ||
| 586 | char *ng; | ||
| 587 | regmatch_t match_structs; | ||
| 588 | |||
| 589 | match_found = 0; | ||
| 590 | match_status = regexec(pattern, line2, 1, &match_structs, 0); | ||
| 591 | |||
| 592 | while (match_status == 0) { | ||
| 593 | match_found = 1; | ||
| 594 | if (action) { | ||
| 595 | ng = xasprintf("%s%.*s%s%.*s%s", growline, | ||
| 596 | match_structs.rm_so, line2, HIGHLIGHT, | ||
| 597 | match_structs.rm_eo - match_structs.rm_so, | ||
| 598 | line2 + match_structs.rm_so, NORMAL); | ||
| 599 | } else { | ||
| 600 | ng = xasprintf("%s%.*s%.*s", growline, | ||
| 601 | match_structs.rm_so - 4, line2, | ||
| 602 | match_structs.rm_eo - match_structs.rm_so, | ||
| 603 | line2 + match_structs.rm_so); | ||
| 604 | } | ||
| 605 | free(growline); growline = ng; | ||
| 606 | line2 += match_structs.rm_eo; | ||
| 607 | match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL); | ||
| 608 | } | ||
| 609 | |||
| 610 | if (match_found) { | ||
| 611 | ng = xasprintf("%s%s", growline, line2); | ||
| 612 | free(line); | ||
| 613 | } else { | ||
| 614 | ng = line; | ||
| 615 | } | ||
| 616 | free(growline); | ||
| 617 | return ng; | ||
| 618 | } | 621 | } |
| 619 | 622 | ||
| 623 | #if ENABLE_FEATURE_LESS_REGEXP | ||
| 620 | static void goto_match(int match) | 624 | static void goto_match(int match) |
| 621 | { | 625 | { |
| 622 | /* This goes to a specific match - all line positions of matches are | 626 | buffer_line(match_lines[normalize_match_pos(match)]); |
| 623 | stored within the match_lines[] array. */ | ||
| 624 | if ((match < num_matches) && (match >= 0)) { | ||
| 625 | buffer_line(match_lines[match]); | ||
| 626 | match_pos = match; | ||
| 627 | } | ||
| 628 | } | 627 | } |
| 629 | 628 | ||
| 630 | static void regex_process(void) | 629 | static void regex_process(void) |
| 631 | { | 630 | { |
| 632 | char *uncomp_regex; | 631 | char *uncomp_regex; |
| 633 | int i; | 632 | |
| 634 | int j = 0; | 633 | /* Reset variables */ |
| 635 | regex_t pattern; | 634 | match_lines = xrealloc(match_lines, sizeof(int)); |
| 635 | match_lines[0] = -1; | ||
| 636 | match_pos = 0; | ||
| 637 | num_matches = 0; | ||
| 638 | if (pattern_valid) { | ||
| 639 | regfree(&pattern); | ||
| 640 | pattern_valid = 0; | ||
| 641 | } | ||
| 636 | 642 | ||
| 637 | /* Get the uncompiled regular expression from the user */ | 643 | /* Get the uncompiled regular expression from the user */ |
| 638 | clear_line(); | 644 | clear_line(); |
| @@ -640,56 +646,33 @@ static void regex_process(void) | |||
| 640 | uncomp_regex = xmalloc_getline(inp); | 646 | uncomp_regex = xmalloc_getline(inp); |
| 641 | if (!uncomp_regex || !uncomp_regex[0]) { | 647 | if (!uncomp_regex || !uncomp_regex[0]) { |
| 642 | free(uncomp_regex); | 648 | free(uncomp_regex); |
| 643 | if (num_matches) | 649 | buffer_print(); |
| 644 | goto_match((option_mask32 & LESS_STATE_MATCH_BACKWARDS) | ||
| 645 | ? match_pos - 1 : match_pos + 1); | ||
| 646 | else | ||
| 647 | buffer_print(); | ||
| 648 | return; | 650 | return; |
| 649 | } | 651 | } |
| 650 | 652 | ||
| 651 | /* Compile the regex and check for errors */ | 653 | /* Compile the regex and check for errors */ |
| 652 | xregcomp(&pattern, uncomp_regex, 0); | 654 | xregcomp(&pattern, uncomp_regex, 0); |
| 653 | free(uncomp_regex); | 655 | free(uncomp_regex); |
| 656 | pattern_valid = 1; | ||
| 654 | 657 | ||
| 655 | if (num_matches) { | 658 | /* Run the regex on each line of the current file */ |
| 656 | /* Get rid of all the highlights we added previously */ | 659 | for (match_pos = 0; match_pos <= num_flines; match_pos++) { |
| 657 | for (i = 0; i <= num_flines; i++) { | 660 | if (regexec(&pattern, flines[match_pos], 0, NULL, 0) == 0) { |
| 658 | flines[i] = process_regex_on_line(flines[i], &old_pattern, 0); | 661 | match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); |
| 662 | match_lines[num_matches++] = match_pos; | ||
| 659 | } | 663 | } |
| 660 | } | 664 | } |
| 661 | old_pattern = pattern; | ||
| 662 | 665 | ||
| 663 | /* Reset variables */ | 666 | if (num_matches == 0 || num_flines <= height - 2) { |
| 664 | match_lines = xrealloc(match_lines, sizeof(int)); | 667 | buffer_print(); |
| 665 | match_lines[0] = -1; | 668 | return; |
| 666 | match_pos = 0; | ||
| 667 | num_matches = 0; | ||
| 668 | match_found = 0; | ||
| 669 | /* Run the regex on each line of the current file here */ | ||
| 670 | for (i = 0; i <= num_flines; i++) { | ||
| 671 | flines[i] = process_regex_on_line(flines[i], &pattern, 1); | ||
| 672 | if (match_found) { | ||
| 673 | match_lines = xrealloc(match_lines, (j + 1) * sizeof(int)); | ||
| 674 | match_lines[j] = i; | ||
| 675 | j++; | ||
| 676 | } | ||
| 677 | } | 669 | } |
| 678 | 670 | for (match_pos = 0; match_pos < num_matches; match_pos++) { | |
| 679 | num_matches = j; | 671 | if (match_lines[match_pos] > line_pos) |
| 680 | if ((match_lines[0] != -1) && (num_flines > height - 2)) { | 672 | break; |
| 681 | if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) { | 673 | } |
| 682 | for (i = 0; i < num_matches; i++) { | 674 | if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--; |
| 683 | if (match_lines[i] > line_pos) { | 675 | buffer_line(match_lines[normalize_match_pos(match_pos)]); |
| 684 | match_pos = i - 1; | ||
| 685 | buffer_line(match_lines[match_pos]); | ||
| 686 | break; | ||
| 687 | } | ||
| 688 | } | ||
| 689 | } else | ||
| 690 | buffer_line(match_lines[0]); | ||
| 691 | } else | ||
| 692 | buffer_init(); | ||
| 693 | } | 676 | } |
| 694 | #endif | 677 | #endif |
| 695 | 678 | ||
| @@ -1111,11 +1094,16 @@ int less_main(int argc, char **argv) | |||
| 1111 | inp = xfopen(CURRENT_TTY, "r"); | 1094 | inp = xfopen(CURRENT_TTY, "r"); |
| 1112 | 1095 | ||
| 1113 | get_terminal_width_height(fileno(inp), &width, &height); | 1096 | get_terminal_width_height(fileno(inp), &width, &height); |
| 1097 | if (width < 10 || height < 3) | ||
| 1098 | bb_error_msg_and_die("too narrow here"); | ||
| 1099 | |||
| 1100 | if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; | ||
| 1101 | |||
| 1114 | data_readlines(); | 1102 | data_readlines(); |
| 1115 | 1103 | ||
| 1104 | tcgetattr(fileno(inp), &term_orig); | ||
| 1116 | signal(SIGTERM, sig_catcher); | 1105 | signal(SIGTERM, sig_catcher); |
| 1117 | signal(SIGINT, sig_catcher); | 1106 | signal(SIGINT, sig_catcher); |
| 1118 | tcgetattr(fileno(inp), &term_orig); | ||
| 1119 | 1107 | ||
| 1120 | term_vi = term_orig; | 1108 | term_vi = term_orig; |
| 1121 | term_vi.c_lflag &= (~ICANON & ~ECHO); | 1109 | term_vi.c_lflag &= (~ICANON & ~ECHO); |
