diff --git a/stringfun.c b/stringfun.c new file mode 100644 index 0000000000000000000000000000000000000000..c52442353febac6f4aa2259bb28bc967da63149d --- /dev/null +++ b/stringfun.c @@ -0,0 +1,424 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define BUFFER_SZ 50 + +// prototypes +void usage(char *); +void print_buff(char *, int); +int setup_buff(char *, char *, int); +int count_words(char *, int, int); +// add additional prototypes here +int reverse_string(char *, int, int); +int print_words(char *, int, int); +int replace_string(char *, char *, int, char *, char *); +int get_str_len(char *); + +// Helper function for string length +int get_str_len(char *str) +{ + char *ptr = str; + while (*ptr) + ptr++; + return ptr - str; +} + +int setup_buff(char *buff, char *user_str, int len) +{ + if (!buff || !user_str) + return -2; + + char *src = user_str; + char *dst = buff; + int count = 0; + int space_seen = 1; // Start true to handle leading spaces + + // Skip leading whitespace + while (*src == ' ' || *src == '\t') + src++; + + // Copy and process the string + while (*src != '\0') + { + if (count >= len) + return -1; + + if (*src == ' ' || *src == '\t') + { + if (!space_seen && *(src + 1) != '\0') + { + *dst++ = ' '; + count++; + space_seen = 1; + } + } + else + { + *dst++ = *src; + count++; + space_seen = 0; + } + src++; + } + + // Remove trailing space if it exists + if (count > 0 && *(dst - 1) == ' ') + { + dst--; + count--; + } + + // Pad with dots + while (count < len) + { + *dst++ = '.'; + count++; + } + + return count - (buff + len - dst); +} + +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 (!buff || str_len <= 0 || str_len > len) + return -1; + + char *ptr = buff; + int count = 0; + int in_word = 0; + + while (ptr < buff + str_len) + { + if (*ptr != ' ' && !in_word) + { + count++; + in_word = 1; + } + else if (*ptr == ' ') + { + in_word = 0; + } + ptr++; + } + + return count; +} + +// ADD OTHER HELPER FUNCTIONS HERE FOR OTHER REQUIRED PROGRAM OPTIONS +int reverse_string(char *buff, int len, int str_len) +{ + if (!buff || str_len <= 0 || str_len > len) + return -1; + + char *start = buff; + char *end = buff + str_len - 1; + char temp; + + while (start < end) + { + temp = *start; + *start = *end; + *end = temp; + start++; + end--; + } + + return 0; +} + +int print_words(char *buff, int len, int str_len) +{ + if (!buff || str_len <= 0 || str_len > len) + return -1; + + char *start = buff; + char *current = buff; + int word_count = 0; + int word_len; + + printf("Word Print\n----------\n"); + + while (current < buff + str_len) + { + while (current < buff + str_len && *current == ' ') + { + current++; + } + if (current >= buff + str_len) + break; + + start = current; + word_len = 0; + + while (current < buff + str_len && *current != ' ') + { + word_len++; + current++; + } + + if (word_len > 0) + { + word_count++; + printf("%d. ", word_count); + + char *temp = start; + while (temp < start + word_len) + { + putchar(*temp); + temp++; + } + + printf(" (%d)\n", word_len); + } + } + + printf("Number of words returned: %d\n", word_count); + return word_count; +} + +int replace_string(char *buff, int len, int str_len, char *search, char *replace) +{ + if (!buff || !search || !replace || str_len <= 0 || str_len > len) + return -1; + + char *current = buff; + char *end = buff + str_len; + int search_len = get_str_len(search); + int replace_len = get_str_len(replace); + + // Find search string + int found = 0; + while (current < end - search_len + 1) + { + char *s1 = current; + char *s2 = search; + int match = 1; + + for (int i = 0; i < search_len; i++) + { + if (*s1++ != *s2++) + { + match = 0; + break; + } + } + + if (match) + { + found = 1; + break; + } + current++; + } + + if (!found) + return -2; + + // Calculate new length + int new_len = str_len - search_len + replace_len; + if (new_len > len) + return -3; + + // Shift content if needed + if (replace_len != search_len) + { + char *src = current + search_len; + char *dst = current + replace_len; + + if (replace_len > search_len) + { + char *src_end = end - 1; + char *dst_end = src_end + (replace_len - search_len); + while (src_end >= src) + { + *dst_end-- = *src_end--; + } + } + else + { + while (src < end) + { + *dst++ = *src++; + } + } + } + + // Copy replacement + char *replace_ptr = replace; + while (*replace_ptr) + { + *current++ = *replace_ptr++; + } + + // Update dots + char *dot_start = buff + new_len; + while (dot_start < buff + len) + { + *dot_start++ = '.'; + } + + return new_len; +} + +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 arv[1] does not exist? + // PLACE A COMMENT BLOCK HERE EXPLAINING + /* This is safe because we check argc < 2 before accessing argv[1]. + * If there aren't enough arguments, we exit before attempting to + * access argv[1], preventing any buffer overrun. + */ + 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 + // PLACE A COMMENT BLOCK HERE EXPLAINING + /* This ensures we have enough arguments for all operations except help. + * Every operation needs at least program name, option, and input string, + * so we need at least 3 arguments. + */ + if (argc < 3) + { + usage(argv[0]); + exit(1); + } + + input_string = argv[2]; // capture the user input string + + // TODO: #3 Allocate space for the buffer using malloc and + // handle error if malloc fails by exiting with a + // return code of 99 + buff = (char *)malloc(BUFFER_SZ); + if (!buff) + { + 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); + free(buff); + 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); + free(buff); + exit(2); + } + printf("Word Count: %d\n", rc); + break; + + case 'r': + rc = reverse_string(buff, BUFFER_SZ, user_str_len); + if (rc < 0) + { + printf("Error reversing string\n"); + free(buff); + exit(3); + } + break; + + case 'w': + rc = print_words(buff, BUFFER_SZ, user_str_len); + if (rc < 0) + { + printf("Error printing words\n"); + free(buff); + exit(3); + } + break; + + case 'x': + if (argc != 5) + { + printf("Error: -x requires search and replace strings\n"); + free(buff); + exit(1); + } + rc = replace_string(buff, BUFFER_SZ, user_str_len, argv[3], argv[4]); + if (rc < 0) + { + switch (rc) + { + case -1: + printf("Error: Invalid parameters\n"); + break; + case -2: + printf("Error: Search string not found\n"); + break; + case -3: + printf("Error: Replacement would exceed buffer size\n"); + break; + } + free(buff); + exit(3); + } + break; + + default: + usage(argv[0]); + free(buff); + 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? +// +/* Passing both pointer and length is good practice because: + * 1. Prevents buffer overflows through bounds checking + * 2. Makes functions reusable with different buffer sizes + * 3. Follows principle of least privilege + * 4. Makes code more maintainable if buffer size changes + * 5. Helps catch programming errors through explicit length checks + */ \ No newline at end of file