⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 varformat.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * Variant formatting functions
 *
 * Copyright 2003 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * NOTES
 *  Since the formatting functions aren't properly documented, I used the
 *  Visual Basic documentation as a guide to implementing these functions. This
 *  means that some named or user-defined formats may work slightly differently.
 *  Please submit a test case if you find a difference.
 */

#include "config.h"

#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wine/unicode.h"
#include "winerror.h"
#include "variant.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(variant);

/* Make sure internal conversions to strings use the '.','+'/'-' and ','
 * format chars from the US locale. This enables us to parse the created
 * strings to determine the number of decimal places, exponent, etc.
 */
#define LCID_US MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT)

static const WCHAR szPercent_d[] = { '%','d','\0' };
static const WCHAR szPercentZeroTwo_d[] = { '%','0','2','d','\0' };
static const WCHAR szPercentZeroStar_d[] = { '%','0','*','d','\0' };

#if 0
#define dump_tokens(rgb) do { \
  int i_; TRACE("Tokens->{\n"); \
  for (i_ = 0; i_ < rgb[0]; i_++) \
    TRACE("%s0x%02x", i_?",":"",rgb[i_]); \
  TRACE(" }\n"); \
  } while(0)
#endif

/******************************************************************************
 * Variant-Formats {OLEAUT32}
 *
 * NOTES
 *  When formatting a variant a variety of format strings may be used to generate
 *  different kinds of formatted output. A format string consists of either a named
 *  format, or a user-defined format.
 *
 *  The following named formats are defined:
 *| Name           Description
 *| ----           -----------
 *| General Date   Display Date, and time for non-integer values
 *| Short Date     Short date format as defined by locale settings
 *| Medium Date    Medium date format as defined by locale settings
 *| Long Date      Long date format as defined by locale settings
 *| Short Time     Short Time format as defined by locale settings
 *| Medium Time    Medium time format as defined by locale settings
 *| Long Time      Long time format as defined by locale settings
 *| True/False     Localised text of "True" or "False"
 *| Yes/No         Localised text of "Yes" or "No"
 *| On/Off         Localised text of "On" or "Off"
 *| General Number No thousands separator. No decimal points for integers
 *| Currency       General currency format using localised characters
 *| Fixed          At least one whole and two fractional digits
 *| Standard       Same as 'Fixed', but including decimal separators
 *| Percent        Multiply by 100 and display a trailing '%' character
 *| Scientific     Display with exponent
 *
 *  User-defined formats consist of a combination of tokens and literal
 *  characters. Literal characters are copied unmodified to the formatted
 *  output at the position they occupy in the format string. Any character
 *  that is not recognised as a token is treated as a literal. A literal can
 *  also be specified by preceding it with a backslash character
 *  (e.g. "\L\i\t\e\r\a\l") or enclosing it in double quotes.
 *
 *  A user-defined format can have up to 4 sections, depending on the type of
 *  format. The following table lists sections and their meaning:
 *| Format Type  Sections Meaning
 *| -----------  -------- -------
 *| Number       1        Use the same format for all numbers
 *| Number       2        Use format 1 for positive and 2 for negative numbers
 *| Number       3        Use format 1 for positive, 2 for zero, and 3
 *|                       for negative numbers.
 *| Number       4        Use format 1 for positive, 2 for zero, 3 for
 *|                       negative, and 4 for null numbers.
 *| String       1        Use the same format for all strings
 *| String       2        Use format 2 for null and empty strings, otherwise
 *|                       use format 1.
 *| Date         1        Use the same format for all dates
 *
 *  The formatting tokens fall into several categories depending on the type
 *  of formatted output. For more information on each type, see
 *  VarFormat-Dates(), VarFormat-Strings() and VarFormat-Numbers().
 *
 *  SEE ALSO
 *  VarTokenizeFormatString(), VarFormatFromTokens(), VarFormat(),
 *  VarFormatDateTime(), VarFormatNumber(), VarFormatCurrency().
 */

/******************************************************************************
 * VarFormat-Strings {OLEAUT32}
 *
 * NOTES
 *  When formatting a variant as a string, it is first converted to a VT_BSTR.
 *  The user-format string defines which characters are copied into which
 *  positions in the output string. Literals may be inserted in the format
 *  string. When creating the formatted string, excess characters in the string
 *  (those not consumed by a token) are appended to the end of the output. If
 *  there are more tokens than characters in the string to format, spaces will
 *  be inserted at the start of the string if the '@' token was used.
 *
 *  By default strings are converted to lowercase, or uppercase if the '>' token
 *  is encountered. This applies to the whole string: it is not possible to
 *  generate a mixed-case output string.
 *
 *  In user-defined string formats, the following tokens are recognised:
 *| Token  Description
 *| -----  -----------
 *| '@'    Copy a char from the source, or a space if no chars are left.
 *| '&'    Copy a char from the source, or write nothing if no chars are left.
 *| '<'    Output the whole string as lower-case (the default).
 *| '>'    Output the whole string as upper-case.
 *| '!'    MSDN indicates that this character should cause right-to-left
 *|        copying, however tests show that it is tokenised but not processed.
 */

/*
 * Common format definitions
 */

 /* Fomat types */
#define FMT_TYPE_UNKNOWN 0x0
#define FMT_TYPE_GENERAL 0x1
#define FMT_TYPE_NUMBER  0x2
#define FMT_TYPE_DATE    0x3
#define FMT_TYPE_STRING  0x4

#define FMT_TO_STRING    0x0 /* If header->size == this, act like VB's Str() fn */

typedef struct tagFMT_SHORT_HEADER
{
  BYTE   size;      /* Size of tokenised block (including header), or FMT_TO_STRING */
  BYTE   type;      /* Allowable types (FMT_TYPE_*) */
  BYTE   offset[1]; /* Offset of the first (and only) format section */
} FMT_SHORT_HEADER;

typedef struct tagFMT_HEADER
{
  BYTE   size;      /* Total size of the whole tokenised block (including header) */
  BYTE   type;      /* Allowable types (FMT_TYPE_*) */
  BYTE   starts[4]; /* Offset of each of the 4 format sections, or 0 if none */
} FMT_HEADER;

#define FmtGetPositive(x)  (x->starts[0])
#define FmtGetNegative(x)  (x->starts[1] ? x->starts[1] : x->starts[0])
#define FmtGetZero(x)      (x->starts[2] ? x->starts[2] : x->starts[0])
#define FmtGetNull(x)      (x->starts[3] ? x->starts[3] : x->starts[0])

/*
 * String formats
 */

#define FMT_FLAG_LT  0x1 /* Has '<' (lower case) */
#define FMT_FLAG_GT  0x2 /* Has '>' (upper case) */
#define FMT_FLAG_RTL 0x4 /* Has '!' (Copy right to left) */

typedef struct tagFMT_STRING_HEADER
{
  BYTE   flags;      /* LT, GT, RTL */
  BYTE   unknown1;
  BYTE   unknown2;
  BYTE   copy_chars; /* Number of chars to be copied */
  BYTE   unknown3;
} FMT_STRING_HEADER;

/*
 * Number formats
 */

#define FMT_FLAG_PERCENT   0x1  /* Has '%' (Percentage) */
#define FMT_FLAG_EXPONENT  0x2  /* Has 'e' (Exponent/Scientific notation) */
#define FMT_FLAG_THOUSANDS 0x4  /* Has ',' (Standard use of the thousands separator) */
#define FMT_FLAG_BOOL      0x20 /* Boolean format */

typedef struct tagFMT_NUMBER_HEADER
{
  BYTE   flags;      /* PERCENT, EXPONENT, THOUSANDS, BOOL */
  BYTE   multiplier; /* Multiplier, 100 for percentages */
  BYTE   divisor;    /* Divisor, 1000 if '%%' was used */
  BYTE   whole;      /* Number of digits before the decimal point */
  BYTE   fractional; /* Number of digits after the decimal point */
} FMT_NUMBER_HEADER;

/*
 * Date Formats
 */
typedef struct tagFMT_DATE_HEADER
{
  BYTE   flags;
  BYTE   unknown1;
  BYTE   unknown2;
  BYTE   unknown3;
  BYTE   unknown4;
} FMT_DATE_HEADER;

/*
 * Format token values
 */
#define FMT_GEN_COPY        0x00 /* \n, "lit" => 0,pos,len: Copy len chars from input+pos */
#define FMT_GEN_INLINE      0x01 /*      => 1,len,[chars]: Copy len chars from token stream */
#define FMT_GEN_END         0x02 /* \0,; => 2: End of the tokenised format */
#define FMT_DATE_TIME_SEP   0x03 /* Time separator char */
#define FMT_DATE_DATE_SEP   0x04 /* Date separator char */
#define FMT_DATE_GENERAL    0x05 /* General format date */
#define FMT_DATE_QUARTER    0x06 /* Quarter of the year from 1-4 */
#define FMT_DATE_TIME_SYS   0x07 /* System long time format */
#define FMT_DATE_DAY        0x08 /* Day with no leading 0 */
#define FMT_DATE_DAY_0      0x09 /* Day with leading 0 */
#define FMT_DATE_DAY_SHORT  0x0A /* Short day name */
#define FMT_DATE_DAY_LONG   0x0B /* Long day name */
#define FMT_DATE_SHORT      0x0C /* Short date format */
#define FMT_DATE_LONG       0x0D /* Long date format */
#define FMT_DATE_MEDIUM     0x0E /* Medium date format */
#define FMT_DATE_DAY_WEEK   0x0F /* First day of the week */
#define FMT_DATE_WEEK_YEAR  0x10 /* First week of the year */
#define FMT_DATE_MON        0x11 /* Month with no leading 0 */
#define FMT_DATE_MON_0      0x12 /* Month with leading 0 */
#define FMT_DATE_MON_SHORT  0x13 /* Short month name */
#define FMT_DATE_MON_LONG   0x14 /* Long month name */
#define FMT_DATE_YEAR_DOY   0x15 /* Day of the year with no leading 0 */
#define FMT_DATE_YEAR_0     0x16 /* 2 digit year with leading 0 */
/* NOTE: token 0x17 is not defined, 'yyy' is not valid */
#define FMT_DATE_YEAR_LONG  0x18 /* 4 digit year */
#define FMT_DATE_MIN        0x1A /* Minutes with no leading 0 */
#define FMT_DATE_MIN_0      0x1B /* Minutes with leading 0 */
#define FMT_DATE_SEC        0x1C /* Seconds with no leading 0 */
#define FMT_DATE_SEC_0      0x1D /* Seconds with leading 0 */
#define FMT_DATE_HOUR       0x1E /* Hours with no leading 0 */
#define FMT_DATE_HOUR_0     0x1F /* Hours with leading 0 */
#define FMT_DATE_HOUR_12    0x20 /* Hours with no leading 0, 12 hour clock */
#define FMT_DATE_HOUR_12_0  0x21 /* Hours with leading 0, 12 hour clock */
#define FMT_DATE_TIME_UNK2  0x23
/* FIXME: probably missing some here */
#define FMT_DATE_AMPM_SYS1  0x2E /* AM/PM as defined by system settings */
#define FMT_DATE_AMPM_UPPER 0x2F /* Upper-case AM or PM */
#define FMT_DATE_A_UPPER    0x30 /* Upper-case A or P */
#define FMT_DATE_AMPM_SYS2  0x31 /* AM/PM as defined by system settings */
#define FMT_DATE_AMPM_LOWER 0x32 /* Lower-case AM or PM */
#define FMT_DATE_A_LOWER    0x33 /* Lower-case A or P */
#define FMT_NUM_COPY_ZERO   0x34 /* Copy 1 digit or 0 if no digit */
#define FMT_NUM_COPY_SKIP   0x35 /* Copy 1 digit or skip if no digit */
#define FMT_NUM_DECIMAL     0x36 /* Decimal separator */
#define FMT_NUM_EXP_POS_U   0x37 /* Scientific notation, uppercase, + sign */
#define FMT_NUM_EXP_NEG_U   0x38 /* Scientific notation, uppercase, - sign */
#define FMT_NUM_EXP_POS_L   0x39 /* Scientific notation, lowercase, + sign */
#define FMT_NUM_EXP_NEG_L   0x3A /* Scientific notation, lowercase, - sign */
#define FMT_NUM_CURRENCY    0x3B /* Currency symbol */
#define FMT_NUM_TRUE_FALSE  0x3D /* Convert to "True" or "False" */
#define FMT_NUM_YES_NO      0x3E /* Convert to "Yes" or "No" */
#define FMT_NUM_ON_OFF      0x3F /* Convert to "On" or "Off"  */
#define FMT_STR_COPY_SPACE  0x40 /* Copy len chars with space if no char */
#define FMT_STR_COPY_SKIP   0x41 /* Copy len chars or skip if no char */
/* Wine additions */
#define FMT_WINE_HOURS_12   0x81 /* Hours using 12 hour clockhourCopy len chars or skip if no char */

/* Named Formats and their tokenised values */
static const WCHAR szGeneralDate[] = { 'G','e','n','e','r','a','l',' ','D','a','t','e','\0' };
static const BYTE fmtGeneralDate[0x0a] =
{
  0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_GENERAL,FMT_GEN_END
};

static const WCHAR szShortDate[] = { 'S','h','o','r','t',' ','D','a','t','e','\0' };
static const BYTE fmtShortDate[0x0a] =
{
  0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_SHORT,FMT_GEN_END
};

static const WCHAR szMediumDate[] = { 'M','e','d','i','u','m',' ','D','a','t','e','\0' };
static const BYTE fmtMediumDate[0x0a] =
{
  0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_MEDIUM,FMT_GEN_END
};

static const WCHAR szLongDate[] = { 'L','o','n','g',' ','D','a','t','e','\0' };
static const BYTE fmtLongDate[0x0a] =
{
  0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_LONG,FMT_GEN_END
};

static const WCHAR szShortTime[] = { 'S','h','o','r','t',' ','T','i','m','e','\0' };
static const BYTE fmtShortTime[0x0c] =
{
  0x0c,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_TIME_UNK2,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,FMT_GEN_END
};

static const WCHAR szMediumTime[] = { 'M','e','d','i','u','m',' ','T','i','m','e','\0' };
static const BYTE fmtMediumTime[0x11] =
{
  0x11,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_HOUR_12_0,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,
  FMT_GEN_INLINE,0x01,' ','\0',FMT_DATE_AMPM_SYS1,FMT_GEN_END
};

static const WCHAR szLongTime[] = { 'L','o','n','g',' ','T','i','m','e','\0' };
static const BYTE fmtLongTime[0x0d] =
{
  0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
  0x0,0x0,0x0,0x0,0x0,
  FMT_DATE_TIME_SYS,FMT_GEN_END
};

static const WCHAR szTrueFalse[] = { 'T','r','u','e','/','F','a','l','s','e','\0' };
static const BYTE fmtTrueFalse[0x0d] =
{
  0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
  FMT_NUM_TRUE_FALSE,FMT_GEN_END
};

static const WCHAR szYesNo[] = { 'Y','e','s','/','N','o','\0' };
static const BYTE fmtYesNo[0x0d] =
{
  0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
  FMT_NUM_YES_NO,FMT_GEN_END
};

static const WCHAR szOnOff[] = { 'O','n','/','O','f','f','\0' };
static const BYTE fmtOnOff[0x0d] =
{
  0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
  FMT_NUM_ON_OFF,FMT_GEN_END
};

static const WCHAR szGeneralNumber[] = { 'G','e','n','e','r','a','l',' ','N','u','m','b','e','r','\0' };
static const BYTE fmtGeneralNumber[sizeof(FMT_HEADER)] =
{
  sizeof(FMT_HEADER),FMT_TYPE_GENERAL,sizeof(FMT_HEADER),0x0,0x0,0x0
};

static const WCHAR szCurrency[] = { 'C','u','r','r','e','n','c','y','\0' };
static const BYTE fmtCurrency[0x26] =
{
  0x26,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x12,0x0,0x0,
  /* Positive numbers */
  FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
  FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
  FMT_GEN_END,
  /* Negative numbers */
  FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
  FMT_GEN_INLINE,0x1,'(','\0',FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,
  FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_INLINE,0x1,')','\0',
  FMT_GEN_END
};

static const WCHAR szFixed[] = { 'F','i','x','e','d','\0' };
static const BYTE fmtFixed[0x11] =
{
  0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  0x0,0x0,0x0,0x1,0x2,
  FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
};

static const WCHAR szStandard[] = { 'S','t','a','n','d','a','r','d','\0' };
static const BYTE fmtStandard[0x11] =
{
  0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  FMT_FLAG_THOUSANDS,0x0,0x0,0x1,0x2,
  FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
};

static const WCHAR szPercent[] = { 'P','e','r','c','e','n','t','\0' };
static const BYTE fmtPercent[0x15] =
{
  0x15,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
  FMT_FLAG_PERCENT,0x1,0x0,0x1,0x2,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -