/[sudobot]/trunk/lib/common/io/printf.c
ViewVC logotype

Contents of /trunk/lib/common/io/printf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (show annotations)
Mon Jul 29 17:59:26 2024 UTC (8 months ago) by rakinar2
File MIME type: text/x-c
File size: 7284 byte(s)
chore: add trunk
1 #include "printf.h"
2
3 #include <assert.h>
4 #include <math.h>
5 #include <stdarg.h>
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "../utils/xmalloc.h"
13
14 typedef enum format_flags format_flags_t;
15
16 enum format_flags
17 {
18 FMT_STR = 0b0000000000001,
19 FMT_INT = 0b0000000000010,
20 FMT_UNSIGNED = 0b0000000000100,
21 FMT_DECIMAL = 0b0000000001000,
22 FMT_BINARY = 0b0000000010000,
23 FMT_HEXADECIMAL = 0b0000000100000,
24 FMT_OCTAL = 0b0000001000000,
25 FMT_UNKNOWN = 0b0000010000000,
26 FMT_INT_DEFAULT = 0b0000100000000,
27 FMT_INT_LONG = 0b0001000000000,
28 FMT_INT_LONG_LONG = 0b0010000000000,
29 FMT_INT_SHORT = 0b0100000000000,
30 FMT_LIT_SIZE = 0b1000000000000
31 };
32
33 static void
34 casprintf_buffer_push_char(char **buffer, size_t *buffer_size, const char c)
35 {
36 size_t size = ++(*buffer_size);
37 *buffer = xrealloc(*buffer, size);
38 (*buffer)[size - 1] = c;
39 }
40
41 static void
42 casprintf_buffer_concat_str(char **buffer, size_t *buffer_size, const char *str)
43 {
44 size_t len = strlen(str);
45 *buffer_size += len;
46 size_t size = *buffer_size;
47 *buffer = xrealloc(*buffer, size);
48 strncat(*buffer, str, len);
49 }
50
51 static format_flags_t casprintf_length_flags(
52 const char *start, const char **next, size_t *literal_size, bool *_zero)
53 {
54 format_flags_t flags = 0;
55 char *sizebuf = NULL;
56 size_t buflen = 0;
57 bool zero = false;
58
59 while (*start && *start >= '0' && *start <= '9')
60 {
61 if (*start == '0' && !zero)
62 {
63 zero = true;
64 start++;
65 continue;
66 }
67
68 casprintf_buffer_push_char(&sizebuf, &buflen, *start);
69 start++;
70 }
71
72 if (buflen > 0 || zero)
73 {
74 size_t i = buflen - 1, size = 0, power = 0;
75
76 if (buflen > 0)
77 {
78 while (true)
79 {
80 long long multiplier = (long long) powl(10, power++);
81 int digit = sizebuf[i] - '0';
82
83 assert(digit >= 0);
84
85 size += multiplier * ((unsigned int) digit);
86
87 if (i == 0)
88 break;
89
90 i--;
91 }
92 }
93
94 if (_zero != NULL)
95 {
96 *_zero = zero;
97 }
98
99 if (next != NULL)
100 {
101 *next = start;
102 }
103
104 free(sizebuf);
105 *literal_size = size;
106 return FMT_LIT_SIZE;
107 }
108
109 free(sizebuf);
110
111 while (*start)
112 {
113 switch (*start)
114 {
115 case 'l':
116 if (*(start + 1) == 'l')
117 {
118 flags |= FMT_INT_LONG_LONG;
119 start++;
120 }
121 else
122 flags |= FMT_INT_LONG;
123
124 break;
125
126 case 'h':
127 flags |= FMT_INT_SHORT;
128 break;
129
130 default:
131 #ifdef SUDOBOT_CASPRINTF_VERBOSE
132 fprintf(
133 stderr, "%s: warning: unknown length specifier: %c\n",
134 __func__, *start);
135 #endif
136 goto casprintf_length_flags_end;
137 }
138
139 start++;
140 }
141
142 casprintf_length_flags_end:
143 if (next != NULL)
144 {
145 *next = start;
146 }
147
148 return flags == 0 ? FMT_INT_DEFAULT : flags;
149 }
150
151 static format_flags_t casprintf_type_flag(const char c)
152 {
153 format_flags_t flags = 0;
154
155 switch (c)
156 {
157 case 'i':
158 case 'd':
159 flags = FMT_INT | FMT_DECIMAL;
160 break;
161
162 case 's':
163 flags = FMT_STR;
164 break;
165
166 case 'u':
167 flags = FMT_INT | FMT_UNSIGNED;
168 break;
169
170 default:
171 #ifdef SUDOBOT_CASPRINTF_VERBOSE
172 fprintf(
173 stderr, "%s: warning: unknown type specifier: %c\n", __func__,
174 *start);
175 #endif
176 return FMT_UNKNOWN;
177 }
178
179 return flags;
180 }
181
182 static void casprintf_concat(
183 va_list *args, char **buffer, size_t *buffer_size, format_flags_t flags,
184 size_t literal_size, bool zero)
185 {
186 if (flags & FMT_INT)
187 {
188 uint64_t unsigned_arg =
189 flags & FMT_INT_DEFAULT
190 ? va_arg(*args, unsigned)
191 : (flags & FMT_INT_LONG
192 ? va_arg(*args, unsigned long)
193 : (flags & FMT_INT_LONG_LONG
194 ? va_arg(*args, unsigned long long)
195 : (flags & FMT_INT_SHORT
196 ? va_arg(*args, unsigned int)
197 : va_arg(*args, unsigned int))));
198
199 int64_t signed_arg = (int64_t) unsigned_arg;
200
201 bool is_negative = flags & FMT_UNSIGNED ? false : (signed_arg < 0);
202 uint64_t arg =
203 flags & FMT_UNSIGNED
204 ? unsigned_arg
205 : ((uint64_t) (is_negative ? -signed_arg : signed_arg));
206
207 char *numbuf = NULL;
208 size_t numbuf_size = 0;
209
210 do
211 {
212 const char digit = (arg % 10) + '0';
213 casprintf_buffer_push_char(&numbuf, &numbuf_size, digit);
214 arg /= 10;
215 } while (arg > 0);
216
217 if (is_negative)
218 casprintf_buffer_push_char(buffer, buffer_size, '-');
219
220 size_t i = (literal_size != 0 ? literal_size : numbuf_size) - 1;
221
222 if (zero && literal_size > numbuf_size)
223 {
224 for (size_t zi = 0; zi < (literal_size - numbuf_size); zi++)
225 casprintf_buffer_push_char(buffer, buffer_size, '0');
226 }
227
228 while (true)
229 {
230 casprintf_buffer_push_char(buffer, buffer_size, numbuf[i]);
231
232 if (i == 0)
233 break;
234
235 i--;
236 }
237
238 free(numbuf);
239 }
240 else if (flags & FMT_STR)
241 {
242 const char *str = va_arg(*args, const char *);
243 casprintf_buffer_concat_str(buffer, buffer_size, str);
244 }
245 }
246
247 char *casprintf(const char *format, ...)
248 {
249 char *buffer = NULL;
250 size_t buffer_size = 0;
251 va_list args;
252
253 va_start(args, format);
254
255 while (*format)
256 {
257 if (*format == '%')
258 {
259 format++;
260
261 size_t literal_size = 0;
262 bool zero = false;
263 const char *old_format = format;
264 format_flags_t length_flags =
265 casprintf_length_flags(format, &format, &literal_size, &zero);
266 format_flags_t type_flag = casprintf_type_flag(*format);
267
268 if (type_flag == FMT_UNKNOWN)
269 {
270 casprintf_buffer_push_char(&buffer, &buffer_size, '%');
271
272 for (long int i = 0; i < (format - old_format); i++)
273 casprintf_buffer_push_char(
274 &buffer, &buffer_size, old_format[i]);
275
276 continue;
277 }
278
279 if (type_flag == 0)
280 type_flag = FMT_INT | FMT_DECIMAL;
281 else
282 format++;
283
284 format_flags_t flags = length_flags | type_flag;
285
286 casprintf_concat(
287 &args, &buffer, &buffer_size, flags, literal_size, zero);
288 continue;
289 }
290 else
291 {
292 casprintf_buffer_push_char(&buffer, &buffer_size, *format);
293 }
294
295 format++;
296 }
297
298 casprintf_buffer_push_char(&buffer, &buffer_size, 0);
299 va_end(args);
300 return buffer;
301 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26