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