1 /*
2  * snprintf.c - a portable implementation of snprintf and vsnprintf
3  *
4  */
5 /*
6  * Portions Copyright (C) 2000-2019 The Xastir Group
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24   #include "config.h"
25 #endif  // HAVE_CONFIG_H
26 
27 #include "snprintf.h"
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  * Original:
39  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
40  * A bombproof version of doprnt (dopr) included.
41  * Sigh.  This sort of thing is always nasty do deal with.  Note that
42  * the version here does not include floating point...
43  *
44  * snprintf() is used instead of sprintf() as it does limit checks
45  * for string length.  This covers a nasty loophole.
46  *
47  * The other functions are there to prevent NULL pointers from
48  * causing nast effects.
49  *
50  * More Recently:
51  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
52  *  This was ugly.  It is still ugly.  I opted out of floating point
53  *  numbers, but the formatter understands just about everything
54  *  from the normal C string format, at least as far as I can tell from
55  *  the Solaris 2.5 printf(3S) man page.
56  *
57  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
58  *    Ok, added some minimal floating point support, which means this
59  *    probably requires libm on most operating systems.  Don't yet
60  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
61  *    was pretty badly broken, it just wasn't being exercised in ways
62  *    which showed it, so that's been fixed.  Also, formated the code
63  *    to mutt conventions, and removed dead code left over from the
64  *    original.  Also, there is now a builtin-test, just compile with:
65  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
66  *    and run snprintf for results.
67  *
68  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
69  *    The PGP code was using unsigned hexadecimal formats.
70  *    Unfortunately, unsigned formats simply didn't work.
71  *
72  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
73  *    The original code assumed that both snprintf() and vsnprintf() were
74  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
75  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
76  *
77  *  Andrew Tridgell (tridge@samba.org) Oct 1998
78  *    fixed handling of %.0f
79  *    added test for HAVE_LONG_DOUBLE
80  *
81  *
82  **************************************************************/
83 
84 #include <string.h>
85 #include <ctype.h>
86 #include <sys/types.h>
87 
88 #ifndef HAVE_VSNPRINTF
89 
90 /* varargs declarations: */
91 
92 #if defined(__STDC__)
93   #ifdef HAVE_STDARG_H
94     #include <stdarg.h>
95   #else // HAVE_STDARG_H
96     #ifdef HAVE_STD_ARGS_H
97       #include <std_args.h>
98     #endif  // HAVE_STD_ARGS_H
99   #endif    // HAVE_STDARG_H
100   #include <stdarg.h>
101   #define HAVE_STDARGS  /* let's hope that works everywhere (mj) */
102   #define VA_LOCAL_DECL va_list ap
103   #define VA_START(f) va_start(ap, f)
104   #define VA_SHIFT(v,t); /* no-op for ANSI */
105   #define VA_END va_end(ap)
106 #else   // __STDC__
107   #include <varargs.h>
108   #undef HAVE_STDARGS
109   #define VA_LOCAL_DECL va_list ap
110   #define VA_START(f) va_start(ap)      /* f is ignored! */
111   #define VA_SHIFT(v,t) v = va_arg(ap,t)
112   #define VA_END va_end(ap)
113 #endif  // __STDC__
114 
115 
116 // Must be last include file
117 #include "leak_detection.h"
118 
119 
120 
121 #ifdef HAVE_LONG_DOUBLE
122   #define LDOUBLE long double
123 #else   // HAVE_LONG_DOUBLE
124   #define LDOUBLE double
125 #endif  // HAVE_LONG_DOUBLE
126 
127 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args);
128 static void fmtstr(char *buffer, size_t * currlen, size_t maxlen, char *value,
129                    int flags, int min, int max);
130 static void fmtint(char *buffer, size_t * currlen, size_t maxlen, long value,
131                    int base, int min, int max, int flags);
132 static void fmtfp(char *buffer, size_t * currlen, size_t maxlen,
133                   LDOUBLE fvalue, int min, int max, int flags);
134 static void dopr_outch(char *buffer, size_t * currlen, size_t maxlen, char c);
135 
136 /*
137  * dopr(): poor man's version of doprintf
138  */
139 
140 /* format read states */
141 #define DP_S_DEFAULT 0
142 #define DP_S_FLAGS   1
143 #define DP_S_MIN     2
144 #define DP_S_DOT     3
145 #define DP_S_MAX     4
146 #define DP_S_MOD     5
147 #define DP_S_CONV    6
148 #define DP_S_DONE    7
149 
150 /* format flags - Bits */
151 #define DP_F_MINUS      (1 << 0)
152 #define DP_F_PLUS       (1 << 1)
153 #define DP_F_SPACE      (1 << 2)
154 #define DP_F_NUM        (1 << 3)
155 #define DP_F_ZERO       (1 << 4)
156 #define DP_F_UP         (1 << 5)
157 #define DP_F_UNSIGNED   (1 << 6)
158 
159 /* Conversion Flags */
160 #define DP_C_SHORT   1
161 #define DP_C_LONG    2
162 #define DP_C_LDOUBLE 3
163 
164 #define char_to_int(p) (p - '0')
165 #define MAX(p, q) ((p >= q) ? p : q)
166 
dopr(char * buffer,size_t maxlen,const char * format,va_list args)167 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args)
168 {
169   char ch;
170   long value;
171   LDOUBLE fvalue;
172   char *strvalue;
173   int min;
174   int max;
175   int state;
176   int flags;
177   int cflags;
178   size_t currlen;
179 
180   state = DP_S_DEFAULT;
181   currlen = flags = cflags = min = 0;
182   max = -1;
183   ch = *format++;
184 
185   while (state != DP_S_DONE)
186   {
187     if ((ch == '\0') || (currlen >= maxlen))
188     {
189       state = DP_S_DONE;
190     }
191 
192     switch (state)
193     {
194       case DP_S_DEFAULT:
195         if (ch == '%')
196         {
197           state = DP_S_FLAGS;
198         }
199         else
200         {
201           dopr_outch(buffer, &currlen, maxlen, ch);
202         }
203 
204         ch = *format++;
205         break;
206       case DP_S_FLAGS:
207         switch (ch)
208         {
209           case '-':
210             flags |= DP_F_MINUS;
211             ch = *format++;
212             break;
213           case '+':
214             flags |= DP_F_PLUS;
215             ch = *format++;
216             break;
217           case ' ':
218             flags |= DP_F_SPACE;
219             ch = *format++;
220             break;
221           case '#':
222             flags |= DP_F_NUM;
223             ch = *format++;
224             break;
225           case '0':
226             flags |= DP_F_ZERO;
227             ch = *format++;
228             break;
229           default:
230             state = DP_S_MIN;
231             break;
232         }
233         break;
234       case DP_S_MIN:
235         if (isdigit(ch))
236         {
237           min = 10 * min + char_to_int(ch);
238           ch = *format++;
239         }
240         else if (ch == '*')
241         {
242           min = va_arg(args, int);
243           ch = *format++;
244           state = DP_S_DOT;
245         }
246         else
247         {
248           state = DP_S_DOT;
249         }
250 
251         break;
252       case DP_S_DOT:
253         if (ch == '.')
254         {
255           state = DP_S_MAX;
256           ch = *format++;
257         }
258         else
259         {
260           state = DP_S_MOD;
261         }
262 
263         break;
264       case DP_S_MAX:
265         if (isdigit(ch))
266         {
267           if (max < 0)
268           {
269             max = 0;
270           }
271 
272           max = 10 * max + char_to_int(ch);
273           ch = *format++;
274         }
275         else if (ch == '*')
276         {
277           max = va_arg(args, int);
278           ch = *format++;
279           state = DP_S_MOD;
280         }
281         else
282         {
283           state = DP_S_MOD;
284         }
285 
286         break;
287       case DP_S_MOD:
288         /* Currently, we don't support Long Long, bummer */
289         switch (ch)
290         {
291           case 'h':
292             cflags = DP_C_SHORT;
293             ch = *format++;
294             break;
295           case 'l':
296             cflags = DP_C_LONG;
297             ch = *format++;
298             break;
299           case 'L':
300             cflags = DP_C_LDOUBLE;
301             ch = *format++;
302             break;
303           default:
304             break;
305         }
306         state = DP_S_CONV;
307         break;
308       case DP_S_CONV:
309         switch (ch)
310         {
311           case 'd':
312           case 'i':
313             if (cflags == DP_C_SHORT)
314             {
315               value = va_arg(args, short int);
316             }
317             else if (cflags == DP_C_LONG)
318             {
319               value = va_arg(args, long int);
320             }
321             else
322             {
323               value = va_arg(args, int);
324             }
325 
326             fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
327             break;
328           case 'o':
329             flags |= DP_F_UNSIGNED;
330             if (cflags == DP_C_SHORT)
331             {
332               value = va_arg(args, unsigned short int);
333             }
334             else if (cflags == DP_C_LONG)
335             {
336               value = va_arg(args, unsigned long int);
337             }
338             else
339             {
340               value = va_arg(args, unsigned int);
341             }
342 
343             fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
344             break;
345           case 'u':
346             flags |= DP_F_UNSIGNED;
347             if (cflags == DP_C_SHORT)
348             {
349               value = va_arg(args, unsigned short int);
350             }
351             else if (cflags == DP_C_LONG)
352             {
353               value = va_arg(args, unsigned long int);
354             }
355             else
356             {
357               value = va_arg(args, unsigned int);
358             }
359 
360             fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
361             break;
362           case 'X':
363             flags |= DP_F_UP;
364           case 'x':
365             flags |= DP_F_UNSIGNED;
366             if (cflags == DP_C_SHORT)
367             {
368               value = va_arg(args, unsigned short int);
369             }
370             else if (cflags == DP_C_LONG)
371             {
372               value = va_arg(args, unsigned long int);
373             }
374             else
375             {
376               value = va_arg(args, unsigned int);
377             }
378 
379             fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
380             break;
381           case 'f':
382             if (cflags == DP_C_LDOUBLE)
383             {
384               fvalue = va_arg(args, LDOUBLE);
385             }
386             else
387             {
388               fvalue = va_arg(args, double);
389             }
390 
391             /* um, floating point? */
392             fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
393             break;
394           case 'E':
395             flags |= DP_F_UP;
396           case 'e':
397             if (cflags == DP_C_LDOUBLE)
398             {
399               fvalue = va_arg(args, LDOUBLE);
400             }
401             else
402             {
403               fvalue = va_arg(args, double);
404             }
405             break;
406           case 'G':
407             flags |= DP_F_UP;
408           case 'g':
409             if (cflags == DP_C_LDOUBLE)
410             {
411               fvalue = va_arg(args, LDOUBLE);
412             }
413             else
414             {
415               fvalue = va_arg(args, double);
416             }
417 
418             break;
419           case 'c':
420             dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
421             break;
422           case 's':
423             strvalue = va_arg(args, char *);
424             if (max < 0)
425             {
426               max = maxlen;  /* ie, no max */
427             }
428 
429             fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
430             break;
431           case 'p':
432             strvalue = va_arg(args, void *);
433             fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min,
434                    max, flags);
435             break;
436           case 'n':
437             if (cflags == DP_C_SHORT)
438             {
439               short int *num;
440               num = va_arg(args, short int *);
441               *num = currlen;
442             }
443             else if (cflags == DP_C_LONG)
444             {
445               long int *num;
446               num = va_arg(args, long int *);
447               *num = currlen;
448             }
449             else
450             {
451               int *num;
452               num = va_arg(args, int *);
453               *num = currlen;
454             }
455             break;
456           case '%':
457             dopr_outch(buffer, &currlen, maxlen, ch);
458             break;
459           case 'w':
460             /* not supported yet, treat as next char */
461             ch = *format++;
462             break;
463           default:
464             /* Unknown, skip */
465             break;
466         }
467         ch = *format++;
468         state = DP_S_DEFAULT;
469         flags = cflags = min = 0;
470         max = -1;
471         break;
472       case DP_S_DONE:
473         break;
474       default:
475         /* hmm? */
476         break;  /* some picky compilers need this */
477     }
478   }
479   if (currlen < maxlen - 1)
480   {
481     buffer[currlen] = '\0';
482   }
483   else
484   {
485     buffer[maxlen - 1] = '\0';
486   }
487 }
488 
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max)489 static void fmtstr(char *buffer, size_t * currlen, size_t maxlen,
490                    char *value, int flags, int min, int max)
491 {
492   int padlen, strln; /* amount to pad */
493   int cnt = 0;
494 
495   if (value == 0)
496   {
497     value = "<NULL>";
498   }
499 
500   for (strln = 0; value[strln]; ++strln); /* strlen */
501   padlen = min - strln;
502   if (padlen < 0)
503   {
504     padlen = 0;
505   }
506 
507   if (flags & DP_F_MINUS)
508   {
509     padlen = -padlen;  /* Left Justify */
510   }
511 
512   while ((padlen > 0) && (cnt < max))
513   {
514     dopr_outch(buffer, currlen, maxlen, ' ');
515     --padlen;
516     ++cnt;
517   }
518 
519   while (*value && (cnt < max))
520   {
521     dopr_outch(buffer, currlen, maxlen, *value++);
522     ++cnt;
523   }
524 
525   while ((padlen < 0) && (cnt < max))
526   {
527     dopr_outch(buffer, currlen, maxlen, ' ');
528     ++padlen;
529     ++cnt;
530   }
531 }
532 
533 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
534 
fmtint(char * buffer,size_t * currlen,size_t maxlen,long value,int base,int min,int max,int flags)535 static void fmtint(char *buffer, size_t * currlen, size_t maxlen,
536                    long value, int base, int min, int max, int flags)
537 {
538   int signvalue = 0;
539   unsigned long uvalue;
540   char convert[20];
541   int place = 0;
542   int spadlen = 0; /* amount to space pad */
543   int zpadlen = 0; /* amount to zero pad */
544   int caps = 0;
545 
546   if (max < 0)
547   {
548     max = 0;
549   }
550 
551   uvalue = value;
552 
553   if (!(flags & DP_F_UNSIGNED))
554   {
555     if (value < 0)
556     {
557       signvalue = '-';
558       uvalue = -value;
559     }
560     else if (flags & DP_F_PLUS)     /* Do a sign (+/i) */
561     {
562       signvalue = '+';
563     }
564     else if (flags & DP_F_SPACE)
565     {
566       signvalue = ' ';
567     }
568   }
569 
570   if (flags & DP_F_UP)
571   {
572     caps = 1;  /* Should characters be upper case? */
573   }
574 
575   do
576   {
577     convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
578                        [uvalue % (unsigned) base];
579 
580     uvalue = (uvalue / (unsigned) base);
581   }
582 
583   while (uvalue && (place < 20));
584   if (place == 20)
585   {
586     place--;
587   }
588 
589   convert[place] = 0;
590 
591   zpadlen = max - place;
592   spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
593 
594   if (zpadlen < 0)
595   {
596     zpadlen = 0;
597   }
598 
599   if (spadlen < 0)
600   {
601     spadlen = 0;
602   }
603 
604   if (flags & DP_F_ZERO)
605   {
606     zpadlen = MAX(zpadlen, spadlen);
607     spadlen = 0;
608   }
609 
610   if (flags & DP_F_MINUS)
611   {
612     spadlen = -spadlen;  /* Left Justifty */
613   }
614 
615 #ifdef DEBUG_SNPRINTF
616   dprint(1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
617              zpadlen, spadlen, min, max, place));
618 #endif  // DEBUG_SNPRINTF
619 
620   /* Spaces */
621   while (spadlen > 0)
622   {
623     dopr_outch(buffer, currlen, maxlen, ' ');
624     --spadlen;
625   }
626 
627   /* Sign */
628   if (signvalue)
629   {
630     dopr_outch(buffer, currlen, maxlen, signvalue);
631   }
632 
633   /* Zeros */
634   if (zpadlen > 0)
635   {
636     while (zpadlen > 0)
637     {
638       dopr_outch(buffer, currlen, maxlen, '0');
639       --zpadlen;
640     }
641   }
642 
643   /* Digits */
644   while (place > 0)
645   {
646     dopr_outch(buffer, currlen, maxlen, convert[--place]);
647   }
648 
649   /* Left Justified spaces */
650   while (spadlen < 0)
651   {
652     dopr_outch(buffer, currlen, maxlen, ' ');
653     ++spadlen;
654   }
655 }
656 
abs_val(LDOUBLE value)657 static LDOUBLE abs_val(LDOUBLE value)
658 {
659   LDOUBLE result = value;
660 
661   if (value < 0)
662   {
663     result = -value;
664   }
665 
666   return result;
667 }
668 
pow10(int exp)669 static LDOUBLE pow10(int exp)
670 {
671   LDOUBLE result = 1;
672 
673   while (exp)
674   {
675     result *= 10;
676     exp--;
677   }
678 
679   return result;
680 }
681 
round(LDOUBLE value)682 static long round(LDOUBLE value)
683 {
684   long intpart;
685 
686   intpart = value;
687   value = value - intpart;
688   if (value >= 0.5)
689   {
690     intpart++;
691   }
692 
693   return intpart;
694 }
695 
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)696 static void fmtfp(char *buffer, size_t * currlen, size_t maxlen,
697                   LDOUBLE fvalue, int min, int max, int flags)
698 {
699   int signvalue = 0;
700   LDOUBLE ufvalue;
701   char iconvert[20];
702   char fconvert[20];
703   int iplace = 0;
704   int fplace = 0;
705   int padlen = 0; /* amount to pad */
706   int zpadlen = 0;
707   int caps = 0;
708   long intpart;
709   long fracpart;
710 
711   /*
712   * AIX manpage says the default is 0, but Solaris says the default
713   * is 6, and sprintf on AIX defaults to 6
714   */
715   if (max < 0)
716   {
717     max = 6;
718   }
719 
720   ufvalue = abs_val(fvalue);
721 
722   if (fvalue < 0)
723   {
724     signvalue = '-';
725   }
726   else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
727   {
728     signvalue = '+';
729   }
730   else if (flags & DP_F_SPACE)
731   {
732     signvalue = ' ';
733   }
734 
735 #if 0
736   if (flags & DP_F_UP)
737   {
738     caps = 1;  /* Should characters be upper case? */
739   }
740 #endif  // 0
741 
742   intpart = ufvalue;
743 
744   /*
745   * Sorry, we only support 9 digits past the decimal because of our
746   * conversion method
747   */
748   if (max > 9)
749   {
750     max = 9;
751   }
752 
753   /* We "cheat" by converting the fractional part to integer by
754   * multiplying by a factor of 10
755   */
756   fracpart = round((pow10(max)) * (ufvalue - intpart));
757 
758   if (fracpart >= pow10(max))
759   {
760     intpart++;
761     fracpart -= pow10(max);
762   }
763 
764   /* Convert integer part */
765   do
766   {
767     iconvert[iplace++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
768     intpart = (intpart / 10);
769   }
770   while (intpart && (iplace < 20));
771 
772   if (iplace == 20)
773   {
774     iplace--;
775   }
776 
777   iconvert[iplace] = 0;
778 
779   /* Convert fractional part */
780   do
781   {
782     fconvert[fplace++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
783     fracpart = (fracpart / 10);
784   }
785   while (fracpart && (fplace < 20));
786 
787   if (fplace == 20)
788   {
789     fplace--;
790   }
791 
792   fconvert[fplace] = 0;
793 
794   /* -1 for decimal point, another -1 if we are printing a sign */
795   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
796   zpadlen = max - fplace;
797 
798   if (zpadlen < 0)
799   {
800     zpadlen = 0;
801   }
802 
803   if (padlen < 0)
804   {
805     padlen = 0;
806   }
807 
808   if (flags & DP_F_MINUS)
809   {
810     padlen = -padlen;  /* Left Justifty */
811   }
812 
813   if ((flags & DP_F_ZERO) && (padlen > 0))
814   {
815     if (signvalue)
816     {
817       dopr_outch(buffer, currlen, maxlen, signvalue);
818       --padlen;
819       signvalue = 0;
820     }
821     while (padlen > 0)
822     {
823       dopr_outch(buffer, currlen, maxlen, '0');
824       --padlen;
825     }
826   }
827 
828   while (padlen > 0)
829   {
830     dopr_outch(buffer, currlen, maxlen, ' ');
831     --padlen;
832   }
833 
834   if (signvalue)
835   {
836     dopr_outch(buffer, currlen, maxlen, signvalue);
837   }
838 
839   while (iplace > 0)
840   {
841     dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
842   }
843 
844   /*
845   * Decimal point.  This should probably use locale to find the correct
846   * char to print out.
847   */
848   if (max > 0)
849   {
850     dopr_outch(buffer, currlen, maxlen, '.');
851 
852     while (fplace > 0)
853     {
854       dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
855     }
856   }
857 
858   while (zpadlen > 0)
859   {
860     dopr_outch(buffer, currlen, maxlen, '0');
861     --zpadlen;
862   }
863 
864   while (padlen < 0)
865   {
866     dopr_outch(buffer, currlen, maxlen, ' ');
867     ++padlen;
868   }
869 }
870 
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)871 static void dopr_outch(char *buffer, size_t * currlen, size_t maxlen, char c)
872 {
873   if (*currlen < maxlen)
874   {
875     buffer[(*currlen)++] = c;
876   }
877 }
878 
xastir_vsnprintf(char * str,size_t count,const char * fmt,va_list args)879 int xastir_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
880 {
881   str[0] = 0;
882   dopr(str, count, fmt, args);
883   return (strlen(str));
884 }
885 #endif /* !HAVE_VSNPRINTF */
886 
887 #ifndef HAVE_SNPRINTF
888 #ifdef HAVE_STDARGS
xastir_snprintf(char * str,size_t count,const char * fmt,...)889   int xastir_snprintf(char *str, size_t count, const char *fmt, ...)
890 #else // HAVE_STDARGS
891   int xastir_snprintf(va_alist) va_dcl
892 #endif    // HAVE_STDARGS
893 {
894 #  ifndef HAVE_STDARGS
895   char *str;
896   size_t count;
897   char *fmt;
898 #  endif    // HAVE_STDARGS
899   VA_LOCAL_DECL;
900 
901   VA_START(fmt);
902   VA_SHIFT(str, char *);
903   VA_SHIFT(count, size_t);
904   VA_SHIFT(fmt, char *);
905   (void) xastir_vsnprintf(str, count, fmt, ap);
906   VA_END;
907   return (strlen(str));
908 }
909 #endif /* !HAVE_SNPRINTF */
910 
911 
912