source: mainline/uspace/app/bdsh/cmds/modules/cat/cat.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * Copyright (c) 2011, Martin Sucha
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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.
17 *
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.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <getopt.h>
33#include <str.h>
34#include <io/console.h>
35#include <io/color.h>
36#include <io/style.h>
37#include <io/keycode.h>
38#include <errno.h>
39#include <vfs/vfs.h>
40#include <assert.h>
41
42#include "config.h"
43#include "util.h"
44#include "errors.h"
45#include "entry.h"
46#include "cat.h"
47#include "cmds.h"
48
49static const char *cmdname = "cat";
50#define CAT_VERSION "0.0.1"
51#define CAT_DEFAULT_BUFLEN 1024
52#define CAT_FULL_FILE 0
53
54static const char *hexchars = "0123456789abcdef";
55
56static bool paging_enabled = false;
57static size_t chars_remaining = 0;
58static size_t lines_remaining = 0;
59static sysarg_t console_cols = 0;
60static sysarg_t console_rows = 0;
61static bool should_quit = false;
62static bool dash_represents_stdin = false;
63static unsigned int lineno = 0;
64static bool number = false;
65static bool last_char_was_newline = false;
66
67static console_ctrl_t *console = NULL;
68
69static 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' },
79 { 0, 0, 0, 0 }
80};
81
82/* Dispays help for cat in various levels */
83void help_cmd_cat(unsigned int level)
84{
85 if (level == HELP_SHORT) {
86 printf("`%s' shows the contents of files\n", cmdname);
87 } else {
88 help_cmd_cat(HELP_SHORT);
89 printf(
90 "Usage: %s [options] <file1> [file2] [...]\n"
91 "Options:\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",
102 cmdname, cmdname);
103 }
104
105 return;
106}
107
108static void waitprompt(void)
109{
110 console_set_pos(console, 0, console_rows-1);
111 console_set_color(console, COLOR_WHITE, COLOR_BLUE, 0);
112
113 printf("ENTER/SPACE/PAGE DOWN - next page, "
114 "ESC/Q - quit, C - continue unpaged");
115 fflush(stdout);
116
117 console_set_style(console, STYLE_NORMAL);
118}
119
120static void waitkey(void)
121{
122 cons_event_t ev;
123 kbd_event_t *kev;
124
125 while (true) {
126 if (!console_get_event(console, &ev)) {
127 return;
128 }
129 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
130 kev = &ev.ev.key;
131
132 if (kev->key == KC_ESCAPE || kev->key == KC_Q) {
133 should_quit = true;
134 return;
135 }
136 if (kev->key == KC_C) {
137 paging_enabled = false;
138 return;
139 }
140 if (kev->key == KC_ENTER || kev->key == KC_SPACE ||
141 kev->key == KC_PAGE_DOWN) {
142 return;
143 }
144 }
145 }
146 assert(false);
147}
148
149static void newpage(void)
150{
151 console_clear(console);
152 chars_remaining = console_cols;
153 lines_remaining = console_rows - 1;
154}
155
156static void paged_char(wchar_t c)
157{
158 if (last_char_was_newline && number) {
159 lineno++;
160 printf("%6u ", lineno);
161 }
162 putchar(c);
163 last_char_was_newline = c == '\n';
164 if (paging_enabled) {
165 chars_remaining--;
166 if (c == '\n' || chars_remaining == 0) {
167 chars_remaining = console_cols;
168 lines_remaining--;
169 }
170 if (lines_remaining == 0) {
171 fflush(stdout);
172 waitprompt();
173 waitkey();
174 newpage();
175 }
176 }
177}
178
179static unsigned int cat_file(const char *fname, size_t blen, bool hex,
180 off64_t head, off64_t tail, bool tail_first)
181{
182 int fd, count = 0, reads = 0;
183 size_t bytes;
184 char *buff = NULL;
185 size_t i;
186 size_t offset = 0, copied_bytes = 0;
187 off64_t file_size = 0, length = 0;
188 aoff64_t pos = 0;
189 errno_t rc;
190
191 bool reading_stdin = dash_represents_stdin && (str_cmp(fname, "-") == 0);
192
193 if (reading_stdin) {
194 fd = fileno(stdin);
195 /* Allow storing the whole UTF-8 character. */
196 blen = STR_BOUNDS(1);
197 } else {
198 errno_t rc = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ, &fd);
199 if (rc != EOK) {
200 fd = -1;
201 }
202 }
203
204 if (fd < 0) {
205 printf("Unable to open %s\n", fname);
206 return 1;
207 }
208
209 if (NULL == (buff = (char *) malloc(blen + 1))) {
210 vfs_put(fd);
211 printf("Unable to allocate enough memory to read %s\n",
212 fname);
213 return 1;
214 }
215
216 if (tail != CAT_FULL_FILE) {
217 vfs_stat_t st;
218
219 if (vfs_stat(fd, &st) != EOK) {
220 vfs_put(fd);
221 free(buff);
222 printf("Unable to vfs_stat %d\n", fd);
223 return 1;
224 }
225 file_size = st.size;
226 if (head == CAT_FULL_FILE) {
227 head = file_size;
228 length = tail;
229 } else if (tail_first) {
230 length = head;
231 } else {
232 if (tail > head)
233 tail = head;
234 length = tail;
235 }
236
237 if (tail_first) {
238 pos = (tail >= file_size) ? 0 : (file_size - tail);
239 } else {
240 pos = ((head - tail) >= file_size) ? 0 : (head - tail);
241 }
242 } else
243 length = head;
244
245 do {
246 size_t bytes_to_read;
247 if (reading_stdin) {
248 bytes_to_read = 1;
249 } else {
250 if ((length != CAT_FULL_FILE) &&
251 (length - (off64_t)count <= (off64_t)(blen - copied_bytes))) {
252 bytes_to_read = (size_t) (length - count);
253 } else {
254 bytes_to_read = blen - copied_bytes;
255 }
256 }
257
258 rc = vfs_read(fd, &pos, buff + copied_bytes, bytes_to_read,
259 &bytes);
260 copied_bytes = 0;
261
262 if (rc == EOK && bytes > 0) {
263 buff[bytes] = '\0';
264 offset = 0;
265 for (i = 0; i < bytes && !should_quit; i++) {
266 if (hex) {
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' : ' ');
270 }
271 else {
272 wchar_t c = str_decode(buff, &offset, bytes);
273 if (c == 0) {
274 /* Reached end of string */
275 break;
276 } else if (c == U_SPECIAL && offset + 2 >= (size_t)bytes) {
277 /* If an extended character is cut off due to the size of the buffer,
278 we will copy it over to the next buffer so it can be read correctly. */
279 copied_bytes = bytes - offset + 1;
280 memcpy(buff, buff + offset - 1, copied_bytes);
281 break;
282 }
283 paged_char(c);
284 }
285
286 }
287 count += bytes;
288 reads++;
289 }
290
291 if (reading_stdin)
292 fflush(stdout);
293 } while (rc == EOK && bytes > 0 && !should_quit && (count < length || length == CAT_FULL_FILE));
294
295 vfs_put(fd);
296 if (rc != EOK) {
297 printf("Error reading %s\n", fname);
298 free(buff);
299 return 1;
300 }
301
302 free(buff);
303
304 return 0;
305}
306
307/* Main entry point for cat, accepts an array of arguments */
308int cmd_cat(char **argv)
309{
310 unsigned int argc, i, ret = 0;
311 size_t buffer = 0;
312 int c, opt_ind;
313 aoff64_t head = CAT_FULL_FILE, tail = CAT_FULL_FILE;
314 bool hex = false;
315 bool more = false;
316 bool tailFirst = false;
317 sysarg_t rows, cols;
318 errno_t rc;
319
320 /*
321 * reset global state
322 * TODO: move to structure?
323 */
324 paging_enabled = false;
325 chars_remaining = 0;
326 lines_remaining = 0;
327 console_cols = 0;
328 console_rows = 0;
329 should_quit = false;
330 dash_represents_stdin = false;
331 console = console_init(stdin, stdout);
332 number = false;
333 lineno = 0;
334 /* This enables printing of the first number. */
335 last_char_was_newline = true;
336
337
338 argc = cli_count_args(argv);
339
340 for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
341 c = getopt_long(argc, argv, "xhvmH:t:b:sn", long_options, &opt_ind);
342 switch (c) {
343 case 'h':
344 help_cmd_cat(HELP_LONG);
345 return CMD_SUCCESS;
346 case 'v':
347 printf("%s\n", CAT_VERSION);
348 return CMD_SUCCESS;
349 case 'H':
350 if (!optarg || str_uint64_t(optarg, NULL, 10, false, &head) != EOK ) {
351 puts("Invalid head size\n");
352 return CMD_FAILURE;
353 }
354 break;
355 case 't':
356 if (!optarg || str_uint64_t(optarg, NULL, 10, false, &tail) != EOK ) {
357 puts("Invalid tail size\n");
358 return CMD_FAILURE;
359 }
360 if (head == CAT_FULL_FILE)
361 tailFirst = true;
362 break;
363 case 'b':
364 if (!optarg || str_size_t(optarg, NULL, 10, false, &buffer) != EOK ) {
365 puts("Invalid buffer size\n");
366 return CMD_FAILURE;
367 }
368 break;
369 case 'm':
370 more = true;
371 break;
372 case 'x':
373 hex = true;
374 break;
375 case 's':
376 dash_represents_stdin = true;
377 break;
378 case 'n':
379 number = true;
380 break;
381 }
382 }
383
384 argc -= optind;
385
386 if (argc < 1) {
387 printf("%s - incorrect number of arguments. Try `%s --help'\n",
388 cmdname, cmdname);
389 return CMD_FAILURE;
390 }
391
392 if (buffer < 4)
393 buffer = CAT_DEFAULT_BUFLEN;
394
395 if (more) {
396 rc = console_get_size(console, &cols, &rows);
397 if (rc != EOK) {
398 printf("%s - cannot get console size\n", cmdname);
399 return CMD_FAILURE;
400 }
401 console_cols = cols;
402 console_rows = rows;
403 paging_enabled = true;
404 newpage();
405 }
406
407 for (i = optind; argv[i] != NULL && !should_quit; i++)
408 ret += cat_file(argv[i], buffer, hex, head, tail, tailFirst);
409
410 if (ret)
411 return CMD_FAILURE;
412 else
413 return CMD_SUCCESS;
414}
415
Note: See TracBrowser for help on using the repository browser.