Skip to content
Snippets Groups Projects
Commit 19bbd273 authored by Adorn Binoy's avatar Adorn Binoy
Browse files

First homework assignment

parent 67ed2676
Branches
No related tags found
No related merge requests found
# Compiler settings
CC = gcc
CFLAGS = -Wall -Wextra -g
# Target executable name
TARGET = stringfun
# Default target
all: $(TARGET)
# Compile source to executable
$(TARGET): stringfun.c
$(CC) $(CFLAGS) -o $(TARGET) $^
# Clean up build files
clean:
rm -f $(TARGET)
# Phony targets
.PHONY: all clean
\ No newline at end of file
File added
#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);
// Implementatino of the setup buff as per the directions
int setup_buff(char *buff, char *user_str, int len) {
char *dest = buff; // Pointer to the buffer
char prevChar = ' '; // Tracks previous character for spaces
int charCounter = 0; // Number of characters processed
// Null check for input string
if (user_str == NULL) {
printf("Error: Null input string.\n");
return -1;
}
// Process user_str and fill the buffer
while (*user_str != '\0' && charCounter < len) {
// Copy every nonwhite space character into internal buffer and updates variables
if (*user_str != ' ' && *user_str != '\t') {
*dest = *user_str;
dest++;
charCounter++;
prevChar = *user_str;
}
// Checks for double space and replaces with single space
else if (prevChar != ' ') {
*dest = ' ';
dest++;
charCounter++;
prevChar = ' ';
}
// Increments input pointer to next memory location
user_str++;
}
// Checks for the user supplied string being too large (end never reached)
if (*user_str != '\0') {
printf("Error: Provided input string is too long.\n");
return -1;
}
// Fill the rest of the buffer with dots
while (charCounter < len) {
*dest = '.';
dest++;
charCounter++;
}
// If no errors, return the length of the user supplied string
return charCounter;
}
// Function to print the entire buffer
void print_buff(char *buff, int len) {
printf("Buffer: %.*s\n", len, buff);
}
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) {
// Exit if string length greater than the buffer length
if (str_len > len) {
return -1;
}
// Exit if the string is empty
if (str_len == 0) {
return 0;
}
int totalWords = 0;
// Loops through the buffer and updates totalWords based on current/previous character being a space
for (int i = 0; i < str_len; i++) {
if (buff[i] != ' ' && (i == 0 || buff[i - 1] == ' ')) {
totalWords++;
}
}
return totalWords;
}
void reverseString(char *buff, int str_len) {
int actual_length = 0;
for (int i = 0; i < str_len; i++) {
if (buff[i] != '.') {
actual_length++;
} else {
break;
}
}
char *start = buff;
char *end = buff + actual_length - 1;
while (start < end) {
char temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
void printWords(char *buff, int str_len) {
int wordCounter = 0; // Index of the word from the input
int startIndex = -1; // Tracks the start index of the word
printf("Word Print\n----------\n");
for (int i = 0; i < str_len; i++) {
// Ignore dots explicitly
if (buff[i] == '.') {
if (startIndex != -1) {
// Print the current word and reset startIndex
printf("%.*s (%d)\n", i - startIndex, &buff[startIndex], i - startIndex);
startIndex = -1;
}
continue;
}
// Start of a new word. Keep track of characters.
if (buff[i] != ' ') {
if (startIndex == -1) {
wordCounter++;
startIndex = i;
printf("%d. ", wordCounter);
}
}
// End of the current word. Reset the index for the next word
else if (startIndex != -1) {
printf("%.*s (%d)\n", i - startIndex, &buff[startIndex], i - startIndex);
startIndex = -1;
}
}
// Prints output for the last word if it exists.
if (startIndex != -1) {
printf("%.*s (%d)\n", str_len - startIndex, &buff[startIndex], str_len - startIndex);
}
}
int main(int argc, char *argv[]) {
char *buff; // Placeholder 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?
// This code is safe because the first part of the if statement checks to make ensure if there are enough arguments.
// So if this first test fails, the second part won't be ran and the code exits.
if ((argc < 2) || (*argv[1] != '-')) {
printf("Error: Invalid option. Please try again.\n");
usage(argv[0]);
exit(1);
}
opt = (char)*(argv[1] + 1);
// handle the help flag and then exit normally
if (opt == 'h') {
usage(argv[0]);
exit(0);
}
//TODO: #2 Document the purpose of the if statement below
// This if statement makes sure that an input string is provided along with the command.
if (argc < 3) {
printf("Error: Missing input string.\n");
usage(argv[0]);
exit(1);
}
input_string = argv[2];
//TODO: #3 Allocate space for the buffer using malloc and
// handle error if malloc fails by exiting with a
// return code of 99 and prints error message.
buff = (char *)malloc(BUFFER_SZ * sizeof(char));
if (buff == NULL) {
printf("Error: Memory allocation failed.\n");
exit(99);
}
// Initialize buffer
for (int i = 0; i < BUFFER_SZ; i++) {
buff[i] = '.';
}
user_str_len = setup_buff(buff, input_string, BUFFER_SZ);
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;
case 'r':
reverseString(buff, user_str_len);
// Print the reversed string without the dots
printf("Reversed String: ");
for (int i = 0; i < user_str_len; i++) {
if (buff[i] != '.') {
putchar(buff[i]);
}
}
printf("\n");
break;
case 'w':
printWords(buff, user_str_len);
break;
case 'x':
if (argc < 5) {
printf("Error: Not enough arguments\n");
exit(1);
}
printf("Not implemented!\n");
break;
//TODO: #5 Implement the other cases for 'r' and 'w' by extending
// the case statement options
default:
usage(argv[0]);
exit(1);
}
//TODO: #6 Don't 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 the length makes it so that buffer overflows can be prevented since the helper functions are not aware of the size that is defined in main.
// It reduces risk and makes the code more future proof in case the size of the buffer changes.
// Finally, it makes the code more readable as the function's purpose can be observed easier and clearly.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment