Skip to content
Snippets Groups Projects
Select Git revision
  • d91207624062184c45d75951638655b240311f18
  • master default
2 results

stringfun.c

Blame
  • user avatar
    vht24 authored
    0dc6b073
    History
    stringfun.c 9.70 KiB
    #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);
    
    //prototypes for functions to handle required functionality
    int  count_words(char *, int, int);
    //add additional prototypes here
    int reverse_string(char *, int);
    int print_words(char *, int);
    int replace_words(char *, const char *, const char *, int);
    
    int setup_buff(char *buff, char *user_str, int len){
        //TODO: #4:  Implement the setup buff as per the directions
        char *src = user_str;
        char *dest = buff;
        int count = 0, is_space = 0;
    
        // Skip leading spaces
        while (*src == ' ' || *src == '\t') {
            src++;
        }
    
        // Process the string
        while (*src != '\0' && count < len) {
            // Check for space
            if (*src == ' ' || *src == '\t') {
                if (!is_space) {  // Avoid consecutive spaces
                    *dest++ = ' ';
                    count++;
                    is_space = 1;
                }
            } else {
                *dest++ = *src;
                count++;
                is_space = 0;
            }
            src++;
        }
    
        // Trim trailing space
        if (count > 0 && *(dest - 1) == ' ') {
            dest--;
            count--;
        }
    
        if (*src != '\0') return -1;  // Input exceeds buffer size
    
        // Fill remaining buffer with dots
        while (count < len) {
            *dest++ = '.';
            count++;
        }
    
        return count;
    }
    
    void print_buff(char *buff, int len){
        printf("Buffer:  ");
        putchar('[');
        for (int i=0; i<len; i++){
            putchar(*(buff+i));
        }
        putchar(']');
        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 == NULL || len <= 0 || str_len <= 0) {
            return -1; // Error
        }
    
        int word_count = 0;
    
        for (int i = 0; i < str_len; i++){
            if (*(buff + i) == ' '){
                word_count++;
            }
        }
    
        return word_count;
    }
    
    //ADD OTHER HELPER FUNCTIONS HERE FOR OTHER REQUIRED PROGRAM OPTIONS
    int reverse_string(char *buff, int str_len) {
        if (buff == NULL || str_len <= 0) {
            return -1; // Error
        }
    
        // Determine the actual length
        int actual_len = 0;
        while (actual_len < str_len && *(buff + actual_len) != '.') {
            actual_len++;
        }
    
        // Reverse string
        char *start = buff;
        char *end = buff + actual_len - 1;
        char temp;
    
        while (start < end) {
            temp = *start;
            *start = *end;
            *end = temp;
    
            start++;
            end--;
        }
    
        // Print the reversed string
        printf("Reversed String: ");
        for (int i = 0; i < actual_len; i++) {
            putchar(*(buff + i));
        }
        putchar('\n');
    
        return 0;
    }
    
    int print_words(char *buff, int str_len) {
        if (buff == NULL || str_len <= 0) {
            return -1; // Error
        }
    
        // Determine the actual length
        int actual_len = 0;
        while (actual_len < str_len && *(buff + actual_len) != '.') {
            actual_len++;
        }
    
        printf("Word Print\n----------\n");
        int word_start = 0, word_length = 0, word_count = 1;
    
        for (int i = 0; i <= actual_len; i++) {
            // Detect the end of a word
            if (i == actual_len || *(buff + i) == ' ') {
                if (word_length > 0) {
                    // Print the current word
                    printf("%d. ", word_count++);
                    for (int j = word_start; j < word_start + word_length; j++) {
                        putchar(*(buff + j));
                    }
                    printf(" (%d)\n", word_length);
                    word_length = 0;
                }
            } else {
                // Track the start of a word and its length
                if (word_length == 0) word_start = i;
                word_length++;
            }
        }
    
        return 0;
    }
    
    int replace_word(char *buff, const char *find, const char *replace, int len) {
        if (buff == NULL || find == NULL || replace == NULL || len <= 0) {
            return -1; // Error
        }
    
        char *start = buff;
        char *match = NULL;
        int find_len = 0, replace_len = 0;
    
        // Calculate lengths of `find` and `replace`
        while (*(find + find_len) != '\0') {
            find_len++;
        }
        while (*(replace + replace_len) != '\0') {
            replace_len++;
        }
    
        // Search for the word to replace
        while (*start != '\0' && start < buff + len) {
            if (*start == *find) { // Possible match found
                char *temp_buff = start;
                char *temp_find = (char *)find;
                while (*temp_buff == *temp_find && *temp_find != '\0') {
                    temp_buff++;
                    temp_find++;
                }
    
                if (*temp_find == '\0') { // Full match found
                    match = start;
                    break;
                }
            }
            start++;
        }
    
        if (match == NULL) {
            printf("Word not found: %s\n", find);
            return -1; // No replacement found
        }
    
        // If replacement makes the string too long, truncate
        int remaining_len = len - (match - buff);
        if (replace_len > find_len && replace_len - find_len > remaining_len) {
            printf("Error: Replacement would exceed buffer size\n");
            return -1; // Error
        }
    
        // Perform the replacement
        char *end_of_buff = buff + len; // To prevent buffer overflow
        char *src = match + find_len;        // Start of the rest of the string
        char *dest = match + replace_len;    // New position after replacement
    
        if (replace_len != find_len) {
            // Shift the remaining part of the string
            while (end_of_buff > dest && src < end_of_buff) {
                *(end_of_buff - 1) = *(src + (end_of_buff - dest - 1));
                end_of_buff--;
            }
        }
    
        // Copy the replacement word into the buffer
        for (int i = 0; i < replace_len; i++) {
            *(match + i) = *(replace + i);
        }
    
        printf("Modified String: ");
        for (int i = 0; i < len && *(buff + i) != '.'; i++) {
            putchar(*(buff + i));
        }
        putchar('\n');
    
        return 0;
    }
    
    
    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?
        // argc < 2, ensures that the program does not attempt to access 
        // argv[1] if no arguments are passed (avoiding out-of-bounds
        // memory access).
        // *argv[1] != '-', validates that the provided argument starts with a -,
        // ensuring it's an option flag.
    
        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
        // This ensures that the program does not proceed if the user has not 
        // provided the required input string, as any other operator other than
        // -h need string as input.
        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 * sizeof(char));
        if (!buff){
            printf("Error: Memory allocation failed\n");
            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);
            if (rc < 0) {
                printf("Error counting words, rc = %d\n", rc);
                free(buff);
                return -1;
            }
            printf("Word Count: %d\n", rc);
            break;
    
        case 'r':
            rc = reverse_string(buff, user_str_len);
            if (rc < 0) {
                printf("Error reversing string, rc = %d\n", rc);
                free(buff);
                return -1;
            }
            break;
    
        case 'w':
            rc = print_words(buff, user_str_len);
            if (rc < 0) {
                printf("Error printing words, rc = %d\n", rc);
                free(buff);
                return -1;
            }
            break;
        case 'x':
            if (argc < 5) {
                printf("Error: Missing arguments for -x. Usage: ./program -x \"string\" \"find\" \"replace\"\n");
                free(buff);
                return -1;
            }
    
            rc = replace_word(buff, argv[3], argv[4], BUFFER_SZ);
            if (rc < 0) {
                printf("Error replacing word\n");
                free(buff);
                return -1;
            }
            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?
    //  
    //          PLACE YOUR ANSWER HERE
    // Although we know buff has exactly 50 bytes, passing the length explicitly
    // Makes the function reusable with buffers of different sizes in other contexts.
    // Improves code readability by clarifying the buffer's size directly in the function call.