https://github.com/j123b567/scpi-parser

Dependents:   scpi_sx127x scpi_sx127x_firstTest MLX90418_I2C_master

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers parser.c Source File

parser.c

00001 /*-
00002  * Copyright (c) 2012-2013 Jan Breuer,
00003  *
00004  * All Rights Reserved
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are
00008  * met:
00009  * 1. Redistributions of source code must retain the above copyright notice,
00010  *    this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00018  * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
00019  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
00022  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00023  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00024  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00025  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  */
00027 
00028 /**
00029  * @file   scpi_parser.c
00030  * @date   Thu Nov 15 10:58:45 UTC 2012
00031  *
00032  * @brief  SCPI parser implementation
00033  *
00034  *
00035  */
00036 
00037 #include <ctype.h>
00038 #include <string.h>
00039 
00040 #include "scpi/config.h"
00041 #include "scpi/parser.h"
00042 #include "parser_private.h"
00043 #include "lexer_private.h"
00044 #include "scpi/error.h"
00045 #include "scpi/constants.h"
00046 #include "scpi/utils.h"
00047 
00048 /**
00049  * Write data to SCPI output
00050  * @param context
00051  * @param data
00052  * @param len - lenght of data to be written
00053  * @return number of bytes written
00054  */
00055 static size_t writeData(scpi_t * context, const char * data, size_t len) {
00056     return context->interface->write(context, data, len);
00057 }
00058 
00059 /**
00060  * Flush data to SCPI output
00061  * @param context
00062  * @return
00063  */
00064 static int flushData(scpi_t * context) {
00065     if (context && context->interface && context->interface->flush) {
00066         return context->interface->flush(context);
00067     } else {
00068         return SCPI_RES_OK;
00069     }
00070 }
00071 
00072 /**
00073  * Write result delimiter to output
00074  * @param context
00075  * @return number of bytes written
00076  */
00077 static size_t writeDelimiter(scpi_t * context) {
00078     if (context->output_count > 0) {
00079         return writeData(context, ",", 1);
00080     } else {
00081         return 0;
00082     }
00083 }
00084 
00085 /**
00086  * Conditionaly write "New Line"
00087  * @param context
00088  * @return number of characters written
00089  */
00090 static size_t writeNewLine(scpi_t * context) {
00091     if (context->output_count > 0) {
00092         size_t len;
00093 #ifndef SCPI_LINE_ENDING
00094 #error no termination character defined
00095 #endif
00096         len = writeData(context, SCPI_LINE_ENDING, strlen(SCPI_LINE_ENDING));
00097         flushData(context);
00098         return len;
00099     } else {
00100         return 0;
00101     }
00102 }
00103 
00104 /**
00105  * Conditionaly write ";"
00106  * @param context
00107  * @return number of characters written
00108  */
00109 static size_t writeSemicolon(scpi_t * context) {
00110     if (context->output_count > 0) {
00111         return writeData(context, ";", 1);
00112     } else {
00113         return 0;
00114     }
00115 }
00116 
00117 /**
00118  * Process command
00119  * @param context
00120  */
00121 static void processCommand(scpi_t * context) {
00122     const scpi_command_t * cmd = context->param_list.cmd;
00123     lex_state_t * state = &context->param_list.lex_state;
00124 
00125     /* conditionaly write ; */
00126     writeSemicolon(context);
00127 
00128     context->cmd_error = FALSE;
00129     context->output_count = 0;
00130     context->input_count = 0;
00131 
00132     /* if callback exists - call command callback */
00133     if (cmd->callback != NULL) {
00134         if ((cmd->callback(context) != SCPI_RES_OK) && !context->cmd_error) {
00135             SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR);
00136         }
00137     }
00138 
00139     /* set error if command callback did not read all parameters */
00140     if (state->pos < (state->buffer + state->len) && !context->cmd_error) {
00141         SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED);
00142     }
00143 }
00144 
00145 /**
00146  * Cycle all patterns and search matching pattern. Execute command callback.
00147  * @param context
00148  * @result TRUE if context->paramlist is filled with correct values
00149  */
00150 static scpi_bool_t findCommandHeader(scpi_t * context, const char * header, int len) {
00151     int32_t i;
00152     const scpi_command_t * cmd;
00153 
00154     for (i = 0; context->cmdlist[i].pattern != NULL; i++) {
00155         cmd = &context->cmdlist[i];
00156         if (matchCommand(cmd->pattern, header, len, NULL, 0)) {
00157             context->param_list.cmd = cmd;
00158             return TRUE;
00159         }
00160     }
00161     return FALSE;
00162 }
00163 
00164 /**
00165  * Parse one command line
00166  * @param context
00167  * @param data - complete command line
00168  * @param len - command line length
00169  * @return 1 if the last evaluated command was found
00170  */
00171 int SCPI_Parse(scpi_t * context, char * data, int len) {
00172     int result = 0;
00173     scpi_parser_state_t * state;
00174     int r;
00175     scpi_token_t cmd_prev = {SCPI_TOKEN_UNKNOWN, NULL, 0};
00176 
00177     if (context == NULL) {
00178         return -1;
00179     }
00180 
00181     state = &context->parser_state;
00182     context->output_count = 0;
00183 
00184     while (1) {
00185         result = 0;
00186 
00187         r = scpiParser_detectProgramMessageUnit(state, data, len);
00188 
00189         if (state->programHeader.type == SCPI_TOKEN_INVALID) {
00190             SCPI_ErrorPush(context, SCPI_ERROR_INVALID_CHARACTER);
00191         } else if (state->programHeader.len > 0) {
00192 
00193             composeCompoundCommand(&cmd_prev, &state->programHeader);
00194 
00195             if (findCommandHeader(context, state->programHeader.ptr, state->programHeader.len)) {
00196 
00197                 context->param_list.lex_state.buffer = state->programData.ptr;
00198                 context->param_list.lex_state.pos = context->param_list.lex_state.buffer;
00199                 context->param_list.lex_state.len = state->programData.len;
00200                 context->param_list.cmd_raw.data = state->programHeader.ptr;
00201                 context->param_list.cmd_raw.position = 0;
00202                 context->param_list.cmd_raw.length = state->programHeader.len;
00203 
00204                 processCommand(context);
00205 
00206                 result = 1;
00207                 cmd_prev = state->programHeader;
00208             } else {
00209                 SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER);
00210             }
00211         }
00212 
00213         if (r < len) {
00214             data += r;
00215             len -= r;
00216         } else {
00217             break;
00218         }
00219 
00220     }
00221 
00222     /* conditionaly write new line */
00223     writeNewLine(context);
00224 
00225     return result;
00226 }
00227 
00228 /**
00229  * Initialize SCPI context structure
00230  * @param context
00231  * @param command_list
00232  * @param buffer
00233  * @param interface
00234  */
00235 void SCPI_Init(scpi_t * context) {
00236     if (context->idn[0] == NULL) {
00237         context->idn[0] = SCPI_DEFAULT_1_MANUFACTURE;
00238     }
00239     if (context->idn[1] == NULL) {
00240         context->idn[1] = SCPI_DEFAULT_2_MODEL;
00241     }
00242     if (context->idn[2] == NULL) {
00243         context->idn[2] = SCPI_DEFAULT_3;
00244     }
00245     if (context->idn[3] == NULL) {
00246         context->idn[3] = SCPI_DEFAULT_4_REVISION;
00247     }
00248 
00249     context->buffer.position = 0;
00250     SCPI_ErrorInit(context);
00251 }
00252 
00253 /**
00254  * Interface to the application. Adds data to system buffer and try to search
00255  * command line termination. If the termination is found or if len=0, command
00256  * parser is called.
00257  *
00258  * @param context
00259  * @param data - data to process
00260  * @param len - length of data
00261  * @return
00262  */
00263 int SCPI_Input(scpi_t * context, const char * data, int len) {
00264     int result = 0;
00265     size_t totcmdlen = 0;
00266     int cmdlen = 0;
00267 
00268     if (len == 0) {
00269         context->buffer.data[context->buffer.position] = 0;
00270         result = SCPI_Parse(context, context->buffer.data, context->buffer.position);
00271         context->buffer.position = 0;
00272     } else {
00273         int buffer_free;
00274 
00275         buffer_free = context->buffer.length - context->buffer.position;
00276         if (len > (buffer_free - 1)) {
00277             return -1;
00278         }
00279         memcpy(&context->buffer.data[context->buffer.position], data, len);
00280         context->buffer.position += len;
00281         context->buffer.data[context->buffer.position] = 0;
00282 
00283 
00284         while (1) {
00285             cmdlen = scpiParser_detectProgramMessageUnit(&context->parser_state, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen);
00286             totcmdlen += cmdlen;
00287 
00288             if (context->parser_state.termination == SCPI_MESSAGE_TERMINATION_NL) {
00289                 result = SCPI_Parse(context, context->buffer.data, totcmdlen);
00290                 memmove(context->buffer.data, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen);
00291                 context->buffer.position -= totcmdlen;
00292                 totcmdlen = 0;
00293             } else {
00294                 if (context->parser_state.programHeader.type == SCPI_TOKEN_UNKNOWN) break;
00295                 if (totcmdlen >= context->buffer.position) break;
00296             }
00297         }
00298     }
00299 
00300     return result;
00301 }
00302 
00303 /* writing results */
00304 
00305 /**
00306  * Write raw string result to the output
00307  * @param context
00308  * @param data
00309  * @return
00310  */
00311 size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len) {
00312     size_t result = 0;
00313     result += writeDelimiter(context);
00314     result += writeData(context, data, len);
00315     context->output_count++;
00316     return result;
00317 }
00318 
00319 /**
00320  * Write integer value to the result
00321  * @param context
00322  * @param val
00323  * @return
00324  */
00325 size_t SCPI_ResultInt(scpi_t * context, int32_t val) {
00326     return SCPI_ResultIntBase(context, val, 10);
00327 }
00328 
00329 /**
00330  * Return prefix of nondecimal base
00331  * @param base
00332  * @return
00333  */
00334 static const char * getBasePrefix(int8_t base) {
00335     switch (base) {
00336         case 2: return "#B";
00337         case 8: return "#Q";
00338         case 16: return "#H";
00339         default: return NULL;
00340     }
00341 }
00342 
00343 /**
00344  * Write integer value in specific base to the result
00345  * @param context
00346  * @param val
00347  * @param base
00348  * @return
00349  */
00350 size_t SCPI_ResultIntBase(scpi_t * context, int32_t val, int8_t base) {
00351     char buffer[33];
00352     const char * basePrefix;
00353     size_t result = 0;
00354     size_t len;
00355 
00356     len = SCPI_LongToStr(val, buffer, sizeof (buffer), base);
00357     basePrefix = getBasePrefix(base);
00358 
00359     result += writeDelimiter(context);
00360     if (basePrefix != NULL) {
00361         result += writeData(context, basePrefix, 2);
00362     }
00363     result += writeData(context, buffer, len);
00364     context->output_count++;
00365     return result;
00366 }
00367 
00368 /**
00369  * Write double walue to the result
00370  * @param context
00371  * @param val
00372  * @return
00373  */
00374 size_t SCPI_ResultDouble(scpi_t * context, double val) {
00375     char buffer[32];
00376     size_t result = 0;
00377     size_t len = SCPI_DoubleToStr(val, buffer, sizeof (buffer));
00378     result += writeDelimiter(context);
00379     result += writeData(context, buffer, len);
00380     context->output_count++;
00381     return result;
00382 
00383 }
00384 
00385 /**
00386  * Write string withn " to the result
00387  * @param context
00388  * @param data
00389  * @return
00390  */
00391 size_t SCPI_ResultText(scpi_t * context, const char * data) {
00392     size_t result = 0;
00393     result += writeDelimiter(context);
00394     result += writeData(context, "\"", 1);
00395     // TODO: convert " to ""
00396     result += writeData(context, data, strlen(data));
00397     result += writeData(context, "\"", 1);
00398     context->output_count++;
00399     return result;
00400 }
00401 
00402 /**
00403  * Write arbitrary block program data to the result
00404  * @param context
00405  * @param data
00406  * @param len
00407  * @return
00408  */
00409 size_t SCPI_ResultArbitraryBlock(scpi_t * context, const char * data, size_t len) {
00410     size_t result = 0;
00411     char block_header[12];
00412     size_t header_len;
00413     block_header[0] = '#';
00414     SCPI_LongToStr(len, block_header + 2, 10, 10);
00415 
00416     header_len = strlen(block_header + 2);
00417     block_header[1] = (char)(header_len + '0');
00418 
00419     result += writeData(context, block_header, header_len + 2);
00420     result += writeData(context, data, len);
00421 
00422     context->output_count++;
00423     return result;
00424 }
00425 
00426 /**
00427  * Write boolean value to the result
00428  * @param context
00429  * @param val
00430  * @return
00431  */
00432 size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val) {
00433     return SCPI_ResultIntBase(context, val ? 1 : 0, 10);
00434 }
00435 
00436 /* parsing parameters */
00437 
00438 /**
00439  * Invalidate token
00440  * @param token
00441  * @param ptr
00442  */
00443 static void invalidateToken(scpi_token_t * token, char * ptr) {
00444     token->len = 0;
00445     token->ptr = ptr;
00446     token->type = SCPI_TOKEN_UNKNOWN;
00447 }
00448 
00449 /**
00450  * Get one parameter from command line
00451  * @param context
00452  * @param parameter
00453  * @param mandatory
00454  * @return
00455  */
00456 scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory) {
00457     lex_state_t * state;
00458 
00459     if (!parameter) {
00460         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00461         return FALSE;
00462     }
00463 
00464     invalidateToken(parameter, NULL);
00465 
00466     state = &context->param_list.lex_state;
00467 
00468     if (state->pos >= (state->buffer + state->len)) {
00469         if (mandatory) {
00470             SCPI_ErrorPush(context, SCPI_ERROR_MISSING_PARAMETER);
00471         } else {
00472             parameter->type = SCPI_TOKEN_PROGRAM_MNEMONIC; // TODO: select something different
00473         }
00474         return FALSE;
00475     }
00476     if (context->input_count != 0) {
00477         scpiLex_Comma(state, parameter);
00478         if (parameter->type != SCPI_TOKEN_COMMA) {
00479             invalidateToken(parameter, NULL);
00480             SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SEPARATOR);
00481             return FALSE;
00482         }
00483     }
00484 
00485     context->input_count++;
00486 
00487     scpiParser_parseProgramData(&context->param_list.lex_state, parameter);
00488 
00489     switch (parameter->type) {
00490         case SCPI_TOKEN_HEXNUM:
00491         case SCPI_TOKEN_OCTNUM:
00492         case SCPI_TOKEN_BINNUM:
00493         case SCPI_TOKEN_PROGRAM_MNEMONIC:
00494         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA:
00495         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX:
00496         case SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA:
00497         case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA:
00498         case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA:
00499         case SCPI_TOKEN_PROGRAM_EXPRESSION:
00500             return TRUE;
00501         default:
00502             invalidateToken(parameter, NULL);
00503             SCPI_ErrorPush(context, SCPI_ERROR_INVALID_STRING_DATA);
00504             return FALSE;
00505     }
00506 }
00507 
00508 /**
00509  * Detect if parameter is number
00510  * @param parameter
00511  * @param suffixAllowed
00512  * @return
00513  */
00514 scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed) {
00515     switch (parameter->type) {
00516         case SCPI_TOKEN_HEXNUM:
00517         case SCPI_TOKEN_OCTNUM:
00518         case SCPI_TOKEN_BINNUM:
00519         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA:
00520             return TRUE;
00521         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX:
00522             return suffixAllowed;
00523         default:
00524             return FALSE;
00525     }
00526 }
00527 
00528 /**
00529  * Convert parameter to integer
00530  * @param context
00531  * @param parameter
00532  * @param value result
00533  * @return TRUE if succesful
00534  */
00535 scpi_bool_t SCPI_ParamToInt(scpi_t * context, scpi_parameter_t * parameter, int32_t * value) {
00536 
00537     if (!value) {
00538         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00539         return FALSE;
00540     }
00541 
00542     switch (parameter->type) {
00543         case SCPI_TOKEN_HEXNUM:
00544             return strToLong(parameter->ptr, value, 16) > 0 ? TRUE : FALSE;
00545         case SCPI_TOKEN_OCTNUM:
00546             return strToLong(parameter->ptr, value, 8) > 0 ? TRUE : FALSE;
00547         case SCPI_TOKEN_BINNUM:
00548             return strToLong(parameter->ptr, value, 2) > 0 ? TRUE : FALSE;
00549         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA:
00550         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX:
00551             return strToLong(parameter->ptr, value, 10) > 0 ? TRUE : FALSE;
00552     }
00553     return FALSE;
00554 }
00555 
00556 /**
00557  * Convert parameter to double
00558  * @param context
00559  * @param parameter
00560  * @param value result
00561  * @return TRUE if succesful
00562  */
00563 scpi_bool_t SCPI_ParamToDouble(scpi_t * context, scpi_parameter_t * parameter, double * value) {
00564     scpi_bool_t result = FALSE;
00565     int32_t valint;
00566 
00567     if (!value) {
00568         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00569         return FALSE;
00570     }
00571 
00572     switch (parameter->type) {
00573         case SCPI_TOKEN_HEXNUM:
00574         case SCPI_TOKEN_OCTNUM:
00575         case SCPI_TOKEN_BINNUM:
00576             result = SCPI_ParamToInt(context, parameter, &valint);
00577             *value = valint;
00578             break;
00579         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA:
00580         case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX:
00581             result = strToDouble(parameter->ptr, value) > 0 ? TRUE : FALSE;
00582             break;
00583     }
00584     return result;
00585 }
00586 
00587 /**
00588  * Read floating point parameter
00589  * @param context
00590  * @param value
00591  * @param mandatory
00592  * @return
00593  */
00594 scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) {
00595     scpi_bool_t result;
00596     scpi_parameter_t param;
00597 
00598     if (!value) {
00599         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00600         return FALSE;
00601     }
00602 
00603     result = SCPI_Parameter(context, &param, mandatory);
00604     if (result) {
00605         if (SCPI_ParamIsNumber(&param, FALSE)) {
00606             SCPI_ParamToDouble(context, &param, value);
00607         } else if (SCPI_ParamIsNumber(&param, TRUE)) {
00608             SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED);
00609             result = FALSE;
00610         } else {
00611             SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR);
00612             result = FALSE;
00613         }
00614     }
00615     return result;
00616 }
00617 
00618 /**
00619  * Read integer parameter
00620  * @param context
00621  * @param value
00622  * @param mandatory
00623  * @return
00624  */
00625 scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory) {
00626     scpi_bool_t result;
00627     scpi_parameter_t param;
00628 
00629     if (!value) {
00630         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00631         return FALSE;
00632     }
00633 
00634     result = SCPI_Parameter(context, &param, mandatory);
00635     if (result) {
00636         if (SCPI_ParamIsNumber(&param, FALSE)) {
00637             SCPI_ParamToInt(context, &param, value);
00638         } else if (SCPI_ParamIsNumber(&param, TRUE)) {
00639             SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED);
00640             result = FALSE;
00641         } else {
00642             SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR);
00643             result = FALSE;
00644         }
00645     }
00646     return result;
00647 }
00648 
00649 /**
00650  * Read character parameter
00651  * @param context
00652  * @param value
00653  * @param len
00654  * @param mandatory
00655  * @return
00656  */
00657 scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) {
00658     scpi_bool_t result;
00659     scpi_parameter_t param;
00660 
00661     if (!value || !len) {
00662         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00663         return FALSE;
00664     }
00665 
00666     result = SCPI_Parameter(context, &param, mandatory);
00667     if (result) {
00668         *value = param.ptr;
00669         *len = param.len;
00670 
00671         // TODO: return also parameter type (ProgramMnemonic, ArbitraryBlockProgramData, SingleQuoteProgramData, DoubleQuoteProgramData
00672     }
00673 
00674     return result;
00675 }
00676 
00677 /**
00678  * Get arbitrary block program data and returns pointer to data
00679  * @param context
00680  * @param value result pointer to data
00681  * @param len result length of data
00682  * @param mandatory
00683  * @return
00684  */
00685 scpi_bool_t SCPI_ParamArbitraryBlock(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) {
00686     scpi_bool_t result;
00687     scpi_parameter_t param;
00688 
00689     if (!value || !len) {
00690         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00691         return FALSE;
00692     }
00693 
00694     result = SCPI_Parameter(context, &param, mandatory);
00695     if (result) {
00696         if (param.type == SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA) {
00697             *value = param.ptr;
00698             *len = param.len;
00699         } else {
00700             SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR);
00701             result = FALSE;
00702         }
00703     }
00704 
00705     return result;
00706 }
00707 
00708 scpi_bool_t SCPI_ParamCopyText(scpi_t * context, char * buffer, size_t buffer_len, size_t * copy_len, scpi_bool_t mandatory) {
00709     scpi_bool_t result;
00710     scpi_parameter_t param;
00711     size_t i_from;
00712     size_t i_to;
00713     char quote;
00714 
00715     if (!buffer || !copy_len) {
00716         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00717         return FALSE;
00718     }
00719 
00720     result = SCPI_Parameter(context, &param, mandatory);
00721     if (result) {
00722 
00723         switch (param.type) {
00724             case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA:
00725             case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA:
00726                 quote = param.type == SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA ? '\'' : '"';
00727                 for (i_from = 0, i_to = 0; i_from < (size_t) param.len; i_from++) {
00728                     if (i_from >= buffer_len) {
00729                         break;
00730                     }
00731                     buffer[i_to] = param.ptr[i_from];
00732                     i_to++;
00733                     if (param.ptr[i_from] == quote) {
00734                         i_from++;
00735                     }
00736                 }
00737                 break;
00738             default:
00739                 SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR);
00740                 result = FALSE;
00741         }
00742     }
00743 
00744     return result;
00745 }
00746 
00747 /**
00748  * Convert parameter to choice
00749  * @param context
00750  * @param parameter - should be PROGRAM_MNEMONIC
00751  * @param options - NULL terminated list of choices
00752  * @param value - index to options
00753  * @return
00754  */
00755 scpi_bool_t SCPI_ParamToChoice(scpi_t * context, scpi_parameter_t * parameter, const scpi_choice_def_t * options, int32_t * value) {
00756     size_t res;
00757     scpi_bool_t result = FALSE;
00758 
00759     if (!options || !value) {
00760         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00761         return FALSE;
00762     }
00763 
00764     if (parameter->type == SCPI_TOKEN_PROGRAM_MNEMONIC) {
00765         for (res = 0; options[res].name; ++res) {
00766             if (matchPattern(options[res].name, strlen(options[res].name), parameter->ptr, parameter->len, NULL)) {
00767                 *value = options[res].tag;
00768                 result = TRUE;
00769                 break;
00770             }
00771         }
00772 
00773         if (!result) {
00774             SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE);
00775         }
00776     } else {
00777         SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR);
00778     }
00779 
00780     return result;
00781 }
00782 
00783 /**
00784  * Find tag in choices and returns its first textual representation
00785  * @param options specifications of choices numbers (patterns)
00786  * @param tag numerical representatio of choice
00787  * @param text result text
00788  * @return TRUE if succesfule, else FALSE
00789  */
00790 scpi_bool_t SCPI_ChoiceToName(const scpi_choice_def_t * options, int32_t tag, const char ** text) {
00791     int i;
00792 
00793     for (i = 0; options[i].name != NULL; i++) {
00794         if (options[i].tag == tag) {
00795             *text = options[i].name;
00796             return TRUE;
00797         }
00798     }
00799 
00800     return FALSE;
00801 }
00802 
00803 /**
00804  * Read BOOL parameter (0,1,ON,OFF)
00805  * @param context
00806  * @param value
00807  * @param mandatory
00808  * @return
00809  */
00810 scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) {
00811     scpi_bool_t result;
00812     scpi_parameter_t param;
00813     int32_t intval;
00814 
00815     scpi_choice_def_t bool_options[] = {
00816         {"OFF", 0},
00817         {"ON", 1},
00818         SCPI_CHOICE_LIST_END /* termination of option list */
00819     };
00820 
00821     if (!value) {
00822         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00823         return FALSE;
00824     }
00825 
00826     result = SCPI_Parameter(context, &param, mandatory);
00827 
00828     if (result) {
00829         if (param.type == SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA) {
00830             SCPI_ParamToInt(context, &param, &intval);
00831             *value = intval ? TRUE : FALSE;
00832         } else {
00833             result = SCPI_ParamToChoice(context, &param, bool_options, &intval);
00834             if (result) {
00835                 *value = intval ? TRUE : FALSE;
00836             }
00837         }
00838     }
00839 
00840     return result;
00841 }
00842 
00843 /**
00844  * Read value from list of options
00845  * @param context
00846  * @param options
00847  * @param value
00848  * @param mandatory
00849  * @return
00850  */
00851 scpi_bool_t SCPI_ParamChoice(scpi_t * context, const scpi_choice_def_t * options, int32_t * value, scpi_bool_t mandatory) {
00852     scpi_bool_t result;
00853     scpi_parameter_t param;
00854 
00855     if (!options || !value) {
00856         SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR);
00857         return FALSE;
00858     }
00859 
00860     result = SCPI_Parameter(context, &param, mandatory);
00861     if (result) {
00862         result = SCPI_ParamToChoice(context, &param, options, value);
00863     }
00864 
00865     return result;
00866 }
00867 
00868 /**
00869  * Parse one parameter and detect type
00870  * @param state
00871  * @param token
00872  * @return
00873  */
00874 int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) {
00875     scpi_token_t tmp;
00876     int result = 0;
00877     int wsLen;
00878     int suffixLen;
00879     int realLen = 0;
00880     realLen += scpiLex_WhiteSpace(state, &tmp);
00881 
00882     if (result == 0) result = scpiLex_NondecimalNumericData(state, token);
00883     if (result == 0) result = scpiLex_CharacterProgramData(state, token);
00884     if (result == 0) {
00885         result = scpiLex_DecimalNumericProgramData(state, token);
00886         if (result != 0) {
00887             wsLen = scpiLex_WhiteSpace(state, &tmp);
00888             suffixLen = scpiLex_SuffixProgramData(state, &tmp);
00889             if (suffixLen > 0) {
00890                 token->len += wsLen + suffixLen;
00891                 token->type = SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX;
00892                 result = token->len;
00893             }
00894         }
00895     }
00896 
00897     if (result == 0) result = scpiLex_StringProgramData(state, token);
00898     if (result == 0) result = scpiLex_ArbitraryBlockProgramData(state, token);
00899     if (result == 0) result = scpiLex_ProgramExpression(state, token);
00900 
00901     realLen += scpiLex_WhiteSpace(state, &tmp);
00902 
00903     return result + realLen;
00904 }
00905 
00906 /**
00907  * Skip all parameters to correctly detect end of command line.
00908  * @param state
00909  * @param token
00910  * @param numberOfParameters
00911  * @return
00912  */
00913 int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) {
00914 
00915     int result;
00916     scpi_token_t tmp;
00917     int paramCount = 0;
00918 
00919     token->len = -1;
00920     token->type = SCPI_TOKEN_ALL_PROGRAM_DATA;
00921     token->ptr = state->pos;
00922 
00923 
00924     for (result = 1; result != 0; result = scpiLex_Comma(state, &tmp)) {
00925         token->len += result;
00926 
00927         if (result == 0) {
00928             token->type = SCPI_TOKEN_UNKNOWN;
00929             token->len = 0;
00930             paramCount = -1;
00931             break;
00932         }
00933 
00934         result = scpiParser_parseProgramData(state, &tmp);
00935         if (tmp.type != SCPI_TOKEN_UNKNOWN) {
00936             token->len += result;
00937         } else {
00938             token->type = SCPI_TOKEN_UNKNOWN;
00939             token->len = 0;
00940             paramCount = -1;
00941             break;
00942         }
00943         paramCount++;
00944     }
00945 
00946     if (token->len == -1) {
00947         token->len = 0;
00948     }
00949 
00950     if (numberOfParameters != NULL) {
00951         *numberOfParameters = paramCount;
00952     }
00953     return token->len;
00954 }
00955 
00956 /**
00957  * Skip complete command line - program header and parameters
00958  * @param state
00959  * @param buffer
00960  * @param len
00961  * @return
00962  */
00963 int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, char * buffer, int len) {
00964     lex_state_t lex_state;
00965     scpi_token_t tmp;
00966     int result = 0;
00967 
00968     lex_state.buffer = lex_state.pos = buffer;
00969     lex_state.len = len;
00970     state->numberOfParameters = 0;
00971 
00972     /* ignore whitespace at the begginig */
00973     scpiLex_WhiteSpace(&lex_state, &tmp);
00974 
00975     if (scpiLex_ProgramHeader(&lex_state, &state->programHeader) >= 0) {
00976         if (scpiLex_WhiteSpace(&lex_state, &tmp) > 0) {
00977             scpiParser_parseAllProgramData(&lex_state, &state->programData, &state->numberOfParameters);
00978         } else {
00979             invalidateToken(&state->programData, lex_state.pos);
00980         }
00981     } else {
00982         invalidateToken(&state->programHeader, lex_state.buffer);
00983         invalidateToken(&state->programData, lex_state.buffer);
00984     }
00985 
00986     if (result == 0) result = scpiLex_NewLine(&lex_state, &tmp);
00987     if (result == 0) result = scpiLex_Semicolon(&lex_state, &tmp);
00988 
00989     if (!scpiLex_IsEos(&lex_state) && (result == 0)) {
00990         lex_state.pos++;
00991 
00992         state->programHeader.len = 1;
00993         state->programHeader.type = SCPI_TOKEN_INVALID;
00994 
00995         invalidateToken(&state->programData, lex_state.buffer);
00996     }
00997 
00998     if (SCPI_TOKEN_SEMICOLON == tmp.type) {
00999         state->termination = SCPI_MESSAGE_TERMINATION_SEMICOLON;
01000     } else if (SCPI_TOKEN_NL == tmp.type) {
01001         state->termination = SCPI_MESSAGE_TERMINATION_NL;
01002     } else {
01003         state->termination = SCPI_MESSAGE_TERMINATION_NONE;
01004     }
01005 
01006     return lex_state.pos - lex_state.buffer;
01007 }
01008 
01009 /**
01010  * Check current command
01011  *  - suitable for one handle to multiple commands
01012  * @param context
01013  * @param cmd
01014  * @return
01015  */
01016 scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd) {
01017     const char * pattern;
01018 
01019     if (!context->param_list.cmd) {
01020         return FALSE;
01021     }
01022 
01023     pattern = context->param_list.cmd->pattern;
01024     return matchCommand (pattern, cmd, strlen (cmd), NULL, 0);
01025 }
01026 
01027 /**
01028  * Return the .tag field of the matching scpi_command_t
01029  * @param context
01030  * @return
01031  */
01032 int32_t SCPI_CmdTag(scpi_t * context) {
01033     if (context->param_list.cmd) {
01034         return context->param_list.cmd->tag;
01035     } else {
01036         return 0;
01037     }
01038 }
01039 
01040 scpi_bool_t SCPI_Match(const char * pattern, const char * value, size_t len) {
01041     return matchCommand (pattern, value, len, NULL, 0);
01042 }
01043 
01044 scpi_bool_t SCPI_CommandNumbers(scpi_t * context, int32_t * numbers, size_t len) {
01045     return matchCommand (context->param_list.cmd->pattern,  context->param_list.cmd_raw.data, context->param_list.cmd_raw.length, numbers, len);
01046 }