[= AutoGen5 Template -*- Mode: C -*-

h
c

 (define base-name  "")
 (define BASE-NAME  "")
 (define element-type "")
 (define init-done  #f)
 (define is-64-bit  #f)
 (define is-array   #f)
 (define name-width 0)
 (define desc-width 0)
 (define bit-list   "")
 (define bit-name   "")

 (define id-name (lambda (sfx)
    (string-append
        prefix "_" (string-upcase! (string->c-name! (get "b-name"))) sfx
 )  ))

 (define mask-name (lambda (sfx)
    (string-append
        prefix "_" (string-upcase! (string->c-name! (get "m-name"))) sfx
 )  ))

=]
[= INVOKE preamble

=][=

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

PURPOSE:
   This template produces a header file with an enumeration of all the defined
   bits.  If the bit collection fits in a 32 or 64 bit word, then the bit values
   will also be #defined.  A set of macros will also be defined for manipulating
   the bit collections.  The interface to these macros is consistent, even if
   the implementation has to resort to arrays of words to hold all the bits.
   (That is, if there are more than 64 bits.)

   Unless suppressed, a .c file is also produced with two routines for convert-
   ing from a list of bit names to a bit collection and back again.

YOU SUPPLY:
   mask-name    This name is used to derive the typedef for the bit collection.
   prefix       a prefix appended before the bit names in the enumeration and
                bit definitions.  If omitted, "mask-name" is used.
   bit          the definition of each bit:
      b-name    the name for the bit.
      b-what    a short half-line description of the bit

   mask         a bit mask - a collection of the described bits.
                This is disabled if there are more than 64 bits.
      m-name    bit mask name
      b-name    a list of bit names participating in the mask
      m-inc     a previously defined mask name.  Its bits are included in this.

   un-mask      Identical to "mask", except that it consists of all the bits
                *except* the bits enumerated with "b-name" and "m-inc".

   no-code      Specify this to suppress the conversion routines.

THE TEMPLATE PRODUCES:
   <base-name>.h the header, with external declarations of the conversion
                 routines.

   <base-name>.c converts a space and/or comma separated list of names into
                 a bit collection, and back again.

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

CASE (suffix)       =][=

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

== h

=]
[=
 (shell "sedcmd='s/$/_val} +/;s/^/${/'")
 (make-header-guard "bit_mask") =]
#include <stdint.h>

typedef [=

 (define type-name (string-append base-name "_bits_t"))
 (define tmp
    (if (< (high-lim "bit") 32) (begin
        (set! element-type "uint32_t")
        "uint32_t %s_bits_t")
    (if (< (high-lim "bit") 64) (begin
        (set! element-type "uint64_t")
        (set! is-64-bit #t)
        "uint64_t %s_bits_t" )
      (begin
        (set! element-type "uint32_t")
        (set! is-array #t)
        (sprintf "uint32_t %%s_bits_t[%s]"
                 (shellf "mask_ct=`calc '( %d + 32 ) / 32'` ; echo $mask_ct"
                         (high-lim "bit"))  ) ))  ))

 (sprintf tmp base-name)

=];
typedef enum {[=

FOR bit     =][=

  (set! bit-name (string->c-name! (get "b-name")))
  (shellf
   "%1$s_val=`calc '2 ^ %2$d'`\nmask_val=`calc \"${mask_val} + ${%1$s_val}\"`"
   bit-name (for-index))

  (set! tmp (string-length bit-name))
  (if (> tmp name-width)
      (set! name-width tmp))
  (set! tmp (string-length (get "b-what")))
  (if (> tmp desc-width)
      (set! desc-width tmp)) =][=

ENDFOR bit  =][=

  (define define-width (+ name-width 6 (string-length prefix)))
  (define enum-fmt (sprintf "\n        %%-%ds =%%4d%%s /* %%-%ds */"
                            define-width desc-width))

=][=

FOR bit     =][=

  (sprintf enum-fmt (id-name "_ID") (for-index)
           (if (last-for?) " " ",") (get "b-what")) =][=
ENDFOR bit

= = = = = = = = = = = = = = = = =][=

IF (ag-fprintf 0 "\n} %s_enum_t;\n" base-name)
   (define def-fmt (sprintf "\n#define %%-%ds " define-width))

   (< (high-lim "bit") 32)      =][=

  INVOKE emit-word-macro  one = 'U'  mask-fmt = "%08XU" =][=

ELIF (< (high-lim "bit") 64)    =][=

  INVOKE emit-word-macro  one = 'ULL'  mask-fmt = "%016XULL" =][=

ELSE more than 64 bits          =][=

  INVOKE emit-multi-macros      =][=

ENDIF  how many bits            =][=

IF (if (exist? "extra-defs")
       (emit (string-append "\n\n" (get "extra-defs") "\n")))

   (not (exist? "no-code")) =]
/*
 *  Return a string containing the names of the bits set.
 */
extern char *
[= (. base-name) =]_names([= (. type-name) =] bits);

#define INV_[= (. BASE-NAME) =] -1
#define DUP_[= (. BASE-NAME) =] -2

/*
 *  Set the bits in "bits" as specified by the input string "str".
 *  If any names are untranslatable (not in the name list or are
 *  ambiguous in that they match the initial portion of more than
 *  one entry), it will return -1 or -2, respectively.
 *  Otherwise, it returns the number of bits set in "bits".
 */
extern int
[= (. base-name) =]_bits(
    [= (. type-name) =] * const bits,
    char const * str);
[= ENDIF =]
#endif /* [= (. header-guard)   =] */[=

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

== c


=]
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
[=

 (if (exist? "no-code") (out-delete))
 (ag-fprintf 0 "#include \"%s\"\n" header-file)
 (string-table-new "nm")
 (string-table-add "nm" "* INVALID *")
 (define ix 0)
 (define offset-list "")
 (define sorted-off  "")        =][=

FOR bit (for-from 0) (for-by 1) =][=

  (if (exist? "b-name")
      (begin
         (set! tmp         (string-downcase! (string->c-name! (get "b-name"))))
         (set! ix          (string-table-add "nm" tmp))
         (set! offset-list (string-append offset-list (sprintf "%d\n" ix)))
         (set! sorted-off  (string-append sorted-off
                           (sprintf "%-40s { %3d, %3d }\n" tmp ix (for-index))))
      )

      (set! offset-list (string-append offset-list "0\n" ))
  )         =][=

ENDFOR bit  =][=

 (emit-string-table "nm")
 (sprintf "\nchar *\n%1$s_names( %1$s_bits_t bits )\n{" base-name)
=]
    static int const nm_ixa[ [= (+ 1 (high-lim "bit")) =] ] = {
[=

 (define string-table-size (lambda (st-name)
    (hash-ref (hash-ref stt-table st-name) "current-index") ))

 (emit (shellf "columns -I8 -S, --spread=1 <<_EOF_\n%s_EOF_" offset-list))
=] };

    static char buf[ [= (+ (string-table-size "nm") (count "bit")) =] ];
    char * buf_p = buf;
    int ix = 0;
[=

IF (< (high-lim "bit") 64)

=]
    while (bits != 0) {
        if ((bits & 1) != 0) {
            char const * p = nm + nm_ixa[ix];

            if (buf_p > buf) {
                *(buf_p++) = ',';
                *(buf_p++) = ' ';
            }

            if (p == nm) {
            Oops:
                strncpy(buf_p, nm, sizeof (buf) - (buf_p - buf));
                break;
            }

            while ((*(buf_p++) = *(p++)) != '\0')   ;
            buf_p--;
        }
        bits >>= 1;
        if (++ix > [= (high-lim "bit") =]) {
            if (bits != 0)
                goto Oops;
            break;
        }
    }[=

ELSE  more than 64:

=]
    int bix     = 0;
    int bit_lim = 32;
    do  {
        uint32_t bit_word = bits[bix];
        int ix = bix * 32;

        while (bit_word != 0) {
            if ((bit_word & 1) != 0) {
                char const * p = nm + nm_ixa[ix];

                if (buf_p > buf) {
                    *(buf_p++) = ',';
                    *(buf_p++) = ' ';
                }

                if (p == nm) {
                Oops:
                    strncpy(buf_p, nm, sizeof (buf) - (buf_p - buf));
                    break;
                }

                while ((*(buf_p++) = *(p++)) != '\0')   ;
                buf_p--;
            }
            bit_word >>= 1;
            if (++ix > [= (high-lim "bit") =]) {
                if (bit_word != 0)
                    goto Oops;
                return buf;
            }
        }
    } while (++bix < [= `echo $mask_ct` =]);[=

ENDIF

=]

    return buf;
}

static int
str_to_id( char const * str, char const ** p_str )
{
    static char nm_buf[ [= (+ 1 name-width) =] ];
    int    res  = -1;
    int    part = 1;
    size_t len  = 0;

    /*
     *  Extract the lower cased name with '-' replaced with '_'
     */
    {
        char * p   = nm_buf;

        for (;;) {
            char ch = *(str++);
            switch (ch) {
            case '-':
                ch = '_';
                /* FALLTHROUGH */

            case '_':
                break;

            default:
                if (isupper(ch))
                    ch = tolower(ch);
                else if (! isalnum(ch)) {
                    str--;
                    goto have_name;
                }
            }

            if (++len > [= (. name-width) =])
                return -1;

            *(p++) = ch;
        } have_name :;

        *p = '\0';
        len = p - nm_buf;
        if (len == 0)
            return INV_[= (. BASE-NAME) =];
    }

    /*
     * Search the alphabetized table
     */
    do  {
        static struct {
            unsigned short const nm_off, val;
        } nm_ixa[ [= (count "bit") =] ] = {
[=
 (shellf (string-append
          "(sort | sed 's/.*{/{/' | columns -I8 -S, --spread=1)<<_EOF_\n"
          sorted-off "_EOF_"
 ))
=] };

        int av;
        int lo = 0;
        int hi = [= (- (count "bit") 1) =];

        /*
         *  Binary search for first match
         */
        do  {
            char const * p;
            int df;

            av = (lo + hi) / 2;
            p  = nm + nm_ixa[av].nm_off;
            df = strncmp(p, nm_buf, len);

            if (df == 0) {
                res = nm_ixa[av].val;
                if (p[len] == '\0')
                    part = 0;

                break;
            }

            if (df > 0)
                 hi = av - 1;
            else lo = av + 1;

        } while (lo <= hi);

        if (res < 0)
            return INV_[= (. BASE-NAME) =];

        if (part == 0)
            break;

        /*
         * Partial match.  Look for preceeding matches.  One may be full match.
         */
        lo = av;
        while (lo > 0) {
            char const * p = nm + nm_ixa[--lo].nm_off;
            int df = strncmp(p, nm_buf, len);
            if (df != 0)
                break;
            if (p[len] == '\0') {
                part = 0;
                res = nm_ixa[lo].val;
                break;
            }
            part++;
        }

        if (part > 1) {
            *p_str = nm_buf;
            return DUP_[= (. BASE-NAME) =];
        }

        if ((part == 0) || (av == [= (- (count "bit") 1) =]))
            break;

        /*
         * Look for a successor match.  No full match possible.
         */
        {
            char const * p = nm + nm_ixa[av+1].nm_off;
            int df = strncmp(p, nm_buf, len);
            if (df == 0) {
                *p_str = nm_buf;
                return DUP_[= (. BASE-NAME) =];
            }
        }
    } while (0);

    while (isspace(*str))  str++;
    *p_str = str;
    return res;
}

int
[=

# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

(. base-name) =]_bits(
    [= (. type-name) =] * const bits[= (if is-array "_p")=],
    char const * str)
{[=
  IF (. is-array) =]
    [= (. element-type) =] * const bits = (void*)bits_p;[=
  ENDIF =]
    int    ct  = 0;
    int    res = 0;

    memset(bits, '\0', sizeof([= (. type-name) =]));

    another_bit:

    while (isspace(*str) || (*str == ','))  str++;

    for (;;) {
        if (isdigit(*str)) {
            [=(. element-type) =] num =
                ([=(. element-type) =])strtoull(str, &str, 0);
            *bits |= num;
            ct += (num != 0);

        } else if (isalpha(*str)) {
            res = str_to_id(str, &str);
            if (res < 0) {
                if (res == DUP_[= (. BASE-NAME) =])
                    fprintf(stderr, "duplicate matches for '%s'\n", str);
                goto fail_exit;
            }
            ct++;
[=

 IF (. is-array)    =]
            bits[res/32] |= 1 << (res & 0x1F);[=
 ELIF (. is-64-bit) =]
            *bits |= 1ULL << res;[=
 ELSE =]
            *bits |= 1 << res;[=
 ENDIF

=]
        } else switch (*str) {
        case ',':
            goto another_bit;

        case '\0':
            return ct;

        default:
            res = INV_[= (. BASE-NAME) =];
            goto fail_exit;
        }
    }

    fail_exit:
    memset(bits, '\0', sizeof(*bits));
    return res;
}

#ifdef TEST_BITS

static char const bit_names[] =
[=
(kr-string (string-append "The known " base-name " bit names are:\n"
  (shellf (string-append
    "(sort | columns -I2 --spread=1\n) <<_EOF_\n"
    (string-downcase! (join "\n" (stack "bit.b-name")))
    "\n_EOF_"))
  "\n" ))
 =];

int
main( int argc, char** argv )
{
    static char const fmt_z[] = "'%s' yields: %s\n";
    [= (. type-name) =] bts;
    if (argc != 2) {
        fputs(bit_names, stderr);
        return 1;
    }
    {
        int ct = [= (. base-name) =]_bits(&bts, argv[1]);
        if (ct <= 0) {
            char const * pz;
            switch (ct) {
            case 0: pz = "no results"; break;
            case INV_[= (. BASE-NAME) =]: pz = "invalid name"; break;
            case DUP_[= (. BASE-NAME) =]: pz = "multiple match"; break;
            }
            fprintf(stderr, fmt_z, argv[1], pz);
            fputs(bit_names, stderr);
            return 1;
        }
    }
    {
        char * pz = [= (. base-name) =]_names( bts );
        printf(fmt_z, argv[1], pz);
    }
    return 0;
}
#endif
[=

ESAC                =]
/* end of [= (out-name) =] */
[=#

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

DEFINE  preamble    =][=

 (if (not init-done) (begin

     (shell "calc() { bc <<_EOF_
$*
_EOF_
}
         mask_val=0")
     (if (not (exist? "mask-name"))
         (error "no defined bit mask name"))
     (set! init-done #t)
     (set! base-name (string-downcase! (string->c-name! (get "mask-name"))))
     (set! BASE-NAME (string-upcase base-name))
     (set! prefix    (string-upcase (string->c-name!
                     (if (exist? "prefix") (get "prefix") base-name) )))
 )   )

 (dne " * " "/* ")  =]
 */
[=

ENDDEF  preamble

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

DEFINE  emit-bit-list       =][=

    (if (exist? "b-name")
        (set! bit-list (string-append (join "\n" (stack "b-name")) "\n"))
        (set! bit-list "")
    )   =][=

    FOR m-inc   =][=
       (set! tmp (string->c-name! (get "m-inc")))
       (set! bit-list (string-append bit-list
                      (shellf "echo \"${%s}\"" tmp) "\n"))
                =][=
    ENDFOR m-inc=][=

    (set! bit-list (string->c-name! bit-list))
    (shellf "%s='%s'" (string->c-name! (get "m-name")) bit-list)

    (emit (shell (string-append

       "(sort -u | columns -I8 --spread=1 -S' |' --format=" prefix "_%s_BIT "
                "--line=' \\'\n) <<\\_EOF_\n"
       (string-upcase bit-list)
       "_EOF_"

    )))

    (shell (string-append
     "sum=`(sort -u | \
	sed -e \"${sedcmd}\"\n) <<\\_EOF_\n"
     bit-list
     "_EOF_\n`\n"
     "sum=`eval calc ${sum} 0`\n"
     "printf ' \\\\\\n        /* 0x%016X */\\n' ${sum}"
    ))
=][=

ENDDEF  emit-bit-list

= = = = = = = = = = = = = = = = = = = = =][=

DEFINE  emit-word-macro     =][=

  (sprintf def-fmt (string-append prefix "_NO_BITS")) =]0[= one =][=

  FOR bit       =][=

    (sprintf def-fmt (id-name "_BIT")) =](1[=one=] << [= (id-name "_ID") =])[=

  ENDFOR        =][=

  (ag-fprintf 0 def-fmt (string-append BASE-NAME "_MASK"))
  (shellf "printf 0x%s ${mask_val}" (get "mask-fmt"))

  =][=

  FOR mask

    =]
[= (sprintf def-fmt (mask-name "_MASK")) =]( \
[= INVOKE emit-bit-list =] )[=

  ENDFOR mask   =][=

  FOR un-mask

    =]
[= (sprintf def-fmt (mask-name "_MASK")) =]( [=
   (string-append BASE-NAME "_MASK")     =] & ~( \
[= INVOKE emit-bit-list =] ))[=

  ENDFOR un-mask=][=

  (if (exist? "defined") (string-append "\n\n" (get "defined")))

=]

#define   SET_[= (. BASE-NAME) =](_m, _b) \
              do { (_m) |= 1[= one =] << _b; } while (0)
#define CLEAR_[= (. BASE-NAME) =](_m, _b) \
              do { (_m) &= ~(1[= one =] << _b); } while (0)
#define  TEST_[= (. BASE-NAME) =](_m, _b) (((_m) & (1[= one =] << _b)) != 0)
#define   AND_[= (. BASE-NAME) =](_d, _s1, _s2) \
              do { (_d) = (_s1) & (_s2); } while (0)
#define    OR_[= (. BASE-NAME) =](_d, _s1, _s2) \
              do { (_d) = (_s1) | (_s2); } while (0)
#define   XOR_[= (. BASE-NAME) =](_d, _s1, _s2) \
              do { (_d) = (_s1) ^ (_s2); } while (0)
#define   NOT_[= (. BASE-NAME) =](_d, _s) \
              do { (_d) = ~(_s); } while (0)
[=
ENDDEF  emit-word-macro

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =][=

DEFINE  emit-loop-macro

=][=

  (sprintf "#define %5s_%s" (get "mac-name") BASE-NAME) =][=

  CASE op-code  =][=
  == "~"        =](_d, _s)[=       (set! tmp one-arg-op)=][=
  *             =](_d, _s1, _s2)[= (set! tmp two-arg-op)=][=
  ESAC op-code  =] \
              [= (. iterate) =] \
              [= (sprintf tmp (get "op-code"))          =][=

ENDDEF  emit-loop-macro

= = = = = = = = = = = = = = = = = = = =][=

DEFINE  emit-multi-macros

=][=

defined

=]

#define   SET_[=
(define iterate (sprintf "do { int _ix_ = 0; for (;_ix_ < %s; _ix_++) {"
                         (shell "echo $mask_ct")  ))
(define two-arg-op "(_d)[_ix_] = (_s1)[_ix_] %s (_s2)[_ix_]; } } while (0)")
(define one-arg-op "(_d)[_ix_] = %s(_s)[_ix_]; } } while (0)")

                     BASE-NAME =](_m, _b) \
              do { (_m)[(_b)/32] |= 1U << ((_b) % 32); } while (0)
#define CLEAR_[= (. BASE-NAME) =](_m, _b) \
              do { (_m)[(_b)/32] &= ~(1U << ((_b) % 32)); } while (0)
#define  TEST_[= (. BASE-NAME)   =]( _m, _b) \
              (((_m)[(_b)/32] & (1U << ((_b) % 32))) != 0)
[= INVOKE emit-loop-macro op-code = "&"  mac-name = AND  =]
[= INVOKE emit-loop-macro op-code = "|"  mac-name =  OR  =]
[= INVOKE emit-loop-macro op-code = "^"  mac-name = XOR  =]
[= INVOKE emit-loop-macro op-code = "~"  mac-name = NOT  =]
[=

ENDDEF  emit-multi-macros   =][=

# End of bits.tpl  \=]