ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers test_env.cpp Source File

test_env.cpp

00001 /*
00002  * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include <ctype.h>
00019 #include <cstdio>
00020 #include <string.h>
00021 #include "mbed.h"
00022 #include "greentea-client/test_env.h"
00023 #include "greentea-client/greentea_serial.h"
00024 #include "greentea-client/greentea_metrics.h"
00025 
00026 
00027 /**
00028  *   Generic test suite transport protocol keys
00029  */
00030 const char* GREENTEA_TEST_ENV_END = "end";
00031 const char* GREENTEA_TEST_ENV_EXIT = "__exit";
00032 const char* GREENTEA_TEST_ENV_SYNC = "__sync";
00033 const char* GREENTEA_TEST_ENV_TIMEOUT = "__timeout";
00034 const char* GREENTEA_TEST_ENV_HOST_TEST_NAME = "__host_test_name";
00035 const char* GREENTEA_TEST_ENV_HOST_TEST_VERSION = "__version";
00036 
00037 /**
00038  *   Test suite success code strings
00039  */
00040 const char* GREENTEA_TEST_ENV_SUCCESS = "success";
00041 const char* GREENTEA_TEST_ENV_FAILURE = "failure";
00042 
00043 /**
00044  *   Test case transport protocol start/finish keys
00045  */
00046 const char* GREENTEA_TEST_ENV_TESTCASE_NAME = "__testcase_name";
00047 const char* GREENTEA_TEST_ENV_TESTCASE_COUNT = "__testcase_count";
00048 const char* GREENTEA_TEST_ENV_TESTCASE_START = "__testcase_start";
00049 const char* GREENTEA_TEST_ENV_TESTCASE_FINISH = "__testcase_finish";
00050 const char* GREENTEA_TEST_ENV_TESTCASE_SUMMARY = "__testcase_summary";
00051 // Code Coverage (LCOV)  transport protocol keys
00052 const char* GREENTEA_TEST_ENV_LCOV_START = "__coverage_start";
00053 
00054 /**
00055  *   Auxilary functions
00056  */
00057 static void greentea_notify_timeout(const int);
00058 static void greentea_notify_hosttest(const char *);
00059 static void greentea_notify_completion(const int);
00060 static void greentea_notify_version();
00061 static void greentea_write_string(const char *str);
00062 
00063 /** \brief Handshake with host and send setup data (timeout and host test name)
00064  *  \details This function will send preamble to master.
00065  *           After host test name is received master will invoke host test script
00066  *           and add hos test's callback handlers to main event loop
00067  *           This function is blocking.
00068  */
00069 void GREENTEA_SETUP(const int timeout, const char *host_test_name) {
00070     greentea_metrics_setup();
00071     // Key-value protocol handshake function. Waits for {{__sync;...}} message
00072     // Sync preamble: "{{__sync;0dad4a9d-59a3-4aec-810d-d5fb09d852c1}}"
00073     // Example value of sync_uuid == "0dad4a9d-59a3-4aec-810d-d5fb09d852c1"
00074     char _key[8] = {0};
00075     char _value[48] = {0};
00076     while (1) {
00077         greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00078         greentea_write_string("mbedmbedmbedmbedmbedmbedmbedmbed\r\n");
00079         if (strcmp(_key, GREENTEA_TEST_ENV_SYNC) == 0) {
00080             // Found correct __sunc message
00081             greentea_send_kv(_key, _value);
00082             break;
00083         }
00084     }
00085 
00086     greentea_notify_version();
00087     greentea_notify_timeout(timeout);
00088     greentea_notify_hosttest(host_test_name);
00089 }
00090 
00091 /** \brief Notify host (__exit message) side that test suite execution was complete
00092  *  \result Test suite result
00093  *  \details If __exit is not received by host side we will assume TIMEOUT
00094  */
00095 void GREENTEA_TESTSUITE_RESULT(const int result) {
00096     greentea_notify_completion(result);
00097 }
00098 
00099 /**
00100  *  Test Case support
00101  */
00102 
00103 /** \brief Notify host side that test case started
00104  *  \details test_case_name Test case name
00105  */
00106 void GREENTEA_TESTCASE_START(const char *test_case_name) {
00107     greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_START, test_case_name);
00108 }
00109 
00110 /** \brief Notify host side that test case finished
00111  *  \details test_case_name Test case name
00112  *  \details result Test case result (0 -OK, non zero...)
00113  */
00114 void GREENTEA_TESTCASE_FINISH(const char *test_case_name, const size_t passes, const size_t failed) {
00115     greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_FINISH, test_case_name, passes, failed);
00116 }
00117 
00118 /**
00119  *****************************************************************************
00120  *  Auxilary functions and key-value protocol support
00121  *****************************************************************************
00122  */
00123 
00124 
00125 /**
00126  *****************************************************************************
00127  *  LCOV support
00128  *****************************************************************************
00129  */
00130 #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE
00131 extern "C" void __gcov_flush(void);
00132 extern bool coverage_report;
00133 
00134 /**
00135  * \brief Send code coverage (gcov/LCOV) notification to master
00136  *
00137  *        Generates preamble of message sent to notify host about code coverage data dump.
00138  *
00139  *        This function is used by mbedOS software
00140  *        (see: mbed-drivers/source/retarget.cpp file) to generate code coverage
00141  *        messages to host. When code coverage feature is turned on slave will
00142  *        print-out code coverage data in form of key-value protocol.
00143  *        Message with code coverage data will contain message name, path to code
00144  *        coverage output file host will touch and fill with code coverage binary
00145  *        payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X").
00146  *
00147  * \param path to file with code coverage payload (set by gcov instrumentation)
00148  *
00149  */
00150 void greentea_notify_coverage_start(const char *path) {
00151     printf("{{%s;%s;", GREENTEA_TEST_ENV_LCOV_START, path);
00152 }
00153 
00154 /**
00155  *  \brief Sufix for code coverage message to master (closing statement)
00156  *
00157  *         This function is used by mbedOS software
00158  *         (see: mbed-drivers/source/retarget.cpp file) to generate code coverage
00159  *         messages to host. When code coverage feature is turned on slave will
00160  *         print-out code coverage data in form of key-value protocol.
00161  *         Message with code coverage data will contain message name, path to code
00162  *         coverage output file host will touch and fill with code coverage binary
00163  *         payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X").
00164  *
00165  *         Companion function greentea_notify_coverage_start() defines code coverage message structure
00166  *
00167  */
00168 void greentea_notify_coverage_end() {
00169     printf("}}" NL);
00170 }
00171 
00172 #endif
00173 
00174 /**
00175  *****************************************************************************
00176  *  Key-value protocol support
00177  *****************************************************************************
00178  */
00179 
00180 /**
00181  * \brief Write the preamble characters to the serial port
00182  *
00183  *        This function writes the preamble "{{" which is required
00184  *        for key-value comunication between the target and the host.
00185  *        This uses a Rawserial object, greentea_serial, which provides
00186  *        a direct interface to the USBTX and USBRX serial pins and allows
00187  *        the direct writing of characters using the putc() method.
00188  *        This suite of functions are provided to allow for serial communication
00189  *        to the host from within a thread/ISR.
00190  *
00191  */
00192 inline void greentea_write_preamble()
00193 {
00194     greentea_serial->putc('{');
00195     greentea_serial->putc('{');
00196 }
00197  
00198 /**
00199  * \brief Write the postamble characters to the serial port
00200  *
00201  *        This function writes the postamble "{{\n" which is required
00202  *        for key-value comunication between the target and the host.
00203  *        This uses a Rawserial object, greentea_serial, which provides
00204  *        a direct interface to the USBTX and USBRX serial pins and allows
00205  *        the direct writing of characters using the putc() method. 
00206  *        This suite of functions are provided to allow for serial communication
00207  *        to the host from within a thread/ISR.
00208  *
00209  */
00210 inline void greentea_write_postamble()
00211 {
00212     greentea_serial->putc('}');
00213     greentea_serial->putc('}');
00214     greentea_serial->putc('\r');
00215     greentea_serial->putc('\n');
00216 }
00217 
00218 /**
00219  * \brief Write a string to the serial port
00220  *
00221  *        This function writes a '\0' terminated string from the target
00222  *        to the host. It writes directly to the serial port using the
00223  *        greentea_serial, Rawserial object.
00224  *
00225  * \param str - string value
00226  *
00227  */
00228 inline void greentea_write_string(const char *str)
00229 {
00230     while (*str != '\0') {
00231         greentea_serial->putc(*str);
00232         str ++;
00233     }
00234 }
00235 
00236 
00237 /**
00238  * \brief Write an int to the serial port
00239  *
00240  *        This function writes an integer value from the target
00241  *        to the host. The integer value is converted to a string and 
00242  *        and then written character by character directly to the serial 
00243  *        port using the greentea_serial, Rawserial object.
00244  *        sprintf() is used to convert the int to a string. Sprintf if
00245  *        inherently thread safe so can be used.
00246  *
00247  * \param val - integer value
00248  *
00249  */
00250 #define MAX_INT_STRING_LEN 15
00251 inline void greentea_write_int(const int val)
00252 {
00253     char intval[MAX_INT_STRING_LEN];
00254     unsigned int i = 0;
00255     sprintf(intval, "%d", val);
00256     while (intval[i] != '\0') {
00257         greentea_serial->putc(intval[i]);
00258         i++;
00259     }
00260 }
00261 
00262 /**
00263  * \brief Encapsulate and send key-value message from DUT to host
00264  *
00265  *        This function uses underlying functions to write directly
00266  *        to the serial port, (USBTX). This allows KVs to be used
00267  *        from within interrupt context.
00268  *
00269  * \param key Message key (message/event name)
00270  * \param value Message payload, string value
00271  *
00272  */
00273 void greentea_send_kv(const char *key, const char *val) {
00274     if (key && val) {
00275         greentea_write_preamble();
00276         greentea_write_string(key);
00277         greentea_serial->putc(';');
00278         greentea_write_string(val);
00279         greentea_write_postamble();
00280     }
00281 }
00282 
00283 /**
00284  * \brief Encapsulate and send key-value message from DUT to host
00285  *
00286  *        This function uses underlying functions to write directly
00287  *        to the serial port, (USBTX). This allows KVs to be used
00288  *        from within interrupt context.
00289  *        Last value is an integer to avoid integer to string conversion
00290  *        made by the user.
00291  *
00292  * \param key Message key (message/event name)
00293  * \param value Message payload, integer value
00294  *
00295  */
00296 void greentea_send_kv(const char *key, const int val) {
00297     if (key) {
00298         greentea_write_preamble();
00299         greentea_write_string(key);
00300         greentea_serial->putc(';');
00301         greentea_write_int(val);
00302         greentea_write_postamble();
00303     }
00304 }
00305  
00306 /**
00307  * \brief Encapsulate and send key-value-value message from DUT to host
00308  *
00309  *        This function uses underlying functions to write directly
00310  *        to the serial port, (USBTX). This allows KVs to be used
00311  *        from within interrupt context.
00312  *        Last value is an integer to avoid integer to string conversion
00313  *        made by the user.
00314  *
00315  * \param key Message key (message/event name)
00316  * \param value Message payload, string value
00317  * \param result Send additional integer formatted data
00318  *
00319  */
00320 void greentea_send_kv(const char *key, const char *val, const int result) {
00321     if (key) {
00322         greentea_write_preamble();
00323         greentea_write_string(key);
00324         greentea_serial->putc(';');
00325         greentea_write_string(val);
00326         greentea_serial->putc(';');
00327         greentea_write_int(result);
00328         greentea_write_postamble();
00329 
00330     }
00331 }
00332 
00333 /**
00334  * \brief Encapsulate and send key-value-value-value message from DUT to host
00335  *
00336  *        This function uses underlying functions to write directly
00337  *        to the serial port, (USBTX). This allows KVs to be used
00338  *        from within interrupt context.
00339  *        Last 2 values are integers to avoid integer to string conversion
00340  *        made by the user.
00341  *
00342  *        Names of the parameters: this function is used to send test case
00343  *        name with number of passes and failures to host. But it can be used
00344  *        to send any key-value-value-value (string-string-integer-integer)
00345  *        set to host.
00346  *
00347  * \param key Message key (message/event name)
00348  * \param value Message payload, string value
00349  * \param passes Send additional integer formatted data
00350  * \param failures Send additional integer formatted data
00351  *
00352  */
00353 void greentea_send_kv(const char *key, const char *val, const int passes, const int failures) {
00354     if (key) {
00355         greentea_write_preamble();
00356         greentea_write_string(key);
00357         greentea_serial->putc(';');
00358         greentea_write_string(val);
00359         greentea_serial->putc(';');
00360         greentea_write_int(passes);
00361         greentea_serial->putc(';');
00362         greentea_write_int(failures);
00363         greentea_write_postamble();
00364     }
00365 }
00366 
00367 /**
00368  * \brief Encapsulate and send key-value-value message from DUT to host
00369  *
00370  *        This function uses underlying functions to write directly 
00371  *        to the serial port, (USBTX). This allows key-value-value to be used 
00372  *        from within interrupt context.
00373  *        Both values are integers to avoid integer to string conversion 
00374  *        made by the user.
00375  *
00376  *        Names of the parameters: this function is used to send number
00377  *        of passes and failures to host. But it can be used to send any
00378  *        key-value-value (string-integer-integer) message to host.
00379  *
00380  * \param key Message key (message/event name)
00381  * \param value Message payload, integer value
00382  * \param passes Send additional integer formatted data
00383  * \param failures Send additional integer formatted data
00384  *
00385  */
00386 void greentea_send_kv(const char *key, const int passes, const int failures) {
00387     if (key) {
00388         greentea_write_preamble();
00389         greentea_write_string(key);
00390         greentea_serial->putc(';');
00391         greentea_write_int(passes);
00392         greentea_serial->putc(';');
00393         greentea_write_int(failures);
00394         greentea_write_postamble();
00395     }
00396 }
00397 
00398 /**
00399  * \brief Send message with timeout to master in seconds
00400  *
00401  *        GREENTEA_TEST_ENV_TIMEOUT message is part of preamble
00402  *        sent from DUT to host during synchronisation (beginning of test
00403  *        suite execution).
00404  *
00405  *        Notification about total test suite timeout. Timeout is measured
00406  *        from the moment of GREENTEA_TEST_ENV_TIMEOUT reception by host.
00407  *        If timeout is reached host (and host test) will be stopped and
00408  *        control will return to Greentea.
00409  *
00410  * \param timeout Test suite timeout in seconds
00411  *
00412  */
00413 static void greentea_notify_timeout(const int timeout) {
00414     greentea_send_kv(GREENTEA_TEST_ENV_TIMEOUT, timeout);
00415 }
00416 
00417 /**
00418  * \brief Send host test name to master
00419  *
00420  *        GREENTEA_TEST_ENV_HOST_TEST_NAME message is part of preamble
00421  *        sent from DUT to host during synchronisation (beginning of test
00422  *        suite execution).
00423  *
00424  *        Host test Python script implements host side callbacks
00425  *        for key-value events sent from DUT to host. Host test's
00426  *        callbacks are registered after GREENTEA_TEST_ENV_HOST_TEST_NAME
00427  *        message reaches host.
00428  *
00429  * \param host_test_name Host test name, host test will be loaded by mbedhtrun
00430  */
00431 static void greentea_notify_hosttest(const char *host_test_name) {
00432     greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_NAME, host_test_name);
00433 }
00434 
00435 /**
00436  * \brief Send to master information that test suite finished its execution
00437  *
00438  *        GREENTEA_TEST_ENV_END and GREENTEA_TEST_ENV_EXIT messages
00439  *        are sent just before test suite execution finishes (noting
00440  *        else to do). You can place it just before you return from your
00441  *        main() function.
00442  *
00443  *        Code coverage: If MEBD_CFG_DEBUG_OPTIONS_COVERAGE is set in the
00444  *        project via build configuration function will output series
00445  *        of code coverage messages GREENTEA_TEST_ENV_LCOV_START with code
00446  *        coverage binary data. This data is captured by Greentea and can
00447  *        be used to generate LCOV reports.
00448  *
00449  * \param result Test suite result from DUT (0 - FAIl, !0 - SUCCESS)
00450  *
00451  */
00452 static void greentea_notify_completion(const int result) {
00453     const char *val = result ? GREENTEA_TEST_ENV_SUCCESS : GREENTEA_TEST_ENV_FAILURE;
00454 #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE
00455     coverage_report = true;
00456     __gcov_flush();
00457     coverage_report = false;
00458 #endif
00459     greentea_metrics_report();
00460     greentea_send_kv(GREENTEA_TEST_ENV_END, val);
00461     greentea_send_kv(GREENTEA_TEST_ENV_EXIT, 0);
00462 }
00463 
00464 /**
00465  * \brief Send to master greentea-client version
00466  */
00467 static void greentea_notify_version() {
00468     greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_VERSION, MBED_GREENTEA_CLIENT_VERSION_STRING);
00469 }
00470 
00471 /**
00472  *****************************************************************************
00473  *  Parse engine for KV values which replaces scanf
00474  *****************************************************************************
00475  *
00476  *  Example usage:
00477  *
00478  *  char key[10];
00479  *  char value[48];
00480  *
00481  *  greentea_parse_kv(key, value, 10, 48);
00482  *  greentea_parse_kv(key, value, 10, 48);
00483  *
00484  */
00485 
00486 
00487 static int gettok(char *, const int);
00488 static int getNextToken(char *, const int);
00489 static int HandleKV(char *,  char *,  const int,  const int);
00490 static int isstring(int);
00491 static int _get_char();
00492 
00493 /**
00494  *  \brief Current token of key-value protocol's tokenizer
00495  */
00496 static int CurTok = 0;
00497 
00498 /**
00499  *  \enum Token enumeration for key-value protocol tokenizer
00500  *
00501  *        This enum is used by key-value protocol tokenizer
00502  *        to detect parts of protocol in stream.
00503  *
00504  *        tok_eof       ::= EOF (end of file)
00505  *        tok_open      ::= "{{"
00506  *        tok_close     ::= "}}"
00507  *        tok_semicolon ::= ";"
00508  *        tok_string    ::= [a-zA-Z0-9_-!@#$%^&*()]+    // See isstring() function
00509  *
00510  */
00511 enum Token {
00512     tok_eof = -1,
00513     tok_open = -2,
00514     tok_close = -3,
00515     tok_semicolon = -4,
00516     tok_string = -5
00517 };
00518 
00519 /**
00520  * \brief Read character from stream of data
00521  *
00522  *        Closure for default "get character" function.
00523  *        This function is used to read characters from the stream
00524  *        (default is serial port RX). Key-value protocol tokenizer
00525  *        will build stream of tokes used by key-value protocol to
00526  *        detect valid messages.
00527  *
00528  *        If EOF is received parser finishes parsing and stops. In
00529  *        situation where we have serial port stream of data parsing
00530  *        goes forever.
00531  *
00532  * \return Next character from the stream or EOF if stream has ended.
00533  *
00534  */
00535 static int _get_char() {
00536     return greentea_serial->getc();
00537 }
00538 
00539 /**
00540  * \brief parse input string for key-value pairs: {{key;value}}
00541  *        This function should replace scanf() used to
00542  *        check for incoming messages from master. All data
00543  *        parsed and rejected is discarded.
00544  *
00545  * \param out_key Ouput data with key
00546  * \param out_value Ouput data with value
00547  * \param out_key_size out_key total size
00548  * \param out_value_size out_value total data
00549  *
00550  * success != 0 when key-value pair was found
00551  * success == 0 when end of the stream was found
00552  *
00553  */
00554 int greentea_parse_kv(char *out_key,
00555                       char *out_value,
00556                       const int out_key_size,
00557                       const int out_value_size) {
00558     getNextToken(0, 0);
00559     while (1) {
00560         switch (CurTok) {
00561         case tok_eof:
00562             return 0;
00563 
00564         case tok_open:
00565             if (HandleKV(out_key, out_value, out_key_size, out_value_size)) {
00566                 // We've found {{ KEY ; VALUE }} expression
00567                 return 1;
00568             }
00569             break;
00570 
00571         default:
00572             // Load next token and pray...
00573             getNextToken(0, 0);
00574             break;
00575         }
00576     }
00577 }
00578 
00579 /**
00580  *  \brief Get next token from stream
00581  *
00582  *         Key-value TOKENIZER feature
00583  *
00584  *         This function is used by key-value parser determine
00585  *         if key-value message is embedded in stream data.
00586  *
00587  *  \param str Output parameters to store token string value
00588  *  \param str_size Size of 'str' parameter in bytes (characters)
00589  *
00590  */
00591 static int getNextToken(char *str, const int str_size) {
00592     return CurTok = gettok(str, str_size);
00593 }
00594 
00595 /**
00596  *  \brief Check if character is punctuation character
00597  *
00598  *          Auxilary key-value TOKENIZER function
00599  *
00600  *          Defines if character is in subset of allowed punctuation
00601  *          characters which can be part of a key or value string.
00602  *          Not allowed characters are: ";{}"
00603  *
00604  *  \param c Input character to check
00605  *  \return Return 1 if character is allowed punctuation character, otherwise return false
00606  *
00607  */
00608 static int ispunctuation(int c) {
00609     static const char punctuation[] = "_-!@#$%^&*()=+:<>,./?\\\"'";  // No ";{}"
00610     for (size_t i=0; i< sizeof(punctuation); ++i) {
00611         if (c == punctuation[i]) {
00612             return 1;
00613         }
00614     }
00615     return 0;
00616 }
00617 
00618 /**
00619  *  \brief Check if character is string token character
00620  *
00621  *          Auxilary key-value TOKENIZER function
00622  *
00623  *          Defines if character is in subset of allowed string
00624  *          token characters.
00625  *          String defines set of characters which can be a key or value string.
00626  *
00627  *          Allowed subset includes:
00628  *          - Alphanumerical characters
00629  *          - Digits
00630  *          - White spaces and
00631  *          - subset of punctuation characters.
00632  *
00633  *  \param c Input character to check
00634  *  \return Return 1 if character is allowed punctuation character, otherwise return false
00635  *
00636  */
00637 static int isstring(int c) {
00638     return (isalpha(c) ||
00639             isdigit(c) ||
00640             isspace(c) ||
00641             ispunctuation(c));
00642 }
00643 
00644 /**
00645  *  \brief TOKENIZER of key-value protocol
00646  *
00647  *         Actual key-value TOKENIZER engine
00648  *
00649  *         TOKENIZER defines #Token enum to map recognized tokens to integer values.
00650  *
00651  *         <TOK_EOF>       ::= EOF (end of file)
00652  *         <TOK_OPEN>      ::= "{{"
00653  *         <TOK_CLOSE>     ::= "}}"
00654  *         <TOK_SEMICOLON> ::= ";"
00655  *         <TOK_STRING>    ::= [a-zA-Z0-9_-!@#$%^&*()]+    // See isstring() function *
00656  *
00657  *  \param out_str Output string with parsed token (string)
00658  *  \param str_size Size of str buffer we can use
00659  *
00660  *  \return Return #Token enum value used by parser to check for key-value occurrences
00661  *
00662  */
00663 static int gettok(char *out_str, const int str_size) {
00664     static int LastChar = '!';
00665     static int str_idx = 0;
00666 
00667     // whitespace ::=
00668     while (isspace(LastChar)) {
00669         LastChar = _get_char();
00670     }
00671 
00672     // string ::= [a-zA-Z0-9_-!@#$%^&*()]+
00673     if (isstring(LastChar)) {
00674         str_idx = 0;
00675         if (out_str && str_idx < str_size - 1) {
00676             out_str[str_idx++] = LastChar;
00677         }
00678 
00679         while (isstring((LastChar = _get_char())))
00680             if (out_str && str_idx < str_size - 1) {
00681                 out_str[str_idx++] = LastChar;
00682             }
00683         if (out_str && str_idx < str_size) {
00684             out_str[str_idx] = '\0';
00685         }
00686 
00687         return tok_string;
00688     }
00689 
00690     // semicolon ::= ';'
00691     if (LastChar == ';') {
00692         LastChar = _get_char();
00693         return tok_semicolon;
00694     }
00695 
00696     // open ::= '{{'
00697     if (LastChar == '{') {
00698         LastChar = _get_char();
00699         if (LastChar == '{') {
00700             LastChar = _get_char();
00701             return tok_open;
00702         }
00703     }
00704 
00705     // close ::= '}'
00706     if (LastChar == '}') {
00707         LastChar = _get_char();
00708         if (LastChar == '}') {
00709             //LastChar = _get_char();
00710             return tok_close;
00711         }
00712     }
00713 
00714     if (LastChar == EOF)
00715         return tok_eof;
00716 
00717     // Otherwise, just return the character as its ascii value.
00718     int ThisChar = LastChar;
00719     LastChar = _get_char();
00720     return ThisChar;
00721 }
00722 
00723 /**
00724  *  \brief Key-value parser
00725  *
00726  *         Key-value message grammar
00727  *
00728  *         <MESSAGE>: <TOK_OPEN> <TOK_STRING> <TOK_SEMICOLON> <TOK_STRING> <TOK_CLOSE>
00729  *
00730  *         Examples:
00731  *         message:     "{{__timeout; 1000}}"
00732  *                      "{{__sync; 12345678-1234-5678-1234-567812345678}}"
00733  *
00734  *  \param out_key Output buffer to store key string value
00735  *  \param out_value Output buffer to store value string value
00736  *  \param out_key_size Buffer 'out_key' buffer size
00737  *  \param out_value_size Buffer 'out_value_size' buffer size
00738  *  \return Returns 1 if key-value message was parsed successfully in stream of tokens from tokenizer
00739  *
00740  */
00741 static int HandleKV(char *out_key,
00742                     char *out_value,
00743                     const int out_key_size,
00744                     const int out_value_size) {
00745     // We already started with <open>
00746     if (getNextToken(out_key, out_key_size) == tok_string) {
00747         if (getNextToken(0, 0) == tok_semicolon) {
00748             if (getNextToken(out_value, out_value_size) == tok_string) {
00749                 if (getNextToken(0, 0) == tok_close) {
00750                     // <open> <string> <semicolon> <string> <close>
00751                     // Found "{{KEY;VALUE}}" expression
00752                     return 1;
00753                 }
00754             }
00755         }
00756     }
00757     getNextToken(0, 0);
00758     return 0;
00759 }