From 4adbd0c075c6abb6df66908554868329956c98c3 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Tue, 23 Sep 2025 10:50:54 -0700 Subject: [PATCH 01/24] Feature: Adding initial intergration of join as a system call --- Makefile | 1 + kernel/syscall.c | 2 ++ kernel/syscall.h | 1 + user/join.c | 32 ++++++++++++++++++++++++++++++++ user/user.h | 1 + user/usys.pl | 1 + 6 files changed, 38 insertions(+) create mode 100644 user/join.c diff --git a/Makefile b/Makefile index cf031b0..34a9540 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,7 @@ UPROGS=\ $U/_logstress\ $U/_forphan\ $U/_dorphan\ + $U/_join\ fs.img: mkfs/mkfs README.md $(UPROGS) mkfs/mkfs fs.img README.md $(UPROGS) diff --git a/kernel/syscall.c b/kernel/syscall.c index 076d965..4c431a6 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -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_join(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_join] sys_join, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index 3dd926d..79645b7 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,4 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_join 22 diff --git a/user/join.c b/user/join.c new file mode 100644 index 0000000..0dbb4ff --- /dev/null +++ b/user/join.c @@ -0,0 +1,32 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main(int argc, char *argv[]){ + // ensure correct usage of command line args + if (argc != 3){ + printf("Usage: join file1.txt file2.txt\n"); + exit(1); + } + + printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); + + + // initiate data structure for file content + // set two arrays of 50 lines and 256 chars each + char file1_lines[50][256]; + char file2_lines[50][256]; + + + // read both files + printf("Reading first file..."); + int count1 = read_file_lines(argv[1], file1_lines, 50); + + printf("Reading second file..."); + int count2 = read_file_lines(argv[2], file2_lines, 50); + + // join both files + printf("joining both files"); + join_files(file1_lines, count1, file2_lines, count2); + + exit(0); +} diff --git a/user/user.h b/user/user.h index ac84de9..5dcac5a 100644 --- a/user/user.h +++ b/user/user.h @@ -24,6 +24,7 @@ int getpid(void); char* sys_sbrk(int,int); int pause(int); int uptime(void); +void join(void); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index c5d4c3a..58f406f 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,3 +42,4 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); +entry("join"); From 0fc2d869d97310c82b0dcf984448e11e49df4c15 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Tue, 23 Sep 2025 11:31:11 -0700 Subject: [PATCH 02/24] Feature: Adding methods to join syscall --- kernel/sysproc.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++ user/join.c | 6 +- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3044d00..2dc74c7 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -105,3 +105,166 @@ sys_uptime(void) release(&tickslock); return xticks; } + +void +sys_join(void) +{ + // // Hector's functions + // void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2); + // void print_joined_line(char* field, char* rest1, char* rest2); + // char* get_rest_of_line(char* line); + // + // + // // Demetrius's functions + // char* get_first_field(char* line); + // int read_file_lines(char* filename, char lines[][256], int count2); + + /** + * Extracts the first field (word) from a line + * Returns a pointer to the first field, or NULL if line is empty + */ + char* get_first_field(char* line) { + if (line == NULL || *line == '\0') { + return NULL; + } + + // Skip leading whitespace + while (*line == ' ' || *line == '\t') { + line++; + } + + // If line is empty after skipping whitespace + if (*line == '\0') { + return NULL; + } + + return line; + } + + /** + * Function to get the rest of the line so it is everything + * after the first word + */ + char* get_rest_of_line(char* line){ + + // return empty if line is not valid + if(line == NULL || *line == '\0'){ + return ""; + } + + char* ptr = line; + // skip leading whitespace + while(*ptr == ' ' || *ptr == '\t'){ + ptr++; + } + // skip first field until reached whitespace or end of line + while (*ptr != '\0' && *ptr != ' ' && *ptr != '\t' ){ + ptr++; + } + // skip whitespace between first field and rest + while(*ptr == ' ' || *ptr == '\t'){ + ptr++; + } + return ptr; + } + + /** + * Reads lines from a file into the provided array + * Returns the number of lines successfully read + */ + int read_file_lines(char* filename, char lines[][256], int max_count) { + int fd = open(filename, 0); // 0 = O_RDONLY + if (fd < 0) { + printf("Error: Cannot open file '%s'\n", filename); + return 0; + } + + int line_count = 0; + char buffer[256]; + int pos = 0; + char c; + + while (line_count < max_count && read(fd, &c, 1) > 0) { + if (c == '\n') { + // End of line found + buffer[pos] = '\0'; + + // Copy the line to the array + int i = 0; + while (i < 255 && buffer[i] != '\0') { + lines[line_count][i] = buffer[i]; + i++; + } + lines[line_count][i] = '\0'; + + line_count++; + pos = 0; // Reset position for next line + } else if (pos < 255) { + // Add character to buffer + buffer[pos] = c; + pos++; + } + } + + // Handle the last line if file doesn't end with newline + if (pos > 0 && line_count < max_count) { + buffer[pos] = '\0'; + int i = 0; + while (i < 255 && buffer[i] != '\0') { + lines[line_count][i] = buffer[i]; + i++; + } + lines[line_count][i] = '\0'; + line_count++; + } + + close(fd); + return line_count; + } + /** + * function prints joined line + * "first word" "rest line file 1" "rest line file 2" + */ + void print_joined_line(char* field, char* rest1, char* rest2){ + printf("%s %s %s\n", field, rest1, rest2); + } + + /** + * Compares lines from both files and looks for matches in the + * first field + * If the fields match then print joined line + */ + void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2){ + + // iterate through each line from file 1 + for (int i =0; i< count1; i++){ + char* field1 = get_first_field(field1_lines[i]); // reads full line but only gets first word to check for match + // skip is field is empty + if(field1 == 0){ + continue; + } + + + // iterate through lines in file two + for (int j =0; j < count2; j++){ + char* field2 = get_first_field(field2_lines[j]); // read full line but only gets first word + // skip if field is empty + if(field2 == 0){ + continue; + } + + // check if both words are equal returns 0 if true + if(strcmp(field1, field2) == 0){ + // get rest of lines for both files from where we got field + char* rest1 = get_rest_of_line(file1_lines[i]); + char* rest2 = get_rest_of_line(file2_lines[j]); + + print_joined_line(field1, rest1, rest2); + + + } + } + } + printf("join_files() completed"); + } +} diff --git a/user/join.c b/user/join.c index 0dbb4ff..9709db8 100644 --- a/user/join.c +++ b/user/join.c @@ -19,14 +19,14 @@ int main(int argc, char *argv[]){ // read both files printf("Reading first file..."); - int count1 = read_file_lines(argv[1], file1_lines, 50); + int count1 = join.read_file_lines(argv[1], file1_lines, 50); printf("Reading second file..."); - int count2 = read_file_lines(argv[2], file2_lines, 50); + int count2 = join.read_file_lines(argv[2], file2_lines, 50); // join both files printf("joining both files"); - join_files(file1_lines, count1, file2_lines, count2); + join.join_files(file1_lines, count1, file2_lines, count2); exit(0); } From 12bce47b7a4dfb451412006ea813f4921f89c47b Mon Sep 17 00:00:00 2001 From: HECTOR NUNEZ Date: Tue, 23 Sep 2025 18:16:52 -0700 Subject: [PATCH 03/24] bug fixes to join implementation, added to user dir only --- kernel/syscall.c | 3 +- kernel/syscall.h | 1 - kernel/sysproc.c | 162 ------------------------------- user/join.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--- user/user.h | 1 - user/usys.pl | 1 - 6 files changed, 238 insertions(+), 178 deletions(-) diff --git a/kernel/syscall.c b/kernel/syscall.c index 4c431a6..6dc5deb 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,7 +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_join(void); + // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -127,7 +127,6 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, -[SYS_join] sys_join, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index 79645b7..3dd926d 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,4 +20,3 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 -#define SYS_join 22 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 2dc74c7..6cc0bc6 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -106,165 +106,3 @@ sys_uptime(void) return xticks; } -void -sys_join(void) -{ - // // Hector's functions - // void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2); - // void print_joined_line(char* field, char* rest1, char* rest2); - // char* get_rest_of_line(char* line); - // - // - // // Demetrius's functions - // char* get_first_field(char* line); - // int read_file_lines(char* filename, char lines[][256], int count2); - - /** - * Extracts the first field (word) from a line - * Returns a pointer to the first field, or NULL if line is empty - */ - char* get_first_field(char* line) { - if (line == NULL || *line == '\0') { - return NULL; - } - - // Skip leading whitespace - while (*line == ' ' || *line == '\t') { - line++; - } - - // If line is empty after skipping whitespace - if (*line == '\0') { - return NULL; - } - - return line; - } - - /** - * Function to get the rest of the line so it is everything - * after the first word - */ - char* get_rest_of_line(char* line){ - - // return empty if line is not valid - if(line == NULL || *line == '\0'){ - return ""; - } - - char* ptr = line; - // skip leading whitespace - while(*ptr == ' ' || *ptr == '\t'){ - ptr++; - } - // skip first field until reached whitespace or end of line - while (*ptr != '\0' && *ptr != ' ' && *ptr != '\t' ){ - ptr++; - } - // skip whitespace between first field and rest - while(*ptr == ' ' || *ptr == '\t'){ - ptr++; - } - return ptr; - } - - /** - * Reads lines from a file into the provided array - * Returns the number of lines successfully read - */ - int read_file_lines(char* filename, char lines[][256], int max_count) { - int fd = open(filename, 0); // 0 = O_RDONLY - if (fd < 0) { - printf("Error: Cannot open file '%s'\n", filename); - return 0; - } - - int line_count = 0; - char buffer[256]; - int pos = 0; - char c; - - while (line_count < max_count && read(fd, &c, 1) > 0) { - if (c == '\n') { - // End of line found - buffer[pos] = '\0'; - - // Copy the line to the array - int i = 0; - while (i < 255 && buffer[i] != '\0') { - lines[line_count][i] = buffer[i]; - i++; - } - lines[line_count][i] = '\0'; - - line_count++; - pos = 0; // Reset position for next line - } else if (pos < 255) { - // Add character to buffer - buffer[pos] = c; - pos++; - } - } - - // Handle the last line if file doesn't end with newline - if (pos > 0 && line_count < max_count) { - buffer[pos] = '\0'; - int i = 0; - while (i < 255 && buffer[i] != '\0') { - lines[line_count][i] = buffer[i]; - i++; - } - lines[line_count][i] = '\0'; - line_count++; - } - - close(fd); - return line_count; - } - /** - * function prints joined line - * "first word" "rest line file 1" "rest line file 2" - */ - void print_joined_line(char* field, char* rest1, char* rest2){ - printf("%s %s %s\n", field, rest1, rest2); - } - - /** - * Compares lines from both files and looks for matches in the - * first field - * If the fields match then print joined line - */ - void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2){ - - // iterate through each line from file 1 - for (int i =0; i< count1; i++){ - char* field1 = get_first_field(field1_lines[i]); // reads full line but only gets first word to check for match - // skip is field is empty - if(field1 == 0){ - continue; - } - - - // iterate through lines in file two - for (int j =0; j < count2; j++){ - char* field2 = get_first_field(field2_lines[j]); // read full line but only gets first word - // skip if field is empty - if(field2 == 0){ - continue; - } - - // check if both words are equal returns 0 if true - if(strcmp(field1, field2) == 0){ - // get rest of lines for both files from where we got field - char* rest1 = get_rest_of_line(file1_lines[i]); - char* rest2 = get_rest_of_line(file2_lines[j]); - - print_joined_line(field1, rest1, rest2); - - - } - } - } - printf("join_files() completed"); - } -} diff --git a/user/join.c b/user/join.c index 9709db8..847d3e6 100644 --- a/user/join.c +++ b/user/join.c @@ -1,32 +1,258 @@ #include "kernel/types.h" #include "user/user.h" +#include "kernel/stat.h" + + + // // Hector's functions + //void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2); + //void print_joined_line(char* field, char* rest1, char* rest2); + //char* get_rest_of_line(char* line); + // + // + // // Demetrius's functions + //char* get_first_field(char* line); + // int read_file_lines(char* filename, char lines[][256], int count2); + + // helper functions + //compare_strings(char* str1, char* str2) + //terminate_first_field(char* line) + /** + * String comparison iterates through each char in both + * strings to see if they match if non return non-zero + */ + + static char file1_lines[50][256]; + static char file2_lines[50][256]; + + int compare_strings(char* str1, char* str2){ + while (*str1 && *str2){ + if (*str1 != *str2){ + return *str1 - *str2; + } + str1++; + str2++; + } + return *str1 - *str2; + } + + /** + * Extracts the first field (word) from a line + * Returns a pointer to the first field, or NULL if line is empty + */ + + char* get_first_field(char* line) { + if (line == 0 || *line == '\0') { + return 0; + } + + // Skip leading whitespace + while (*line == ' ' || *line == '\t') { + line++; + } + + // If line is empty after skipping whitespace + if (*line == '\0') { + return 0; + } + + return line; + } + + /** + * Function to get the rest of the line so it is everything + * after the first word + */ + char* get_rest_of_line(char* line){ + + // return empty if line is not valid + if(line == 0 || *line == '\0'){ + return ""; + } + + char* ptr = line; + // skip leading whitespace + while(*ptr == ' ' || *ptr == '\t'){ + ptr++; + } + // skip first field until reached whitespace or end of line + while (*ptr != '\0' && *ptr != ' ' && *ptr != '\t' ){ + ptr++; + } + // skip whitespace between first field and rest + while(*ptr == ' ' || *ptr == '\t'){ + ptr++; + } + return ptr; + } + + void terminate_first_field(char* line){ + char* ptr = line; + + // skip whitespace + while (*ptr == ' ' || *ptr == '\t'){ + ptr++; + } + + // get the end of the first word + while (*ptr != '\0' && *ptr != ' ' && *ptr != '\n' && *ptr != '\t'){ + ptr++; + } + // terminate first word + if(*ptr != '\0'){ + *ptr = '\0'; + } + } + + /** + * Reads lines from a file into the provided array + * Returns the number of lines successfully read + */ + int read_file_lines(char* filename, char lines[][256], int max_count) { + int fd = open(filename, 0); // 0 = O_RDONLY + if (fd < 0) { + printf("Error: Cannot open file '%s'\n", filename); + return 0; + } + + int line_count = 0; + char buffer[256]; + int pos = 0; + char c; + + while (line_count < max_count && read(fd, &c, 1) > 0) { + if (c == '\n') { + // End of line found + buffer[pos] = '\0'; + + // Copy the line to the array + int i = 0; + while (i < 255 && buffer[i] != '\0') { + lines[line_count][i] = buffer[i]; + i++; + } + lines[line_count][i] = '\0'; + + line_count++; + pos = 0; // Reset position for next line + } else if (pos < 255) { + // Add character to buffer + buffer[pos] = c; + pos++; + } + } + + // Handle the last line if file doesn't end with newline + if (pos > 0 && line_count < max_count) { + buffer[pos] = '\0'; + int i = 0; + while (i < 255 && buffer[i] != '\0') { + lines[line_count][i] = buffer[i]; + i++; + } + lines[line_count][i] = '\0'; + line_count++; + } + + close(fd); + return line_count; + } + + /** + * function prints joined line + * "first word" "rest line file 1" "rest line file 2" + */ + void print_joined_line(char* field, char* rest1, char* rest2){ + printf("%s %s %s\n", field, rest1, rest2); + } + + /** + * Compares lines from both files and looks for matches in the + * first field + * If the fields match then print joined line + */ + void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2){ + + // iterate through each line from file 1 + for (int i =0; i< count1; i++){ + char line1_copy[256]; // make copy to avoid messing with original + int k = 0; + while (k<255 && file1_lines[i][k] != '\0'){ + line1_copy[k] = file1_lines[i][k]; + k++; + } + line1_copy[k] = '\0'; + + char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match + // skip is field is empty + if(field1 == 0){ + continue; + } + + terminate_first_field(field1); // get rid of first word to make comparing easier + + // iterate through lines in file two + for (int j =0; j < count2; j++){ + char line2_copy[256]; // make copy to avoid messing with original + int m = 0; + while (m<255 && file2_lines[j][m] != '\0'){ + line2_copy[m] = file2_lines[j][m]; + m++; + } + line2_copy[m] = '\0'; + + char* field2 = get_first_field(line2_copy); // read full line but only gets first word + // skip if field is empty + if(field2 == 0){ + continue; + } + terminate_first_field(field2); + + // check if both words are equal returns 0 if true + if(compare_strings(field1, field2) == 0){ + // get rest of lines for both files from where we got field + char* rest1 = get_rest_of_line(file1_lines[i]); + char* rest2 = get_rest_of_line(file2_lines[j]); + + print_joined_line(field1, rest1, rest2); + + + } + } + } +} int main(int argc, char *argv[]){ // ensure correct usage of command line args + printf("DEBUG: starting join...\n"); if (argc != 3){ printf("Usage: join file1.txt file2.txt\n"); exit(1); } + printf("DEBUG: Args okay, reading files\n"); printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); - // initiate data structure for file content - // set two arrays of 50 lines and 256 chars each - char file1_lines[50][256]; - char file2_lines[50][256]; - - // read both files printf("Reading first file..."); - int count1 = join.read_file_lines(argv[1], file1_lines, 50); + int count1 = read_file_lines(argv[1], file1_lines, 50); + printf("Read %d lines from file1\n", count1); + if(count1 == 0){ + printf("ERROR: failed to read lines from %s\n", argv[1]); + exit(1); + } printf("Reading second file..."); - int count2 = join.read_file_lines(argv[2], file2_lines, 50); - + int count2 = read_file_lines(argv[2], file2_lines, 50); + printf("Read %d lines from file2\n", count2); + if(count2 == 0){ + printf("ERROR: failed to read lines from %s\n", argv[2]); + exit(1); + } // join both files - printf("joining both files"); - join.join_files(file1_lines, count1, file2_lines, count2); + printf("joining both files\n"); + join_files(file1_lines, count1, file2_lines, count2); + printf("join completed\n"); exit(0); } diff --git a/user/user.h b/user/user.h index 5dcac5a..ac84de9 100644 --- a/user/user.h +++ b/user/user.h @@ -24,7 +24,6 @@ int getpid(void); char* sys_sbrk(int,int); int pause(int); int uptime(void); -void join(void); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index 58f406f..c5d4c3a 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,4 +42,3 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); -entry("join"); From a0b3e9fa6c6b7e4ce3138d762f184f2a7e46f2db Mon Sep 17 00:00:00 2001 From: HECTOR NUNEZ Date: Tue, 23 Sep 2025 18:32:23 -0700 Subject: [PATCH 04/24] updating join, adding it to user dir only --- user/join.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/user/join.c b/user/join.c index 847d3e6..86dd19d 100644 --- a/user/join.c +++ b/user/join.c @@ -171,6 +171,7 @@ * If the fields match then print joined line */ void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2){ + int matches_found = 0; // iterate through each line from file 1 for (int i =0; i< count1; i++){ @@ -209,6 +210,8 @@ // check if both words are equal returns 0 if true if(compare_strings(field1, field2) == 0){ + matches_found++; + // get rest of lines for both files from where we got field char* rest1 = get_rest_of_line(file1_lines[i]); char* rest2 = get_rest_of_line(file2_lines[j]); @@ -218,17 +221,19 @@ } } + + } + if(matches_found == 0){ + printf("No matches found\n"); } } int main(int argc, char *argv[]){ // ensure correct usage of command line args - printf("DEBUG: starting join...\n"); if (argc != 3){ printf("Usage: join file1.txt file2.txt\n"); exit(1); } - printf("DEBUG: Args okay, reading files\n"); printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); From 1cd9c45502e786dc635308823b8116b2857bd500 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 24 Sep 2025 13:25:05 -0700 Subject: [PATCH 05/24] cleaned up indentation in join_files and added writing to txt file for the joined file --- user/join.c | 118 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/user/join.c b/user/join.c index 86dd19d..533ec0d 100644 --- a/user/join.c +++ b/user/join.c @@ -1,11 +1,12 @@ #include "kernel/types.h" #include "user/user.h" #include "kernel/stat.h" +#include "kernel/fcntl.h" // // Hector's functions - //void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2); - //void print_joined_line(char* field, char* rest1, char* rest2); + //void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file)); + //void print_joined_line(int output_file, char* field, char* rest1, char* rest2); //char* get_rest_of_line(char* line); // // @@ -16,6 +17,7 @@ // helper functions //compare_strings(char* str1, char* str2) //terminate_first_field(char* line) + /** * String comparison iterates through each char in both * strings to see if they match if non return non-zero @@ -108,8 +110,8 @@ * Returns the number of lines successfully read */ int read_file_lines(char* filename, char lines[][256], int max_count) { - int fd = open(filename, 0); // 0 = O_RDONLY - if (fd < 0) { + int file = open(filename, 0); // 0 = O_RDONLY + if (file < 0) { printf("Error: Cannot open file '%s'\n", filename); return 0; } @@ -119,7 +121,7 @@ int pos = 0; char c; - while (line_count < max_count && read(fd, &c, 1) > 0) { + while (line_count < max_count && read(file, &c, 1) > 0) { if (c == '\n') { // End of line found buffer[pos] = '\0'; @@ -153,7 +155,7 @@ line_count++; } - close(fd); + close(file); return line_count; } @@ -161,46 +163,61 @@ * function prints joined line * "first word" "rest line file 1" "rest line file 2" */ - void print_joined_line(char* field, char* rest1, char* rest2){ + void print_joined_line(int output_file, char* field, char* rest1, char* rest2){ printf("%s %s %s\n", field, rest1, rest2); + + // Write to the file + write(output_file, field, strlen(field)); + write(output_file, " ", 1); + write(output_file, rest1, strlen(rest1)); + write(output_file, " ", 1); + write(output_file, rest2, strlen(rest2)); + write(output_file, "\n", 1); } - /** - * Compares lines from both files and looks for matches in the - * first field - * If the fields match then print joined line - */ - void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2){ - int matches_found = 0; - - // iterate through each line from file 1 - for (int i =0; i< count1; i++){ - char line1_copy[256]; // make copy to avoid messing with original - int k = 0; - while (k<255 && file1_lines[i][k] != '\0'){ - line1_copy[k] = file1_lines[i][k]; - k++; - } - line1_copy[k] = '\0'; - - char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match - // skip is field is empty - if(field1 == 0){ - continue; - } + /** + * Compares lines from both files and looks for matches in the + * first field + * If the fields match then print joined line + */ + void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file){ + int matches_found = 0; + + // Open the output file for writing (create it if it doesn't exist) + int output_fd = open(output_file, O_CREATE | O_RDWR); + if (output_fd < 0) { + printf("Error: Cannot open output file '%s'\n", output_file); + return; + } + + // iterate through each line from file 1 + for (int i =0; i< count1; i++){ + char line1_copy[256]; // make copy to avoid messing with original + int k = 0; + while (k<255 && file1_lines[i][k] != '\0'){ + line1_copy[k] = file1_lines[i][k]; + k++; + } + line1_copy[k] = '\0'; + + char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match + // skip is field is empty + if(field1 == 0){ + continue; + } + + terminate_first_field(field1); // get rid of first word to make comparing easier - terminate_first_field(field1); // get rid of first word to make comparing easier - // iterate through lines in file two for (int j =0; j < count2; j++){ - char line2_copy[256]; // make copy to avoid messing with original - int m = 0; - while (m<255 && file2_lines[j][m] != '\0'){ - line2_copy[m] = file2_lines[j][m]; - m++; - } - line2_copy[m] = '\0'; - + char line2_copy[256]; // make copy to avoid messing with original + int m = 0; + while (m<255 && file2_lines[j][m] != '\0'){ + line2_copy[m] = file2_lines[j][m]; + m++; + } + line2_copy[m] = '\0'; + char* field2 = get_first_field(line2_copy); // read full line but only gets first word // skip if field is empty if(field2 == 0){ @@ -210,23 +227,23 @@ // check if both words are equal returns 0 if true if(compare_strings(field1, field2) == 0){ - matches_found++; + matches_found++; // get rest of lines for both files from where we got field char* rest1 = get_rest_of_line(file1_lines[i]); char* rest2 = get_rest_of_line(file2_lines[j]); - print_joined_line(field1, rest1, rest2); - + print_joined_line(output_fd, field1, rest1, rest2); } - } - - } - if(matches_found == 0){ - printf("No matches found\n"); + } + } + if(matches_found == 0){ + printf("No matches found\n"); + } + // Close the output file + close(output_fd); } -} int main(int argc, char *argv[]){ // ensure correct usage of command line args @@ -256,7 +273,8 @@ int main(int argc, char *argv[]){ } // join both files printf("joining both files\n"); - join_files(file1_lines, count1, file2_lines, count2); + join_files(file1_lines, count1, file2_lines, count2, "joined.txt"); + printf("saving in a new file called joined.txt\n"); printf("join completed\n"); exit(0); From 274d6faf532cc07529dbc2a8b38c0a01903e14d3 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 24 Sep 2025 13:30:34 -0700 Subject: [PATCH 06/24] added txt files to be tested --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 34a9540..88f3211 100644 --- a/Makefile +++ b/Makefile @@ -144,8 +144,8 @@ UPROGS=\ $U/_dorphan\ $U/_join\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt + mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt -include kernel/*.d user/*.d From 37af92b4ee560dd1da82df6d24200373534c3f37 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 24 Sep 2025 13:36:42 -0700 Subject: [PATCH 07/24] added 2 test txts with 25 lines each of ~40 chars per line --- file1.txt | 25 +++++++++++++++++++++++++ file2.txt | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt diff --git a/file1.txt b/file1.txt new file mode 100644 index 0000000..2cd7416 --- /dev/null +++ b/file1.txt @@ -0,0 +1,25 @@ +Alice Engineering Dept Robotics +Bob Physics Dept Quantum Studies +Charlie Chemistry Dept Polymers +Diana Math Dept Applied Modeling +Ethan Biology Dept Genetics Focus +Fiona Economics Dept Global Trade +George Literature Dept Classics +Hannah History Dept Ancient World +Ian Philosophy Dept Ethics +Julia Music Dept Composition +Kevin Art Dept Modern Painting +Laura CS Dept Machine Learning +Mark Stats Dept Data Analysis +Nina Linguistics Dept Semantics +Oliver Sociology Dept Inequality +Paula Psychology Dept Cognition +Quinn Anthropology Dept Culture +Rachel Astronomy Dept Cosmology +Steve PoliSci Dept Governance +Tina EnvSci Dept Climate Studies +Uma Business Dept Marketing +Victor Education Dept Pedagogy +Wendy Geography Dept GIS Mapping +Xavier Law Dept International +Yara Medicine Dept Physiology diff --git a/file2.txt b/file2.txt new file mode 100644 index 0000000..413c653 --- /dev/null +++ b/file2.txt @@ -0,0 +1,25 @@ +GradeA GPA3.9 HighPerformance +GradeB GPA3.1 AveragePerformance +GradeA GPA3.8 StrongPerformance +GradeA GPA3.7 SolidPerformance +GradeB GPA3.4 GoodPerformance +GradeC GPA2.5 BelowPerformance +GradeC GPA2.7 WeakPerformance +GradeB GPA3.3 ConsistentWork +GradeA GPA3.9 ExcellentWork +GradeA GPA4.0 OutstandingWork +GradeB GPA3.2 SatisfactoryWork +GradeB GPA3.0 FairPerformance +GradeA GPA3.5 StrongAnalysis +GradeA GPA3.6 ClearThinking +GradeB GPA3.4 GroupWorkGood +GradeC GPA2.8 NeedsImprovement +GradeC GPA2.6 PoorRetention +GradeA GPA3.9 ExcellentResearch +GradeA GPA3.8 HighAchievement +GradeB GPA3.2 StableWork +GradeB GPA3.0 AverageResults +GradeA GPA3.8 GoodLearning +GradeA GPA3.9 HighAccuracy +GradeB GPA3.1 FairResults +GradeB GPA3.3 SolidWork From ef839f5472f9fb2f60c60b7dd05cae768fad43cf Mon Sep 17 00:00:00 2001 From: HECTOR NUNEZ Date: Wed, 24 Sep 2025 19:39:00 -0700 Subject: [PATCH 08/24] added functionality to output results to an output file in os --- Makefile | 4 ++-- grades.txt | 5 +++++ prices.txt | 5 +++++ products.txt | 5 +++++ students.txt | 5 +++++ user/join.c | 46 +++++++++++++++++++++++++++++++--------------- 6 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 grades.txt create mode 100644 prices.txt create mode 100644 products.txt create mode 100644 students.txt diff --git a/Makefile b/Makefile index 88f3211..2d9ed25 100644 --- a/Makefile +++ b/Makefile @@ -144,8 +144,8 @@ UPROGS=\ $U/_dorphan\ $U/_join\ -fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt - mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt +fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt + mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt -include kernel/*.d user/*.d diff --git a/grades.txt b/grades.txt new file mode 100644 index 0000000..07b9dfb --- /dev/null +++ b/grades.txt @@ -0,0 +1,5 @@ +001 A+ Excellent Outstanding +002 B+ Good Solid +003 A- Very Strong +005 B Fair Average +006 C Below Standard diff --git a/prices.txt b/prices.txt new file mode 100644 index 0000000..e98bf5e --- /dev/null +++ b/prices.txt @@ -0,0 +1,5 @@ +P001 $999 Premium High +P003 $599 Mid Reasonable +P004 $299 Budget Low +P007 $199 Clearance Sale +P008 $149 Discount Special diff --git a/products.txt b/products.txt new file mode 100644 index 0000000..7d85455 --- /dev/null +++ b/products.txt @@ -0,0 +1,5 @@ +P001 Laptop Electronics Computing +P002 Phone Mobile Communications +P003 Tablet Portable Media +P004 Watch Wearable Tech +P005 Headphones Audio Sound diff --git a/students.txt b/students.txt new file mode 100644 index 0000000..7b57ae3 --- /dev/null +++ b/students.txt @@ -0,0 +1,5 @@ +001 Alice Engineering Robotics +002 Bob Physics Quantum +003 Charlie Chemistry Polymers +004 Diana Math Modeling +005 Ethan Biology Genetics diff --git a/user/join.c b/user/join.c index 533ec0d..11b7644 100644 --- a/user/join.c +++ b/user/join.c @@ -166,14 +166,17 @@ void print_joined_line(int output_file, char* field, char* rest1, char* rest2){ printf("%s %s %s\n", field, rest1, rest2); - // Write to the file - write(output_file, field, strlen(field)); - write(output_file, " ", 1); - write(output_file, rest1, strlen(rest1)); - write(output_file, " ", 1); - write(output_file, rest2, strlen(rest2)); - write(output_file, "\n", 1); + // check if there is a valid file descriptor + if (output_file >= 0){ + // Write to the file + write(output_file, field, strlen(field)); + write(output_file, " ", 1); + write(output_file, rest1, strlen(rest1)); + write(output_file, " ", 1); + write(output_file, rest2, strlen(rest2)); + write(output_file, "\n", 1); } + } /** * Compares lines from both files and looks for matches in the @@ -182,12 +185,16 @@ */ void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file){ int matches_found = 0; + int output_fd = -1; // Open the output file for writing (create it if it doesn't exist) - int output_fd = open(output_file, O_CREATE | O_RDWR); + if(output_file != 0){ + + output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); if (output_fd < 0) { printf("Error: Cannot open output file '%s'\n", output_file); return; + } } // iterate through each line from file 1 @@ -246,15 +253,20 @@ } int main(int argc, char *argv[]){ + // no output file before join + char* output_file = 0; // ensure correct usage of command line args - if (argc != 3){ - printf("Usage: join file1.txt file2.txt\n"); + if (argc == 4){ + output_file = argv[3]; + printf("Joining files '%s' and '%s' -> output to '%s'...\n", argv[1], argv[2], argv[3]); + }else if (argc == 3){ + printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); + } + else{ + printf("Usage: join file1.txt file2.txt [outputfile.txt]\n"); exit(1); } - printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); - - // read both files printf("Reading first file..."); int count1 = read_file_lines(argv[1], file1_lines, 50); @@ -273,8 +285,12 @@ int main(int argc, char *argv[]){ } // join both files printf("joining both files\n"); - join_files(file1_lines, count1, file2_lines, count2, "joined.txt"); - printf("saving in a new file called joined.txt\n"); + + join_files(file1_lines, count1, file2_lines, count2, output_file); + if(output_file != 0){ + printf("Output saved to %s\n", output_file); + } + printf("join completed\n"); exit(0); From 3aac05e7b743d124ed4f1e52eebdbf81af9331ed Mon Sep 17 00:00:00 2001 From: HECTOR NUNEZ Date: Thu, 25 Sep 2025 17:59:06 -0700 Subject: [PATCH 09/24] added comments to join.c --- user/join.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/user/join.c b/user/join.c index 11b7644..8374218 100644 --- a/user/join.c +++ b/user/join.c @@ -184,13 +184,14 @@ * If the fields match then print joined line */ void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file){ - int matches_found = 0; - int output_fd = -1; + int matches_found = 0; // count for amount of matches found + int output_fd = -1; // value of file descriptor of output file (-1 no valid file) // Open the output file for writing (create it if it doesn't exist) if(output_file != 0){ - - output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); + // changes the value of output_fd to indicate we have a valid file + output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | + // write access only | empty file if already exists) if (output_fd < 0) { printf("Error: Cannot open output file '%s'\n", output_file); return; @@ -201,6 +202,8 @@ for (int i =0; i< count1; i++){ char line1_copy[256]; // make copy to avoid messing with original int k = 0; + + // string copy while (k<255 && file1_lines[i][k] != '\0'){ line1_copy[k] = file1_lines[i][k]; k++; @@ -219,6 +222,8 @@ for (int j =0; j < count2; j++){ char line2_copy[256]; // make copy to avoid messing with original int m = 0; + + // string copy while (m<255 && file2_lines[j][m] != '\0'){ line2_copy[m] = file2_lines[j][m]; m++; @@ -230,7 +235,7 @@ if(field2 == 0){ continue; } - terminate_first_field(field2); + terminate_first_field(field2); // get rid of first word to make comparing easier // check if both words are equal returns 0 if true if(compare_strings(field1, field2) == 0){ @@ -240,11 +245,13 @@ char* rest1 = get_rest_of_line(file1_lines[i]); char* rest2 = get_rest_of_line(file2_lines[j]); + // print using format print_joined_line(output_fd, field1, rest1, rest2); } } } + // print message if no matches were found if(matches_found == 0){ printf("No matches found\n"); } @@ -285,7 +292,7 @@ int main(int argc, char *argv[]){ } // join both files printf("joining both files\n"); - + // call function that joins files into output file join_files(file1_lines, count1, file2_lines, count2, output_file); if(output_file != 0){ printf("Output saved to %s\n", output_file); From 90259f24f3324c88c93e9bb64df8024fa6107430 Mon Sep 17 00:00:00 2001 From: HECTOR NUNEZ Date: Thu, 25 Sep 2025 18:32:11 -0700 Subject: [PATCH 10/24] adding larger test files --- Fog_Emp.txt | 40 ++++++++++++++++++++++++++++++++++++++++ Fog_Perf.txt | 30 ++++++++++++++++++++++++++++++ MC_Item.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++ MC_Price.txt | 29 +++++++++++++++++++++++++++++ Makefile | 4 ++-- 5 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Fog_Emp.txt create mode 100644 Fog_Perf.txt create mode 100644 MC_Item.txt create mode 100644 MC_Price.txt diff --git a/Fog_Emp.txt b/Fog_Emp.txt new file mode 100644 index 0000000..300bcae --- /dev/null +++ b/Fog_Emp.txt @@ -0,0 +1,40 @@ +EMP001 Alice Johnson Manager Sales Northeast +EMP002 Bob Smith Developer IT Backend +EMP003 Carol Davis Analyst Finance Budgets +EMP004 David Wilson Designer Marketing Creative +EMP005 Emma Brown Support Operations Customer +EMP006 Frank Miller Engineer IT Systems +EMP007 Grace Lee Specialist HR Recruiting +EMP008 Henry Chen Coordinator Marketing Events +EMP009 Iris Taylor Administrator Finance Payroll +EMP010 Jack Rodriguez Developer IT Frontend +EMP011 Kate Thompson Manager Operations Supply +EMP012 Luis Garcia Analyst Data Business +EMP013 Maria Gonzalez Designer UX Research +EMP014 Nathan Park Engineer Software Senior +EMP015 Olivia White Specialist Sales Enterprise +EMP016 Paul Kim Coordinator HR Benefits +EMP017 Quinn Adams Administrator IT Security +EMP018 Rachel Turner Manager Finance Treasury +EMP019 Samuel Lee Developer Mobile Applications +EMP020 Tara Patel Analyst Marketing Consumer +EMP021 Ulysses Jones Engineer DevOps Cloud +EMP022 Victoria Chang Specialist Operations Logistics +EMP023 William Hayes Coordinator Sales Regional +EMP024 Ximena Rivera Administrator Marketing Social +EMP025 Yuki Tanaka Manager IT Infrastructure +EMP026 Zachary Moore Developer Full Stack +EMP027 Abby Foster Analyst HR Metrics +EMP028 Blake Cooper Engineer Quality Assurance +EMP029 Chloe Ward Specialist Finance Audit +EMP030 Dylan Scott Coordinator IT Help +EMP031 Elena Morales Administrator Operations Facilities +EMP032 Felix Chen Manager Sales International +EMP033 Gina Roberts Developer Database Systems +EMP034 Hugo Martinez Analyst Operations Process +EMP035 Ivy Johnson Specialist Marketing Digital +EMP036 Jake Williams Engineer Network Security +EMP037 Kara Davis Coordinator Finance Accounting +EMP038 Logan Brown Administrator HR Training +EMP039 Mia Thompson Manager Operations Quality +EMP040 Noah Garcia Developer API Services diff --git a/Fog_Perf.txt b/Fog_Perf.txt new file mode 100644 index 0000000..689f6ab --- /dev/null +++ b/Fog_Perf.txt @@ -0,0 +1,30 @@ +EMP001 Excellent Rating5 Promotion Ready +EMP003 Good Rating4 Steady Progress +EMP005 Outstanding Rating5 Top Performer +EMP007 Satisfactory Rating3 Meeting Goals +EMP009 Excellent Rating5 Leadership Potential +EMP011 Good Rating4 Strong Contributor +EMP013 Outstanding Rating5 Innovation Leader +EMP015 Satisfactory Rating3 Reliable Worker +EMP017 Good Rating4 Technical Expert +EMP019 Excellent Rating5 Rising Star +EMP021 Outstanding Rating5 Architecture Lead +EMP023 Satisfactory Rating3 Consistent Results +EMP025 Excellent Rating5 Strategic Thinker +EMP027 Good Rating4 Data Expert +EMP029 Outstanding Rating5 Process Optimizer +EMP031 Satisfactory Rating3 Team Player +EMP033 Excellent Rating5 Database Guru +EMP035 Good Rating4 Creative Marketer +EMP037 Outstanding Rating5 Financial Wizard +EMP039 Satisfactory Rating3 Quality Focused +EMP002 Good Rating4 Code Quality +EMP004 Excellent Rating5 Design Leader +EMP006 Outstanding Rating5 System Architect +EMP008 Satisfactory Rating3 Event Success +EMP010 Good Rating4 Frontend Expert +EMP012 Excellent Rating5 Analytics Pro +EMP014 Outstanding Rating5 Senior Mentor +EMP016 Satisfactory Rating3 Benefits Admin +EMP018 Good Rating4 Treasury Manager +EMP020 Excellent Rating5 Market Researcher diff --git a/MC_Item.txt b/MC_Item.txt new file mode 100644 index 0000000..225b5a7 --- /dev/null +++ b/MC_Item.txt @@ -0,0 +1,45 @@ +SKU1001 Laptop Dell Inspiron 15 +SKU1002 Monitor Samsung 27inch 4K +SKU1003 Keyboard Mechanical RGB Gaming +SKU1004 Mouse Wireless Ergonomic Office +SKU1005 Headphones Noise Cancelling Premium +SKU1006 Webcam HD 1080p Conference +SKU1007 Printer Laser Color Network +SKU1008 Scanner Flatbed Document High +SKU1009 Tablet iPad Pro 12inch +SKU1010 Smartphone iPhone 15 Pro +SKU1011 Smartwatch Apple Watch Series +SKU1012 Speaker Bluetooth Portable Waterproof +SKU1013 Charger USB-C Fast Wireless +SKU1014 Cable HDMI 4K Premium +SKU1015 Adapter USB Hub Multi +SKU1016 Drive SSD External 1TB +SKU1017 Memory RAM DDR4 32GB +SKU1018 Graphics Card RTX 4080 +SKU1019 Processor Intel i7 13th +SKU1020 Motherboard ASUS Gaming ATX +SKU1021 Case PC Tower RGB +SKU1022 Fan Cooling Liquid AIO +SKU1023 Power Supply 850W Modular +SKU1024 Router WiFi 6 Mesh +SKU1025 Switch Network 24 Port +SKU1026 Camera DSLR Canon Professional +SKU1027 Lens Telephoto 70-200mm Canon +SKU1028 Tripod Carbon Fiber Professional +SKU1029 Light LED Panel Studio +SKU1030 Microphone USB Condenser Podcast +SKU1031 Interface Audio USB Recording +SKU1032 Mixer Digital 8 Channel +SKU1033 Drone Quadcopter 4K Camera +SKU1034 Battery Drone LiPo 3S +SKU1035 Controller Game Wireless Pro +SKU1036 Console Gaming Latest Generation +SKU1037 Headset Gaming Surround 7.1 +SKU1038 Chair Gaming Ergonomic Racing +SKU1039 Desk Gaming L-Shaped Standing +SKU1040 Monitor Arm Dual Display +SKU1041 Lamp Desk LED Adjustable +SKU1042 Fan Desk USB Quiet +SKU1043 Heater Space Ceramic Safety +SKU1044 Purifier Air HEPA Smart +SKU1045 Humidifier Ultrasonic Cool Mist diff --git a/MC_Price.txt b/MC_Price.txt new file mode 100644 index 0000000..604d552 --- /dev/null +++ b/MC_Price.txt @@ -0,0 +1,29 @@ +SKU1001 $899.99 InStock Warehouse-A +SKU1003 $149.99 InStock Warehouse-B +SKU1005 $299.99 LowStock Warehouse-A +SKU1007 $449.99 InStock Warehouse-C +SKU1009 $1099.99 OutOfStock Backorder +SKU1011 $399.99 InStock Warehouse-A +SKU1013 $79.99 InStock Warehouse-B +SKU1015 $49.99 InStock Warehouse-C +SKU1017 $189.99 LowStock Warehouse-A +SKU1019 $329.99 InStock Warehouse-B +SKU1021 $129.99 InStock Warehouse-C +SKU1023 $159.99 LowStock Warehouse-A +SKU1025 $299.99 InStock Warehouse-B +SKU1027 $899.99 OutOfStock Backorder +SKU1029 $199.99 InStock Warehouse-C +SKU1031 $149.99 InStock Warehouse-A +SKU1033 $599.99 LowStock Warehouse-B +SKU1035 $69.99 InStock Warehouse-C +SKU1037 $199.99 InStock Warehouse-A +SKU1039 $399.99 LowStock Warehouse-B +SKU1041 $89.99 InStock Warehouse-C +SKU1043 $79.99 InStock Warehouse-A +SKU1045 $129.99 InStock Warehouse-B +SKU1002 $349.99 InStock Warehouse-A +SKU1004 $59.99 InStock Warehouse-B +SKU1006 $89.99 LowStock Warehouse-C +SKU1008 $249.99 InStock Warehouse-A +SKU1010 $999.99 InStock Warehouse-B +SKU1012 $99.99 InStock Warehouse-C diff --git a/Makefile b/Makefile index 2d9ed25..59646e5 100644 --- a/Makefile +++ b/Makefile @@ -144,8 +144,8 @@ UPROGS=\ $U/_dorphan\ $U/_join\ -fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt - mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt +fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt + mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt -include kernel/*.d user/*.d From d60e143dae0a369b92f17fbc7cd81c3b7f2fd9d0 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Fri, 26 Sep 2025 18:52:27 -0700 Subject: [PATCH 11/24] Fix: minor indentation @user/join.c --- user/join.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/user/join.c b/user/join.c index 8374218..883eedc 100644 --- a/user/join.c +++ b/user/join.c @@ -175,7 +175,7 @@ write(output_file, " ", 1); write(output_file, rest2, strlen(rest2)); write(output_file, "\n", 1); - } + } } /** @@ -189,12 +189,12 @@ // Open the output file for writing (create it if it doesn't exist) if(output_file != 0){ - // changes the value of output_fd to indicate we have a valid file - output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | - // write access only | empty file if already exists) - if (output_fd < 0) { - printf("Error: Cannot open output file '%s'\n", output_file); - return; + // changes the value of output_fd to indicate we have a valid file + output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | + // write access only | empty file if already exists) + if (output_fd < 0) { + printf("Error: Cannot open output file '%s'\n", output_file); + return; } } @@ -268,8 +268,7 @@ int main(int argc, char *argv[]){ printf("Joining files '%s' and '%s' -> output to '%s'...\n", argv[1], argv[2], argv[3]); }else if (argc == 3){ printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); - } - else{ + }else{ printf("Usage: join file1.txt file2.txt [outputfile.txt]\n"); exit(1); } @@ -286,7 +285,7 @@ int main(int argc, char *argv[]){ printf("Reading second file..."); int count2 = read_file_lines(argv[2], file2_lines, 50); printf("Read %d lines from file2\n", count2); - if(count2 == 0){ + if(count2 == 0){ printf("ERROR: failed to read lines from %s\n", argv[2]); exit(1); } From 1965e9bafd944a206a3f78b5599587333d088b67 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Fri, 26 Sep 2025 19:39:22 -0700 Subject: [PATCH 12/24] Feature: Added empty txt checking and minor syntax fixing --- user/join.c | 69 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/user/join.c b/user/join.c index 883eedc..3c09611 100644 --- a/user/join.c +++ b/user/join.c @@ -111,10 +111,22 @@ */ int read_file_lines(char* filename, char lines[][256], int max_count) { int file = open(filename, 0); // 0 = O_RDONLY + struct stat st; // stat struct contains a member called size, which holds the file's size in bytes + if (file < 0) { - printf("Error: Cannot open file '%s'\n", filename); + close(file); return 0; } + + if (fstat(file, &st) < 0) { //check metadata + close(file); + return -1; + } + + if (st.size == 0) { //check size to see if empty txt or not, join does not work if any or both files empty + close(file); + return -2; + } int line_count = 0; char buffer[256]; @@ -163,11 +175,11 @@ * function prints joined line * "first word" "rest line file 1" "rest line file 2" */ - void print_joined_line(int output_file, char* field, char* rest1, char* rest2){ + void print_joined_line(int output_file, char* field, char* rest1, char* rest2) { printf("%s %s %s\n", field, rest1, rest2); // check if there is a valid file descriptor - if (output_file >= 0){ + if (output_file >= 0) { // Write to the file write(output_file, field, strlen(field)); write(output_file, " ", 1); @@ -183,12 +195,12 @@ * first field * If the fields match then print joined line */ - void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file){ + void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file) { int matches_found = 0; // count for amount of matches found int output_fd = -1; // value of file descriptor of output file (-1 no valid file) // Open the output file for writing (create it if it doesn't exist) - if(output_file != 0){ + if(output_file != 0) { // changes the value of output_fd to indicate we have a valid file output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | // write access only | empty file if already exists) @@ -199,12 +211,12 @@ } // iterate through each line from file 1 - for (int i =0; i< count1; i++){ + for (int i =0; i< count1; i++) { char line1_copy[256]; // make copy to avoid messing with original int k = 0; // string copy - while (k<255 && file1_lines[i][k] != '\0'){ + while (k<255 && file1_lines[i][k] != '\0') { line1_copy[k] = file1_lines[i][k]; k++; } @@ -212,19 +224,19 @@ char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match // skip is field is empty - if(field1 == 0){ + if(field1 == 0) { continue; } terminate_first_field(field1); // get rid of first word to make comparing easier // iterate through lines in file two - for (int j =0; j < count2; j++){ + for (int j =0; j < count2; j++) { char line2_copy[256]; // make copy to avoid messing with original int m = 0; // string copy - while (m<255 && file2_lines[j][m] != '\0'){ + while (m<255 && file2_lines[j][m] != '\0') { line2_copy[m] = file2_lines[j][m]; m++; } @@ -232,13 +244,13 @@ char* field2 = get_first_field(line2_copy); // read full line but only gets first word // skip if field is empty - if(field2 == 0){ + if(field2 == 0) { continue; } terminate_first_field(field2); // get rid of first word to make comparing easier // check if both words are equal returns 0 if true - if(compare_strings(field1, field2) == 0){ + if(compare_strings(field1, field2) == 0) { matches_found++; // get rest of lines for both files from where we got field @@ -252,23 +264,23 @@ } } // print message if no matches were found - if(matches_found == 0){ + if(matches_found == 0) { printf("No matches found\n"); } // Close the output file close(output_fd); } -int main(int argc, char *argv[]){ +int main(int argc, char *argv[]) { // no output file before join char* output_file = 0; // ensure correct usage of command line args - if (argc == 4){ + if (argc == 4) { output_file = argv[3]; printf("Joining files '%s' and '%s' -> output to '%s'...\n", argv[1], argv[2], argv[3]); - }else if (argc == 3){ + } else if (argc == 3) { printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); - }else{ + } else { printf("Usage: join file1.txt file2.txt [outputfile.txt]\n"); exit(1); } @@ -277,23 +289,38 @@ int main(int argc, char *argv[]){ printf("Reading first file..."); int count1 = read_file_lines(argv[1], file1_lines, 50); printf("Read %d lines from file1\n", count1); - if(count1 == 0){ + if(count1 == 0) { printf("ERROR: failed to read lines from %s\n", argv[1]); exit(1); + } else if(count1 == -1) { + printf("ERROR: failed to retrive metadata from %s\n", argv[1]); + exit(1); + } else if(count1 == -2) { + printf("ERROR: %s is empty\n", argv[1]); + exit(1); + } else { + printf("File %s exists\n", argv[1]); } - + printf("Reading second file..."); int count2 = read_file_lines(argv[2], file2_lines, 50); printf("Read %d lines from file2\n", count2); - if(count2 == 0){ + if(count2 == 0) { printf("ERROR: failed to read lines from %s\n", argv[2]); exit(1); + }else if(count1 == -1) { + printf("ERROR: failed to retrive metadata from %s\n", argv[2]); + exit(1); + }else if(count1 == -2) { + printf("ERROR: %s is empty\n", argv[2]); + exit(1); } + // join both files printf("joining both files\n"); // call function that joins files into output file join_files(file1_lines, count1, file2_lines, count2, output_file); - if(output_file != 0){ + if(output_file != 0) { printf("Output saved to %s\n", output_file); } From f2e40db80cfc36e859f39438eb508d0f4137408c Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Fri, 26 Sep 2025 19:47:12 -0700 Subject: [PATCH 13/24] Fix: minor output fix --- user/join.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user/join.c b/user/join.c index 3c09611..ae4dd48 100644 --- a/user/join.c +++ b/user/join.c @@ -288,7 +288,7 @@ int main(int argc, char *argv[]) { // read both files printf("Reading first file..."); int count1 = read_file_lines(argv[1], file1_lines, 50); - printf("Read %d lines from file1\n", count1); + printf("Read %d lines from file 1\n", count1); if(count1 == 0) { printf("ERROR: failed to read lines from %s\n", argv[1]); exit(1); @@ -304,7 +304,7 @@ int main(int argc, char *argv[]) { printf("Reading second file..."); int count2 = read_file_lines(argv[2], file2_lines, 50); - printf("Read %d lines from file2\n", count2); + printf("Read %d lines from file 2\n", count2); if(count2 == 0) { printf("ERROR: failed to read lines from %s\n", argv[2]); exit(1); From e94b663069eff098ca8e7f2663f49d3d78048b91 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Fri, 26 Sep 2025 20:27:15 -0700 Subject: [PATCH 14/24] Test Case: Adding an empty txt test case --- Makefile | 4 ++-- empty.txt | 0 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 empty.txt diff --git a/Makefile b/Makefile index 59646e5..b093679 100644 --- a/Makefile +++ b/Makefile @@ -144,8 +144,8 @@ UPROGS=\ $U/_dorphan\ $U/_join\ -fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt - mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt +fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt empty.txt + mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt empty.txt -include kernel/*.d user/*.d diff --git a/empty.txt b/empty.txt new file mode 100644 index 0000000..e69de29 From b1f84f4d309a1db757c4e70df05af0639df91fe8 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Fri, 26 Sep 2025 20:29:07 -0700 Subject: [PATCH 15/24] Fix: changed a variable identifier name to fix logic error --- user/join.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/user/join.c b/user/join.c index ae4dd48..cde6a0f 100644 --- a/user/join.c +++ b/user/join.c @@ -286,9 +286,8 @@ int main(int argc, char *argv[]) { } // read both files - printf("Reading first file..."); + printf("Attempting to read first file...\n"); int count1 = read_file_lines(argv[1], file1_lines, 50); - printf("Read %d lines from file 1\n", count1); if(count1 == 0) { printf("ERROR: failed to read lines from %s\n", argv[1]); exit(1); @@ -299,21 +298,24 @@ int main(int argc, char *argv[]) { printf("ERROR: %s is empty\n", argv[1]); exit(1); } else { - printf("File %s exists\n", argv[1]); + printf("File %s exists\n", argv[1]); + printf("Read %d lines from file 1\n", count1); } - printf("Reading second file..."); + printf("Attempting to read second file...\n"); int count2 = read_file_lines(argv[2], file2_lines, 50); - printf("Read %d lines from file 2\n", count2); if(count2 == 0) { printf("ERROR: failed to read lines from %s\n", argv[2]); exit(1); - }else if(count1 == -1) { + } else if(count2 == -1) { printf("ERROR: failed to retrive metadata from %s\n", argv[2]); exit(1); - }else if(count1 == -2) { + } else if(count2 == -2) { printf("ERROR: %s is empty\n", argv[2]); exit(1); + } else { + printf("File %s exists\n", argv[2]); + printf("Read %d lines from file 2\n", count2); } // join both files From 1cab6fed953243c0ee92b755e6abc3b4170f6282 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 1 Oct 2025 16:20:03 -0700 Subject: [PATCH 16/24] Feature: MakeFile can now pick up all the test files from its own folder --- Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b093679..94ecd48 100644 --- a/Makefile +++ b/Makefile @@ -144,19 +144,26 @@ UPROGS=\ $U/_dorphan\ $U/_join\ -fs.img: mkfs/mkfs README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt empty.txt +TDIR = tests + +fs.img: mkfs/mkfs README.md $(UPROGS) $(TDIR)/file1.txt $(TDIR)/file2.txt $(TDIR)/students.txt $(TDIR)/grades.txt $(TDIR)/products.txt $(TDIR)/prices.txt $(TDIR)/Fog_Emp.txt $(TDIR)/Fog_Perf.txt $(TDIR)/MC_Item.txt $(TDIR)/MC_Price.txt $(TDIR)/empty.txt + cp $(TDIR)/*.txt . mkfs/mkfs fs.img README.md $(UPROGS) file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt empty.txt -include kernel/*.d user/*.d clean: - rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ + rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg file1.txt file2.txt students.txt grades.txt products.txt prices.txt Fog_Emp.txt Fog_Perf.txt MC_Item.txt MC_Price.txt empty.txt \ */*.o */*.d */*.asm */*.sym \ $K/kernel fs.img \ mkfs/mkfs .gdbinit \ $U/usys.S \ $(UPROGS) +qemu-clean: check-qemu-version $K/kernel fs.img + $(QEMU) $(QEMUOPTS) + $(MAKE) clean + # try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) # QEMU's gdb stub command line changed in 0.11 From b67a45d6c1be8677a2eed7bcf0ac3b0c85791aa9 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 1 Oct 2025 16:21:19 -0700 Subject: [PATCH 17/24] Restructure: Moving all the txt files from root into tests dir --- Fog_Emp.txt => tests/Fog_Emp.txt | 0 Fog_Perf.txt => tests/Fog_Perf.txt | 0 MC_Item.txt => tests/MC_Item.txt | 0 MC_Price.txt => tests/MC_Price.txt | 0 empty.txt => tests/empty.txt | 0 file1.txt => tests/file1.txt | 0 file2.txt => tests/file2.txt | 0 grades.txt => tests/grades.txt | 0 prices.txt => tests/prices.txt | 0 products.txt => tests/products.txt | 0 students.txt => tests/students.txt | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename Fog_Emp.txt => tests/Fog_Emp.txt (100%) rename Fog_Perf.txt => tests/Fog_Perf.txt (100%) rename MC_Item.txt => tests/MC_Item.txt (100%) rename MC_Price.txt => tests/MC_Price.txt (100%) rename empty.txt => tests/empty.txt (100%) rename file1.txt => tests/file1.txt (100%) rename file2.txt => tests/file2.txt (100%) rename grades.txt => tests/grades.txt (100%) rename prices.txt => tests/prices.txt (100%) rename products.txt => tests/products.txt (100%) rename students.txt => tests/students.txt (100%) diff --git a/Fog_Emp.txt b/tests/Fog_Emp.txt similarity index 100% rename from Fog_Emp.txt rename to tests/Fog_Emp.txt diff --git a/Fog_Perf.txt b/tests/Fog_Perf.txt similarity index 100% rename from Fog_Perf.txt rename to tests/Fog_Perf.txt diff --git a/MC_Item.txt b/tests/MC_Item.txt similarity index 100% rename from MC_Item.txt rename to tests/MC_Item.txt diff --git a/MC_Price.txt b/tests/MC_Price.txt similarity index 100% rename from MC_Price.txt rename to tests/MC_Price.txt diff --git a/empty.txt b/tests/empty.txt similarity index 100% rename from empty.txt rename to tests/empty.txt diff --git a/file1.txt b/tests/file1.txt similarity index 100% rename from file1.txt rename to tests/file1.txt diff --git a/file2.txt b/tests/file2.txt similarity index 100% rename from file2.txt rename to tests/file2.txt diff --git a/grades.txt b/tests/grades.txt similarity index 100% rename from grades.txt rename to tests/grades.txt diff --git a/prices.txt b/tests/prices.txt similarity index 100% rename from prices.txt rename to tests/prices.txt diff --git a/products.txt b/tests/products.txt similarity index 100% rename from products.txt rename to tests/products.txt diff --git a/students.txt b/tests/students.txt similarity index 100% rename from students.txt rename to tests/students.txt From 8d3025d638ea2eba57a5b14020729f853699b958 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 1 Oct 2025 16:36:21 -0700 Subject: [PATCH 18/24] Feature: Documentation shows up on README.md page on github --- README.md | 3 +- docs/Documentation.md | 68 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 docs/Documentation.md diff --git a/README.md b/README.md index 85f6164..70988fa 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,5 @@ Fall 2025 Edition ![FogOS](docs/fogos.gif) - +## Documentation +[Documentation](docs/Documentation.md) diff --git a/docs/Documentation.md b/docs/Documentation.md new file mode 100644 index 0000000..916a0e9 --- /dev/null +++ b/docs/Documentation.md @@ -0,0 +1,68 @@ +# Software Documentation + +## Building the Software + +### Build Steps +```bash +make +``` +This will: +- Compile the kernel from source files in the `kernel/` directory +- Compile user programs from the `user/` directory +- Copy test data files from `tests/` to the root directory +- Create the filesystem image (`fs.img`) containing all programs and data files + +## Running the Software + +### Start QEMU +```bash +make qemu +``` +This launches the RISC-V emulator with the compiled kernel and filesystem. + +### Exit QEMU +Press `Ctrl+A` then `X` to quit the emulator. + +### Run with automatic cleanup [OPTIONAL] +```bash +make qemu-clean +``` +This runs QEMU and automatically cleans build artifacts after you exit. + +## Testing the Software + +### Access test data files +Once in QEMU, the test data files are available in the filesystem: +- `file1.txt`, `file2.txt` - Sample department data +- `MC_Item.txt`, `MC_Price.txt` - Inventory data +- `students.txt`, `grades.txt` - Student records +- `products.txt`, `prices.txt` - Product information +- `Fog_Emp.txt`, `Fog_Perf.txt` - Employee data +- `empty.txt` - Empty test file + +### Run built-in test programs +The system includes several test programs in `/user`: +- `usertests` - Comprehensive system tests +- `forktest` - Process creation tests +- `grind` - Stress testing + +## Cleaning Up + +### Remove all build artifacts and copied test files +```bash +make clean +``` +This removes: +- Compiled object files (*.o) +- Kernel binary and filesystem image +- Temporary files +- Test data files copied to root (preserves originals in `testData/`) +- Note: `compile_flags.txt` in root is preserved + +## Project Structure + +- `kernel/` - Operating system kernel source code +- `user/` - User-space programs and utilities +- `testData/` - Original test data files (preserved during clean) +- `mkfs/` - Filesystem creation utility +- `Makefile` - Build configuration and targets From a412e195794d273de2cb5df7d96e77d3fcb0bd97 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 1 Oct 2025 16:40:42 -0700 Subject: [PATCH 19/24] Edit: README.md layout edit for ease of access to Documentation --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 70988fa..e0035be 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,26 @@ Fall 2025 Edition ![FogOS](docs/fogos.gif) ## Documentation -[Documentation](docs/Documentation.md) + +### Quick Links +- [Building the Software](docs/Documentation.md#building-the-software) +- [Running the Software](docs/Documentation.md#running-the-software) +- [Testing the Software](docs/Documentation.md#testing-the-software) +- [Cleaning Up](docs/Documentation.md#cleaning-up) +- [Project Structure](docs/Documentation.md#project-structure) + +### Quick Start +```bash +# Build the project +make + +# Run QEMU +make qemu + +# Exit: Ctrl+A then X + +# Clean up +make clean +``` + +For detailed documentation, see [Documentation.md](docs/Documentation.md) From 7d810e885bb95c48fd6ffbdcbe7ed9af198895ff Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Wed, 1 Oct 2025 16:48:42 -0700 Subject: [PATCH 20/24] Edit: Correcting some errors in documentation --- docs/Documentation.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/Documentation.md b/docs/Documentation.md index 916a0e9..140a94a 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -40,11 +40,16 @@ Once in QEMU, the test data files are available in the filesystem: - `Fog_Emp.txt`, `Fog_Perf.txt` - Employee data - `empty.txt` - Empty test file -### Run built-in test programs -The system includes several test programs in `/user`: -- `usertests` - Comprehensive system tests -- `forktest` - Process creation tests -- `grind` - Stress testing +### Run join command +You can test the join functionality with two input files: +```bash +join file1.txt file2.txt +``` + +You can also specify an output file: +```bash +join file1.txt file2.txt output.txt +``` ## Cleaning Up @@ -63,6 +68,6 @@ This removes: - `kernel/` - Operating system kernel source code - `user/` - User-space programs and utilities -- `testData/` - Original test data files (preserved during clean) +- `tests/` - Original test data files (preserved during clean) - `mkfs/` - Filesystem creation utility - `Makefile` - Build configuration and targets From c7db0d5c32f1bf846277844f19ef8a9202359ee5 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Mon, 6 Oct 2025 04:14:29 -0700 Subject: [PATCH 21/24] Added clear explanation of what join does, how it works, and expected output format with examples --- docs/Documentation.md | 50 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/Documentation.md b/docs/Documentation.md index 140a94a..10193f0 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -2,6 +2,11 @@ ## Building the Software +### Prerequisites +- RISC-V toolchain (riscv64-unknown-elf-gcc or riscv64-linux-gnu-gcc) +- QEMU RISC-V emulator (version 7.2 or higher) +- Make build system + ### Build Steps ```bash make @@ -9,7 +14,7 @@ make This will: - Compile the kernel from source files in the `kernel/` directory - Compile user programs from the `user/` directory -- Copy test data files from `tests/` to the root directory +- Copy test data files from `testData/` to the root directory - Create the filesystem image (`fs.img`) containing all programs and data files ## Running the Software @@ -23,7 +28,9 @@ This launches the RISC-V emulator with the compiled kernel and filesystem. ### Exit QEMU Press `Ctrl+A` then `X` to quit the emulator. -### Run with automatic cleanup [OPTIONAL] +**Important:** After exiting QEMU, run `make clean` to remove build artifacts and copied test files. + +### Run with automatic cleanup ```bash make qemu-clean ``` @@ -31,9 +38,15 @@ This runs QEMU and automatically cleans build artifacts after you exit. ## Testing the Software +### Understanding the join command +The `join` command merges two text files based on matching first fields (words) in each line. For each match found, it outputs a combined line in the format: +``` + +``` + ### Access test data files Once in QEMU, the test data files are available in the filesystem: -- `file1.txt`, `file2.txt` - Sample department data +- `file1.txt`, `file2.txt` - Sample department data (25 lines each) - `MC_Item.txt`, `MC_Price.txt` - Inventory data - `students.txt`, `grades.txt` - Student records - `products.txt`, `prices.txt` - Product information @@ -41,15 +54,40 @@ Once in QEMU, the test data files are available in the filesystem: - `empty.txt` - Empty test file ### Run join command -You can test the join functionality with two input files: + +**Basic usage (output to console):** ```bash join file1.txt file2.txt ``` +Expected output: 25 joined lines matching names from both files, displaying department info with grades. -You can also specify an output file: +**With output file:** ```bash join file1.txt file2.txt output.txt ``` +This saves the joined results to `output.txt` while also displaying them on console. + +**Get help:** +```bash +join --help +``` + +**Example output format:** +When joining students.txt and grades.txt, each matching line combines: +- The matching first field (e.g., "001") +- The remainder of the line from students.txt (e.g., "Alice Engineering Robotics") +- The remainder of the line from grades.txt (e.g., "A+ Excellent Outstanding") + +``` +001 Alice Engineering Robotics A+ Excellent Outstanding +002 Bob Physics Quantum B+ Good Solid +003 Charlie Chemistry Polymers A- Very Strong +005 Ethan Biology Genetics B Fair Average +join completed +... +``` + +The output shows 4 total matches (one for each person in both files). ## Cleaning Up @@ -68,6 +106,6 @@ This removes: - `kernel/` - Operating system kernel source code - `user/` - User-space programs and utilities -- `tests/` - Original test data files (preserved during clean) +- `testData/` - Original test data files (preserved during clean) - `mkfs/` - Filesystem creation utility - `Makefile` - Build configuration and targets From 93c7017dd06437a3023a0001ff0ac0bccac595a0 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Mon, 6 Oct 2025 04:42:28 -0700 Subject: [PATCH 22/24] Added --help flag and fixed indentation and formatting inconsistencies throughout join.c --- user/join.c | 554 +++++++++++++++++++++++++++------------------------- 1 file changed, 283 insertions(+), 271 deletions(-) diff --git a/user/join.c b/user/join.c index cde6a0f..7ccb79a 100644 --- a/user/join.c +++ b/user/join.c @@ -4,329 +4,341 @@ #include "kernel/fcntl.h" - // // Hector's functions - //void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file)); - //void print_joined_line(int output_file, char* field, char* rest1, char* rest2); - //char* get_rest_of_line(char* line); - // - // - // // Demetrius's functions - //char* get_first_field(char* line); - // int read_file_lines(char* filename, char lines[][256], int count2); - - // helper functions - //compare_strings(char* str1, char* str2) - //terminate_first_field(char* line) +// // Hector's functions +//void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file)); +//void print_joined_line(int output_file, char* field, char* rest1, char* rest2); +//char* get_rest_of_line(char* line); +// +// +// // Demetrius's functions +//char* get_first_field(char* line); +// int read_file_lines(char* filename, char lines[][256], int count2); + +// helper functions +//compare_strings(char* str1, char* str2) +//terminate_first_field(char* line) + +/** + * String comparison iterates through each char in both + * strings to see if they match if non return non-zero + */ + +static char file1_lines[50][256]; +static char file2_lines[50][256]; + +int compare_strings(char* str1, char* str2) { + while (*str1 && *str2) { + if (*str1 != *str2) { + return *str1 - *str2; + } + str1++; + str2++; + } + return *str1 - *str2; +} + +/** + * Extracts the first field (word) from a line + * Returns a pointer to the first field, or NULL if line is empty + */ + +char* get_first_field(char* line) { + if (line == 0 || *line == '\0') { + return 0; + } + + // Skip leading whitespace + while (*line == ' ' || *line == '\t') { + line++; + } - /** - * String comparison iterates through each char in both - * strings to see if they match if non return non-zero - */ - - static char file1_lines[50][256]; - static char file2_lines[50][256]; - - int compare_strings(char* str1, char* str2){ - while (*str1 && *str2){ - if (*str1 != *str2){ - return *str1 - *str2; - } - str1++; - str2++; - } - return *str1 - *str2; + // If line is empty after skipping whitespace + if (*line == '\0') { + return 0; } + + return line; +} - /** - * Extracts the first field (word) from a line - * Returns a pointer to the first field, or NULL if line is empty - */ +/** + * Function to get the rest of the line so it is everything + * after the first word +*/ +char* get_rest_of_line(char* line) { - char* get_first_field(char* line) { - if (line == 0 || *line == '\0') { - return 0; - } - - // Skip leading whitespace - while (*line == ' ' || *line == '\t') { - line++; - } - - // If line is empty after skipping whitespace - if (*line == '\0') { - return 0; - } - - return line; + // return empty if line is not valid + if(line == 0 || *line == '\0') { + return ""; } - /** - * Function to get the rest of the line so it is everything - * after the first word - */ - char* get_rest_of_line(char* line){ + char* ptr = line; + // skip leading whitespace + while(*ptr == ' ' || *ptr == '\t') { + ptr++; + } + // skip first field until reached whitespace or end of line + while (*ptr != '\0' && *ptr != ' ' && *ptr != '\t' ) { + ptr++; + } + // skip whitespace between first field and rest + while(*ptr == ' ' || *ptr == '\t') { + ptr++; + } + return ptr; +} - // return empty if line is not valid - if(line == 0 || *line == '\0'){ - return ""; - } +void terminate_first_field(char* line) { + char* ptr = line; - char* ptr = line; - // skip leading whitespace - while(*ptr == ' ' || *ptr == '\t'){ - ptr++; - } - // skip first field until reached whitespace or end of line - while (*ptr != '\0' && *ptr != ' ' && *ptr != '\t' ){ - ptr++; - } - // skip whitespace between first field and rest - while(*ptr == ' ' || *ptr == '\t'){ - ptr++; - } - return ptr; + // skip whitespace + while (*ptr == ' ' || *ptr == '\t') { + ptr++; } - void terminate_first_field(char* line){ - char* ptr = line; - - // skip whitespace - while (*ptr == ' ' || *ptr == '\t'){ - ptr++; - } - - // get the end of the first word - while (*ptr != '\0' && *ptr != ' ' && *ptr != '\n' && *ptr != '\t'){ - ptr++; - } - // terminate first word - if(*ptr != '\0'){ - *ptr = '\0'; - } + // get the end of the first word + while (*ptr != '\0' && *ptr != ' ' && *ptr != '\n' && *ptr != '\t') { + ptr++; + } + // terminate first word + if(*ptr != '\0') { + *ptr = '\0'; } +} - /** - * Reads lines from a file into the provided array - * Returns the number of lines successfully read - */ - int read_file_lines(char* filename, char lines[][256], int max_count) { - int file = open(filename, 0); // 0 = O_RDONLY - struct stat st; // stat struct contains a member called size, which holds the file's size in bytes - - if (file < 0) { - close(file); - return 0; - } +/** + * Reads lines from a file into the provided array + * Returns the number of lines successfully read + * Returns 0 if file cannot be opened + * Returns -1 if metadata cannot be retrieved + * Returns -2 if file is empty + */ +int read_file_lines(char* filename, char lines[][256], int max_count) { + int file = open(filename, 0); // 0 = O_RDONLY + struct stat st; // stat struct contains a member called size, which holds the file's size in bytes + + if (file < 0) { + close(file); + return 0; + } - if (fstat(file, &st) < 0) { //check metadata - close(file); - return -1; - } + if (fstat(file, &st) < 0) { //check metadata + close(file); + return -1; + } - if (st.size == 0) { //check size to see if empty txt or not, join does not work if any or both files empty - close(file); - return -2; - } - - int line_count = 0; - char buffer[256]; - int pos = 0; - char c; - - while (line_count < max_count && read(file, &c, 1) > 0) { - if (c == '\n') { - // End of line found - buffer[pos] = '\0'; - - // Copy the line to the array - int i = 0; - while (i < 255 && buffer[i] != '\0') { - lines[line_count][i] = buffer[i]; - i++; - } - lines[line_count][i] = '\0'; - - line_count++; - pos = 0; // Reset position for next line - } else if (pos < 255) { - // Add character to buffer - buffer[pos] = c; - pos++; - } - } - - // Handle the last line if file doesn't end with newline - if (pos > 0 && line_count < max_count) { + if (st.size == 0) { //check size to see if empty txt or not, join does not work if any or both files empty + close(file); + return -2; + } + + int line_count = 0; + char buffer[256]; + int pos = 0; + char c; + + while (line_count < max_count && read(file, &c, 1) > 0) { + if (c == '\n') { + // End of line found buffer[pos] = '\0'; + + // Copy the line to the array int i = 0; while (i < 255 && buffer[i] != '\0') { lines[line_count][i] = buffer[i]; i++; } lines[line_count][i] = '\0'; + line_count++; + pos = 0; // Reset position for next line + } else if (pos < 255) { + // Add character to buffer + buffer[pos] = c; + pos++; } - - close(file); - return line_count; } - - /** - * function prints joined line - * "first word" "rest line file 1" "rest line file 2" - */ - void print_joined_line(int output_file, char* field, char* rest1, char* rest2) { - printf("%s %s %s\n", field, rest1, rest2); - - // check if there is a valid file descriptor - if (output_file >= 0) { - // Write to the file - write(output_file, field, strlen(field)); - write(output_file, " ", 1); - write(output_file, rest1, strlen(rest1)); - write(output_file, " ", 1); - write(output_file, rest2, strlen(rest2)); - write(output_file, "\n", 1); - } - } + + // Handle the last line if file doesn't end with newline + if (pos > 0 && line_count < max_count) { + buffer[pos] = '\0'; + int i = 0; + while (i < 255 && buffer[i] != '\0') { + lines[line_count][i] = buffer[i]; + i++; + } + lines[line_count][i] = '\0'; + line_count++; + } - /** - * Compares lines from both files and looks for matches in the - * first field - * If the fields match then print joined line - */ - void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file) { - int matches_found = 0; // count for amount of matches found - int output_fd = -1; // value of file descriptor of output file (-1 no valid file) - - // Open the output file for writing (create it if it doesn't exist) - if(output_file != 0) { - // changes the value of output_fd to indicate we have a valid file - output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | - // write access only | empty file if already exists) - if (output_fd < 0) { - printf("Error: Cannot open output file '%s'\n", output_file); - return; - } + close(file); + return line_count; +} + +/** + * function prints joined line + * "first word" "rest line file 1" "rest line file 2" +*/ +void print_joined_line(int output_file, char* field, char* rest1, char* rest2) { + printf("%s %s %s\n", field, rest1, rest2); + + // check if there is a valid file descriptor + if (output_file >= 0) { + // Write to the file + write(output_file, field, strlen(field)); + write(output_file, " ", 1); + write(output_file, rest1, strlen(rest1)); + write(output_file, " ", 1); + write(output_file, rest2, strlen(rest2)); + write(output_file, "\n", 1); + } +} + +/** + * Compares lines from both files and looks for matches in the + * first field + * If the fields match then print joined line +*/ +void join_files(char file1_lines[][256], int count1, char file2_lines[][256], int count2, const char* output_file) { + int matches_found = 0; // count for amount of matches found + int output_fd = -1; // value of file descriptor of output file (-1 no valid file) + + // Open the output file for writing (create it if it doesn't exist) + if(output_file != 0) { + // changes the value of output_fd to indicate we have a valid file + output_fd = open(output_file, O_CREATE | O_WRONLY | O_TRUNC); // open file with flags (create new file if it does not exist | + // write access only | empty file if already exists) + if (output_fd < 0) { + printf("Error: Cannot open output file '%s'\n", output_file); + return; + } + } + + // iterate through each line from file 1 + for (int i = 0; i < count1; i++) { + char line1_copy[256]; // make copy to avoid messing with original + int k = 0; + + // string copy + while (k < 255 && file1_lines[i][k] != '\0') { + line1_copy[k] = file1_lines[i][k]; + k++; + } + line1_copy[k] = '\0'; + + char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match + // skip is field is empty + if(field1 == 0) { + continue; } - // iterate through each line from file 1 - for (int i =0; i< count1; i++) { - char line1_copy[256]; // make copy to avoid messing with original - int k = 0; + terminate_first_field(field1); // get rid of first word to make comparing easier + + // iterate through lines in file two + for (int j = 0; j < count2; j++) { + char line2_copy[256]; // make copy to avoid messing with original + int m = 0; // string copy - while (k<255 && file1_lines[i][k] != '\0') { - line1_copy[k] = file1_lines[i][k]; - k++; + while (m < 255 && file2_files[j][m] != '\0') { + line2_copy[m] = file2_lines[j][m]; + m++; } - line1_copy[k] = '\0'; - - char* field1 = get_first_field(line1_copy); // reads full line but only gets first word to check for match - // skip is field is empty - if(field1 == 0) { + line2_copy[m] = '\0'; + + char* field2 = get_first_field(line2_copy); // read full line but only gets first word + // skip if field is empty + if(field2 == 0) { continue; } + terminate_first_field(field2); // get rid of first word to make comparing easier - terminate_first_field(field1); // get rid of first word to make comparing easier - - // iterate through lines in file two - for (int j =0; j < count2; j++) { - char line2_copy[256]; // make copy to avoid messing with original - int m = 0; - - // string copy - while (m<255 && file2_lines[j][m] != '\0') { - line2_copy[m] = file2_lines[j][m]; - m++; - } - line2_copy[m] = '\0'; - - char* field2 = get_first_field(line2_copy); // read full line but only gets first word - // skip if field is empty - if(field2 == 0) { - continue; - } - terminate_first_field(field2); // get rid of first word to make comparing easier - - // check if both words are equal returns 0 if true - if(compare_strings(field1, field2) == 0) { - matches_found++; - - // get rest of lines for both files from where we got field - char* rest1 = get_rest_of_line(file1_lines[i]); - char* rest2 = get_rest_of_line(file2_lines[j]); - - // print using format - print_joined_line(output_fd, field1, rest1, rest2); - - } - } - } - // print message if no matches were found - if(matches_found == 0) { - printf("No matches found\n"); - } - // Close the output file - close(output_fd); + // check if both words are equal returns 0 if true + if(compare_strings(field1, field2) == 0) { + matches_found++; + + // get rest of lines for both files from where we got field + char* rest1 = get_rest_of_line(file1_lines[i]); + char* rest2 = get_rest_of_line(file2_lines[j]); + + // print using format + print_joined_line(output_fd, field1, rest1, rest2); + } + } + } + + // print message if no matches were found + if(matches_found == 0) { + printf("No matches found\n"); } + + // Close the output file + close(output_fd); +} + +void print_usage() { + printf("Usage: join file1.txt file2.txt [outputfile.txt]\n"); + printf("\nDescription:\n"); + printf(" Joins two files based on matching first fields in each line.\n"); + printf(" Output format: \n"); + printf("\nArguments:\n"); + printf(" file1.txt First input file\n"); + printf(" file2.txt Second input file\n"); + printf(" outputfile.txt (Optional) Output file for results\n"); + printf("\nOptions:\n"); + printf(" --help Display this help message\n"); +} int main(int argc, char *argv[]) { - // no output file before join - char* output_file = 0; - // ensure correct usage of command line args + // Handle --help flag + if (argc == 2 && compare_strings(argv[1], "--help") == 0) { + print_usage(); + exit(0); + } + + char* output_file = 0; + + // Validate command line arguments if (argc == 4) { - output_file = argv[3]; - printf("Joining files '%s' and '%s' -> output to '%s'...\n", argv[1], argv[2], argv[3]); - } else if (argc == 3) { - printf("Joining files '%s' and '%s'...\n", argv[1], argv[2]); - } else { - printf("Usage: join file1.txt file2.txt [outputfile.txt]\n"); + output_file = argv[3]; + } else if (argc != 3) { + print_usage(); exit(1); } - // read both files - printf("Attempting to read first file...\n"); + // Read both files int count1 = read_file_lines(argv[1], file1_lines, 50); - if(count1 == 0) { - printf("ERROR: failed to read lines from %s\n", argv[1]); - exit(1); - } else if(count1 == -1) { - printf("ERROR: failed to retrive metadata from %s\n", argv[1]); - exit(1); - } else if(count1 == -2) { - printf("ERROR: %s is empty\n", argv[1]); + if(count1 <= 0) { + if(count1 == 0) { + printf("ERROR: failed to open %s\n", argv[1]); + } else if(count1 == -1) { + printf("ERROR: failed to retrieve metadata from %s\n", argv[1]); + } else if(count1 == -2) { + printf("ERROR: %s is empty\n", argv[1]); + } + print_usage(); exit(1); - } else { - printf("File %s exists\n", argv[1]); - printf("Read %d lines from file 1\n", count1); } - printf("Attempting to read second file...\n"); int count2 = read_file_lines(argv[2], file2_lines, 50); - if(count2 == 0) { - printf("ERROR: failed to read lines from %s\n", argv[2]); - exit(1); - } else if(count2 == -1) { - printf("ERROR: failed to retrive metadata from %s\n", argv[2]); - exit(1); - } else if(count2 == -2) { - printf("ERROR: %s is empty\n", argv[2]); + if(count2 <= 0) { + if(count2 == 0) { + printf("ERROR: failed to open %s\n", argv[2]); + } else if(count2 == -1) { + printf("ERROR: failed to retrieve metadata from %s\n", argv[2]); + } else if(count2 == -2) { + printf("ERROR: %s is empty\n", argv[2]); + } + print_usage(); exit(1); - } else { - printf("File %s exists\n", argv[2]); - printf("Read %d lines from file 2\n", count2); } - // join both files - printf("joining both files\n"); - // call function that joins files into output file + // Join both files join_files(file1_lines, count1, file2_lines, count2, output_file); if(output_file != 0) { - printf("Output saved to %s\n", output_file); + printf("Output saved to %s\n", output_file); } printf("join completed\n"); - + exit(0); } From c83b01093ff8a9982d638eee1ec3d79176af9bec Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Mon, 6 Oct 2025 04:45:36 -0700 Subject: [PATCH 23/24] Correcting test folder name --- docs/Documentation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Documentation.md b/docs/Documentation.md index 10193f0..4df007c 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -14,7 +14,7 @@ make This will: - Compile the kernel from source files in the `kernel/` directory - Compile user programs from the `user/` directory -- Copy test data files from `testData/` to the root directory +- Copy test data files from `tests/` to the root directory - Create the filesystem image (`fs.img`) containing all programs and data files ## Running the Software @@ -99,13 +99,13 @@ This removes: - Compiled object files (*.o) - Kernel binary and filesystem image - Temporary files -- Test data files copied to root (preserves originals in `testData/`) +- Test data files copied to root (preserves originals in `tests/`) - Note: `compile_flags.txt` in root is preserved ## Project Structure - `kernel/` - Operating system kernel source code - `user/` - User-space programs and utilities -- `testData/` - Original test data files (preserved during clean) +- `tests/` - Original test data files (preserved during clean) - `mkfs/` - Filesystem creation utility - `Makefile` - Build configuration and targets From a49742500a41eb07531a18d0ac9bae7b82e610b3 Mon Sep 17 00:00:00 2001 From: DEMETRIUS CHATTERJEE Date: Mon, 6 Oct 2025 05:01:20 -0700 Subject: [PATCH 24/24] Simplied user feedback when storing results to a file --- user/join.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/user/join.c b/user/join.c index 7ccb79a..a53d34b 100644 --- a/user/join.c +++ b/user/join.c @@ -179,8 +179,11 @@ int read_file_lines(char* filename, char lines[][256], int max_count) { * "first word" "rest line file 1" "rest line file 2" */ void print_joined_line(int output_file, char* field, char* rest1, char* rest2) { - printf("%s %s %s\n", field, rest1, rest2); - + // Only print to console if NOT writing to a file + if (output_file < 0) { + printf("%s %s %s\n", field, rest1, rest2); + } + // check if there is a valid file descriptor if (output_file >= 0) { // Write to the file @@ -239,7 +242,7 @@ void join_files(char file1_lines[][256], int count1, char file2_lines[][256], in int m = 0; // string copy - while (m < 255 && file2_files[j][m] != '\0') { + while (m < 255 && file2_lines[j][m] != '\0') { line2_copy[m] = file2_lines[j][m]; m++; }