1#! /bin/sh
2#  -*- Mode: Shell-script -*-
3# str2m.test --- test str2enum and str2mask functionality
4#
5# Author:            Bruce Korb <bkorb@gnu.org>
6##
7## This file is part of AutoGen.
8## AutoGen Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
9##
10## AutoGen is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by the
12## Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## AutoGen is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18## See the GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License along
21## with this program.  If not, see <http://www.gnu.org/licenses/>.
22##
23#
24# ----------------------------------------------------------------------
25
26. ./defs
27gperf=`command -v gperf`
28test -x "$gperf" || {
29    echo "$0 - cannot run without gperf installed" >&2
30    exit 0
31}
32
33# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
34
35echo creating $testname.def
36cat > $testname.def <<'_EOF_'
37AutoGen Definitions str2enum;
38
39cmd[0]  = one;
40cmd[3]  = three;
41cmd[7]  = seven;
42cmd[11] = eleven;
43cmd[19] = ninteen;
44
45type = kwd;
46mask = { m-name = one-seven; m-bit = one, seven; };
47mask = { m-name = not-one-seven; m-bit = three, eleven, ninteen; m-invert; };
48_EOF_
49
50cmd_list=`${SED} -n '/^cmd/{s/.*= *//;s/;//;p;}' $testname.def`
51cmd_list=`echo $cmd_list`
52# # # # # # # # # # EXPECTED OUTPUT FILES # # # # # # #
53
54echo creating $testname-h.base
55# this is the output we should expect to see
56cat > $testname-h.base <<- _EOF_
57	typedef enum {
58	    STR2M_INVALID_KWD = 0,
59	    STR2M_KWD_ONE      = 1,
60	    STR2M_KWD_THREE    = 4,
61	    STR2M_KWD_SEVEN    = 8,
62	    STR2M_KWD_ELEVEN   = 12,
63	    STR2M_KWD_NINTEEN  = 20,
64	    STR2M_COUNT_KWD
65	} str2m_enum_t;
66
67	extern str2m_enum_t
68	find_str2m_kwd(char const * str, size_t len);
69
70	extern char const *
71	str2m_name(str2m_enum_t id);
72
73	#endif /* STR2ENUM_STR2M_H_GUARD */
74	_EOF_
75
76cat > $testname-c.base <<- _EOF_
77	 * Convert a command (keyword) to a str2m_enum_t enumeration value.
78	 *
79	 * @param[in] str   a string that should start with a known key word.
80	 * @param[in] len   the provided length of the keyword at \a str.
81	 * @returns the enumeration value.
82	 * If not found, that value is STR2M_INVALID_KWD.
83	 */
84	str2m_enum_t
85	find_str2m_kwd(char const * str, size_t len)
86	{
87	    str2m_map_t const * map;
88
89	    map = find_str2m_name(str, (unsigned int)len);
90	    return (map == NULL) ? STR2M_INVALID_KWD : map->str2m_id;
91	}
92
93	/**
94	 * Convert an str2m_enum_t value into a string.
95	 *
96	 * @param[in] id  the enumeration value
97	 * @returns the associated string, or "* UNDEFINED *" if \a id
98	 * is out of range.
99	 */
100	char const *
101	str2m_name(str2m_enum_t id)
102	{
103	    static char const undef[] = "* UNDEFINED *";
104	    static char const * const nm_table[] = {
105	        [STR2M_KWD_ELEVEN  ] = "eleven",
106	        [STR2M_KWD_NINTEEN ] = "ninteen",
107	        [STR2M_KWD_ONE     ] = "one",
108	        [STR2M_KWD_SEVEN   ] = "seven",
109	        [STR2M_KWD_THREE   ] = "three" };
110	    char const * res = undef;
111	    if (id < STR2M_COUNT_KWD) {
112	        res = nm_table[id];
113	        if (res == NULL)
114	            res = undef;
115	    }
116	    return res;
117	}
118
119	/* end of str2m.c */
120	_EOF_
121
122# # # # # # # # # # RUN THE TEST # # # # # # #
123
124AGCMD="-L ${srcdir}/.. -L ${top_srcdir}/autoopts/tpl"
125echo run_ag x ${AGCMD} $testname.def
126run_ag x ${AGCMD} $testname.def || \
127    failure ${AGCMD} failed
128
129get_h_text="/^typedef enum/,/#endif.*GUARD/p"
130${SED} -n "${get_h_text}" $testname.h > $testname-h.res
131fpair="$testname-h.base $testname-h.res"
132cmp -s $fpair || \
133    failure "$testname $fpair failed`echo
134        diff $fpair`"
135get_c_text="/ Convert .*to a ${testname}_enum_t/,/end of $testname.c/p"
136${SED} -n "${get_c_text}" $testname.c > $testname-c.res
137
138fpair="$testname-c.base $testname-c.res"
139cmp -s $fpair || \
140    failure "$testname $fpair failed`echo
141        diff $fpair`"
142
143# # # # # # # # # # OUTPUT FILES PART 2 # # # # # # #
144
145echo creating $testname-h2.base
146TNAME=`echo $testname | tr '[a-z]' '[A-Z]'`
147rep_name="s/${testname}/${testname}_2/g;s/${TNAME}/${TNAME}_2/g"
148cat > $testname-h2.base <<- _EOF_
149	#ifndef STR2ENUM_STR2M_2_H_GUARD
150	#define STR2ENUM_STR2M_2_H_GUARD 1
151	#include <sys/types.h>
152	#include <inttypes.h>
153
154	/** integral type for holding str2m_2 masks */
155	typedef uint32_t str2m_2_mask_t;
156
157	/** bits defined for str2m_2_mask_t */
158	#define STR2M_2_KWD_ONE                0x00001U
159	#define STR2M_2_KWD_THREE              0x00008U
160	#define STR2M_2_KWD_SEVEN              0x00080U
161	#define STR2M_2_KWD_ELEVEN             0x00800U
162	#define STR2M_2_KWD_NINTEEN            0x80000U
163
164	/** bits in ONE_SEVEN mask:
165	 *  one   seven */
166	#define STR2M_2_KWD_ONE_SEVEN_MASK     0x00081U
167
168	/** bits omitted from NOT_ONE_SEVEN mask:
169	 *  three   eleven  ninteen */
170	#define STR2M_2_KWD_NOT_ONE_SEVEN_MASK 0x00081U
171
172	/** all bits in str2m_2_mask_t masks */
173	#define STR2M_2_KWD_MASK_ALL           0x80889U
174
175	/** no bits in str2m_2_mask_t */
176	#define STR2M_2_KWD_EMPTY              0x00000U
177
178	/** buffer size needed to hold all bit names for str2m_2_mask_t masks */
179	#define MAX_STR2M_2_NAME_SIZE 31
180
181	extern str2m_2_mask_t
182	str2m_2_str2mask(char const * str, str2m_2_mask_t old);
183
184	extern size_t
185	str2m_2_mask2str(str2m_2_mask_t mask, char * buf, size_t len);
186
187	#endif /* STR2ENUM_STR2M_2_H_GUARD */
188	_EOF_
189
190mask_all=`
191    sed -n '/KWD_MASK_ALL/{;s/.*0x/0x/;s/UL*$//;p;q;}' $testname-h2.base`
192
193cat > $testname-c2.base <<- _EOF_
194	 * Convert a command (keyword) to a str2m_2_enum_t enumeration value.
195	 *
196	 * @param[in] str   a string that should start with a known key word.
197	 * @param[in] len   the provided length of the keyword at \a str.
198	 * @returns the enumeration value.
199	 * If not found, that value is STR2M_2_COUNT_KWDBNM.
200	 */
201	static str2m_2_enum_t
202	find_str2m_2_kwdbnm(char const * str, size_t len)
203	{
204	    str2m_2_map_t const * map;
205
206	    map = find_str2m_2_name(str, (unsigned int)len);
207	    return (map == NULL) ? STR2M_2_COUNT_KWDBNM : map->str2m_2_id;
208	}
209
210	/**
211	 * Convert an str2m_2_enum_t value into a string.
212	 *
213	 * @param[in] id  the enumeration value
214	 * @returns the associated string, or "* UNDEFINED *" if \a id
215	 * is out of range.
216	 */
217	static char const *
218	str2m_2_name(str2m_2_enum_t id)
219	{
220	    static char const undef[] = "* UNDEFINED *";
221	    static char const * const nm_table[] = {
222	        [STR2M_2_KWDBNM_ELEVEN  ] = "eleven",
223	        [STR2M_2_KWDBNM_NINTEEN ] = "ninteen",
224	        [STR2M_2_KWDBNM_ONE     ] = "one",
225	        [STR2M_2_KWDBNM_SEVEN   ] = "seven",
226	        [STR2M_2_KWDBNM_THREE   ] = "three" };
227	    char const * res = undef;
228	    if (id < STR2M_2_COUNT_KWDBNM) {
229	        res = nm_table[id];
230	        if (res == NULL)
231	            res = undef;
232	    }
233	    return res;
234	}
235
236	/**
237	 * Convert a string to a str2m_2_mask_t mask.
238	 * Bit names prefixed with a hyphen have the bit removed from the mask.
239	 * If the string starts with a '-', '+' or '|' character, then
240	 * the old value is used as a base, otherwise the result mask
241	 * is initialized to zero.  Separating bit names with '+' or '|'
242	 * characters is optional.  By default, the bits are "or"-ed into the
243	 * result.
244	 *
245	 * @param[in] str string with a list of bit names
246	 * @param[in] old previous value, used if \a str starts with a '+' or '-'.
247	 *
248	 * @returns an unsigned integer with the bits set.
249	 */
250	str2m_2_mask_t
251	str2m_2_str2mask(char const * str, str2m_2_mask_t old)
252	{
253	    static char const white[] = ", \t\f";
254	    static char const name_chars[] =
255	        "ehilnorstv"
256	        "EHILNORSTV";
257
258	    str2m_2_mask_t res = 0;
259	    int have_data = 0;
260
261	    for (;;) {
262	        str2m_2_enum_t val;
263	        unsigned int val_len;
264	        unsigned int invert = 0;
265	
266	        str += strspn(str, white);
267	        switch (*str) {
268	        case NUL: return res;
269	        case '-': case '~':
270	            invert = 1;
271	            /* FALLTHROUGH */
272
273	        case '+': case '|':
274	            if (have_data == 0)
275	                res = old;
276
277	            str += 1 + strspn(str + 1, white);
278	            if (*str == NUL)
279	                return 0;
280	        }
281
282	        val_len = strspn(str, name_chars);
283	        if (val_len == 0)
284	            return 0;
285	        val = find_str2m_2_kwdbnm(str, val_len);
286	        if (val == STR2M_2_COUNT_KWDBNM)
287	            return 0;
288	        if (invert)
289	            res &= ~((str2m_2_mask_t)1 << val);
290	        else
291	            res |= (str2m_2_mask_t)1 << val;
292	        have_data = 1;
293	        str += val_len;
294	    }
295	}
296
297	/**
298	 * Convert a str2m_2_mask_t mask to a string.
299	 *
300	 * @param[in]  mask  the mask with the bits to be named
301	 * @param[out] buf   where to store the result.  This may be NULL.
302	 * @param[in]  len   size of the output buffer
303	 * @results    The full length of the space needed for the result,
304	 * including the terminating NUL byte.  The actual result will not
305	 * overwrite \a len bytes at \a buf.  This value will also never
306	 * exceed MAX_STR2M_2_NAME_SIZE.
307	 */
308	size_t
309	str2m_2_mask2str(str2m_2_mask_t mask, char * buf, size_t len)
310	{
311	    str2m_2_enum_t val = (str2m_2_enum_t)0;
312	    size_t res = 0;
313	    if (buf == NULL) len = 0;
314
315	    for (; mask != 0; val++, mask >>= 1) {
316	        char const * p;
317	        size_t l;
318
319	        if (val >= STR2M_2_COUNT_KWDBNM)
320	            break;
321
322	        if ((mask & 1) == 0)
323	            continue;
324
325	        p = str2m_2_name(val);
326	        if (*p == '*')
327	            continue; /* ignore invalid bits */
328
329	        l = strlen(p) + 1; /* includes NUL byte or spacer byte */
330	        if (l <= len) {
331	            if (res > 0)
332	                *(buf++) = ' ';
333	            memcpy(buf, p, l);
334	            buf += l - 1;
335	            len -= l;
336	        }
337	        res += l;
338	    }
339	    return (res == 0) ? 1 : res;
340	}
341	/* end of str2m-2.c */
342	_EOF_
343
344# # # # # # # # # # RUN THE TEST PART 2 # # # # # # #
345
346echo run_ag x ${AGCMD} -T str2mask $testname.def
347echo "base-name = '$testname-2'; prefix = '${testname}_2';" >> $testname.def
348run_ag x ${AGCMD} -T str2mask $testname.def || \
349    failure ${AGCMD} failed
350get_h_text='/#ifndef .*_GUARD/,/#endif .*_GUARD/p'
351${SED} -n "${get_h_text}" $testname-2.h > $testname-h2.res
352get_c_text=`echo "$get_c_text" | ${SED} "$rep_name"`
353${SED} -n "/_names+/d;${get_c_text}" $testname-2.c > $testname-c2.res
354
355fpair="$testname-h2.base $testname-h2.res"
356cmp -s $fpair || \
357    failure "$testname-2 $fpair failed`echo
358        diff $fpair`"
359
360fpair="$testname-c2.base $testname-c2.res"
361cmp -s $fpair || \
362    failure "$testname-2 $fpair failed`echo
363        diff $fpair`"
364
365# # # # # # # # # # RUN THE TEST PART 3 # # # # # # #
366
367echo running $testname-2.c program
368chmod 666 $testname-2.c
369cmd_sum=`echo $cmd_list | sed 's/ / + /g'`
370cat > $testname-main.c <<- _EOF_
371	#include <stdio.h>
372	#include "$testname-2.c"
373	int main(int argc, char ** argv) {
374	    str2m_2_mask_t mask =
375	        str2m_2_str2mask("${cmd_sum}", 0);
376	    char buf[MAX_STR2M_2_NAME_SIZE];
377	    size_t l = str2m_2_mask2str(mask, buf, MAX_STR2M_2_NAME_SIZE);
378
379	    printf("0x%04X --> %u bytes: '%s'\n",
380	           (unsigned int)mask, (unsigned int)l, buf);
381	    if (l != MAX_STR2M_2_NAME_SIZE) {
382	        fprintf(stderr, "expected len:  %u, actual:  %u\n",
383	                MAX_STR2M_2_NAME_SIZE, (unsigned int)l);
384	        return 1;
385	    }
386	    mask = str2m_2_str2mask("- three - eleven - ninteen", mask);
387	    if (mask != STR2M_2_KWD_NOT_ONE_SEVEN_MASK) {
388	        fprintf(stderr, "0x%04X != 0x%04X\n", mask,
389	                STR2M_2_KWD_NOT_ONE_SEVEN_MASK);
390	        return 1;
391	    }
392	    return 0;
393	}
394	_EOF_
395
396${CC:-cc} -o ${testname} $testname-main.c || \
397    failure "cannot compile $testname-2.c"
398./${testname} > ${testname}-btest
399echo "$mask_all --> $(( ${#cmd_list} + 1 )) bytes: '$cmd_list'" \
400    > ${testname}-bbase
401fpair="${testname}-bbase ${testname}-btest"
402cmp -s $fpair || \
403    failure "$testname $fpair run failed`echo
404        diff $fpair`"
405
406cleanup
407
408## Local Variables:
409## mode: shell-script
410## indent-tabs-mode: nil
411## sh-indentation: 4
412## sh-basic-offset: 4
413## End:
414
415# end of str2m.test
416