Professional Documents
Culture Documents
Giulio12-Somt Negngn
Giulio12-Somt Negngn
Some not-so-experienced C programmers might be confused by the statement in line 9, but it will make
sense if you consider that, if f is a function pointer (remember that you passed as argument &toupper or
&tolower ), *f is the function itself. Therefore, you can imagine replacing (*f)() with either toupper() or
tolower() , like this:
*s = toupper(*s);
Then, it becomes obvious that line 9 simply picks the first character of the string and stores it back in the
same position after conversion.
The parentheses around *f are necessary because the parentheses of a function call take precedence
over the dereferencing asterisk (i.e., taking the address of the function). Therefore, if you wrote
*s = *f(*s);
you would effectively be doing the following:
*s = *(f(*s));
That is, you would be attempting to execute f (which is a pointer to a function) as if it were a function
(which it's not). One good place to see the precedence of operators is en.cppreference.com/w/c/language/
operator_precedence . It shows that the parentheses of a function call have precedence 1, while the
dereferencing asterisk (which they call indirection ) has precedence 2. Obviously, you could always use
additional parentheses to set precedences by hand if you are not sure about the default or to remove a
warning issued by an overzealous development environment, but you might like to be a minimalist in this
matter, so that when you see additional parentheses, you know that they need to be there.
The logic of the function is trivial: you go through the string and convert one character at a time until
you hit the terminating NULL .
String Clean Up
What I mean by string clean up is:
Replace all non-printing characters with spaces
Remove all leading spaces
Remove all trailing spaces
Replace all sequences of spaces with a single space
Consider, for example, the string " \t Giulio \t \x15 \r Zambon\t\t \x19\r" . After performing the
four operations listed above, it would look like this: "Giulio Zambon" . Listing 6-21 shows how you do this.
Listing 6-21. str_clean_up()
1. //---------------------------------------------------------------- str_clean_up
2. void str_clean_up(Str *str) {
3. #if STR_LOG
4. printf("=== str_clean_up: %p\n", str);
5. #endif
6. if (str != NULL && str->s != NULL) {
7. char *s0 = str->s;
8. char *s1 = s0;
CHAPTER 6 STRING UTILITIES
160
9. while (*s0 > '\0' && *s0 <= ' ') s0++; // remove leading junk
10. if (*s0 == '\0') {
11. *s1++ = ' ';
12. *s1 = '\0';
13. }
14. else {
15. int space_set = 0;
16. while (*s0 != '\0') {
17. if (*s0 > ' ') {
18. *s1++ = *s0;
19. space_set = 0;
20. }
21. else if (!space_set) {
22. *s1++ = ' ';
23. space_set = 1;
24. }
25. s0++;
26. }
27. if (s1 > str->s) { // we did something
28. *s1-- = '\0';
29. if (*s1 == ' ') *s1 = '\0'; // remove the trailing space
\Giulio
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
and replaced *s0 and *s1 respectively with s[k0] and s[k1] , but using a pointer is more compact and, IMO,
neater.
Line 9 is where you skip the leading spaces and non-printing characters. Notice that you cannot skip
NULL s because otherwise you would go through the whole computers memory!
The check in line 10 and the setting of s1 in lines 11 and 12 take care of the possibility that the whole input
string might consist of spaces and non-input characters. In that case, you compact it into a single space.
space_set defined in line 15 is a state variable: when set to zero it indicates that the previous character
of s0 you checked was a character you want to keep (i.e., neither a space nor a non-printing character). In
other words, space_set is true when the last character was a space or a non-printing character.
Lines 18 and 19 are executed when the current input character (i.e., the character pointed by s0 ) is to be
kept. Accordingly, you copy it to the position pointed by s1 and increment s1 . But you also reset space_set .
This means that, whenever you encounter several printing characters in sequence, you repeatedly clear
space_set . You might ask: why dont we only set it when necessary? Like in:
if (space_set) space_set = 0;
CHAPTER 6 STRING UTILITIES
161
But, whos to say that a check for non-zero followed by a jump is computationally more economical
than a straight setting to zero? Im not sure. The thing is, I always avoid checks if I can. And, in any case, a
straight assignment is clearer.
If the if statement in line 17 fails, it means that the current character is either a space or a non-printing
character. You only need to do something if it is the first one, because you want to reduce the sequences of
spaces and non-printing characters to single spaces.
If it is the first one (i.e., if space_set is false ), you write a space to the output (line 22) and remember
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
v
v
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
that you have encountered a space or a non-printing character in input (line 23).
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
v
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))
In any case, regardless of what you have encountered in input, you increment the input pointer (line 25)
and move on.
After going through the whole input string, in line 27 you check that you have changed the output
pointer. If that is the case, you know that the output string contains at least one character, and you close it
with a NULL (line 28).
But wait a minute! What if there were trailing spaces and non-printing characters? Well, if there were,
they will have been compacted into a single space. Therefore, you only need to check whether the last
character of the string is a space. If it is, you overwrite it with a NULL (line 29) and you are done.
String Remove
Sometimes it is useful to remove a portion of a string. For this purpose, you can use one of the following
macros:
#define STR_remove(str, from, before) str_remove(str, from, before)
#define STR_remove_from(str, from) str_remove(str, from, STR_len(str))