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
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,19 @@ UPROGS=\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_wc_test\
$U/_zombie\
$U/_logstress\
$U/_forphan\
$U/_dorphan\

fs.img: mkfs/mkfs README.md $(UPROGS)
mkfs/mkfs fs.img README.md $(UPROGS)
#fs.img: mkfs/mkfs README.md $(UPROGS) tests/wc
# mkfs/mkfs fs.img README.md $(UPROGS) tests/wc

fs.img: mkfs/mkfs README.md $(UPROGS) \
empty.txt nlonly.txt hello.txt words.txt tabsnl.txt multi.txt longline.txt
mkfs/mkfs fs.img README.md $(UPROGS) \
empty.txt nlonly.txt hello.txt words.txt tabsnl.txt multi.txt longline.txt

-include kernel/*.d user/*.d

Expand Down
57 changes: 57 additions & 0 deletions docs/wc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# wc — Count lines, words, characters, bytes, and max line length

## Usage

The `wc` program prints statistics about its input files. By default, it displays **all counts** with labels. You can use flags to select which counts to display:

- `-l` : **Line count** (number of `\n` newlines)
- `-w` : **Word count** (sequences of non-whitespace: not space, tab, carriage return, newline, vertical tab)
- `-c` : **Character count** (counts every character, including newlines and tabs; ASCII expected)
- `-b` : **Byte count** (same as `-c` on xv6)
- `-L` : **Max line length** (length of the longest line, including its newline)

If multiple flags are given, each selected count is displayed with a label.

### Examples

```sh
$ wc README.md
Line count: 42
Word count: 278
Character count: 1536
Byte count: 1536
Max line length: 62
File name: README.md

$ wc -l README.md
Line count: 42
File name: README.md

$ wc -L longline.txt
Max line length: 301
File name: longline.txt
```

---

## Testing

An automated test runner `/wc_test` is provided. It validates each flag across a set of sample files bundled in the image. **The tests ensure correctness for lines, words, characters, bytes, and max line length.**

### To run tests:

1. Boot the OS and open the shell.
2. Run:
```
/wc_test
```

3. The test runner will output `PASS` or `FAIL` for each test case:
```
PASS: wc -l empty.txt
PASS: wc -w empty.txt
...
wc_test summary: 35 pass, 0 fail
```

4. If all tests pass, your implementation is
Empty file added empty.txt
Empty file.
1 change: 1 addition & 0 deletions hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
2 changes: 2 additions & 0 deletions kernel/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ extern uint64 sys_unlink(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
extern uint64 sys_wc(void);

// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
Expand All @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_wc] sys_wc,
};

void
Expand Down
1 change: 1 addition & 0 deletions kernel/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_wc 22
7 changes: 7 additions & 0 deletions kernel/sysproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,10 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}

uint64
sys_wc(void)
{
printf("Hello from your ne wc function!\n");
return 0;
}
1 change: 1 addition & 0 deletions longline.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
2 changes: 2 additions & 0 deletions multi.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
one two
three
1 change: 1 addition & 0 deletions nlonly.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions tabsnl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a b c

4 changes: 4 additions & 0 deletions user/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
hello world
hello again
this is a test file
hello world
9 changes: 9 additions & 0 deletions user/types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef unsigned long ulong;

typedef unsigned long long uint64;
typedef long long int64;
typedef unsigned int uint32;
typedef int int32;
1 change: 1 addition & 0 deletions user/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ int getpid(void);
char* sys_sbrk(int,int);
int pause(int);
int uptime(void);
int wc(void);

// ulib.c
int stat(const char*, struct stat*);
Expand Down
1 change: 1 addition & 0 deletions user/usys.pl
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ sub entry {
entry("sbrk");
entry("pause");
entry("uptime");
entry("wc");
119 changes: 84 additions & 35 deletions user/wc.c
Original file line number Diff line number Diff line change
@@ -1,54 +1,103 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#include "types.h"
#include "user.h"

char buf[512];
#define BUF_SIZE 512

void
wc(int fd, char *name)
{
int i, n;
int l, w, c, inword;
int is_word_char(char c) {
return !(c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
}

void wc_stats(int fd, char *name, int show_lines, int show_words, int show_chars, int show_bytes, int show_max_line) {
int lines = 0, words = 0, chars = 0, bytes = 0;
int max_line_len = 0, current_line_len = 0, inword = 0;
char buf[BUF_SIZE];
int n;

l = w = c = 0;
inword = 0;
while((n = read(fd, buf, sizeof(buf))) > 0){
for(i=0; i<n; i++){
c++;
if(buf[i] == '\n')
l++;
if(strchr(" \r\t\n\v", buf[i]))
while ((n = read(fd, buf, sizeof(buf))) > 0) {
bytes += n;
for (int i = 0; i < n; i++) {
char c = buf[i];
chars++;
current_line_len++;
if (c == '\n') {
lines++;
if (current_line_len > max_line_len)
max_line_len = current_line_len;
current_line_len = 0;
}
if (is_word_char(c)) {
if (!inword) {
words++;
inword = 1;
}
} else {
inword = 0;
else if(!inword){
w++;
inword = 1;
}
}
}
if(n < 0){
// Check for last line without newline
if (current_line_len > 0 && current_line_len > max_line_len)
max_line_len = current_line_len;

if (n < 0) {
printf("wc: read error\n");
exit(1);
}
printf("%d %d %d %s\n", l, w, c, name);
}

int
main(int argc, char *argv[])
{
int fd, i;
int flag_used = show_lines || show_words || show_chars || show_bytes || show_max_line;
if (flag_used) {
if (show_lines) {
printf("Line count: %d\t(Each '\\n' in the file is counted as a line)\n", lines);
}
if (show_words) {
printf("Word count: %d\t(A word is a sequence of non-whitespace characters)\n", words);
}
if (show_chars) {
printf("Character count: %d\t(Counts every character including newlines and tabs)\n", chars);
}
if (show_bytes) {
printf("Byte count: %d\t(Total bytes read from the file)\n", bytes);
}
if (show_max_line) {
printf("Max line length: %d\t(The longest line, including its newline, in characters)\n", max_line_len);
}
printf("File name: %s\n", name);
} else {
printf("Line count: %d\t(Each '\\n' in the file is counted as a line)\n", lines);
printf("Word count: %d\t(A word is a sequence of non-whitespace characters)\n", words);
printf("Character count: %d\t(Counts every character including newlines and tabs)\n", chars);
printf("Byte count: %d\t(Total bytes read from the file)\n", bytes);
printf("Max line length: %d\t(The longest line, including its newline, in characters)\n", max_line_len);
printf("File name: %s\n", name);
}
}

if(argc <= 1){
wc(0, "");
int main(int argc, char *argv[]) {
int show_lines = 0, show_words = 0, show_chars = 0, show_bytes = 0, show_max_line = 0, file_start = 1;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
for (int j = 1; argv[i][j]; j++) {
if (argv[i][j] == 'l') show_lines = 1;
else if (argv[i][j] == 'w') show_words = 1;
else if (argv[i][j] == 'c') show_chars = 1;
else if (argv[i][j] == 'b') show_bytes = 1;
else if (argv[i][j] == 'L') show_max_line = 1;
}
file_start++;
}
}
if (argc == file_start) {
wc_stats(0, "", show_lines, show_words, show_chars, show_bytes, show_max_line);
exit(0);
}

for(i = 1; i < argc; i++){
if((fd = open(argv[i], O_RDONLY)) < 0){
for (i = file_start; i < argc; i++) {
int fd = open(argv[i], 0);
if (fd < 0) {
printf("wc: cannot open %s\n", argv[i]);
exit(1);
continue;
}
wc(fd, argv[i]);
wc_stats(fd, argv[i], show_lines, show_words, show_chars, show_bytes, show_max_line);
close(fd);
}
exit(0);
Expand Down
Loading