#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 // 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 ) { //AAAA<0> -> AAAA<0> while(!IsWhiteChar(*source) && length--) source++; if(length>0) *source = 0x00; return source; } */ //----------------------------------- /* int strsep( char * source, unsigned int length ) { int first = 0; //BBBBBAAAA<0> -> 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>=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; } */