-: 0:Source:ZIP/mnyfmt.c -: 0:Graph:mnyfmt.gcno -: 0:Data:mnyfmt.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:// (C) Copyright Adolfo Di Mare 2011 -: 2:// Use, modification and distribution are subject to the -: 3:// Boost Software License, Version 1.0. -: 4:// (See accompanying file LICENSE_1_0.txt or copy at -: 5:// http://www.boost.org/LICENSE_1_0.txt) -: 6: -: 7:// Revision history: -: 8:// Oct 2011 Adolfo Di Mare ==> Initial (non boost) version -: 9:// Jun+Ago 2012 Adolfo Di Mare ==> CE correction -: 10: -: 11:// mnyfmt.c (C) 2011 adolfo@di-mare.com -: 12: -: 13:// Define this macro if your compiler does not have a (long long) data type -: 14:// #define MNYFMT_NO_LONG_LONG -: 15: -: 16:#include "mnyfmt.h" -: 17: -: 18:#ifdef __cplusplus -: 19:// To compile the C source with a C++ compiler, override the file's extension. -: 20:// The GNU gcc compiler does this with option -x: gcc -x c++ -: 21:#endif -: 22: -: 23:/** \file mnyfmt.c -: 24: \brief Implementation for \c mnyfmt(). -: 25: -: 26: \author Adolfo Di Mare -: 27: \date 2011 -: 28:*/ -: 29: -: 30:// Examples and unit test cases are in in file mnyfmtts.c -: 31: -: 32:/** Formats and stores in \c fmtstr the money amount. -: 33: -: 34: Before invocation, the formatting pattern (picture clause) is stored in -: 35: result string \c fmtstr. To avoid using \c (double) values that have many -: 36: round off problems, the parameter for this function is an integer scaled -: 37: to 10^CE digits. For example, when using CE==2 digits, the monetary value -: 38: "$2,455.87" is representad by the integer '245587', and if CE==4 digits -: 39: are used, the integer value would be '24558700'. -: 40: -: 41: - The (integer) value to format is \c moneyval. -: 42: - Overwrites \c fmtstr with the formatted value. -: 43: - On error, leaves \c fmtstr untouched and returns \c (char*)(0). -: 44: - If the \c fmtstr does not have enough format characters \c '9' for -: 45: the integer part to format, of if the \c '-' cannot fit on top of -: 46: a \c '9' character, \c fmtstr remains untouched and the value -: 47: returned is \c (char*)(0). -: 48: -: 49: - The valid range for CE, the 'currenct exponent', is [0..6] -: 50: [ a CE of 7 or bigger leaves \c fmtstr untouched and the value -: 51: returned is \c (char*)(0) ]. -: 52: - The first occurrence of the character \c dec is the decimal fraction -: 53: separator (usually \c'.' or \c','). -: 54: -: 55: - When the decimal fraction separator character \c dec does not appear -: 56: in \c fmtstr it is assumed to be \c '\0' (end of string character). -: 57: - After the \c dec separator all the leading consecutive \c '9' format -: 58: characters are substituted with the corresponding digit from the -: 59: decimal part in \c moneyval, using digit zero \c '0' as fill character. -: 60: - All digits that inmediatly follow the decimal fraction separator are -: 61: changed either to zero or to the corresponding digit taken from the -: 62: decimal part in \c moneyval. -: 63: - Both the integer part and the fractional part are filled with the digits -: 64: that correspond to its position. This means that a format string like -: 65: \c "9999.9999" wild yield \c "0123.8700" as result when -: 66: \c moneyval==1238700 and \c CE==4. -: 67: - Characters trailing after the \c dec separator that are not the \c '9' -: 68: format digit are left untouched. -: 69: - All format characters \c '9' appearing before the decimal separator \c -: 70: dec will be replaced by digit zero \c '0' if the corresponding digit in -: 71: \c moneyval is not significant. -: 72: - When \c moneyval is negative, the \c '-' sign will be place over the -: 73: \c '9' immediately before the more significant digit. -: 74: - Non format characters in \c fmtstr are left untouched. -: 75: - The negative sign always is \c '-' and it is always placed on top of -: 76: the corresponding format character. -: 77: -: 78: - Returns \c (char*)(0) when the formatted value does not fit within -: 79: \c strlen(fmtstr) characters. -: 80: - Returns a pointer to the first significant digit in the formatted string, -: 81: within \c fmtstr or to the \c '-' sign if the formatted value is negative. -: 82: - Before storing the format string in \c fmtstr, the programmer must ensure -: 83: that \c fmtstr is big enough to hold the format string. -: 84: -: 85: \remark -: 86: - This routine basically substitutes each \c '9' character in \c fmtstr for -: 87: its corresponding decimal digit, or \c '0' when it is not a significant -: 88: digit. All other characters within \c fmtstr remain untouched. -: 89: - As it happens with many C functions, before invocation the programmer -: 90: must be sure that \c fmtstr is big enough to copy on it the complete -: 91: format string, as otherwise memory beyond \c fmtstr would be overwritten. -: 92: - There is no \c (wchart_t) version for this function, as it is meant to -: 93: place digits in a formatting string. After placement, the result string -: 94: can be converted to other forms. -: 95: -: 96: \dontinclude mnyfmtts.c -: 97: \skipline test.example -: 98: \until }} -: 99: -: 100: \see mnyfmtts.c -: 101:*/ 315: 102:char* mnyfmt(char *fmtstr, char dec, mnyfmt_long moneyval, unsigned CE) { 315: 102-block 0 -: 103: #ifndef __cplusplus 315: 104: const int false = 0; const int true = !false; -: 105: #endif -: 106: static mnyfmt_long TENPOW[6+1] = { 1,10,100,1000,10000,100000,1000000 }; 315: 107: if ( CE>(-1+sizeof(TENPOW)/sizeof(*TENPOW)) ) { return 0; } 315: 107-block 0 1: 107-block 1 -: 108: -: 109: char *pDec, *pSign, *p; -: 110: unsigned nDigits, nFrac; -: 111: unsigned i, nNines; -: 112: int isPositive; // (0<=moneyval) 628: 113: char digit[ 8 * ( sizeof(mnyfmt_long) > CE -: 114: ? sizeof(mnyfmt_long) 314: 115: : CE 314: 116: ) / 3 ]; -: 117: // ensure that digit[] can hold more than 2 digits -: 118: typedef int check_digit_size[ ( (2<=sizeof(CE)) ? 1 : -1 ) ]; -: 119: 314: 120: if (!( fmtstr) ) { return 0; } // null pointer mistake 314: 120-block 0 1: 120-block 1 -: 121:// if (!(*fmtstr) ) { return 0; } // null string mistake -: 122: -: 123: // determine dec position and store it in 'pDec' 313: 124: pDec = fmtstr; // points to the decimal separator (or eos) 313: 125: nNines = 0; // # of 'mnyfmt_format_chars' in fmtstr before decimal separator 3379: 126: while ( *pDec!=0 ) { 313: 126-block 0 3379: 126-block 1 3269: 127: if ( *pDec==mnyfmt_format_char ) { ++nNines; } // count 3269: 127-block 0 2163: 127-block 1 3269: 128: if ( *pDec==dec ) { break; } // mark 3269: 128-block 0 203: 128-block 1 3066: 129: ++pDec; 3066: 129-block 0 -: 130: } 313: 131: if ( pDec==fmtstr || nNines == 0 ) { return 0; } // NULL error 313: 131-block 0 307: 131-block 1 8: 131-block 2 -: 132: -: 133: // separate the integer and the fractional parts 305: 134: if ( 0 <= moneyval ) { isPositive = true;} 305: 134-block 0 226: 134-block 1 -: 135: else { 79: 136: isPositive = false; 79: 137: moneyval = -moneyval; 79: 138: if ( moneyval<0 ) { return 0; } // -LONG_LONG_MAX-1 case 79: 138-block 0 1: 138-block 1 -: 139: } 304: 140: nFrac = ( moneyval % TENPOW[CE] ); // fractional part 304: 141: moneyval = ( moneyval / TENPOW[CE] ); // integer part -: 142: -: 143: // store moneyval's digits in array digit[] 304: 144: if ( moneyval==0 ) { 304: 144-block 0 35: 145: nDigits = 1; // number of digits in integer part 35: 146: digit[0] = 0; 35: 146-block 0 -: 147: } -: 148: else { 269: 149: nDigits = 0; 269: 149-block 0 -: 150: do { // get all integer part digits 1622: 151: digit[nDigits] = moneyval % 10; 1622: 152: moneyval /= 10; 1622: 153: ++nDigits; 1622: 154: } while ( moneyval!=0 ); 1622: 154-block 0 -: 155: } -: 156: -: 157: // check that fmtstr has enough mnyfmt_format_chars 304: 158: if ( isPositive ) { 304: 158-block 0 226: 159: if ( nNines < nDigits ) { return 0; } 226: 159-block 0 2: 159-block 1 -: 160: } -: 161: else { // use one mnyfmt_format_char for '-' sign 78: 162: if ( nNines < nDigits+1 ) { return 0; } 78: 162-block 0 1: 162-block 1 -: 163: } -: 164: -: 165: // store digit[] into fmtstr[] 301: 166: p = pDec; 301: 167: pSign = 0; // pSign && pSignificative 2436: 168: for ( i=0; i store '-' 490: 174-block 0 77: 175: *p = '-'; 77: 176: pSign = p; 77: 177: isPositive = true; 77: 177-block 0 -: 178: } -: 179: else { 413: 180: *p = '0'; // replace leading mnyfmt_format_chars with '0' 413: 180-block 0 -: 181: } 2135: 182: --p; -: 183: } -: 184: -: 185: // deal with the fractional part 301: 186: if ( *pDec==0 ) { 301: 186-block 0 -: 187: // eos ==> ignore nFrac -: 188: } -: 189: else { 196: 190: p = pDec+1; 581: 191: for ( i=0; i0 ) { 560: 197-block 0 364: 198: --i; 364: 199: *p = digit[i]+'0'; 364: 199-block 0 -: 200: } -: 201: else { 196: 202: *p = '0'; 196: 202-block 0 -: 203: } -: 204: } -: 205: } -: 206: -: 207: -: 208: // deal with the fractional part 301: 209: if (true) { } 301: 209-block 0 #####: 210: else if ( *pDec==0 ) { $$$$$: 210-block 0 -: 211: // eos ==> ignore nFrac -: 212: } -: 213: else { #####: 214: p = pDec+1; #####: 215: if ( (*p) != mnyfmt_format_char ) { $$$$$: 215-block 0 -: 216: // no format for fraction ==> ignore nFrac -: 217: } -: 218: -: 219: // find last in 'fmtstr' -: 220: // store all digits fron nFrac into digit[] #####: 221: for ( i=0; i