source: bootcd/isolinux/syslinux-6.03/com32/lib/vsnprintf.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * vsnprintf.c
3 *
4 * vsnprintf(), from which the rest of the printf()
5 * family is built
6 */
7
8#include <stdarg.h>
9#include <stddef.h>
10#include <inttypes.h>
11#include <string.h>
12#include <limits.h>
13#include <stdio.h>
14
15enum flags {
16    FL_ZERO = 0x01,             /* Zero modifier */
17    FL_MINUS = 0x02,            /* Minus modifier */
18    FL_PLUS = 0x04,             /* Plus modifier */
19    FL_TICK = 0x08,             /* ' modifier */
20    FL_SPACE = 0x10,            /* Space modifier */
21    FL_HASH = 0x20,             /* # modifier */
22    FL_SIGNED = 0x40,           /* Number is signed */
23    FL_UPPER = 0x80             /* Upper case digits */
24};
25
26/* These may have to be adjusted on certain implementations */
27enum ranks {
28    rank_char = -2,
29    rank_short = -1,
30    rank_int = 0,
31    rank_long = 1,
32    rank_longlong = 2
33};
34
35#define MIN_RANK        rank_char
36#define MAX_RANK        rank_longlong
37
38#define INTMAX_RANK     rank_longlong
39#define SIZE_T_RANK     rank_long
40#define PTRDIFF_T_RANK  rank_long
41
42#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
43
44static size_t
45format_int(char *q, size_t n, uintmax_t val, enum flags flags,
46           int base, int width, int prec)
47{
48    char *qq;
49    size_t o = 0, oo;
50    static const char lcdigits[] = "0123456789abcdef";
51    static const char ucdigits[] = "0123456789ABCDEF";
52    const char *digits;
53    uintmax_t tmpval;
54    int minus = 0;
55    int ndigits = 0, nchars;
56    int tickskip, b4tick;
57
58    /* Select type of digits */
59    digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
60
61    /* If signed, separate out the minus */
62    if (flags & FL_SIGNED && (intmax_t) val < 0) {
63        minus = 1;
64        val = (uintmax_t) (-(intmax_t) val);
65    }
66
67    /* Count the number of digits needed.  This returns zero for 0. */
68    tmpval = val;
69    while (tmpval) {
70        tmpval /= base;
71        ndigits++;
72    }
73
74    /* Adjust ndigits for size of output */
75
76    if (flags & FL_HASH && base == 8) {
77        if (prec < ndigits + 1)
78            prec = ndigits + 1;
79    }
80
81    if (ndigits < prec) {
82        ndigits = prec;         /* Mandatory number padding */
83    } else if (val == 0) {
84        ndigits = 1;            /* Zero still requires space */
85    }
86
87    /* For ', figure out what the skip should be */
88    if (flags & FL_TICK) {
89        tickskip = (base == 16) ? 4 : 3;
90    } else {
91        tickskip = ndigits;     /* No tick marks */
92    }
93
94    /* Tick marks aren't digits, but generated by the number converter */
95    ndigits += (ndigits - 1) / tickskip;
96
97    /* Now compute the number of nondigits */
98    nchars = ndigits;
99
100    if (minus || (flags & (FL_PLUS | FL_SPACE)))
101        nchars++;               /* Need space for sign */
102    if ((flags & FL_HASH) && base == 16) {
103        nchars += 2;            /* Add 0x for hex */
104    }
105
106    /* Emit early space padding */
107    if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) {
108        while (width > nchars) {
109            EMIT(' ');
110            width--;
111        }
112    }
113
114    /* Emit nondigits */
115    if (minus)
116        EMIT('-');
117    else if (flags & FL_PLUS)
118        EMIT('+');
119    else if (flags & FL_SPACE)
120        EMIT(' ');
121
122    if ((flags & FL_HASH) && base == 16) {
123        EMIT('0');
124        EMIT((flags & FL_UPPER) ? 'X' : 'x');
125    }
126
127    /* Emit zero padding */
128    if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) {
129        while (width > nchars) {
130            EMIT('0');
131            width--;
132        }
133    }
134
135    /* Generate the number.  This is done from right to left. */
136    q += ndigits;               /* Advance the pointer to end of number */
137    o += ndigits;
138    qq = q;
139    oo = o;                     /* Temporary values */
140
141    b4tick = tickskip;
142    while (ndigits > 0) {
143        if (!b4tick--) {
144            qq--;
145            oo--;
146            ndigits--;
147            if (oo < n)
148                *qq = '_';
149            b4tick = tickskip - 1;
150        }
151        qq--;
152        oo--;
153        ndigits--;
154        if (oo < n)
155            *qq = digits[val % base];
156        val /= base;
157    }
158
159    /* Emit late space padding */
160    while ((flags & FL_MINUS) && width > nchars) {
161        EMIT(' ');
162        width--;
163    }
164
165    return o;
166}
167
168int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
169{
170    const char *p = format;
171    char ch;
172    char *q = buffer;
173    size_t o = 0;               /* Number of characters output */
174    uintmax_t val = 0;
175    int rank = rank_int;        /* Default rank */
176    int width = 0;
177    int prec = -1;
178    int base;
179    size_t sz;
180    enum flags flags = 0;
181    enum {
182        st_normal,              /* Ground state */
183        st_flags,               /* Special flags */
184        st_width,               /* Field width */
185        st_prec,                /* Field precision */
186        st_modifiers            /* Length or conversion modifiers */
187    } state = st_normal;
188    const char *sarg;           /* %s string argument */
189    char carg;                  /* %c char argument */
190    int slen;                   /* String length */
191
192    while ((ch = *p++)) {
193        switch (state) {
194        case st_normal:
195            if (ch == '%') {
196                state = st_flags;
197                flags = 0;
198                rank = rank_int;
199                width = 0;
200                prec = -1;
201            } else {
202                EMIT(ch);
203            }
204            break;
205
206        case st_flags:
207            switch (ch) {
208            case '-':
209                flags |= FL_MINUS;
210                break;
211            case '+':
212                flags |= FL_PLUS;
213                break;
214            case '\'':
215                flags |= FL_TICK;
216                break;
217            case ' ':
218                flags |= FL_SPACE;
219                break;
220            case '#':
221                flags |= FL_HASH;
222                break;
223            case '0':
224                flags |= FL_ZERO;
225                break;
226            default:
227                state = st_width;
228                p--;            /* Process this character again */
229                break;
230            }
231            break;
232
233        case st_width:
234            if (ch >= '0' && ch <= '9') {
235                width = width * 10 + (ch - '0');
236            } else if (ch == '*') {
237                width = va_arg(ap, int);
238                if (width < 0) {
239                    width = -width;
240                    flags |= FL_MINUS;
241                }
242            } else if (ch == '.') {
243                prec = 0;       /* Precision given */
244                state = st_prec;
245            } else {
246                state = st_modifiers;
247                p--;            /* Process this character again */
248            }
249            break;
250
251        case st_prec:
252            if (ch >= '0' && ch <= '9') {
253                prec = prec * 10 + (ch - '0');
254            } else if (ch == '*') {
255                prec = va_arg(ap, int);
256                if (prec < 0)
257                    prec = -1;
258            } else {
259                state = st_modifiers;
260                p--;            /* Process this character again */
261            }
262            break;
263
264        case st_modifiers:
265            switch (ch) {
266                /* Length modifiers - nonterminal sequences */
267            case 'h':
268                rank--;         /* Shorter rank */
269                break;
270            case 'l':
271                rank++;         /* Longer rank */
272                break;
273            case 'j':
274                rank = INTMAX_RANK;
275                break;
276            case 'z':
277                rank = SIZE_T_RANK;
278                break;
279            case 't':
280                rank = PTRDIFF_T_RANK;
281                break;
282            case 'L':
283            case 'q':
284                rank += 2;
285                break;
286            default:
287                /* Output modifiers - terminal sequences */
288                state = st_normal;      /* Next state will be normal */
289                if (rank < MIN_RANK)    /* Canonicalize rank */
290                    rank = MIN_RANK;
291                else if (rank > MAX_RANK)
292                    rank = MAX_RANK;
293
294                switch (ch) {
295                case 'P':       /* Upper case pointer */
296                    flags |= FL_UPPER;
297                    /* fall through */
298                case 'p':       /* Pointer */
299                    base = 16;
300                    prec = (CHAR_BIT * sizeof(void *) + 3) / 4;
301                    flags |= FL_HASH;
302                    val = (uintmax_t) (uintptr_t) va_arg(ap, void *);
303                    goto is_integer;
304
305                case 'd':       /* Signed decimal output */
306                case 'i':
307                    base = 10;
308                    flags |= FL_SIGNED;
309                    switch (rank) {
310                    case rank_char:
311                        /* Yes, all these casts are needed... */
312                        val =
313                            (uintmax_t) (intmax_t) (signed char)va_arg(ap,
314                                                                       signed
315                                                                       int);
316                        break;
317                    case rank_short:
318                        val =
319                            (uintmax_t) (intmax_t) (signed short)va_arg(ap,
320                                                                        signed
321                                                                        int);
322                        break;
323                    case rank_int:
324                        val = (uintmax_t) (intmax_t) va_arg(ap, signed int);
325                        break;
326                    case rank_long:
327                        val = (uintmax_t) (intmax_t) va_arg(ap, signed long);
328                        break;
329                    case rank_longlong:
330                        val =
331                            (uintmax_t) (intmax_t) va_arg(ap, signed long long);
332                        break;
333                    }
334                    goto is_integer;
335                case 'o':       /* Octal */
336                    base = 8;
337                    goto is_unsigned;
338                case 'u':       /* Unsigned decimal */
339                    base = 10;
340                    goto is_unsigned;
341                case 'X':       /* Upper case hexadecimal */
342                    flags |= FL_UPPER;
343                    /* fall through */
344                case 'x':       /* Hexadecimal */
345                    base = 16;
346                    goto is_unsigned;
347
348is_unsigned:
349                    switch (rank) {
350                    case rank_char:
351                        val =
352                            (uintmax_t) (unsigned char)va_arg(ap, unsigned int);
353                        break;
354                    case rank_short:
355                        val =
356                            (uintmax_t) (unsigned short)va_arg(ap,
357                                                               unsigned int);
358                        break;
359                    case rank_int:
360                        val = (uintmax_t) va_arg(ap, unsigned int);
361                        break;
362                    case rank_long:
363                        val = (uintmax_t) va_arg(ap, unsigned long);
364                        break;
365                    case rank_longlong:
366                        val = (uintmax_t) va_arg(ap, unsigned long long);
367                        break;
368                    }
369                    /* fall through */
370
371is_integer:
372                    sz = format_int(q, (o < n) ? n - o : 0, val, flags, base,
373                                    width, prec);
374                    q += sz;
375                    o += sz;
376                    break;
377
378                case 'c':       /* Character */
379                    carg = (char)va_arg(ap, int);
380                    sarg = &carg;
381                    slen = 1;
382                    goto is_string;
383                case 's':       /* String */
384                    sarg = va_arg(ap, const char *);
385                    sarg = sarg ? sarg : "(null)";
386                    slen = strlen(sarg);
387                    goto is_string;
388
389is_string:
390                    {
391                        char sch;
392                        int i;
393
394                        if (prec != -1 && slen > prec)
395                            slen = prec;
396
397                        if (width > slen && !(flags & FL_MINUS)) {
398                            char pad = (flags & FL_ZERO) ? '0' : ' ';
399                            while (width > slen) {
400                                EMIT(pad);
401                                width--;
402                            }
403                        }
404                        for (i = slen; i; i--) {
405                            sch = *sarg++;
406                            EMIT(sch);
407                        }
408                        if (width > slen && (flags & FL_MINUS)) {
409                            while (width > slen) {
410                                EMIT(' ');
411                                width--;
412                            }
413                        }
414                    }
415                    break;
416
417                case 'n':       /* Output the number of characters written */
418                    {
419                        switch (rank) {
420                        case rank_char:
421                            *va_arg(ap, signed char *) = o;
422                            break;
423                        case rank_short:
424                            *va_arg(ap, signed short *) = o;
425                            break;
426                        case rank_int:
427                            *va_arg(ap, signed int *) = o;
428                            break;
429                        case rank_long:
430                            *va_arg(ap, signed long *) = o;
431                            break;
432                        case rank_longlong:
433                            *va_arg(ap, signed long long *) = o;
434                            break;
435                        }
436                    }
437                    break;
438
439                default:        /* Anything else, including % */
440                    EMIT(ch);
441                    break;
442                }
443            }
444        }
445    }
446
447    /* Null-terminate the string */
448    if (o < n)
449        *q = '\0';              /* No overflow */
450    else if (n > 0)
451        buffer[n - 1] = '\0';   /* Overflow - terminate at end of buffer */
452
453    return o;
454}
Note: See TracBrowser for help on using the repository browser.