Skip to content
Snippets Groups Projects
Commit 0dc6b073 authored by vht24's avatar vht24
Browse files

Upload assignment

parent e074d371
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);
//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.
\ No newline at end of file
#!/usr/bin/env bats
@test "no args shows usage" {
run ./stringfun
[ "$status" -eq 1 ]
[ "${lines[0]}" = "usage: ./stringfun [-h|c|r|w|x] \"string\" [other args]" ]
}
@test "bad args shows usage" {
run ./stringfun -z "Bad arg usage"
[ "$status" -eq 1 ]
[ "${lines[0]}" = "usage: ./stringfun [-h|c|r|w|x] \"string\" [other args]" ]
}
@test "check -h" {
run ./stringfun -h
[ "$status" -eq 0 ]
[ "${lines[0]}" = "usage: ./stringfun [-h|c|r|w|x] \"string\" [other args]" ]
}
@test "wordcount" {
run ./stringfun -c "There should be eight words in this sentence"
[ "$status" -eq 0 ]
[ "$output" = "Word Count: 8
Buffer: [There should be eight words in this sentence......]" ]
}
@test "remove extra spaces" {
run ./stringfun -c " The strange spaces should be removed from this "
[ "$status" -eq 0 ]
[ "$output" = "Word Count: 8
Buffer: [The strange spaces should be removed from this....]" ]
}
@test "reverse" {
run ./stringfun -r "Reversed sentences look very weird"
[ "$status" -eq 0 ]
[ "$output" = "Buffer: [driew yrev kool secnetnes desreveR................]" ]
}
@test "print words" {
run ./stringfun -w "Lets get a lot of words to test"
[ "$status" -eq 0 ]
[ "$output" = "Word Print
----------
1. Lets(4)
2. get(3)
3. a(1)
4. lot(3)
5. of(2)
6. words(5)
7. to(2)
8. test(4)
Number of words returned: 8
Buffer: [Lets get a lot of words to test...................]" ]
}
@test "check max length" {
run ./stringfun -r "This is the maximum length string that should work"
[ "$status" -eq 0 ]
[ "$output" = "Buffer: [krow dluohs taht gnirts htgnel mumixam eht si sihT]" ]
}
@test "check over max length" {
run ./stringfun -w "This is a string that does not work as it is too long"
[ "$status" -ne 0 ]
}
@test "basic string search replace" {
run ./stringfun -x "This is a bad test" bad great
[ "$output" = "Buffer: [This is a great test..............................]" ] ||
[ "$output" = "Not Implemented!" ]
}
@test "search replace not found" {
run ./stringfun -x "This is a a long string for testing" bad great
[ "$status" -ne 0 ] ||
[ "$output" = "Not Implemented!" ]
}
@test "basic overflow search replace" {
run ./stringfun -x "This is a super long string for testing my program" testing validating
[ "$output" = "Buffer: [This is a super long string for validating my prog]" ] ||
[ "$output" = "Not Implemented!" ]
}
@test "test overflow string replace" {
run ./stringfun -x "This is a super long string for testing my program" testing validating
[ "$output" = "Buffer: [This is a super long string for validating my prog]" ] ||
[ "$output" = "Not Implemented!" ]
}
@test "test shorter string replace" {
run ./stringfun -x "This is a super long string for testing my program" program app
[ "$output" = "Buffer: [This is a super long string for testing my app....]" ] ||
[ "$output" = "Not Implemented!" ]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment