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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
/**
* file: cat.c
* command: cat
* author: Tyler Murphy
*/
#include "command.h"
#include "lslib.h"
#include <ctype.h>
#include <stdlib.h>
/**
* Flags that are to be used with cat
*/
static struct {
bool number_lines; /* if to number the lines */
bool number_non_empty; /* if to number empty lines */
bool change_non_print; /* if to change non printable characters to be printable */
bool change_tabs; /* if to change tabs to ^I */
bool end_lines_dollar; /* if to print a $ at the end of lines */
} flags;
/**
* Help function for cat
*/
static void help(void) {
printf("Usage: cat [-nbvteA] [FILE]...\n\n");
printf("Print FILEs to stdout\n\n");
printf("\t-n Number output lines\n");
printf("\t-b Number nonempty lines\n");
printf("\t-v Show nonprinting characters as ^x or M-x\n");
printf("\t-t ...and tabs as ^I\n");
printf("\t-e ...and end lines with $\n");
printf("\t-A Same as -vte\n");
}
/**
* Give a file pointer, cat all its contents
* @param file the file to cat
*/
static void cat_file(FILE* file) {
char c; /* current character read */
size_t read; /* amount read */
/* default arguments */
size_t line = 1; /* what line we are on, 1 indexed */
bool empty = true; /* if the current line is empty (nothing printed) */
bool newline = true; /* if we are on a new line */
/* read file a character at a time */
while ((read = fread(&c, 1, 1, file)) != 0) {
/* if its a new line, update the new line pointer */
if (c == '\n') {
if (empty && flags.number_lines) { /* print line number if set to */
printf("\t%ld ", line);
}
if (flags.end_lines_dollar) { /* pint $ if set to */
printf("$");
}
line++;
newline = true; /* tell that the new line number is set to be printed */
empty = true; /* set line to empty */
goto print;
} else {
empty = false;
}
/* skip next check if not newline */
if (!newline) {
goto print;
}
/* if on a new line and is a valid char, print line number */
if (!empty && (flags.number_non_empty || flags.number_lines)) {
printf("\t%ld ", line);
newline = false;
}
print:
/* print character */
if (!flags.change_non_print || printable_char(c)) {
if (flags.change_tabs && c == '\t') {
fwrite("^I", 1, 2, stdout); /* print ^I instead of tab */
} else {
fwrite(&c, 1, 1, stdout); /* print character to stdout */
}
} else { /* if set to change non print, fix char code */
c |= '@';
fwrite(&c, 1, 1, stdout);
}
}
}
/**
* Takes in each argument that has a single - and parses it
* @param c the character after the -
* @param next the next argument in argv that hasnt been parsed
* @reutrn if the next arg was used or if the arg was invalid
*/
static int short_arg(char c, char* next) {
UNUSED(next);
switch (c) {
case 'n':
flags.number_lines = true;
break;
case 'b':
flags.number_non_empty = true;
break;
case 'v':
flags.change_non_print = true;
break;
case 't':
flags.change_non_print = true;
flags.change_tabs = true;
break;
case 'e':
flags.change_non_print = true;
flags.end_lines_dollar = true;
break;
case 'A':
flags.change_non_print = true;
flags.change_tabs = true;
flags.end_lines_dollar = true;
break;
default:
return ARG_INVALID;
}
return ARG_UNUSED;
}
/**
* Output a files contents
*/
COMMAND(cat_main) {
int start;
int arg_len;
int i;
/* set flag defaults */
flags.number_lines = false;
flags.number_non_empty = false;
flags.change_non_print = false;
flags.change_tabs = false;
flags.end_lines_dollar = false;
/* parse user arguments */
start = parse_args(argc, argv, help, short_arg, NULL);
/* get the number of arguments to cat */
arg_len = argc - start;
/* if not read from stdin */
if (arg_len < 1) {
cat_file(stdin);
return EXIT_SUCCESS;
}
/* foreach file, try to open it, and then cat it */
for (i = start; i < argc; i++) {
FILE* in = get_file(argv[i], "r"); /* open file as read */
cat_file(in); /* print it out */
if (in != stdin)
fclose(in);
}
return EXIT_SUCCESS; /* success */
}
|