Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h
.PRECIOUS: %.o

UPROGS=\
$U/_test\
$U/_cat\
$U/_echo\
$U/_forktest\
Expand Down
129 changes: 113 additions & 16 deletions user/cat.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,136 @@

char buf[512];

void
cat(int fd)
{
//flag variables
uint8 nflag = 0;
uint8 vflag = 0;
uint8 bflag = 0;
uint8 Eflag = 0;

/**
* @cat Display file content with various flags.
*
* This function reads the content of a file descriptor and displays it
* according to the specified flags. It supports line numbering, showing
* non-printing characters, numbering non-blank lines, and displaying
* end-of-line markers.
*
* @param fd The file descriptor to read from.
* @param line_numbering If non-zero, number all output lines.
* @param show_non_printing If non-zero, show non-printing characters in caret notation.
* @param number_non_blank If non-zero, number only non-blank lines.
* @param show_end_of_line If non-zero, display '$' at the end of each line.
*/

void cat(int fd, int line_numbering, int show_non_printing, int number_non_blank, int show_end_of_line) {
int n;
int line_number = 1;
int i;
int is_blank_line = 1;

while((n = read(fd, buf, sizeof(buf))) > 0) {
if (write(1, buf, n) != n) {
fprintf(2, "cat: write error\n");
exit(1);
//read the file content in chunks
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (i = 0; i < n; i++) {
//check if the current character is part of a blank line
if (buf[i] != '\n' && buf[i] != '\r' && buf[i] != '\t' && buf[i] != ' ') {
is_blank_line = 0;
}

//add line numbers if the -n or -b flag is set
if ((line_numbering || (number_non_blank && !is_blank_line)) && (i == 0 || buf[i-1] == '\n')) {
printf("%6d ", line_number++);
}

//convert non-printing characters to a visible format if the -v flag is set
//tested externally with test.c amd ran with OS as test (user program)
if (show_non_printing && (buf[i] < 32 || buf[i] == 127)) {
if (buf[i] == 127) {
printf("^?");
} else {
printf("^%c", buf[i] + 64);
}
} else {
//write the character to the output
if (write(1, &buf[i], 1) != 1) {
fprintf(2, "cat: write error\n");
exit(1);
}
}

//display $ at the end of each line if the -E flag is set
if (show_end_of_line && buf[i] == '\n') {
if (!number_non_blank || !is_blank_line) {
printf("$");
}
}

//reset blank line flag at the end of each line
if (buf[i] == '\n') {
is_blank_line = 1;
}
}
}
if(n < 0){

//handle read errors (unchanged)
if (n < 0) {
fprintf(2, "cat: read error\n");
exit(1);
}
}

int
main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
int fd, i;
int line_numbering = 0;
int show_non_printing = 0;
int number_non_blank = 0;
int show_end_of_line = 0;

//parse command-line arguments for flags
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
for (int j = 1; argv[i][j] != '\0'; j++) { //allows handling multiple flags in a single argument
switch (argv[i][j]) {
case 'n':
nflag = 1;
break;
case 'v':
vflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'E':
Eflag = 1;
break;
default:
fprintf(2, "Unrecognized flag: -%c\n", argv[i][j]);
exit(1);
}
}
} else {
break;
}
}

//set the flag variables
line_numbering = nflag;
show_non_printing = vflag;
number_non_blank = bflag;
show_end_of_line = Eflag;

if(argc <= 1){
cat(0);
//if no files are specified, read from standard input
if (i == argc) {
cat(0, line_numbering, show_non_printing, number_non_blank, show_end_of_line);
exit(0);
}

for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
//process each file specified in the command-line arguments (unchanged, except for, for and cat args)
for (; i < argc; i++) {
if ((fd = open(argv[i], 0)) < 0) {
fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1);
}
cat(fd);
cat(fd, line_numbering, show_non_printing, number_non_blank, show_end_of_line);
close(fd);
}
exit(0);
Expand Down
47 changes: 47 additions & 0 deletions user/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"


//function to convert non-printing characters to caret notation
//same function as the -v flag, to test
void print_with_caret_notation(const char *str) {
for (int i = 0; i < strlen(str); i++) {
if (str[i] < 32 || str[i] == 127) {
if (str[i] == 127) {
printf("^?");
} else {
printf("^%c", str[i] + 64);
}
} else {
printf("%c", str[i]);
}
}
printf("\n");
}

int main() {
//test cases with non-printing characters
const char *test1 = "Hello\001World"; // SOH (ASCII 1)
const char *test2 = "Tab\tCharacter"; // TAB (ASCII 9)
const char *test3 = "New\nLine"; // LF (ASCII 10)
const char *test4 = "Carriage\rReturn"; // CR (ASCII 13)
const char *test5 = "Delete\177Char"; // DEL (ASCII 127)

printf("Test 1: ");
print_with_caret_notation(test1);

printf("Test 2: ");
print_with_caret_notation(test2);

printf("Test 3: ");
print_with_caret_notation(test3);

printf("Test 4: ");
print_with_caret_notation(test4);

printf("Test 5: ");
print_with_caret_notation(test5);

return 0;
}
13 changes: 13 additions & 0 deletions user/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Hello, World!
This is a test file.

It contains some non-printing characters.
Here is a tab: <- tab character
Here is a newline:

Here is a carriage return:
<- carriage return character
Here is a bell character:<- bell character
Here is a delete character:<- delete character
Here is a form feed character:<- form feed character
Here is a vertical tab character:<- vertical tab character