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