strformat.cpp revision 44bcb269c031b80dc35c64aa1a6bb25d3b83cd68
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/* $Id$ */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** @file
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * innotek Portable Runtime - String Formatter.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006-2007 innotek GmbH
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * available from http://www.virtualbox.org. This file is free software;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * you can redistribute it and/or modify it under the terms of the GNU
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * General Public License (GPL) as published by the Free Software
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * The contents of this file may alternatively be used under the terms
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * of the Common Development and Distribution License Version 1.0
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * VirtualBox OSE distribution, in which case the provisions of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * CDDL are applicable instead of those of the GPL.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * You may elect to license modified versions of this file under the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * terms and conditions of either the GPL or the CDDL or both.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*******************************************************************************
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync* Defined Constants *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync*******************************************************************************/
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync/*#define MAX(a, b) ((a) >= (b) ? (a) : (b))
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync#define MIN(a, b) ((a) < (b) ? (a) : (b)) */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*******************************************************************************
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync* Header Files *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync*******************************************************************************/
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#define LOG_GROUP RTLOGGROUP_STRING
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/string.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#include <iprt/assert.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#ifdef IN_RING3
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync# include <iprt/alloc.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync# include <iprt/err.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync# include <iprt/uni.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#endif
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#include <iprt/string.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#include <iprt/stdarg.h>
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#include "internal/string.h"
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/* Wrappers for converting to iprt facilities. */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#define SSToDS(ptr) ptr
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#define kASSERT Assert
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#define KENDIAN_LITTLE 1
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#define KENDIAN KENDIAN_LITTLE
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#define KSIZE size_t
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsynctypedef struct
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync uint32_t ulLo;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync uint32_t ulHi;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync} KSIZE64;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/*******************************************************************************
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync* Internal Functions *
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync*******************************************************************************/
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic unsigned _strnlen(const char *psz, unsigned cchMax);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic unsigned _strnlenUtf16(PCRTUTF16 pwsz, unsigned cchMax);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic int rtStrFormatNumber(char *psz, KSIZE64 ullValue, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Finds the length of a string up to cchMax.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @returns Length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param psz Pointer to string.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param cchMax Max length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic unsigned _strnlen(const char *psz, unsigned cchMax)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync const char *pszC = psz;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync while (cchMax-- > 0 && *psz != '\0')
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync psz++;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync return psz - pszC;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync}
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Finds the length of a string up to cchMax.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @returns Length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param pwsz Pointer to string.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param cchMax Max length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic unsigned _strnlenUtf16(PCRTUTF16 pwsz, unsigned cchMax)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#ifdef IN_RING3
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync unsigned cwc = 0;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync while (cchMax-- > 0)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTUNICP cp;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync int rc = RTUtf16GetCpEx(&pwsz, &cp);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync AssertRC(rc);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if (RT_FAILURE(rc) || !cp)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync break;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync }
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync return cwc;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#else /* !IN_RING3 */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync PCRTUTF16 pwszC = pwsz;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync while (cchMax-- > 0 && *pwsz != '\0')
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync pwsz++;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync return pwsz - pwszC;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#endif /* !IN_RING3 */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync}
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Finds the length of a string up to cchMax.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @returns Length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param pusz Pointer to string.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param cchMax Max length.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncstatic unsigned _strnlenUni(PCRTUNICP pusz, unsigned cchMax)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync PCRTUNICP puszC = pusz;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync while (cchMax-- > 0 && *pusz != '\0')
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync pusz++;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync return pusz - puszC;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync}
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Formats an integer number according to the parameters.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync *
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @returns Length of the formatted number.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param psz Pointer to output string buffer of sufficient size.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param u64Value Value to format.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param uiBase Number representation base.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param cchWidth Width.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param cchPrecision Precision.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @param fFlags Flags (NTFS_*).
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsyncRTDECL(int) RTStrFormatNumber(char *psz, uint64_t u64Value, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync return rtStrFormatNumber(psz, *(KSIZE64 *)(void *)&u64Value, uiBase, cchWidth, cchPrecision, fFlags);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync}
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Formats an integer number according to the parameters.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync *
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * @returns Length of the number.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param psz Pointer to output string.
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * @param ullValue Value. Using the high part is optional.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param uiBase Number representation base.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param cchWidth Width
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param cchPrecision Precision.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param fFlags Flags (NTFS_*).
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsyncstatic int rtStrFormatNumber(char *psz, KSIZE64 ullValue, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync{
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync const char * pachDigits = "0123456789abcdef";
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync char * pszStart = psz;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int cchValue;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync unsigned long ul;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#if 0
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync unsigned long ullow;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync#endif
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync int i;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int j;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/** @todo Formatting of 64 bit numbers is broken, fix it! */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /*
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Validate and addjust input...
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/** @todo r=bird: Dmitry, who is calling this code with uiBase == 0? */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (uiBase == 0)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync uiBase = 10;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync kASSERT((uiBase >= 2 || uiBase <= 16));
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (fFlags & RTSTR_F_CAPITAL)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync pachDigits = "0123456789ABCDEF";
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if (fFlags & RTSTR_F_LEFT)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync fFlags &= ~RTSTR_F_ZEROPAD;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /*
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Determin value length
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync cchValue = 0;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync uint64_t u64 = *(uint64_t *)(void *)&ullValue;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if ((fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulHi & 0x80000000))
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync u64 = -(int64_t)u64;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync do
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync cchValue++;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync u64 /= uiBase;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync } while (u64);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync }
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync else
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync do
de77073de5b4af1b1b5bce23357daea25c1c4d6fvboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync cchValue++;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync ul /= uiBase;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync } while (ul);
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /*
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Sign (+/-).
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync i = 0;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (fFlags & RTSTR_F_VALSIGNED)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if ((ullValue.ulHi || (fFlags & RTSTR_F_64BIT) ? ullValue.ulHi : ullValue.ulLo) & 0x80000000)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync ullValue.ulLo = -(int32_t)ullValue.ulLo;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if (ullValue.ulHi)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync ullValue.ulHi = ~ullValue.ulHi;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync psz[i++] = '-';
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync }
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync else if (fFlags & (RTSTR_F_PLUS | RTSTR_F_BLANK))
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync psz[i++] = (char)(fFlags & RTSTR_F_PLUS ? '+' : ' ');
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Special (0/0x).
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync if ((fFlags & RTSTR_F_SPECIAL) && (uiBase % 8) == 0)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync psz[i++] = '0';
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (uiBase == 16)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync psz[i++] = (char)(fFlags & RTSTR_F_CAPITAL ? 'X' : 'x');
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * width - only if ZEROPAD
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync cchWidth -= i + cchValue;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (fFlags & RTSTR_F_ZEROPAD)
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync while (--cchWidth >= 0)
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync {
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync psz[i++] = '0';
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync cchPrecision--;
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync }
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync else if (!(fFlags & RTSTR_F_LEFT) && cchWidth > 0)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync {
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync for (j = i-1; j >= 0; j--)
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync psz[cchWidth + j] = psz[j];
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync for (j = 0; j < cchWidth; j++)
de77073de5b4af1b1b5bce23357daea25c1c4d6fvboxsync psz[j] = ' ';
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync i += cchWidth;
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync }
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync psz += i;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync /*
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync * precision
*/
while (--cchPrecision >= cchValue)
*psz++ = '0';
/*
* write number - not good enough but it works
*/
psz += cchValue;
i = -1;
if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
{
uint64_t u64 = *(uint64_t *)(void *)&ullValue;
do
{
psz[i--] = pachDigits[u64 % uiBase];
u64 /= uiBase;
} while (u64);
}
else
{
ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
do
{
psz[i--] = pachDigits[ul % uiBase];
ul /= uiBase;
} while (ul);
}
/*
* width if RTSTR_F_LEFT
*/
if (fFlags & RTSTR_F_LEFT)
while (--cchWidth >= 0)
*psz++ = ' ';
*psz = '\0';
return psz - pszStart;
}
/**
* Partial implementation of a printf like formatter.
* It doesn't do everything correct, and there is no floating point support.
* However, it supports custom formats by the means of a format callback.
*
* @returns number of bytes formatted.
* @param pfnOutput Output worker.
* Called in two ways. Normally with a string an it's length.
* For termination, it's called with NULL for string, 0 for length.
* @param pvArgOutput Argument to the output worker.
* @param pfnFormat Custom format worker.
* @param pvArgFormat Argument to the format worker.
* @param pszFormat Format string.
* @param InArgs Argument list.
*/
RTDECL(size_t) RTStrFormatV(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, va_list InArgs)
{
va_list args;
KSIZE cch = 0;
const char *pszStartOutput = pszFormat;
va_copy(args, InArgs); /* make a copy so we can reference it (AMD64 / gcc). */
while (*pszFormat != '\0')
{
if (*pszFormat == '%')
{
/* output pending string. */
if (pszStartOutput != pszFormat)
cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
/* skip '%' */
pszFormat++;
if (*pszFormat == '%') /* '%%'-> '%' */
pszStartOutput = pszFormat++;
else
{
unsigned int fFlags = 0;
int cchWidth = -1;
int cchPrecision = -1;
unsigned int uBase = 10;
char chArgSize;
/* flags */
for (;;)
{
switch (*pszFormat++)
{
case '#': fFlags |= RTSTR_F_SPECIAL; continue;
case '-': fFlags |= RTSTR_F_LEFT; continue;
case '+': fFlags |= RTSTR_F_PLUS; continue;
case ' ': fFlags |= RTSTR_F_BLANK; continue;
case '0': fFlags |= RTSTR_F_ZEROPAD; continue;
}
pszFormat--;
break;
}
/* width */
if (ISDIGIT(*pszFormat))
{
for (cchWidth = 0; ISDIGIT(*pszFormat); pszFormat++)
{
cchWidth *= 10;
cchWidth += *pszFormat - '0';
}
fFlags |= RTSTR_F_WIDTH;
}
else if (*pszFormat == '*')
{
pszFormat++;
cchWidth = va_arg(args, int);
if (cchWidth < 0)
{
cchWidth = -cchWidth;
fFlags |= RTSTR_F_LEFT;
}
fFlags |= RTSTR_F_WIDTH;
}
/* precision */
if (*pszFormat == '.')
{
pszFormat++;
if (ISDIGIT(*pszFormat))
{
for (cchPrecision = 0; ISDIGIT(*pszFormat); pszFormat++)
{
cchPrecision *= 10;
cchPrecision += *pszFormat - '0';
}
}
else if (*pszFormat == '*')
{
pszFormat++;
cchPrecision = va_arg(args, int);
}
if (cchPrecision < 0)
cchPrecision = 0;
fFlags |= RTSTR_F_PRECISION;
}
/* argsize */
chArgSize = *pszFormat;
if (chArgSize != 'l' && chArgSize != 'L' && chArgSize != 'h' && chArgSize != 'j' && chArgSize != 'z' && chArgSize != 't')
chArgSize = 0;
else
{
pszFormat++;
if (*pszFormat == 'l' && chArgSize == 'l')
{
chArgSize = 'L';
pszFormat++;
}
else if (*pszFormat == 'h' && chArgSize == 'h')
{
chArgSize = 'H';
pszFormat++;
}
}
/*
* The type.
*/
switch (*pszFormat++)
{
/* char */
case 'c':
{
char ch;
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth > 0)
cch += pfnOutput(pvArgOutput, " ", 1);
ch = (char)va_arg(args, int);
cch += pfnOutput(pvArgOutput, SSToDS(&ch), 1);
while (--cchWidth > 0)
cch += pfnOutput(pvArgOutput, " ", 1);
break;
}
#ifndef IN_RING3
case 'S': /* Unicode string as current code page -> Unicode as UTF-8 in GC/R0. */
chArgSize = 'l';
/* fall thru */
#endif
case 's': /* Unicode string as utf8 */
{
if (chArgSize == 'l')
{
/* utf-16 -> utf-8 */
int cchStr;
PCRTUTF16 pwszStr = va_arg(args, PRTUTF16);
if (!VALID_PTR(pwszStr))
{
static RTUTF16 s_wszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
pwszStr = s_wszNull;
}
cchStr = _strnlenUtf16(pwszStr, (unsigned)cchPrecision);
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
while (cchStr-- > 0)
{
#ifdef IN_RING3
RTUNICP Cp;
RTUtf16GetCpEx(&pwszStr, &Cp);
char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
char *pszEnd = RTStrPutCp(szUtf8, Cp);
cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
#else
char ch = (char)*pwszStr++;
cch += pfnOutput(pvArgOutput, &ch, 1);
#endif
}
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
}
else if (chArgSize == 'L')
{
/* unicp -> utf8 */
int cchStr;
PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
if (!VALID_PTR(puszStr))
{
static RTUNICP s_uszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
puszStr = s_uszNull;
}
cchStr = _strnlenUni(puszStr, (unsigned)cchPrecision);
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
while (cchStr-- > 0)
{
#ifdef IN_RING3
char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
char *pszEnd = RTStrPutCp(szUtf8, *puszStr++);
cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
#else
char ch = (char)*puszStr++;
cch += pfnOutput(pvArgOutput, &ch, 1);
#endif
}
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
}
else
{
int cchStr;
const char *pszStr = va_arg(args, char*);
if (!VALID_PTR(pszStr))
pszStr = "<NULL>";
cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
cch += pfnOutput(pvArgOutput, pszStr, cchStr);
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
}
break;
}
#ifdef IN_RING3
case 'S': /* Unicode string as current code page. */
{
if (chArgSize == 'l')
{
/* UTF-16 */
int cchStr;
PCRTUTF16 pwsz2Str = va_arg(args, PRTUTF16);
if (!VALID_PTR(pwsz2Str))
{
static RTUTF16 s_wsz2Null[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
pwsz2Str = s_wsz2Null;
}
cchStr = _strnlenUtf16(pwsz2Str, (unsigned)cchPrecision);
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
if (cchStr)
{
/* allocate temporary buffer. */
PRTUTF16 pwsz2Tmp = (PRTUTF16)RTMemTmpAlloc((cchStr + 1) * sizeof(RTUTF16));
memcpy(pwsz2Tmp, pwsz2Str, cchStr * sizeof(RTUTF16));
pwsz2Tmp[cchStr] = '\0';
char *pszUtf8;
int rc = RTUtf16ToUtf8(pwsz2Tmp, &pszUtf8);
if (RT_SUCCESS(rc))
{
char *pszCurCp;
rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszUtf8);
if (RT_SUCCESS(rc))
{
cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
RTStrFree(pszCurCp);
}
RTStrFree(pszUtf8);
}
if (RT_FAILURE(rc))
while (cchStr-- > 0)
cch += pfnOutput(pvArgOutput, "\x7f", 1);
RTMemTmpFree(pwsz2Tmp);
}
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
}
else if (chArgSize == 'L')
{
/* UCS-32 */
AssertMsgFailed(("Not implemented yet\n"));
}
else
{
/* UTF-8 */
int cchStr;
const char *pszStr = va_arg(args, char *);
if (!VALID_PTR(pszStr))
pszStr = "<NULL>";
cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
if (!(fFlags & RTSTR_F_LEFT))
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
if (cchStr)
{
/* allocate temporary buffer. */
char *pszTmp = (char *)RTMemTmpAlloc(cchStr + 1);
memcpy(pszTmp, pszStr, cchStr);
pszTmp[cchStr] = '\0';
char *pszCurCp;
int rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszTmp);
if (RT_SUCCESS(rc))
{
cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
RTStrFree(pszCurCp);
}
else
while (cchStr-- > 0)
cch += pfnOutput(pvArgOutput, "\x7f", 1);
RTMemTmpFree(pszTmp);
}
while (--cchWidth >= cchStr)
cch += pfnOutput(pvArgOutput, " ", 1);
}
break;
}
#endif
/*-----------------*/
/* integer/pointer */
/*-----------------*/
case 'd':
case 'i':
case 'o':
case 'p':
case 'u':
case 'x':
case 'X':
{
char achNum[64]; /* FIXME */
int cchNum;
uint64_t u64Value;
switch (pszFormat[-1])
{
case 'd': /* signed decimal integer */
case 'i':
fFlags |= RTSTR_F_VALSIGNED;
break;
case 'o':
uBase = 8;
break;
case 'p':
fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
uBase = 16;
if (cchWidth < 0)
cchWidth = sizeof(char *) * 2;
break;
case 'u':
uBase = 10;
break;
case 'X':
fFlags |= RTSTR_F_CAPITAL;
case 'x':
uBase = 16;
break;
}
if (pszFormat[-1] == 'p')
u64Value = va_arg(args, uintptr_t);
else if (fFlags & RTSTR_F_VALSIGNED)
{
if (chArgSize == 'L')
{
u64Value = va_arg(args, int64_t);
fFlags |= RTSTR_F_64BIT;
}
else if (chArgSize == 'l')
{
u64Value = va_arg(args, signed long);
fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
}
else if (chArgSize == 'h')
{
u64Value = va_arg(args, /* signed short */ int);
fFlags |= RTSTR_GET_BIT_FLAG(signed short);
}
else if (chArgSize == 'H')
{
u64Value = va_arg(args, /* int8_t */ int);
fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
}
else if (chArgSize == 'j')
{
u64Value = va_arg(args, /*intmax_t*/ int64_t);
fFlags |= RTSTR_F_64BIT;
}
else if (chArgSize == 'z')
{
u64Value = va_arg(args, size_t);
fFlags |= RTSTR_GET_BIT_FLAG(size_t);
}
else if (chArgSize == 't')
{
u64Value = va_arg(args, ptrdiff_t);
fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
}
else
{
u64Value = va_arg(args, signed int);
fFlags |= RTSTR_GET_BIT_FLAG(signed int);
}
}
else
{
if (chArgSize == 'L')
{
u64Value = va_arg(args, uint64_t);
fFlags |= RTSTR_F_64BIT;
}
else if (chArgSize == 'l')
{
u64Value = va_arg(args, unsigned long);
fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
}
else if (chArgSize == 'h')
{
u64Value = va_arg(args, /* unsigned short */ int);
fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
}
else if (chArgSize == 'H')
{
u64Value = va_arg(args, /* uint8_t */ int);
fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
}
else if (chArgSize == 'j')
{
u64Value = va_arg(args, /*uintmax_t*/ int64_t);
fFlags |= RTSTR_F_64BIT;
}
else if (chArgSize == 'z')
{
u64Value = va_arg(args, size_t);
fFlags |= RTSTR_GET_BIT_FLAG(size_t);
}
else if (chArgSize == 't')
{
u64Value = va_arg(args, ptrdiff_t);
fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
}
else
{
u64Value = va_arg(args, unsigned int);
fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
}
}
cchNum = RTStrFormatNumber((char *)SSToDS(&achNum), u64Value, uBase, cchWidth, cchPrecision, fFlags);
cch += pfnOutput(pvArgOutput, (char *)SSToDS(&achNum), cchNum);
break;
}
/*
* Nested extension.
*/
case 'N':
{
const char *pszFormatNested = va_arg(args, const char *);
va_list *pArgsNested = va_arg(args, va_list *);
va_list ArgsNested;
va_copy(ArgsNested, *pArgsNested);
Assert(pszFormatNested);
cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
break;
}
/*
* innotek Portable Runtime Extensions.
*/
case 'R':
{
if (*pszFormat != '[')
{
pszFormat--;
cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
}
else
{
pszFormat--;
cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
}
break;
}
#ifdef RT_WITH_VBOX
/*
* VBox extensions.
*/
case 'V':
{
pszFormat--;
cch += rtstrFormatVBox(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
break;
}
#endif
/*
* Custom format.
*/
default:
{
if (pfnFormat)
{
pszFormat--;
cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
}
break;
}
}
pszStartOutput = pszFormat;
}
}
else
pszFormat++;
}
/* output pending string. */
if (pszStartOutput != pszFormat)
cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
/* terminate the output */
pfnOutput(pvArgOutput, NULL, 0);
return cch;
}
/**
* Partial implementation of a printf like formatter.
* It doesn't do everything correct, and there is no floating point support.
* However, it supports custom formats by the means of a format callback.
*
* @returns number of bytes formatted.
* @param pfnOutput Output worker.
* Called in two ways. Normally with a string an it's length.
* For termination, it's called with NULL for string, 0 for length.
* @param pvArgOutput Argument to the output worker.
* @param pfnFormat Custom format worker.
* @param pvArgFormat Argument to the format worker.
* @param pszFormat Format string.
* @param ... Argument list.
*/
RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
{
size_t cch;
va_list args;
va_start(args, pszFormat);
cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
va_end(args);
return cch;
}