diff --git a/1-C-Refresher/stringfun.c b/1-C-Refresher/stringfun.c new file mode 100644 index 0000000000000000000000000000000000000000..72763ba635b2322f6376ac9f9a604b5927eca406 --- /dev/null +++ b/1-C-Refresher/stringfun.c @@ -0,0 +1,333 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> + +#define BUFFER_SZ 50 + +//prototypes +void usage(char *); +void print_buff(char *, int); +int setup_buff(char *, char *, int); + +//prototypes for functions to handle required functionality +int count_words(char *, int, int); +//add additional prototypes here +int reverse_str(char*, int, int); +int find_word(char *, char*); +int replace_word(char*, char*, char*, int, int); +int print_words(char*, int, int); + +int setup_buff(char *buff, char *user_str, int len){ + //TODO: #4: Implement the setup buff as per the directions + if (len > BUFFER_SZ) return -1; + + int str_len = 0; + char *temp = buff; + + while (*user_str == ' ') buff++; // skip leading spaces + + while (*user_str != '\0' && str_len < len - 1) { + if (*user_str == ' ' && (*(user_str + 1) == ' ' || *(user_str + 1) == '\0')) { + user_str++; + continue; + } + + *temp = *user_str; + temp++; + user_str++; + str_len++; + } + + *temp = '\0'; + + return str_len; + +} + +void print_buff(char *buff, int len){ + printf("Buffer: "); + for (int i=0; i<len; i++){ + putchar(*(buff+i)); + } + putchar('\n'); +} + +void usage(char *exename){ + printf("usage: %s [-h|c|r|w|x] \"string\" [other args]\n", exename); +} + +int count_words(char *buff, int len, int str_len){ + if (len < str_len || buff == NULL) return -1; + + int ctr = 0; + bool counting = false; + + while (*buff != '\0') { + if (*buff != ' ' && !counting) { + counting = true; + ctr++; + } else if (*buff == ' ' && counting) counting = false; + + buff++; + } + return ctr; +} + +//ADD OTHER HELPER FUNCTIONS HERE FOR OTHER REQUIRED PROGRAM OPTIONS +int reverse_str(char *buff, int buff_len, int str_len) { + if (buff_len == 0) { + printf("Empty String"); + return -1; + } + + char *end = buff; + + while (*end != '\0') end++; + end--; + + char *rev_str = malloc(sizeof(char) * (str_len + 1)); + if (rev_str == NULL) { + printf("Memory allocation error\n"); + return -1; + } + + char *temp = rev_str; + while(end >= buff) { + *temp = *end; + temp++; + end--; + } + + *temp = '\0'; + + printf("Reversed String: %s\n", rev_str); + free(rev_str); + return 0; + +} + +int find_word(char *buff, char *word) { + const char *b = buff; + const char *w = word; + + int index = 0; // to track the starting index of the word + + while (*b != '\0') { + if (*b == *w) { + const char *b_temp = b; + const char *w_temp = w; + + while (*w_temp != '\0' && *b_temp == *w_temp) { + b_temp++; + w_temp++; + } + + if (*w_temp == '\0') { + return index; + } + } + + b++; + index++; + } + + return -1; // word not found +} + +int replace_word(char *buff, char *word, char *replacement, int buff_len, int str_len) { + if (str_len > buff_len || str_len == 0) return -2; // invalid input + + int index = find_word(buff, word); + if (index == -1) return -1; // word doesn't exist in string + + int word_len = 0; + while (word[word_len] != '\0') word_len++; + + int replacement_len = 0; + while (replacement[replacement_len] != '\0') replacement_len++; + + if (str_len - word_len + replacement_len > buff_len) return -3; // replcaement exceeds buffer size + + if (replacement_len != word_len) { + char *end = buff + str_len; + char *src = buff + index + word_len; + char *dest = buff + index + replacement_len; + + if (replacement_len > word_len) { + while (end >= src) { + *(dest--) = *(end--); + } + } else { + while (*src != '\0') { + *(dest++) = *(src++); + } + *dest = '\0'; + } + } + + char *replace_ptr = buff + index; + const char *replacement_ptr = replacement; + + while (*replacement_ptr != '\0') *replace_ptr++ = *replacement_ptr++; + + printf("Modified String: %s\n", buff); + return 0; + +} + +int print_words(char *buff, int buff_len, int str_len) { + if (str_len > buff_len) return -1; + if (str_len == 0) return 0; + + int word_count = 0; // to start + int char_ctr = 0; // length of the current word + bool at_start = true; // if we are currently counting words + + printf("Word Print\n----------\n"); + + while (*buff != '\0') { + if (*buff != ' ' && at_start) { + word_count++; + at_start = false; // we are processing a new word + printf("%d. ", word_count); + } + + if (*buff == ' ' && !at_start) { // we hit the end of the word + printf(" (%d)\n", char_ctr); // length of the current word + char_ctr = 0; // start count for the next word + at_start = true; // we are starting a new word + } else { // we are just encountering a normal character + printf("%c", *buff); // current character + char_ctr++; // add to length of current string + } + buff++; + } + if (!at_start) printf(" (%d)\n", char_ctr); // this is for the last word + return word_count; // hold total number of words +} + + +int main(int argc, char *argv[]){ + + char *buff; //placehoder for the internal buffer + char *input_string; //holds the string provided by the user on cmd line + char opt; //used to capture user option from cmd line + int rc; //used for return codes + int user_str_len; //length of user supplied string + + //TODO: #1. WHY IS THIS SAFE, aka what if argv[1] does not exist? + /* + Since argv[0] is the name of the executable, argv[1] is the first parameter used as input. + If argv[1] doesn't exist, then the program could run into problems. The code below prevents those. + */ + if ((argc < 2) || (*argv[1] != '-')){ + usage(argv[0]); + exit(1); + } + + opt = (char)*(argv[1]+1); //get the option flag + + //handle the help flag and then exit normally + if (opt == 'h'){ + usage(argv[0]); + exit(0); + } + + //WE NOW WILL HANDLE THE REQUIRED OPERATIONS + + //TODO: #2 Document the purpose of the if statement below + /* + If the number of arguments is less than 3 (including the executable's name), + print the usage of the program (the possible flags and input), then exit. + */ + if (argc < 3){ + usage(argv[0]); + exit(1); + } + + input_string = argv[2]; //capture the user input string + + char *choice; + char *replacement; + if (opt == 'x') { + if (argc == 5) { + choice = argv[3]; + replacement = argv[4]; + } else { + printf("Error: invalid number of arguments"); + exit(2); + } + } + + //TODO: #3 Allocate space for the buffer using malloc and + // handle error if malloc fails by exiting with a + // return code of 99 + buff = malloc(sizeof(char) * BUFFER_SZ); + if (buff == NULL) { + printf("Error setting up buffer"); + exit(99); + } + + user_str_len = setup_buff(buff, input_string, BUFFER_SZ); //see todos + if (user_str_len < 0){ + printf("Error setting up buffer, error = %d", user_str_len); + exit(2); + } + + switch (opt){ + case 'c': + rc = count_words(buff, BUFFER_SZ, user_str_len); //you need to implement + if (rc < 0){ + printf("Error counting words, rc = %d", rc); + exit(2); + } + printf("Word Count: %d\n", rc); + break; + + //TODO: #5 Implement the other cases for 'r' and 'w' by extending + // the case statement options + case 'r': + rc = reverse_str(buff, BUFFER_SZ, user_str_len); + if (rc < 0) { + printf("Error reversing words, rc = %d", rc); + exit(2); + } + break; + case 'w': + rc = print_words(buff, BUFFER_SZ, user_str_len); + if (rc < 0) { + printf("Error printing words, rc = %d", rc); + exit(2); + } + break; + case 'x': + rc = replace_word(buff, choice, replacement, BUFFER_SZ, user_str_len); + if (rc < 0) { + printf("Error replacing words, rc = %d", rc); + exit(2); + } + break; + default: + usage(argv[0]); + exit(1); + } + + //TODO: #6 Dont forget to free your buffer before exiting + print_buff(buff,BUFFER_SZ); + free(buff); + exit(0); +} + +//TODO: #7 Notice all of the helper functions provided in the +// starter take both the buffer as well as the length. Why +// do you think providing both the pointer and the length +// is a good practice, after all we know from main() that +// the buff variable will have exactly 50 bytes? +// +// In a case where the global BUFFER_SZ variable is changed, +// you don't want the behavior of each of the functions to +// to be affacted negatively. Providing both the pointer to +// the buffer and the length is good so that the length is +// never in question for any individual function. +