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

Upload assignment

parent e074d371
No related branches found
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