VirtualBox

source: vbox/trunk/src/VBox/Runtime/strformat.cpp@ 4672

Last change on this file since 4672 was 4371, checked in by vboxsync, 17 years ago

r=bird: Who is calling this code with uiBase == 0?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.3 KB
Line 
1/* $Id: strformat.cpp 4371 2007-08-24 20:56:59Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - String Formatter.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Defined Constants *
21*******************************************************************************/
22#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
23/*#define MAX(a, b) ((a) >= (b) ? (a) : (b))
24#define MIN(a, b) ((a) < (b) ? (a) : (b)) */
25
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP RTLOGGROUP_STRING
31#include <iprt/string.h>
32#include <iprt/assert.h>
33#ifdef IN_RING3
34#include <iprt/alloc.h>
35#include <iprt/err.h>
36#endif
37#include <iprt/string.h>
38#include <iprt/stdarg.h>
39#include "internal/string.h"
40
41/* Wrappers for converting to iprt facilities. */
42#define SSToDS(ptr) ptr
43#define kASSERT Assert
44#define KENDIAN_LITTLE 1
45#define KENDIAN KENDIAN_LITTLE
46#define KSIZE size_t
47typedef struct
48{
49 uint32_t ulLo;
50 uint32_t ulHi;
51} KSIZE64;
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57static unsigned _strnlen(const char *psz, unsigned cchMax);
58static unsigned _strnlenUCS2(PCRTUCS2 pucs, unsigned cchMax);
59static int rtStrFormatNumber(char *psz, KSIZE64 ullValue, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags);
60
61
62/**
63 * Finds the length of a string up to cchMax.
64 * @returns Length.
65 * @param psz Pointer to string.
66 * @param cchMax Max length.
67 */
68static unsigned _strnlen(const char *psz, unsigned cchMax)
69{
70 const char *pszC = psz;
71
72 while (cchMax-- > 0 && *psz != '\0')
73 psz++;
74
75 return psz - pszC;
76}
77
78
79/**
80 * Finds the length of a string up to cchMax.
81 * @returns Length.
82 * @param pucs Pointer to string.
83 * @param cchMax Max length.
84 */
85static unsigned _strnlenUCS2(PCRTUCS2 pucs, unsigned cchMax)
86{
87 PCRTUCS2 pucsC = pucs;
88
89 while (cchMax-- > 0 && *pucs != '\0')
90 pucs++;
91
92 return pucs - pucsC;
93}
94
95
96/**
97 * Finds the length of a string up to cchMax.
98 * @returns Length.
99 * @param pusz Pointer to string.
100 * @param cchMax Max length.
101 */
102static unsigned _strnlenUni(PCRTUNICP pusz, unsigned cchMax)
103{
104 PCRTUNICP puszC = pusz;
105
106 while (cchMax-- > 0 && *pusz != '\0')
107 pusz++;
108
109 return pusz - puszC;
110}
111
112
113/**
114 * Formats an integer number according to the parameters.
115 *
116 * @returns Length of the formatted number.
117 * @param psz Pointer to output string buffer of sufficient size.
118 * @param u64Value Value to format.
119 * @param uiBase Number representation base.
120 * @param cchWidth Width.
121 * @param cchPrecision Precision.
122 * @param fFlags Flags (NTFS_*).
123 */
124RTDECL(int) RTStrFormatNumber(char *psz, uint64_t u64Value, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags)
125{
126 return rtStrFormatNumber(psz, *(KSIZE64 *)(void *)&u64Value, uiBase, cchWidth, cchPrecision, fFlags);
127}
128
129
130
131/**
132 * Formats an integer number according to the parameters.
133 *
134 * @returns Length of the number.
135 * @param psz Pointer to output string.
136 * @param ullValue Value. Using the high part is optional.
137 * @param uiBase Number representation base.
138 * @param cchWidth Width
139 * @param cchPrecision Precision.
140 * @param fFlags Flags (NTFS_*).
141 */
142static int rtStrFormatNumber(char *psz, KSIZE64 ullValue, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags)
143{
144 const char * pachDigits = "0123456789abcdef";
145 char * pszStart = psz;
146 int cchValue;
147 unsigned long ul;
148#if 0
149 unsigned long ullow;
150#endif
151 int i;
152 int j;
153
154/** @todo Formatting of 64 bit numbers is broken, fix it! */
155
156 /*
157 * Validate and addjust input...
158 */
159/** @todo r=bird: Dmitry, who is calling this code with uiBase == 0? */
160 if (uiBase == 0)
161 uiBase = 10;
162 kASSERT((uiBase >= 2 || uiBase <= 16));
163 if (fFlags & RTSTR_F_CAPITAL)
164 pachDigits = "0123456789ABCDEF";
165 if (fFlags & RTSTR_F_LEFT)
166 fFlags &= ~RTSTR_F_ZEROPAD;
167
168 /*
169 * Determin value length
170 */
171 cchValue = 0;
172 if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
173 {
174 uint64_t u64 = *(uint64_t *)(void *)&ullValue;
175 if ((fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulHi & 0x80000000))
176 u64 = -(int64_t)u64;
177 do
178 {
179 cchValue++;
180 u64 /= uiBase;
181 } while (u64);
182 }
183 else
184 {
185 ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
186 do
187 {
188 cchValue++;
189 ul /= uiBase;
190 } while (ul);
191 }
192
193 /*
194 * Sign (+/-).
195 */
196 i = 0;
197 if (fFlags & RTSTR_F_VALSIGNED)
198 {
199 if ((ullValue.ulHi || (fFlags & RTSTR_F_64BIT) ? ullValue.ulHi : ullValue.ulLo) & 0x80000000)
200 {
201 ullValue.ulLo = -(int32_t)ullValue.ulLo;
202 if (ullValue.ulHi)
203 ullValue.ulHi = ~ullValue.ulHi;
204 psz[i++] = '-';
205 }
206 else if (fFlags & (RTSTR_F_PLUS | RTSTR_F_BLANK))
207 psz[i++] = (char)(fFlags & RTSTR_F_PLUS ? '+' : ' ');
208 }
209
210 /*
211 * Special (0/0x).
212 */
213 if (fFlags & RTSTR_F_SPECIAL && (uiBase % 8) == 0)
214 {
215 psz[i++] = '0';
216 if (uiBase == 16)
217 psz[i++] = (char)(fFlags & RTSTR_F_CAPITAL ? 'X' : 'x');
218 }
219
220 /*
221 * width - only if ZEROPAD
222 */
223 cchWidth -= i + cchValue;
224 if (fFlags & RTSTR_F_ZEROPAD)
225 while (--cchWidth >= 0)
226 {
227 psz[i++] = '0';
228 cchPrecision--;
229 }
230 else if (!(fFlags & RTSTR_F_LEFT) && cchWidth > 0)
231 {
232 for (j = i-1; j >= 0; j--)
233 psz[cchWidth + j] = psz[j];
234 for (j = 0; j < cchWidth; j++)
235 psz[j] = ' ';
236 i += cchWidth;
237 }
238 psz += i;
239
240
241 /*
242 * precision
243 */
244 while (--cchPrecision >= cchValue)
245 *psz++ = '0';
246
247 /*
248 * write number - not good enough but it works
249 */
250 psz += cchValue;
251 i = -1;
252 if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
253 {
254 uint64_t u64 = *(uint64_t *)(void *)&ullValue;
255 do
256 {
257 psz[i--] = pachDigits[u64 % uiBase];
258 u64 /= uiBase;
259 } while (u64);
260 }
261 else
262 {
263 ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
264 do
265 {
266 psz[i--] = pachDigits[ul % uiBase];
267 ul /= uiBase;
268 } while (ul);
269 }
270
271
272 /*
273 * width if RTSTR_F_LEFT
274 */
275 if (fFlags & RTSTR_F_LEFT)
276 while (--cchWidth >= 0)
277 *psz++ = ' ';
278
279 *psz = '\0';
280 return psz - pszStart;
281}
282
283
284/**
285 * Partial implementation of a printf like formatter.
286 * It doesn't do everything correct, and there is no floating point support.
287 * However, it supports custom formats by the means of a format callback.
288 *
289 * @returns number of bytes formatted.
290 * @param pfnOutput Output worker.
291 * Called in two ways. Normally with a string an it's length.
292 * For termination, it's called with NULL for string, 0 for length.
293 * @param pvArgOutput Argument to the output worker.
294 * @param pfnFormat Custom format worker.
295 * @param pvArgFormat Argument to the format worker.
296 * @param pszFormat Format string.
297 * @param InArgs Argument list.
298 */
299RTDECL(size_t) RTStrFormatV(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, va_list InArgs)
300{
301 /* make a copy so we can reference it (AMD64 / gcc). */
302 va_list args;
303 va_copy(args, InArgs);
304
305 KSIZE cch = 0;
306 const char *pszStartOutput = pszFormat;
307 while (*pszFormat != '\0')
308 {
309 if (*pszFormat == '%')
310 {
311 /* output pending string. */
312 if (pszStartOutput != pszFormat)
313 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
314
315 /* skip '%' */
316 pszFormat++;
317 if (*pszFormat == '%') /* '%%'-> '%' */
318 pszStartOutput = pszFormat++;
319 else
320 {
321 unsigned int fFlags = 0;
322 int cchWidth = -1;
323 int cchPrecision = -1;
324 unsigned int uBase = 10;
325 char chArgSize;
326
327 /* flags */
328 for (;;)
329 {
330 switch (*pszFormat++)
331 {
332 case '#': fFlags |= RTSTR_F_SPECIAL; continue;
333 case '-': fFlags |= RTSTR_F_LEFT; continue;
334 case '+': fFlags |= RTSTR_F_PLUS; continue;
335 case ' ': fFlags |= RTSTR_F_BLANK; continue;
336 case '0': fFlags |= RTSTR_F_ZEROPAD; continue;
337 }
338 pszFormat--;
339 break;
340 }
341
342 /* width */
343 if (ISDIGIT(*pszFormat))
344 {
345 for (cchWidth = 0; ISDIGIT(*pszFormat); pszFormat++)
346 {
347 cchWidth *= 10;
348 cchWidth += *pszFormat - '0';
349 }
350 fFlags |= RTSTR_F_WIDTH;
351 }
352 else if (*pszFormat == '*')
353 {
354 pszFormat++;
355 cchWidth = va_arg(args, int);
356 if (cchWidth < 0)
357 {
358 cchWidth = -cchWidth;
359 fFlags |= RTSTR_F_LEFT;
360 }
361 fFlags |= RTSTR_F_WIDTH;
362 }
363
364 /* precision */
365 if (*pszFormat == '.')
366 {
367 pszFormat++;
368 if (ISDIGIT(*pszFormat))
369 {
370 for (cchPrecision = 0; ISDIGIT(*pszFormat); pszFormat++)
371 {
372 cchPrecision *= 10;
373 cchPrecision += *pszFormat - '0';
374 }
375
376 }
377 else if (*pszFormat == '*')
378 {
379 pszFormat++;
380 cchPrecision = va_arg(args, int);
381 }
382 if (cchPrecision < 0)
383 cchPrecision = 0;
384 fFlags |= RTSTR_F_PRECISION;
385 }
386
387 /* argsize */
388 chArgSize = *pszFormat;
389 if (chArgSize != 'l' && chArgSize != 'L' && chArgSize != 'h' && chArgSize != 'j' && chArgSize != 'z' && chArgSize != 't')
390 chArgSize = 0;
391 else
392 {
393 pszFormat++;
394 if (*pszFormat == 'l' && chArgSize == 'l')
395 {
396 chArgSize = 'L';
397 pszFormat++;
398 }
399 else if (*pszFormat == 'h' && chArgSize == 'h')
400 {
401 chArgSize = 'H';
402 pszFormat++;
403 }
404 }
405
406 /*
407 * The type.
408 */
409 switch (*pszFormat++)
410 {
411 /* char */
412 case 'c':
413 {
414 char ch;
415
416 if (!(fFlags & RTSTR_F_LEFT))
417 while (--cchWidth > 0)
418 cch += pfnOutput(pvArgOutput, " ", 1);
419
420 ch = (char)va_arg(args, int);
421 cch += pfnOutput(pvArgOutput, SSToDS(&ch), 1);
422
423 while (--cchWidth > 0)
424 cch += pfnOutput(pvArgOutput, " ", 1);
425 break;
426 }
427
428#ifndef IN_RING3
429 case 'S': /* Unicode string as current code page. */
430 chArgSize = 'l';
431 /* fall thru */
432#endif
433 case 's': /* Unicode string as utf8 */
434 {
435 if (chArgSize == 'l')
436 {
437 /* ucs2 -> utf8 */
438 int cchStr;
439 PCRTUTF16 pwszStr = va_arg(args, PRTUTF16);
440
441 if (!VALID_PTR(pwszStr))
442 {
443 static RTUTF16 s_wszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
444 pwszStr = s_wszNull;
445 }
446 cchStr = _strnlenUCS2(pwszStr, (unsigned)cchPrecision);
447 if (!(fFlags & RTSTR_F_LEFT))
448 while (--cchWidth >= cchStr)
449 cch += pfnOutput(pvArgOutput, " ", 1);
450 while (cchStr-- > 0)
451 {
452#ifdef IN_RING3
453 RTUNICP Cp;
454 RTUtf16GetCpEx(&pwszStr, &Cp);
455 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
456 char *pszEnd = RTStrPutCp(szUtf8, Cp);
457 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
458#else
459 char ch = (char)*pwszStr++;
460 cch += pfnOutput(pvArgOutput, &ch, 1);
461#endif
462 }
463 while (--cchWidth >= cchStr)
464 cch += pfnOutput(pvArgOutput, " ", 1);
465 }
466 else if (chArgSize == 'L')
467 {
468 /* unicp -> utf8 */
469 int cchStr;
470 PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
471
472 if (!VALID_PTR(puszStr))
473 {
474 static RTUNICP uszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
475 puszStr = uszNull;
476 }
477 cchStr = _strnlenUni(puszStr, (unsigned)cchPrecision);
478 if (!(fFlags & RTSTR_F_LEFT))
479 while (--cchWidth >= cchStr)
480 cch += pfnOutput(pvArgOutput, " ", 1);
481
482 while (cchStr-- > 0)
483 {
484#ifdef IN_RING3
485 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
486 char *pszEnd = RTStrPutCp(szUtf8, *puszStr++);
487 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
488#else
489 char ch = (char)*puszStr++;
490 cch += pfnOutput(pvArgOutput, &ch, 1);
491#endif
492 }
493 while (--cchWidth >= cchStr)
494 cch += pfnOutput(pvArgOutput, " ", 1);
495 }
496 else
497 {
498 int cchStr;
499 const char *pszStr = va_arg(args, char*);
500
501 if (!VALID_PTR(pszStr))
502 pszStr = "<NULL>";
503 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
504 if (!(fFlags & RTSTR_F_LEFT))
505 while (--cchWidth >= cchStr)
506 cch += pfnOutput(pvArgOutput, " ", 1);
507
508 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
509
510 while (--cchWidth >= cchStr)
511 cch += pfnOutput(pvArgOutput, " ", 1);
512 }
513 break;
514 }
515
516#ifdef IN_RING3
517 case 'S': /* Unicode string as current code page. */
518 {
519 if (chArgSize == 'l')
520 {
521 int cchStr;
522 PCRTUCS2 pucs2Str = va_arg(args, PRTUCS2);
523 if (!VALID_PTR(pucs2Str))
524 {
525 static RTUCS2 ucs2Null[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
526 pucs2Str = ucs2Null;
527 }
528
529 cchStr = _strnlenUCS2(pucs2Str, (unsigned)cchPrecision);
530 if (!(fFlags & RTSTR_F_LEFT))
531 while (--cchWidth >= cchStr)
532 cch += pfnOutput(pvArgOutput, " ", 1);
533
534 if (cchStr)
535 {
536 /* allocate temporary buffer. */
537 PRTUCS2 pucs2Tmp = (PRTUCS2)RTMemAlloc((cchStr + 1) * sizeof(RTUCS2));
538 memcpy(pucs2Tmp, pucs2Str, cchStr * sizeof(RTUCS2));
539 pucs2Tmp[cchStr] = '\0';
540
541 char *pszUtf8;
542 int rc = RTStrUcs2ToUtf8(&pszUtf8, pucs2Tmp);
543 if (RT_SUCCESS(rc))
544 {
545 char *pszCurCp;
546 rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszUtf8);
547 if (RT_SUCCESS(rc))
548 {
549 cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
550 RTStrFree(pszCurCp);
551 }
552 RTStrFree(pszUtf8);
553 }
554 if (RT_FAILURE(rc))
555 while (cchStr-- > 0)
556 cch += pfnOutput(pvArgOutput, "\x7f", 1);
557 RTMemFree(pucs2Tmp);
558 }
559
560 while (--cchWidth >= cchStr)
561 cch += pfnOutput(pvArgOutput, " ", 1);
562 }
563 else if (chArgSize == 'L')
564 {
565 AssertMsgFailed(("Not implemented yet\n"));
566 }
567 else
568 {
569 int cchStr;
570 const char *pszStr = va_arg(args, char*);
571
572 if (!VALID_PTR(pszStr))
573 pszStr = "<NULL>";
574 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
575 if (!(fFlags & RTSTR_F_LEFT))
576 while (--cchWidth >= cchStr)
577 cch += pfnOutput(pvArgOutput, " ", 1);
578
579 if (cchStr)
580 {
581 /* allocate temporary buffer. */
582 char *pszTmp = (char *)RTMemAlloc(cchStr + 1);
583 memcpy(pszTmp, pszStr, cchStr);
584 pszTmp[cchStr] = '\0';
585
586 char *pszCurCp;
587 int rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszTmp);
588 if (RT_SUCCESS(rc))
589 {
590 cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
591 RTStrFree(pszCurCp);
592 }
593 else
594 while (cchStr-- > 0)
595 cch += pfnOutput(pvArgOutput, "\x7f", 1);
596 RTMemFree(pszTmp);
597 }
598
599 while (--cchWidth >= cchStr)
600 cch += pfnOutput(pvArgOutput, " ", 1);
601 }
602 break;
603 }
604#endif
605
606
607 /*-----------------*/
608 /* integer/pointer */
609 /*-----------------*/
610 case 'd':
611 case 'i':
612 case 'o':
613 case 'p':
614 case 'u':
615 case 'x':
616 case 'X':
617 {
618 char achNum[64]; /* FIXME */
619 int cchNum;
620 uint64_t u64Value;
621
622 switch (pszFormat[-1])
623 {
624 case 'd': /* signed decimal integer */
625 case 'i':
626 fFlags |= RTSTR_F_VALSIGNED;
627 break;
628
629 case 'o':
630 uBase = 8;
631 break;
632
633 case 'p':
634 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
635 uBase = 16;
636 if (cchWidth < 0)
637 cchWidth = sizeof(char *) * 2;
638 break;
639
640 case 'u':
641 uBase = 10;
642 break;
643
644 case 'X':
645 fFlags |= RTSTR_F_CAPITAL;
646 case 'x':
647 uBase = 16;
648 break;
649 }
650
651 if (pszFormat[-1] == 'p')
652 u64Value = va_arg(args, uintptr_t);
653 else if (fFlags & RTSTR_F_VALSIGNED)
654 {
655 if (chArgSize == 'L')
656 {
657 u64Value = va_arg(args, int64_t);
658 fFlags |= RTSTR_F_64BIT;
659 }
660 else if (chArgSize == 'l')
661 {
662 u64Value = va_arg(args, signed long);
663 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
664 }
665 else if (chArgSize == 'h')
666 {
667 u64Value = va_arg(args, /* signed short */ int);
668 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
669 }
670 else if (chArgSize == 'H')
671 {
672 u64Value = va_arg(args, /* int8_t */ int);
673 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
674 }
675 else if (chArgSize == 'j')
676 {
677 u64Value = va_arg(args, /*intmax_t*/ int64_t);
678 fFlags |= RTSTR_F_64BIT;
679 }
680 else if (chArgSize == 'z')
681 {
682 u64Value = va_arg(args, size_t);
683 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
684 }
685 else if (chArgSize == 't')
686 {
687 u64Value = va_arg(args, ptrdiff_t);
688 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
689 }
690 else
691 {
692 u64Value = va_arg(args, signed int);
693 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
694 }
695 }
696 else
697 {
698 if (chArgSize == 'L')
699 {
700 u64Value = va_arg(args, uint64_t);
701 fFlags |= RTSTR_F_64BIT;
702 }
703 else if (chArgSize == 'l')
704 {
705 u64Value = va_arg(args, unsigned long);
706 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
707 }
708 else if (chArgSize == 'h')
709 {
710 u64Value = va_arg(args, /* unsigned short */ int);
711 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
712 }
713 else if (chArgSize == 'H')
714 {
715 u64Value = va_arg(args, /* uint8_t */ int);
716 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
717 }
718 else if (chArgSize == 'j')
719 {
720 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
721 fFlags |= RTSTR_F_64BIT;
722 }
723 else if (chArgSize == 'z')
724 {
725 u64Value = va_arg(args, size_t);
726 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
727 }
728 else if (chArgSize == 't')
729 {
730 u64Value = va_arg(args, ptrdiff_t);
731 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
732 }
733 else
734 {
735 u64Value = va_arg(args, unsigned int);
736 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
737 }
738 }
739 cchNum = RTStrFormatNumber((char *)SSToDS(&achNum), u64Value, uBase, cchWidth, cchPrecision, fFlags);
740 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&achNum), cchNum);
741 break;
742 }
743
744 /*
745 * Nested extension.
746 */
747 case 'N':
748 {
749 const char *pszFormatNested = va_arg(args, const char *);
750 va_list *pArgsNested = va_arg(args, va_list *);
751 va_list ArgsNested;
752 va_copy(ArgsNested, *pArgsNested);
753 Assert(pszFormatNested);
754 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
755 break;
756 }
757
758 /*
759 * innotek Portable Runtime Extensions.
760 */
761 case 'R':
762 {
763 pszFormat--;
764 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
765 break;
766 }
767
768#ifdef RT_WITH_VBOX
769 /*
770 * VBox extensions.
771 */
772 case 'V':
773 {
774 pszFormat--;
775 cch += rtstrFormatVBox(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
776 break;
777 }
778#endif
779
780 /*
781 * Custom format.
782 */
783 default:
784 {
785 if (pfnFormat)
786 {
787 pszFormat--;
788 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
789 }
790 break;
791 }
792 }
793 pszStartOutput = pszFormat;
794 }
795 }
796 else
797 pszFormat++;
798 }
799
800 /* output pending string. */
801 if (pszStartOutput != pszFormat)
802 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
803
804 /* terminate the output */
805 pfnOutput(pvArgOutput, NULL, 0);
806
807 return cch;
808}
809
810
811/**
812 * Partial implementation of a printf like formatter.
813 * It doesn't do everything correct, and there is no floating point support.
814 * However, it supports custom formats by the means of a format callback.
815 *
816 * @returns number of bytes formatted.
817 * @param pfnOutput Output worker.
818 * Called in two ways. Normally with a string an it's length.
819 * For termination, it's called with NULL for string, 0 for length.
820 * @param pvArgOutput Argument to the output worker.
821 * @param pfnFormat Custom format worker.
822 * @param pvArgFormat Argument to the format worker.
823 * @param pszFormat Format string.
824 * @param ... Argument list.
825 */
826RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
827{
828 size_t cch;
829 va_list args;
830 va_start(args, pszFormat);
831 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
832 va_end(args);
833 return cch;
834}
835
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette