1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146#include <sched.h>
147
148#include "libbb.h"
149#include "common_bufsiz.h"
150#if ENABLE_FEATURE_LESS_REGEXP
151#include "xregex.h"
152#endif
153
154
155#define ESC "\033"
156
157#define HIGHLIGHT ESC"[7m"
158#define NORMAL ESC"[m"
159
160#define CLEAR ESC"[H"ESC"[J"
161
162#define CLEAR_2_EOL ESC"[K"
163
164enum {
165
166 MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
167
168 TILDES = 1,
169};
170
171
172enum {
173 FLAG_E = 1 << 0,
174 FLAG_M = 1 << 1,
175 FLAG_m = 1 << 2,
176 FLAG_N = 1 << 3,
177 FLAG_TILDE = 1 << 4,
178 FLAG_I = 1 << 5,
179 FLAG_F = 1 << 6,
180 FLAG_S = (1 << 7) * ENABLE_FEATURE_LESS_TRUNCATE,
181 FLAG_R = (1 << 8) * ENABLE_FEATURE_LESS_RAW,
182
183 LESS_STATE_MATCH_BACKWARDS = 1 << 15,
184};
185
186#if !ENABLE_FEATURE_LESS_REGEXP
187enum { pattern_valid = 0 };
188#endif
189
190struct globals {
191 int cur_fline;
192 int kbd_fd;
193 int kbd_fd_orig_flags;
194 int less_gets_pos;
195
196 size_t last_line_pos;
197 unsigned max_fline;
198 unsigned max_lineno;
199 unsigned max_displayed_line;
200 unsigned width;
201#if ENABLE_FEATURE_LESS_WINCH
202 unsigned winch_counter;
203#endif
204 ssize_t eof_error;
205 ssize_t readpos;
206 ssize_t readeof;
207 const char **buffer;
208 const char **flines;
209 const char *empty_line_marker;
210 unsigned num_files;
211 unsigned current_file;
212 char *filename;
213 char **files;
214#if ENABLE_FEATURE_LESS_FLAGS
215 int num_lines;
216# define REOPEN_AND_COUNT (-1)
217# define REOPEN_STDIN (-2)
218# define NOT_REGULAR_FILE (-3)
219#endif
220#if ENABLE_FEATURE_LESS_MARKS
221 unsigned num_marks;
222 unsigned mark_lines[15][2];
223#endif
224#if ENABLE_FEATURE_LESS_REGEXP
225 unsigned *match_lines;
226 int match_pos;
227 int wanted_match;
228 int num_matches;
229 regex_t pattern;
230 smallint pattern_valid;
231#endif
232#if ENABLE_FEATURE_LESS_RAW
233 smallint in_escape;
234#endif
235#if ENABLE_FEATURE_LESS_ASK_TERMINAL
236 smallint winsize_err;
237#endif
238 smallint terminated;
239 struct termios term_orig, term_less;
240 char kbd_input[KEYCODE_BUFFER_SIZE];
241};
242#define G (*ptr_to_globals)
243#define cur_fline (G.cur_fline )
244#define kbd_fd (G.kbd_fd )
245#define less_gets_pos (G.less_gets_pos )
246#define last_line_pos (G.last_line_pos )
247#define max_fline (G.max_fline )
248#define max_lineno (G.max_lineno )
249#define max_displayed_line (G.max_displayed_line)
250#define width (G.width )
251#define winch_counter (G.winch_counter )
252
253#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
254#define eof_error (G.eof_error )
255#define readpos (G.readpos )
256#define readeof (G.readeof )
257#define buffer (G.buffer )
258#define flines (G.flines )
259#define empty_line_marker (G.empty_line_marker )
260#define num_files (G.num_files )
261#define current_file (G.current_file )
262#define filename (G.filename )
263#define files (G.files )
264#define num_lines (G.num_lines )
265#define num_marks (G.num_marks )
266#define mark_lines (G.mark_lines )
267#if ENABLE_FEATURE_LESS_REGEXP
268#define match_lines (G.match_lines )
269#define match_pos (G.match_pos )
270#define num_matches (G.num_matches )
271#define wanted_match (G.wanted_match )
272#define pattern (G.pattern )
273#define pattern_valid (G.pattern_valid )
274#endif
275#define terminated (G.terminated )
276#define term_orig (G.term_orig )
277#define term_less (G.term_less )
278#define kbd_input (G.kbd_input )
279#define INIT_G() do { \
280 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
281 less_gets_pos = -1; \
282 empty_line_marker = "~"; \
283 current_file = 1; \
284 eof_error = 1; \
285 terminated = 1; \
286 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \
287} while (0)
288
289
290
291
292
293#define MEMPTR(p) ((char*)(p) - 4)
294#define LINENO(p) (*(uint32_t*)((p) - 4))
295
296
297
298static void set_tty_cooked(void)
299{
300 fflush_all();
301 tcsetattr(kbd_fd, TCSANOW, &term_orig);
302}
303
304
305
306static void move_cursor(int line, int col)
307{
308 printf(ESC"[%u;%uH", line, col);
309}
310
311static void clear_line(void)
312{
313 printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
314}
315
316static void print_hilite(const char *str)
317{
318 printf(HIGHLIGHT"%s"NORMAL, str);
319}
320
321static void print_statusline(const char *str)
322{
323 clear_line();
324 printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
325}
326
327
328static void restore_tty(void)
329{
330 set_tty_cooked();
331 if (!(G.kbd_fd_orig_flags & O_NONBLOCK))
332 ndelay_off(kbd_fd);
333 clear_line();
334}
335
336static NOINLINE void less_exit(void)
337{
338 restore_tty();
339 exit_SUCCESS();
340}
341
342#if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \
343 || ENABLE_FEATURE_LESS_WINCH
344static void re_wrap(void)
345{
346 int w = width;
347 int new_line_pos;
348 int src_idx;
349 int dst_idx;
350 int new_cur_fline = 0;
351 uint32_t lineno;
352 char linebuf[w + 1];
353 const char **old_flines = flines;
354 const char *s;
355 char **new_flines = NULL;
356 char *d;
357
358 if (option_mask32 & FLAG_N)
359 w -= 8;
360
361 src_idx = 0;
362 dst_idx = 0;
363 s = old_flines[0];
364 lineno = LINENO(s);
365 d = linebuf;
366 new_line_pos = 0;
367 while (1) {
368 *d = *s;
369 if (*d != '\0') {
370 new_line_pos++;
371 if (*d == '\t') {
372 new_line_pos += 7;
373 new_line_pos &= (~7);
374 }
375 s++;
376 d++;
377 if (new_line_pos >= w) {
378 int sz;
379
380 *d = '\0';
381 next_new:
382 sz = (d - linebuf) + 1;
383 d = ((char*)xmalloc(sz + 4)) + 4;
384 LINENO(d) = lineno;
385 memcpy(d, linebuf, sz);
386 new_flines = xrealloc_vector(new_flines, 8, dst_idx);
387 new_flines[dst_idx] = d;
388 dst_idx++;
389 if (new_line_pos < w) {
390
391 if (src_idx > max_fline)
392 break;
393 lineno = LINENO(s);
394 }
395 d = linebuf;
396 new_line_pos = 0;
397 }
398 continue;
399 }
400
401 free(MEMPTR(old_flines[src_idx]));
402
403 if (cur_fline == src_idx)
404 new_cur_fline = dst_idx;
405 src_idx++;
406
407 if (src_idx > max_fline)
408 goto next_new;
409 s = old_flines[src_idx];
410 if (lineno != LINENO(s)) {
411
412
413 goto next_new;
414 }
415 }
416
417 free(old_flines);
418 flines = (const char **)new_flines;
419
420 max_fline = dst_idx - 1;
421 last_line_pos = new_line_pos;
422 cur_fline = new_cur_fline;
423
424#if ENABLE_FEATURE_LESS_REGEXP
425 pattern_valid = 0;
426#endif
427}
428#endif
429
430#if ENABLE_FEATURE_LESS_REGEXP
431static void fill_match_lines(unsigned pos);
432#else
433#define fill_match_lines(pos) ((void)0)
434#endif
435
436static int at_end(void)
437{
438 return (option_mask32 & FLAG_S)
439 ? !(cur_fline <= max_fline &&
440 max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
441 : !(max_fline > cur_fline + max_displayed_line);
442}
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470static void read_lines(void)
471{
472 char *current_line, *p;
473 int w = width;
474 char last_terminated = terminated;
475 time_t last_time = 0;
476 int retry_EAGAIN = 2;
477#if ENABLE_FEATURE_LESS_REGEXP
478 unsigned old_max_fline = max_fline;
479#endif
480
481#define readbuf bb_common_bufsiz1
482 setup_common_bufsiz();
483
484
485 if (max_fline + 1 > MAXLINES)
486 return;
487
488 if (option_mask32 & FLAG_N)
489 w -= 8;
490
491 p = current_line = ((char*)xmalloc(w + 5)) + 4;
492 if (!last_terminated) {
493 const char *cp = flines[max_fline];
494 p = stpcpy(p, cp);
495 free(MEMPTR(cp));
496
497 } else {
498 max_fline++;
499 last_line_pos = 0;
500 }
501
502 while (1) {
503 *p = '\0';
504 terminated = 0;
505 while (1) {
506 char c;
507
508 if (readpos >= readeof) {
509 int flags = ndelay_on(0);
510
511 while (1) {
512 time_t t;
513
514 errno = 0;
515 eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
516 if (errno != EAGAIN)
517 break;
518 t = time(NULL);
519 if (t != last_time) {
520 last_time = t;
521 if (--retry_EAGAIN < 0)
522 break;
523 }
524 sched_yield();
525 }
526 fcntl(0, F_SETFL, flags);
527 readpos = 0;
528 readeof = eof_error;
529 if (eof_error <= 0)
530 goto reached_eof;
531 retry_EAGAIN = 1;
532 }
533 c = readbuf[readpos];
534
535
536
537 if (c == '\x8' && last_line_pos && p[-1] != '\t') {
538 readpos++;
539 last_line_pos--;
540
541 *--p = '\0';
542 continue;
543 }
544#if ENABLE_FEATURE_LESS_RAW
545 if (option_mask32 & FLAG_R) {
546 if (c == '\033')
547 goto discard;
548 if (G.in_escape) {
549 if (isdigit(c)
550 || c == '['
551 || c == ';'
552 || c == 'm'
553 ) {
554 discard:
555 G.in_escape = (c != 'm');
556 readpos++;
557 continue;
558 }
559
560 G.in_escape = 0;
561 }
562 }
563#endif
564 {
565 size_t new_last_line_pos = last_line_pos + 1;
566 if (c == '\t') {
567 new_last_line_pos += 7;
568 new_last_line_pos &= (~7);
569 }
570 if ((int)new_last_line_pos > w)
571 break;
572 last_line_pos = new_last_line_pos;
573 }
574
575 readpos++;
576 if (c == '\n') {
577 terminated = 1;
578 last_line_pos = 0;
579 break;
580 }
581
582 if (c == '\0') c = '\n';
583 *p++ = c;
584 *p = '\0';
585 }
586#if 0
587
588
589
590
591
592
593 if (!last_terminated && !current_line[0]) {
594 last_terminated = 1;
595 max_lineno++;
596 continue;
597 }
598#endif
599 reached_eof:
600 last_terminated = terminated;
601 flines = xrealloc_vector(flines, 8, max_fline);
602
603 flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4;
604 LINENO(flines[max_fline]) = max_lineno;
605 if (terminated)
606 max_lineno++;
607
608 if (max_fline >= MAXLINES) {
609 eof_error = 0;
610 break;
611 }
612 if (!at_end()) {
613#if !ENABLE_FEATURE_LESS_REGEXP
614 break;
615#else
616 if (wanted_match >= num_matches) {
617 fill_match_lines(old_max_fline);
618 old_max_fline = max_fline;
619 }
620 if (wanted_match < num_matches)
621 break;
622#endif
623 }
624 if (eof_error <= 0) {
625 break;
626 }
627 max_fline++;
628 current_line = ((char*)xmalloc(w + 5)) + 4;
629 p = current_line;
630 last_line_pos = 0;
631 }
632
633 if (eof_error < 0) {
634 if (errno == EAGAIN) {
635 eof_error = 1;
636 } else {
637 print_statusline(bb_msg_read_error);
638 }
639 }
640#if ENABLE_FEATURE_LESS_FLAGS
641 else if (eof_error == 0)
642 num_lines = max_lineno;
643#endif
644
645 fill_match_lines(old_max_fline);
646#if ENABLE_FEATURE_LESS_REGEXP
647
648 wanted_match = -1;
649#endif
650#undef readbuf
651}
652
653#if ENABLE_FEATURE_LESS_FLAGS
654static int safe_lineno(int fline)
655{
656 if (fline >= max_fline)
657 fline = max_fline - 1;
658
659
660 if (fline < 0)
661 return 0;
662
663 return LINENO(flines[fline]) + 1;
664}
665
666
667static void update_num_lines(void)
668{
669 int count, fd;
670 struct stat stbuf;
671 ssize_t len, i;
672 char buf[4096];
673
674
675 if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) {
676 count = 0;
677 fd = open("/proc/self/fd/0", O_RDONLY);
678 if (fd < 0 && num_lines == REOPEN_AND_COUNT) {
679
680 fd = open(filename, O_RDONLY);
681 }
682 if (fd < 0) {
683
684 num_lines = NOT_REGULAR_FILE;
685 return;
686 }
687 if (fstat(fd, &stbuf) != 0 || !S_ISREG(stbuf.st_mode)) {
688 num_lines = NOT_REGULAR_FILE;
689 goto do_close;
690 }
691 while ((len = safe_read(fd, buf, sizeof(buf))) > 0) {
692 for (i = 0; i < len; ++i) {
693 if (buf[i] == '\n' && ++count == MAXLINES)
694 goto done;
695 }
696 }
697 done:
698 num_lines = count;
699 do_close:
700 close(fd);
701 }
702}
703
704
705static void m_status_print(void)
706{
707 int first, last;
708 unsigned percent;
709
710 if (less_gets_pos >= 0)
711 return;
712
713 clear_line();
714 printf(HIGHLIGHT"%s", filename);
715 if (num_files > 1)
716 printf(" (file %i of %i)", current_file, num_files);
717
718 first = safe_lineno(cur_fline);
719 last = (option_mask32 & FLAG_S)
720 ? MIN(first + max_displayed_line, max_lineno)
721 : safe_lineno(cur_fline + max_displayed_line);
722 printf(" lines %i-%i", first, last);
723
724 update_num_lines();
725 if (num_lines >= 0)
726 printf("/%i", num_lines);
727
728 if (at_end()) {
729 printf(" (END)");
730 if (num_files > 1 && current_file != num_files)
731 printf(" - next: %s", files[current_file]);
732 } else if (num_lines > 0) {
733 percent = (100 * last + num_lines/2) / num_lines;
734 printf(" %i%%", percent <= 100 ? percent : 100);
735 }
736 printf(NORMAL);
737}
738#endif
739
740
741static void status_print(void)
742{
743 const char *p;
744
745 if (less_gets_pos >= 0)
746 return;
747
748
749#if ENABLE_FEATURE_LESS_FLAGS
750 if (option_mask32 & (FLAG_M|FLAG_m)) {
751 m_status_print();
752 return;
753 }
754
755#endif
756
757 clear_line();
758 if (cur_fline && !at_end()) {
759 bb_putchar(':');
760 return;
761 }
762 p = "(END)";
763 if (!cur_fline)
764 p = filename;
765 if (num_files > 1) {
766 printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
767 p, current_file, num_files);
768 return;
769 }
770 print_hilite(p);
771}
772
773static const char controls[] ALIGN1 =
774
775 "\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
776 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
777 "\x7f\x9b";
778static const char ctrlconv[] ALIGN1 =
779
780
781 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
782 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
783
784static void print_lineno(const char *line)
785{
786 const char *fmt = " ";
787 unsigned n = n;
788
789 if (line != empty_line_marker) {
790
791 fmt = "%7u ";
792 n = LINENO(line) + 1;
793 if (n > 9999999 && MAXLINES > 9999999) {
794 n %= 10000000;
795 fmt = "%07u ";
796 }
797 }
798 printf(fmt, n);
799}
800
801
802#if ENABLE_FEATURE_LESS_REGEXP
803static void print_found(const char *line)
804{
805 int match_status;
806 int eflags;
807 char *growline;
808 regmatch_t match_structs;
809
810 char buf[width+1];
811 const char *str = line;
812 char *p = buf;
813 size_t n;
814
815 while (*str) {
816 n = strcspn(str, controls);
817 if (n) {
818 if (!str[n]) break;
819 memcpy(p, str, n);
820 p += n;
821 str += n;
822 }
823 n = strspn(str, controls);
824 memset(p, '.', n);
825 p += n;
826 str += n;
827 }
828 strcpy(p, str);
829
830
831
832
833
834
835
836 str = buf;
837 growline = NULL;
838 eflags = 0;
839 goto start;
840
841 while (match_status == 0) {
842 char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
843 growline ? growline : "",
844 (int)match_structs.rm_so, str,
845 (int)(match_structs.rm_eo - match_structs.rm_so),
846 str + match_structs.rm_so);
847 free(growline);
848 growline = new;
849 str += match_structs.rm_eo;
850 line += match_structs.rm_eo;
851 eflags = REG_NOTBOL;
852 start:
853
854 match_status = regexec(&pattern, line, 1, &match_structs, eflags);
855
856 if (match_structs.rm_so >= match_structs.rm_eo)
857 match_status = 1;
858 }
859
860 printf("%s%s\n", growline ? growline : "", str);
861 free(growline);
862}
863#else
864void print_found(const char *line);
865#endif
866
867static void print_ascii(const char *str)
868{
869 char buf[width+1];
870 char *p;
871 size_t n;
872
873 while (*str) {
874 n = strcspn(str, controls);
875 if (n) {
876 if (!str[n]) break;
877 printf("%.*s", (int) n, str);
878 str += n;
879 }
880 n = strspn(str, controls);
881 p = buf;
882 do {
883 if (*str == 0x7f)
884 *p++ = '?';
885 else if (*str == (char)0x9b)
886
887
888 *p++ = '{';
889 else
890 *p++ = ctrlconv[(unsigned char)*str];
891 str++;
892 } while (--n);
893 *p = '\0';
894 print_hilite(buf);
895 }
896 puts(str);
897}
898
899
900static void buffer_print(void)
901{
902 unsigned i;
903
904 move_cursor(0, 0);
905 for (i = 0; i <= max_displayed_line; i++) {
906 printf(CLEAR_2_EOL);
907 if (option_mask32 & FLAG_N)
908 print_lineno(buffer[i]);
909 if (pattern_valid)
910 print_found(buffer[i]);
911 else
912 print_ascii(buffer[i]);
913 }
914 if ((option_mask32 & (FLAG_E|FLAG_F))
915 && eof_error <= 0
916 ) {
917 i = option_mask32 & FLAG_F ? 0 : cur_fline;
918 if (max_fline - i <= max_displayed_line)
919 less_exit();
920 }
921 status_print();
922}
923
924static void buffer_fill_and_print(void)
925{
926 unsigned i;
927#if ENABLE_FEATURE_LESS_TRUNCATE
928 int fpos = cur_fline;
929
930 if (option_mask32 & FLAG_S) {
931
932 while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1]))
933 fpos--;
934 }
935
936 i = 0;
937 while (i <= max_displayed_line && fpos <= max_fline) {
938 int lineno = LINENO(flines[fpos]);
939 buffer[i] = flines[fpos];
940 i++;
941 do {
942 fpos++;
943 } while ((fpos <= max_fline)
944 && (option_mask32 & FLAG_S)
945 && lineno == LINENO(flines[fpos])
946 );
947 }
948#else
949 for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
950 buffer[i] = flines[cur_fline + i];
951 }
952#endif
953 for (; i <= max_displayed_line; i++) {
954 buffer[i] = empty_line_marker;
955 }
956 buffer_print();
957}
958
959
960static void goto_lineno(int target)
961{
962 if (target <= 0 ) {
963 cur_fline = 0;
964 }
965 else if (target > LINENO(flines[cur_fline])) {
966 retry:
967 while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline)
968 ++cur_fline;
969
970 if (LINENO(flines[cur_fline]) != target && eof_error > 0) {
971 read_lines();
972 goto retry;
973 }
974 }
975 else {
976
977 while (LINENO(flines[cur_fline]) != target && cur_fline > 0)
978 --cur_fline;
979 }
980}
981
982static void cap_cur_fline(void)
983{
984 if ((option_mask32 & FLAG_S)) {
985 if (cur_fline > max_fline)
986 cur_fline = max_fline;
987 if (LINENO(flines[cur_fline]) + max_displayed_line > max_lineno + TILDES) {
988 goto_lineno(max_lineno - max_displayed_line + TILDES);
989 read_lines();
990 }
991 }
992 else {
993 if (cur_fline + max_displayed_line > max_fline + TILDES)
994 cur_fline = max_fline - max_displayed_line + TILDES;
995 if (cur_fline < 0)
996 cur_fline = 0;
997 }
998}
999
1000
1001static void buffer_down(int nlines)
1002{
1003 if ((option_mask32 & FLAG_S))
1004 goto_lineno(LINENO(flines[cur_fline]) + nlines);
1005 else
1006 cur_fline += nlines;
1007 read_lines();
1008 cap_cur_fline();
1009 buffer_fill_and_print();
1010}
1011
1012static void buffer_up(int nlines)
1013{
1014 if ((option_mask32 & FLAG_S)) {
1015 goto_lineno(LINENO(flines[cur_fline]) - nlines);
1016 }
1017 else {
1018 cur_fline -= nlines;
1019 if (cur_fline < 0)
1020 cur_fline = 0;
1021 }
1022 read_lines();
1023 buffer_fill_and_print();
1024}
1025
1026
1027
1028static void buffer_to_line(int linenum, int is_lineno)
1029{
1030 if (linenum <= 0)
1031 cur_fline = 0;
1032 else if (is_lineno)
1033 goto_lineno(linenum);
1034 else
1035 cur_fline = linenum;
1036 read_lines();
1037 cap_cur_fline();
1038 buffer_fill_and_print();
1039}
1040
1041static void buffer_line(int linenum)
1042{
1043 buffer_to_line(linenum, FALSE);
1044}
1045
1046static void buffer_lineno(int lineno)
1047{
1048 buffer_to_line(lineno, TRUE);
1049}
1050
1051static void open_file_and_read_lines(void)
1052{
1053 if (filename) {
1054 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
1055#if ENABLE_FEATURE_LESS_FLAGS
1056 num_lines = REOPEN_AND_COUNT;
1057#endif
1058 } else {
1059
1060
1061 filename = xstrdup(bb_msg_standard_input);
1062#if ENABLE_FEATURE_LESS_FLAGS
1063 num_lines = REOPEN_STDIN;
1064#endif
1065 }
1066 readpos = 0;
1067 readeof = 0;
1068 last_line_pos = 0;
1069 terminated = 1;
1070 read_lines();
1071}
1072
1073
1074static void reinitialize(void)
1075{
1076 unsigned i;
1077
1078 if (flines) {
1079 for (i = 0; i <= max_fline; i++)
1080 free(MEMPTR(flines[i]));
1081 free(flines);
1082 flines = NULL;
1083 }
1084
1085 max_fline = -1;
1086 cur_fline = 0;
1087 max_lineno = 0;
1088 open_file_and_read_lines();
1089#if ENABLE_FEATURE_LESS_ASK_TERMINAL
1090 if (G.winsize_err)
1091 printf(ESC"[999;999H" ESC"[6n");
1092#endif
1093 buffer_fill_and_print();
1094}
1095
1096static int64_t getch_nowait(void)
1097{
1098 int rd;
1099 int64_t key64;
1100 struct pollfd pfd[2];
1101
1102 pfd[0].fd = STDIN_FILENO;
1103 pfd[0].events = POLLIN;
1104 pfd[1].fd = kbd_fd;
1105 pfd[1].events = POLLIN;
1106 again:
1107 tcsetattr(kbd_fd, TCSANOW, &term_less);
1108
1109
1110
1111
1112
1113
1114 rd = 1;
1115
1116 if (at_end()) {
1117 if (eof_error > 0)
1118 rd = 0;
1119 }
1120
1121 if (less_gets_pos >= 0)
1122 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
1123 fflush_all();
1124
1125 if (kbd_input[0] == 0) {
1126#if ENABLE_FEATURE_LESS_WINCH
1127 while (1) {
1128 int r;
1129
1130 r = poll(pfd + rd, 2 - rd, -1);
1131 if ( winch_counter)
1132 return '\\';
1133 if (r) break;
1134 }
1135#else
1136 safe_poll(pfd + rd, 2 - rd, -1);
1137#endif
1138 }
1139
1140
1141
1142 key64 = safe_read_key(kbd_fd, kbd_input, -2);
1143 if ((int)key64 == -1) {
1144 if (errno == EAGAIN) {
1145
1146
1147 read_lines();
1148 buffer_fill_and_print();
1149 goto again;
1150 }
1151
1152 less_exit();
1153 }
1154 set_tty_cooked();
1155 return key64;
1156}
1157
1158
1159
1160
1161static int64_t less_getch(int pos)
1162{
1163 int64_t key64;
1164 int key;
1165
1166 again:
1167 less_gets_pos = pos;
1168 key = key64 = getch_nowait();
1169 less_gets_pos = -1;
1170
1171
1172
1173
1174
1175 if (key >= 0 && key < ' ' && key != 0x0d && key != 8)
1176 goto again;
1177
1178 return key64;
1179}
1180
1181static char* less_gets(int sz)
1182{
1183 int c;
1184 unsigned i = 0;
1185 char *result = xzalloc(1);
1186
1187 while (1) {
1188 c = '\0';
1189 less_gets_pos = sz + i;
1190 c = getch_nowait();
1191 if (c == 0x0d) {
1192 result[i] = '\0';
1193 less_gets_pos = -1;
1194 return result;
1195 }
1196 if (c == 0x7f)
1197 c = 8;
1198 if (c == 8 && i) {
1199 printf("\x8 \x8");
1200 i--;
1201 }
1202 if (c < ' ')
1203 continue;
1204 if (i >= width - sz - 1)
1205 continue;
1206 bb_putchar(c);
1207 result[i++] = c;
1208 result = xrealloc(result, i+1);
1209 }
1210}
1211
1212static void examine_file(void)
1213{
1214 char *new_fname;
1215
1216 print_statusline("Examine: ");
1217 new_fname = less_gets(sizeof("Examine: ") - 1);
1218 if (!new_fname[0]) {
1219 status_print();
1220 err:
1221 free(new_fname);
1222 return;
1223 }
1224 if (access(new_fname, R_OK) != 0) {
1225 print_statusline("Cannot read this file");
1226 goto err;
1227 }
1228 free(filename);
1229 filename = new_fname;
1230
1231
1232
1233
1234 files[0] = filename;
1235 num_files = current_file = 1;
1236 reinitialize();
1237}
1238
1239
1240
1241
1242
1243static void change_file(int direction)
1244{
1245 if (current_file != ((direction > 0) ? num_files : 1)) {
1246 current_file = direction ? current_file + direction : 1;
1247 free(filename);
1248 filename = xstrdup(files[current_file - 1]);
1249 reinitialize();
1250 } else {
1251 print_statusline(direction > 0 ? "No next file" : "No previous file");
1252 }
1253}
1254
1255static void remove_current_file(void)
1256{
1257 unsigned i;
1258
1259 if (num_files < 2)
1260 return;
1261
1262 if (current_file != 1) {
1263 change_file(-1);
1264 for (i = 3; i <= num_files; i++)
1265 files[i - 2] = files[i - 1];
1266 num_files--;
1267 } else {
1268 change_file(1);
1269 for (i = 2; i <= num_files; i++)
1270 files[i - 2] = files[i - 1];
1271 num_files--;
1272 current_file--;
1273 }
1274}
1275
1276static void colon_process(void)
1277{
1278 int keypress;
1279
1280
1281 print_statusline(" :");
1282
1283 keypress = less_getch(2);
1284 switch (keypress) {
1285 case 'd':
1286 remove_current_file();
1287 break;
1288 case 'e':
1289 examine_file();
1290 break;
1291#if ENABLE_FEATURE_LESS_FLAGS
1292 case 'f':
1293 m_status_print();
1294 break;
1295#endif
1296 case 'n':
1297 change_file(1);
1298 break;
1299 case 'p':
1300 change_file(-1);
1301 break;
1302 case 'q':
1303 less_exit();
1304 break;
1305 case 'x':
1306 change_file(0);
1307 break;
1308 }
1309}
1310
1311#if ENABLE_FEATURE_LESS_REGEXP
1312static void normalize_match_pos(int match)
1313{
1314 if (match >= num_matches)
1315 match = num_matches - 1;
1316 if (match < 0)
1317 match = 0;
1318 match_pos = match;
1319}
1320
1321static void goto_match(int match)
1322{
1323 if (!pattern_valid)
1324 return;
1325 if (match < 0)
1326 match = 0;
1327
1328 if (match >= num_matches && eof_error > 0) {
1329 wanted_match = match;
1330 read_lines();
1331 }
1332 if (num_matches) {
1333 normalize_match_pos(match);
1334 buffer_line(match_lines[match_pos]);
1335 } else {
1336 print_statusline("No matches found");
1337 }
1338}
1339
1340static void fill_match_lines(unsigned pos)
1341{
1342 if (!pattern_valid)
1343 return;
1344
1345 while (pos <= max_fline) {
1346
1347 if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
1348
1349 && !(num_matches && match_lines[num_matches-1] == pos)
1350 ) {
1351 match_lines = xrealloc_vector(match_lines, 4, num_matches);
1352 match_lines[num_matches++] = pos;
1353 }
1354 pos++;
1355 }
1356}
1357
1358static void regex_process(void)
1359{
1360 char *uncomp_regex, *err;
1361
1362
1363 free(match_lines);
1364 match_lines = NULL;
1365 match_pos = 0;
1366 num_matches = 0;
1367 if (pattern_valid) {
1368 regfree(&pattern);
1369 pattern_valid = 0;
1370 }
1371
1372
1373 clear_line();
1374 bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
1375 uncomp_regex = less_gets(1);
1376 if (!uncomp_regex[0]) {
1377 free(uncomp_regex);
1378 buffer_print();
1379 return;
1380 }
1381
1382
1383 err = regcomp_or_errmsg(&pattern, uncomp_regex,
1384 (option_mask32 & FLAG_I) ? REG_ICASE : 0);
1385 free(uncomp_regex);
1386 if (err) {
1387 print_statusline(err);
1388 free(err);
1389 return;
1390 }
1391
1392 pattern_valid = 1;
1393 match_pos = 0;
1394 fill_match_lines(0);
1395 while (match_pos < num_matches) {
1396 if ((int)match_lines[match_pos] > cur_fline)
1397 break;
1398 match_pos++;
1399 }
1400 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
1401 match_pos--;
1402
1403
1404
1405
1406 goto_match(match_pos);
1407}
1408#endif
1409
1410static void number_process(int first_digit)
1411{
1412 unsigned i;
1413 int num;
1414 int keypress;
1415 char num_input[sizeof(int)*4];
1416
1417 num_input[0] = first_digit;
1418
1419
1420 clear_line();
1421 printf(":%c", first_digit);
1422
1423
1424 i = 1;
1425 while (i < sizeof(num_input)-1) {
1426 keypress = less_getch(i + 1);
1427 if ((unsigned)keypress > 255 || !isdigit(keypress))
1428 break;
1429 num_input[i] = keypress;
1430 bb_putchar(keypress);
1431 i++;
1432 }
1433
1434 num_input[i] = '\0';
1435 num = bb_strtou(num_input, NULL, 10);
1436
1437 if (num < 1 || num > MAXLINES) {
1438 buffer_print();
1439 return;
1440 }
1441
1442
1443 switch (keypress) {
1444 case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
1445 buffer_down(num);
1446 break;
1447 case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u':
1448 buffer_up(num);
1449 break;
1450 case 'g': case '<': case 'G': case '>':
1451 buffer_lineno(num - 1);
1452 break;
1453 case 'p': case '%':
1454#if ENABLE_FEATURE_LESS_FLAGS
1455 update_num_lines();
1456 num = num * (num_lines > 0 ? num_lines : max_lineno) / 100;
1457#else
1458 num = num * max_lineno / 100;
1459#endif
1460 buffer_lineno(num);
1461 break;
1462#if ENABLE_FEATURE_LESS_REGEXP
1463 case 'n':
1464 goto_match(match_pos + num);
1465 break;
1466 case '/':
1467 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1468 regex_process();
1469 break;
1470 case '?':
1471 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1472 regex_process();
1473 break;
1474#endif
1475 }
1476}
1477
1478#if ENABLE_FEATURE_LESS_DASHCMD
1479static void flag_change(void)
1480{
1481 int keypress;
1482
1483 clear_line();
1484 bb_putchar('-');
1485 keypress = less_getch(1);
1486
1487 switch (keypress) {
1488 case 'M':
1489 option_mask32 ^= FLAG_M;
1490 break;
1491 case 'm':
1492 option_mask32 ^= FLAG_m;
1493 break;
1494 case 'E':
1495 option_mask32 ^= FLAG_E;
1496 break;
1497 case '~':
1498 option_mask32 ^= FLAG_TILDE;
1499 break;
1500#if ENABLE_FEATURE_LESS_TRUNCATE
1501 case 'S':
1502 option_mask32 ^= FLAG_S;
1503 buffer_fill_and_print();
1504 break;
1505#endif
1506#if ENABLE_FEATURE_LESS_LINENUMS
1507 case 'N':
1508 option_mask32 ^= FLAG_N;
1509 re_wrap();
1510 buffer_fill_and_print();
1511 break;
1512#endif
1513 }
1514}
1515
1516#ifdef BLOAT
1517static void show_flag_status(void)
1518{
1519 int keypress;
1520 int flag_val;
1521
1522 clear_line();
1523 bb_putchar('_');
1524 keypress = less_getch(1);
1525
1526 switch (keypress) {
1527 case 'M':
1528 flag_val = option_mask32 & FLAG_M;
1529 break;
1530 case 'm':
1531 flag_val = option_mask32 & FLAG_m;
1532 break;
1533 case '~':
1534 flag_val = option_mask32 & FLAG_TILDE;
1535 break;
1536 case 'N':
1537 flag_val = option_mask32 & FLAG_N;
1538 break;
1539 case 'E':
1540 flag_val = option_mask32 & FLAG_E;
1541 break;
1542 default:
1543 flag_val = 0;
1544 break;
1545 }
1546
1547 clear_line();
1548 printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
1549}
1550#endif
1551
1552#endif
1553
1554static void save_input_to_file(void)
1555{
1556 const char *msg = "";
1557 char *current_line;
1558 unsigned i;
1559 FILE *fp;
1560
1561 print_statusline("Log file: ");
1562 current_line = less_gets(sizeof("Log file: ")-1);
1563 if (current_line[0]) {
1564 fp = fopen_for_write(current_line);
1565 if (!fp) {
1566 msg = "Error opening log file";
1567 goto ret;
1568 }
1569 for (i = 0; i <= max_fline; i++)
1570 fprintf(fp, "%s\n", flines[i]);
1571 fclose(fp);
1572 msg = "Done";
1573 }
1574 ret:
1575 print_statusline(msg);
1576 free(current_line);
1577}
1578
1579#if ENABLE_FEATURE_LESS_MARKS
1580static void add_mark(void)
1581{
1582 int letter;
1583
1584 print_statusline("Mark: ");
1585 letter = less_getch(sizeof("Mark: ") - 1);
1586
1587 if (isalpha(letter)) {
1588
1589 if (num_marks == 14)
1590 num_marks = 0;
1591
1592 mark_lines[num_marks][0] = letter;
1593 mark_lines[num_marks][1] = cur_fline;
1594 num_marks++;
1595 } else {
1596 print_statusline("Invalid mark letter");
1597 }
1598}
1599
1600static void goto_mark(void)
1601{
1602 int letter;
1603 int i;
1604
1605 print_statusline("Go to mark: ");
1606 letter = less_getch(sizeof("Go to mark: ") - 1);
1607 clear_line();
1608
1609 if (isalpha(letter)) {
1610 for (i = 0; i <= num_marks; i++)
1611 if (letter == mark_lines[i][0]) {
1612 buffer_line(mark_lines[i][1]);
1613 break;
1614 }
1615 if (num_marks == 14 && letter != mark_lines[14][0])
1616 print_statusline("Mark not set");
1617 } else
1618 print_statusline("Invalid mark letter");
1619}
1620#endif
1621
1622#if ENABLE_FEATURE_LESS_BRACKETS
1623static char opp_bracket(char bracket)
1624{
1625 switch (bracket) {
1626 case '{': case '[':
1627 bracket++;
1628 case '(':
1629 bracket++;
1630 break;
1631 case '}': case ']':
1632 bracket--;
1633 case ')':
1634 bracket--;
1635 break;
1636 };
1637 return bracket;
1638}
1639
1640static void match_right_bracket(char bracket)
1641{
1642 unsigned i = cur_fline;
1643
1644 if (i >= max_fline
1645 || strchr(flines[i], bracket) == NULL
1646 ) {
1647 print_statusline("No bracket in top line");
1648 return;
1649 }
1650
1651 bracket = opp_bracket(bracket);
1652 for (; i < max_fline; i++) {
1653 if (strchr(flines[i], bracket) != NULL) {
1654
1655
1656
1657
1658 buffer_line(i - max_displayed_line);
1659 return;
1660 }
1661 }
1662 print_statusline("No matching bracket found");
1663}
1664
1665static void match_left_bracket(char bracket)
1666{
1667 int i = cur_fline + max_displayed_line;
1668
1669 if (i >= max_fline
1670 || strchr(flines[i], bracket) == NULL
1671 ) {
1672 print_statusline("No bracket in bottom line");
1673 return;
1674 }
1675
1676 bracket = opp_bracket(bracket);
1677 for (; i >= 0; i--) {
1678 if (strchr(flines[i], bracket) != NULL) {
1679
1680
1681
1682
1683 buffer_line(i);
1684 return;
1685 }
1686 }
1687 print_statusline("No matching bracket found");
1688}
1689#endif
1690
1691static void keypress_process(int keypress)
1692{
1693 switch (keypress) {
1694 case KEYCODE_DOWN: case 'e': case 'j': case 0x0d:
1695 buffer_down(1);
1696 break;
1697 case KEYCODE_UP: case 'y': case 'k':
1698 buffer_up(1);
1699 break;
1700 case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f':
1701 buffer_down(max_displayed_line + 1);
1702 break;
1703 case KEYCODE_PAGEUP: case 'w': case 'b':
1704 buffer_up(max_displayed_line + 1);
1705 break;
1706 case 'd':
1707 buffer_down((max_displayed_line + 1) / 2);
1708 break;
1709 case 'u':
1710 buffer_up((max_displayed_line + 1) / 2);
1711 break;
1712 case KEYCODE_HOME: case 'g': case 'p': case '<': case '%':
1713 buffer_line(0);
1714 break;
1715 case KEYCODE_END: case 'G': case '>':
1716 cur_fline = MAXLINES;
1717 read_lines();
1718 buffer_line(cur_fline);
1719 break;
1720 case 'q': case 'Q':
1721 less_exit();
1722 break;
1723#if ENABLE_FEATURE_LESS_MARKS
1724 case 'm':
1725 add_mark();
1726 buffer_print();
1727 break;
1728 case '\'':
1729 goto_mark();
1730 buffer_print();
1731 break;
1732#endif
1733 case 'r': case 'R':
1734
1735
1736
1737 buffer_print();
1738 break;
1739
1740
1741
1742 case 's':
1743 save_input_to_file();
1744 break;
1745 case 'E':
1746 examine_file();
1747 break;
1748#if ENABLE_FEATURE_LESS_FLAGS
1749 case '=':
1750 m_status_print();
1751 break;
1752#endif
1753#if ENABLE_FEATURE_LESS_REGEXP
1754 case '/':
1755 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1756 regex_process();
1757 break;
1758 case 'n':
1759 goto_match(match_pos + 1);
1760 break;
1761 case 'N':
1762 goto_match(match_pos - 1);
1763 break;
1764 case '?':
1765 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1766 regex_process();
1767 break;
1768#endif
1769#if ENABLE_FEATURE_LESS_DASHCMD
1770 case '-':
1771 flag_change();
1772 buffer_print();
1773 break;
1774#ifdef BLOAT
1775 case '_':
1776 show_flag_status();
1777 break;
1778#endif
1779#endif
1780#if ENABLE_FEATURE_LESS_BRACKETS
1781 case '{': case '(': case '[':
1782 match_right_bracket(keypress);
1783 break;
1784 case '}': case ')': case ']':
1785 match_left_bracket(keypress);
1786 break;
1787#endif
1788 case ':':
1789 colon_process();
1790 break;
1791 }
1792
1793 if (isdigit(keypress))
1794 number_process(keypress);
1795}
1796
1797static void sig_catcher(int sig)
1798{
1799 restore_tty();
1800 kill_myself_with_sig(sig);
1801}
1802
1803#if ENABLE_FEATURE_LESS_WINCH
1804static void sigwinch_handler(int sig UNUSED_PARAM)
1805{
1806 winch_counter++;
1807}
1808#endif
1809
1810int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1811int less_main(int argc, char **argv)
1812{
1813 char *tty_name;
1814 int tty_fd;
1815
1816 INIT_G();
1817
1818
1819
1820
1821
1822
1823
1824 getopt32(argv, "EMmN~IF"
1825 IF_FEATURE_LESS_TRUNCATE("S")
1826 IF_FEATURE_LESS_RAW("R")
1827 "sX"
1828 );
1829 argv += optind;
1830 num_files = argc - optind;
1831 files = argv;
1832
1833
1834
1835 if (ENABLE_FEATURE_LESS_ENV) {
1836 char *c = getenv("LESS");
1837 if (c) while (*c) switch (*c++) {
1838 case 'F':
1839 option_mask32 |= FLAG_F;
1840 break;
1841 case 'M':
1842 option_mask32 |= FLAG_M;
1843 break;
1844 case 'R':
1845 option_mask32 |= FLAG_R;
1846 break;
1847 case 'S':
1848 option_mask32 |= FLAG_S;
1849 break;
1850 default:
1851 break;
1852 }
1853 }
1854
1855
1856
1857 if (!isatty(STDOUT_FILENO))
1858 return bb_cat(argv);
1859
1860 if (!num_files) {
1861 if (isatty(STDIN_FILENO)) {
1862
1863 bb_show_usage();
1864 }
1865 } else {
1866 filename = xstrdup(files[0]);
1867 }
1868
1869 if (option_mask32 & FLAG_TILDE)
1870 empty_line_marker = "";
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880 tty_name = xmalloc_ttyname(STDOUT_FILENO);
1881 if (tty_name) {
1882 tty_fd = open(tty_name, O_RDONLY);
1883 free(tty_name);
1884 if (tty_fd < 0)
1885 goto try_ctty;
1886 } else {
1887
1888 try_ctty:
1889 tty_fd = open(CURRENT_TTY, O_RDONLY);
1890 if (tty_fd < 0) {
1891
1892 tty_fd = STDOUT_FILENO;
1893 }
1894 }
1895 G.kbd_fd_orig_flags = ndelay_on(tty_fd);
1896 kbd_fd = tty_fd;
1897
1898 get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT);
1899
1900 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(tty_fd, &width, &max_displayed_line);
1901
1902 if (width < 20 || max_displayed_line < 3)
1903 return bb_cat(argv);
1904 max_displayed_line -= 2;
1905
1906
1907 bb_signals(BB_FATAL_SIGS, sig_catcher);
1908#if ENABLE_FEATURE_LESS_WINCH
1909 signal(SIGWINCH, sigwinch_handler);
1910#endif
1911
1912 buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
1913 reinitialize();
1914 while (1) {
1915 int64_t keypress;
1916
1917#if ENABLE_FEATURE_LESS_WINCH
1918 while (WINCH_COUNTER) {
1919 again:
1920 winch_counter--;
1921 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
1922 IF_FEATURE_LESS_ASK_TERMINAL(got_size:)
1923
1924 if (width < 20)
1925 width = 20;
1926 if (max_displayed_line < 3)
1927 max_displayed_line = 3;
1928 max_displayed_line -= 2;
1929 free(buffer);
1930 buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
1931
1932
1933 if (WINCH_COUNTER)
1934 goto again;
1935 re_wrap();
1936 if (WINCH_COUNTER)
1937 goto again;
1938 buffer_fill_and_print();
1939
1940
1941 }
1942 keypress = less_getch(-1);
1943# if ENABLE_FEATURE_LESS_ASK_TERMINAL
1944 if ((int32_t)keypress == KEYCODE_CURSOR_POS) {
1945 uint32_t rc = (keypress >> 32);
1946 width = (rc & 0x7fff);
1947 max_displayed_line = ((rc >> 16) & 0x7fff);
1948 goto got_size;
1949 }
1950# endif
1951#else
1952 keypress = less_getch(-1);
1953#endif
1954 keypress_process(keypress);
1955 }
1956}
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164