Skip to content
Snippets Groups Projects
Select Git revision
  • 141ec1c72c907ab0a28e6fc30239ae6c09863253
  • main default
2 results

stringfun

Blame
  • stringfun.c 15.96 KiB
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdbool.h>
    
    
    #define BUFFER_SZ 50
    #define SPACE_CHAR ' '
    
    //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
    void reverse_string(char *, int);
    void word_print(char *, int);
    int string_replace(char *, int, char *, char *);
    int my_strlen(char *); // Custom strlen function
    
    void usage(char *exename){
        printf("usage: %s [-h|c|r|w|x] \"string\" [other args]\n", exename);
        
       //printf("Options:\n");
       // printf("  -h : Display help information\n");
       // printf("  -c : Count words in the string\n");
       // printf("  -r : Reverse the string\n");
       // printf("  -w : Print words and their lengths\n");
       // printf("  -x : Replace a word in the string\n");
    
    }
    
    // Custom implementation of strlen() to calculate string length
    int my_strlen(char *str) {
        int length = 0;    // Initialize length counter
        while (*(str + length) != '\0') { // Loop until null terminator is reached
            length++;    // Increment length for each character
        }
        return length;   // Return the calculated length
    }
    
    int setup_buff(char *buff, char *user_str, int len){
        //TODO: #4:  Implement the setup buff as per the directions
        int user_str_len = 0;   // Tracks the length of the user string
        int buff_idx = 0;     // Index for the buffer
        bool space_pending = false;    // Tracks whether a space is waiting to be added
        char *p = user_str;  // Pointer to iterate through the user string
    
        bool started = false;   // Tracks if non-space characters have been encountered
    
      //  if (my_strlen(user_str) > len) {
         //   return -1;   // Input string is too large for the buffer
       // }
    
      //  if (my_strlen(user_str) == 0) {
       //     return -2; // Return error for empty input
      //  }
        
    
        // Process each character in the input string
        while (*p != '\0') {
            if (*p == SPACE_CHAR || *p == '\t') { // Check for whitespace
                if (started && !space_pending && buff_idx < len) { // Add a single space only once
                    //if (buff_idx < len) {
                    *(buff + buff_idx++) = SPACE_CHAR;
                    user_str_len++;
                    space_pending = true;   // Set space pending to true
                  //  }
                }
            } else { 
               // break;
                // Non-whitespace character
                
                if (buff_idx <= len) {
                    *(buff + buff_idx++) = *p;  // Copy the character to the buffer
                    user_str_len++;
                    space_pending = false;   // Reset space pending for new word
                    started = true; 
                } else {
                    break;
                }
                
            }
            p++; // Move to the next character
        }
        // Remove the trailing space if any
        if (buff_idx > 0 && *(buff + buff_idx - 1) == SPACE_CHAR) {
            buff_idx--;
            user_str_len--;
        }
    
        // Fill the remaining buffer space with dots
        while (buff_idx < len) {
            *(buff + buff_idx++) = '.';
        }
    
        return (user_str_len > len) ? -1 : user_str_len; // Return length or error
    
        //return 0; //for now just so the code compiles. 
    }
    
    void print_buff(char *buff, int len){
        printf("Buffer:  [");
        for (int i=0; i<len; i++){
            putchar(*(buff+i));
        }
        printf("]\n");
    }
    
    
    
    int count_words(char *buff, int len, int str_len){
        //YOU MUST IMPLEMENT
    
        // Ensure that str_len does not exceed len to avoid buffer overflows
        if (str_len > len) {
            printf("Error: String length exceeds buffer length.\n");
            return -1; // Return error code for invalid input
        }
    
        int wc = 0;   // Word count
        bool word_start = false;  // Tracks if we are at the start of a word
    
        for (int i = 0; i < str_len; i++) {
            char current = *(buff + i);   // Get the current character
    
            //if (current == '.') {
                // Stop counting at the first dot
              //  break;
           // }
    
            if (!word_start && current != SPACE_CHAR) {
                wc++; // Increment word count at word start
                word_start = true; // Mark that we are in a word
            } else if (current == SPACE_CHAR) {
                word_start = false; // End the current word
            }
        }
    
        return wc; // Return the total word count
    
    
       // return 0;
    }
    
    //ADD OTHER HELPER FUNCTIONS HERE FOR OTHER REQUIRED PROGRAM OPTIONS
    
    // Reverse the string in the buffer in place
    void reverse_string(char *buff, int str_len) {
        int start = 0;   // Start index
        int end = str_len - 1;  // End index
    
        while (start < end) {  // Swap until the indices meet
            char temp = *(buff + start);   // Temporary storage for start character
            *(buff + start) = *(buff + end); // Swap start and end characters
            *(buff + end) = temp;
    
            start++;  // Move start index forward
            end--;  // Move end index backward
        }
    }
    
    
    
    void word_print(char *buff, int str_len) {
        int word_count = 0;  // Total word count
        int char_count = 0;  // Length of the current word
        bool at_start = true; // Tracks if at the start of a new word
    
        printf("Word Print\n");
        printf("----------\n");
    
    
        // Loop through the buffer for the string length
        for (int i = 0; i < str_len; i++) {
            char current = *(buff + i); // Get the current character
    
            if (at_start && current != SPACE_CHAR) {
                // New word found
                word_count++;
                printf("%d. ", word_count); // Print word index
                at_start = false;  // Mark that we are processing a word
            }
    
            if (current == SPACE_CHAR) {
                // End of a word
                if (char_count > 0) {
                    printf("(%d)\n", char_count); // Print word length
                    char_count = 0;  // Reset character count for next word
                    at_start = true; // Mark start of a new word
                }
            } else {
                if (*(buff + i) == '.') {
                    // Stop processing at the first dot (truncated word)
                    break;
                }
                putchar(current); // Print the current character
                char_count++;  // Increment the character count
            }
        }
    
        // Handle the last word (if it doesn't end with a space)
        if (char_count > 0) {
            printf("(%d)\n", char_count); // Print the length of the last word
        }
    
        printf("\nNumber of words returned: %d\n", word_count);
    }
    
    /*
    
    int string_replace(char *buff, int len, char *find, char *replace) {
        char *match = strstr(buff, find);  // Find the first occurrence of the substring
        if (!match) {
            return -2; // Substring not found
        }
    
        int find_len = my_strlen(find);       // Length of the substring to find
        int replace_len = my_strlen(replace); // Length of the replacement string
        int buff_len = my_strlen(buff);       // Current length of the buffer content
    
        // Calculate the new length after replacement
        int new_len = buff_len - find_len + replace_len;
    
        if (new_len > len) {
            // Truncate the replacement string to fit within the buffer
            replace_len -= (new_len - len);
            new_len = len; // Adjust the final buffer length
        }
    
        // Check if truncation has shortened the replacement string to zero
        if (replace_len <= 0) {
            return -1; // Error: Replacement string cannot fit in the buffer
        }
    
        // Move characters after the match to accommodate the replacement
        memmove(match + replace_len,            // Destination: shift characters after replacement
                match + find_len,               // Source: characters after the found substring
                buff_len - (match - buff) - find_len); // Number of characters to move
    
        // Copy the (possibly truncated) replacement string into the buffer
        memcpy(match, replace, replace_len);
    
        // Fill the remaining buffer with dots
        for (int i = new_len; i < len; i++) {
            buff[i] = '.'; // Fill unused space with dots
        }
    
        // Ensure null-termination for safety
        buff[len] = '\0';
    
        return 0; // Success
    }
    */
    
    int string_replace(char *buff, int len, char *find, char *replace) {
        char *match = strstr(buff, find);  // Locate the substring to replace
        if (!match) {
            return -2; // Substring not found
        }
    
        int find_len = my_strlen(find);       // Length of the substring to find
        int replace_len = my_strlen(replace); // Length of the replacement string
        int buff_len = my_strlen(buff);       // Current length of the buffer content
    
        // Calculate the new length after replacement
        int new_len = buff_len - find_len + replace_len;
    
        // Allocate a temporary buffer
        char *temp_buff = (char *)malloc(len);
        if (!temp_buff) {
            return -3; // Memory allocation failed
        }
    
        // Copy everything up to the match into the temporary buffer
        int prefix_len = match - buff;
        strncpy(temp_buff, buff, prefix_len);
    
        // Append the (possibly truncated) replacement string to the temporary buffer
        if (replace_len + prefix_len > len) {
            replace_len = len - prefix_len; // Truncate the replacement string if necessary
        }
        strncpy(temp_buff + prefix_len, replace, replace_len);
    
        // Append the remainder of the original buffer after the match
        int suffix_start = prefix_len + find_len;
        int remaining_space = len - (prefix_len + replace_len);
        if (remaining_space > 0) {
            strncpy(temp_buff + prefix_len + replace_len, buff + suffix_start, remaining_space);
        }
    
        // Ensure the result is properly padded with dots
        for (int i = new_len; i < len; i++) {
            temp_buff[i] = '.';
        }
    
        // Copy the result back to the original buffer
        memcpy(buff, temp_buff, len);
    
        // Free the temporary buffer
        free(temp_buff);
    
        return 0; // Success
    }
    
    
    
    
    
    
    
    
    
    int main(int argc, char *argv[]){
    
        char *buff; // = malloc(BUFFER_SZ);      // Allocate buffer             //placehoder for the internal buffer
        
        
        //if (!buff) {
           // printf("Error: Memory allocation failed.\n");
          //  exit(99);                        // Exit on memory allocation failure
        //}
    
    
        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 check ensures that the program has received the correct number of arguments.
        - If argc < 2, argv[1] does not exist, so the program safely exits with usage instructions.
        - The second condition (*argv[1] != '-') ensures that the first argument provided
        starts with a dash ('-'), as required by the command-line syntax.
        By combining these checks, 
        we prevent undefined behavior from accessing argv[1] 
        when it does not exist.
        */
        
    
    
        if ((argc < 2) || (*argv[1] != '-')){
            usage(argv[0]);
           // free(buff);
            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]);
          //  free(buff);
            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 check ensures that the user provides the required arguments:
        - argc < 3 indicates that the user has not supplied both an option (argv[1])
        and the input string (argv[2]).
        - If this condition is 'true', the program prints usage instructions
        and exits with an error code of 1 to indicate invalid usage.
        This prevents undefined behavior caused by accessing missing arguments.
        */
        
    
    
    
    
        if (argc < 3){
            usage(argv[0]);
         //   free(buff);
            exit(1);
        }
    
    
    
        input_string = argv[2]; //capture the user input string
        //user_str_len = setup_buff(buff, input_string, BUFFER_SZ);
    
        //TODO:  #3 Allocate space for the buffer using malloc and
        //          handle error if malloc fails by exiting with a 
        //          return code of 99
        // CODE GOES HERE FOR #3
    
        // Allocate space for the buffer
        buff = (char *) malloc(BUFFER_SZ);
        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);
            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);
                print_buff(buff, BUFFER_SZ);
                break;
            }
    
    
            //TODO:  #5 Implement the other cases for 'r' and 'w' by extending
            //       the case statement options
            case 'r':{
                reverse_string(buff, user_str_len);
               // printf("Reversed String: %.*s\n", user_str_len, buff);
                print_buff(buff,BUFFER_SZ);
                break;
            }
    
            case 'w':{
                //printf("Word Print\n----------\n");
                word_print(buff, user_str_len);
                print_buff(buff,BUFFER_SZ);
                break;
            }
            
            case 'x': {
                if (argc < 5) {
                //printf("Not Implemented!\n");
                free(buff);
                exit(1);
            }
    
            char *find = argv[3];
            char *replace = argv[4];
    
            rc = string_replace(buff, BUFFER_SZ, find, replace);
            if (rc == -2) {
               // printf("Not Implemented!\n");
                free(buff);
                exit(3);
            } else if (rc == -1) {
               // printf("Not Implemented!\n");
                free(buff);
                exit(3);
            }
    
            // Print the buffer
            printf("Buffer:  [");
            for (int i = 0; i < BUFFER_SZ; i++) {
                putchar(buff[i]);
            }
            printf("]\n");
    
            //printf("Not Implemented!\n");
            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);
        return 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
    
    /*
    Passing both the buffer pointer and its length to the helper functions 
    is considered good practice for several reasons:
    
    1. Code Resuable:
        The functions become more general and reusable. They are not tied 
        to a specific buffer size, allowing them to work with buffers 
        of different sizes in other programs or contexts.
    
    2. Flexibility:
        If the buffer size (BUFFER_SZ) changes in the future, the helper 
        functions do not need to be modified. The updated size is passed 
        automatically, making the code easier to maintain.
    
    3. Safety:
        Explicitly passing the length ensures that the helper functions 
        do not read or write beyond the allocated memory. This prevents 
        buffer overflows, which could lead to undefined behavior or 
        security vulnerabilities.
    
    4. Error Checking:
        Having the buffer length allows the helper functions to validate 
        inputs (e.g., ensuring the string length does not exceed the buffer 
        size). This improves the program's robustness by catching errors early.
    
    5. Readable:
        Including the buffer length makes it clear that the function 
        operates on a fixed-size buffer and enforces constraints. 
        It also serves as documentation for the function's behavior 
        and expected input.
    */