#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.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); void reverse_string(char *, int); void word_print(char *, int, int); int setup_buff(char *buff, char *user_str, int len) { //TODO: #4: Implemented the setup buff as per the directions char *b_ptr = buff; char *u_ptr = user_str; int user_len = 0; bool last_was_space = false; while (*u_ptr != '\0') { if (user_len >= len) { return -1; // Error: Input string exceeds buffer size } if (*u_ptr == ' ' || *u_ptr == '\t') { if (!last_was_space) { *b_ptr = ' '; b_ptr++; user_len++; last_was_space = true; } } else { *b_ptr = *u_ptr; b_ptr++; user_len++; last_was_space = false; } u_ptr++; } while (user_len < len) { *b_ptr = '.'; b_ptr++; user_len++; } return user_len; } int count_words(char *buff, int len, int str_len) { if (str_len > len) { return -1; // Error: String length exceeds buffer size } int word_count = 0; bool at_start = true; for (int i = 0; i < str_len; i++) { char c = *(buff + i); if (at_start && c != ' ') { word_count++; at_start = false; } else if (c == ' ') { at_start = true; } } return word_count; } void reverse_string(char *buff, int str_len) { for (int i = 0, j = str_len - 1; i < j; i++, j--) { char temp = *(buff + i); *(buff + i) = *(buff + j); *(buff + j) = temp; } } void word_print(char *buff, int len, int str_len) { printf("Word Print\n----------\n"); int word_start = 0; for (int i = 0; i <= str_len; i++) { if (i == str_len || *(buff + i) == ' ') { printf("%.*s (%d)\n", i - word_start, buff + word_start, i - word_start); word_start = i + 1; } } } 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 main(int argc, char *argv[]) { char *buff; char *input_string; char opt; int rc; int user_str_len; // TODO: #1 Explain why this is safe // This ensures that argv[1] exists and starts with '-'. If not, the program exits. if ((argc < 2) || (*argv[1] != '-')) { usage(argv[0]); exit(1); } opt = (char) *(argv[1] + 1); // Get the option flag if (opt == 'h') { usage(argv[0]); exit(0); } // TODO: #2 Explain the purpose of this check // This ensures the user provided a string as the second argument (argv[2]). if (argc < 3) { usage(argv[0]); exit(1); } input_string = argv[2]; // Capture user input string // TODO: #3 Allocate buffer and handle memory allocation failure buff = (char *) malloc(BUFFER_SZ); if (buff == NULL) { printf("Error: Memory allocation failed.\n"); exit(99); } user_str_len = setup_buff(buff, input_string, BUFFER_SZ); if (user_str_len < 0) { printf("Error setting up buffer, error = %d\n", user_str_len); free(buff); exit(2); } switch (opt) { case 'c': // Word count rc = count_words(buff, BUFFER_SZ, user_str_len); if (rc < 0) { printf("Error counting words, rc = %d\n", rc); free(buff); exit(2); } printf("Word Count: %d\n", rc); break; //TODO: #5 Implemented the other cases for 'r' and 'w' by extending //the case statement options case 'r': // Reverse string reverse_string(buff, user_str_len); printf("Reversed String: %.*s\n", user_str_len, buff); break; case 'w': // Word print word_print(buff, BUFFER_SZ, user_str_len); break; default: usage(argv[0]); free(buff); exit(1); } // TODO: #6 Free the buffer before exiting free(buff); exit(0); } // TODO: #7 Answer // Providing both the pointer and length ensures safer processing, as functions // cannot assume strings are null-terminated or rely on external constraints. // This minimizes memory-related errors.