%% options %% header #ifdef __cplusplus extern "C" { #endif /** Convert input line into key value pairs. @param kvp Array of key value structures for results. @param szp Size of array (in), used elements (out). @param il Input line to process. @param app Application structure for diagnostics, may be NULL. @return 1 on success (number of elements in sz), 0 on error. */ int dk3str_to_key_value( dk3_key_value_t *kvp, size_t *szp, dkChar *il, dk3_app_t *app ); #ifdef __cplusplus } #endif %% state machine [options] name = dk3strkv_stm write header = no [states] S_START # Waiting for anything to happen S_KEY # In a key S_EXPECT # Expect value S_VALUE # Having value S_SQ_EXPECT # Expect single quoted value S_DQ_EXPECT # Expect double quoted value S_SQ_VALUE # Having single quoted value S_DQ_VALUE # Having double quoted value S_ERROR # Error occured [inputs] I_OT # Any other character I_WH # White space I_SQ # Single quote I_DQ # Double quote I_BS # Backslash I_EQ # Equal sign [output] O_ERROR # Error occured O_NO # Do nothing O_SQ # Squeeze O_SKEY # Start key O_ENDKEY # End key string (change to finalizer) O_ENDCOUNT # End key string and count upwards O_ENDVAL # End value string (change to finalizer) O_SVAL # Start value O_SQ_SVAL # Squeeze and start value O_SEVAL # Start and end value [rules] * * S_ERROR O_ERROR S_START I_WH S_START O_NO S_START I_OT S_KEY O_SKEY S_KEY * S_KEY O_NO S_KEY I_WH S_START O_ENDCOUNT S_KEY I_EQ S_EXPECT O_ENDKEY S_EXPECT I_WH S_EXPECT O_NO S_EXPECT * S_VALUE O_SVAL S_EXPECT I_BS S_VALUE O_SQ_SVAL S_EXPECT I_SQ S_SQ_EXPECT O_NO S_EXPECT I_DQ S_DQ_EXPECT O_NO S_VALUE * S_VALUE O_NO S_VALUE I_BS S_VALUE O_SQ S_VALUE I_WH S_START O_ENDVAL S_SQ_EXPECT * S_SQ_VALUE O_SVAL S_SQ_EXPECT I_BS S_SQ_VALUE O_SQ_SVAL S_SQ_EXPECT I_SQ S_START O_SEVAL S_SQ_VALUE * S_SQ_VALUE O_NO S_SQ_VALUE I_SQ S_START O_ENDVAL S_SQ_VALUE I_BS S_SQ_VALUE O_SQ S_DQ_EXPECT * S_DQ_VALUE O_SVAL S_DQ_EXPECT I_BS S_DQ_VALUE O_SQ_SVAL S_DQ_EXPECT I_DQ S_START O_SEVAL S_DQ_VALUE * S_DQ_VALUE O_NO S_DQ_VALUE I_BS S_DQ_VALUE O_SQ S_DQ_VALUE I_DQ S_START O_ENDVAL %% module #include "dk3all.h" #include "dk3strkv.h" /** Classify input character @param c Character to classify. @return Character class. */ static int dk3strkv_classify(dkChar c) { int back = I_OT; $? "+ dk3strkv_classify %d", (int)c switch(c) { case dkT(' '): case dkT('\t'): { back = I_WH; } break; case dkT('"'): { back = I_DQ; } break; case dkT('\''): { back = I_SQ; } break; case dkT('\\'): { back = I_BS; } break; case dkT('='): { back = I_EQ; } break; } $? "- dk3strkv_classify %d", back return back; } /** Empty line. */ static dkChar const dk3strkv_empty_line[] = { dkT("") }; /** Report error for unexpected end of text. @param app Application structure for diagnostics, may be NULL. @param il Input line to complain about. */ static void dk3strkv_error_unexpected_eot(dk3_app_t *app, dkChar *il) { dkChar const *ptr; if(app) { ptr = il; if(!(ptr)) { ptr = dk3strkv_empty_line; } dk3app_log_i3(app, DK3_LL_ERROR, 389, 390, ptr); } } /** Report error, too many key value items. @param app Application structure for diagnostics, may be NULL. @param il Input line to complain about. @param pos Position in line. */ static void dk3strkv_error_too_many(dk3_app_t *app, dkChar *il, unsigned long pos) { dkChar buffer[64]; dkChar const *ptr; if(app) { ptr = il; if(!(ptr)) { ptr = dk3strkv_empty_line; } #if VERSION_BEFORE_20140716 dk3sf_sprintf3(buffer, dkT("%lu"), pos); dk3app_log_i5(app, DK3_LL_ERROR, 386, 387, 388, buffer, ptr); #else if (dk3ma_um_to_string(buffer, DK3_SIZEOF(buffer, dkChar), (dk3_um_t)pos)) { dk3app_log_i5(app, DK3_LL_ERROR, 386, 387, 388, buffer, ptr); } #endif } } int dk3str_to_key_value( dk3_key_value_t *kvp, size_t *szp, dkChar *il, dk3_app_t *app ) { dkChar *ptr; /* Traverse string. */ dkChar *mycopy; /* Private copy for diagnostics. */ unsigned long charno; /* Character number. */ size_t i; /* Current key value pair index. */ int icl; /* Input class. */ int act; /* Action to take. */ int stm; /* State machine. */ int cc; /* Flag: Can continue. */ int rtm = 0; /* Reported too many. */ int error = 0; /* Flag: Have error. */ int back = 0; $? "+ dk3str_to_key_value \"%s\"", il if((kvp) && (szp)) { for(i = 0; i < *szp; i++) { kvp[i].key = NULL; kvp[i].val = NULL; } } if((kvp) && (szp) && (il)) { if(*szp) { mycopy = dk3str_dup_app(il, app); i = 0; ptr = il; cc = 1; charno = 1UL; dk3strkv_stm_reset(&stm); while(cc) { if(*ptr) { $? ". begin of loop \"%s\"", ptr icl = dk3strkv_classify(*ptr); $? ". icl = %d", icl act = dk3strkv_stm_step(&stm, icl); $? ". act = %d", act switch(act) { case O_ERROR: { $? ". error" error = 1; } break; case O_SQ: { $? ". O_SQ" dk3str_cpy(ptr, &(ptr[1])); if(*ptr) { charno++; } else { cc = 0; error = 1; dk3strkv_error_unexpected_eot(app, mycopy); } } break; case O_SKEY: { $? ". O_SKEY" if(i < (*szp)) { kvp[i].key = ptr; } else { error = 1; if(0 == rtm) { rtm = 1; dk3strkv_error_too_many(app, mycopy, charno); } } } break; case O_ENDKEY: { $? ". O_ENDKEY" *ptr = dkT('\0'); } break; case O_ENDCOUNT: { $? ". O_ENDCOUNT" *ptr = dkT('\0'); i++; } break; case O_ENDVAL: { $? ". O_ENDVAL" *ptr = dkT('\0'); i++; } break; case O_SVAL: { $? ". O_SVAL" if(i < (*szp)) { kvp[i].val = ptr; } else { error = 1; if(0 == rtm) { rtm = 1; dk3strkv_error_too_many(app, mycopy, charno); } } } break; case O_SQ_SVAL: { $? ". O_SQ_SVAL" dk3str_cpy(ptr, &(ptr[1])); if(*ptr) { charno++; if(i < (*szp)) { kvp[i].val = ptr; } else { error = 1; if(0 == rtm) { rtm = 1; dk3strkv_error_too_many(app, mycopy, charno); } } } else { cc = 0; error = 1; dk3strkv_error_unexpected_eot(app, mycopy); } } break; case O_SEVAL: { $? ". O_SEVAL" *ptr = dkT('\0'); if(i < (*szp)) { kvp[i].val = ptr; i++; } else { error = 1; if(0 == rtm) { rtm = 1; dk3strkv_error_too_many(app, mycopy, charno); } } } break; } ptr++; charno++; } else { cc = 0; } } switch(stm) { case S_START: { $? ". nothing to finish" if(0 == error) { back = 1; *szp = i; } } break; case S_KEY: { $? ". finish key" if(0 == error) { i++; back = 1; *szp = i; } } break; case S_VALUE: { $? ". finish value" if(0 == error) { i++; back = 1; *szp = i; } } break; } dk3_release(mycopy); } } else { if((kvp) && (szp) && (!(il))) { *szp = 0; back = 1; } } $? "- dk3str_to_key_value %d", back return back; }