#include "gpib_parser_casts.h"
#include "gpib_parser_validators_numbers.h" // Validators for numbers
#include "gpib_parser_chars.h"              // Service characters definitions
#include "gpib_parser_validators.h"
#include "gpib_parser_numutils.h"
#include "gpib_parser_strutils.h"
#include <stdlib.h>  // atoi

#include "usb\utils.h" // s_memcpy()
//------------------------------------------------------------------------------
static char SCPIBuffer[ STRCACHE_BUFFER_SIZE ];
//------------------------------------------------------------------------------

// GPIB_StrChkFormat:
// Checks the string @str limited by @length characters for matching to the format @mode.
// In case all of the characters til the end of string (not far than @length characters) are
// match to the format @mode, function returns TRUE. otherwise, it returns FALSE.
BOOL GPIB_StrChkFormat( const char * str, int length, const eStringCheckMode_t mode )
{
    // return FALSE in case @str is NULL, or @length is zero
    if( NULL == str || 0 >= length ) 
        return FALSE;
    
    // result by default is FALSE
    BOOL rval = FALSE;

    // Check til the end of string limited by @length
    // Every step decrement @length and increment @str
    for( (void)0; (length > 0); length --, str ++ )
    {     
        // Check for the end-of-string
        // Check for the end-of-line        
        if( IsEndOfString( *str ) || IsEndOfLine( *str ) )
            break; // If found, interrupt the processing and return the current result
        
        switch( mode )
        {
            case eSCM_DEC:       rval = IsDemicalChar( *str );                   break;
            case eSCM_HEX:       rval = IsHexademicalChar( *str );               break;
            case eSCM_FLOAT:     rval = IsFloatChar( *str );                     break;
            case eSCM_EXP:       rval = IsExponentialChar( *str );               break;
            case eSCM_BIN:       rval = IsBinaryChar( *str );                    break;
            case eSCM_MNEMONIC:  rval = IsCharacter( *str ) || IsNumber( *str ); break;
            case eSCM_DIGIT:     rval = IsDigit( *str );                         break;
        }
        
        // In case of first unsuccessful result, break and return current result (false)
        if( !(rval) ) break;
    }    

    return rval;
}

//---------------------------
// @GPIB_StrCopyToCache:
// Caches the given string @source into the internal buffer and returns the new pointer
char * GPIB_StrCopyToCache( const char * source, size_t length )
{ 
    char * dest;

    if( length+1 <= STRCACHE_BUFFER_SIZE )
        dest = &SCPIBuffer[0];
    else
        dest = NULL;
     
    if( !IsCorrectPointer (dest) ) 
        dest = NULL;
    else
    {
        s_memcpy( dest, source, length );
        strucase( dest, length - 1 );
        dest [ length ] = 0;
    }
  
    return dest;
}

// GPIB_GetStringEnd:
// Searches for the end of the string @gpibstr until the end of @gpibstr is reached
// or @length characters is processed 
// Note: the string must be quotted in single or double quotes.
const char * GPIB_GetStringEnd( const char * gpibstr, size_t length )
{
    // It is assumed the quotted string can not be shorter than 2 characters
    if( (NULL == gpibstr) || (2 > length) )
        return NULL;
    
    // detect the quotting mode: single or doulbe
    eQuoteMode_t qMode = eqmNone;    
    if( IsSingleQuote( *gpibstr ) )
    {
        qMode = eqmSingle;
    }    
    else
    if( IsDoubleQuote( *gpibstr ) )
    {
        qMode = eqmDouble;
    }    
    else
    {   
        return NULL;
    }

    const char * end = NULL;        // end of the string pointer
    
    BOOL bQuoteOpened = FALSE;
    
    // for each of @length characters in the string
    for( const char * iter = gpibstr; iter != (gpibstr + length); ++iter )
    {
        // If the current parameter is end-of-line or end-of-string,
        // and the cycle is still counting, it means error condition,
        // due to @length is specified incorrectly.
        if( IsEndOfString( *iter ) || IsEndOfLine( *iter ) )
        {
            return NULL;
        }
        
        if( IsQuote(*iter,qMode) )
        {
            // If the quotted string opened (opening quote passed)
            if( bQuoteOpened )
            {
                // End-of-quotted string found
                end = ++iter;
                break;
            }            
            else
            {
                // The quotted string opened
                bQuoteOpened = TRUE;
            }
        }                
    }
    
    return end;
}

// GPIB_GetParameterEnd:
// Searches for the end signature of the parameter in @string until the 
// end of @string string is reached or @length characters is processed.
// The first "white" character is treated as an end signature.
// Does not support SCPI DataBlock format.
const char * GPIB_GetParameterEnd( const char * string, size_t length )
{    
    if( (NULL == string) || (0 == length) )
        return NULL;
    
    const char * end = (string + length);  // end of the string pointer
    
    // for each of @length characters in the string
    for( const char * iter = string; iter != end; ++iter )
    {
        // If the current parameter is end-of-line,  end-of-string or white-character
        // then it means the end of the parameter reached.        
        if( IsEndOfString( *iter ) || IsEndOfLine( *iter ) || IsWhiteChar( *iter ) )
        {
            return iter;
        } 
        
        // If the current parameter is an argument separator then
        // it means the end of the parameter reached.        
        if( IsArgSeparatorChar( *iter ) )
        {
            return iter;
        } 
    }
    
    // Neither end-of-line, end-of-string or white-character found.
    // The end of the parameter is out of string.
    return end;
}


// GPIB_StrChr:
// Searches a character @chr in the string beginning from @str and limited by @end.
// If the function faces a null-character or end-of-line condition, it stops searching.
// Function returns the pointer to the found character in @str, or NULL if not found.
const char * GPIB_StrChr( const char * str, const char chr, const char * end )
{
    for( const char * iter = str; iter != end; ++iter )
    {
        // Check the limits
        if( IsEndOfString( *iter ) || IsEndOfLine( *iter ) )    
        {
            // end-of-string condition: interrupt the searching
            // end-of-line condition: interrupt the searching
            break;
        }
        
        if( chr == *iter )
        {
            return iter;
        }
    }
    
    // not found
    return NULL;
}

// GPIB_StrLen:
// Calculates the length of string.
// The function assumes that the string ends with either end-of-line character
//  or end-of-string character.
size_t GPIB_StrLen( const char * str )
{
    if( NULL == str )
        return 0;
    
    for( const char * iter = str; (void)iter, !0; ++iter )
    {
        // Check the limits
        if( IsEndOfString( *iter ) || IsEndOfLine( *iter ) )    
        {
            // end-of-string condition: end the searching
            // end-of-line condition: end the searching
            return SubPointers( iter, str );
        }
    }
    
    // never reachs this point
    return 0;
}

// GPIB_CheckHeader:
// Check if the given @header is valid GPIB header.
// Returns TRUE if it is, and FALSE otherwise.
//BOOL GPIB_CheckHeader( const char * header )
BOOL GPIB_CheckHeader( const char * header, const char * end )
{
    if( NULL == header )
        return FALSE;
    
    // disallow the number in the beginning of @header
    if( IsNUMBER( *header ) )
        return FALSE;
        
    BOOL rval = TRUE;
    
    // allow one 'MANDATORY' signature (by default '*', e.g "*IDN?") in 
    //  the beginning of the string
    if( GPIB_CHAR_MANDATORY_IEEE488_SIGN == *header )
    {
        // skip only the first MANDATORY character
        ++ header;
    }
        
    for( const char * iter = header; (void)iter, !0; ++iter )  
    {
        // Check the end condition
        if( iter == end )
        {  
            (void)rval;
            break;
        }    
        
        // Check the limits
        if( IsEndOfString( *iter ) || IsEndOfLine( *iter ) )    
        {
            // end-of-string condition: interrupt validating
            // end-of-line condition: interrupt validating
            break;
        }
        
        // check if the character is valid for the GPIB header body
        if( ! IsValid_GPIBHeaderBodyChar( *iter ) )
        {
            // if at least one character is invalid, break and return FALSE
            rval = FALSE;
            break;
        }
    }
      
    return rval;
}

// GPIB_HeaderCompare:
// Compares a couple of GPIB command headers.
// Note: the comparation is case insensitive.
// @headerTree - the original command header, including an optional command 
//   signature (if required), e.g. optional "[SYSTem]" or mandatory "SYST"
// @headerUser - actually entered command header
// Note: for optional headers it is not required a paired ']' in the end of header.
// Returns TRUE if headers are identical, and FALSE otherwise.
BOOL GPIB_HeaderCompare(  const char * headerTree, const char * headerUser )
{ 
    BOOL bOptional = FALSE;
    BOOL bFirst    = TRUE;
    
    //size_t strlen_Tree = GPIB_StrLen( headerTree );
    //size_t strlen_User = GPIB_StrLen( headerUser );
    
    
    if( IsOptionalHeader(headerTree) ) 
    { 
        
        headerTree++; 
        bOptional = TRUE;         
    }
    
    const char * iter_tree;
    const char * iter_user;
    
    // For each character in the original string @headerTree and the 
    // entered string @headerUser simultaneosly.
    for( iter_tree = headerTree,  // iterator for original string
         iter_user = headerUser,  // iterator for entered string
         bFirst = TRUE;           // the first character sign
         (void)iter_tree,         // 
         (void)iter_user,         //
         !0;                      // do this cycle forever
         ++iter_tree,             // next character in the original string
         ++iter_user,             // next character in the entered string
         bFirst = FALSE )         // reset @bFirst after the first character in the string
    {
        // Check the limits:
        // If the end of original string is faced.
        if( IsEndOfString( *iter_tree ) || IsEndOfLine( *iter_tree ) )    
        {
            // and the end of the entered string is faced too
            if( IsEndOfString( *iter_user ) || IsEndOfLine( *iter_user ) )    
            {
                // headers are identical due to all the characters til 
                // the end of both strings are identical.                
                return TRUE;
            }
        }
        else
        // The end of original string is NOT faced yet.
        {
            // But if the end of the entered string is faced
            if( IsEndOfString( *iter_user ) || IsEndOfLine( *iter_user ) )    
            {
                // end of compare
                // strings mismatch
                
                // But wait, let's check for the optional header signature in 
                // the end of original string (by default, ']')
                // Check if the header is optional
                if( bOptional )
                {
                    // yes, it is optional
                    
                    // check if the next character in the original string is
                    // a closing sign of optional header (by default, ']')
                    if( GPIB_CHAR_OPTIONALHEADER_ENDSIGN == *iter_tree )
                    {
                        // yes, it is.
                        // Actually, the headers are identical til the
                        // closing sign.
                        
                        // ignore the rest part of original string, and
                        // assume the headers are identical
                        return TRUE;
                    }
                }
            
                // No choice left: headers mismatch                
                break;
            }
        }
        
        // Check for optional marker (by default, '[') in the beginning of the original string
        if( (bFirst) && (GPIB_CHAR_OPTIONALHEADER_SIGN == *iter_tree) )
        {
            // This header is a header of optional SCPI-subsystem 
            bOptional = TRUE;
            
            // skip this character
            iter_tree++;
            
            // continue the comparation
            continue;
        }
        
        // characters mismatch
        // Note: comparation is case insensetive.
        if( tolower(*iter_tree) != tolower(*iter_user)  )
        {
            // strings mismatch
            break;
        }
    }
    
    // headers mismatch 
    return FALSE;
}


// GPIB_StringToNumber: 
// Converts the string into a number
// @str - source string pointer
// @chars - number of characters to be converted (can not be greater than 10)
// @pReturn - pointer to the receiving variable, can be NULL
// Result: TRUE in case of success, FALSE otherwise.
// In case the given number is valid and @pReturn is NULL, function just return TRUE
// This behaviour can be used to validate numbers without parsing.
BOOL GPIB_StringToNumber( const char * str, size_t chars,  int32_t * pReturn )
{    
    char buffer[ 11 ] = {0};
    
    if( chars > 10 ) 
        return FALSE;       // too long string 
    
    if( chars == 0 ) 
        return FALSE;       // too short string 
    
    // validate the string
    
    BOOL allow_sign = TRUE;      // flag: allow the sign operator (plus/minus)
    BOOL allow_space = TRUE;     // flag: allow the space
    BOOL allow_digit = TRUE;     // flag: allow a digit    
    BOOL digits = FALSE;         // flag: digits detected
    
    for( const char * iter = str; iter != (str+chars); ++iter )
    {
        if( '\0' == *iter )
            break;
        
        // the space faced
        if( ' ' == *iter )
        {
            // if the space is allowed
            if( allow_space )
            {
                // if digits already faced
                if( digits )
                {
                    // allow only spaces til the end
                    allow_digit = FALSE;
                }
                
                continue;
            }
            else
            {
                return FALSE;           // error: space is unexpected
            }
        }        
        
        // the sign faced
        if( '+' == *iter || '-' == *iter )
        {
            if( allow_sign )
            {
                allow_sign = FALSE;     // only one operator is allowed
                allow_space = FALSE;    // disallow the space after the operator                
                continue;
            }
            
            return FALSE;               // error: extra sign operator
            
        }

        // a digit faced
        if( IsDigit( *iter ) )
        {
            if( allow_digit )
            {
                allow_sign = FALSE;     // operator must be before any digit
                allow_space = TRUE;     // allow any amount of spaces after last digit
                digits = TRUE;          // digits faced
                continue;
            }
            
            return FALSE;               // error: separated tokens detected
        }
        
        return FALSE;                   // error by default. the "continue" must prevent this return
    }
    
    // here: the string contains a valid number
    
    // if @pReturn is NULL, just return the result of validating
    if( NULL == pReturn )
        return TRUE;
        
    s_memcpy( buffer, str, chars );    
    buffer[chars] = '\0';   // null terminator
    
    *pReturn = atoi( buffer );
    
    return TRUE;
}

// IsDataBlock:
// The data transfer block validator: Binary transfer format (IEEE488.2)
// Format: the data block consist of the following elements: '#', N, Z, DATA
// '#' - data block signature character;
// N - a demical digit, amount of digits in Z;
// Z - a demical number (multi-digit), the size of binary bytes in DATA.
// DATA - binary data bytes.
// e.g. "#213Hello, world!"
//  N=2  - amount of digits in Z      
//  Z=13 - amount of binary bytes (strlen(DATA)=13)     
// DATA = "Hello, world!"
//
// @begin - the string to validate
// @length - the length of string @begin
// @p_nsize - optional pointer which will be filled up with the datablock size in bytes
// @pRetCode - optional pointer to store either the error variable to be filled with the 
//   error code, or in case sucess the binary data payload size in bytes.
// *pRetCode can be:
// > (err_IsDataBlock_Success) - success (never returns);
// > (err_IsDataBlock_NotFound) - datablock not found; 
// > (err_IsDataBlock_Invalid) - datablock found, but has invalid format
// > (err_IsDataBlock_Trimmed) - datablock found, but it is trimmed
// > (err_IsDataBlock_Huge) - datablock found, but it's too large
// > the binary payload size
//
// Returns TRUE only if the datablock format is valid, all the parameter of datablock
// are valid and do not violates any ranges.
BOOL IsDataBlock( const char * begin, size_t length, size_t * p_nsize, int * pRetCode )
{
    int error = 0;
    size_t N = 0;
    int32_t Z = 0;
    const char * iter = NULL;
    
    // at least 2 bytes required
    if( NULL == begin || 2 > length )
    {   
        // invalid parameters
        // error: datablock not found
        error = err_IsDataBlock_NotFound;
        
        goto L_IsDataBlock_ERR;
    }    
    
    // check the signature
    if( GPIB_CHAR_DATATABLE_SIGN != *begin )
    {    
        // invalid: no signature
        // error: datablock not found
        error = err_IsDataBlock_NotFound;
        
        goto L_IsDataBlock_ERR;
    }    

    iter = begin;
    
    {  // decrease length and shift iterator: '#'
        length--;   
        iter++;     
    }
    
    if( !IsDemicalChar( *iter ) )
    {    
        // invalid: "N" must be a demical digit (amount of characters in "Z")
        // error: datablock not found
        error = err_IsDataBlock_NotFound;
        
        goto L_IsDataBlock_ERR;
    }
    
    N = DecCharToNum( *iter );   // number of characters in "Z"
    
    {  // decrease length and shift iterator: 'N'
        length--;   
        iter++;     
    }
    
    if( N == 0 )
    {    
        // invalid: N can not be zero
        // error: datablock found with invalid format
        error = err_IsDataBlock_Invalid;
        
        goto L_IsDataBlock_ERR;  
    }
    
    if( (length == 0) || (length < N) )
    {    
        // empty data block
        // error: trimmed datablock found
        error = err_IsDataBlock_Trimmed;
        
        goto L_IsDataBlock_ERR;   
    }
    
    // check the int32 overflow condition: 
    // If N=9, it means Z is less than 1000000000
    // and there no necessary to provide any checks.
    // Lets choose this way.
    if( N > 9 )
    {    
        // integer overflow
        // error: datablock is too large.
        error = err_IsDataBlock_Huge;
        
        goto L_IsDataBlock_ERR;   
    }
    
    // validate "Z" - must be a demical digits
    if( !GPIB_StrChkFormat( iter, N, eSCM_DIGIT ) )
    {    
        // invalid: invalid format of "Z"
        // error: datablock found with invalid format
        error = err_IsDataBlock_Invalid;
        
        goto L_IsDataBlock_ERR;   
    }
       
    Z = 0;
    
    // Parse the number "Z"
    if( !GPIB_StringToNumber( iter, N, &Z ) )
    {    
        // invalid number "Z"
        // error: datablock found with invalid format
        error = err_IsDataBlock_Invalid;
        
        goto L_IsDataBlock_ERR;   
    }    
    
    {  // decrease length and shift iterator: 'Z'
        length-=N;   
        iter+=N;     
    }
    
    // check if the datablock fits into the given string 
    if( Z > length )
    {    
        // trimmed data block
        // error: trimmed datablock found
        error = err_IsDataBlock_Trimmed;
        
        goto L_IsDataBlock_ERR;   
    }
    // Okay, it seems to be valid...
    
    {  // decrease length and shift iterator: 'DATA'
        length-=Z;   
        iter+=Z;     
    }
    
    // if @p_nsize is specified
    if( NULL != p_nsize )
        *p_nsize = SubPointers( iter, begin );      // store the datablock size in bytes
    
    // store the binary data size into @error
    if( NULL != pRetCode ) *pRetCode = Z;
    
    return TRUE;
    
L_IsDataBlock_ERR:

    if( NULL != pRetCode ) *pRetCode = error;
    return FALSE;
}

// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
// #########################################################################
//-----------------------------------
/*
char * s_strchrex( char * str, char chr, char * end )
{
  register char * strp = str;
  char * ret = NULL;
  while(ret==NULL)
  { if((unsigned int)end <= (unsigned int)str) break;  
    if(*strp==chr) ret = strp;
    strp++;
  }
 return ret;
}
*/
/*
char * strterm( char * source, unsigned int length )
{
 //<W><W><W>AAAA<W><W><0>  -> <W><W><W>AAAA<0>
  while(!IsWhiteChar(*source) && length--)
     source++;
  if(length>0) *source = 0x00;

   return source;
}
*/
//-----------------------------------
/*
int strsep( char * source, unsigned int length )
{
  int first = 0;
 //<W><W>BBBBB<W><W>AAAA<W><W><0>  -> <W><W><W>AAAA<0>
  while(IsWhiteChar(*source) && length--) source++;

  first = length;

  while(!IsWhiteChar(*source) && length--)
     source++;

  if(length>0) *source = 0x00;

  return first;
}
*/


// ===================================== Преобразование чисел в строки ===========================
// -----------------------
//
//
//
//
//

/*
// ------------------- FloatsToString --------------
//
// Использование:
//
// char buffer[32];
// float f1,f2,f3,f4,f5,f6,f7,f8=3.142;
// GPIB_PrintFloat( buffer, 32, 1000, 8, &f1, &f2, &f3, &f4, &f5, &f6, &f7, &f8 );
//                              ^^^^ точноть после запятой
//
// buffer=="0.000,0.000,0.000,0.000,0.000,0.000,0.000,3.142"
unsigned int GPIB_PrintFloatEx( char * out, int buffersize, unsigned int accuracy, int count, float * f1, ...)
{
  int l=0,c=0;
  unsigned char buffer[10] = {0};
  while( count-- && c<buffersize )
  {
     if(c){
     *(out+c) = ',';
     c++;}

     l = Float2StrEx( f1,  buffer, 10, accuracy);
     s_memcpy( out+c, buffer, l );
     c+=l;

   f1++;
  }
  return c;
}
*/
/*
unsigned int GPIB_PrintHex( char * out, int buffersize, int count, int * hex1, ...)
{
  int c=0;
  while( count-- && c < buffersize )
  {
     if(c)
        c+= snprintf( out+c, buffersize-c, "," );

     c+= snprintf( out+c, buffersize-c, "%x", *hex1 );

   hex1++;
  }
  return c;
}
*/


// ---------------------------------------------------------------------------------------------
/*
unsigned int GPIB_PrintBin( char * out, int buffersize, int count, unsigned int  bin, ...)
{
  int c=0;
  unsigned int * bin1 = &bin;
  unsigned int mask;
  while( count-- && c < buffersize )
  {
     if(c)
        c+= snprintf( out+c, buffersize-c, "," );
        // -----------------------------
          if( (c < buffersize) )  *(out+c++) = '0';
          if( (c < buffersize) )  *(out+c++) = 'b';
          mask = 0x80000000; // 0b10000000000000000000000000000000
          while( mask && (c < buffersize) )
               {
               if( mask & *bin1 )
                  *(out+c++) = '1';
               else
                  *(out+c++) = '0';

               if( mask & (1<<0) )
                 mask = 0x00000000;
               else
                 mask>>=1;
               }
        // -----------------------------
   bin1++;
  }
  return c;
}
*/

/*
// ------------ s_printf ----------------------------------
// Поддержка спецификаторов:
// 0 %c - char
// 1 %s - string
// 2 %d - int
// 3 %u - unsigned int
// 4 %f - float (!)
// 5 %x - hex
// 6 %b - bin
// 7 %o - oct
// 8 %e - scientific number
// -------------------------- ЗНАЧЕНИЯ ПЕРЕДАЮТСЯ ПО УКАЗАТЕЛЮ !
unsigned int s_nprintf( char * out, int buffersize, char * spec, /_* char , int , float , unsigned  *_/... )
{
  // При передаче параметров через стек в функцию, которая не имеет формальных аргументов
  // действует правило приведения float->double. Double на стеке вырванивается по 8 байт
  //
  //
  char * pointer = (char*)(&spec)+sizeof(char*);
  unsigned int c = 0;  // -- количество выводимых в буфер символов
  unsigned int k = 0;  // -- для цикла по спецификаторам
  unsigned int z = 0;  // -- для поиска спецификатора
  int t = 0;
  char * s, * __s;   // -- хранит сдвиг по spec
  int zmin = -1;      // -- для поиска спецификатора
  #define fc 9
  unsigned char specs[fc] = { 'c', 's', 'd', 'u', 'f', 'x', 'b', 'o', 'e' };
 
   while( c < buffersize && *spec  && t!=-1)
   if(*spec == '%'){

      // ------------ определение типа ------------
      for(k=0,t=-1,zmin=-1;k<=fc;k++)
       if( (__s= GPIB_StrChr( spec, specs[k], NULL)) || (__s= GPIB_StrChr( spec, 0x20 + specs[k], NULL))) // ucase support also
        { 
          z=(unsigned int)__s - (unsigned int)spec;
          if(zmin > z || zmin ==-1)  
          { zmin = z;
            t=k;   // тип 
            s=__s; // определяет конец спецификатора
          }
        }
     //--------------------------------------------
       switch(t)
       {
        case 0:     // %c
          // -- char преобразован к int
          c+=snprintf( out+c, buffersize-c, "%c", *((int *)pointer));
          pointer+=sizeof(unsigned int);
          spec = s+1;
        break;
        case 1:     // %s
          c+=snprintf( out+c, buffersize-c, "%s", (char *)pointer);
          pointer+=sizeof(char*);
          spec = s+1;
        break;
        case 2:     // %d
          c+=snprintf( out+c, buffersize-c, "%d", *((int *)pointer));
          pointer+=sizeof(int);
          spec = s+1;
        break;
        case 3:     // %u
          c+=snprintf( out+c, buffersize-c, "%u", *((unsigned int *)pointer));
          pointer+=sizeof(unsigned int);
          spec = s+1;
        break;
        case 4:     // %f
        // case 8:     // %e
          if((int)pointer&0x07) pointer+= (int)pointer%8;               // Стандартное продвижение типов. Если вводить функцию без формальных
                                                                          // параметров, то float будет преобразован к double, char к int и т.п.
                                                                             // double весит 8 байт, поэтому надо это учитывать при передвижении указателя
          c+=snprintf( out+c, buffersize-c, "%lf", *((double *)pointer));
          pointer+=sizeof(double); // double!
          spec = s+1;
        break;
        case 5:     // %x
           c+=snprintf( out+c, buffersize-c, "%x", *((unsigned int *)pointer));
           pointer+=sizeof(unsigned int); 
           spec = s+1; 
        break;
        case 6:     // %b
           c+=GPIB_PrintBin( out+c, buffersize-c, 1, *((unsigned int*)pointer));
           pointer+=sizeof(unsigned int); // double!
           spec = s+1; 
        break;
        case 7:     // %o
          c+=snprintf( out+c, buffersize-c, "%o", *((unsigned int *)pointer));
          pointer+=sizeof(unsigned int); 
          spec = s+1;
        break;
        case 8:     // %e
          c+=snprintf( out+c, buffersize-c, "%e", *((double*)pointer));
          pointer+=sizeof(double); 
          spec = s+1;
        break;
        default:
          if( t==-1) break;
          spec = s+1;
       }

   } else 
     {snprintf( out+c, buffersize-c, "%c", *(spec++) ); c++;}
   
 return c;
}  
*/