snprintf.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /*
  2. * snprintf.c - a portable implementation of snprintf and vsnprintf
  3. *
  4. * $Id: snprintf.c,v 1.4 2001/04/12 02:39:44 guppy Exp $
  5. */
  6. /*
  7. * Portions Copyright (C) 2000, 2001 Eggheads Development Team
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. /*
  24. * Copyright Patrick Powell 1995
  25. * This code is based on code written by Patrick Powell (papowell@astart.com)
  26. * It may be used for any purpose as long as this notice remains intact
  27. * on all source code distributions
  28. */
  29. /**************************************************************
  30. * Original:
  31. * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
  32. * A bombproof version of doprnt (dopr) included.
  33. * Sigh. This sort of thing is always nasty do deal with. Note that
  34. * the version here does not include floating point...
  35. *
  36. * snprintf() is used instead of sprintf() as it does limit checks
  37. * for string length. This covers a nasty loophole.
  38. *
  39. * The other functions are there to prevent NULL pointers from
  40. * causing nast effects.
  41. *
  42. * More Recently:
  43. * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
  44. * This was ugly. It is still ugly. I opted out of floating point
  45. * numbers, but the formatter understands just about everything
  46. * from the normal C string format, at least as far as I can tell from
  47. * the Solaris 2.5 printf(3S) man page.
  48. *
  49. * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
  50. * Ok, added some minimal floating point support, which means this
  51. * probably requires libm on most operating systems. Don't yet
  52. * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
  53. * was pretty badly broken, it just wasn't being exercised in ways
  54. * which showed it, so that's been fixed. Also, formated the code
  55. * to mutt conventions, and removed dead code left over from the
  56. * original. Also, there is now a builtin-test, just compile with:
  57. * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
  58. * and run snprintf for results.
  59. *
  60. * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
  61. * The PGP code was using unsigned hexadecimal formats.
  62. * Unfortunately, unsigned formats simply didn't work.
  63. *
  64. * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
  65. * The original code assumed that both snprintf() and vsnprintf() were
  66. * missing. Some systems only have snprintf() but not vsnprintf(), so
  67. * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
  68. *
  69. * Andrew Tridgell (tridge@samba.org) Oct 1998
  70. * fixed handling of %.0f
  71. * added test for HAVE_LONG_DOUBLE
  72. *
  73. * Fabian Knittel <fknittel@gmx.de> Apr 2000 for eggdrop 1.5.3
  74. * Indented code to match eggdrop style. Adjusted to fit into eggdrops
  75. * build environment. Added `egg_' prefixes to snprintf and vsnprintf.
  76. *
  77. **************************************************************/
  78. #include <string.h>
  79. #include <ctype.h>
  80. #include <sys/types.h>
  81. #ifndef HAVE_VSNPRINTF
  82. /* varargs declarations: */
  83. #if defined(__STDC__)
  84. # ifdef HAVE_STDARG_H
  85. # include <stdarg.h>
  86. # else
  87. # ifdef HAVE_STD_ARGS_H
  88. # include <std_args.h>
  89. # endif
  90. # endif
  91. # include <stdarg.h>
  92. # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
  93. # define VA_LOCAL_DECL va_list ap
  94. # define VA_START(f) va_start(ap, f)
  95. # define VA_SHIFT(v,t) ; /* no-op for ANSI */
  96. # define VA_END va_end(ap)
  97. #else
  98. # include <varargs.h>
  99. # undef HAVE_STDARGS
  100. # define VA_LOCAL_DECL va_list ap
  101. # define VA_START(f) va_start(ap) /* f is ignored! */
  102. # define VA_SHIFT(v,t) v = va_arg(ap,t)
  103. # define VA_END va_end(ap)
  104. #endif
  105. #ifdef HAVE_LONG_DOUBLE
  106. #define LDOUBLE long double
  107. #else
  108. #define LDOUBLE double
  109. #endif
  110. static void dopr(char *buffer, size_t maxlen, const char *format,
  111. va_list args);
  112. static void fmtstr(char *buffer, size_t * currlen, size_t maxlen,
  113. char *value, int flags, int min, int max);
  114. static void fmtint(char *buffer, size_t * currlen, size_t maxlen,
  115. long value, int base, int min, int max, int flags);
  116. static void fmtfp(char *buffer, size_t * currlen, size_t maxlen,
  117. LDOUBLE fvalue, int min, int max, int flags);
  118. static void dopr_outch(char *buffer, size_t * currlen, size_t maxlen,
  119. char c);
  120. /*
  121. * dopr(): poor man's version of doprintf
  122. */
  123. /* format read states */
  124. #define DP_S_DEFAULT 0
  125. #define DP_S_FLAGS 1
  126. #define DP_S_MIN 2
  127. #define DP_S_DOT 3
  128. #define DP_S_MAX 4
  129. #define DP_S_MOD 5
  130. #define DP_S_CONV 6
  131. #define DP_S_DONE 7
  132. /* format flags - Bits */
  133. #define DP_F_MINUS (1 << 0)
  134. #define DP_F_PLUS (1 << 1)
  135. #define DP_F_SPACE (1 << 2)
  136. #define DP_F_NUM (1 << 3)
  137. #define DP_F_ZERO (1 << 4)
  138. #define DP_F_UP (1 << 5)
  139. #define DP_F_UNSIGNED (1 << 6)
  140. /* Conversion Flags */
  141. #define DP_C_SHORT 1
  142. #define DP_C_LONG 2
  143. #define DP_C_LDOUBLE 3
  144. #define char_to_int(p) (p - '0')
  145. #define MAX(p,q) ((p >= q) ? p : q)
  146. static void dopr(char *buffer, size_t maxlen, const char *format,
  147. va_list args)
  148. {
  149. char ch;
  150. long value;
  151. LDOUBLE fvalue;
  152. char *strvalue;
  153. int min;
  154. int max;
  155. int state;
  156. int flags;
  157. int cflags;
  158. size_t currlen;
  159. state = DP_S_DEFAULT;
  160. currlen = flags = cflags = min = 0;
  161. max = -1;
  162. ch = *format++;
  163. while (state != DP_S_DONE) {
  164. if ((ch == '\0') || (currlen >= maxlen))
  165. state = DP_S_DONE;
  166. switch (state) {
  167. case DP_S_DEFAULT:
  168. if (ch == '%')
  169. state = DP_S_FLAGS;
  170. else
  171. dopr_outch(buffer, &currlen, maxlen, ch);
  172. ch = *format++;
  173. break;
  174. case DP_S_FLAGS:
  175. switch (ch) {
  176. case '-':
  177. flags |= DP_F_MINUS;
  178. ch = *format++;
  179. break;
  180. case '+':
  181. flags |= DP_F_PLUS;
  182. ch = *format++;
  183. break;
  184. case ' ':
  185. flags |= DP_F_SPACE;
  186. ch = *format++;
  187. break;
  188. case '#':
  189. flags |= DP_F_NUM;
  190. ch = *format++;
  191. break;
  192. case '0':
  193. flags |= DP_F_ZERO;
  194. ch = *format++;
  195. break;
  196. default:
  197. state = DP_S_MIN;
  198. break;
  199. }
  200. break;
  201. case DP_S_MIN:
  202. if (isdigit(ch)) {
  203. min = 10 * min + char_to_int(ch);
  204. ch = *format++;
  205. } else if (ch == '*') {
  206. min = va_arg(args, int);
  207. ch = *format++;
  208. state = DP_S_DOT;
  209. } else
  210. state = DP_S_DOT;
  211. break;
  212. case DP_S_DOT:
  213. if (ch == '.') {
  214. state = DP_S_MAX;
  215. ch = *format++;
  216. } else
  217. state = DP_S_MOD;
  218. break;
  219. case DP_S_MAX:
  220. if (isdigit(ch)) {
  221. if (max < 0)
  222. max = 0;
  223. max = 10 * max + char_to_int(ch);
  224. ch = *format++;
  225. } else if (ch == '*') {
  226. max = va_arg(args, int);
  227. ch = *format++;
  228. state = DP_S_MOD;
  229. } else
  230. state = DP_S_MOD;
  231. break;
  232. case DP_S_MOD:
  233. /* Currently, we don't support Long Long, bummer */
  234. switch (ch) {
  235. case 'h':
  236. cflags = DP_C_SHORT;
  237. ch = *format++;
  238. break;
  239. case 'l':
  240. cflags = DP_C_LONG;
  241. ch = *format++;
  242. break;
  243. case 'L':
  244. cflags = DP_C_LDOUBLE;
  245. ch = *format++;
  246. break;
  247. default:
  248. break;
  249. }
  250. state = DP_S_CONV;
  251. break;
  252. case DP_S_CONV:
  253. switch (ch) {
  254. case 'd':
  255. case 'i':
  256. if (cflags == DP_C_SHORT)
  257. value = va_arg(args, short int);
  258. else if (cflags == DP_C_LONG)
  259. value = va_arg(args, long int);
  260. else
  261. value = va_arg(args, int);
  262. fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
  263. break;
  264. case 'o':
  265. flags |= DP_F_UNSIGNED;
  266. if (cflags == DP_C_SHORT)
  267. value = va_arg(args, unsigned short int);
  268. else if (cflags == DP_C_LONG)
  269. value = va_arg(args, unsigned long int);
  270. else
  271. value = va_arg(args, unsigned int);
  272. fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
  273. break;
  274. case 'u':
  275. flags |= DP_F_UNSIGNED;
  276. if (cflags == DP_C_SHORT)
  277. value = va_arg(args, unsigned short int);
  278. else if (cflags == DP_C_LONG)
  279. value = va_arg(args, unsigned long int);
  280. else
  281. value = va_arg(args, unsigned int);
  282. fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
  283. break;
  284. case 'X':
  285. flags |= DP_F_UP;
  286. case 'x':
  287. flags |= DP_F_UNSIGNED;
  288. if (cflags == DP_C_SHORT)
  289. value = va_arg(args, unsigned short int);
  290. else if (cflags == DP_C_LONG)
  291. value = va_arg(args, unsigned long int);
  292. else
  293. value = va_arg(args, unsigned int);
  294. fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
  295. break;
  296. case 'f':
  297. if (cflags == DP_C_LDOUBLE)
  298. fvalue = va_arg(args, LDOUBLE);
  299. else
  300. fvalue = va_arg(args, double);
  301. /* um, floating point? */
  302. fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
  303. break;
  304. case 'E':
  305. flags |= DP_F_UP;
  306. case 'e':
  307. if (cflags == DP_C_LDOUBLE)
  308. fvalue = va_arg(args, LDOUBLE);
  309. else
  310. fvalue = va_arg(args, double);
  311. break;
  312. case 'G':
  313. flags |= DP_F_UP;
  314. case 'g':
  315. if (cflags == DP_C_LDOUBLE)
  316. fvalue = va_arg(args, LDOUBLE);
  317. else
  318. fvalue = va_arg(args, double);
  319. break;
  320. case 'c':
  321. dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
  322. break;
  323. case 's':
  324. strvalue = va_arg(args, char *);
  325. if (max < 0)
  326. max = maxlen; /* ie, no max */
  327. fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
  328. break;
  329. case 'p':
  330. strvalue = va_arg(args, void *);
  331. fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max,
  332. flags);
  333. break;
  334. case 'n':
  335. if (cflags == DP_C_SHORT) {
  336. short int *num;
  337. num = va_arg(args, short int *);
  338. *num = currlen;
  339. } else if (cflags == DP_C_LONG) {
  340. long int *num;
  341. num = va_arg(args, long int *);
  342. *num = currlen;
  343. } else {
  344. int *num;
  345. num = va_arg(args, int *);
  346. *num = currlen;
  347. }
  348. break;
  349. case '%':
  350. dopr_outch(buffer, &currlen, maxlen, ch);
  351. break;
  352. case 'w':
  353. /* not supported yet, treat as next char */
  354. ch = *format++;
  355. break;
  356. default:
  357. /* Unknown, skip */
  358. break;
  359. }
  360. ch = *format++;
  361. state = DP_S_DEFAULT;
  362. flags = cflags = min = 0;
  363. max = -1;
  364. break;
  365. case DP_S_DONE:
  366. break;
  367. default:
  368. /* hmm? */
  369. break; /* some picky compilers need this */
  370. }
  371. }
  372. if (currlen < maxlen - 1)
  373. buffer[currlen] = '\0';
  374. else
  375. buffer[maxlen - 1] = '\0';
  376. }
  377. static void fmtstr(char *buffer, size_t * currlen, size_t maxlen,
  378. char *value, int flags, int min, int max)
  379. {
  380. int padlen,
  381. strln; /* amount to pad */
  382. int cnt = 0;
  383. if (value == 0) {
  384. value = "<NULL>";
  385. }
  386. for (strln = 0; value[strln]; ++strln); /* strlen */
  387. padlen = min - strln;
  388. if (padlen < 0)
  389. padlen = 0;
  390. if (flags & DP_F_MINUS)
  391. padlen = -padlen; /* Left Justify */
  392. while ((padlen > 0) && (cnt < max)) {
  393. dopr_outch(buffer, currlen, maxlen, ' ');
  394. --padlen;
  395. ++cnt;
  396. }
  397. while (*value && (cnt < max)) {
  398. dopr_outch(buffer, currlen, maxlen, *value++);
  399. ++cnt;
  400. }
  401. while ((padlen < 0) && (cnt < max)) {
  402. dopr_outch(buffer, currlen, maxlen, ' ');
  403. ++padlen;
  404. ++cnt;
  405. }
  406. }
  407. /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
  408. static void fmtint(char *buffer, size_t * currlen, size_t maxlen,
  409. long value, int base, int min, int max, int flags)
  410. {
  411. int signvalue = 0;
  412. unsigned long uvalue;
  413. char convert[20];
  414. int place = 0;
  415. int spadlen = 0; /* amount to space pad */
  416. int zpadlen = 0; /* amount to zero pad */
  417. int caps = 0;
  418. if (max < 0)
  419. max = 0;
  420. uvalue = value;
  421. if (!(flags & DP_F_UNSIGNED)) {
  422. if (value < 0) {
  423. signvalue = '-';
  424. uvalue = -value;
  425. } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
  426. signvalue = '+';
  427. else if (flags & DP_F_SPACE)
  428. signvalue = ' ';
  429. }
  430. if (flags & DP_F_UP)
  431. caps = 1; /* Should characters be upper case? */
  432. do {
  433. convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
  434. [uvalue % (unsigned) base];
  435. uvalue = (uvalue / (unsigned) base);
  436. }
  437. while (uvalue && (place < 20));
  438. if (place == 20)
  439. place--;
  440. convert[place] = 0;
  441. zpadlen = max - place;
  442. spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
  443. if (zpadlen < 0)
  444. zpadlen = 0;
  445. if (spadlen < 0)
  446. spadlen = 0;
  447. if (flags & DP_F_ZERO) {
  448. zpadlen = MAX(zpadlen, spadlen);
  449. spadlen = 0;
  450. }
  451. if (flags & DP_F_MINUS)
  452. spadlen = -spadlen; /* Left Justifty */
  453. #ifdef DEBUG_SNPRINTF
  454. dprint(1,
  455. (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
  456. zpadlen, spadlen, min, max, place));
  457. #endif
  458. /* Spaces */
  459. while (spadlen > 0) {
  460. dopr_outch(buffer, currlen, maxlen, ' ');
  461. --spadlen;
  462. }
  463. /* Sign */
  464. if (signvalue)
  465. dopr_outch(buffer, currlen, maxlen, signvalue);
  466. /* Zeros */
  467. if (zpadlen > 0) {
  468. while (zpadlen > 0) {
  469. dopr_outch(buffer, currlen, maxlen, '0');
  470. --zpadlen;
  471. }
  472. }
  473. /* Digits */
  474. while (place > 0)
  475. dopr_outch(buffer, currlen, maxlen, convert[--place]);
  476. /* Left Justified spaces */
  477. while (spadlen < 0) {
  478. dopr_outch(buffer, currlen, maxlen, ' ');
  479. ++spadlen;
  480. }
  481. }
  482. static LDOUBLE abs_val(LDOUBLE value)
  483. {
  484. LDOUBLE result = value;
  485. if (value < 0)
  486. result = -value;
  487. return result;
  488. }
  489. static LDOUBLE pow10(int exp)
  490. {
  491. LDOUBLE result = 1;
  492. while (exp) {
  493. result *= 10;
  494. exp--;
  495. }
  496. return result;
  497. }
  498. static long round(LDOUBLE value)
  499. {
  500. long intpart;
  501. intpart = value;
  502. value = value - intpart;
  503. if (value >= 0.5)
  504. intpart++;
  505. return intpart;
  506. }
  507. static void fmtfp(char *buffer, size_t * currlen, size_t maxlen,
  508. LDOUBLE fvalue, int min, int max, int flags)
  509. {
  510. int signvalue = 0;
  511. LDOUBLE ufvalue;
  512. char iconvert[20];
  513. char fconvert[20];
  514. int iplace = 0;
  515. int fplace = 0;
  516. int padlen = 0; /* amount to pad */
  517. int zpadlen = 0;
  518. int caps = 0;
  519. long intpart;
  520. long fracpart;
  521. /*
  522. * AIX manpage says the default is 0, but Solaris says the default
  523. * is 6, and sprintf on AIX defaults to 6
  524. */
  525. if (max < 0)
  526. max = 6;
  527. ufvalue = abs_val(fvalue);
  528. if (fvalue < 0)
  529. signvalue = '-';
  530. else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
  531. signvalue = '+';
  532. else if (flags & DP_F_SPACE)
  533. signvalue = ' ';
  534. #if 0
  535. if (flags & DP_F_UP)
  536. caps = 1; /* Should characters be upper case? */
  537. #endif
  538. intpart = ufvalue;
  539. /*
  540. * Sorry, we only support 9 digits past the decimal because of our
  541. * conversion method
  542. */
  543. if (max > 9)
  544. max = 9;
  545. /* We "cheat" by converting the fractional part to integer by
  546. * multiplying by a factor of 10
  547. */
  548. fracpart = round((pow10(max)) * (ufvalue - intpart));
  549. if (fracpart >= pow10(max)) {
  550. intpart++;
  551. fracpart -= pow10(max);
  552. }
  553. /* Convert integer part */
  554. do {
  555. iconvert[iplace++] =
  556. (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
  557. intpart = (intpart / 10);
  558. }
  559. while (intpart && (iplace < 20));
  560. if (iplace == 20)
  561. iplace--;
  562. iconvert[iplace] = 0;
  563. /* Convert fractional part */
  564. do {
  565. fconvert[fplace++] =
  566. (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
  567. fracpart = (fracpart / 10);
  568. }
  569. while (fracpart && (fplace < 20));
  570. if (fplace == 20)
  571. fplace--;
  572. fconvert[fplace] = 0;
  573. /* -1 for decimal point, another -1 if we are printing a sign */
  574. padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
  575. zpadlen = max - fplace;
  576. if (zpadlen < 0)
  577. zpadlen = 0;
  578. if (padlen < 0)
  579. padlen = 0;
  580. if (flags & DP_F_MINUS)
  581. padlen = -padlen; /* Left Justifty */
  582. if ((flags & DP_F_ZERO) && (padlen > 0)) {
  583. if (signvalue) {
  584. dopr_outch(buffer, currlen, maxlen, signvalue);
  585. --padlen;
  586. signvalue = 0;
  587. }
  588. while (padlen > 0) {
  589. dopr_outch(buffer, currlen, maxlen, '0');
  590. --padlen;
  591. }
  592. }
  593. while (padlen > 0) {
  594. dopr_outch(buffer, currlen, maxlen, ' ');
  595. --padlen;
  596. }
  597. if (signvalue)
  598. dopr_outch(buffer, currlen, maxlen, signvalue);
  599. while (iplace > 0)
  600. dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
  601. /*
  602. * Decimal point. This should probably use locale to find the correct
  603. * char to print out.
  604. */
  605. if (max > 0) {
  606. dopr_outch(buffer, currlen, maxlen, '.');
  607. while (fplace > 0)
  608. dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
  609. }
  610. while (zpadlen > 0) {
  611. dopr_outch(buffer, currlen, maxlen, '0');
  612. --zpadlen;
  613. }
  614. while (padlen < 0) {
  615. dopr_outch(buffer, currlen, maxlen, ' ');
  616. ++padlen;
  617. }
  618. }
  619. static void dopr_outch(char *buffer, size_t * currlen, size_t maxlen,
  620. char c)
  621. {
  622. if (*currlen < maxlen)
  623. buffer[(*currlen)++] = c;
  624. }
  625. int egg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  626. {
  627. str[0] = 0;
  628. dopr(str, count, fmt, args);
  629. return (strlen(str));
  630. }
  631. #endif /* !HAVE_VSNPRINTF */
  632. #ifndef HAVE_SNPRINTF
  633. # ifdef HAVE_STDARGS
  634. int egg_snprintf(char *str, size_t count, const char *fmt, ...)
  635. # else
  636. int egg_snprintf(va_alist) va_dcl
  637. # endif
  638. {
  639. # ifndef HAVE_STDARGS
  640. char *str;
  641. size_t count;
  642. char *fmt;
  643. # endif
  644. VA_LOCAL_DECL;
  645. VA_START(fmt);
  646. VA_SHIFT(str, char *);
  647. VA_SHIFT(count, size_t);
  648. VA_SHIFT(fmt, char *);
  649. (void) egg_vsnprintf(str, count, fmt, ap);
  650. VA_END;
  651. return (strlen(str));
  652. }
  653. #endif /* !HAVE_SNPRINTF */