home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Snippets / Local Format / locfmt.c next >
Encoding:
C/C++ Source or Header  |  1995-12-16  |  15.0 KB  |  617 lines  |  [TEXT/R*ch]

  1. /**********************************************************
  2.  
  3.     Copyright (c) Dale Semchishen 1995
  4.     All Rights Reserved
  5.  
  6.     locfmt.c
  7.  
  8.     Description:
  9.         Localized string formatting library.
  10.  
  11.         The routines in this package will convert numbers into
  12.         localized pascal strings. In other words the strings
  13.         will be formatted appropriately for the region you are
  14.         in as defined by the International Resources 'itl0', etc.
  15.  
  16.     Global functions:
  17.         locfmt_Num2Str()        - convert an integer to a local number str
  18.         locfmt_Str2Str()        - convert pascal str to a local number str
  19.         locfmt_Dec2Str()        - convert 'decimal' str to a local number str
  20.  
  21.         locfmt_Num2CurrStr()    - convert an integer to a local currency str
  22.         locfmt_Dec2CurrStr()    - convert 'decimal' str to a local currency str
  23.  
  24.  
  25.     History:
  26.         Apr 20 1995            - Creation
  27.  
  28.  **********************************************************/
  29.  
  30. #include "locfmt.h"
  31.  
  32. #include <TextUtils.h>
  33. #include <IntlResources.h>
  34. #include <string.h>
  35.  
  36.  
  37.  
  38. /*--------------- Local Function Prototypes ---------------*/
  39.  
  40. static Intl0Hndl locfmt_GetIntl0
  41. (
  42.     char        *thousands_ref,    /* out: thousands separator */
  43.     char        *decimal_ref    /* out: decimal separator */
  44. );
  45.  
  46.  
  47.  
  48. /**********************************************************
  49.  
  50.     locfmt_Num2Str - convert an integer to a local number str
  51.  
  52.     Description:
  53.         This routine will convert a signed integer to a
  54.         formatted string that is localized for your region.
  55.         
  56.         (eg) -1024 will be converted to
  57.                 "-1,024" for North America
  58.                 "-1.024" for Dutch
  59.                 "-1 024" for Finnish
  60.  
  61.     Return value:
  62.  
  63. **********************************************************/
  64. void locfmt_Num2Str
  65. (
  66.     Byte        *localstr_ref,    /* out: formatted string (Pascal) */
  67.     long        number            /* in: the integer to convert */
  68. )
  69. {
  70.     Str15        unformated_str;
  71.  
  72.  
  73.     /* convert to a localized number string */
  74.     NumToString( number, unformated_str );
  75.     locfmt_Str2Str( localstr_ref, unformated_str );
  76. }
  77.  
  78.  
  79.  
  80. /**********************************************************
  81.  
  82.     locfmt_Dec2Str - convert 'decimal' str to a local number str
  83.  
  84.     Description:
  85.         This routine will convert a 'decimal' string
  86.         to a formatted string that is localized for your region.
  87.  
  88.         For a simple integer string, number_str->exp should be 0
  89.  
  90.         If number_str->exp is negative, then a fractional
  91.         number is being converted and will be formatted as such.
  92.  
  93.         For more information about the 'decimal' type
  94.         see SANE documentation.
  95.  
  96.     Return value:
  97.  
  98. **********************************************************/
  99. void locfmt_Dec2Str
  100. (
  101.     Byte        *localstr_ref,    /* out: formatted string (Pascal) */
  102.     decimal        *number_str        /* in: the 'decimal' string to convert */
  103. )
  104. {
  105.     short        idx;
  106.     Str32        unformated_str;
  107.     
  108.     
  109.     /* IF invalid parameters */
  110.     if( (number_str == NULL)   ||
  111.         (localstr_ref == NULL) ||
  112.         (localstr_ref == (Byte *)number_str) )
  113.     {
  114.         return;
  115.     }
  116.  
  117.  
  118.     /* IF a negative number */
  119.     if( number_str->sgn )
  120.     {
  121.         unformated_str[0] = number_str->sig.length + 1;
  122.         unformated_str[1] = '-';
  123.         memcpy( &unformated_str[2], number_str->sig.text, number_str->sig.length );
  124.     }
  125.     /* ELSE a positive number */
  126.     else
  127.     {
  128.         unformated_str[0] = number_str->sig.length;
  129.         memcpy( &unformated_str[1], number_str->sig.text, number_str->sig.length );
  130.     }
  131.  
  132.  
  133.     /* IF number has a fractional part */
  134.     if( number_str->exp < 0 )
  135.     {
  136.         /* make room for decimal point */
  137.         idx = unformated_str[0] + number_str->exp + 1;
  138.         memmove( &unformated_str[ idx+1 ], &unformated_str[ idx ],
  139.                  unformated_str[0] - idx + 1 );
  140.  
  141.         /* insert decimal point in the string */
  142.         unformated_str[ idx ] = '.';
  143.         unformated_str[0] += 1;
  144.     }
  145.     /* ELSE IF number has to be multiplied by a power of 10 */
  146.     else if( number_str->exp > 0 )
  147.     {
  148.         /* append zeros */
  149.         memset( &unformated_str[ unformated_str[0] + 1 ], '0', number_str->exp );
  150.         unformated_str[0] += number_str->exp;
  151.     }
  152.  
  153.  
  154.     /* convert to a localized number string */
  155.     locfmt_Str2Str( localstr_ref, unformated_str );
  156. }
  157.  
  158.  
  159.  
  160. /**********************************************************
  161.  
  162.     locfmt_Str2Str - convert pascal str to a local number str
  163.  
  164.     Description:
  165.         This routine will convert a pascal string
  166.         to a formatted string that is localized for your region.
  167.  
  168.         For the input string parameter 'number_str' a decimal point
  169.         can be used to indicate the fractional part of the number.
  170.         If your numeric string is an integer, then do not include
  171.         a decimal point.
  172.         
  173.         (eg) "-2048.5" will be converted to
  174.                 "-2,048.5" for North America
  175.                 "-2.048,5" for Dutch
  176.                 "-2 048,5" for Finnish
  177.  
  178.     Note:
  179.         The address of the input and output strings must be different
  180.  
  181.     Return value:
  182.  
  183. **********************************************************/
  184. void locfmt_Str2Str
  185. (
  186.     Byte        *localstr_ref,    /* out: formatted string (Pascal) */
  187.     Byte        *number_str        /* in:  string to convert (Pascal) */
  188. )
  189. {
  190.     char        decimal_separator;
  191.     char        thousands_separator;
  192.     char        *fractionpart_ptr;
  193.     char        *input_ptr;
  194.     Byte        *outputstart_ptr;
  195.     short        integer_digits;
  196.     short        fraction_digits;
  197.     short        counter;
  198.  
  199.  
  200.     /* IF invalid parameters */
  201.     if( (number_str == NULL)   ||
  202.         (localstr_ref == NULL) ||
  203.         (localstr_ref == number_str) )
  204.     {
  205.         return;
  206.     }
  207.  
  208.  
  209.     outputstart_ptr = localstr_ref++;
  210.  
  211.     /* get the separator characters for your region */
  212.     locfmt_GetIntl0( &thousands_separator, &decimal_separator );
  213.  
  214.     /* IF a negative number */
  215.     if( number_str[1] == '-' )
  216.     {
  217.         input_ptr = (char *) &number_str[2];
  218.         counter = number_str[0] - 1;
  219.  
  220.         /* output negative sign */
  221.         *localstr_ref++ = '-';
  222.     }
  223.     /* ELSE a positive number */
  224.     else
  225.     {
  226.         input_ptr = (char *) &number_str[1];
  227.         counter = number_str[0];
  228.     }
  229.  
  230.  
  231.     /* IF there is a fractional part */
  232.     fractionpart_ptr = memchr( input_ptr, '.', counter );
  233.     if( fractionpart_ptr != NULL )
  234.     {
  235.         integer_digits = fractionpart_ptr - input_ptr;
  236.         fraction_digits = counter - integer_digits - 1;
  237.     }
  238.     /* ELSE no fractions */
  239.     else
  240.     {
  241.         integer_digits = counter;
  242.         fraction_digits = 0;
  243.     }
  244.  
  245.  
  246.     /* WHILE there are integer digits to output */
  247.     while( integer_digits-- > 0 )
  248.     {
  249.         /* output a single digit */
  250.         *localstr_ref++ = *input_ptr++;
  251.  
  252.         /* IF its time to insert the thousand's separator */
  253.         if( (integer_digits > 0) &&
  254.             (integer_digits % 3) == 0 )
  255.         {
  256.             *localstr_ref++ = thousands_separator;
  257.         }
  258.     }
  259.  
  260.  
  261.     /* IF there is a fractional part */
  262.     if( fractionpart_ptr != NULL )
  263.     {
  264.         *localstr_ref++ = decimal_separator;
  265.  
  266.         /* output trailing digits */
  267.         memcpy( localstr_ref, &input_ptr[1], fraction_digits );
  268.     }
  269.  
  270.  
  271.     /* output length of formatted string */
  272.     *outputstart_ptr = (localstr_ref - outputstart_ptr) + fraction_digits - 1;
  273. }
  274.  
  275.  
  276.  
  277.  
  278. /**********************************************************
  279.  
  280.     locfmt_Num2CurrStr - convert an integer to a local currency str
  281.  
  282.     Description:
  283.         This routine will convert a signed integer to a
  284.         formatted currency string that is localized for
  285.         your region.
  286.  
  287.         The signed integer will be converted to a string
  288.         and then decimal point character for your region
  289.         will be inserted based on the value of 'fraction_digits'
  290.         parameter.
  291.         
  292.         (eg) locfmt_Num2CurrStr( buf, -1024, 2) will generate
  293.              the following strings
  294.  
  295.                 "($1,024.00)"   for North America
  296.                 "(Fl.1.024,00)" for Dutch
  297.                 "-1 024,00mk"   for Finnish
  298.     Note:
  299.         Typically the 'fraction_digits' parameter will have
  300.         a value of 2, but if you specify a value of 0 then
  301.         a decimal separator will not be inserted in the
  302.         formatted currency string because you are displaying
  303.         an integer.
  304.  
  305.     Return value:
  306.  
  307. **********************************************************/
  308. void locfmt_Num2CurrStr
  309. (
  310.     Byte        *localstr_ref,    /* out: formatted currency string (Pascal) */
  311.     long        number,            /* in:  the integer to convert */
  312.     short        fraction_digits    /* in:  number of digits to right of decimal point */
  313. )
  314. {
  315.     decimal        unformated_str;
  316.  
  317.  
  318.     /* IF negative amount */
  319.     if( number < 0 )
  320.     {
  321.         /* convert to 'decimal' string */
  322.         unformated_str.sgn = 1;
  323.         unformated_str.exp = 0;
  324.         NumToString( -number, &unformated_str.sig.length );
  325.     }
  326.     /* ELSE positive amount */
  327.     else
  328.     {
  329.         /* convert to 'decimal' string */
  330.         unformated_str.sgn = 0;
  331.         unformated_str.exp = 0;
  332.         NumToString( number, &unformated_str.sig.length );
  333.     }
  334.  
  335.     /* convert to a localized currency string */
  336.     locfmt_Dec2CurrStr( localstr_ref, &unformated_str, fraction_digits );
  337. }
  338.  
  339.  
  340.  
  341. /**********************************************************
  342.  
  343.     locfmt_Dec2CurrStr - convert pascal str to a local currency str
  344.  
  345.     Description:
  346.         This routine will convert a pascal string
  347.         to a formatted currency string that is localized
  348.         for your region.
  349.  
  350.         The signed integer will be converted to a string
  351.         and then decimal point character for your region
  352.         will be inserted based on the value of 'fraction_digits'
  353.         parameter.
  354.         
  355.         (eg) dec_number.sgn = 1;
  356.              dec_number.exp = 0;
  357.              dec_number.sig.length = 4;
  358.              dec_number.sig.text[0] = 1;
  359.              dec_number.sig.text[1] = 0;
  360.              dec_number.sig.text[2] = 2;
  361.              dec_number.sig.text[3] = 4;
  362.              locfmt_Str2CurrStr( buf, &dec_number, 2);
  363.              
  364.              will generate the following strings
  365.  
  366.                 "($1,024.00)" for North America
  367.                 "(Fl.1.024,00)" for Dutch
  368.                 "-1 024,00mk" for Finnish
  369.  
  370.     Note:
  371.         Typically the 'fraction_digits' parameter will have
  372.         a value of 2, but if you specify a value of 0 then
  373.         a decimal separator will not be inserted in the
  374.         formatted currency string because you are displaying
  375.         an integer.
  376.  
  377.     Return value:
  378.  
  379. **********************************************************/
  380. void locfmt_Dec2CurrStr
  381. (
  382.     Byte        *localstr_ref,    /* out: formatted currency string (Pascal) */
  383.     decimal        *number_ptr,    /* in:  string to convert */
  384.     short        fraction_digits    /* in:  number of digits to right of decimal point */
  385. )
  386. {
  387.     Byte        format;
  388.     short        count;
  389.     Boolean        integer_part;
  390.     short        integer_digits;
  391.     Byte        *outputstart_ptr;
  392.     char        *input_ptr;
  393.     char        *last_ptr;
  394.     char        *trail_ptr;
  395.     char        currency_symbol[ 4 ];
  396.     char        decimal_separator;
  397.     char        thousands_separator;
  398.     Intl0Ptr    international_ptr;
  399.     Intl0Hndl    international_hdl;
  400.  
  401.  
  402.     /* IF invalid parameters */
  403.     if( (number_ptr == NULL)   ||
  404.         (localstr_ref == NULL) ||
  405.         (localstr_ref == (Byte *)number_ptr) )
  406.     {
  407.         return;
  408.     }
  409.  
  410.  
  411.     outputstart_ptr = localstr_ref++;
  412.  
  413.     /* IF able to get the separator characters for your region */
  414.     international_hdl = locfmt_GetIntl0( &thousands_separator, &decimal_separator );
  415.     if( international_hdl != NULL )
  416.     {
  417.         international_ptr = (*international_hdl);
  418.  
  419.         format = international_ptr->currFmt;
  420.         currency_symbol[ 0 ] = international_ptr->currSym1;
  421.         currency_symbol[ 1 ] = international_ptr->currSym2;
  422.         currency_symbol[ 2 ] = international_ptr->currSym3;
  423.         currency_symbol[ 3 ] = '\0';
  424.     }
  425.     /* ELSE no international resource */
  426.     else
  427.     {
  428.         /* use North American formatting */
  429.         format = currSymLead | currTrailingZ | currLeadingZ;
  430.         currency_symbol[ 0 ] = '$';
  431.         currency_symbol[ 1 ] = '\0';
  432.     }
  433.  
  434.  
  435.     /* IF a negative number */
  436.     if( number_ptr->sgn )
  437.     {
  438.         /* IF minus sign should be used for a negative number */
  439.         if( format & currNegSym )
  440.         {
  441.             *localstr_ref++ = '-';
  442.         }
  443.         /* ELSE use brackets for negative number */
  444.         else
  445.         {
  446.             *localstr_ref++ = '(';
  447.         }
  448.     }
  449.  
  450.     /* IF the currency symbol should be displayed in front of the number */
  451.     if( format & currSymLead )
  452.     {
  453.         /* output currency symbol(s) */
  454.         for( input_ptr=currency_symbol; *input_ptr; )
  455.             *localstr_ref++ = *input_ptr++;
  456.     }
  457.  
  458.     integer_part = false;
  459.     input_ptr = (char *) number_ptr->sig.text;
  460.     integer_digits = number_ptr->sig.length - fraction_digits;
  461.  
  462.     /* IF there should be leading zeros AND there are no digits before separator */
  463.     if( (format & currLeadingZ) && (integer_digits <= 0) )
  464.     {
  465.         *localstr_ref++ = '0';
  466.         integer_part = true;
  467.     }
  468.     /* ELSE WHILE there are digits before the decimal separator */
  469.     else while( integer_digits-- > 0 )
  470.     {
  471.         /* output a single digit */
  472.         *localstr_ref++ = *input_ptr++;
  473.         integer_part = true;
  474.  
  475.         /* IF its time to insert the thousand's separator */
  476.         if( (integer_digits > 0) &&
  477.             (integer_digits % 3) == 0 )
  478.         {
  479.             *localstr_ref++ = thousands_separator;
  480.         }
  481.     }
  482.  
  483.     trail_ptr = (char *) &number_ptr->sig.text[ number_ptr->sig.length ];
  484.  
  485.     /* IF there can be trailing zeros */
  486.     if( format & currTrailingZ )
  487.     {
  488.         /* output decimal separator */
  489.         if( fraction_digits > 0 )
  490.             *localstr_ref++ = decimal_separator;
  491.  
  492.         /* pad with zeros if required to right justify the fraction digits */
  493.         for( count=fraction_digits - (trail_ptr - input_ptr); count-- > 0; )
  494.             *localstr_ref++ = '0';
  495.  
  496.         /* output digits after the decimal separator */
  497.         while( input_ptr < trail_ptr )
  498.             *localstr_ref++ = *input_ptr++;
  499.     }
  500.     /* ELSE no trailing zeros */
  501.     else
  502.     {
  503.         if( fraction_digits > 0 )
  504.         {
  505.             /* determine where the last non-zero digit is */
  506.             while( trail_ptr > input_ptr )
  507.             {
  508.                 if( *--trail_ptr != '0' )
  509.                     break;
  510.             }
  511.     
  512.             /* IF there are any non-zero digits after decimal separator */
  513.             if( *trail_ptr != '0' )
  514.             {
  515.                 /* output decimal separator */
  516.                 *localstr_ref++ = decimal_separator;
  517.  
  518.                 last_ptr = (char *) &number_ptr->sig.text[number_ptr->sig.length];
  519.                 count = fraction_digits - (last_ptr - trail_ptr) - (trail_ptr - input_ptr);
  520.  
  521.                 /* pad with zeros if required to right justify the fraction digits */
  522.                 while( count-- > 0 )
  523.                     *localstr_ref++ = '0';
  524.  
  525.                 /* output digits after the decimal separator */
  526.                 while( (fraction_digits-- > 0) && (input_ptr <= trail_ptr) )
  527.                     *localstr_ref++ = *input_ptr++;
  528.             }
  529.             /* ELSE IF there were no integer digits output */
  530.             else if( !integer_part  )
  531.             {
  532.                 *localstr_ref++ = '0';
  533.             }
  534.         }
  535.     }
  536.  
  537.     /* IF the currency symbol should be displayed in after the number */
  538.     if( !(format & currSymLead) )
  539.     {
  540.         /* output currency symbol(s) */
  541.         for( input_ptr=currency_symbol; *input_ptr; )
  542.             *localstr_ref++ = *input_ptr++;
  543.     }
  544.  
  545.     /* IF a negative number AND brackets should be used for it */
  546.     if( number_ptr->sgn && !(format & currNegSym) )
  547.     {
  548.         *localstr_ref++ = ')';
  549.     }
  550.  
  551.     /* output length of formatted string */
  552.     *outputstart_ptr = (localstr_ref - outputstart_ptr) - 1;
  553. }
  554.  
  555.  
  556.  
  557.  
  558. /**********************************************************
  559.  
  560.     locfmt_GetIntl0 - get the Intl0 resource handle
  561.  
  562.     Description:
  563.         This routine will return the Intl10 resource
  564.         handle.  Also the thousands separator and decimal separator
  565.         will be validated and returned.
  566.  
  567.     Return value:
  568.         A handle to the Intl0 resource
  569.         or
  570.         NULL if failed
  571.  
  572. **********************************************************/
  573. static Intl0Hndl locfmt_GetIntl0
  574. (
  575.     char        *thousands_ref,    /* out: thousands separator */
  576.     char        *decimal_ref    /* out: decimal separator */
  577. )
  578. {
  579.     Intl0Hndl    international_hdl;
  580.     char        decimal_separator;
  581.     char        thousands_separator;
  582.  
  583.  
  584.     /* IF unable to get International Resource 'itl0' */
  585.     international_hdl = (Intl0Hndl) GetIntlResource( 0 );
  586.     if( international_hdl == NULL )
  587.     {
  588.         /* use North American standard */
  589.         thousands_separator = ',';
  590.         decimal_separator = '.';
  591.     }
  592.     else
  593.     {
  594.         thousands_separator = (*international_hdl)->thousSep;
  595.  
  596.         /* IF the thousand's separator is invalid */
  597.         if( (thousands_separator == '-') ||
  598.             ((thousands_separator >= '0') && (thousands_separator <= '9')) )
  599.         {
  600.             thousands_separator = '?';
  601.         }
  602.  
  603.         decimal_separator = (*international_hdl)->decimalPt;
  604.  
  605.         /* IF the decimal separator is invalid */
  606.         if( (decimal_separator == thousands_separator) ||
  607.             (decimal_separator == '-')                 ||
  608.             ((decimal_separator >= '0') && (decimal_separator <= '9')) )
  609.         {
  610.             decimal_separator = '?';
  611.         }
  612.     }
  613.  
  614.  
  615.     *decimal_ref = decimal_separator;
  616.     *thousands_ref = thousands_separator;
  617. }