2 * Copyright (c) 2008 Tim Post
3 * Copyright (c) 2011, Martin Sucha
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <io/console.h>
37 #include <io/keycode.h>
49 static const char *cmdname
= "cat";
50 #define CAT_VERSION "0.0.1"
51 #define CAT_DEFAULT_BUFLEN 1024
52 #define CAT_FULL_FILE 0
54 static const char *hexchars
= "0123456789abcdef";
56 static bool paging_enabled
= false;
57 static size_t chars_remaining
= 0;
58 static size_t lines_remaining
= 0;
59 static sysarg_t console_cols
= 0;
60 static sysarg_t console_rows
= 0;
61 static bool should_quit
= false;
62 static bool dash_represents_stdin
= false;
63 static unsigned int lineno
= 0;
64 static bool number
= false;
65 static bool last_char_was_newline
= false;
67 static console_ctrl_t
*console
= NULL
;
69 static struct option
const long_options
[] = {
70 { "help", no_argument
, 0, 'h' },
71 { "version", no_argument
, 0, 'v' },
72 { "head", required_argument
, 0, 'H' },
73 { "tail", required_argument
, 0, 't' },
74 { "buffer", required_argument
, 0, 'b' },
75 { "more", no_argument
, 0, 'm' },
76 { "hex", no_argument
, 0, 'x' },
77 { "stdin", no_argument
, 0, 's' },
78 { "number", no_argument
, 0, 'n' },
82 /* Dispays help for cat in various levels */
83 void help_cmd_cat(unsigned int level
)
85 if (level
== HELP_SHORT
) {
86 printf("`%s' shows the contents of files\n", cmdname
);
88 help_cmd_cat(HELP_SHORT
);
90 "Usage: %s [options] <file1> [file2] [...]\n"
92 " -h, --help A short option summary\n"
93 " -v, --version Print version information and exit\n"
94 " -H, --head ## Print only the first ## bytes\n"
95 " -t, --tail ## Print only the last ## bytes\n"
96 " -b, --buffer ## Set the read buffer size to ##\n"
97 " -m, --more Pause after each screen full\n"
98 " -x, --hex Print bytes as hex values\n"
99 " -s, --stdin Treat `-' in file list as standard input\n"
100 " -n, --number Number all output lines\n"
101 "Currently, %s is under development, some options don't work.\n",
108 static void waitprompt(void)
110 console_set_pos(console
, 0, console_rows
- 1);
111 console_set_color(console
, COLOR_WHITE
, COLOR_BLUE
, 0);
113 printf("ENTER/SPACE/PAGE DOWN - next page, "
114 "ESC/Q - quit, C - continue unpaged");
117 console_set_style(console
, STYLE_NORMAL
);
120 static void waitkey(void)
126 if (!console_get_event(console
, &ev
)) {
129 if (ev
.type
== CEV_KEY
&& ev
.ev
.key
.type
== KEY_PRESS
) {
132 if (kev
->key
== KC_ESCAPE
|| kev
->key
== KC_Q
) {
136 if (kev
->key
== KC_C
) {
137 paging_enabled
= false;
140 if (kev
->key
== KC_ENTER
|| kev
->key
== KC_SPACE
||
141 kev
->key
== KC_PAGE_DOWN
) {
149 static void newpage(void)
151 console_clear(console
);
152 chars_remaining
= console_cols
;
153 lines_remaining
= console_rows
- 1;
156 static void paged_char(wchar_t c
)
158 if (last_char_was_newline
&& number
) {
160 printf("%6u ", lineno
);
163 last_char_was_newline
= c
== '\n';
164 if (paging_enabled
) {
166 if (c
== '\n' || chars_remaining
== 0) {
167 chars_remaining
= console_cols
;
170 if (lines_remaining
== 0) {
179 static unsigned int cat_file(const char *fname
, size_t blen
, bool hex
,
180 off64_t head
, off64_t tail
, bool tail_first
)
182 int fd
, count
= 0, reads
= 0;
186 size_t offset
= 0, copied_bytes
= 0;
187 off64_t file_size
= 0, length
= 0;
191 bool reading_stdin
= dash_represents_stdin
&& (str_cmp(fname
, "-") == 0);
195 /* Allow storing the whole UTF-8 character. */
196 blen
= STR_BOUNDS(1);
198 errno_t rc
= vfs_lookup_open(fname
, WALK_REGULAR
, MODE_READ
, &fd
);
205 printf("Unable to open %s\n", fname
);
209 if (NULL
== (buff
= (char *) malloc(blen
+ 1))) {
211 printf("Unable to allocate enough memory to read %s\n",
216 if (tail
!= CAT_FULL_FILE
) {
219 if (vfs_stat(fd
, &st
) != EOK
) {
222 printf("Unable to vfs_stat %d\n", fd
);
226 if (head
== CAT_FULL_FILE
) {
229 } else if (tail_first
) {
238 pos
= (tail
>= file_size
) ? 0 : (file_size
- tail
);
240 pos
= ((head
- tail
) >= file_size
) ? 0 : (head
- tail
);
246 size_t bytes_to_read
;
250 if ((length
!= CAT_FULL_FILE
) &&
251 (length
- (off64_t
)count
<= (off64_t
)(blen
- copied_bytes
))) {
252 bytes_to_read
= (size_t) (length
- count
);
254 bytes_to_read
= blen
- copied_bytes
;
258 rc
= vfs_read(fd
, &pos
, buff
+ copied_bytes
, bytes_to_read
,
262 if (rc
== EOK
&& bytes
> 0) {
265 for (i
= 0; i
< bytes
&& !should_quit
; i
++) {
267 paged_char(hexchars
[((uint8_t)buff
[i
]) / 16]);
268 paged_char(hexchars
[((uint8_t)buff
[i
]) % 16]);
269 paged_char(((count
+ i
+ 1) & 0xf) == 0 ? '\n' : ' ');
271 wchar_t c
= str_decode(buff
, &offset
, bytes
);
273 /* Reached end of string */
275 } else if (c
== U_SPECIAL
&& offset
+ 2 >= (size_t)bytes
) {
276 /* If an extended character is cut off due to the size of the buffer,
277 we will copy it over to the next buffer so it can be read correctly. */
278 copied_bytes
= bytes
- offset
+ 1;
279 memcpy(buff
, buff
+ offset
- 1, copied_bytes
);
292 } while (rc
== EOK
&& bytes
> 0 && !should_quit
&& (count
< length
|| length
== CAT_FULL_FILE
));
296 printf("Error reading %s\n", fname
);
306 /* Main entry point for cat, accepts an array of arguments */
307 int cmd_cat(char **argv
)
309 unsigned int argc
, i
, ret
= 0;
312 aoff64_t head
= CAT_FULL_FILE
, tail
= CAT_FULL_FILE
;
315 bool tailFirst
= false;
321 * TODO: move to structure?
323 paging_enabled
= false;
329 dash_represents_stdin
= false;
330 console
= console_init(stdin
, stdout
);
333 /* This enables printing of the first number. */
334 last_char_was_newline
= true;
337 argc
= cli_count_args(argv
);
345 c
= getopt_long(argc
, argv
, "xhvmH:t:b:sn", long_options
, &opt_ind
);
348 help_cmd_cat(HELP_LONG
);
351 printf("%s\n", CAT_VERSION
);
354 if (!optarg
|| str_uint64_t(optarg
, NULL
, 10, false, &head
) != EOK
) {
355 puts("Invalid head size");
360 if (!optarg
|| str_uint64_t(optarg
, NULL
, 10, false, &tail
) != EOK
) {
361 puts("Invalid tail size");
364 if (head
== CAT_FULL_FILE
)
368 if (!optarg
|| str_size_t(optarg
, NULL
, 10, false, &buffer
) != EOK
) {
369 puts("Invalid buffer size");
380 dash_represents_stdin
= true;
391 printf("%s - incorrect number of arguments. Try `%s --help'\n",
397 buffer
= CAT_DEFAULT_BUFLEN
;
400 rc
= console_get_size(console
, &cols
, &rows
);
402 printf("%s - cannot get console size\n", cmdname
);
407 paging_enabled
= true;
411 for (i
= optind
; argv
[i
] != NULL
&& !should_quit
; i
++)
412 ret
+= cat_file(argv
[i
], buffer
, hex
, head
, tail
, tailFirst
);