Blob Blame History Raw
#! /bin/sh
#  -*- Mode: Shell-script -*-
# str2m.test --- test str2enum and str2mask functionality
#
# Author:            Bruce Korb <bkorb@gnu.org>
##
## 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 <http://www.gnu.org/licenses/>.
##
#
# ----------------------------------------------------------------------

. ./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 <sys/types.h>
	#include <inttypes.h>

	/** 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 <sys/types.h>
	#include <string.h>
	#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 <stdio.h>
	#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