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