34 KiB
Introduction
A string is actually a collection of characters. In the C language, a string is an array of the char type with a terminating null character \0 at the end. Without the terminating character, it's just a character array. In C, there isn't a dedicated string variable. However, the str in varch encapsulates array strings, making it a string variable where you don't need to worry about the underlying storage structure. Although str still stores character sets in contiguous address spaces, the work related to storage is handled by the methods provided by str, and it also offers methods that are in line with string operations, facilitating flexible manipulation of strings. For convenience, traditional C language strings will be referred to as array strings later, and the ones provided by varch will be called str strings.
Interface
Creation and Deletion of str String Objects
str_t str_create(void *string);
void str_delete(str_t str);
#define str(str) // For more convenient use, a macro definition is wrapped around str_create
#define _str(str) // A macro definition is wrapped around str_delete, and the str is set to NULL after deletion
Here, str_t is the structure of str. The creation method will return an str object, and it will return NULL if the creation fails. The string parameter is used to pass in the initializing string. This string is defined as the void * type, so it can support passing both array strings and str strings for initialization (all subsequent mentions of void *string can support these two types of strings for initialization). The deletion method is used to delete the passed-in str object. The creation method and the deletion method should be used in pairs. Once created, the str object should be deleted when it's no longer in use.
void test(void)
{
str_t ss = str("Hello str!"); // Use an array string for assignment construction
str_t copy = str(ss); // Use an str string for copy construction
_str(ss); // Delete str
_str(copy); // Delete str
}
str String's Array String
#define str_c_str(str)
#define _S(str)
str stores strings in contiguous address spaces, and by obtaining this address, you can access the actually stored string. However, it's not recommended to modify the string as an array string in this way during use. There's also a method char* str_data(str_t str, int pos); that has a similar function. str_c_str() is implemented by using str_data, and it's not recommended to replace str_c_str() with this method. And _S() is a shorter version.
void test(void)
{
str_t ss = str("Hello str!"); // Use an array string for assignment construction
str_t copy = str(ss); // Use an str string for copy construction
printf("ss: %s\r\n", _S(ss));
printf("copy: %s\r\n", _S(copy));
_str(ss); // Delete str
_str(copy); // Delete str
}
Results:
ss: Hello str!
copy: Hello str!
str Assignment
str_t str_assign(str_t str, void *string);
str abstracts the string as a class, and the str_assign method performs an assignment operation similar to the = operator. As mentioned before, void *string can support both array strings and str strings.
void test(void)
{
str_t ss = str(""); // Initialize as an empty string
printf("ss: %s\r\n", _S(ss));
str_assign(ss, "Hello str!"); // Assign the value "Hello str!"
printf("ss: %s\r\n", _S(ss));
str_assign(ss, "This is the assignment method!"); // Reassign the value
printf("ss: %s\r\n", _S(ss));
_str(ss);
}
Results:
ss:
ss: Hello str!
ss: This is the assignment method!
From this, we can see that str doesn't require you to worry about the size of the defined space like array strings do. Instead, str internally adapts to the data.
str Concatenation
#define str_append(str,...)
Besides having an assignment operation similar to the = operator, str also has a concatenation operation similar to the += operator. The method has variable arguments after the formal parameter, which means it can concatenate at least one string of any quantity. Again, these can be either array strings or str strings. The prototype of the str_append method is str_t str_append_series(str_t str,...);. The prototype function with variable arguments requires ending with NULL, and it's not recommended to use the prototype method.
void test(void)
{
str_t name = str("123456789");
str_t ident = str("qq");
str_t email = str("");
str_append(email, name, "@", ident, ".com"); // Concatenate several strings
printf("%s\r\n", str_c_str(email));
_str(name);
_str(ident);
_str(email);
}
Results:
123456789@qq.com
str Insertion
str_t str_insert(str_t str, int pos, void *string);
This method inserts a string into str at the specified position and returns str itself. pos is the insertion position, and string is the source string to be inserted.
void test(void)
{
str_t ss = str("0123456");
str_insert(ss, 3, "|insert|"); // Insert the string at position 3
printf("ss: %s\r\n", str_c_str(ss));
_str(ss);
}
Results:
ss: 012|insert|3456
str Removal
str_t str_erase(str_t str, int pos, int len);
This method removes a segment of characters from str and returns str itself. pos is the removal position, and len is the length of the segment to be removed.
void test(void)
{
str_t ss = str("0123456789");
str_erase(ss, 3, 3); // Remove three characters starting from position 3, that is, remove "345"
printf("ss: %s\r\n", str_c_str(ss));
_str(ss);
}
Results:
ss: 0126789
str Push Back and Pop Back
int str_push_back(str_t str, char c);
char str_pop_back(str_t str);
These two methods are used to push and pop a character from the end of the str string respectively. The push_back method returns 1 for success and 0 for failure, and the pop_back method returns the popped character. The storage structure of str is similar to that of vector, and operations at the end are relatively efficient.
void test(void)
{
str_t ss = str("0123456789");
str_push_back(ss, 'A');
printf("ss: %s\r\n", str_c_str(ss));
printf("pop %c\r\n", str_pop_back(ss));
printf("ss: %s\r\n", str_c_str(ss));
_str(ss);
}
Results:
ss: 0123456789A
pop A
ss: 0123456789
str String Comparison
int str_compare(str_t str, void *string);
This method compares str with another string. A return value of 0 means they are equal, -1 means str is less than the other string, and 1 means str is greater than the other string.
void test(void)
{
str_t ss = str("01234");
str_t copy = str(ss);
printf("compare: %d\r\n", str_compare(ss, copy));
printf("compare: %d\r\n", str_compare(ss, "01233"));
printf("compare: %d\r\n", str_compare(ss, "012345"));
_str(ss);
_str(copy);
}
Results:
compare: 0
compare: 1
compare: -1
str Creating a Substring
str_t str_substr(str_t str, int pos, int len);
This method creates a new substring from str. Remember to call the _str(str) method to delete it after you finish using it. pos is used to pass in the starting position, and len is used to pass in the length of the substring to be created.
void test(void)
{
str_t ss = str("0123456789");
str_t sub = str_substr(ss, 4, 2);
printf("ss: %s\r\n", str_c_str(ss));
printf("sub: %s\r\n", str_c_str(sub));
_str(ss);
_str(sub);
}
Results:
ss: 0123456789
sub: 45
str String Length
int str_length(str_t str);
This is the standard method for obtaining the length of an str string. Of course, you can also use the strlen method, but for str objects, it's not necessary because this standard method doesn't take time to measure the length as it already has the recorded length available.
void test(void)
{
str_t ss = str("0123456789");
printf("length: %d\r\n", str_length(ss));
_str(ss);
}
Results:
length: 10
str Character Reading and Writing
#define str_at(str, i)
str provides operations for reading and writing characters within it, and you can use them just like elements of an array. However, remember not to modify a character to \0.
void test(void)
{
str_t ss = str("0123456789");
printf("ss[3] = %c\r\n", str_at(ss, 3));
str_at(ss, 3) = 'A';
printf("ss: %s\r\n", _S(ss));
_str(ss);
}
Results:
ss[3] = 3
ss: 012A456789
str String Searching
int str_find(str_t str, void *string, int pos);
int str_rfind(str_t str, void *string, int pos);
These two methods are used to search for a character substring in the forward and reverse directions respectively. string is used to pass in the string to be searched for, and pos is used to pass in the starting index for the search. The return value is the position index of the substring if found, and str_npos is returned if not found.
void test(void)
{
str_t ss = str("0123456789");
printf("find = %d\r\n", str_find(ss, "3456", 0)); // Start searching from position 0
printf("rfind = %d\r\n", str_rfind(ss, "23", str_length(ss) - 1)); // Start searching backward from the end
printf("find = %d\r\n", str_find(ss, "0000", 0)); // Search for a non-existent string
_str(ss);
}
Results:
find = 3
rfind = 2
find = 2147483647
str Searching for Matching Character Sets
int str_find_first_of(str_t str, void *string, int pos);
int str_find_first_not_of(str_t str, void *string, int pos);
int str_find_last_of(str_t str, void *string, int pos);
int str_find_last_not_of(str_t str, void *string, int pos);
These four methods are different from str_find and str_rfind. The find and rfind methods require a complete match of all characters, while these four methods only need to match any one of the characters in the character set to be considered a match. Similarly, string is used to pass in the string (the matching character set), pos is used to pass in the starting index for the search, and the return value is the position index of the substring if found, and str_npos is returned if not found. These four functions are used to find the "first occurrence of any character in the set", "first occurrence of any character not in the set", "last occurrence of any character in the set", and "last occurrence of any character not in the set" respectively.
Example: To obtain the file name and the drive letter from a file path in a practical application.
void test(void)
{
str_t ss = str("C:/workspace/project/C/00 - vlib/Project/main.c"); // Given a file path first
str_t pan = NULL; // Initialize to NULL, not constructed
str_t filename = NULL; // Initialize to NULL, not constructed
pan = str_substr(ss, 0, str_find_first_of(ss, ":", 0)); // First find the first occurrence of ":"
filename = str_substr(ss, str_find_last_of(ss, "\\/", str_length(ss)-1) + 1, str_length(ss)); // Find the last occurrence of "\\" or "/" as the path separator
printf("pan: %s\r\n", str_c_str(pan));
printf("filename: %s\r\n", str_c_str(filename));
_str(ss);
_str(pan);
_str(filename);
}
Results:
pan: C
filename: main.c
str String Reversal
str_t str_reverse(str_t str, int begin, int end);
This method reverses the entire string within the specified interval of str. begin is the starting position of the interval, and end is the ending position of the interval.
void test(void)
{
str_t ss = str("0123456789");
str_reverse(ss, 2, 8);
printf("ss = %s\r\n", str_c_str(ss));
_str(ss);
}
Results:
ss = 0187654329
str Character Replacement
str_t str_replace(str_t str, int pos, int len, void *string);
This method replaces the characters at the specified position with a specified length by a given string.
void test(void)
{
str_t ss = str("My name is ZhangSan!");
printf("ss = %s\r\n", str_c_str(ss));
str_replace(ss, 11, 8, "Lisi"); // Replace "ZhangSan" with "Lisi"
printf("ss = %s\r\n", str_c_str(ss));
_str(ss);
}
Results:
ss = My name is ZhangSan!
ss = My name is Lisi!
str String Swap
void str_swap(str_t str, str_t swap);
This method swaps two strings.
void test(void)
{
str_t s1 = str("This s1!");
str_t s2 = str("This s2!");
str_swap(s1, s2);
printf("s1 = %s\r\n", str_c_str(s1));
printf("s2 = %s\r\n", str_c_str(s2));
_str(s1);
_str(s2);
}
Results:
s1 = This s2!
s2 = This s1!
Copying str String to an Array
- Function Functionality and Parameter Introduction:
- The function
int str_copy(str_t str, int pos, int len, char *buf);is used to copy the characters at the specified position in thestrstring to the specified arraybuf. Among them, the parameterposindicates the starting position of the copy,lenrepresents the maximum length of the copy, andbufis the array used to receive the copied data. The function will finally return the actual length of the copied content. - For example, in the following test code:
- The function
void test(void)
{
str_t ss = str("0123456789");
char array[32] = { 0 };
int length = 0;
length = str_copy(ss, 5, sizeof(array), array); // Start copying from the position, and the maximum length of the copy is the length of the array
array[length] = '\0'; // Add a terminator at the end for easy printing
printf("length = %d, copy: %s\r\n", length, array);
_str(ss);
}
The running result is:
length = 5, copy: 56789
From the code execution process, it starts copying from the 5th position of the str object ss. Since the set maximum copy length is the length of the array array (which is long enough here), 5 characters "56789" are actually copied to the array array. Then, a terminator \0 is added and the result is printed. Finally, the str object is correctly released.
String Formatting of str
- Function Functionality and Features:
- The function
str_t str_format(str_t str, char *format,...);realizes the string formatting operation ofstr. It borrows from and rewrites thesprintffunction. Its usage is basically the same as that ofsprintf, but it also has unique features. It adds the function that the format specifier%scan support thestr_ttype. This means that when formatting a string,%scan not only support thechar*type string as traditionally, but also support thestr_tobject, achieving compatibility between the two string types. In addition, thestr_formatfunction will perform segmented dynamic expansion during the formatting process. Users don't need to worry about whether the length of thebufexceeds the limit as when using thesprintffunction, making the operation more convenient. - Here is a test example:
- The function
void test(void)
{
str_t ss = str("");
str_t pp = str("format");
str_format(ss, "Hello str! %s %s! int:%d, float:%.2f, char:%c", pp, "function", 18, 175.5, 'A');
printf("ss: %s\r\n", str_c_str(ss));
_str(ss);
_str(pp);
}
The output result is:
ss: Hello str! format function! int:18, float:175.50, char:A
From this example, it can be clearly seen that when using the str_format function, there is no need to consider the space size required for the content to be formatted in advance. As long as the parameters are passed in according to the format requirements, the formatting operation can be successfully completed, which is very convenient.
Reference Examples
Example 1: Judging Palindrome Strings
- Code Logic and Implementation Steps:
- The following is the code for judging whether the input string is a palindrome:
void test(void)
{
str_t s = str("");
str_t r = str("");
char buf[64] = {0};
while (1)
{
scanf("%s", buf); // Input a string
str_assign(s, buf); // Assign the input string to s
str_assign(r, s); // Assign s to r
str_reverse(r, 0, str_length(r) - 1); // Reverse r
if (str_compare(s, r) == 0) // Compare whether s and r are the same
{
printf("This is a palindrome string!\r\n");
}
else
{
printf("This is not a palindrome string!\r\n");
}
}
_str(s);
_str(r);
}
- This code continuously receives the strings input by the user in a loop. First, it assigns the input string to
sthrough thestr_assignfunction, then assignsstor. Next, it uses thestr_reversefunction to reverse therstring. Finally, it compares whethersand the reversedrare the same through thestr_comparefunction. If they are the same, it outputs that it is a palindrome string; if not, it outputs that it is not a palindrome string. For example, when inputting "qwewq", after the above operations, the comparison result is equal, and it will output "This is a palindrome string!". When inputting "zxcvfd", the comparison result is not equal, and it will output "This is not a palindrome string!".
Example 2: Deleting Repeated Characters in a String
- Code Logic and Implementation Idea:
- The following is the code for deleting repeated characters in a string:
void test(void)
{
str_t s = str("");
str_t r = str("");
char buf[64] = {0};
while (1)
{
scanf("%s", buf); // Input a string
str_assign(s, buf); // Assign the input string to s
str_assign(r, ""); // Set r to an empty string
for (int i = 0, n = str_length(s); i < n; i++) // Traverse s
{
char t[2] = {str_at(s, i),0}; // Take out the character of s and form a short array string
if (str_find(r, t, 0) == str_npos) // Judge whether this array string is in r, that is, judge whether the character of s is in r
{
str_append(r, t); // If it is not in r, append it to r
}
}
printf("%s\r\n", _S(r));
}
_str(s);
_str(r);
}
- The code first receives the string input by the user and assigns it to
s, and initializesras an empty string. Then, it traverses each character of thesstring. Each time, it takes out a character to form a short array stringt, and then uses thestr_findfunction to judge whetherthas appeared inr. If it has not appeared (that is, returnsstr_npos), it uses thestr_appendfunction to append the character tor. Finally, it outputs the processedrstring, thus realizing the function of removing repeated characters. For example, when inputting "16783679816488742135468794", after processing, it will output "167839425", and the repeated characters are removed.
Source Code Analysis
str Structure
- Purpose of Structure Design and Meaning of Members:
- The structures of the
strcontainer are implicit. Such a design ensures the independence and security of the module, avoids external arbitrary access and modification of structure members, and thus prevents the destruction of thestrstorage structure. Its type declaration istypedef struct STR *str_t;. When using it,str_tcan be used to represent this structure type. - The specific definition of the
STRstructure is as follows:
- The structures of the
typedef struct STR
{
char ident; /* ident of str */
char* base; /* base address of string */
int length; /* length of str */
int capacity; /* capacity of str */
} STR;
- The
identmember in the structure is the basis for distinguishing between array strings andstrstrings. Thebasemember points to the base address where the string is actually stored. Thelengthrepresents the size ofstr, that is, the length of the string. Thecapacityis the space capacity allocated for actually storing the string. However, in general usage scenarios, there is no need to pay special attention to capacity-related matters.
Distinguishing between Array Strings and str Strings
- Distinguishing Mechanism and Code Implementation:
- Array strings and
strstrings are distinguished by theidentmember in theSTRstructure. The distinguishing code is as follows:
- Array strings and
typedef struct
{
char *base;
int length;
} str_info_t;
static str_info_t string_info(void *string)
{
str_info_t info;
if (((char *)string)[0] == 0) /* empty array string */
{
info.base = (char *)string;
info.length = 0;
}
else if (((str_t)string)->ident == ident()) /* str type */
{
info.base = ((str_t)string)->base;
info.length = ((str_t)string)->length;
}
else /* array string type */
{
info.base = (char *)string;
info.length = strlen((char *)string);
}
return info;
}
- Because
identis in the first position of the structure, whenvoid *stringis passed in, the content of the first byte of its address is first judged. If it is\0, it is an empty array string. If it isident()(which is defined as-1), it represents anstrstring. Other values represent array strings. The reason for choosing-1to distinguish is that in common ASCII encoding (the character range is0 ~ 127), Unicode encoding (the range is0x0000 ~ 0x10FFFF), and UTF-8 encoding, there will be no situation where it starts with-1(the corresponding unsigned value is0xFF). So it is feasible to use it as a distinguishing mark in these common encoding scenarios. In the APIs related tostr, whenever it is necessary to distinguish the string type, thestring_infofunction will be called to obtain thestr_info_ttype information containing the base address and length of the string.
str Self-Adapting Length
- Implementation Principle Taking the Assignment Function as an Example:
- Looking at the self-adapting length mechanism of
strfrom thestr_assignassignment function, the code is as follows:
- Looking at the self-adapting length mechanism of
str_t str_assign(str_t str, void *string)
{
str_info_t info;
if (!str) return NULL;
if (!string) return NULL;;
info = string_info(string); // Obtain the information (base address and length) of the string
if (!str_alter_capacity(str, info.length)) return NULL; // Adjust the capacity according to the length
strcpy(str->base, info.base); // Copy the string to the str space
str->length = info.length; // Update the length
return str;
}
- First, it will obtain the relevant information (base address and length) of the string to be assigned through the
string_infofunction. Then, it calls thestr_alter_capacityfunction to adjust the capacity of the targetstrobject according to the obtained length. If the capacity adjustment is successful, it copies the source string to the space of the targetstrobject and updates its length. Finally, it returns the adjustedstrobject. If the capacity adjustment fails, it returnsNULL. Inside thestr_alter_capacityfunction, it will first judge whether the capacity needs to be changed. If thestrobject has not been allocated space originally, it will allocate new space throughmalloc. If there is already space, it will reallocate space throughreallocand finally update the capacity value and return the corresponding result.
str Obtaining the Actual Stored Data
- Function Implementation and Error Prevention Mechanism:
- The function
char* str_data(str_t str, int pos)is used to obtain the address of the character at the specified index position in thestrobject. Functions such asstr_at,str_c_str, and_Sare all implemented based on it. The code is as follows:
- The function
char* str_data(str_t str, int pos)
{
if (!str || pos < 0 || pos >= str->length)
{
error = 0; /* reset error area */
return &error;
}
return &str->base[pos];
}
- The function will first perform parameter validity judgment. If the passed-in
strobject is empty or the specified positionposis illegal (less than 0 or exceeding the string length range), it will reset a static variableerrordefined in the module and return the address oferror. This is to prevent the situation that returningNULLcauses the program to crash due to subsequent operations on the null address. If the parameters are legal, it will return the address after offsetting the address pointed to bybasein thestrstructure bypospositions, that is, the address of the character at the corresponding index position.
str String Replacement Principle
- Key Steps and Logical Processing in the Replacement Operation:
- The function
str_t str_replace(str_t str, which involves the following key aspects in the replacement operation:
- The function
str_t str_replace(str_t str, int pos, int len, void *string)
{
str_info_t info;
char *overlap = NULL;
if (!str) return NULL;
if (!string) return NULL;
if (pos < 0 || pos > str->length) return NULL;
if (len > str->length - pos) len = str->length - pos;
info = string_info(string);
/* Check if addresses overlap
This step is to check whether the addresses overlap, that is, whether the address of the string information overlaps with the original address of str.
The processing mechanism for overlap is to allocate a piece of space, copy the content to be replaced,
and then replace the copied content later.
*/
if (str->base <= info.base && info.base <= str->base + str->length && pos < str->length)
{
overlap = (char *)malloc(info.length + 1);
if (!overlap) return NULL;
strcpy(overlap, info.base);
info.base = overlap;
}
/*
This step judges the length relationship between the string to be replaced and the replacing string, that is, judges whether the capacity needs to be adjusted.
*/
if (info.length > len) // The replacing string is longer and needs to expand the capacity
{
/* First expand the capacity, and the expanded capacity is the length difference */
if (str_alter_capacity(str, str->length + (info.length - len)) == 0)
{
if (overlap) free(overlap);
return NULL;
}
/* Move the latter part of the data backward to make room for storing the longer replacing string */
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
/* Copy the replacing string into the vacated space */
memcpy(&str->base[pos], info.base, info.length);
}
else if (info.length < len) /* The length becomes smaller */
{
/* Move the latter data forward, leaving the length of info for the replacing string */
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
/* Copy the replacing string into the vacated space */
memcpy(&str->base[pos], info.base, info.length);
/* For shrinking, adjust the capacity later */
str_alter_capacity(str, str->length + (info.length - len));
}
else /* The length has not changed */
{
/* Directly overwrite */
memcpy(&str->base[pos], info.base, info.length);
}
str->length += (info.length - len);
str->base[str->length] = 0;
if (overlap) free(overlap);
return str;
}
The function first performs some basic parameter validity checks and obtains information about the string to be inserted through the string_info function. Then, it checks whether the addresses of the original string and the string to be inserted overlap. If there is an overlap, it will allocate additional space to handle it properly. Next, it compares the lengths of the two strings to determine whether to expand, shrink, or directly overwrite the capacity of the original string. Finally, it updates the length of the string in the str object, adds a terminator, and releases the temporarily allocated space if necessary, and then returns the modified str object. And the functions str_insert and str_erase are implemented based on the str_replace function. For example, str_insert is implemented by calling str_replace with a length of 0 for the replacement part, and str_erase is implemented by calling str_replace with an empty string for the replacement part.
Principle of str Concatenation
The actual operation is to call the str_append_series function for parameter passing, and append a NULL at the end of the variable arguments.
Inside the str_append_series function, it processes the variable arguments. It inserts each segment of strings to the end of the str one by one through the str_insert function to complete the concatenation. The specific code is as follows:
#define str_append(str,...) str_append_series((str), ##__VA_ARGS__, NULL)
str_t str_append_series(str_t str,...)
{
va_list args;
void* s = NULL;
if (!str) return NULL;
va_start(args, str);
s = va_arg(args, void*);
while (s)
{
/* Insert at the end */
if (!str_insert(str, str->length, s))
{
va_end(args);
return NULL;
}
s = va_arg(args, void*);
}
va_end(args);
return str;
}
Source Code of str Formatting
The source code of the str_format function is relatively long. The general process is to traverse the format string. When encountering the % symbol, specific formatting operations will be carried out. Taking the % symbol as the delimiter, the entire format string is divided into segments for dynamic expansion and assignment. The detailed code is shown below:
str_t str_format(str_t str, char *format,...)
{
va_list args;
int i = 0;
int len = 0; /* length of sub string */
char *s = NULL; /* temp string */
str_info_t info; /* str info */
double dbl = 0.0; /* double precision floating point */
char *begin = NULL; /* begin of format */
char qualifier = 0; /* conversion qualifier for integer as 'h','l','L' */
int width = 0; /* transition field width */
int precision = 0; /* minimum digits of integers and maximum digits of strings */
char tfmt[16] = "%"; /* temporary format */
if (!str) return NULL;
str_assign(str, "");
va_start(args, format);
for (; *format; format++)
{
/* When it is an ordinary character */
if (*format!= '%')
{
if (!begin) begin = format; // Record the starting position of the ordinary character segment
continue; // Skip this loop iteration and move to the next one
}
/* If the starting position of the ordinary character has been recorded, it means the previous part of the current position is ordinary characters */
if (begin)
{
len = format - begin; // Record the length for later expansion
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
while (len--) str->base[str->length++] = *begin++; // Copy the ordinary characters in
begin = NULL;
}
/* Start processing the formatted data segment from here */
begin = format;
while (1) // Record the prefix symbol flag
{
/* Also skips the first '%' */
format++;
if ((*format!= '0') &&
(*format!= ' ') &&
(*format!= '+') &&
(*format!= '-') &&
(*format!= '#')) break;
}
/* Get field width */
width = -1;
if (is_digit(*format))
{
format += get_digit(format, &width);
}
else if (*format == '*')
{
format++;
width = va_arg(args, int);
if (width < 0) width = -width;
}
/* Get the precision */
precision = -1;
if (*format == '.')
{
format++;
if (is_digit(*format)) format += get_digit(format, &precision);
else if (*format == '*')
{
format++;
precision = va_arg(args, int);
}
if (precision < 0) precision = 0;
}
/* Get the conversion qualifier */
qualifier = 0;
if (*format == 'h' || *format == 'l' || *format == 'L')
{
qualifier = *format;
format++;
if (qualifier == 'l' && *format == 'l')
{
qualifier = 'L';
format++;
}
}
/* Format distribution */
switch (*format)
{
case 'c':
// Estimate how much space is needed
// Call sprintf to append a character at the end
...
case 's':
// Estimate how much space is needed
// Call sprintf to append a segment of string at the end
...
case 'p':
// Estimate how much space is needed
// Call sprintf to append a pointer at the end
...
case 'a':
case 'A':
case 'e':
case 'E':
case 'g':
case 'G':
case 'f':
// Estimate how much space is needed
// Call sprintf to append a floating-point number at the end
...
case 'o':
case 'X':
case 'x':
case 'd':
case 'i':
case 'u':
// Estimate how much space is needed
// Call sprintf to append an integer at the end
...
case '%':
// Push in %
...
default:
// Push in %
...
}
}
/* Copy the tail string to str */
if (begin)
{
len = format - begin;
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
while (len--) str->base[str->length++] = *begin++;
}
if (!str_alter_capacity(str, str->length)) goto FAIL_CAPACITY;
str->base[str->length] = '\0';
va_end(args);
return str;
FAIL_CAPACITY:
str->base[str->length] = '\0';
va_end(args);
return NULL;
}