root readme, directory structure, and copyright headers
[visor] / libvisor / src / viprintf.c
1 /*
2 visor - lightweight system-independent embeddable text editor framework
3 Copyright (C)  2019 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include "vilibc.h"
19
20 enum {
21         OUT_BUF
22 };
23
24 static int intern_printf(int out, char *buf, unsigned long sz, const char *fmt, va_list ap);
25 static void bwrite(int out, char *buf, unsigned long buf_sz, char *str, int sz);
26 static void utoa(unsigned int val, char *buf, int base);
27 static void itoa(int val, char *buf, int base);
28
29 int sprintf(char *buf, const char *fmt, ...)
30 {
31         int res;
32         va_list ap;
33
34         va_start(ap, fmt);
35         res = intern_printf(OUT_BUF, buf, 0, fmt, ap);
36         va_end(ap);
37         return res;
38 }
39
40 int vsprintf(char *buf, const char *fmt, va_list ap)
41 {
42         return intern_printf(OUT_BUF, buf, 0, fmt, ap);
43 }
44
45 int snprintf(char *buf, unsigned long sz, const char *fmt, ...)
46 {
47         int res;
48         va_list ap;
49
50         va_start(ap, fmt);
51         res = intern_printf(OUT_BUF, buf, sz, fmt, ap);
52         va_end(ap);
53         return res;
54 }
55
56 int vsnprintf(char *buf, unsigned long sz, const char *fmt, va_list ap)
57 {
58         return intern_printf(OUT_BUF, buf, sz, fmt, ap);
59 }
60
61 /* intern_printf provides all the functionality needed by all the printf
62  * variants.
63  * - buf: optional buffer onto which the formatted results are written. If null
64  *   then the output goes to the terminal through putchar calls. This is used
65  *   by the (v)sprintf variants which write to an array of char.
66  * - sz: optional maximum size of the output, 0 means unlimited. This is used
67  *   by the (v)snprintf variants to avoid buffer overflows.
68  * The rest are obvious, format string and variable argument list.
69  */
70 static char *convc = "dioxXucsfeEgGpn%";
71
72 #define IS_CONV(c)      strchr(convc, c)
73
74 #define BUF(x)  ((x) ? (x) + cnum : (x))
75 #define SZ(x)   ((x) ? (x) - cnum : (x))
76
77 static int intern_printf(int out, char *buf, unsigned long sz, const char *fmt, va_list ap)
78 {
79         char conv_buf[32];
80         char *str;
81         int i, slen;
82         const char *fstart = 0;
83
84         /* state */
85         int cnum = 0;
86         int base = 10;
87         int alt = 0;
88         int fwidth = 0;
89         int padc = ' ';
90         int sign = 0;
91         int left_align = 0;
92         int hex_caps = 0;
93         int unsig = 0;
94         int num, unum;
95
96         while(*fmt) {
97                 if(*fmt == '%') {
98                         fstart = fmt++;
99                         continue;
100                 }
101
102                 if(fstart) {
103                         if(IS_CONV(*fmt)) {
104                                 switch(*fmt) {
105                                 case 'X':
106                                         hex_caps = 1;
107
108                                 case 'x':
109                                 case 'p':
110                                         base = 16;
111
112                                         if(alt) {
113                                                 bwrite(out, BUF(buf), SZ(sz), "0x", 2);
114                                                 cnum += 2;
115                                         }
116
117                                 case 'u':
118                                         unsig = 1;
119
120                                         if(0) {
121                                 case 'o':
122                                                 base = 8;
123
124                                                 if(alt) {
125                                                         bwrite(out, BUF(buf), SZ(sz), "0", 1);
126                                                         cnum++;
127                                                 }
128                                         }
129
130                                 case 'd':
131                                 case 'i':
132                                         if(unsig) {
133                                                 unum = va_arg(ap, unsigned int);
134                                                 utoa(unum, conv_buf, base);
135                                         } else {
136                                                 num = va_arg(ap, int);
137                                                 itoa(num, conv_buf, base);
138                                         }
139                                         if(hex_caps) {
140                                                 for(i=0; conv_buf[i]; i++) {
141                                                         conv_buf[i] = toupper(conv_buf[i]);
142                                                 }
143                                         }
144
145                                         slen = strlen(conv_buf);
146
147                                         if(left_align) {
148                                                 if(!unsig && sign && num >= 0) {
149                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
150                                                         cnum++;
151                                                 }
152                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
153                                                 cnum += slen;
154                                                 padc = ' ';
155                                         }
156                                         for(i=slen; i<fwidth; i++) {
157                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
158                                                 cnum++;
159                                         }
160                                         if(!left_align) {
161                                                 if(!unsig && sign && num >= 0) {
162                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
163                                                         cnum++;
164                                                 }
165                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
166                                                 cnum += slen;
167                                         }
168                                         break;
169
170                                 case 'c':
171                                         {
172                                                 char c = va_arg(ap, int);
173                                                 bwrite(out, BUF(buf), SZ(sz), &c, 1);
174                                                 cnum++;
175                                         }
176                                         break;
177
178                                 case 's':
179                                         str = va_arg(ap, char*);
180                                         slen = strlen(str);
181
182                                         if(left_align) {
183                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
184                                                 cnum += slen;
185                                                 padc = ' ';
186                                         }
187                                         for(i=slen; i<fwidth; i++) {
188                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
189                                                 cnum++;
190                                         }
191                                         if(!left_align) {
192                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
193                                                 cnum += slen;
194                                         }
195                                         break;
196
197                                 case 'n':
198                                         *va_arg(ap, int*) = cnum;
199                                         break;
200
201                                 default:
202                                         break;
203                                 }
204
205                                 /* restore default conversion state */
206                                 base = 10;
207                                 alt = 0;
208                                 fwidth = 0;
209                                 padc = ' ';
210                                 hex_caps = 0;
211
212                                 fstart = 0;
213                                 fmt++;
214                         } else {
215                                 switch(*fmt) {
216                                 case '#':
217                                         alt = 1;
218                                         break;
219
220                                 case '+':
221                                         sign = 1;
222                                         break;
223
224                                 case '-':
225                                         left_align = 1;
226                                         break;
227
228                                 case 'l':
229                                 case 'L':
230                                         break;
231
232                                 case '0':
233                                         padc = '0';
234                                         break;
235
236                                 default:
237                                         if(isdigit(*fmt)) {
238                                                 const char *fw = fmt;
239                                                 while(*fmt && isdigit(*fmt)) fmt++;
240
241                                                 fwidth = atoi(fw);
242                                                 continue;
243                                         }
244                                 }
245                                 fmt++;
246                         }
247                 } else {
248                         bwrite(out, BUF(buf), SZ(sz), (char*)fmt++, 1);
249                         cnum++;
250                 }
251         }
252
253         return cnum;
254 }
255
256 /* bwrite is called by intern_printf to transparently handle writing into a
257  * buffer or to the terminal
258  */
259 static void bwrite(int out, char *buf, unsigned long buf_sz, char *str, int sz)
260 {
261         if(out == OUT_BUF) {
262                 if(buf_sz && buf_sz <= sz) sz = buf_sz;
263                 buf[sz] = 0;
264                 memcpy(buf, str, sz);
265         }
266 }
267
268 static void utoa(unsigned int val, char *buf, int base)
269 {
270         static char rbuf[16];
271         char *ptr = rbuf;
272
273         if(val == 0) {
274                 *ptr++ = '0';
275         }
276
277         while(val) {
278                 unsigned int digit = val % base;
279                 *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
280                 val /= base;
281         }
282
283         ptr--;
284
285         while(ptr >= rbuf) {
286                 *buf++ = *ptr--;
287         }
288         *buf = 0;
289 }
290
291 static void itoa(int val, char *buf, int base)
292 {
293         static char rbuf[16];
294         char *ptr = rbuf;
295         int neg = 0;
296
297         if(val < 0) {
298                 neg = 1;
299                 val = -val;
300         }
301
302         if(val == 0) {
303                 *ptr++ = '0';
304         }
305
306         while(val) {
307                 int digit = val % base;
308                 *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
309                 val /= base;
310         }
311
312         if(neg) {
313                 *ptr++ = '-';
314         }
315
316         ptr--;
317
318         while(ptr >= rbuf) {
319                 *buf++ = *ptr--;
320         }
321         *buf = 0;
322 }
323