1 /*
2 **  OSSP uuid - Universally Unique Identifier
3 **  Copyright (c) 2004-2008 Ralf S. Engelschall <rse@engelschall.com>
4 **  Copyright (c) 2004-2008 The OSSP Project <http://www.ossp.org/>
5 **
6 **  This file is part of OSSP uuid, a library for the generation
7 **  of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/
8 **
9 **  Permission to use, copy, modify, and distribute this software for
10 **  any purpose with or without fee is hereby granted, provided that
11 **  the above copyright notice and this permission notice appear in all
12 **  copies.
13 **
14 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
15 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
18 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
21 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
24 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 **  SUCH DAMAGE.
26 **
27 **  uuid_str.c: string formatting functions
28 */
29 
30 /*
31  * Copyright Patrick Powell 1995
32  * This code is based on code written by Patrick Powell <papowell@astart.com>
33  * It may be used for any purpose as long as this notice remains intact
34  * on all source code distributions.
35  */
36 
37 /*
38  * This code contains numerious changes and enhancements which were
39  * made by lots of contributors over the last years to Patrick Powell's
40  * original code:
41  *
42  * o Patrick Powell <papowell@astart.com>      (1995)
43  * o Brandon Long <blong@fiction.net>          (1996, for Mutt)
44  * o Thomas Roessler <roessler@guug.de>        (1998, for Mutt)
45  * o Michael Elkins <me@cs.hmc.edu>            (1998, for Mutt)
46  * o Andrew Tridgell <tridge@samba.org>        (1998, for Samba)
47  * o Luke Mewburn <lukem@netbsd.org>           (1999, for LukemFTP)
48  * o Ralf S. Engelschall <rse@engelschall.com> (1999, for OSSP)
49  */
50 
51 /* own headers (part 1/2) */
52 #include "uuid_ac.h"
53 
54 /* system headers */
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <string.h>
58 #include <ctype.h>
59 
60 /* own headers (part 2/2) */
61 #include "uuid_str.h"
62 
63 #if HAVE_LONG_LONG
64 #define LLONG long long
65 #else
66 #define LLONG long
67 #endif
68 
69 #if HAVE_LONG_DOUBLE
70 #define LDOUBLE long double
71 #else
72 #define LDOUBLE double
73 #endif
74 
75 static void fmtstr     (char *, size_t *, size_t, char *, int, int, int);
76 static void fmtint     (char *, size_t *, size_t, LLONG, int, int, int, int);
77 static void fmtfp      (char *, size_t *, size_t, LDOUBLE, int, int, int);
78 static void dopr_outch (char *, size_t *, size_t, int);
79 
80 /* format read states */
81 #define DP_S_DEFAULT    0
82 #define DP_S_FLAGS      1
83 #define DP_S_MIN        2
84 #define DP_S_DOT        3
85 #define DP_S_MAX        4
86 #define DP_S_MOD        5
87 #define DP_S_CONV       6
88 #define DP_S_DONE       7
89 
90 /* format flags - Bits */
91 #define DP_F_MINUS      (1 << 0)
92 #define DP_F_PLUS       (1 << 1)
93 #define DP_F_SPACE      (1 << 2)
94 #define DP_F_NUM        (1 << 3)
95 #define DP_F_ZERO       (1 << 4)
96 #define DP_F_UP         (1 << 5)
97 #define DP_F_UNSIGNED   (1 << 6)
98 
99 /* conversion flags */
100 #define DP_C_SHORT      1
101 #define DP_C_LONG       2
102 #define DP_C_LDOUBLE    3
103 #define DP_C_LLONG      4
104 
105 /* some handy macros */
106 #define char_to_int(p) (p - '0')
107 #define STR_MAX(p,q) ((p >= q) ? p : q)
108 #define NUL '\0'
109 
110 static void
dopr(char * buffer,size_t maxlen,size_t * retlen,const char * format,va_list args)111 dopr(
112     char *buffer,
113     size_t maxlen,
114     size_t *retlen,
115     const char *format,
116     va_list args)
117 {
118     char ch;
119     LLONG value;
120     LDOUBLE fvalue;
121     char *strvalue;
122     int min;
123     int max;
124     int state;
125     int flags;
126     int cflags;
127     size_t currlen;
128 
129     state = DP_S_DEFAULT;
130     flags = currlen = cflags = min = 0;
131     max = -1;
132     ch = *format++;
133 
134     if (buffer == NULL)
135         maxlen = 999999;
136 
137     while (state != DP_S_DONE) {
138         if ((ch == NUL) || (currlen >= maxlen))
139             state = DP_S_DONE;
140 
141         switch (state) {
142         case DP_S_DEFAULT:
143             if (ch == '%')
144                 state = DP_S_FLAGS;
145             else
146                 dopr_outch(buffer, &currlen, maxlen, ch);
147             ch = *format++;
148             break;
149         case DP_S_FLAGS:
150             switch (ch) {
151                 case '-':
152                     flags |= DP_F_MINUS;
153                     ch = *format++;
154                     break;
155                 case '+':
156                     flags |= DP_F_PLUS;
157                     ch = *format++;
158                     break;
159                 case ' ':
160                     flags |= DP_F_SPACE;
161                     ch = *format++;
162                     break;
163                 case '#':
164                     flags |= DP_F_NUM;
165                     ch = *format++;
166                     break;
167                 case '0':
168                     flags |= DP_F_ZERO;
169                     ch = *format++;
170                     break;
171                 default:
172                     state = DP_S_MIN;
173                     break;
174             }
175             break;
176         case DP_S_MIN:
177             if (isdigit((unsigned char)ch)) {
178                 min = 10 * min + char_to_int(ch);
179                 ch = *format++;
180             } else if (ch == '*') {
181                 min = va_arg(args, int);
182                 ch = *format++;
183                 state = DP_S_DOT;
184             } else
185                 state = DP_S_DOT;
186             break;
187         case DP_S_DOT:
188             if (ch == '.') {
189                 state = DP_S_MAX;
190                 ch = *format++;
191             } else
192                 state = DP_S_MOD;
193             break;
194         case DP_S_MAX:
195             if (isdigit((unsigned char)ch)) {
196                 if (max < 0)
197                     max = 0;
198                 max = 10 * max + char_to_int(ch);
199                 ch = *format++;
200             } else if (ch == '*') {
201                 max = va_arg(args, int);
202                 ch = *format++;
203                 state = DP_S_MOD;
204             } else
205                 state = DP_S_MOD;
206             break;
207         case DP_S_MOD:
208             switch (ch) {
209                 case 'h':
210                     cflags = DP_C_SHORT;
211                     ch = *format++;
212                     break;
213                 case 'l':
214                     if (*format == 'l') {
215                         cflags = DP_C_LLONG;
216                         format++;
217                     } else
218                         cflags = DP_C_LONG;
219                     ch = *format++;
220                     break;
221                 case 'q':
222                     cflags = DP_C_LLONG;
223                     ch = *format++;
224                     break;
225                 case 'L':
226                     cflags = DP_C_LDOUBLE;
227                     ch = *format++;
228                     break;
229                 default:
230                     break;
231             }
232             state = DP_S_CONV;
233             break;
234         case DP_S_CONV:
235             switch (ch) {
236             case 'd':
237             case 'i':
238                 switch (cflags) {
239                 case DP_C_SHORT:
240                     value = (short int)va_arg(args, int);
241                     break;
242                 case DP_C_LONG:
243                     value = va_arg(args, long int);
244                     break;
245                 case DP_C_LLONG:
246                     value = va_arg(args, LLONG);
247                     break;
248                 default:
249                     value = va_arg(args, int);
250                     break;
251                 }
252                 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
253                 break;
254             case 'X':
255                 flags |= DP_F_UP;
256                 /* FALLTHROUGH */
257             case 'x':
258             case 'o':
259             case 'u':
260                 flags |= DP_F_UNSIGNED;
261                 switch (cflags) {
262                     case DP_C_SHORT:
263                         value = (unsigned short int)va_arg(args, unsigned int);
264                         break;
265                     case DP_C_LONG:
266                         value = (LLONG)va_arg(args, unsigned long int);
267                         break;
268                     case DP_C_LLONG:
269                         value = va_arg(args, unsigned LLONG);
270                         break;
271                     default:
272                         value = (LLONG)va_arg(args, unsigned int);
273                         break;
274                 }
275                 fmtint(buffer, &currlen, maxlen, value,
276                        ch == 'o' ? 8 : (ch == 'u' ? 10 : 16),
277                        min, max, flags);
278                 break;
279             case 'f':
280                 if (cflags == DP_C_LDOUBLE)
281                     fvalue = va_arg(args, LDOUBLE);
282                 else
283                     fvalue = va_arg(args, double);
284                 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
285                 break;
286             case 'E':
287                 flags |= DP_F_UP;
288                 /* FALLTHROUGH */
289             case 'e':
290                 if (cflags == DP_C_LDOUBLE)
291                     fvalue = va_arg(args, LDOUBLE);
292                 else
293                     fvalue = va_arg(args, double);
294                 break;
295             case 'G':
296                 flags |= DP_F_UP;
297                 /* FALLTHROUGH */
298             case 'g':
299                 if (cflags == DP_C_LDOUBLE)
300                     fvalue = va_arg(args, LDOUBLE);
301                 else
302                     fvalue = va_arg(args, double);
303                 break;
304             case 'c':
305                 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
306                 break;
307             case 's':
308                 strvalue = va_arg(args, char *);
309                 if (max < 0)
310                     max = maxlen;
311                 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
312                 break;
313             case 'p':
314                 value = (long)va_arg(args, void *);
315                 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
316                 break;
317             case 'n': /* XXX */
318                 if (cflags == DP_C_SHORT) {
319                     short int *num;
320                     num = va_arg(args, short int *);
321                     *num = currlen;
322                 } else if (cflags == DP_C_LONG) { /* XXX */
323                     long int *num;
324                     num = va_arg(args, long int *);
325                     *num = (long int) currlen;
326                 } else if (cflags == DP_C_LLONG) { /* XXX */
327                     LLONG *num;
328                     num = va_arg(args, LLONG *);
329                     *num = (LLONG) currlen;
330                 } else {
331                     int *num;
332                     num = va_arg(args, int *);
333                     *num = currlen;
334                 }
335                 break;
336             case '%':
337                 dopr_outch(buffer, &currlen, maxlen, ch);
338                 break;
339             case 'w':
340                 /* not supported yet, treat as next char */
341                 ch = *format++;
342                 break;
343             default:
344                 /* unknown, skip */
345                 break;
346             }
347             ch = *format++;
348             state = DP_S_DEFAULT;
349             flags = cflags = min = 0;
350             max = -1;
351             break;
352         case DP_S_DONE:
353             break;
354         default:
355             break;
356         }
357     }
358     if (currlen >= maxlen - 1)
359         currlen = maxlen - 1;
360     if (buffer != NULL)
361         buffer[currlen] = NUL;
362     *retlen = currlen;
363     return;
364 }
365 
366 static void
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max)367 fmtstr(
368     char *buffer,
369     size_t *currlen,
370     size_t maxlen,
371     char *value,
372     int flags,
373     int min,
374     int max)
375 {
376     int padlen, strln;
377     int cnt = 0;
378 
379     if (value == NULL)
380         value = "<NULL>";
381     for (strln = 0; value[strln] != '\0'; strln++)
382         ;
383     padlen = min - strln;
384     if (padlen < 0)
385         padlen = 0;
386     if (flags & DP_F_MINUS)
387         padlen = -padlen;
388 
389     while ((padlen > 0) && (cnt < max)) {
390         dopr_outch(buffer, currlen, maxlen, ' ');
391         --padlen;
392         ++cnt;
393     }
394     while (*value && (cnt < max)) {
395         dopr_outch(buffer, currlen, maxlen, *value++);
396         ++cnt;
397     }
398     while ((padlen < 0) && (cnt < max)) {
399         dopr_outch(buffer, currlen, maxlen, ' ');
400         ++padlen;
401         ++cnt;
402     }
403 }
404 
405 static void
fmtint(char * buffer,size_t * currlen,size_t maxlen,LLONG value,int base,int min,int max,int flags)406 fmtint(
407     char *buffer,
408     size_t *currlen,
409     size_t maxlen,
410     LLONG value,
411     int base,
412     int min,
413     int max,
414     int flags)
415 {
416     int signvalue = 0;
417     unsigned LLONG uvalue;
418     char convert[20];
419     int place = 0;
420     int spadlen = 0;
421     int zpadlen = 0;
422     int caps = 0;
423 
424     if (max < 0)
425         max = 0;
426     uvalue = value;
427     if (!(flags & DP_F_UNSIGNED)) {
428         if (value < 0) {
429             signvalue = '-';
430             uvalue = -value;
431         } else if (flags & DP_F_PLUS)
432             signvalue = '+';
433         else if (flags & DP_F_SPACE)
434             signvalue = ' ';
435     }
436     if (flags & DP_F_UP)
437         caps = 1;
438     do {
439         convert[place++] =
440             (caps ? "0123456789ABCDEF" : "0123456789abcdef")
441             [uvalue % (unsigned) base];
442         uvalue = (uvalue / (unsigned) base);
443     } while (uvalue && (place < 20));
444     if (place == 20)
445         place--;
446     convert[place] = 0;
447 
448     zpadlen = max - place;
449     spadlen = min - STR_MAX(max, place) - (signvalue ? 1 : 0);
450     if (zpadlen < 0)
451         zpadlen = 0;
452     if (spadlen < 0)
453         spadlen = 0;
454     if (flags & DP_F_ZERO) {
455         zpadlen = STR_MAX(zpadlen, spadlen);
456         spadlen = 0;
457     }
458     if (flags & DP_F_MINUS)
459         spadlen = -spadlen;
460 
461     /* spaces */
462     while (spadlen > 0) {
463         dopr_outch(buffer, currlen, maxlen, ' ');
464         --spadlen;
465     }
466 
467     /* sign */
468     if (signvalue)
469         dopr_outch(buffer, currlen, maxlen, signvalue);
470 
471     /* zeros */
472     if (zpadlen > 0) {
473         while (zpadlen > 0) {
474             dopr_outch(buffer, currlen, maxlen, '0');
475             --zpadlen;
476         }
477     }
478     /* digits */
479     while (place > 0)
480         dopr_outch(buffer, currlen, maxlen, convert[--place]);
481 
482     /* left justified spaces */
483     while (spadlen < 0) {
484         dopr_outch(buffer, currlen, maxlen, ' ');
485         ++spadlen;
486     }
487     return;
488 }
489 
490 static LDOUBLE
math_abs(LDOUBLE value)491 math_abs(LDOUBLE value)
492 {
493     LDOUBLE result = value;
494     if (value < 0)
495         result = -value;
496     return result;
497 }
498 
499 static LDOUBLE
math_pow10(int exponent)500 math_pow10(int exponent)
501 {
502     LDOUBLE result = 1;
503     while (exponent > 0) {
504         result *= 10;
505         exponent--;
506     }
507     return result;
508 }
509 
510 static long
math_round(LDOUBLE value)511 math_round(LDOUBLE value)
512 {
513     long intpart;
514     intpart = (long) value;
515     value = value - intpart;
516     if (value >= 0.5)
517         intpart++;
518     return intpart;
519 }
520 
521 static void
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)522 fmtfp(
523     char *buffer,
524     size_t *currlen,
525     size_t maxlen,
526     LDOUBLE fvalue,
527     int min,
528     int max,
529     int flags)
530 {
531     int signvalue = 0;
532     LDOUBLE ufvalue;
533     char iconvert[20];
534     char fconvert[20];
535     int iplace = 0;
536     int fplace = 0;
537     int padlen = 0;
538     int zpadlen = 0;
539     int caps = 0;
540     long intpart;
541     long fracpart;
542 
543     if (max < 0)
544         max = 6;
545     ufvalue = math_abs(fvalue);
546     if (fvalue < 0)
547         signvalue = '-';
548     else if (flags & DP_F_PLUS)
549         signvalue = '+';
550     else if (flags & DP_F_SPACE)
551         signvalue = ' ';
552 
553     intpart = (long)ufvalue;
554 
555     /* sorry, we only support 9 digits past the decimal because of our
556        conversion method */
557     if (max > 9)
558         max = 9;
559 
560     /* we "cheat" by converting the fractional part to integer by
561        multiplying by a factor of 10 */
562     fracpart = math_round((math_pow10(max)) * (ufvalue - intpart));
563 
564     if (fracpart >= math_pow10(max)) {
565         intpart++;
566         fracpart -= (long)math_pow10(max);
567     }
568 
569     /* convert integer part */
570     do {
571         iconvert[iplace++] =
572             (caps ? "0123456789ABCDEF"
573               : "0123456789abcdef")[intpart % 10];
574         intpart = (intpart / 10);
575     } while (intpart && (iplace < 20));
576     if (iplace == 20)
577         iplace--;
578     iconvert[iplace] = 0;
579 
580     /* convert fractional part */
581     do {
582         fconvert[fplace++] =
583             (caps ? "0123456789ABCDEF"
584               : "0123456789abcdef")[fracpart % 10];
585         fracpart = (fracpart / 10);
586     } while (fracpart && (fplace < 20));
587     if (fplace == 20)
588         fplace--;
589     fconvert[fplace] = 0;
590 
591     /* -1 for decimal point, another -1 if we are printing a sign */
592     padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
593     zpadlen = max - fplace;
594     if (zpadlen < 0)
595         zpadlen = 0;
596     if (padlen < 0)
597         padlen = 0;
598     if (flags & DP_F_MINUS)
599         padlen = -padlen;
600 
601     if ((flags & DP_F_ZERO) && (padlen > 0)) {
602         if (signvalue) {
603             dopr_outch(buffer, currlen, maxlen, signvalue);
604             --padlen;
605             signvalue = 0;
606         }
607         while (padlen > 0) {
608             dopr_outch(buffer, currlen, maxlen, '0');
609             --padlen;
610         }
611     }
612     while (padlen > 0) {
613         dopr_outch(buffer, currlen, maxlen, ' ');
614         --padlen;
615     }
616     if (signvalue)
617         dopr_outch(buffer, currlen, maxlen, signvalue);
618 
619     while (iplace > 0)
620         dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
621 
622     /*
623      * Decimal point. This should probably use locale to find the correct
624      * char to print out.
625      */
626     if (max > 0) {
627         dopr_outch(buffer, currlen, maxlen, '.');
628 
629         while (fplace > 0)
630             dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
631     }
632     while (zpadlen > 0) {
633         dopr_outch(buffer, currlen, maxlen, '0');
634         --zpadlen;
635     }
636 
637     while (padlen < 0) {
638         dopr_outch(buffer, currlen, maxlen, ' ');
639         ++padlen;
640     }
641     return;
642 }
643 
644 static void
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,int c)645 dopr_outch(
646     char *buffer,
647     size_t *currlen,
648     size_t maxlen,
649     int c)
650 {
651     if (*currlen < maxlen) {
652         if (buffer != NULL)
653             buffer[(*currlen)] = (char)c;
654         (*currlen)++;
655     }
656     return;
657 }
658 
659 int
str_vsnprintf(char * str,size_t count,const char * fmt,va_list args)660 str_vsnprintf(
661     char *str,
662     size_t count,
663     const char *fmt,
664     va_list args)
665 {
666     size_t retlen;
667 
668     if (str != NULL)
669         str[0] = NUL;
670     dopr(str, count, &retlen, fmt, args);
671     return retlen;
672 }
673 
674 int
str_snprintf(char * str,size_t count,const char * fmt,...)675 str_snprintf(
676     char *str,
677     size_t count,
678     const char *fmt,
679     ...)
680 {
681     va_list ap;
682     int rv;
683 
684     va_start(ap, fmt);
685     rv = str_vsnprintf(str, count, fmt, ap);
686     va_end(ap);
687     return rv;
688 }
689 
690 char *
str_vasprintf(const char * fmt,va_list ap)691 str_vasprintf(
692     const char *fmt,
693     va_list ap)
694 {
695     char *rv;
696     int n;
697     va_list ap_tmp;
698 
699     va_copy(ap_tmp, ap);
700     n = str_vsnprintf(NULL, 0, fmt, ap_tmp);
701     if ((rv = (char *)malloc(n+1)) == NULL)
702         return NULL;
703     str_vsnprintf(rv, n+1, fmt, ap);
704     return rv;
705 }
706 
707 char *
str_asprintf(const char * fmt,...)708 str_asprintf(
709     const char *fmt,
710     ...)
711 {
712     va_list ap;
713     char *rv;
714 
715     va_start(ap, fmt);
716     rv = str_vasprintf(fmt, ap);
717     va_end(ap);
718     return rv;
719 }
720 
721 int
str_vrsprintf(char ** str,const char * fmt,va_list ap)722 str_vrsprintf(
723     char **str,
724     const char *fmt,
725     va_list ap)
726 {
727     int rv;
728     size_t n;
729     va_list ap_tmp;
730 
731     if (str == NULL)
732         return -1;
733     if (*str == NULL) {
734         *str = str_vasprintf(fmt, ap);
735         rv = strlen(*str);
736     }
737     else {
738         va_copy(ap_tmp, ap);
739         n = strlen(*str);
740         rv = str_vsnprintf(NULL, 0, fmt, ap_tmp);
741         if ((*str = (char *)realloc(*str, n+rv+1)) == NULL)
742             return -1;
743         str_vsnprintf((*str)+n, rv+1, fmt, ap);
744     }
745     return rv;
746 }
747 
748 int
str_rsprintf(char ** str,const char * fmt,...)749 str_rsprintf(
750     char **str,
751     const char *fmt,
752     ...)
753 {
754     va_list ap;
755     int rv;
756 
757     va_start(ap, fmt);
758     rv = str_vrsprintf(str, fmt, ap);
759     va_end(ap);
760     return rv;
761 }
762 
763