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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (hide 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 rakinar2 575 #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