aboutsummaryrefslogtreecommitdiff
path: root/miscutils/less.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-12-20 02:46:48 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-12-20 02:46:48 +0000
commit9a7cef930f7e299c0e5d9d1ddeb3f95db6749eb7 (patch)
treeb8f6595e61380091ba42740cf1ad8bb684e55336 /miscutils/less.c
parentb95636c52fbb058a39548bcbc4e86456ebbd7b7b (diff)
downloadbusybox-w32-9a7cef930f7e299c0e5d9d1ddeb3f95db6749eb7.tar.gz
busybox-w32-9a7cef930f7e299c0e5d9d1ddeb3f95db6749eb7.tar.bz2
busybox-w32-9a7cef930f7e299c0e5d9d1ddeb3f95db6749eb7.zip
less: somewhat buggy applet, but nice. Muchly reduced
xstrdup'ing and memory consumption. Made linewrap saner. regex matching code was awful - still buggy, but not as leaky as before. Made buffer size configurable. Killed several static and on-stack buffers. Hopefully eliminated staircase effect on Ctrl-C (unable to reproduce).
Diffstat (limited to 'miscutils/less.c')
-rw-r--r--miscutils/less.c413
1 files changed, 201 insertions, 212 deletions
diff --git a/miscutils/less.c b/miscutils/less.c
index 03ffd78ed..73500671d 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -32,7 +32,7 @@
32 32
33#include "busybox.h" 33#include "busybox.h"
34 34
35#ifdef CONFIG_FEATURE_LESS_REGEXP 35#if ENABLE_FEATURE_LESS_REGEXP
36#include "xregex.h" 36#include "xregex.h"
37#endif 37#endif
38 38
@@ -64,13 +64,12 @@
64/* The escape code to clear the screen */ 64/* The escape code to clear the screen */
65#define CLEAR "\033[H\033[J" 65#define CLEAR "\033[H\033[J"
66 66
67/* Maximum number of lines in a file */ 67#define MAXLINES CONFIG_FEATURE_LESS_MAXLINES
68#define MAXLINES 10000
69 68
70static int height; 69static int height;
71static int width; 70static int width;
72static char **files; 71static char **files;
73static char filename[256]; 72static char *filename;
74static char **buffer; 73static char **buffer;
75static char **flines; 74static char **flines;
76static int current_file = 1; 75static int current_file = 1;
@@ -79,24 +78,21 @@ static int num_flines;
79static int num_files = 1; 78static int num_files = 1;
80 79
81/* Command line options */ 80/* Command line options */
82static unsigned flags;
83#define FLAG_E 1 81#define FLAG_E 1
84#define FLAG_M (1<<1) 82#define FLAG_M (1<<1)
85#define FLAG_m (1<<2) 83#define FLAG_m (1<<2)
86#define FLAG_N (1<<3) 84#define FLAG_N (1<<3)
87#define FLAG_TILDE (1<<4) 85#define FLAG_TILDE (1<<4)
88/* hijack command line options variable for internal state vars */ 86/* hijack command line options variable for internal state vars */
89#define LESS_STATE_INP_STDIN (1<<5) 87#define LESS_STATE_PAST_EOF (1<<5)
90#define LESS_STATE_PAST_EOF (1<<6) 88#define LESS_STATE_MATCH_BACKWARDS (1<<6)
91#define LESS_STATE_MATCH_BACKWARDS (1<<7)
92/* INP_STDIN is used to change behaviour when input comes from stdin */
93 89
94#ifdef CONFIG_FEATURE_LESS_MARKS 90#if ENABLE_FEATURE_LESS_MARKS
95static int mark_lines[15][2]; 91static int mark_lines[15][2];
96static int num_marks; 92static int num_marks;
97#endif 93#endif
98 94
99#ifdef CONFIG_FEATURE_LESS_REGEXP 95#if ENABLE_FEATURE_LESS_REGEXP
100static int match_found; 96static int match_found;
101static int *match_lines; 97static int *match_lines;
102static int match_pos; 98static int match_pos;
@@ -143,7 +139,7 @@ static int tless_getch(void)
143 them accordingly */ 139 them accordingly */
144 140
145 if (input == '\033' && getc(inp) == '[') { 141 if (input == '\033' && getc(inp) == '[') {
146 unsigned int i; 142 unsigned i;
147 input = getc(inp); 143 input = getc(inp);
148 set_tty_cooked(); 144 set_tty_cooked();
149 145
@@ -152,13 +148,12 @@ static int tless_getch(void)
152 return 20 + i; 148 return 20 + i;
153 else if ((i = input - REAL_PAGE_UP) < 4) 149 else if ((i = input - REAL_PAGE_UP) < 4)
154 return 24 + i; 150 return 24 + i;
151 else
152 return 0; /* ?? */
155 } 153 }
156 /* The input is a normal ASCII value */ 154 /* The input is a normal ASCII value */
157 else { 155 set_tty_cooked();
158 set_tty_cooked(); 156 return input;
159 return input;
160 }
161 return 0;
162} 157}
163 158
164/* Move the cursor to a position (x,y), where (0,0) is the 159/* Move the cursor to a position (x,y), where (0,0) is the
@@ -174,51 +169,62 @@ static void clear_line(void)
174 printf("\033[K"); 169 printf("\033[K");
175} 170}
176 171
177/* This adds line numbers to every line, as the -N flag necessitates */
178static void add_linenumbers(void)
179{
180 int i;
181
182 for (i = 0; i <= num_flines; i++) {
183 char *new = xasprintf("%5d %s", i + 1, flines[i]);
184 free(flines[i]);
185 flines[i] = new;
186 }
187}
188
189static void data_readlines(void) 172static void data_readlines(void)
190{ 173{
191 int i; 174 unsigned i;
192 char current_line[256]; 175 unsigned n = 1;
176 int w = width;
177 char *last_nl = (char*)1; /* "not NULL" */
178 char *current_line;
193 FILE *fp; 179 FILE *fp;
194 180
195 fp = (flags & LESS_STATE_INP_STDIN) ? stdin : xfopen(filename, "r"); 181 fp = filename ? xfopen(filename, "r") : stdin;
196 flines = NULL; 182 flines = NULL;
197 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) { 183 if (option_mask32 & FLAG_N) {
198 strcpy(current_line, ""); 184 w -= 6;
199 fgets(current_line, 256, fp); 185 if (w < 1) w = 1; /* paranoia */
186 }
187 for (i = 0; !feof(fp) && i <= MAXLINES; i++) {
188 flines = xrealloc(flines, (i+1) * sizeof(char *));
189
190 current_line = xmalloc(w);
191 again:
192 current_line[0] = '\0';
193 fgets(current_line, w, fp);
200 if (fp != stdin) 194 if (fp != stdin)
201 die_if_ferror(fp, filename); 195 die_if_ferror(fp, filename);
202 flines = xrealloc(flines, (i+1) * sizeof(char *)); 196
203 flines[i] = xstrdup(current_line); 197 /* Corner case: linewrap with only '\n' wrapping */
198 /* Looks ugly on screen, so we handle it specially */
199 if (!last_nl && current_line[0] == '\n') {
200 last_nl = (char*)1; /* "not NULL" */
201 n++;
202 goto again;
203 }
204 last_nl = last_char_is(current_line, '\n');
205 if (last_nl)
206 *last_nl = '\0';
207 if (option_mask32 & FLAG_N) {
208 flines[i] = xasprintf((n <= 99999) ? "%5u %s" : "%05u %s",
209 n % 100000, current_line);
210 free(current_line);
211 if (last_nl)
212 n++;
213 } else {
214 flines[i] = xrealloc(current_line, strlen(current_line)+1);
215 }
204 } 216 }
205 num_flines = i - 2; 217 num_flines = i - 2;
206 218
207 /* Reset variables for a new file */ 219 /* Reset variables for a new file */
208 220
209 line_pos = 0; 221 line_pos = 0;
210 flags &= ~LESS_STATE_PAST_EOF; 222 option_mask32 &= ~LESS_STATE_PAST_EOF;
211 223
212 fclose(fp); 224 fclose(fp);
213
214 if (inp == NULL)
215 inp = (flags & LESS_STATE_INP_STDIN) ? xfopen(CURRENT_TTY, "r") : stdin;
216
217 if (flags & FLAG_N)
218 add_linenumbers();
219} 225}
220 226
221#ifdef CONFIG_FEATURE_LESS_FLAGS 227#if ENABLE_FEATURE_LESS_FLAGS
222 228
223/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes 229/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
224 * on my build. */ 230 * on my build. */
@@ -232,19 +238,18 @@ static void m_status_print(void)
232{ 238{
233 int percentage; 239 int percentage;
234 240
235 if (!(flags & LESS_STATE_PAST_EOF)) { 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 ",
240 line_pos + 1, line_pos + height - 1, num_flines + 1); 246 line_pos + 1, line_pos + height - 1, num_flines + 1);
241 else { 247 } else {
242 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, 248 printf("%s%s lines %i-%i/%i ", HIGHLIGHT,
243 filename, line_pos + 1, line_pos + height - 1, 249 filename, line_pos + 1, line_pos + height - 1,
244 num_flines + 1); 250 num_flines + 1);
245 } 251 }
246 } 252 } else {
247 else {
248 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, 253 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename,
249 line_pos + 1, line_pos + height - 1, num_flines + 1); 254 line_pos + 1, line_pos + height - 1, num_flines + 1);
250 } 255 }
@@ -253,13 +258,11 @@ static void m_status_print(void)
253 printf("(END) %s", NORMAL); 258 printf("(END) %s", NORMAL);
254 if ((num_files > 1) && (current_file != num_files)) 259 if ((num_files > 1) && (current_file != num_files))
255 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL); 260 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
256 } 261 } else {
257 else {
258 percentage = calc_percent(); 262 percentage = calc_percent();
259 printf("%i%% %s", percentage, NORMAL); 263 printf("%i%% %s", percentage, NORMAL);
260 } 264 }
261 } 265 } else {
262 else {
263 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, 266 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename,
264 line_pos + 1, num_flines + 1, num_flines + 1); 267 line_pos + 1, num_flines + 1, num_flines + 1);
265 if ((num_files > 1) && (current_file != num_files)) 268 if ((num_files > 1) && (current_file != num_files))
@@ -287,10 +290,10 @@ static void medium_status_print(void)
287static void status_print(void) 290static void status_print(void)
288{ 291{
289 /* Change the status if flags have been set */ 292 /* Change the status if flags have been set */
290#ifdef CONFIG_FEATURE_LESS_FLAGS 293#if ENABLE_FEATURE_LESS_FLAGS
291 if (flags & FLAG_M) 294 if (option_mask32 & FLAG_M)
292 m_status_print(); 295 m_status_print();
293 else if (flags & FLAG_m) 296 else if (option_mask32 & FLAG_m)
294 medium_status_print(); 297 medium_status_print();
295 /* No flags set */ 298 /* No flags set */
296 else { 299 else {
@@ -300,16 +303,14 @@ static void status_print(void)
300 if (num_files > 1) 303 if (num_files > 1)
301 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", 304 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ",
302 current_file, " of ", num_files, ")", NORMAL); 305 current_file, " of ", num_files, ")", NORMAL);
303 } 306 } else if (line_pos == num_flines - height + 2) {
304 else if (line_pos == num_flines - height + 2) {
305 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL); 307 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
306 if ((num_files > 1) && (current_file != num_files)) 308 if ((num_files > 1) && (current_file != num_files))
307 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL); 309 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
308 } 310 } else {
309 else {
310 putchar(':'); 311 putchar(':');
311 } 312 }
312#ifdef CONFIG_FEATURE_LESS_FLAGS 313#if ENABLE_FEATURE_LESS_FLAGS
313 } 314 }
314#endif 315#endif
315} 316}
@@ -322,13 +323,12 @@ static void buffer_print(void)
322 printf("%s", CLEAR); 323 printf("%s", CLEAR);
323 if (num_flines >= height - 2) { 324 if (num_flines >= height - 2) {
324 for (i = 0; i < height - 1; i++) 325 for (i = 0; i < height - 1; i++)
325 printf("%s", buffer[i]); 326 printf("%.*s\n", width, buffer[i]);
326 } 327 } else {
327 else {
328 for (i = 1; i < (height - 1 - num_flines); i++) 328 for (i = 1; i < (height - 1 - num_flines); i++)
329 putchar('\n'); 329 putchar('\n');
330 for (i = 0; i < height - 1; i++) 330 for (i = 0; i < height - 1; i++)
331 printf("%s", buffer[i]); 331 printf("%.*s\n", width, buffer[i]);
332 } 332 }
333 333
334 status_print(); 334 status_print();
@@ -341,21 +341,18 @@ static void buffer_init(void)
341 341
342 if (buffer == NULL) { 342 if (buffer == NULL) {
343 /* malloc the number of lines needed for the buffer */ 343 /* malloc the number of lines needed for the buffer */
344 buffer = xrealloc(buffer, height * sizeof(char *)); 344 buffer = xmalloc(height * sizeof(char *));
345 } else {
346 for (i = 0; i < (height - 1); i++)
347 free(buffer[i]);
348 } 345 }
349 346
350 /* Fill the buffer until the end of the file or the 347 /* Fill the buffer until the end of the file or the
351 end of the buffer is reached */ 348 end of the buffer is reached */
352 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) { 349 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
353 buffer[i] = xstrdup(flines[i]); 350 buffer[i] = flines[i];
354 } 351 }
355 352
356 /* If the buffer still isn't full, fill it with blank lines */ 353 /* If the buffer still isn't full, fill it with blank lines */
357 for (; i < (height - 1); i++) { 354 for (; i < (height - 1); i++) {
358 buffer[i] = xstrdup(""); 355 buffer[i] = "";
359 } 356 }
360} 357}
361 358
@@ -364,28 +361,25 @@ static void buffer_down(int nlines)
364{ 361{
365 int i; 362 int i;
366 363
367 if (!(flags & LESS_STATE_PAST_EOF)) { 364 if (!(option_mask32 & LESS_STATE_PAST_EOF)) {
368 if (line_pos + (height - 3) + nlines < num_flines) { 365 if (line_pos + (height - 3) + nlines < num_flines) {
369 line_pos += nlines; 366 line_pos += nlines;
370 for (i = 0; i < (height - 1); i++) { 367 for (i = 0; i < (height - 1); i++) {
371 free(buffer[i]); 368 buffer[i] = flines[line_pos + i];
372 buffer[i] = xstrdup(flines[line_pos + i]);
373 } 369 }
374 } 370 } else {
375 else {
376 /* As the number of lines requested was too large, we just move 371 /* As the number of lines requested was too large, we just move
377 to the end of the file */ 372 to the end of the file */
378 while (line_pos + (height - 3) + 1 < num_flines) { 373 while (line_pos + (height - 3) + 1 < num_flines) {
379 line_pos += 1; 374 line_pos += 1;
380 for (i = 0; i < (height - 1); i++) { 375 for (i = 0; i < (height - 1); i++) {
381 free(buffer[i]); 376 buffer[i] = flines[line_pos + i];
382 buffer[i] = xstrdup(flines[line_pos + i]);
383 } 377 }
384 } 378 }
385 } 379 }
386 380
387 /* We exit if the -E flag has been set */ 381 /* We exit if the -E flag has been set */
388 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines)) 382 if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines))
389 tless_exit(0); 383 tless_exit(0);
390 } 384 }
391} 385}
@@ -395,22 +389,19 @@ static void buffer_up(int nlines)
395 int i; 389 int i;
396 int tilde_line; 390 int tilde_line;
397 391
398 if (!(flags & LESS_STATE_PAST_EOF)) { 392 if (!(option_mask32 & LESS_STATE_PAST_EOF)) {
399 if (line_pos - nlines >= 0) { 393 if (line_pos - nlines >= 0) {
400 line_pos -= nlines; 394 line_pos -= nlines;
401 for (i = 0; i < (height - 1); i++) { 395 for (i = 0; i < (height - 1); i++) {
402 free(buffer[i]); 396 buffer[i] = flines[line_pos + i];
403 buffer[i] = xstrdup(flines[line_pos + i]);
404 } 397 }
405 } 398 } else {
406 else {
407 /* As the requested number of lines to move was too large, we 399 /* As the requested number of lines to move was too large, we
408 move one line up at a time until we can't. */ 400 move one line up at a time until we can't. */
409 while (line_pos != 0) { 401 while (line_pos != 0) {
410 line_pos -= 1; 402 line_pos -= 1;
411 for (i = 0; i < (height - 1); i++) { 403 for (i = 0; i < (height - 1); i++) {
412 free(buffer[i]); 404 buffer[i] = flines[line_pos + i];
413 buffer[i] = xstrdup(flines[line_pos + i]);
414 } 405 }
415 } 406 }
416 } 407 }
@@ -423,19 +414,17 @@ static void buffer_up(int nlines)
423 /* Going backwards nlines lines has taken us to a point where 414 /* Going backwards nlines lines has taken us to a point where
424 nothing is past the EOF, so we revert to normal. */ 415 nothing is past the EOF, so we revert to normal. */
425 if (line_pos < num_flines - height + 3) { 416 if (line_pos < num_flines - height + 3) {
426 flags &= ~LESS_STATE_PAST_EOF; 417 option_mask32 &= ~LESS_STATE_PAST_EOF;
427 buffer_up(nlines); 418 buffer_up(nlines);
428 } 419 } else {
429 else {
430 /* We only move part of the buffer, as the rest 420 /* We only move part of the buffer, as the rest
431 is past the EOF */ 421 is past the EOF */
432 for (i = 0; i < (height - 1); i++) { 422 for (i = 0; i < (height - 1); i++) {
433 free(buffer[i]); 423 if (i < tilde_line - nlines + 1) {
434 if (i < tilde_line - nlines + 1) 424 buffer[i] = flines[line_pos + i];
435 buffer[i] = xstrdup(flines[line_pos + i]); 425 } else {
436 else {
437 if (line_pos >= num_flines - height + 2) 426 if (line_pos >= num_flines - height + 2)
438 buffer[i] = xstrdup("~\n"); 427 buffer[i] = "~";
439 } 428 }
440 } 429 }
441 } 430 }
@@ -445,7 +434,7 @@ static void buffer_up(int nlines)
445static void buffer_line(int linenum) 434static void buffer_line(int linenum)
446{ 435{
447 int i; 436 int i;
448 flags &= ~LESS_STATE_PAST_EOF; 437 option_mask32 &= ~LESS_STATE_PAST_EOF;
449 438
450 if (linenum < 0 || linenum > num_flines) { 439 if (linenum < 0 || linenum > num_flines) {
451 clear_line(); 440 clear_line();
@@ -453,23 +442,20 @@ static void buffer_line(int linenum)
453 } 442 }
454 else if (linenum < (num_flines - height - 2)) { 443 else if (linenum < (num_flines - height - 2)) {
455 for (i = 0; i < (height - 1); i++) { 444 for (i = 0; i < (height - 1); i++) {
456 free(buffer[i]); 445 buffer[i] = flines[linenum + i];
457 buffer[i] = xstrdup(flines[linenum + i]);
458 } 446 }
459 line_pos = linenum; 447 line_pos = linenum;
460 buffer_print(); 448 buffer_print();
461 } 449 } else {
462 else {
463 for (i = 0; i < (height - 1); i++) { 450 for (i = 0; i < (height - 1); i++) {
464 free(buffer[i]);
465 if (linenum + i < num_flines + 2) 451 if (linenum + i < num_flines + 2)
466 buffer[i] = xstrdup(flines[linenum + i]); 452 buffer[i] = flines[linenum + i];
467 else 453 else
468 buffer[i] = xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n"); 454 buffer[i] = (option_mask32 & FLAG_TILDE) ? "" : "~";
469 } 455 }
470 line_pos = linenum; 456 line_pos = linenum;
471 /* Set past_eof so buffer_down and buffer_up act differently */ 457 /* Set past_eof so buffer_down and buffer_up act differently */
472 flags |= LESS_STATE_PAST_EOF; 458 option_mask32 |= LESS_STATE_PAST_EOF;
473 buffer_print(); 459 buffer_print();
474 } 460 }
475} 461}
@@ -490,22 +476,13 @@ static void reinitialise(void)
490 476
491static void examine_file(void) 477static void examine_file(void)
492{ 478{
493 int newline_offset;
494
495 clear_line(); 479 clear_line();
496 printf("Examine: "); 480 printf("Examine: ");
497 fgets(filename, 256, inp); 481 free(filename);
498 482 filename = xmalloc_getline(inp);
499 /* As fgets adds a newline to the end of an input string, we 483 files[num_files] = filename;
500 need to remove it */
501 newline_offset = strlen(filename) - 1;
502 filename[newline_offset] = '\0';
503
504 files[num_files] = xstrdup(filename);
505 current_file = num_files + 1; 484 current_file = num_files + 1;
506 num_files++; 485 num_files++;
507
508 flags &= ~LESS_STATE_INP_STDIN;
509 reinitialise(); 486 reinitialise();
510} 487}
511 488
@@ -518,10 +495,10 @@ static void change_file(int direction)
518{ 495{
519 if (current_file != ((direction > 0) ? num_files : 1)) { 496 if (current_file != ((direction > 0) ? num_files : 1)) {
520 current_file = direction ? current_file + direction : 1; 497 current_file = direction ? current_file + direction : 1;
521 strcpy(filename, files[current_file - 1]); 498 free(filename);
499 filename = xstrdup(files[current_file - 1]);
522 reinitialise(); 500 reinitialise();
523 } 501 } else {
524 else {
525 clear_line(); 502 clear_line();
526 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL); 503 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
527 } 504 }
@@ -537,8 +514,7 @@ static void remove_current_file(void)
537 files[i - 2] = files[i - 1]; 514 files[i - 2] = files[i - 1];
538 num_files--; 515 num_files--;
539 buffer_print(); 516 buffer_print();
540 } 517 } else {
541 else {
542 change_file(1); 518 change_file(1);
543 for (i = 2; i <= num_files; i++) 519 for (i = 2; i <= num_files; i++)
544 files[i - 2] = files[i - 1]; 520 files[i - 2] = files[i - 1];
@@ -564,7 +540,7 @@ static void colon_process(void)
564 case 'e': 540 case 'e':
565 examine_file(); 541 examine_file();
566 break; 542 break;
567#ifdef CONFIG_FEATURE_LESS_FLAGS 543#if ENABLE_FEATURE_LESS_FLAGS
568 case 'f': 544 case 'f':
569 clear_line(); 545 clear_line();
570 m_status_print(); 546 m_status_print();
@@ -587,7 +563,7 @@ static void colon_process(void)
587 } 563 }
588} 564}
589 565
590#ifdef CONFIG_FEATURE_LESS_REGEXP 566#if ENABLE_FEATURE_LESS_REGEXP
591/* The below two regular expression handler functions NEED development. */ 567/* The below two regular expression handler functions NEED development. */
592 568
593/* Get a regular expression from the user, and then go through the current 569/* Get a regular expression from the user, and then go through the current
@@ -595,48 +571,50 @@ static void colon_process(void)
595 571
596static char *process_regex_on_line(char *line, regex_t *pattern, int action) 572static char *process_regex_on_line(char *line, regex_t *pattern, int action)
597{ 573{
574/* UNTESTED. LOOKED BUGGY AND LEAKY AS HELL. */
575/* FIXED. NEED TESTING. */
576 /* 'line' should be either returned or free()ed */
577
598 /* This function takes the regex and applies it to the line. 578 /* This function takes the regex and applies it to the line.
599 Each part of the line that matches has the HIGHLIGHT 579 Each part of the line that matches has the HIGHLIGHT
600 and NORMAL escape sequences placed around it by 580 and NORMAL escape sequences placed around it by
601 insert_highlights if action = 1, or has the escape sequences 581 insert_highlights if action = 1, or has the escape sequences
602 removed if action = 0, and then the line is returned. */ 582 removed if action = 0, and then the line is returned. */
603 int match_status; 583 int match_status;
604 char *line2 = xmalloc((sizeof(char) * (strlen(line) + 1)) + 64); 584 char *line2 = line;
605 char *growline = ""; 585 char *growline = xstrdup("");
586 char *ng;
606 regmatch_t match_structs; 587 regmatch_t match_structs;
607 588
608 line2 = xstrdup(line);
609
610 match_found = 0; 589 match_found = 0;
611 match_status = regexec(pattern, line2, 1, &match_structs, 0); 590 match_status = regexec(pattern, line2, 1, &match_structs, 0);
612 591
613 while (match_status == 0) { 592 while (match_status == 0) {
614 if (match_found == 0) 593 match_found = 1;
615 match_found = 1;
616
617 if (action) { 594 if (action) {
618 growline = xasprintf("%s%.*s%s%.*s%s", growline, 595 ng = xasprintf("%s%.*s%s%.*s%s", growline,
619 match_structs.rm_so, line2, HIGHLIGHT, 596 match_structs.rm_so, line2, HIGHLIGHT,
620 match_structs.rm_eo - match_structs.rm_so, 597 match_structs.rm_eo - match_structs.rm_so,
621 line2 + match_structs.rm_so, NORMAL); 598 line2 + match_structs.rm_so, NORMAL);
622 } 599 } else {
623 else { 600 ng = xasprintf("%s%.*s%.*s", growline,
624 growline = xasprintf("%s%.*s%.*s", growline,
625 match_structs.rm_so - 4, line2, 601 match_structs.rm_so - 4, line2,
626 match_structs.rm_eo - match_structs.rm_so, 602 match_structs.rm_eo - match_structs.rm_so,
627 line2 + match_structs.rm_so); 603 line2 + match_structs.rm_so);
628 } 604 }
629 605 free(growline); growline = ng;
630 line2 += match_structs.rm_eo; 606 line2 += match_structs.rm_eo;
631 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL); 607 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
632 } 608 }
633 609
634 growline = xasprintf("%s%s", growline, line2); 610 if (match_found) {
635 611 ng = xasprintf("%s%s", growline, line2);
636 return (match_found ? growline : line); 612 free(line);
637 613 } else {
614 ng = line;
615 }
638 free(growline); 616 free(growline);
639 free(line2); 617 return ng;
640} 618}
641 619
642static void goto_match(int match) 620static void goto_match(int match)
@@ -651,35 +629,33 @@ static void goto_match(int match)
651 629
652static void regex_process(void) 630static void regex_process(void)
653{ 631{
654 char uncomp_regex[100]; 632 char *uncomp_regex;
655 char *current_line;
656 int i; 633 int i;
657 int j = 0; 634 int j = 0;
658 regex_t pattern; 635 regex_t pattern;
636
659 /* Get the uncompiled regular expression from the user */ 637 /* Get the uncompiled regular expression from the user */
660 clear_line(); 638 clear_line();
661 putchar((flags & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/'); 639 putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
662 uncomp_regex[0] = 0; 640 uncomp_regex = xmalloc_getline(inp);
663 fgets(uncomp_regex, sizeof(uncomp_regex), inp); 641 if (!uncomp_regex || !uncomp_regex[0]) {
664 642 free(uncomp_regex);
665 if (strlen(uncomp_regex) == 1) {
666 if (num_matches) 643 if (num_matches)
667 goto_match((flags & LESS_STATE_MATCH_BACKWARDS) 644 goto_match((option_mask32 & LESS_STATE_MATCH_BACKWARDS)
668 ? match_pos - 1 : match_pos + 1); 645 ? match_pos - 1 : match_pos + 1);
669 else 646 else
670 buffer_print(); 647 buffer_print();
671 return; 648 return;
672 } 649 }
673 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
674 650
675 /* Compile the regex and check for errors */ 651 /* Compile the regex and check for errors */
676 xregcomp(&pattern, uncomp_regex, 0); 652 xregcomp(&pattern, uncomp_regex, 0);
653 free(uncomp_regex);
677 654
678 if (num_matches) { 655 if (num_matches) {
679 /* Get rid of all the highlights we added previously */ 656 /* Get rid of all the highlights we added previously */
680 for (i = 0; i <= num_flines; i++) { 657 for (i = 0; i <= num_flines; i++) {
681 current_line = process_regex_on_line(flines[i], &old_pattern, 0); 658 flines[i] = process_regex_on_line(flines[i], &old_pattern, 0);
682 flines[i] = xstrdup(current_line);
683 } 659 }
684 } 660 }
685 old_pattern = pattern; 661 old_pattern = pattern;
@@ -692,8 +668,7 @@ static void regex_process(void)
692 match_found = 0; 668 match_found = 0;
693 /* Run the regex on each line of the current file here */ 669 /* Run the regex on each line of the current file here */
694 for (i = 0; i <= num_flines; i++) { 670 for (i = 0; i <= num_flines; i++) {
695 current_line = process_regex_on_line(flines[i], &pattern, 1); 671 flines[i] = process_regex_on_line(flines[i], &pattern, 1);
696 flines[i] = xstrdup(current_line);
697 if (match_found) { 672 if (match_found) {
698 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int)); 673 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
699 match_lines[j] = i; 674 match_lines[j] = i;
@@ -703,7 +678,7 @@ static void regex_process(void)
703 678
704 num_matches = j; 679 num_matches = j;
705 if ((match_lines[0] != -1) && (num_flines > height - 2)) { 680 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
706 if (flags & LESS_STATE_MATCH_BACKWARDS) { 681 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) {
707 for (i = 0; i < num_matches; i++) { 682 for (i = 0; i < num_matches; i++) {
708 if (match_lines[i] > line_pos) { 683 if (match_lines[i] > line_pos) {
709 match_pos = i - 1; 684 match_pos = i - 1;
@@ -711,11 +686,9 @@ static void regex_process(void)
711 break; 686 break;
712 } 687 }
713 } 688 }
714 } 689 } else
715 else
716 buffer_line(match_lines[0]); 690 buffer_line(match_lines[0]);
717 } 691 } else
718 else
719 buffer_init(); 692 buffer_init();
720} 693}
721#endif 694#endif
@@ -724,9 +697,8 @@ static void number_process(int first_digit)
724{ 697{
725 int i = 1; 698 int i = 1;
726 int num; 699 int num;
727 char num_input[80]; 700 char num_input[sizeof(int)*4]; /* more than enough */
728 char keypress; 701 char keypress;
729 char *endptr;
730 702
731 num_input[0] = first_digit; 703 num_input[0] = first_digit;
732 704
@@ -734,8 +706,11 @@ static void number_process(int first_digit)
734 clear_line(); 706 clear_line();
735 printf(":%c", first_digit); 707 printf(":%c", first_digit);
736 708
737 /* Receive input until a letter is given (max 80 chars)*/ 709 /* Receive input until a letter is given */
738 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) { 710 while (i < sizeof(num_input)-1) {
711 num_input[i] = tless_getch();
712 if (!num_input[i] || !isdigit(num_input[i]))
713 break;
739 putchar(num_input[i]); 714 putchar(num_input[i]);
740 i++; 715 i++;
741 } 716 }
@@ -743,8 +718,9 @@ static void number_process(int first_digit)
743 /* Take the final letter out of the digits string */ 718 /* Take the final letter out of the digits string */
744 keypress = num_input[i]; 719 keypress = num_input[i];
745 num_input[i] = '\0'; 720 num_input[i] = '\0';
746 num = strtol(num_input, &endptr, 10); 721 num = bb_strtou(num_input, NULL, 10);
747 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) { 722 /* on format error, num == -1 */
723 if (num < 1 || num > MAXLINES) {
748 buffer_print(); 724 buffer_print();
749 return; 725 return;
750 } 726 }
@@ -764,16 +740,16 @@ static void number_process(int first_digit)
764 case 'p': case '%': 740 case 'p': case '%':
765 buffer_line(((num / 100) * num_flines) - 1); 741 buffer_line(((num / 100) * num_flines) - 1);
766 break; 742 break;
767#ifdef CONFIG_FEATURE_LESS_REGEXP 743#if ENABLE_FEATURE_LESS_REGEXP
768 case 'n': 744 case 'n':
769 goto_match(match_pos + num); 745 goto_match(match_pos + num);
770 break; 746 break;
771 case '/': 747 case '/':
772 flags &= ~LESS_STATE_MATCH_BACKWARDS; 748 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
773 regex_process(); 749 regex_process();
774 break; 750 break;
775 case '?': 751 case '?':
776 flags |= LESS_STATE_MATCH_BACKWARDS; 752 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
777 regex_process(); 753 regex_process();
778 break; 754 break;
779#endif 755#endif
@@ -782,7 +758,7 @@ static void number_process(int first_digit)
782 } 758 }
783} 759}
784 760
785#ifdef CONFIG_FEATURE_LESS_FLAGCS 761#if ENABLE_FEATURE_LESS_FLAGCS
786static void flag_change(void) 762static void flag_change(void)
787{ 763{
788 int keypress; 764 int keypress;
@@ -793,16 +769,16 @@ static void flag_change(void)
793 769
794 switch (keypress) { 770 switch (keypress) {
795 case 'M': 771 case 'M':
796 flags ^= FLAG_M; 772 option_mask32 ^= FLAG_M;
797 break; 773 break;
798 case 'm': 774 case 'm':
799 flags ^= FLAG_m; 775 option_mask32 ^= FLAG_m;
800 break; 776 break;
801 case 'E': 777 case 'E':
802 flags ^= FLAG_E; 778 option_mask32 ^= FLAG_E;
803 break; 779 break;
804 case '~': 780 case '~':
805 flags ^= FLAG_TILDE; 781 option_mask32 ^= FLAG_TILDE;
806 break; 782 break;
807 default: 783 default:
808 break; 784 break;
@@ -820,19 +796,19 @@ static void show_flag_status(void)
820 796
821 switch (keypress) { 797 switch (keypress) {
822 case 'M': 798 case 'M':
823 flag_val = flags & FLAG_M; 799 flag_val = option_mask32 & FLAG_M;
824 break; 800 break;
825 case 'm': 801 case 'm':
826 flag_val = flags & FLAG_m; 802 flag_val = option_mask32 & FLAG_m;
827 break; 803 break;
828 case '~': 804 case '~':
829 flag_val = flags & FLAG_TILDE; 805 flag_val = option_mask32 & FLAG_TILDE;
830 break; 806 break;
831 case 'N': 807 case 'N':
832 flag_val = flags & FLAG_N; 808 flag_val = option_mask32 & FLAG_N;
833 break; 809 break;
834 case 'E': 810 case 'E':
835 flag_val = flags & FLAG_E; 811 flag_val = option_mask32 & FLAG_E;
836 break; 812 break;
837 default: 813 default:
838 flag_val = 0; 814 flag_val = 0;
@@ -855,26 +831,31 @@ static void full_repaint(void)
855 831
856static void save_input_to_file(void) 832static void save_input_to_file(void)
857{ 833{
858 char current_line[256]; 834 char *current_line;
859 int i; 835 int i;
860 FILE *fp; 836 FILE *fp;
861 837
862 clear_line(); 838 clear_line();
863 printf("Log file: "); 839 printf("Log file: ");
864 fgets(current_line, 256, inp); 840 current_line = xmalloc_getline(inp);
865 current_line[strlen(current_line) - 1] = '\0'; 841 if (strlen(current_line) > 0) {
866 if (strlen(current_line) > 1) { 842 fp = fopen(current_line, "w");
867 fp = xfopen(current_line, "w"); 843 free(current_line);
844 if (!fp) {
845 printf("%s%s%s", HIGHLIGHT, "Error opening log file", NORMAL);
846 return;
847 }
868 for (i = 0; i < num_flines; i++) 848 for (i = 0; i < num_flines; i++)
869 fprintf(fp, "%s", flines[i]); 849 fprintf(fp, "%s\n", flines[i]);
870 fclose(fp); 850 fclose(fp);
871 buffer_print(); 851 buffer_print();
852 return;
872 } 853 }
873 else 854 free(current_line);
874 printf("%s%s%s", HIGHLIGHT, "No log file", NORMAL); 855 printf("%s%s%s", HIGHLIGHT, "No log file", NORMAL);
875} 856}
876 857
877#ifdef CONFIG_FEATURE_LESS_MARKS 858#if ENABLE_FEATURE_LESS_MARKS
878static void add_mark(void) 859static void add_mark(void)
879{ 860{
880 int letter; 861 int letter;
@@ -892,8 +873,7 @@ static void add_mark(void)
892 mark_lines[num_marks][0] = letter; 873 mark_lines[num_marks][0] = letter;
893 mark_lines[num_marks][1] = line_pos; 874 mark_lines[num_marks][1] = line_pos;
894 num_marks++; 875 num_marks++;
895 } 876 } else {
896 else {
897 clear_line(); 877 clear_line();
898 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL); 878 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
899 } 879 }
@@ -917,14 +897,13 @@ static void goto_mark(void)
917 } 897 }
918 if ((num_marks == 14) && (letter != mark_lines[14][0])) 898 if ((num_marks == 14) && (letter != mark_lines[14][0]))
919 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL); 899 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
920 } 900 } else
921 else
922 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL); 901 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
923} 902}
924#endif 903#endif
925 904
926 905
927#ifdef CONFIG_FEATURE_LESS_BRACKETS 906#if ENABLE_FEATURE_LESS_BRACKETS
928 907
929static char opp_bracket(char bracket) 908static char opp_bracket(char bracket)
930{ 909{
@@ -977,8 +956,7 @@ static void match_left_bracket(char bracket)
977 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL); 956 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
978 printf("%s", flines[line_pos + height]); 957 printf("%s", flines[line_pos + height]);
979 sleep(4); 958 sleep(4);
980 } 959 } else {
981 else {
982 for (i = line_pos + height - 2; i >= 0; i--) { 960 for (i = line_pos + height - 2; i >= 0; i--) {
983 if (strchr(flines[i], opp_bracket(bracket)) != NULL) { 961 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
984 bracket_line = i; 962 bracket_line = i;
@@ -993,7 +971,7 @@ static void match_left_bracket(char bracket)
993 } 971 }
994} 972}
995 973
996#endif /* CONFIG_FEATURE_LESS_BRACKETS */ 974#endif /* FEATURE_LESS_BRACKETS */
997 975
998static void keypress_process(int keypress) 976static void keypress_process(int keypress)
999{ 977{
@@ -1031,7 +1009,7 @@ static void keypress_process(int keypress)
1031 case 'q': case 'Q': 1009 case 'q': case 'Q':
1032 tless_exit(0); 1010 tless_exit(0);
1033 break; 1011 break;
1034#ifdef CONFIG_FEATURE_LESS_MARKS 1012#if ENABLE_FEATURE_LESS_MARKS
1035 case 'm': 1013 case 'm':
1036 add_mark(); 1014 add_mark();
1037 buffer_print(); 1015 buffer_print();
@@ -1048,21 +1026,20 @@ static void keypress_process(int keypress)
1048 full_repaint(); 1026 full_repaint();
1049 break; 1027 break;
1050 case 's': 1028 case 's':
1051 if (flags & LESS_STATE_INP_STDIN) 1029 save_input_to_file();
1052 save_input_to_file();
1053 break; 1030 break;
1054 case 'E': 1031 case 'E':
1055 examine_file(); 1032 examine_file();
1056 break; 1033 break;
1057#ifdef CONFIG_FEATURE_LESS_FLAGS 1034#if ENABLE_FEATURE_LESS_FLAGS
1058 case '=': 1035 case '=':
1059 clear_line(); 1036 clear_line();
1060 m_status_print(); 1037 m_status_print();
1061 break; 1038 break;
1062#endif 1039#endif
1063#ifdef CONFIG_FEATURE_LESS_REGEXP 1040#if ENABLE_FEATURE_LESS_REGEXP
1064 case '/': 1041 case '/':
1065 flags &= ~LESS_STATE_MATCH_BACKWARDS; 1042 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1066 regex_process(); 1043 regex_process();
1067 break; 1044 break;
1068 case 'n': 1045 case 'n':
@@ -1072,11 +1049,11 @@ static void keypress_process(int keypress)
1072 goto_match(match_pos - 1); 1049 goto_match(match_pos - 1);
1073 break; 1050 break;
1074 case '?': 1051 case '?':
1075 flags |= LESS_STATE_MATCH_BACKWARDS; 1052 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1076 regex_process(); 1053 regex_process();
1077 break; 1054 break;
1078#endif 1055#endif
1079#ifdef CONFIG_FEATURE_LESS_FLAGCS 1056#if ENABLE_FEATURE_LESS_FLAGCS
1080 case '-': 1057 case '-':
1081 flag_change(); 1058 flag_change();
1082 buffer_print(); 1059 buffer_print();
@@ -1085,7 +1062,7 @@ static void keypress_process(int keypress)
1085 show_flag_status(); 1062 show_flag_status();
1086 break; 1063 break;
1087#endif 1064#endif
1088#ifdef CONFIG_FEATURE_LESS_BRACKETS 1065#if ENABLE_FEATURE_LESS_BRACKETS
1089 case '{': case '(': case '[': 1066 case '{': case '(': case '[':
1090 match_right_bracket(keypress); 1067 match_right_bracket(keypress);
1091 break; 1068 break;
@@ -1104,30 +1081,42 @@ static void keypress_process(int keypress)
1104 number_process(keypress); 1081 number_process(keypress);
1105} 1082}
1106 1083
1107int less_main(int argc, char **argv) { 1084static void sig_catcher(int sig ATTRIBUTE_UNUSED)
1085{
1086 set_tty_cooked();
1087 exit(1);
1088}
1108 1089
1090int less_main(int argc, char **argv)
1091{
1109 int keypress; 1092 int keypress;
1110 1093
1111 flags = getopt32(argc, argv, "EMmN~"); 1094 getopt32(argc, argv, "EMmN~");
1112
1113 argc -= optind; 1095 argc -= optind;
1114 argv += optind; 1096 argv += optind;
1115 files = argv; 1097 files = argv;
1116 num_files = argc; 1098 num_files = argc;
1117 1099
1118 if (!num_files) { 1100 if (!num_files) {
1119 if (ttyname(STDIN_FILENO) == NULL) 1101 if (isatty(STDIN_FILENO)) {
1120 flags |= LESS_STATE_INP_STDIN; 1102 /* Just "less"? No file and no redirection? */
1121 else {
1122 bb_error_msg("missing filename"); 1103 bb_error_msg("missing filename");
1123 bb_show_usage(); 1104 bb_show_usage();
1124 } 1105 }
1125 } 1106 } else
1107 filename = xstrdup(files[0]);
1108
1109 /* FIXME: another popular pager, most, detects when stdout
1110 * is not a tty and turns into cat */
1111 inp = xfopen(CURRENT_TTY, "r");
1126 1112
1127 strcpy(filename, (flags & LESS_STATE_INP_STDIN) ? bb_msg_standard_input : files[0]); 1113 get_terminal_width_height(fileno(inp), &width, &height);
1128 get_terminal_width_height(0, &width, &height);
1129 data_readlines(); 1114 data_readlines();
1115
1116 signal(SIGTERM, sig_catcher);
1117 signal(SIGINT, sig_catcher);
1130 tcgetattr(fileno(inp), &term_orig); 1118 tcgetattr(fileno(inp), &term_orig);
1119
1131 term_vi = term_orig; 1120 term_vi = term_orig;
1132 term_vi.c_lflag &= (~ICANON & ~ECHO); 1121 term_vi.c_lflag &= (~ICANON & ~ECHO);
1133 term_vi.c_iflag &= (~IXON & ~ICRNL); 1122 term_vi.c_iflag &= (~IXON & ~ICRNL);