Przemyslaw Czerpak wrote:
5. For easy use it's necessary to have in core code function which will
   work like printf(). Otherwise it will be hard to create strings for
   translations which are not context dependent.
   Such function should be in base escape characters like %s or %d
   compatible with C so we can use dedicated tools to operate on .pot
   files. Anyhow for better flexibility we should add support for stringify
   and format any item size. F.e. %s should also work for numeric, date and
   logical items. In general it should be possible to create formatting
similar to the one given by transform and picture clasue. We can add support for passing picture clase directly in formatted
   string or as parameter. We can also add some additional extensions.
   The function name is less important. We can call it hb_strFormat().
   It's time to implement it.
Yes. We need it. The problem is, if we had to be C compatible, or we need to to implement our own Harbour specific type specifiers.
A simple implementation could be:
  %d = LTRIM(STR(nValue)) or LTRIM(STR(ROUND(nValue, 0))) ???
  %s = cValue or TRIM(cValue) ???
but we will always want some new extensions. I still can not make agreement with myself what specifiers should be. Do you have some ideas about it? From i18n point of view a very important thing is %1$d extension. It is not related to speficier type problem, so I we need to implement it for sure.

Just like you I still cannot take final decision.
For use parameter indexing is a must.

Hi,


I've wrote some basic version of the function.

Implemented:
  - specifiers: c (char), s (string), d (decimal), f (float);
- flags: - (left align), + (print + sign), space (print space sign), 0 (zero padding);
  - width;
  - decimals;
  - parameter numbers;

% [<param>$] [+|-|space|0] [<width>][.<dec>][c|s|d|f]

The goal was to be C compatible, i.e.:
  - do not print STR() style '*' for to short width numbers;
  - do not convert types (see below, for exceptions).

Harbour has different types than C, and full C compatibility is impossible, or function behaviour will look weird, without any adaption to Harbour. So, I've made a few small exceptions/extensions to strict C compatible implementation: - %d allows to take LOGICAL parameter type, and function prints "1" and "0", for .T. and .F.. In C true/false is represented by integer values (of some width) 1 and 0, so, introducing this extension for LOGICAL type, I feel more C compatible. - %f In C language %f uses 6 decimal places, but harbour has <decimal> format inside numeric item, so, I've used this value for default. Without this, we will need to show explicit <decimal> format for many things like FIELD values, to get result we usually expect. However, I've not used default default value for <width>, because nature of sprintf() is to print numbers without left padding, using default <width> will do most numeric print left padded and very incompatible to C.

For discussion: I've implemented character %c printing using numeric values (in C char type is numeric type), but it is common in Harbour to represent character as strings on length 1. Maybe, we should extend %c, to accept string type and use first character of string.

For discussion: what function name should be used? Since the goal is to be C compatible, I guess HB_SPRINTF() is better than HB_STRFORMAT().


Best regards,
Mindaugas


Source
======
#include <ctype.h>
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapierr.h"


typedef struct
{
   char*   pData;
   ULONG   ulLen;
   ULONG   ulMax;
} BUFFERTYPE;


static void bufadd( BUFFERTYPE* pBuf, char* pAdd, ULONG ulLen )
{
   if( pBuf->ulLen + ulLen >= pBuf->ulMax )
   {
      pBuf->ulMax += pBuf->ulMax >> 1 + ulLen;
      pBuf->pData = ( char* ) hb_xrealloc( pBuf->pData, pBuf->ulMax );
   }
   memcpy( pBuf->pData + pBuf->ulLen, pAdd, ulLen );
   pBuf->ulLen += ulLen;
   pBuf->pData[ pBuf->ulLen ] = '\0';
}


PHB_ITEM hb_sprintf( PHB_ITEM pItemReturn, PHB_ITEM pItemFormat, int iCount, PHB_ITEM* pItemArray )
{
   BUFFERTYPE  buffer;
   PHB_ITEM    pItem;
   char        *pFmt, *pFmtEnd, *pFmtSave;
   int         i, iParam, iParamNo, iWidth, iDec;
   ULONG       ulSize;
   BOOL        fLeftAlign, fForceSign, fPadZero, fSpaceSign;

   pFmt = hb_itemGetCPtr( pItemFormat );
   ulSize = hb_itemGetCLen( pItemFormat );
   pFmtEnd = pFmt + ulSize;

   buffer.ulMax = ulSize + 16;
   buffer.ulLen = 0;
   buffer.pData = ( char* ) hb_xgrab( buffer.ulMax );
   buffer.pData[ 0 ] = '\0';

   iParam = 0;
   while( pFmt < pFmtEnd )
   {
      if( *pFmt != '%' )
      {
         bufadd( &buffer, pFmt++, 1 );
         continue;
      }

      pFmtSave = pFmt;

      if( ++pFmt >= pFmtEnd )
         continue;

      if( *pFmt == '%' )
      {
         bufadd( &buffer, pFmt++, 1 );
         continue;
      }

      iWidth = iDec = -1;
      fLeftAlign = fForceSign = fPadZero = fSpaceSign = 0;

      /* parse parameter number */
      iParamNo = 0;
      while( isdigit( *pFmt ) )
      {
         iParamNo = iParamNo * 10 + *pFmt++ - '0';
      }
      if( iParamNo > 0 && *pFmt == '$' )
      {
         pFmt++;
      }
      else
      {
         iParamNo = -1;
         pFmt = pFmtSave + 1;
      }

      /* Parse flags */
      do
      {
         switch( *pFmt )
         {
            case '-':
               fLeftAlign = 1;
               continue;
            case '+':
               fForceSign = 1;
               continue;
            case ' ':
               fSpaceSign = 1;
               continue;
            case '0':
               fPadZero = 1;
               continue;
         }
         break;
      } while( * ++pFmt );

      /* Parse width */
      if( isdigit( *pFmt ) )
      {
         iWidth = 0;
         while( isdigit( *pFmt ) )
            iWidth = iWidth * 10 + *pFmt++ - '0';
      }

      /* Parse decimals */
      if( *pFmt == '.' )
      {
         pFmt++;
         iDec = 0;
         if( isdigit( *pFmt ) )
         {
            while( isdigit( *pFmt ) )
               iDec = iDec * 10 + *pFmt++ - '0';
         }
      }

      /* Parse specifier */
      if( *pFmt == 'c' || *pFmt == 'd' || *pFmt == 'f' || *pFmt == 's' )
      {
         if( iParamNo == -1 )
            iParamNo = ++iParam;

         if( iParamNo > iCount )
         {
            hb_xfree( buffer.pData );
hb_errRT_BASE_SubstR( EG_ARG, 1099, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
            return NULL;
         }

         pItem = pItemArray[ iParamNo - 1 ];
      }

      switch( *pFmt )
      {
         case 'c':
         {
            char    buf[ 1 ];

            buf[ 0 ] = ( char ) hb_itemGetNI( pItem );
            if( fLeftAlign )
            {
               bufadd( &buffer, buf, 1 );
            }
            if( iWidth > 1 )
            {
               for( i = 1; i < iWidth; i++ )
                  bufadd( &buffer, " ", 1 );
            }
            if( ! fLeftAlign )
            {
               bufadd( &buffer, buf, 1 );
            }
            break;
         }

         case 'd':
         {
            char      *pStr = NULL, *pStr2;
            int       iSize, iExtra;

            if( HB_IS_NUMERIC( pItem ) )
            {
               iSize = sizeof( HB_LONG ) * 3 + 1;
               pStr2 = pStr = ( char* ) hb_xgrab( iSize + 1 );
               hb_itemStrBuf( pStr, pItem, iSize, 0 );
               while( *pStr2 == ' ' )  pStr2++;
               iSize = strlen( pStr2 );
            }
            else if ( HB_IS_LOGICAL( pItem ) )
            {
               iSize = 1;
               if( hb_itemGetL( pItem ) )
                  pStr2 = "1";
               else
                  pStr2 = "0";
            }
            else
            {
               iSize = 1;
               pStr2 = "0";
            }

            iExtra = 0;
            if( ( fForceSign || fSpaceSign ) && *pStr2 != '-' )
               iExtra = 1;

            if( fLeftAlign )
            {
               /* Zero padding is ignored on left Align */
               if( *pStr2 != '-' )
               {
                  /* ForceSign has priority over SpaceSign */
                  if( fForceSign )
                     bufadd( &buffer, "+", 1 );
                  else
                  {
                     if( fSpaceSign )
                        bufadd( &buffer, " ", 1 );
                  }
               }
               bufadd( &buffer, pStr2, ( ULONG ) iSize );
               for( i = iSize + iExtra; i < iWidth; i++ )
                  bufadd( &buffer, " ", 1 );
            }
            else
            {
               /* Right align */
               if( fPadZero )
               {
                  if( *pStr2 == '-' )
                  {
                     bufadd( &buffer, pStr2++, 1 );
                  }
                  else
                  {
                     /* ForceSign has priority over SpaceSign */
                     if( fForceSign )
                        bufadd( &buffer, "+", 1 );
                     else
                     {
                        if( fSpaceSign )
                           bufadd( &buffer, " ", 1 );
                     }
                  }
                  for( i = iSize + iExtra; i < iWidth; i++ )
                     bufadd( &buffer, "0", 1 );

                  bufadd( &buffer, pStr2, strlen( pStr2 ) );
               }
               else
               {
                  for( i = iSize + iExtra; i < iWidth; i++ )
                     bufadd( &buffer, " ", 1 );

                  if( *pStr2 != '-' )
                  {
                     /* ForceSign has priority over SpaceSign */
                     if( fForceSign )
                        bufadd( &buffer, "+", 1 );
                     else
                     {
                        if( fSpaceSign )
                           bufadd( &buffer, " ", 1 );
                     }
                  }
                  bufadd( &buffer, pStr2, ( ULONG ) iSize );
               }
            }

            if( pStr )
               hb_xfree( pStr );
            break;
         }

         case 'f':
         {
            char      *pStr = NULL, *pStr2;
            int       iSize, iExtra, iD;

            if( HB_IS_NUMERIC( pItem ) )
            {
               hb_itemGetNLen( pItem, &iSize, &iD );

               if( iDec != -1 )
               {
                  iSize += iDec - iD + 1;
                  iD = iDec;
               }

               /* Let 255 be a limit for number length */
               if( iSize > 255 )
                  iSize = 255;
               if( iD > 253 )
                  iD = 253;
               if( iSize < iD + 2 )
                  iSize = iD + 2;

               pStr2 = pStr = ( char* ) hb_xgrab( iSize + 1 );
               hb_itemStrBuf( pStr, pItem, iSize, iD );

               if( pStr[ 0 ] == '*' && iSize < 255 )
               {
                  pStr2 = pStr = ( char* ) hb_xrealloc( pStr, 256 );
                  hb_itemStrBuf( pStr, pItem, 255, iD );
               }
               while( *pStr2 == ' ' )  pStr2++;
               iSize = strlen( pStr2 );
            }
            else
            {
               iSize = 1;
               pStr2 = "0";
            }

            iExtra = 0;
            if( ( fForceSign || fSpaceSign ) && *pStr2 != '-' )
               iExtra = 1;

            if( fLeftAlign )
            {
               /* Zero padding is ignored on left Align */
               if( *pStr2 != '-' )
               {
                  /* ForceSign has priority over SpaceSign */
                  if( fForceSign )
                     bufadd( &buffer, "+", 1 );
                  else
                  {
                     if( fSpaceSign )
                        bufadd( &buffer, " ", 1 );
                  }
               }
               bufadd( &buffer, pStr2, ( ULONG ) iSize );
               for( i = iSize + iExtra; i < iWidth; i++ )
                  bufadd( &buffer, " ", 1 );
            }
            else
            {
               /* Right align */
               if( fPadZero )
               {
                  if( *pStr2 == '-' )
                  {
                     bufadd( &buffer, pStr2++, 1 );
                  }
                  else
                  {
                     /* ForceSign has priority over SpaceSign */
                     if( fForceSign )
                        bufadd( &buffer, "+", 1 );
                     else
                     {
                        if( fSpaceSign )
                           bufadd( &buffer, " ", 1 );
                     }
                  }
                  for( i = iSize + iExtra; i < iWidth; i++ )
                     bufadd( &buffer, "0", 1 );

                  bufadd( &buffer, pStr2, strlen( pStr2 ) );
               }
               else
               {
                  for( i = iSize + iExtra; i < iWidth; i++ )
                     bufadd( &buffer, " ", 1 );

                  if( *pStr2 != '-' )
                  {
                     /* ForceSign has priority over SpaceSign */
                     if( fForceSign )
                        bufadd( &buffer, "+", 1 );
                     else
                     {
                        if( fSpaceSign )
                           bufadd( &buffer, " ", 1 );
                     }
                  }
                  bufadd( &buffer, pStr2, ( ULONG ) iSize );
               }
            }

            if( pStr )
               hb_xfree( pStr );
            break;
         }

         case 's':
         {
            char*   pStr = hb_itemGetCPtr( pItem );

            ulSize = hb_itemGetCLen( pItem );
            if( fLeftAlign )
            {
               bufadd( &buffer, pStr, ulSize );
            }
            if( iWidth > 1 )
            {
               for( i = (int) ulSize; i < iWidth; i++ )
                  bufadd( &buffer, " ", 1 );
            }
            if( ! fLeftAlign )
            {
               bufadd( &buffer, pStr, ulSize );
            }
            break;
         }

         default:
            bufadd( &buffer, pFmtSave, pFmt - pFmtSave + 1 );
      }
      pFmt++;
   }

   pItemReturn = hb_itemPutCL( pItemReturn, buffer.pData, buffer.ulLen );
   hb_xfree( buffer.pData );
   return pItemReturn;
}


HB_FUNC( HB_SPRINTF )
{
   PHB_ITEM  pFormat = hb_param( 1, HB_IT_STRING );
   int       i, iParams = hb_pcount();
   PHB_ITEM* pItemArray = NULL;

   if( pFormat )
   {
      if( iParams > 1 )
      {
pItemArray = ( PHB_ITEM* ) hb_xgrab( ( iParams - 1 ) * sizeof( PHB_ITEM ) );
         for( i = 1; i < iParams; i++ )
            pItemArray[ i - 1 ] = hb_param( i + 1, HB_IT_ANY );
      }

hb_itemReturnRelease( hb_sprintf( NULL, pFormat, iParams - 1, pItemArray ) );

      if( iParams > 1 )
      {
         hb_xfree( pItemArray );
      }
   }
   else
hb_errRT_BASE_SubstR( EG_ARG, 1099, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}
_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour

Reply via email to