diff options
-rw-r--r-- | libbb/xregcomp.c | 7 | ||||
-rw-r--r-- | miscutils/less.c | 484 |
2 files changed, 238 insertions, 253 deletions
diff --git a/libbb/xregcomp.c b/libbb/xregcomp.c index 4bcb9aedf..6e9a29f49 100644 --- a/libbb/xregcomp.c +++ b/libbb/xregcomp.c | |||
@@ -8,16 +8,13 @@ | |||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <stdio.h> | ||
12 | #include "libbb.h" | 11 | #include "libbb.h" |
13 | #include "xregex.h" | 12 | #include "xregex.h" |
14 | 13 | ||
15 | |||
16 | |||
17 | void xregcomp(regex_t *preg, const char *regex, int cflags) | 14 | void xregcomp(regex_t *preg, const char *regex, int cflags) |
18 | { | 15 | { |
19 | int ret; | 16 | int ret = regcomp(preg, regex, cflags); |
20 | if ((ret = regcomp(preg, regex, cflags)) != 0) { | 17 | if (ret) { |
21 | int errmsgsz = regerror(ret, preg, NULL, 0); | 18 | int errmsgsz = regerror(ret, preg, NULL, 0); |
22 | char *errmsg = xmalloc(errmsgsz); | 19 | char *errmsg = xmalloc(errmsgsz); |
23 | regerror(ret, preg, errmsg, errmsgsz); | 20 | regerror(ret, preg, errmsg, errmsgsz); |
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); |