diff --git a/.gitignore b/.gitignore index 8eeceec..6f34b18 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ *.out .vscode/ +.gdb_history -concordance/ \ No newline at end of file +concordance/ +etc/ \ No newline at end of file diff --git a/Week 9/1_listints/s1_listints.c b/Week 9/1_listints/s1_listints.c new file mode 100644 index 0000000..376fa49 --- /dev/null +++ b/Week 9/1_listints/s1_listints.c @@ -0,0 +1,150 @@ + +// SOLUTION 1 - DYNAMIC MEMORY WITH realloc() + +/* +* 1. Write a C program, named listints, which will print the integers requested + via a single command-line argument. The list of integers is to appear in + strictly increasing order, with each requested integer appearing once and only + once (even if duplicated in the request). +* Simple examples of its use are: + prompt> listints 8 + will print: 8 + + prompt> listints 3,5,9 + will print: 3 5 9 + + prompt> listints 1-10 + will print: 1 2 3 4 5 6 7 8 9 10 + + prompt> listints 1-10,6 + will print: 1 2 3 4 5 6 7 8 9 10 +* 🌶 🌶 And some much more difficult examples: + prompt> listints 2000-2020,40-50 + prompt> listints 1-10,2010-2020,3000000-3000010 +*/ + +#include +#include +#include + +#define RANGE_SEP '-' +#define LIST_SEP ',' + +typedef struct { + long start; + long end; +} RANGE; + +int sort_range(const void *v1, const void *v2) +{ + RANGE* range_1 = (RANGE*)v1; + RANGE* range_2 = (RANGE*)v2; + return (range_1->start - range_2->start); +} + +char *substr(char *str, char *end) +{ + size_t length = end - str; + char *substring = malloc(length + 1); + strncpy(substring,str,length); + *(substring+length) = '\0'; + return substring; +} + +RANGE convert_to_range(char *range_str) +{ + char *sep = strchr(range_str, RANGE_SEP); + RANGE range; + if (sep != NULL) { + sep++; + range.start = atol(substr(range_str,sep-1)); + range.end = atol(sep); + } else { + range.start = atol(range_str); + range.end = range.start; + } + + if (range.end < range.start) { + fprintf(stderr, "ERROR: End range must be before start range.\n"); + exit(EXIT_FAILURE); + } + return range; +} + +RANGE *split_ranges(char *list, size_t *list_size) +{ + size_t range_list_max = 1; + RANGE *range_list = malloc(sizeof(RANGE)); + if (range_list == NULL) { + fprintf(stderr, "ERROR: Couldn't allocate memory.\n"); + exit(EXIT_FAILURE); + } + RANGE *range = range_list; + + char *end; + while ( (end = strchr(list, LIST_SEP)) != NULL ) { + // EXTRACT ONE RANGE. + char *element = substr(list, end); + + // EXPAND MEMORY + if (*list_size >= range_list_max) { + range_list_max *= 2; + range_list = realloc(range_list, range_list_max * sizeof(RANGE)); + if (range_list == NULL) { + fprintf(stderr, "ERROR: Couldn't allocate memory.\n"); + exit(EXIT_FAILURE); + } + range = range_list + *list_size; + } + + *range = convert_to_range(element); + range++; + + *list_size = (size_t)(range - range_list); + list = end + 1; + } + + qsort(range_list, *list_size, sizeof(RANGE), sort_range); + return range_list; +} + +long max(long v1, long v2) +{ + if (v1 < v2) { + return v2; + } + return v1; +} + +void print_ranges(RANGE *range_list, size_t list_size) +{ + long max_number = 0; + for (size_t i = 0; i < list_size; i++) { + RANGE *range = range_list + i; + for (size_t n = max(range->start, max_number); n <= range->end; n++) + { + max_number = n+1; + printf("%lu ",n); + } + } + putchar('\n'); +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: listints [INTEGER RANGE]\n"); + exit(EXIT_FAILURE); + } + + // STRING WITH A ',' APPENDED TO MAKE THE SPLIT WORK. + char *ext_str = malloc(strlen(argv[1]) + 1); + strcpy(ext_str,argv[1]); + strcat(ext_str,","); + + size_t list_size = 0; + RANGE *range_list = split_ranges(ext_str, &list_size); + print_ranges(range_list, list_size); + return 0; +} diff --git a/Week 9/1_listints/s2_listints.c b/Week 9/1_listints/s2_listints.c new file mode 100644 index 0000000..588b866 --- /dev/null +++ b/Week 9/1_listints/s2_listints.c @@ -0,0 +1,145 @@ + +// SOLUTION 1 - NO DYNAMIC MEMORY ALLOCATION + +/* +* 1. Write a C program, named listints, which will print the integers requested + via a single command-line argument. The list of integers is to appear in + strictly increasing order, with each requested integer appearing once and only + once (even if duplicated in the request). +* Simple examples of its use are: + prompt> listints 8 + will print: 8 + + prompt> listints 3,5,9 + will print: 3 5 9 + + prompt> listints 1-10 + will print: 1 2 3 4 5 6 7 8 9 10 + + prompt> listints 1-10,6 + will print: 1 2 3 4 5 6 7 8 9 10 +* 🌶 🌶 And some much more difficult examples: + prompt> listints 2000-2020,40-50 + prompt> listints 1-10,2010-2020,3000000-3000010 +*/ + +#include +#include +#include + +#define RANGE_SEP '-' +#define LIST_SEP ',' + +typedef struct { + long start; + long end; +} RANGE; + +int sort_range(const void *v1, const void *v2) +{ + RANGE* range_1 = (RANGE*)v1; + RANGE* range_2 = (RANGE*)v2; + return (range_1->start - range_2->start); +} + +char *substr(char *str, char *end) +{ + size_t length = end - str; + char *substring = malloc(length + 1); + strncpy(substring,str,length); + *(substring+length) = '\0'; + return substring; +} + +RANGE convert_to_range(char *range_str) +{ + char *sep = strchr(range_str, RANGE_SEP); + RANGE range; + if (sep != NULL) { + sep++; + range.start = atol(substr(range_str,sep-1)); + range.end = atol(sep); + } else { + range.start = atol(range_str); + range.end = range.start; + } + + if (range.end < range.start) { + fprintf(stderr, "ERROR: End range must be before start range.\n"); + exit(EXIT_FAILURE); + } + return range; +} + +RANGE *split_ranges(char *list, size_t list_size) +{ + RANGE *range_list = malloc(list_size * sizeof(RANGE)); + if (range_list == NULL) { + fprintf(stderr, "ERROR: Couldn't allocate memory.\n"); + exit(EXIT_FAILURE); + } + RANGE *range = range_list; + + char *end; + while ( (end = strchr(list, LIST_SEP)) != NULL ) { + // EXTRACT ONE RANGE. + char *element = substr(list, end); + *range = convert_to_range(element); + range++; + list = end + 1; + } + + qsort(range_list, list_size, sizeof(RANGE), sort_range); + return range_list; +} + +long max(long v1, long v2) +{ + if (v1 < v2) { + return v2; + } + return v1; +} + +void print_ranges(RANGE *range_list, size_t list_size) +{ + long max_number = 0; + for (size_t i = 0; i < list_size; i++) { + RANGE *range = range_list + i; + for (size_t n = max(range->start, max_number); n <= range->end; n++) + { + max_number = n+1; + printf("%lu ",n); + } + } + putchar('\n'); +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: listints [INTEGER RANGE]\n"); + exit(EXIT_FAILURE); + } + + // STRING WITH A ',' APPENDED TO MAKE THE SPLIT WORK. + char *ext_str = malloc(strlen(argv[1]) + 1); + strcpy(ext_str,argv[1]); + strcat(ext_str,","); + + // COUNT RANGES REQUIRED. + size_t ranges = 0; + size_t i = 0; + char c; + while ((c = ext_str[i]) != '\0') { + i++; + if (c == ',') { + ranges++; + } + } + + RANGE *range_list = split_ranges(ext_str, ranges); + print_ranges(range_list, ranges); + return 0; +} diff --git a/Week 9/3_mywc/mywc.c b/Week 9/3_mywc/mywc.c new file mode 100644 index 0000000..665944e --- /dev/null +++ b/Week 9/3_mywc/mywc.c @@ -0,0 +1,110 @@ +/* +* 3. Both Linux and macOS provide a standard command named wc (an abbreviation for + wordcount!) which determines the number of lines, words, and characters in a + named file. You can read about this command using the online documentation: + (man wc). +* For this task, you will develop your own version of the wc program named mywc. + +* a. Firstly, write a function named counter() that calculates and prints out + the number of lines contained within a file. Your counter() function should + take one argument, a character array that provides a filename. +* b. Next, modify your mywc program and the counter() function to either count + the lines of a file, named on the command-line, or to count the input + "arriving" via the stdin stream. We often described such a program as a + filter. +* c. Next, extend your counter() function so that it now also counts, and + prints, the number of characters and words found in the file. Be careful with + the meaning of a "word" - it's just a sequence of any characters surrounded by + whitespace characters. Check your printed results against those of the + standard wc program. +* d. Next, observe that our counter() function is printing its three + results - as we can only return a single result. Modify the counter() function + so that its three calculated results are now "given" back to the calling + function through pointers passed as parameters. Now, the calling function will + have the results placed in its own local variables, and be able to print + (or use) the results itself. + +* 4. 🌶 Now, using the standard getopt() function introduced in Lecture 17, add + support for command-line options to your mywc utility from Task-3. +* Add command-line options to request: + + -c to report the number of characters, + -l to report the number of lines, + -L to report the maximum line length (as supported on Linux, but not macOS), + and + -w to report the number of words. + +* Ensure that your program still works with meaningful defaults if no options + are provided on the command-line. +* Ensure that your program checks that the provided command-line arguments are + sensible - if they are not, issue an appropriate error message to stderr after + all options have been processed. +*/ + +#include +#include +#include +#include + +void counter_stats(FILE *file, int *lines, int *words, int *characters) +{ + char buffer[BUFSIZ]; + // PROCESS IN CHUNKS + bool word = false; + while ( fgets(buffer, BUFSIZ, file) != NULL ) { + char *c = &buffer[0]; + while ( *c != '\0' ) { + // COUNT WORDS + if (word && isspace(*c)) { + (*words)++; + word = false; + } else if (!word) { + word = true; + } + // COUNT CHARACTERS + (*characters)++; + // COUNT NEWLINES + if ( *c == '\n' ) { + (*lines)++; + } + c++; + } + } +} + +// NUMBER OF LINES IN A FILE. +void counter(char *path, int *lines, int *words, int *characters) +{ + FILE *file; + if (path == NULL) { + file = stdin; + } else { + file = fopen(path, "r"); + } + + if (file != NULL) { + counter_stats(file, lines, words, characters); + } else { + fprintf(stderr, "Error reading file.\n"); + exit(EXIT_FAILURE); + } + +} + +int main(int argc, char *argv[]) +{ + char *file = argv[1]; + // CHECK IF WE ARE ACCEPTING INPUT FROM A PIPE. + if (argc > 2) { + fprintf(stderr, "Usage: mywc [FILE]\n"); + exit(EXIT_FAILURE); + } + + int lines; + int words; + int characters; + counter(argv[1], &lines, &words, &characters); + printf( "LINES %d, WORDS %d, CHARACTERS %d\n", + lines, words, characters ); + return 0; +} \ No newline at end of file diff --git a/Week 9/3_mywc/unix-1969-1971.txt b/Week 9/3_mywc/unix-1969-1971.txt new file mode 100644 index 0000000..18c3074 --- /dev/null +++ b/Week 9/3_mywc/unix-1969-1971.txt @@ -0,0 +1,30 @@ +Unix was born in 1969 out of the mind of a computer scientist at Bell +Laboratories, Ken Thompson. Thompson had been a researcher on the Multics +project, an experience which spoiled him for the primitive batch computing +that was the rule almost everywhere else. But the concept of timesharing +was still a novel one in the late 1960s; the first speculations on it had +been uttered barely ten years earlier by computer scientist John McCarthy +(also the inventor of the Lisp language), the first actual deployment had +been in 1962, seven years earlier, and timesharing operating systems were +still experimental and temperamental beasts. + +Computer hardware was at that time more primitive than even people who +were there to see it can now easily recall. The most powerful machines +of the day had less computing power and internal memory than a typical +cellphone of today. Video display terminals were in their infancy +and would not be widely deployed for another six years. The standard +interactive device on the earliest timesharing systems was the ASR-33 +teletype - a slow, noisy device that printed upper-case-only on big +rolls of yellow paper. The ASR-33 was the natural parent of the Unix +tradition of terse commands and sparse responses. + +When Bell Labs withdrew from the Multics research consortium, Ken +Thompson was left with some Multics-inspired ideas about how to build a +file system. He was also left without a machine on which to play a game +he had written called Space Travel, a science-fiction simulation that +involved navigating a rocket through the solar system. Unix began its +life on a scavenged PDP-7 minicomputer, as a platform for the Space Travel +game and a testbed for Thompson's ideas about operating system design. + +[adapted from https://www.catb.org/esr/writings/taoup/html/ch02s01.html] +