#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.