#! /bin/sh # -*- Mode: Shell-script -*- # str2m.test --- test str2enum and str2mask functionality # # Author: Bruce Korb ## ## This file is part of AutoGen. ## AutoGen Copyright (C) 1992-2016 by Bruce Korb - all rights reserved ## ## AutoGen is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by the ## Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## AutoGen is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ## See the GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program. If not, see . ## # # ---------------------------------------------------------------------- . ./defs gperf=`command -v gperf` test -x "$gperf" || { echo "$0 - cannot run without gperf installed" >&2 exit 0 } # # # # # # # # # # DEFINITIONS FILE # # # # # # # # # echo creating $testname.def cat > $testname.def <<'_EOF_' AutoGen Definitions str2enum; cmd[0] = one; cmd[3] = three; cmd[7] = seven; cmd[11] = eleven; cmd[19] = ninteen; type = kwd; mask = { m-name = one-seven; m-bit = one, seven; }; mask = { m-name = not-one-seven; m-bit = three, eleven, ninteen; m-invert; }; _EOF_ cmd_list=`${SED} -n '/^cmd/{s/.*= *//;s/;//;p;}' $testname.def` cmd_list=`echo $cmd_list` # # # # # # # # # # EXPECTED OUTPUT FILES # # # # # # # echo creating $testname-h.base # this is the output we should expect to see cat > $testname-h.base <<- _EOF_ typedef enum { STR2M_INVALID_KWD = 0, STR2M_KWD_ONE = 1, STR2M_KWD_THREE = 4, STR2M_KWD_SEVEN = 8, STR2M_KWD_ELEVEN = 12, STR2M_KWD_NINTEEN = 20, STR2M_COUNT_KWD } str2m_enum_t; extern str2m_enum_t find_str2m_kwd(char const * str, size_t len); extern char const * str2m_name(str2m_enum_t id); #endif /* STR2ENUM_STR2M_H_GUARD */ _EOF_ cat > $testname-c.base <<- _EOF_ * Convert a command (keyword) to a str2m_enum_t enumeration value. * * @param[in] str a string that should start with a known key word. * @param[in] len the provided length of the keyword at \a str. * @returns the enumeration value. * If not found, that value is STR2M_INVALID_KWD. */ str2m_enum_t find_str2m_kwd(char const * str, size_t len) { str2m_map_t const * map; map = find_str2m_name(str, (unsigned int)len); return (map == NULL) ? STR2M_INVALID_KWD : map->str2m_id; } /** * Convert an str2m_enum_t value into a string. * * @param[in] id the enumeration value * @returns the associated string, or "* UNDEFINED *" if \a id * is out of range. */ char const * str2m_name(str2m_enum_t id) { static char const undef[] = "* UNDEFINED *"; static char const * const nm_table[] = { [STR2M_KWD_ELEVEN ] = "eleven", [STR2M_KWD_NINTEEN ] = "ninteen", [STR2M_KWD_ONE ] = "one", [STR2M_KWD_SEVEN ] = "seven", [STR2M_KWD_THREE ] = "three" }; char const * res = undef; if (id < STR2M_COUNT_KWD) { res = nm_table[id]; if (res == NULL) res = undef; } return res; } /* end of str2m.c */ _EOF_ # # # # # # # # # # RUN THE TEST # # # # # # # AGCMD="-L ${srcdir}/.. -L ${top_srcdir}/autoopts/tpl" echo run_ag x ${AGCMD} $testname.def run_ag x ${AGCMD} $testname.def || \ failure ${AGCMD} failed get_h_text="/^typedef enum/,/#endif.*GUARD/p" ${SED} -n "${get_h_text}" $testname.h > $testname-h.res fpair="$testname-h.base $testname-h.res" cmp -s $fpair || \ failure "$testname $fpair failed`echo diff $fpair`" get_c_text="/ Convert .*to a ${testname}_enum_t/,/end of $testname.c/p" ${SED} -n "${get_c_text}" $testname.c > $testname-c.res fpair="$testname-c.base $testname-c.res" cmp -s $fpair || \ failure "$testname $fpair failed`echo diff $fpair`" # # # # # # # # # # OUTPUT FILES PART 2 # # # # # # # echo creating $testname-h2.base TNAME=`echo $testname | tr '[a-z]' '[A-Z]'` rep_name="s/${testname}/${testname}_2/g;s/${TNAME}/${TNAME}_2/g" cat > $testname-h2.base <<- _EOF_ #ifndef STR2ENUM_STR2M_2_H_GUARD #define STR2ENUM_STR2M_2_H_GUARD 1 #include #include /** integral type for holding str2m_2 masks */ typedef uint32_t str2m_2_mask_t; /** bits defined for str2m_2_mask_t */ #define STR2M_2_KWD_ONE 0x00001U #define STR2M_2_KWD_THREE 0x00008U #define STR2M_2_KWD_SEVEN 0x00080U #define STR2M_2_KWD_ELEVEN 0x00800U #define STR2M_2_KWD_NINTEEN 0x80000U /** bits in ONE_SEVEN mask: * one seven */ #define STR2M_2_KWD_ONE_SEVEN_MASK 0x00081U /** bits omitted from NOT_ONE_SEVEN mask: * three eleven ninteen */ #define STR2M_2_KWD_NOT_ONE_SEVEN_MASK 0x00081U /** all bits in str2m_2_mask_t masks */ #define STR2M_2_KWD_MASK_ALL 0x80889U /** no bits in str2m_2_mask_t */ #define STR2M_2_KWD_EMPTY 0x00000U /** buffer size needed to hold all bit names for str2m_2_mask_t masks */ #define MAX_STR2M_2_NAME_SIZE 31 extern str2m_2_mask_t str2m_2_str2mask(char const * str, str2m_2_mask_t old); extern size_t str2m_2_mask2str(str2m_2_mask_t mask, char * buf, size_t len); #endif /* STR2ENUM_STR2M_2_H_GUARD */ _EOF_ mask_all=` sed -n '/KWD_MASK_ALL/{;s/.*0x/0x/;s/UL*$//;p;q;}' $testname-h2.base` cat > $testname-c2.base <<- _EOF_ * Convert a command (keyword) to a str2m_2_enum_t enumeration value. * * @param[in] str a string that should start with a known key word. * @param[in] len the provided length of the keyword at \a str. * @returns the enumeration value. * If not found, that value is STR2M_2_COUNT_KWDBNM. */ static str2m_2_enum_t find_str2m_2_kwdbnm(char const * str, size_t len) { str2m_2_map_t const * map; map = find_str2m_2_name(str, (unsigned int)len); return (map == NULL) ? STR2M_2_COUNT_KWDBNM : map->str2m_2_id; } /** * Convert an str2m_2_enum_t value into a string. * * @param[in] id the enumeration value * @returns the associated string, or "* UNDEFINED *" if \a id * is out of range. */ static char const * str2m_2_name(str2m_2_enum_t id) { static char const undef[] = "* UNDEFINED *"; static char const * const nm_table[] = { [STR2M_2_KWDBNM_ELEVEN ] = "eleven", [STR2M_2_KWDBNM_NINTEEN ] = "ninteen", [STR2M_2_KWDBNM_ONE ] = "one", [STR2M_2_KWDBNM_SEVEN ] = "seven", [STR2M_2_KWDBNM_THREE ] = "three" }; char const * res = undef; if (id < STR2M_2_COUNT_KWDBNM) { res = nm_table[id]; if (res == NULL) res = undef; } return res; } #include #include #ifndef NUL #define NUL '\0' #endif /** * Convert a string to a str2m_2_mask_t mask. * Bit names prefixed with a hyphen have the bit removed from the mask. * If the string starts with a '-', '+' or '|' character, then * the old value is used as a base, otherwise the result mask * is initialized to zero. Separating bit names with '+' or '|' * characters is optional. By default, the bits are "or"-ed into the * result. * * @param[in] str string with a list of bit names * @param[in] old previous value, used if \a str starts with a '+' or '-'. * * @returns an unsigned integer with the bits set. */ str2m_2_mask_t str2m_2_str2mask(char const * str, str2m_2_mask_t old) { static char const white[] = " \t\f"; static char const name_chars[] = "ehilnorstv" "EHILNORSTV"; str2m_2_mask_t res = 0; int have_data = 0; for (;;) { str2m_2_enum_t val; unsigned int val_len; unsigned int invert = 0; str += strspn(str, white); switch (*str) { case NUL: return res; case '-': case '~': invert = 1; /* FALLTHROUGH */ case '+': case '|': if (have_data == 0) res = old; str += 1 + strspn(str + 1, white); if (*str == NUL) return 0; } val_len = strspn(str, name_chars); if (val_len == 0) return 0; val = find_str2m_2_kwdbnm(str, val_len); if (val == STR2M_2_COUNT_KWDBNM) return 0; if (invert) res &= ~((str2m_2_mask_t)1 << val); else res |= (str2m_2_mask_t)1 << val; have_data = 1; str += val_len; } } /** * Convert a str2m_2_mask_t mask to a string. * * @param[in] mask the mask with the bits to be named * @param[out] buf where to store the result. This may be NULL. * @param[in] len size of the output buffer * @results The full length of the space needed for the result, * including the terminating NUL byte. The actual result will not * overwrite \a len bytes at \a buf. This value will also never * exceed MAX_STR2M_2_NAME_SIZE. */ size_t str2m_2_mask2str(str2m_2_mask_t mask, char * buf, size_t len) { str2m_2_enum_t val = (str2m_2_enum_t)0; size_t res = 0; if (buf == NULL) len = 0; for (; mask != 0; val++, mask >>= 1) { char const * p; size_t l; if (val >= STR2M_2_COUNT_KWDBNM) break; if ((mask & 1) == 0) continue; p = str2m_2_name(val); if (*p == '*') continue; /* ignore invalid bits */ l = strlen(p) + 1; /* includes NUL byte or spacer byte */ if (l <= len) { if (res > 0) *(buf++) = ' '; memcpy(buf, p, l); buf += l - 1; len -= l; } res += l; } return (res == 0) ? 1 : res; } /* end of str2m-2.c */ _EOF_ # # # # # # # # # # RUN THE TEST PART 2 # # # # # # # echo run_ag x ${AGCMD} -T str2mask $testname.def echo "base-name = '$testname-2'; prefix = '${testname}_2';" >> $testname.def run_ag x ${AGCMD} -T str2mask $testname.def || \ failure ${AGCMD} failed get_h_text='/#ifndef .*_GUARD/,/#endif .*_GUARD/p' ${SED} -n "${get_h_text}" $testname-2.h > $testname-h2.res get_c_text=`echo "$get_c_text" | ${SED} "$rep_name"` ${SED} -n "/_names+/d;${get_c_text}" $testname-2.c > $testname-c2.res fpair="$testname-h2.base $testname-h2.res" cmp -s $fpair || \ failure "$testname-2 $fpair failed`echo diff $fpair`" fpair="$testname-c2.base $testname-c2.res" cmp -s $fpair || \ failure "$testname-2 $fpair failed`echo diff $fpair`" # # # # # # # # # # RUN THE TEST PART 3 # # # # # # # echo running $testname-2.c program chmod 666 $testname-2.c cmd_sum=`echo $cmd_list | sed 's/ / + /g'` cat > $testname-main.c <<- _EOF_ #include #include "$testname-2.c" int main(int argc, char ** argv) { str2m_2_mask_t mask = str2m_2_str2mask("${cmd_sum}", 0); char buf[MAX_STR2M_2_NAME_SIZE]; size_t l = str2m_2_mask2str(mask, buf, MAX_STR2M_2_NAME_SIZE); printf("0x%04X --> %u bytes: '%s'\n", (unsigned int)mask, (unsigned int)l, buf); if (l != MAX_STR2M_2_NAME_SIZE) { fprintf(stderr, "expected len: %u, actual: %u\n", MAX_STR2M_2_NAME_SIZE, (unsigned int)l); return 1; } mask = str2m_2_str2mask("- three - eleven - ninteen", mask); if (mask != STR2M_2_KWD_NOT_ONE_SEVEN_MASK) { fprintf(stderr, "0x%04X != 0x%04X\n", mask, STR2M_2_KWD_NOT_ONE_SEVEN_MASK); return 1; } return 0; } _EOF_ ${CC:-cc} -o ${testname} $testname-main.c || \ failure "cannot compile $testname-2.c" ./${testname} > ${testname}-btest echo "$mask_all --> $(( ${#cmd_list} + 1 )) bytes: '$cmd_list'" \ > ${testname}-bbase fpair="${testname}-bbase ${testname}-btest" cmp -s $fpair || \ failure "$testname $fpair run failed`echo diff $fpair`" cleanup ## Local Variables: ## mode: shell-script ## indent-tabs-mode: nil ## sh-indentation: 4 ## sh-basic-offset: 4 ## End: # end of str2m.test