1%% options 2copyright owner = Dirk Krause 3copyright year = 2017-xxxx 4SPDX-License-Identifier: BSD-3-Clause 5 6%% header 7 8#ifdef __cplusplus 9extern "C" { 10#endif 11 12/** Convert input line into key value pairs. 13 @param kvp Array of key value structures for results. 14 @param szp Size of array (in), used elements (out). 15 @param il Input line to process. 16 @param app Application structure for diagnostics, may be NULL. 17 @return 1 on success (number of elements in sz), 0 on error. 18*/ 19int 20dk3str_to_key_value( 21 dk3_key_value_t *kvp, 22 size_t *szp, 23 dkChar *il, 24 dk3_app_t *app 25); 26 27#ifdef __cplusplus 28} 29#endif 30 31 32 33%% state machine 34 35[options] 36 name = dk3strkv_stm 37 write header = no 38[states] 39 ST_START # Waiting for anything to happen 40 ST_KEY # In a key 41 ST_EXPECT # Expect value 42 ST_VALUE # Having value 43 ST_SQ_EXPECT # Expect single quoted value 44 ST_DQ_EXPECT # Expect double quoted value 45 ST_SQ_VALUE # Having single quoted value 46 ST_DQ_VALUE # Having double quoted value 47 ST_ERROR # Error occured 48[inputs] 49 I_OT # Any other character 50 I_WH # White space 51 I_SQ # Single quote 52 I_DQ # Double quote 53 I_BS # Backslash 54 I_EQ # Equal sign 55[output] 56 O_ERROR # Error occured 57 O_NO # Do nothing 58 O_SQ # Squeeze 59 O_SKEY # Start key 60 O_ENDKEY # End key string (change to finalizer) 61 O_ENDCOUNT # End key string and count upwards 62 O_ENDVAL # End value string (change to finalizer) 63 O_SVAL # Start value 64 O_SQ_SVAL # Squeeze and start value 65 O_SEVAL # Start and end value 66[rules] 67 * * ST_ERROR O_ERROR 68 69 ST_START I_WH ST_START O_NO 70 ST_START I_OT ST_KEY O_SKEY 71 72 ST_KEY * ST_KEY O_NO 73 ST_KEY I_WH ST_START O_ENDCOUNT 74 ST_KEY I_EQ ST_EXPECT O_ENDKEY 75 76 ST_EXPECT I_WH ST_EXPECT O_NO 77 ST_EXPECT * ST_VALUE O_SVAL 78 ST_EXPECT I_BS ST_VALUE O_SQ_SVAL 79 ST_EXPECT I_SQ ST_SQ_EXPECT O_NO 80 ST_EXPECT I_DQ ST_DQ_EXPECT O_NO 81 82 ST_VALUE * ST_VALUE O_NO 83 ST_VALUE I_BS ST_VALUE O_SQ 84 ST_VALUE I_WH ST_START O_ENDVAL 85 86 ST_SQ_EXPECT * ST_SQ_VALUE O_SVAL 87 ST_SQ_EXPECT I_BS ST_SQ_VALUE O_SQ_SVAL 88 ST_SQ_EXPECT I_SQ ST_START O_SEVAL 89 90 ST_SQ_VALUE * ST_SQ_VALUE O_NO 91 ST_SQ_VALUE I_SQ ST_START O_ENDVAL 92 ST_SQ_VALUE I_BS ST_SQ_VALUE O_SQ 93 94 ST_DQ_EXPECT * ST_DQ_VALUE O_SVAL 95 ST_DQ_EXPECT I_BS ST_DQ_VALUE O_SQ_SVAL 96 ST_DQ_EXPECT I_DQ ST_START O_SEVAL 97 98 ST_DQ_VALUE * ST_DQ_VALUE O_NO 99 ST_DQ_VALUE I_BS ST_DQ_VALUE O_SQ 100 ST_DQ_VALUE I_DQ ST_START O_ENDVAL 101 102%% module 103 104 105#include <libdk3c/dk3all.h> 106#include <libdk3c/dk3strkv.h> 107 108 109 110/** Classify input character 111 @param c Character to classify. 112 @return Character class. 113*/ 114static 115int 116dk3strkv_classify(dkChar c) 117{ 118 int back = I_OT; 119 $? "+ dk3strkv_classify %d", (int)c 120 switch(c) { 121 case dkT(' '): case dkT('\t'): { 122 back = I_WH; 123 } break; 124 case dkT('"'): { 125 back = I_DQ; 126 } break; 127 case dkT('\''): { 128 back = I_SQ; 129 } break; 130 case dkT('\\'): { 131 back = I_BS; 132 } break; 133 case dkT('='): { 134 back = I_EQ; 135 } break; 136 } $? "- dk3strkv_classify %d", back 137 return back; 138} 139 140 141 142/** Empty line. 143*/ 144static dkChar const dk3strkv_empty_line[] = { dkT("") }; 145 146 147 148/** Report error for unexpected end of text. 149 @param app Application structure for diagnostics, may be NULL. 150 @param il Input line to complain about. 151*/ 152static 153void 154dk3strkv_error_unexpected_eot(dk3_app_t *app, dkChar *il) 155{ 156 dkChar const *ptr; 157 if(app) { 158 ptr = il; 159 if(!(ptr)) { ptr = dk3strkv_empty_line; } 160 dk3app_log_i3(app, DK3_LL_ERROR, 389, 390, ptr); 161 } 162} 163 164 165 166/** Report error, too many key value items. 167 @param app Application structure for diagnostics, may be NULL. 168 @param il Input line to complain about. 169 @param pos Position in line. 170*/ 171static 172void 173dk3strkv_error_too_many(dk3_app_t *app, dkChar *il, unsigned long pos) 174{ 175 dkChar buffer[64]; 176 dkChar const *ptr; 177 if(app) { 178 ptr = il; 179 if(!(ptr)) { ptr = dk3strkv_empty_line; } 180#if VERSION_BEFORE_20140716 181 dk3sf_sprintf3(buffer, dkT("%lu"), pos); 182 dk3app_log_i5(app, DK3_LL_ERROR, 386, 387, 388, buffer, ptr); 183#else 184 if (dk3ma_um_to_string(buffer, DK3_SIZEOF(buffer, dkChar), (dk3_um_t)pos)) { 185 dk3app_log_i5(app, DK3_LL_ERROR, 386, 387, 388, buffer, ptr); 186 } 187#endif 188 } 189} 190 191 192 193 194int 195dk3str_to_key_value( 196 dk3_key_value_t *kvp, 197 size_t *szp, 198 dkChar *il, 199 dk3_app_t *app 200) 201{ 202 dkChar *ptr; /* Traverse string. */ 203 dkChar *mycopy; /* Private copy for diagnostics. */ 204 unsigned long charno; /* Character number. */ 205 size_t i; /* Current key value pair index. */ 206 int icl; /* Input class. */ 207 int act; /* Action to take. */ 208 int stm; /* State machine. */ 209 int cc; /* Flag: Can continue. */ 210 int rtm = 0; /* Reported too many. */ 211 int error = 0; /* Flag: Have error. */ 212 int back = 0; 213 $? "+ dk3str_to_key_value \"%!ds\"", il 214 if((kvp) && (szp)) { 215 for(i = 0; i < *szp; i++) { kvp[i].key = NULL; kvp[i].val = NULL; } 216 } 217 if((kvp) && (szp) && (il)) { 218 if(*szp) { 219 mycopy = dk3str_dup_app(il, app); 220 i = 0; 221 ptr = il; cc = 1; charno = 1UL; 222 dk3strkv_stm_reset(&stm); 223 while(cc) { 224 if(*ptr) { $? ". begin of loop \"%!ds\"", ptr 225 icl = dk3strkv_classify(*ptr); $? ". icl = %d", icl 226 act = dk3strkv_stm_step(&stm, icl); $? ". act = %d", act 227 switch(act) { 228 case O_ERROR: { $? ". error" 229 error = 1; 230 } break; 231 case O_SQ: { $? ". O_SQ" 232 dk3str_cpy(ptr, &(ptr[1])); 233 if(*ptr) { 234 charno++; 235 } else { 236 cc = 0; 237 error = 1; 238 dk3strkv_error_unexpected_eot(app, mycopy); 239 } 240 } break; 241 case O_SKEY: { $? ". O_SKEY" 242 if(i < (*szp)) { 243 kvp[i].key = ptr; 244 } else { 245 error = 1; 246 if(0 == rtm) { 247 rtm = 1; 248 dk3strkv_error_too_many(app, mycopy, charno); 249 } 250 } 251 } break; 252 case O_ENDKEY: { $? ". O_ENDKEY" 253 *ptr = dkT('\0'); 254 } break; 255 case O_ENDCOUNT: { $? ". O_ENDCOUNT" 256 *ptr = dkT('\0'); 257 i++; 258 } break; 259 case O_ENDVAL: { $? ". O_ENDVAL" 260 *ptr = dkT('\0'); 261 i++; 262 } break; 263 case O_SVAL: { $? ". O_SVAL" 264 if(i < (*szp)) { 265 kvp[i].val = ptr; 266 } else { 267 error = 1; 268 if(0 == rtm) { 269 rtm = 1; 270 dk3strkv_error_too_many(app, mycopy, charno); 271 } 272 } 273 } break; 274 case O_SQ_SVAL: { $? ". O_SQ_SVAL" 275 dk3str_cpy(ptr, &(ptr[1])); 276 if(*ptr) { 277 charno++; 278 if(i < (*szp)) { 279 kvp[i].val = ptr; 280 } else { 281 error = 1; 282 if(0 == rtm) { 283 rtm = 1; 284 dk3strkv_error_too_many(app, mycopy, charno); 285 } 286 } 287 } else { 288 cc = 0; 289 error = 1; 290 dk3strkv_error_unexpected_eot(app, mycopy); 291 } 292 } break; 293 case O_SEVAL: { $? ". O_SEVAL" 294 *ptr = dkT('\0'); 295 if(i < (*szp)) { 296 kvp[i].val = ptr; 297 i++; 298 } else { 299 error = 1; 300 if(0 == rtm) { 301 rtm = 1; 302 dk3strkv_error_too_many(app, mycopy, charno); 303 } 304 } 305 } break; 306 } 307 ptr++; 308 charno++; 309 } else { 310 cc = 0; 311 } 312 } 313 switch(stm) { 314 case ST_START: { $? ". nothing to finish" 315 if(0 == error) { 316 back = 1; 317 *szp = i; 318 } 319 } break; 320 case ST_KEY: { $? ". finish key" 321 if(0 == error) { 322 i++; 323 back = 1; 324 *szp = i; 325 } 326 } break; 327 case ST_VALUE: { $? ". finish value" 328 if(0 == error) { 329 i++; 330 back = 1; 331 *szp = i; 332 } 333 } break; 334 } 335 dk3_release(mycopy); 336 } 337 } else { 338 if((kvp) && (szp) && (!(il))) { 339 *szp = 0; 340 back = 1; 341 } 342 } $? "- dk3str_to_key_value %d", back 343 return back; 344} 345 346 347 348