VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/strformat.cpp@ 7420

Last change on this file since 7420 was 7417, checked in by vboxsync, 17 years ago

Fixed regression in _strlenUtf16.

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

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