Directory: | ./ |
---|---|
File: | s21_sprintf_handlers.c |
Date: | 2025-07-13 17:59:14 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 210 | 214 | 98.1% |
Branches: | 134 | 166 | 80.7% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <locale.h> | ||
2 | #include <math.h> | ||
3 | #include <stdlib.h> | ||
4 | |||
5 | #include "s21_sprintf.h" | ||
6 | |||
7 | 53 | void handle_specifier_c(char **str, const FormatSpecifier *fs, va_list args) { | |
8 | char arg[16]; | ||
9 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 42 times.
|
53 | if (fs->length == 'l') { |
10 | 11 | wchar_to_str(va_arg(args, wchar_t), arg); | |
11 | } else { | ||
12 | 42 | char_to_str(va_arg(args, int), arg); | |
13 | } | ||
14 | 53 | apply_padding(str, fs, arg); | |
15 | 53 | } | |
16 | |||
17 | 1972 | void handle_specifier_d(char **str, FormatSpecifier *fs, va_list args) { | |
18 | char arg[256]; | ||
19 | 1972 | int_to_str(get_signed_arg(fs, args), arg, fs); | |
20 | 1972 | apply_padding_with_zero(str, fs, arg); | |
21 | 1972 | } | |
22 | |||
23 | 22 | void handle_specifiers_eE(char **str, FormatSpecifier *fs, va_list args) { | |
24 | char arg[256]; | ||
25 | 22 | long double num = | |
26 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
|
22 | fs->length == 'L' ? va_arg(args, long double) : va_arg(args, double); |
27 | 22 | int exponent = 0; | |
28 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 1 times.
|
22 | if (num) { |
29 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 21 times.
|
75 | while (fabsl(num) >= 10.0) { |
30 | 54 | num /= 10.0; | |
31 | 54 | exponent++; | |
32 | } | ||
33 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 21 times.
|
30 | while (fabsl(num) < 1.0) { |
34 | 9 | num *= 10.0; | |
35 | 9 | exponent--; | |
36 | } | ||
37 | } | ||
38 | char mantissa[256]; | ||
39 | 22 | FormatSpecifier mantissa_fs = *fs; | |
40 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
|
22 | mantissa_fs.precision = fs->precision == -1 ? 6 : fs->precision; |
41 | ; | ||
42 | 22 | float_to_str(num, mantissa, &mantissa_fs); | |
43 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
|
22 | s21_sprintf(arg, "%s%c%+03d", mantissa, fs->specifier == 'E' ? 'E' : 'e', |
44 | exponent); | ||
45 | 22 | apply_padding_with_zero(str, fs, arg); | |
46 | 22 | } | |
47 | |||
48 | 22 | void handle_specifier_f(char **str, FormatSpecifier *fs, va_list args) { | |
49 | char arg[256]; | ||
50 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
|
41 | float_to_str(fs->length == 'L' ? va_arg(args, long double) |
51 | 19 | : (long double)va_arg(args, double), | |
52 | arg, fs); | ||
53 | 22 | apply_padding_with_zero(str, fs, arg); | |
54 | 22 | } | |
55 | |||
56 | 28 | void handle_specifiers_gG(char **str, FormatSpecifier *fs, va_list args) { | |
57 | 28 | long double num = | |
58 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
|
28 | (fs->length == 'L') ? va_arg(args, long double) : va_arg(args, double); |
59 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 18 times.
|
28 | int precision = (fs->precision == -1) ? 6 : fs->precision; |
60 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 27 times.
|
28 | if (precision == 0) { |
61 | 1 | precision = 1; | |
62 | } | ||
63 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 24 times.
|
28 | if (num == 0.0) { |
64 | char zero_buf[128]; | ||
65 | 4 | FormatSpecifier fs_f = *fs; | |
66 | 4 | fs_f.precision = precision - 1; | |
67 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | fs_f.specifier = (fs->specifier == 'G') ? 'F' : 'f'; |
68 | 4 | float_to_str(num, zero_buf, &fs_f); | |
69 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if (!fs->alternate_form) { |
70 | 3 | char *dot = s21_strchr(zero_buf, '.'); | |
71 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (dot) { |
72 | 2 | char *end = zero_buf + s21_strlen(zero_buf) - 1; | |
73 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
12 | while (end > dot && *end == '0') *end-- = '\0'; |
74 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (end == dot) *end = '\0'; |
75 | } | ||
76 | } | ||
77 | 4 | apply_padding(str, fs, zero_buf); | |
78 | } else { | ||
79 | 24 | pick_and_apply_notation(num, precision, str, fs); | |
80 | } | ||
81 | 28 | } | |
82 | |||
83 | 24 | void pick_and_apply_notation(long double num, int precision, char **str, | |
84 | FormatSpecifier *fs) { | ||
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | long double abs_num = (num < 0) ? -num : num; |
86 | 24 | int exponent = 0; | |
87 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (abs_num != 0.0) { |
88 | 24 | exponent = (int)floorl(log10l(abs_num)); | |
89 | } | ||
90 | 24 | long double rounding_factor = powl(10.0L, exponent - precision + 1); | |
91 | 24 | long double rounded_num = roundl(num / rounding_factor) * rounding_factor; | |
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | long double abs_rounded = (rounded_num < 0) ? -rounded_num : rounded_num; |
93 | 24 | int rounded_exp = 0; | |
94 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (abs_rounded != 0.0) { |
95 | 24 | rounded_exp = (int)floorl(log10l(abs_rounded)); | |
96 | } | ||
97 |
3/4✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 5 times.
|
24 | int use_fixed = (rounded_exp >= -4 && rounded_exp < precision); |
98 | char buf[256]; | ||
99 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 5 times.
|
24 | if (use_fixed) { |
100 | 19 | int f_prec = precision - (rounded_exp + 1); | |
101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (f_prec < 0) f_prec = 0; |
102 | 19 | FormatSpecifier fs_f = *fs; | |
103 | 19 | fs_f.precision = f_prec; | |
104 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
|
19 | fs_f.specifier = (fs->specifier == 'G') ? 'F' : 'f'; |
105 | 19 | float_to_str(rounded_num, buf, &fs_f); | |
106 | } else { | ||
107 | 5 | convert_to_scientific_notation(rounded_num, precision, buf, fs); | |
108 | } | ||
109 |
4/4✓ Branch 0 taken 19 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 3 times.
|
24 | if (use_fixed && !fs->alternate_form) { |
110 | 16 | char *dot = s21_strchr(buf, '.'); | |
111 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
|
16 | if (dot) { |
112 | 14 | char *end = buf + s21_strlen(buf) - 1; | |
113 |
3/4✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 14 times.
|
16 | while (end > dot && *end == '0') *end-- = '\0'; |
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if (end == dot) *end = '\0'; |
115 | } | ||
116 | } | ||
117 | 24 | apply_padding_with_zero(str, fs, buf); | |
118 | 24 | } | |
119 | |||
120 | 5 | void convert_to_scientific_notation(long double rounded_num, int precision, | |
121 | char *buf, FormatSpecifier *fs) { | ||
122 | 5 | int e_prec = precision - 1; | |
123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (e_prec < 0) e_prec = 0; |
124 | 5 | long double mantissa = rounded_num; | |
125 | 5 | int exp = 0; | |
126 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (mantissa != 0.0) { |
127 | 5 | exp = (int)floorl(log10l(fabsl(mantissa))); | |
128 | 5 | mantissa = mantissa / powl(10.0L, exp); | |
129 | } | ||
130 | char mantissa_buf[128]; | ||
131 | 5 | FormatSpecifier fs_m = *fs; | |
132 | 5 | fs_m.precision = e_prec; | |
133 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
|
5 | fs_m.specifier = (fs->specifier == 'G') ? 'F' : 'f'; |
134 | 5 | float_to_str(mantissa, mantissa_buf, &fs_m); | |
135 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (!fs->alternate_form) { |
136 | 4 | char *dot = s21_strchr(mantissa_buf, '.'); | |
137 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (dot) { |
138 | 4 | char *end = mantissa_buf + s21_strlen(mantissa_buf) - 1; | |
139 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | while (end > dot && *end == '0') *end-- = '\0'; |
140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (end == dot) *end = '\0'; |
141 | } | ||
142 | } | ||
143 | 5 | s21_sprintf(buf, "%s%c%+03d", mantissa_buf, | |
144 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
|
5 | (fs->specifier == 'G') ? 'E' : 'e', exp); |
145 | 5 | } | |
146 | |||
147 | 19 | void handle_specifier_o(char **str, const FormatSpecifier *fs, va_list args) { | |
148 | char arg[256]; | ||
149 | 19 | unsigned long num = get_unsigned_arg(fs, args); | |
150 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
19 | if (num == 0 && fs->precision == 0) { |
151 | 1 | arg[0] = '\0'; | |
152 | } else { | ||
153 | 18 | uint_to_base(num, arg, 8, "01234567"); | |
154 | } | ||
155 | 19 | int num_len = s21_strlen(arg); | |
156 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 11 times.
|
19 | if (fs->precision > num_len) { |
157 | 8 | int zeros_to_pad = fs->precision - num_len; | |
158 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 | for (int i = num_len; i >= 0; i--) { |
159 | 24 | arg[i + zeros_to_pad] = arg[i]; | |
160 | } | ||
161 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 8 times.
|
47 | for (int i = 0; i < zeros_to_pad; i++) { |
162 | 39 | arg[i] = '0'; | |
163 | } | ||
164 | } | ||
165 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
19 | if (fs->alternate_form && num) { |
166 |
2/2✓ Branch 1 taken 32 times.
✓ Branch 2 taken 4 times.
|
36 | for (int i = s21_strlen(arg); i >= 0; i--) { |
167 | 32 | arg[i + 1] = arg[i]; | |
168 | } | ||
169 | 4 | arg[0] = '0'; | |
170 | } | ||
171 | 19 | apply_padding(str, fs, arg); | |
172 | 19 | } | |
173 | |||
174 | 17 | void handle_specifier_u(char **str, const FormatSpecifier *fs, va_list args) { | |
175 | char arg[256]; | ||
176 | 17 | uint_to_str(get_unsigned_arg(fs, args), arg, fs); | |
177 | 17 | apply_padding(str, fs, arg); | |
178 | 17 | } | |
179 | |||
180 | 23 | void handle_specifiers_xX(char **str, FormatSpecifier *fs, va_list args) { | |
181 | char arg[256]; | ||
182 | 23 | unsigned long num = va_arg(args, unsigned long); | |
183 | char num_str[256]; | ||
184 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
23 | if (num == 0 && fs->precision == 0) { |
185 | 1 | num_str[0] = '\0'; | |
186 | } else { | ||
187 | 22 | uint_to_base( | |
188 | num, num_str, 16, | ||
189 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 15 times.
|
22 | fs->specifier == 'X' ? "0123456789ABCDEF" : "0123456789abcdef"); |
190 | } | ||
191 | 23 | s21_size_t num_len = s21_strlen(num_str); | |
192 | char prec_str[256]; | ||
193 | 23 | size_t zeros_to_pad = | |
194 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
|
23 | fs->precision > (int)num_len ? fs->precision - num_len : 0; |
195 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 23 times.
|
31 | for (size_t i = 0; i < zeros_to_pad; i++) { |
196 | 8 | prec_str[i] = '0'; | |
197 | } | ||
198 | 23 | s21_strncpy(prec_str + zeros_to_pad, num_str, s21_strlen(num_str) + 1); | |
199 | 23 | arg[0] = '\0'; | |
200 |
4/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
|
23 | if (fs->alternate_form && num) { |
201 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | s21_strncpy(arg, fs->specifier == 'X' ? "0X" : "0x", 3); |
202 | } | ||
203 | 23 | s21_strncat(arg, prec_str, s21_strlen(prec_str) + 1); | |
204 |
6/8✓ Branch 0 taken 3 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
23 | if (fs->zero_padding && !fs->left_justify && fs->alternate_form && num) { |
205 | 2 | *(*str)++ = arg[0]; | |
206 | 2 | *(*str)++ = arg[1]; | |
207 | 2 | fs->width -= 2; | |
208 | 2 | apply_padding(str, fs, arg + 2); | |
209 | } else { | ||
210 | 21 | apply_padding(str, fs, arg); | |
211 | } | ||
212 | 23 | } | |
213 | |||
214 | 43 | void handle_specifier_s(char **str, FormatSpecifier *fs, va_list args) { | |
215 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 36 times.
|
43 | if (fs->length == 'l') { |
216 | 7 | setlocale(LC_ALL, ""); | |
217 | 7 | handle_wide_str(str, fs, args); | |
218 | } else { | ||
219 | 36 | char *str_arg = va_arg(args, char *); | |
220 | 36 | char *arg = str_arg; | |
221 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 32 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
36 | if (fs->precision != -1 && (s21_size_t)fs->precision < s21_strlen(arg)) { |
222 | 4 | arg = malloc(fs->precision + 1); | |
223 | 4 | s21_strncpy(arg, str_arg, fs->precision); | |
224 | 4 | arg[fs->precision] = '\0'; | |
225 | } | ||
226 | 36 | apply_padding(str, fs, arg); | |
227 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 32 times.
|
36 | if (arg != str_arg) { |
228 | 4 | free(arg); | |
229 | } | ||
230 | } | ||
231 | 43 | } | |
232 | |||
233 | 7 | void handle_wide_str(char **str, FormatSpecifier *fs, va_list args) { | |
234 | 7 | const wchar_t *wstr_arg = va_arg(args, wchar_t *); | |
235 | 7 | char *full_str = NULL; | |
236 | 7 | int error_occurred = 0; | |
237 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (!wstr_arg) { |
238 | ✗ | full_str = "(null)"; | |
239 | } else { | ||
240 | 7 | size_t buf_len = wcstombs(NULL, wstr_arg, 0); | |
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (buf_len == (size_t)-1) { |
242 | ✗ | error_occurred = 1; | |
243 | } else { | ||
244 | 7 | full_str = malloc(buf_len + 1); | |
245 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (!full_str) { |
246 | ✗ | error_occurred = 1; | |
247 | } else { | ||
248 | 7 | wcstombs(full_str, wstr_arg, buf_len + 1); | |
249 | 7 | process_wide_string(full_str, buf_len, fs); | |
250 | } | ||
251 | } | ||
252 | } | ||
253 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (error_occurred) { |
254 | ✗ | full_str = "(null)"; | |
255 | } | ||
256 | 7 | apply_padding(str, fs, full_str); | |
257 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | if (wstr_arg && !error_occurred) { |
258 | 7 | free(full_str); | |
259 | } | ||
260 | 7 | } | |
261 | |||
262 | 7 | void process_wide_string(char *full_str, size_t buf_len, FormatSpecifier *fs) { | |
263 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
7 | if (fs->precision != -1 && (size_t)fs->precision < buf_len) { |
264 | 4 | size_t trunc_len = 0; | |
265 | mbstate_t state; | ||
266 | 4 | s21_memset(&state, 0, sizeof(mbstate_t)); | |
267 | 4 | const char *p = full_str; | |
268 | 4 | size_t bytes_processed = 0; | |
269 | 4 | int done = 0; | |
270 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
|
15 | while (!done && bytes_processed < (size_t)fs->precision) { |
271 | size_t char_len = | ||
272 | 11 | mbrlen(p, (size_t)(fs->precision - bytes_processed), &state); | |
273 |
4/6✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
|
11 | if (char_len == (size_t)-1 || char_len == (size_t)-2 || char_len == 0) { |
274 | 3 | done = 1; | |
275 | } else { | ||
276 | 8 | p += char_len; | |
277 | 8 | bytes_processed += char_len; | |
278 | 8 | trunc_len = bytes_processed; | |
279 | } | ||
280 | } | ||
281 | 4 | full_str[trunc_len] = '\0'; | |
282 | } | ||
283 | 7 | } | |
284 | |||
285 | 6 | void handle_specifier_p(char **str, const FormatSpecifier *fs, va_list args) { | |
286 | char num_str[256]; | ||
287 | 6 | uint_to_base((unsigned long)va_arg(args, void *), num_str, 16, | |
288 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | fs->specifier == 'X' ? "0123456789ABCDEF" : "0123456789abcdef"); |
289 | char arg[256]; | ||
290 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | s21_strncpy(arg, fs->specifier == 'X' ? "0X" : "0x", 3); |
291 | 6 | s21_strncat(arg, num_str, s21_strlen(num_str) + 1); | |
292 | 6 | apply_padding(str, fs, arg); | |
293 | 6 | } | |
294 | |||
295 | 3 | void handle_specifier_n(const char **str, const FormatSpecifier *fs, | |
296 | va_list args, const char *str_start) { | ||
297 | 3 | int chars_written = (int)(*str - str_start); | |
298 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (fs->length == 'l') { |
299 | 1 | long *ptr = va_arg(args, long *); | |
300 | 1 | *ptr = (long)chars_written; | |
301 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | } else if (fs->length == 'h') { |
302 | 1 | short *ptr = va_arg(args, short *); | |
303 | 1 | *ptr = (short)chars_written; | |
304 | } else { | ||
305 | 1 | int *ptr = va_arg(args, int *); | |
306 | 1 | *ptr = chars_written; | |
307 | } | ||
308 | 3 | } | |
309 |