2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A * diagcode library, Sun Private API (PSARC/2004/601)
2N/A *
2N/A * undocumented debugging interface:
2N/A * set environment variable _FM_DC_DEBUG for debug prints to stderr.
2N/A * set it to 1 for extended error messages only.
2N/A * set it to 2 to include success info too on interesting functions.
2N/A * set it to 3 to include success info on trivial functions too.
2N/A * note that this environment variable is only examined in fm_dc_opendict().
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <alloca.h>
2N/A#include <errno.h>
2N/A
2N/A#include <fm/diagcode.h>
2N/A
2N/A/* private (opaque to callers) handle information */
2N/Astruct fm_dc_handle {
2N/A const char *dictname;
2N/A FILE *fp;
2N/A unsigned maxkey;
2N/A int version;
2N/A int debug;
2N/A /* name/value pairs from .dict header */
2N/A struct fm_dc_prop {
2N/A struct fm_dc_prop *next;
2N/A const char *lhs;
2N/A const char *rhs;
2N/A } *props;
2N/A};
2N/A
2N/A/*
2N/A * parameters of the various sizes of diagcodes
2N/A *
2N/A * table must be in ascending order from smallest databits value to largest.
2N/A * when faced with more databits than the last entry, we know we have
2N/A * something that won't fit into a diagcode.
2N/A */
2N/Astatic const struct info {
2N/A int databits; /* number of bits used to hold dictionary value */
2N/A int numx; /* number of digits (also called X's) in code */
2N/A int csumbits; /* number of bits used for checksum */
2N/A int sizeval; /* value encoded into "size" field of code */
2N/A unsigned long long offset; /* databits==0 stands for this value */
2N/A} Info[] = {
2N/A /* diagcode is: dictname-XXXX-XX */
2N/A { 21, 6, 5, 0, 0ULL },
2N/A
2N/A /* diagcode is: dictname-XXXX-XXXX-XX */
2N/A { 38, 10, 8, 1, 2097152ULL },
2N/A
2N/A /* diagcode is: dictname-XXXX-XXXX-XXXX-XX */
2N/A { 55, 14, 11, 2, 274880004096ULL },
2N/A
2N/A /* diagcode is: dictname-XXXX-XXXX-XXXX-XXXX-XX */
2N/A { 72, 18, 14, 3, 36029071898968064ULL }
2N/A};
2N/A#define MAXDATABITS 72 /* highest entry in table above */
2N/A#define MAXCODELEN 25 /* big enough to hold the X's, dashes, and \0 */
2N/A
2N/A/* forward references for functions private to this file */
2N/Atypedef struct bitv bitv;
2N/Astatic const struct info *dictval2info(const bitv *bv);
2N/Astatic const struct info *numx2info(int numx);
2N/Astatic void sortkey(const char *key[]);
2N/Astatic const char *keymatch(const char *linebuf, const char *key[]);
2N/Astatic int buildcode(fm_dc_handle_t *dhp, const char *rhsp,
2N/A char *code, size_t maxcode, char *debugstr);
2N/Astatic bitv *code2dictval(fm_dc_handle_t *dhp, const char *code);
2N/Astruct parsestate {
2N/A char *parseptr; /* next unparsed character in buffer */
2N/A char *rhsp; /* rhs associated with last lhs (or NULL) */
2N/A};
2N/Astatic void startparse(struct parsestate *ps, char *ptr);
2N/Astatic char *nextlhs(struct parsestate *ps);
2N/Astatic char *nextrhs(struct parsestate *ps);
2N/Astatic bitv *bitv_alloc(void);
2N/Astatic void bitv_free(bitv *bv);
2N/Astatic void bitv_shift(bitv *bv, unsigned bits);
2N/Astatic void bitv_setlo(bitv *bv, unsigned bits, unsigned val);
2N/Astatic void bitv_shiftin(bitv *bv, unsigned bits, unsigned val);
2N/Astatic void bitv_shiftinv(bitv *bv, unsigned bits, const bitv *inbv);
2N/Astatic int bitv_bits(const bitv *bv);
2N/Astatic unsigned bitv_chunk(const bitv *bv, unsigned limbit, unsigned lobit);
2N/Astatic int bitv_mul(bitv *bv, unsigned long long val);
2N/Astatic int bitv_add(bitv *bv, unsigned long long val);
2N/Astatic int bitv_sub(bitv *bv, unsigned long long val);
2N/Astatic int bitv_ge(const bitv *bv, unsigned long long val);
2N/Astatic bitv *bitv_strparse(const char *s, int bits);
2N/Astatic int bitv_cmp(const bitv *bv1, const bitv *bv2);
2N/Astatic void crc(unsigned long *crcp, unsigned val);
2N/A
2N/A#define DICTMAXLINE 10240 /* maximum expected dictionary line length */
2N/A
2N/A#define MAXDEBUGSTR 100 /* for debug messages */
2N/A
2N/Astatic const char Suffix[] = ".dict"; /* suffix on dictionary filename */
2N/Astatic const char Defaultpath[] = "/usr/lib/fm/dict";
2N/Astatic const char Debugenv[] = "_FM_DC_DEBUG"; /* debug environment var */
2N/A
2N/A/* properties we look for at top of dictionary */
2N/Astatic const char Header[] = "FMDICT: ";
2N/Astatic const char Name[] = "name";
2N/Astatic const char Version[] = "version";
2N/Astatic const char Maxkey[] = "maxkey";
2N/A
2N/A/* the alphabet used to encode information in a diagcode (base32 digits) */
2N/Astatic const char Alphabet[] = "0123456789ACDEFGHJKLMNPQRSTUVWXY";
2N/A
2N/A/* open a dictionary, return opaque handle */
2N/Afm_dc_handle_t *
2N/Afm_dc_opendict(int version, const char *dirpath, const char *dictname)
2N/A{
2N/A int debug = 0; /* set by environment variable */
2N/A char *debugstr = ""; /* error path debug prefix text */
2N/A fm_dc_handle_t *dhp = NULL;
2N/A char *fname; /* full dict file name */
2N/A char linebuf[DICTMAXLINE]; /* line read from dict */
2N/A int line = 0; /* line number in dict */
2N/A unsigned prop_version = 0; /* version property from dict */
2N/A char *prop_name = ""; /* name property from dict */
2N/A char *lhsp; /* prop left-hand-side */
2N/A char *rhsp; /* prop right-hand-side */
2N/A struct parsestate pstate; /* for startparse(), nextlhs(), etc */
2N/A
2N/A /* undocumented flag, given via environment variable */
2N/A if ((rhsp = getenv(Debugenv)) != NULL)
2N/A debug = atoi(rhsp);
2N/A
2N/A if (debug > 1)
2N/A (void) fprintf(stderr,
2N/A "fm_dc_opendict: ver %d path \"%s\" dict \"%s\": ",
2N/A version, (dirpath == NULL) ? "NULL" : dirpath, dictname);
2N/A else if (debug)
2N/A debugstr = "fm_dc_opendict: "; /* used in error paths */
2N/A
2N/A /* verify caller expects an API version we support */
2N/A if (version < 0 || version > FM_DC_VERSION) {
2N/A if (debug)
2N/A (void) fprintf(stderr, "%sENOTSUP ver not in [0-%d]\n",
2N/A debugstr, FM_DC_VERSION);
2N/A errno = ENOTSUP;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* caller can pass in NULL for default dirpath */
2N/A if (dirpath == NULL)
2N/A dirpath = Defaultpath;
2N/A
2N/A /*
2N/A * allocate buffer for dirpath, slash, dictname, and suffix
2N/A * (sizeof (Suffix) includes the null).
2N/A */
2N/A fname = alloca(strlen(dirpath) + 1 +
2N/A strlen(dictname) + sizeof (Suffix));
2N/A
2N/A /*
2N/A * allocate the handle.
2N/A *
2N/A * allocate the dictname copy kept in the handle.
2N/A *
2N/A * if any of these fail, send back ENOMEM.
2N/A */
2N/A if ((dhp = malloc(sizeof (*dhp))) == NULL ||
2N/A (dhp->dictname = strdup(dictname)) == NULL) {
2N/A if (dhp)
2N/A free(dhp);
2N/A if (debug)
2N/A (void) fprintf(stderr, "%sENOMEM\n", debugstr);
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* initialize the handle */
2N/A (void) strcpy(fname, dirpath);
2N/A (void) strcat(fname, "/");
2N/A (void) strcat(fname, dictname);
2N/A (void) strcat(fname, Suffix);
2N/A dhp->fp = NULL;
2N/A dhp->maxkey = 0;
2N/A dhp->version = version;
2N/A dhp->debug = debug;
2N/A dhp->props = NULL;
2N/A
2N/A /* open the dictionary */
2N/A if (debug > 1)
2N/A (void) fprintf(stderr, "\"%s\": ", fname);
2N/A if ((dhp->fp = fopen(fname, "r")) == NULL) {
2N/A int oerrno = errno; /* fopen() set errno to something */
2N/A
2N/A if (debug > 1)
2N/A perror("fopen");
2N/A else if (debug) {
2N/A (void) fprintf(stderr, "%s%s: ", debugstr, fname);
2N/A errno = oerrno;
2N/A perror("fopen");
2N/A }
2N/A fm_dc_closedict(dhp);
2N/A errno = oerrno;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* pull in the header line and parse it */
2N/A while (fgets(linebuf, DICTMAXLINE, dhp->fp) != NULL) {
2N/A line++;
2N/A if (*linebuf == '\n' || *linebuf == '#')
2N/A continue;
2N/A
2N/A /* first non-comment, non-blank line must be header */
2N/A if (strncmp(linebuf, Header, sizeof (Header) - 1)) {
2N/A fm_dc_closedict(dhp);
2N/A if (debug)
2N/A (void) fprintf(stderr,
2N/A "%sEINVAL: line %d: header expected.\n",
2N/A debugstr, line);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* just wanted header line for now */
2N/A break;
2N/A }
2N/A
2N/A /* walk through name=value pairs in line after Header string */
2N/A startparse(&pstate, &linebuf[sizeof (Header) - 1]);
2N/A while ((lhsp = nextlhs(&pstate)) != NULL) {
2N/A struct fm_dc_prop *propp;
2N/A
2N/A if ((rhsp = nextrhs(&pstate)) == NULL) {
2N/A if (debug)
2N/A (void) fprintf(stderr, "%sEINVAL "
2N/A "%s prop has no value\n", debugstr, lhsp);
2N/A fm_dc_closedict(dhp);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A propp = malloc(sizeof (*propp));
2N/A if (propp == NULL ||
2N/A (propp->lhs = strdup(lhsp)) == NULL ||
2N/A (propp->rhs = strdup(rhsp)) == NULL) {
2N/A if (debug)
2N/A (void) fprintf(stderr, "%sENOMEM\n", debugstr);
2N/A if (propp != NULL) {
2N/A if (propp->lhs != NULL)
2N/A free((void *) propp->lhs);
2N/A free((void *) propp);
2N/A }
2N/A fm_dc_closedict(dhp);
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A propp->next = dhp->props;
2N/A dhp->props = propp;
2N/A
2N/A if (strcmp(lhsp, Name) == 0)
2N/A prop_name = rhsp;
2N/A else if (strcmp(lhsp, Version) == 0)
2N/A prop_version = strtoul(rhsp, NULL, 0);
2N/A else if (strcmp(lhsp, Maxkey) == 0)
2N/A dhp->maxkey = strtoul(rhsp, NULL, 0);
2N/A }
2N/A
2N/A /*
2N/A * require version 1, expected dict name, and maxkey values
2N/A * (note we use "1" here and not FM_DC_VERSION because this code
2N/A * implements version 1, so the check below should not float to
2N/A * newer version numbers if the header file defines them.)
2N/A */
2N/A if (prop_version != 1UL || strcmp(prop_name, dictname) ||
2N/A dhp->maxkey == 0) {
2N/A fm_dc_closedict(dhp);
2N/A if (debug)
2N/A (void) fprintf(stderr,
2N/A "%sEINVAL ver %d name \"%s\" maxkey %d\n",
2N/A debugstr, prop_version, prop_name, dhp->maxkey);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A if (debug > 1)
2N/A (void) fprintf(stderr, "fm_dc_opendict: dhp 0x%p\n",
2N/A (void *)dhp);
2N/A return (dhp);
2N/A}
2N/A
2N/A/* close a dictionary */
2N/Avoid
2N/Afm_dc_closedict(fm_dc_handle_t *dhp)
2N/A{
2N/A struct fm_dc_prop *props;
2N/A struct fm_dc_prop *oprops;
2N/A
2N/A if (dhp->debug > 1)
2N/A (void) fprintf(stderr, "fm_dc_closedict: dhp 0x%p\n",
2N/A (void *)dhp);
2N/A if (dhp->fp)
2N/A (void) fclose(dhp->fp);
2N/A
2N/A free((void *) dhp->dictname);
2N/A
2N/A props = dhp->props;
2N/A while (props) {
2N/A if (props->lhs != NULL)
2N/A free((void *) props->lhs);
2N/A if (props->rhs != NULL)
2N/A free((void *) props->rhs);
2N/A oprops = props;
2N/A props = props->next;
2N/A free((void *) oprops);
2N/A }
2N/A
2N/A free(dhp);
2N/A}
2N/A
2N/A/* return maximum length (in bytes) of diagcodes for a given dictionary */
2N/Asize_t
2N/Afm_dc_codelen(fm_dc_handle_t *dhp)
2N/A{
2N/A size_t len = strlen(dhp->dictname);
2N/A
2N/A /* only one version so far, so dhp->version isn't checked */
2N/A
2N/A if (dhp->debug > 2)
2N/A (void) fprintf(stderr, "fm_dc_codelen: dhp 0x%p: %d\n",
2N/A (void *)dhp, (int)(len + MAXCODELEN));
2N/A return (len + MAXCODELEN);
2N/A}
2N/A
2N/A/* return number of strings in key for a given dictionary */
2N/Aint
2N/Afm_dc_maxkey(fm_dc_handle_t *dhp)
2N/A{
2N/A /* only one version so far, so dhp->version isn't checked */
2N/A
2N/A /* this interface counts the NULL entry */
2N/A if (dhp->debug > 2)
2N/A (void) fprintf(stderr, "fm_dc_maxkey: dhp 0x%p: maxkey %d\n",
2N/A (void *)dhp, dhp->maxkey + 1);
2N/A return (dhp->maxkey + 1);
2N/A}
2N/A
2N/A/* given a key, construct a diagcode */
2N/Aint
2N/Afm_dc_key2code(fm_dc_handle_t *dhp,
2N/A const char *key[], char *code, size_t maxcode)
2N/A{
2N/A char *debugstr = ""; /* error path debug prefix text */
2N/A int line = 0; /* line number in dict */
2N/A char linebuf[DICTMAXLINE]; /* line read from dict */
2N/A const char *rhsp; /* right-hand-side of entry */
2N/A
2N/A /* only one version so far, so dhp->version isn't checked */
2N/A
2N/A if (dhp->debug > 1) {
2N/A int nel;
2N/A
2N/A (void) fprintf(stderr,
2N/A "fm_dc_key2code: dhp 0x%p maxcode %lu ", (void *)dhp,
2N/A (ulong_t)maxcode);
2N/A for (nel = 0; key[nel]; nel++)
2N/A (void) fprintf(stderr, "\"%s\" ", key[nel]);
2N/A } else if (dhp->debug)
2N/A debugstr = "fm_dc_key2code: ";
2N/A
2N/A /* sort the keys */
2N/A sortkey(key);
2N/A
2N/A rewind(dhp->fp);
2N/A
2N/A while (fgets(linebuf, DICTMAXLINE, dhp->fp) != NULL) {
2N/A line++;
2N/A if (*linebuf == '\n' || *linebuf == '#')
2N/A continue;
2N/A
2N/A /* first non-comment, non-blank line must be header */
2N/A if (strncmp(linebuf, Header, sizeof (Header) - 1) == 0)
2N/A continue;
2N/A
2N/A if ((rhsp = keymatch(linebuf, key)) != NULL) {
2N/A char ndebugstr[MAXDEBUGSTR];
2N/A
2N/A if (dhp->debug > 1)
2N/A (void) fprintf(stderr, "match line %d: ", line);
2N/A else {
2N/A (void) snprintf(ndebugstr, MAXDEBUGSTR,
2N/A "fm_dc_key2code: dictionary line %d",
2N/A line);
2N/A debugstr = ndebugstr;
2N/A }
2N/A
2N/A return (buildcode(dhp, rhsp, code, maxcode, debugstr));
2N/A }
2N/A }
2N/A
2N/A /* no match */
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr, "%sENOMSG no match\n", debugstr);
2N/A errno = ENOMSG;
2N/A return (-1);
2N/A}
2N/A
2N/A/* given a diagcode, return the key (array of strings) */
2N/Aint
2N/Afm_dc_code2key(fm_dc_handle_t *dhp, const char *code,
2N/A char *key[], int maxkey)
2N/A{
2N/A char *debugstr = ""; /* error path debug prefix text */
2N/A int line = 0;
2N/A char linebuf[DICTMAXLINE];
2N/A bitv *dictval;
2N/A
2N/A /* only one version so far, so dhp->version isn't checked */
2N/A
2N/A if (dhp->debug > 1)
2N/A (void) fprintf(stderr,
2N/A "fm_dc_code2key: dhp 0x%p code \"%s\" maxkey %d: ",
2N/A (void *)dhp, code, maxkey);
2N/A else if (dhp->debug)
2N/A debugstr = "fm_dc_code2key: ";
2N/A
2N/A /* convert code back to bit vector */
2N/A if ((dictval = code2dictval(dhp, code)) == NULL) {
2N/A /* code2dictval() sets errno */
2N/A if (dhp->debug) {
2N/A int oerrno = errno;
2N/A
2N/A /* handle expected types without printing a number */
2N/A if (errno == ENOMEM)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM code2dictval\n",
2N/A debugstr);
2N/A else if (errno == EINVAL)
2N/A (void) fprintf(stderr,
2N/A "%sEINVAL code2dictval\n",
2N/A debugstr);
2N/A else
2N/A (void) fprintf(stderr,
2N/A "%scode2dictval error %d\n",
2N/A debugstr, oerrno);
2N/A errno = oerrno;
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A rewind(dhp->fp);
2N/A
2N/A while (fgets(linebuf, DICTMAXLINE, dhp->fp) != NULL) {
2N/A char *ptr;
2N/A bitv *thisval;
2N/A char *beginp;
2N/A char *endp;
2N/A int nel;
2N/A
2N/A line++;
2N/A if (*linebuf == '\n' || *linebuf == '#')
2N/A continue;
2N/A
2N/A /* first non-comment, non-blank line must be header */
2N/A if (strncmp(linebuf, Header, sizeof (Header) - 1) == 0)
2N/A continue;
2N/A
2N/A if ((ptr = strchr(linebuf, '=')) == NULL)
2N/A continue; /* ignore malformed entries */
2N/A
2N/A *ptr++ = '\0';
2N/A
2N/A /* pull in value from dictionary */
2N/A if ((thisval = bitv_strparse(ptr, MAXDATABITS)) == NULL) {
2N/A /* bitv_strparse() sets errno */
2N/A if (errno == ENOMEM) {
2N/A bitv_free(dictval);
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM bitv_strparse\n",
2N/A debugstr);
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A /* other than ENOMEM, trudge on... */
2N/A continue;
2N/A }
2N/A
2N/A if (bitv_cmp(thisval, dictval)) {
2N/A bitv_free(thisval);
2N/A continue;
2N/A }
2N/A
2N/A /* if we got here, we found the match */
2N/A bitv_free(thisval);
2N/A bitv_free(dictval);
2N/A beginp = linebuf;
2N/A nel = 0;
2N/A for (;;) {
2N/A while (*beginp && isspace(*beginp))
2N/A beginp++;
2N/A if (*beginp == '\0') {
2N/A /* all done */
2N/A key[nel] = NULL;
2N/A return (0);
2N/A }
2N/A if (nel >= maxkey - 1) {
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM maxkey %d\n",
2N/A debugstr, maxkey);
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A for (endp = beginp; *endp && !isspace(*endp); endp++)
2N/A ;
2N/A if (*endp)
2N/A *endp++ = '\0';
2N/A if ((key[nel++] = strdup(beginp)) == NULL) {
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM strdup\n", debugstr);
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A beginp = endp;
2N/A }
2N/A }
2N/A
2N/A bitv_free(dictval);
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr, "%sENOMSG\n", debugstr);
2N/A errno = ENOMSG;
2N/A return (-1);
2N/A}
2N/A
2N/A/* return the right-hand side of a names property from the dict header */
2N/Aconst char *
2N/Afm_dc_getprop(fm_dc_handle_t *dhp, const char *name)
2N/A{
2N/A struct fm_dc_prop *props;
2N/A
2N/A /* only one version so far, so dhp->version isn't checked */
2N/A
2N/A if (dhp->debug > 2)
2N/A (void) fprintf(stderr, "fm_dc_getprop: dhp 0x%p: \"%s\"",
2N/A (void *)dhp, name);
2N/A
2N/A for (props = dhp->props; props; props = props->next)
2N/A if (strcmp(name, props->lhs) == 0)
2N/A break;
2N/A
2N/A if (dhp->debug > 2)
2N/A (void) fprintf(stderr, "= \"%s\"\n",
2N/A (props == NULL) ? "NULL" : props->rhs);
2N/A
2N/A return ((props == NULL) ? NULL : props->rhs);
2N/A}
2N/A
2N/A/* find the appropriate diagcode format for a given dictval */
2N/Astatic const struct info *
2N/Adictval2info(const bitv *bv)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < sizeof (Info) / sizeof (*Info) - 1; i++)
2N/A if (!bitv_ge(bv, Info[i + 1].offset))
2N/A return (&Info[i]);
2N/A
2N/A /* return largest format */
2N/A return (&Info[sizeof (Info) / sizeof (*Info) - 1]);
2N/A}
2N/A
2N/A/* lookup the diagcode parameters given the number of X's used */
2N/Astatic const struct info *
2N/Anumx2info(int numx)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < sizeof (Info) / sizeof (*Info); i++)
2N/A if (numx == Info[i].numx)
2N/A return (&Info[i]);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/* for use with qsort() */
2N/Astatic int
2N/Amycmp(const void *a, const void *b)
2N/A{
2N/A return (strcmp(*(char **)a, *(char **)b));
2N/A}
2N/A
2N/A/*
2N/A * sortkey -- make sure key[] array is lexically sorted and without repeats
2N/A */
2N/Astatic void
2N/Asortkey(const char *key[])
2N/A{
2N/A int nel;
2N/A int srci; /* source index when iterating through key[] */
2N/A int dsti; /* dest index when storing elements in key[] */
2N/A
2N/A /* count the number of elements in key[] */
2N/A for (nel = 0; key[nel]; nel++)
2N/A ;
2N/A
2N/A if (nel < 2)
2N/A return; /* nothing to sort */
2N/A
2N/A qsort((void *)key, nel, sizeof (char *), mycmp);
2N/A
2N/A /* go through array and remove repeats */
2N/A dsti = 1;
2N/A for (srci = 1; srci < nel; srci++)
2N/A if (strcmp(key[srci], key[dsti - 1]) != 0)
2N/A key[dsti++] = key[srci];
2N/A key[dsti] = NULL;
2N/A}
2N/A
2N/A/*
2N/A * keymatch -- check for matching line from the dictionary
2N/A *
2N/A * assumes that the key[] array has already been lexically sorted.
2N/A * returns NULL if no match, otherwise pointer to first character of RHS.
2N/A */
2N/Astatic const char *
2N/Akeymatch(const char *linebuf, const char *key[])
2N/A{
2N/A int keynum = 0;
2N/A const char *ptr;
2N/A
2N/A while (linebuf) {
2N/A /* skip any initial whitespace in front of name */
2N/A while (*linebuf && isspace(*linebuf))
2N/A linebuf++;
2N/A
2N/A ptr = key[keynum];
2N/A
2N/A if (ptr == NULL && *linebuf == '=') {
2N/A /* match */
2N/A linebuf++;
2N/A while (*linebuf && isspace(*linebuf))
2N/A linebuf++;
2N/A return (linebuf);
2N/A } else if (ptr == NULL)
2N/A return (NULL); /* dict had more strings for key */
2N/A
2N/A /* match the string */
2N/A while (*linebuf)
2N/A if (*ptr == '\0') {
2N/A if (isspace(*linebuf) || *linebuf == '=')
2N/A break; /* match */
2N/A else
2N/A return (NULL); /* dict string longer */
2N/A } else if (*linebuf != *ptr)
2N/A return (NULL); /* string don't match */
2N/A else {
2N/A linebuf++;
2N/A ptr++;
2N/A }
2N/A
2N/A keynum++;
2N/A }
2N/A
2N/A return (NULL); /* no match */
2N/A}
2N/A
2N/A/*
2N/A * buildcode -- given the val from the dictionary, create the diagcode
2N/A */
2N/Astatic int
2N/Abuildcode(fm_dc_handle_t *dhp, const char *rhsp,
2N/A char *code, size_t maxcode, char *debugstr)
2N/A{
2N/A char *codebegin = code; /* remember start of code buffer */
2N/A const struct info *infop; /* Info[] table entry */
2N/A unsigned long csum = 0; /* checksum (CRC) of diagcode */
2N/A const char *ptr;
2N/A bitv *dictval; /* value from dictionary */
2N/A bitv *allbits; /* assembled diagcode in binary */
2N/A int bit; /* for looping through bits */
2N/A int limbit; /* upper bit limit when looping */
2N/A
2N/A /* sanity check that buffer is large enough for diagcode */
2N/A if (maxcode < fm_dc_codelen(dhp)) {
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM maxcode %lu < codelen %lu\n",
2N/A debugstr, (ulong_t)maxcode,
2N/A (ulong_t)fm_dc_codelen(dhp));
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A
2N/A /* handle dictname part of checksum */
2N/A for (ptr = dhp->dictname; *ptr; ptr++) {
2N/A crc(&csum, (unsigned)*ptr);
2N/A *code++ = *ptr;
2N/A }
2N/A
2N/A /* pull in value from dictionary */
2N/A if ((dictval = bitv_strparse(rhsp, MAXDATABITS)) == NULL) {
2N/A /* bitv_strparse() sets errno */
2N/A if (dhp->debug) {
2N/A int oerrno = errno;
2N/A
2N/A /* handle expected types without printing a number */
2N/A if (errno == ENOMEM)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM bitv_strparse\n",
2N/A debugstr);
2N/A else if (errno == ERANGE)
2N/A (void) fprintf(stderr,
2N/A "%sERANGE bitv_strparse\n",
2N/A debugstr);
2N/A else
2N/A (void) fprintf(stderr,
2N/A "%sbitv_strparse error %d\n",
2N/A debugstr, oerrno);
2N/A errno = oerrno;
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A /* determine which format of code we're using */
2N/A infop = dictval2info(dictval);
2N/A
2N/A /* subtract off the offset appropriate for format of code */
2N/A if (dhp->debug > 3)
2N/A (void) fprintf(stderr,
2N/A "%ssubtract offset %llu\n", debugstr, infop->offset);
2N/A if (bitv_sub(dictval, infop->offset) < 0) {
2N/A /*
2N/A * this "cannot happen" since code format was chosen
2N/A * so that offset will be smaller than dictval, and
2N/A * dictval cannot be out of range since bitv_strparse()
2N/A * should have caught it.
2N/A */
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sERANGE from bitv_sub\n", debugstr);
2N/A bitv_free(dictval);
2N/A errno = ERANGE;
2N/A return (-1);
2N/A }
2N/A
2N/A /* assemble all the bits for the diagcode */
2N/A if ((allbits = bitv_alloc()) == NULL) {
2N/A bitv_free(dictval);
2N/A if (dhp->debug)
2N/A (void) fprintf(stderr,
2N/A "%sENOMEM from bitv_alloc\n", debugstr);
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * construct the full diagcode by shifting in information:
2N/A * - 2 bit code type, set to 01
2N/A * - 2 bit size field
2N/A * - the databits of the dictionary code itself
2N/A */
2N/A
2N/A bitv_shiftin(allbits, 2, 1);
2N/A bitv_shiftin(allbits, 2, infop->sizeval);
2N/A bitv_shiftinv(allbits, infop->databits, dictval);
2N/A
2N/A /* insert zeros for checksum */
2N/A bitv_shiftin(allbits, infop->csumbits, 0);
2N/A
2N/A /* compute checksum */
2N/A limbit = infop->numx * 5;
2N/A for (bit = 0; bit < infop->numx; bit++) {
2N/A crc(&csum, bitv_chunk(allbits, limbit, limbit - 5));
2N/A limbit -= 5;
2N/A }
2N/A
2N/A /* insert the computed checksum */
2N/A bitv_setlo(allbits, infop->csumbits, (unsigned)csum);
2N/A
2N/A /* encode binary values according to alphabet */
2N/A limbit = infop->numx * 5;
2N/A for (bit = 0; bit < infop->numx; bit++) {
2N/A if (bit % 4 == 0)
2N/A *code++ = '-';
2N/A *code++ = Alphabet[bitv_chunk(allbits, limbit, limbit - 5)];
2N/A limbit -= 5;
2N/A }
2N/A
2N/A *code = '\0';
2N/A bitv_free(allbits);
2N/A bitv_free(dictval);
2N/A
2N/A if (dhp->debug > 1)
2N/A (void) fprintf(stderr, "code \"%s\"\n", codebegin);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * code2dictval -- convert a diagcode back to a bit vector
2N/A */
2N/Astatic bitv *
2N/Acode2dictval(fm_dc_handle_t *dhp, const char *code)
2N/A{
2N/A const struct info *infop;
2N/A int len = strlen(dhp->dictname);
2N/A bitv *allbits;
2N/A bitv *dictval;
2N/A int numx; /* number of X's we count */
2N/A unsigned long ocsum; /* original checksum in code */
2N/A unsigned long csum; /* our computed checksum */
2N/A int bit; /* for looping through bits */
2N/A int limbit; /* upper bit limit when looping */
2N/A const char *ptr;
2N/A
2N/A /* check dictname part of code */
2N/A if (strncasecmp(code, dhp->dictname, len) ||
2N/A code[len] != '-') {
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* convert code back to a bit vector */
2N/A if ((allbits = bitv_alloc()) == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* we verified it began with dictname and a dash, so skip it */
2N/A code = &code[len + 1];
2N/A numx = 0;
2N/A /* be forgiving about misplaced dashes */
2N/A for (; *code; code++)
2N/A if (*code == '-')
2N/A continue;
2N/A else {
2N/A unsigned val;
2N/A
2N/A for (val = 0; Alphabet[val]; val++)
2N/A if (*code == Alphabet[val])
2N/A break;
2N/A if (Alphabet[val] == '\0') {
2N/A bitv_free(allbits);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A bitv_shiftin(allbits, 5, val);
2N/A numx++;
2N/A }
2N/A
2N/A if ((infop = numx2info(numx)) == NULL) {
2N/A bitv_free(allbits);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* now pull out the csum */
2N/A ocsum = bitv_chunk(allbits, infop->csumbits, 0);
2N/A
2N/A /* set the csum bits to zero */
2N/A bitv_setlo(allbits, infop->csumbits, 0);
2N/A
2N/A /* calculate the checksum and see if it matches */
2N/A csum = 0;
2N/A for (ptr = dhp->dictname; *ptr; ptr++)
2N/A crc(&csum, (unsigned)*ptr);
2N/A limbit = numx * 5;
2N/A for (bit = 0; bit < numx; bit++) {
2N/A crc(&csum, bitv_chunk(allbits, limbit, limbit - 5));
2N/A limbit -= 5;
2N/A }
2N/A csum &= (1 << infop->csumbits) - 1;
2N/A
2N/A if (csum != ocsum) {
2N/A bitv_free(allbits);
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* code looks okay, just return dictval portion */
2N/A if ((dictval = bitv_alloc()) == NULL) {
2N/A bitv_free(allbits);
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A limbit = infop->csumbits + infop->databits;
2N/A while (limbit > infop->csumbits) {
2N/A bitv_shiftin(dictval, 1,
2N/A bitv_chunk(allbits, limbit, limbit - 1));
2N/A limbit--;
2N/A }
2N/A bitv_free(allbits);
2N/A
2N/A /* add in the offset appropriate for the length of code being used */
2N/A if (bitv_add(dictval, infop->offset) < 0) {
2N/A /*
2N/A * overflow "cannot happen" since we've pulled in
2N/A * a given number of bits from the code and the offset
2N/A * is designed not to overflow...
2N/A */
2N/A bitv_free(dictval);
2N/A errno = ERANGE;
2N/A return (NULL);
2N/A }
2N/A
2N/A return (dictval);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * private routines to parse a line into name/value pairs...
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * startparse -- record starting of buffer containing name=value pairs
2N/A */
2N/Astatic void
2N/Astartparse(struct parsestate *ps, char *ptr)
2N/A{
2N/A ps->parseptr = ptr;
2N/A}
2N/A
2N/A/*
2N/A * nextlhs -- return next left-hand-side of name=value pair, or NULL
2N/A *
2N/A * whitespace around the '=' is allowed for, but not required. the
2N/A * lhs is a simple string that does not contain any whitespace or an
2N/A * embedded equals sign. no escaped characters, quotes, etc. are
2N/A * honored here.
2N/A *
2N/A * this routine also parses the rhs and saves a pointer to it
2N/A * in Rhsp so that nextrhs() can return it. if nextrhs() never
2N/A * gets called, we continue looking for the next lhs *after* any
2N/A * rhs that was there.
2N/A */
2N/Astatic char *
2N/Anextlhs(struct parsestate *ps)
2N/A{
2N/A char *lhsp;
2N/A char *copyto;
2N/A int equals = 0;
2N/A int quote = 0;
2N/A int backslash = 0;
2N/A
2N/A /* skip whitespace */
2N/A while (*ps->parseptr && isspace(*ps->parseptr))
2N/A ps->parseptr++;
2N/A
2N/A /* anything left? */
2N/A if (*ps->parseptr == '\0')
2N/A return (NULL);
2N/A
2N/A /* remember start of lhs, assume no rhs until we see '=' */
2N/A lhsp = ps->parseptr;
2N/A
2N/A /* find end of token, no escaped chars, quotes, etc. on lhs */
2N/A while (*ps->parseptr && !isspace(*ps->parseptr))
2N/A if (*ps->parseptr == '=') {
2N/A equals = 1;
2N/A break;
2N/A } else
2N/A ps->parseptr++;
2N/A
2N/A /* null terminate the token, possibly nuking the '=' itself */
2N/A *ps->parseptr++ = '\0';
2N/A
2N/A /* if we haven't seen an '=', see if it happens after whitespace */
2N/A if (!equals) {
2N/A while (*ps->parseptr && isspace(*ps->parseptr))
2N/A ps->parseptr++;
2N/A if (*ps->parseptr == '=') {
2N/A equals = 1;
2N/A ps->parseptr++;
2N/A }
2N/A }
2N/A
2N/A /* skip whitespace */
2N/A while (*ps->parseptr && isspace(*ps->parseptr))
2N/A ps->parseptr++;
2N/A
2N/A /* isolate the rhs if it is there */
2N/A if (!equals || *ps->parseptr == '\0') {
2N/A ps->rhsp = NULL;
2N/A return (lhsp);
2N/A }
2N/A
2N/A if (*ps->parseptr == '"') {
2N/A quote = 1;
2N/A ps->parseptr++;
2N/A }
2N/A
2N/A /* remember the beginning of the rhs */
2N/A ps->rhsp = copyto = ps->parseptr;
2N/A
2N/A /* now scan to the end of the rhs */
2N/A while (*ps->parseptr) {
2N/A if (backslash) {
2N/A switch (*ps->parseptr) {
2N/A case 't':
2N/A *copyto++ = '\t';
2N/A break;
2N/A
2N/A case 'r':
2N/A *copyto++ = '\r';
2N/A break;
2N/A
2N/A case 'n':
2N/A *copyto++ = '\n';
2N/A break;
2N/A
2N/A case 'f':
2N/A *copyto++ = '\f';
2N/A break;
2N/A
2N/A default:
2N/A *copyto++ = *ps->parseptr;
2N/A break;
2N/A }
2N/A
2N/A backslash = 0;
2N/A } else if (*ps->parseptr == '\\')
2N/A backslash = 1;
2N/A else if (quote) {
2N/A if (*ps->parseptr == '"') {
2N/A ps->parseptr++;
2N/A break; /* end of quoted string */
2N/A } else
2N/A *copyto++ = *ps->parseptr;
2N/A } else if (!isspace(*ps->parseptr))
2N/A *copyto++ = *ps->parseptr;
2N/A else {
2N/A ps->parseptr++;
2N/A break; /* rhs terminated by whitespace */
2N/A }
2N/A
2N/A ps->parseptr++;
2N/A }
2N/A *copyto = '\0';
2N/A
2N/A return (lhsp);
2N/A}
2N/A
2N/A/*
2N/A * nextrhs -- return right-hand-side of name=value pair, or NULL
2N/A *
2N/A * this routine can only be used after a lhs has been found with
2N/A * nextlhs(). the rhs consists of a string with no whitespace in it,
2N/A * unless the whitespace is escaped with a backslash. surrounding
2N/A * a string with double quotes is also supported here, as are the
2N/A * common C escape sequences like \t and \n.
2N/A *
2N/A * nextlhs() actually does all the hard work. we just return any
2N/A * rhs that was found by that routine.
2N/A */
2N/Astatic char *
2N/Anextrhs(struct parsestate *ps)
2N/A{
2N/A return (ps->rhsp);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * private routines to manipulate bit vectors (i.e. large integers)
2N/A *
2N/A * if these bit vector routines are ever supposed to be more
2N/A * general, the desired length should be passed in to bitv_alloc()
2N/A * instead of defining a maximum here. but knowing the max ahead
2N/A * of time allows for simpler code and we know the max that will
2N/A * fit into a diagcode. on the minimum side, the below define
2N/A * must be at least sizeof (unsigned).
2N/A */
2N/A#define BITV_MAX_BYTES 15
2N/A
2N/A/* data structure used to hold a bit vector */
2N/Astruct bitv {
2N/A unsigned char v[BITV_MAX_BYTES];
2N/A};
2N/A
2N/A/* allocate a new, zeroed out bit vector */
2N/Astatic bitv *
2N/Abitv_alloc(void)
2N/A{
2N/A int i;
2N/A struct bitv *bv = malloc(sizeof (*bv));
2N/A
2N/A if (bv)
2N/A for (i = 0; i < BITV_MAX_BYTES; i++)
2N/A bv->v[i] = 0;
2N/A
2N/A return (bv);
2N/A}
2N/A
2N/A/* free a bit vector that was allocated with bitv_alloc() */
2N/Astatic void
2N/Abitv_free(bitv *bv)
2N/A{
2N/A free(bv);
2N/A}
2N/A
2N/A/* shift left a bit vector by a given number of bits. fill with zeros. */
2N/Astatic void
2N/Abitv_shift(bitv *bv, unsigned bits)
2N/A{
2N/A while (bits > 0) {
2N/A unsigned iterbits = bits;
2N/A int i;
2N/A
2N/A /* how many bits this iteration? 8 max. */
2N/A if (iterbits > 8)
2N/A iterbits = 8;
2N/A
2N/A for (i = BITV_MAX_BYTES - 1; i > 0; i--) {
2N/A bv->v[i] <<= iterbits;
2N/A bv->v[i] |= bv->v[i - 1] >> (8 - iterbits);
2N/A }
2N/A bv->v[0] <<= iterbits;
2N/A
2N/A bits -= iterbits;
2N/A }
2N/A}
2N/A
2N/A/* force a given number of bits to a specific value */
2N/Astatic void
2N/Abitv_setlo(bitv *bv, unsigned bits, unsigned val)
2N/A{
2N/A int i = 0;
2N/A
2N/A /* assumption: bits * 8 <= sizeof (val) */
2N/A
2N/A while (bits > 0) {
2N/A unsigned iterbits = bits;
2N/A unsigned mask;
2N/A
2N/A if (iterbits > 8)
2N/A iterbits = 8;
2N/A
2N/A mask = (1 << iterbits) - 1;
2N/A
2N/A bv->v[i] &= ~mask;
2N/A bv->v[i] |= val & mask;
2N/A
2N/A val >>= iterbits;
2N/A bits -= iterbits;
2N/A /*
2N/A * the following can't go off end of bv->v[] since
2N/A * BITV_MAX_BYTES is assumed to be at least sizeof
2N/A * unsigned and val can't be more than sizeof unsigned
2N/A * bytes long.
2N/A */
2N/A i++;
2N/A }
2N/A}
2N/A
2N/A/* given a value and number of bits, shift it in from the right */
2N/Astatic void
2N/Abitv_shiftin(bitv *bv, unsigned bits, unsigned val)
2N/A{
2N/A bitv_shift(bv, bits);
2N/A bitv_setlo(bv, bits, val);
2N/A}
2N/A
2N/A/* given a bit vector and a number of bits, shift it in from the right */
2N/Astatic void
2N/Abitv_shiftinv(bitv *bv, unsigned bits, const bitv *inbv)
2N/A{
2N/A int byteindex = bits / 8;
2N/A int iterbits = bits % 8;
2N/A
2N/A /* first handle partial byte shift in */
2N/A bitv_shiftin(bv, iterbits, inbv->v[byteindex--]);
2N/A
2N/A /* now handle any remaining full byte shift ins */
2N/A while (byteindex >= 0)
2N/A bitv_shiftin(bv, 8, inbv->v[byteindex--]);
2N/A}
2N/A
2N/A/* return the number of bits required to hold the current bit vector's value */
2N/Astatic int
2N/Abitv_bits(const bitv *bv)
2N/A{
2N/A int i;
2N/A
2N/A for (i = BITV_MAX_BYTES - 1; i >= 0; i--)
2N/A if (bv->v[i]) {
2N/A int bit;
2N/A
2N/A for (bit = 7; bit >= 0; bit--)
2N/A if ((bv->v[i] >> bit) & 1)
2N/A return (i * 8 + bit + 1);
2N/A
2N/A /* this can't happen, so do *something* */
2N/A return ((i + 1) * 8);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* extract chunks of bits from bit vector */
2N/Astatic unsigned
2N/Abitv_chunk(const bitv *bv, unsigned limbit, unsigned lobit)
2N/A{
2N/A unsigned retval = 0;
2N/A int bit;
2N/A
2N/A /*
2N/A * entry assumptions:
2N/A * limbit > lobit
2N/A * limbit - lobit <= sizeof (unsigned) * 8
2N/A */
2N/A
2N/A for (bit = limbit - 1; bit >= 0 && bit >= lobit; bit--) {
2N/A retval <<= 1;
2N/A retval |= (bv->v[bit / 8] >> (bit % 8)) & 1;
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * multiply by a given value
2N/A *
2N/A * on overflow, bit vector will hold least significant BITV_MAX_BYTES,
2N/A * return value will be -1, and errno will be ERANGE. otherwise
2N/A * return is zero and bit vector holds the product.
2N/A */
2N/Astatic int
2N/Abitv_mul(bitv *bv, unsigned long long val)
2N/A{
2N/A unsigned short result;
2N/A unsigned char prod[BITV_MAX_BYTES];
2N/A unsigned k = 0;
2N/A int valbyte;
2N/A int bvbyte;
2N/A int i;
2N/A
2N/A /* start with a zeroed out bit vector to hold result */
2N/A for (i = 0; i < BITV_MAX_BYTES; i++)
2N/A prod[i] = 0;
2N/A
2N/A /* from most-significant byte of val to least... */
2N/A for (valbyte = 0; valbyte < sizeof (val); valbyte++)
2N/A /* from most significant byte of bv to least */
2N/A for (bvbyte = 0; bvbyte < BITV_MAX_BYTES; bvbyte++) {
2N/A result = ((val >> (valbyte * 8)) & 0xff) *
2N/A bv->v[bvbyte] + k;
2N/A
2N/A if (valbyte + bvbyte >= BITV_MAX_BYTES) {
2N/A /*
2N/A * we're not storing digits past
2N/A * BITV_MAX_BYTES, so if they aren't
2N/A * zeros, then signal an overflow.
2N/A */
2N/A if (result & 0xff) {
2N/A errno = ERANGE;
2N/A return (-1);
2N/A }
2N/A } else
2N/A prod[valbyte + bvbyte] += result & 0xff;
2N/A
2N/A /* "carry the 1..." */
2N/A k = result >> 8;
2N/A }
2N/A
2N/A /* store result in bv */
2N/A for (i = 0; i < BITV_MAX_BYTES; i++)
2N/A bv->v[i] = prod[i];
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * add in a given value
2N/A *
2N/A * on overflow, bit vector will hold least significant BITV_MAX_BYTES,
2N/A * return value will be -1, and errno will be ERANGE. otherwise
2N/A * return is zero and bit vector holds the sum.
2N/A */
2N/Astatic int
2N/Abitv_add(bitv *bv, unsigned long long val)
2N/A{
2N/A int cf = 0; /* carry flag */
2N/A unsigned short result;
2N/A int i;
2N/A
2N/A for (i = 0; i < BITV_MAX_BYTES; i++) {
2N/A if (i < sizeof (val))
2N/A result = cf + bv->v[i] + ((val >> (i * 8)) & 0xff);
2N/A else
2N/A result = cf + bv->v[i];
2N/A
2N/A cf = (result >> 8) & 1;
2N/A bv->v[i] = result & 0xff;
2N/A }
2N/A
2N/A if (cf) {
2N/A errno = ERANGE;
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * subtract out a given value
2N/A *
2N/A * on underflow, bit vector will hold least significant BITV_MAX_BYTES,
2N/A * return value will be -1, and errno will be ERANGE. otherwise
2N/A * return is zero and bit vector holds the difference.
2N/A */
2N/Astatic int
2N/Abitv_sub(bitv *bv, unsigned long long val)
2N/A{
2N/A int bf = 0; /* borrow flag */
2N/A unsigned short minuend;
2N/A unsigned short subtrahend;
2N/A int i;
2N/A
2N/A for (i = 0; i < BITV_MAX_BYTES; i++) {
2N/A minuend = bv->v[i];
2N/A if (i < sizeof (val))
2N/A subtrahend = bf + ((val >> (i * 8)) & 0xff);
2N/A else
2N/A subtrahend = bf;
2N/A if (subtrahend > minuend) {
2N/A bf = 1;
2N/A minuend += 1 << 8;
2N/A } else
2N/A bf = 0;
2N/A
2N/A bv->v[i] = minuend - subtrahend;
2N/A }
2N/A
2N/A if (bf) {
2N/A errno = ERANGE;
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * see if bv is greater than or equal to a given value
2N/A */
2N/Astatic int
2N/Abitv_ge(const bitv *bv, unsigned long long val)
2N/A{
2N/A int bf = 0; /* borrow flag */
2N/A unsigned short minuend;
2N/A unsigned short subtrahend;
2N/A int i;
2N/A
2N/A for (i = 0; i < BITV_MAX_BYTES; i++) {
2N/A minuend = bv->v[i];
2N/A if (i < sizeof (val))
2N/A subtrahend = bf + ((val >> (i * 8)) & 0xff);
2N/A else
2N/A subtrahend = bf;
2N/A if (subtrahend > minuend)
2N/A bf = 1;
2N/A else
2N/A bf = 0;
2N/A }
2N/A
2N/A return (!bf);
2N/A}
2N/A
2N/A/* parse a string into bit vector, honor leading 0/0x for octal/hex */
2N/Astatic bitv *
2N/Abitv_strparse(const char *s, int bits)
2N/A{
2N/A unsigned long long base = 10;
2N/A unsigned long long val;
2N/A bitv *bv = bitv_alloc();
2N/A
2N/A if (bv == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A
2N/A if (*s == '0') {
2N/A s++;
2N/A if (*s == 'x') {
2N/A s++;
2N/A base = 16;
2N/A } else
2N/A base = 8;
2N/A }
2N/A
2N/A while (isxdigit(*s)) {
2N/A /* isxdigit() let's in too much, depending on base */
2N/A if (base == 8 && (*s < '0' || *s > '7'))
2N/A break;
2N/A else if (base == 10 && !isdigit(*s))
2N/A break;
2N/A
2N/A /* convert the digit to binary */
2N/A if (isdigit(*s))
2N/A val = *s - '0';
2N/A else
2N/A val = tolower(*s) - 'a' + 10;
2N/A
2N/A /*
2N/A * multiply our big integer by base,
2N/A * add in the most recent digit,
2N/A * and check for overflow
2N/A */
2N/A if (bitv_mul(bv, base) < 0 ||
2N/A bitv_add(bv, val) < 0 ||
2N/A bitv_bits(bv) > bits) {
2N/A bitv_free(bv);
2N/A errno = ERANGE;
2N/A return (NULL);
2N/A }
2N/A
2N/A s++;
2N/A }
2N/A
2N/A return (bv);
2N/A}
2N/A
2N/A/* return 0 if two bit vectors represent the same number */
2N/Astatic int
2N/Abitv_cmp(const bitv *bv1, const bitv *bv2)
2N/A{
2N/A int i;
2N/A
2N/A for (i = BITV_MAX_BYTES - 1; i >= 0; i--)
2N/A if (bv1->v[i] < bv2->v[i])
2N/A return (-1);
2N/A else if (bv1->v[i] > bv2->v[i])
2N/A return (1);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/* CRC code... */
2N/Astatic unsigned crctab[256] = {
2N/A 0x00000000,
2N/A 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
2N/A 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6,
2N/A 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
2N/A 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC,
2N/A 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F,
2N/A 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A,
2N/A 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
2N/A 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58,
2N/A 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033,
2N/A 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE,
2N/A 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
2N/A 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4,
2N/A 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
2N/A 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5,
2N/A 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
2N/A 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07,
2N/A 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C,
2N/A 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1,
2N/A 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
2N/A 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B,
2N/A 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698,
2N/A 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D,
2N/A 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
2N/A 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F,
2N/A 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
2N/A 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80,
2N/A 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
2N/A 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A,
2N/A 0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629,
2N/A 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C,
2N/A 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
2N/A 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E,
2N/A 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65,
2N/A 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8,
2N/A 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
2N/A 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2,
2N/A 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
2N/A 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74,
2N/A 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
2N/A 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21,
2N/A 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A,
2N/A 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087,
2N/A 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
2N/A 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D,
2N/A 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE,
2N/A 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB,
2N/A 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
2N/A 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09,
2N/A 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
2N/A 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF,
2N/A 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
2N/A};
2N/A
2N/Astatic void
2N/Acrc(unsigned long *crcp, unsigned val)
2N/A{
2N/A *crcp = (*crcp<<8) ^ crctab[(unsigned char)((*crcp>>24)^val)];
2N/A}