Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cs503zchen
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Ziheng Chen
cs503zchen
Commits
899171f1
Commit
899171f1
authored
5 months ago
by
Ziheng Chen
Browse files
Options
Downloads
Patches
Plain Diff
Upload New File
parent
bb61814c
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
w3-hw/stringfun.c
+527
-0
527 additions, 0 deletions
w3-hw/stringfun.c
with
527 additions
and
0 deletions
w3-hw/stringfun.c
0 → 100644
+
527
−
0
View file @
899171f1
#include
<stdio.h>
#include
<string.h>
#include
<stdlib.h>
#include
<stdbool.h>
#define BUFFER_SZ 50
#define SPACE_CHAR ' '
//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
void
reverse_string
(
char
*
,
int
);
void
word_print
(
char
*
,
int
);
int
string_replace
(
char
*
,
int
,
char
*
,
char
*
);
int
my_strlen
(
char
*
);
// Custom strlen function
void
usage
(
char
*
exename
){
printf
(
"usage: %s [-h|c|r|w|x]
\"
string
\"
[other args]
\n
"
,
exename
);
//printf("Options:\n");
// printf(" -h : Display help information\n");
// printf(" -c : Count words in the string\n");
// printf(" -r : Reverse the string\n");
// printf(" -w : Print words and their lengths\n");
// printf(" -x : Replace a word in the string\n");
}
// Custom implementation of strlen() to calculate string length
int
my_strlen
(
char
*
str
)
{
int
length
=
0
;
// Initialize length counter
while
(
*
(
str
+
length
)
!=
'\0'
)
{
// Loop until null terminator is reached
length
++
;
// Increment length for each character
}
return
length
;
// Return the calculated length
}
int
setup_buff
(
char
*
buff
,
char
*
user_str
,
int
len
){
//TODO: #4: Implement the setup buff as per the directions
int
user_str_len
=
0
;
// Tracks the length of the user string
int
buff_idx
=
0
;
// Index for the buffer
bool
space_pending
=
false
;
// Tracks whether a space is waiting to be added
char
*
p
=
user_str
;
// Pointer to iterate through the user string
bool
started
=
false
;
// Tracks if non-space characters have been encountered
// if (my_strlen(user_str) > len) {
// return -1; // Input string is too large for the buffer
// }
// if (my_strlen(user_str) == 0) {
// return -2; // Return error for empty input
// }
// Process each character in the input string
while
(
*
p
!=
'\0'
)
{
if
(
*
p
==
SPACE_CHAR
||
*
p
==
'\t'
)
{
// Check for whitespace
if
(
started
&&
!
space_pending
&&
buff_idx
<
len
)
{
// Add a single space only once
//if (buff_idx < len) {
*
(
buff
+
buff_idx
++
)
=
SPACE_CHAR
;
user_str_len
++
;
space_pending
=
true
;
// Set space pending to true
// }
}
}
else
{
// break;
// Non-whitespace character
if
(
buff_idx
<=
len
)
{
*
(
buff
+
buff_idx
++
)
=
*
p
;
// Copy the character to the buffer
user_str_len
++
;
space_pending
=
false
;
// Reset space pending for new word
started
=
true
;
}
else
{
break
;
}
}
p
++
;
// Move to the next character
}
// Remove the trailing space if any
if
(
buff_idx
>
0
&&
*
(
buff
+
buff_idx
-
1
)
==
SPACE_CHAR
)
{
buff_idx
--
;
user_str_len
--
;
}
// Fill the remaining buffer space with dots
while
(
buff_idx
<
len
)
{
*
(
buff
+
buff_idx
++
)
=
'.'
;
}
return
(
user_str_len
>
len
)
?
-
1
:
user_str_len
;
// Return length or error
//return 0; //for now just so the code compiles.
}
void
print_buff
(
char
*
buff
,
int
len
){
printf
(
"Buffer: ["
);
for
(
int
i
=
0
;
i
<
len
;
i
++
){
putchar
(
*
(
buff
+
i
));
}
printf
(
"]
\n
"
);
}
int
count_words
(
char
*
buff
,
int
len
,
int
str_len
){
//YOU MUST IMPLEMENT
// Ensure that str_len does not exceed len to avoid buffer overflows
if
(
str_len
>
len
)
{
printf
(
"Error: String length exceeds buffer length.
\n
"
);
return
-
1
;
// Return error code for invalid input
}
int
wc
=
0
;
// Word count
bool
word_start
=
false
;
// Tracks if we are at the start of a word
for
(
int
i
=
0
;
i
<
str_len
;
i
++
)
{
char
current
=
*
(
buff
+
i
);
// Get the current character
//if (current == '.') {
// Stop counting at the first dot
// break;
// }
if
(
!
word_start
&&
current
!=
SPACE_CHAR
)
{
wc
++
;
// Increment word count at word start
word_start
=
true
;
// Mark that we are in a word
}
else
if
(
current
==
SPACE_CHAR
)
{
word_start
=
false
;
// End the current word
}
}
return
wc
;
// Return the total word count
// return 0;
}
//ADD OTHER HELPER FUNCTIONS HERE FOR OTHER REQUIRED PROGRAM OPTIONS
// Reverse the string in the buffer in place
void
reverse_string
(
char
*
buff
,
int
str_len
)
{
int
start
=
0
;
// Start index
int
end
=
str_len
-
1
;
// End index
while
(
start
<
end
)
{
// Swap until the indices meet
char
temp
=
*
(
buff
+
start
);
// Temporary storage for start character
*
(
buff
+
start
)
=
*
(
buff
+
end
);
// Swap start and end characters
*
(
buff
+
end
)
=
temp
;
start
++
;
// Move start index forward
end
--
;
// Move end index backward
}
}
void
word_print
(
char
*
buff
,
int
str_len
)
{
int
word_count
=
0
;
// Total word count
int
char_count
=
0
;
// Length of the current word
bool
at_start
=
true
;
// Tracks if at the start of a new word
printf
(
"Word Print
\n
"
);
printf
(
"----------
\n
"
);
// Loop through the buffer for the string length
for
(
int
i
=
0
;
i
<
str_len
;
i
++
)
{
char
current
=
*
(
buff
+
i
);
// Get the current character
if
(
at_start
&&
current
!=
SPACE_CHAR
)
{
// New word found
word_count
++
;
printf
(
"%d. "
,
word_count
);
// Print word index
at_start
=
false
;
// Mark that we are processing a word
}
if
(
current
==
SPACE_CHAR
)
{
// End of a word
if
(
char_count
>
0
)
{
printf
(
"(%d)
\n
"
,
char_count
);
// Print word length
char_count
=
0
;
// Reset character count for next word
at_start
=
true
;
// Mark start of a new word
}
}
else
{
if
(
*
(
buff
+
i
)
==
'.'
)
{
// Stop processing at the first dot (truncated word)
break
;
}
putchar
(
current
);
// Print the current character
char_count
++
;
// Increment the character count
}
}
// Handle the last word (if it doesn't end with a space)
if
(
char_count
>
0
)
{
printf
(
"(%d)
\n
"
,
char_count
);
// Print the length of the last word
}
printf
(
"
\n
Number of words returned: %d
\n
"
,
word_count
);
}
/*
int string_replace(char *buff, int len, char *find, char *replace) {
char *match = strstr(buff, find); // Find the first occurrence of the substring
if (!match) {
return -2; // Substring not found
}
int find_len = my_strlen(find); // Length of the substring to find
int replace_len = my_strlen(replace); // Length of the replacement string
int buff_len = my_strlen(buff); // Current length of the buffer content
// Calculate the new length after replacement
int new_len = buff_len - find_len + replace_len;
if (new_len > len) {
// Truncate the replacement string to fit within the buffer
replace_len -= (new_len - len);
new_len = len; // Adjust the final buffer length
}
// Check if truncation has shortened the replacement string to zero
if (replace_len <= 0) {
return -1; // Error: Replacement string cannot fit in the buffer
}
// Move characters after the match to accommodate the replacement
memmove(match + replace_len, // Destination: shift characters after replacement
match + find_len, // Source: characters after the found substring
buff_len - (match - buff) - find_len); // Number of characters to move
// Copy the (possibly truncated) replacement string into the buffer
memcpy(match, replace, replace_len);
// Fill the remaining buffer with dots
for (int i = new_len; i < len; i++) {
buff[i] = '.'; // Fill unused space with dots
}
// Ensure null-termination for safety
buff[len] = '\0';
return 0; // Success
}
*/
int
string_replace
(
char
*
buff
,
int
len
,
char
*
find
,
char
*
replace
)
{
char
*
match
=
strstr
(
buff
,
find
);
// Locate the substring to replace
if
(
!
match
)
{
return
-
2
;
// Substring not found
}
int
find_len
=
my_strlen
(
find
);
// Length of the substring to find
int
replace_len
=
my_strlen
(
replace
);
// Length of the replacement string
int
buff_len
=
my_strlen
(
buff
);
// Current length of the buffer content
// Calculate the new length after replacement
int
new_len
=
buff_len
-
find_len
+
replace_len
;
// Allocate a temporary buffer
char
*
temp_buff
=
(
char
*
)
malloc
(
len
);
if
(
!
temp_buff
)
{
return
-
3
;
// Memory allocation failed
}
// Copy everything up to the match into the temporary buffer
int
prefix_len
=
match
-
buff
;
strncpy
(
temp_buff
,
buff
,
prefix_len
);
// Append the (possibly truncated) replacement string to the temporary buffer
if
(
replace_len
+
prefix_len
>
len
)
{
replace_len
=
len
-
prefix_len
;
// Truncate the replacement string if necessary
}
strncpy
(
temp_buff
+
prefix_len
,
replace
,
replace_len
);
// Append the remainder of the original buffer after the match
int
suffix_start
=
prefix_len
+
find_len
;
int
remaining_space
=
len
-
(
prefix_len
+
replace_len
);
if
(
remaining_space
>
0
)
{
strncpy
(
temp_buff
+
prefix_len
+
replace_len
,
buff
+
suffix_start
,
remaining_space
);
}
// Ensure the result is properly padded with dots
for
(
int
i
=
new_len
;
i
<
len
;
i
++
)
{
temp_buff
[
i
]
=
'.'
;
}
// Copy the result back to the original buffer
memcpy
(
buff
,
temp_buff
,
len
);
// Free the temporary buffer
free
(
temp_buff
);
return
0
;
// Success
}
int
main
(
int
argc
,
char
*
argv
[]){
char
*
buff
;
// = malloc(BUFFER_SZ); // Allocate buffer //placehoder for the internal buffer
//if (!buff) {
// printf("Error: Memory allocation failed.\n");
// exit(99); // Exit on memory allocation failure
//}
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?
// PLACE A COMMENT BLOCK HERE EXPLAINING
/* This check ensures that the program has received the correct number of arguments.
- If argc < 2, argv[1] does not exist, so the program safely exits with usage instructions.
- The second condition (*argv[1] != '-') ensures that the first argument provided
starts with a dash ('-'), as required by the command-line syntax.
By combining these checks,
we prevent undefined behavior from accessing argv[1]
when it does not exist.
*/
if
((
argc
<
2
)
||
(
*
argv
[
1
]
!=
'-'
)){
usage
(
argv
[
0
]);
// free(buff);
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
]);
// free(buff);
exit
(
0
);
}
//WE NOW WILL HANDLE THE REQUIRED OPERATIONS
//TODO: #2 Document the purpose of the if statement below
// PLACE A COMMENT BLOCK HERE EXPLAINING
/*
This check ensures that the user provides the required arguments:
- argc < 3 indicates that the user has not supplied both an option (argv[1])
and the input string (argv[2]).
- If this condition is 'true', the program prints usage instructions
and exits with an error code of 1 to indicate invalid usage.
This prevents undefined behavior caused by accessing missing arguments.
*/
if
(
argc
<
3
){
usage
(
argv
[
0
]);
// free(buff);
exit
(
1
);
}
input_string
=
argv
[
2
];
//capture the user input string
//user_str_len = setup_buff(buff, input_string, BUFFER_SZ);
//TODO: #3 Allocate space for the buffer using malloc and
// handle error if malloc fails by exiting with a
// return code of 99
// CODE GOES HERE FOR #3
// Allocate space for the buffer
buff
=
(
char
*
)
malloc
(
BUFFER_SZ
);
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);
free
(
buff
);
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
);
free
(
buff
);
exit
(
2
);
}
printf
(
"Word Count: %d
\n
"
,
rc
);
print_buff
(
buff
,
BUFFER_SZ
);
break
;
}
//TODO: #5 Implement the other cases for 'r' and 'w' by extending
// the case statement options
case
'r'
:{
reverse_string
(
buff
,
user_str_len
);
// printf("Reversed String: %.*s\n", user_str_len, buff);
print_buff
(
buff
,
BUFFER_SZ
);
break
;
}
case
'w'
:{
//printf("Word Print\n----------\n");
word_print
(
buff
,
user_str_len
);
print_buff
(
buff
,
BUFFER_SZ
);
break
;
}
case
'x'
:
{
if
(
argc
<
5
)
{
//printf("Not Implemented!\n");
free
(
buff
);
exit
(
1
);
}
char
*
find
=
argv
[
3
];
char
*
replace
=
argv
[
4
];
rc
=
string_replace
(
buff
,
BUFFER_SZ
,
find
,
replace
);
if
(
rc
==
-
2
)
{
// printf("Not Implemented!\n");
free
(
buff
);
exit
(
3
);
}
else
if
(
rc
==
-
1
)
{
// printf("Not Implemented!\n");
free
(
buff
);
exit
(
3
);
}
// Print the buffer
printf
(
"Buffer: ["
);
for
(
int
i
=
0
;
i
<
BUFFER_SZ
;
i
++
)
{
putchar
(
buff
[
i
]);
}
printf
(
"]
\n
"
);
//printf("Not Implemented!\n");
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);
return
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
/*
Passing both the buffer pointer and its length to the helper functions
is considered good practice for several reasons:
1. Code Resuable:
The functions become more general and reusable. They are not tied
to a specific buffer size, allowing them to work with buffers
of different sizes in other programs or contexts.
2. Flexibility:
If the buffer size (BUFFER_SZ) changes in the future, the helper
functions do not need to be modified. The updated size is passed
automatically, making the code easier to maintain.
3. Safety:
Explicitly passing the length ensures that the helper functions
do not read or write beyond the allocated memory. This prevents
buffer overflows, which could lead to undefined behavior or
security vulnerabilities.
4. Error Checking:
Having the buffer length allows the helper functions to validate
inputs (e.g., ensuring the string length does not exceed the buffer
size). This improves the program's robustness by catching errors early.
5. Readable:
Including the buffer length makes it clear that the function
operates on a fixed-size buffer and enforces constraints.
It also serves as documentation for the function's behavior
and expected input.
*/
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment