Home | History | Annotate | Line # | Download | only in keama
conflex.c revision 1.2
      1 /*	$NetBSD: conflex.c,v 1.2 2020/08/03 21:10:57 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  *
     18  *   Internet Systems Consortium, Inc.
     19  *   950 Charter Street
     20  *   Redwood City, CA 94063
     21  *   <info (at) isc.org>
     22  *   https://www.isc.org/
     23  *
     24  */
     25 
     26 #include <sys/cdefs.h>
     27 __RCSID("$NetBSD: conflex.c,v 1.2 2020/08/03 21:10:57 christos Exp $");
     28 
     29 /* From common/conflex.c */
     30 
     31 #include "keama.h"
     32 
     33 #include <sys/types.h>
     34 #include <sys/stat.h>
     35 #include <sys/mman.h>
     36 #include <assert.h>
     37 #include <ctype.h>
     38 #include <fcntl.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <unistd.h>
     42 
     43 static int get_char(struct parse *);
     44 static void unget_char(struct parse *, int);
     45 static void skip_to_eol(struct parse *);
     46 static enum dhcp_token read_whitespace(int c, struct parse *cfile);
     47 static enum dhcp_token read_string(struct parse *);
     48 static enum dhcp_token read_number(int, struct parse *);
     49 static enum dhcp_token read_num_or_name(int, struct parse *);
     50 static enum dhcp_token intern(char *, enum dhcp_token);
     51 
     52 struct parse *
     53 new_parse(int file, char *inbuf, size_t buflen, const char *name, int eolp)
     54 {
     55 	struct parse *tmp;
     56 
     57 	tmp = (struct parse *)malloc(sizeof(struct parse));
     58 	assert(tmp != NULL);
     59 	memset(tmp, 0, sizeof(struct parse));
     60 
     61 	TAILQ_INSERT_TAIL(&parses, tmp);
     62 
     63 	tmp->tlname = name;
     64 	tmp->lpos = tmp->line = 1;
     65 	tmp->cur_line = tmp->line1;
     66 	tmp->prev_line = tmp->line2;
     67 	tmp->token_line = tmp->cur_line;
     68 	tmp->cur_line[0] = tmp->prev_line[0] = 0;
     69 	tmp->file = file;
     70 	tmp->eol_token = eolp;
     71 	TAILQ_INIT(&tmp->comments);
     72 
     73 	if (inbuf != NULL) {
     74 		tmp->inbuf = inbuf;
     75 		tmp->buflen = buflen;
     76 		tmp->bufsiz = 0;
     77 	} else {
     78 		struct stat sb;
     79 
     80 		if (fstat(file, &sb) < 0) {
     81 			fprintf(stderr, "can't stat input\n");
     82 			exit(1);
     83 		}
     84 
     85 		if (sb.st_size == 0)
     86 			return tmp;
     87 
     88 		tmp->bufsiz = tmp->buflen = (size_t) sb.st_size;
     89 		tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED,
     90 				  file, 0);
     91 
     92 		if (tmp->inbuf == MAP_FAILED) {
     93 			fprintf(stderr, "can't map input\n");
     94 			exit(1);
     95 		}
     96 	}
     97 
     98 	return tmp;
     99 }
    100 
    101 void
    102 end_parse(struct parse *cfile)
    103 {
    104 	/* "Memory" config files have no file. */
    105 	if (cfile->file != -1) {
    106 		munmap(cfile->inbuf, cfile->bufsiz);
    107 		close(cfile->file);
    108 	}
    109 
    110 	while (TAILQ_NEXT(cfile) != NULL) {
    111 		struct parse *saved_state;
    112 
    113 		saved_state = TAILQ_NEXT(cfile);
    114 		TAILQ_REMOVE(&parses, saved_state);
    115 		free(saved_state);
    116 	}
    117 
    118 	cfile->stack_size = 0;
    119 	if (cfile->stack != NULL)
    120 		free(cfile->stack);
    121 	cfile->stack = NULL;
    122 	TAILQ_REMOVE(&parses, cfile);
    123 	free(cfile);
    124 }
    125 
    126 /*
    127  * Save the current state of the parser.
    128  *
    129  * Only one state may be saved. Any previous saved state is
    130  * lost.
    131  */
    132 void
    133 save_parse_state(struct parse *cfile) {
    134 	struct parse *tmp;
    135 
    136 	/*
    137 	 * Free any previous saved states.
    138 	 */
    139 	while (TAILQ_NEXT(cfile) != NULL) {
    140 		struct parse *saved_state;
    141 
    142 		saved_state = TAILQ_NEXT(cfile);
    143 		TAILQ_REMOVE(&parses, saved_state);
    144 		free(saved_state);
    145 	}
    146 
    147 	/*
    148 	 * Save our current state.
    149 	 */
    150 	tmp = (struct parse *)malloc(sizeof(struct parse));
    151 	if (tmp == NULL)
    152 		parse_error(cfile, "can't allocate state to be saved");
    153 	memset(tmp, 0, sizeof(struct parse));
    154 	/* save up to comments field */
    155 	memcpy(tmp, cfile, (size_t)&(((struct parse *)0)->comments));
    156 	TAILQ_INSERT_AFTER(&parses, cfile, tmp);
    157 }
    158 
    159 /*
    160  * Return the parser to the previous saved state.
    161  *
    162  * You must call save_parse_state() every time before calling
    163  * restore_parse_state().
    164  */
    165 void
    166 restore_parse_state(struct parse *cfile) {
    167 	struct parse *saved_state;
    168 
    169 	if (TAILQ_NEXT(cfile) == NULL)
    170 		parse_error(cfile, "can't find saved state");
    171 
    172 	saved_state = TAILQ_NEXT(cfile);
    173 	TAILQ_REMOVE(&parses, saved_state);
    174 	/* restore up to comments field */
    175 	memcpy(cfile, saved_state, (size_t)&(((struct parse *)0)->comments));
    176 	free(saved_state);
    177 }
    178 
    179 static int
    180 get_char(struct parse *cfile)
    181 {
    182 	/* My kingdom for WITH... */
    183 	int c;
    184 
    185 	if (cfile->bufix == cfile->buflen) {
    186 		c = EOF;
    187 	} else {
    188 		c = cfile->inbuf[cfile->bufix];
    189 		cfile->bufix++;
    190 	}
    191 
    192 	if (!cfile->ugflag) {
    193 		if (c == EOL) {
    194 			if (cfile->cur_line == cfile->line1) {
    195 				cfile->cur_line = cfile->line2;
    196 				cfile->prev_line = cfile->line1;
    197 			} else {
    198 				cfile->cur_line = cfile->line1;
    199 				cfile->prev_line = cfile->line2;
    200 			}
    201 			cfile->line++;
    202 			cfile->lpos = 1;
    203 			cfile->cur_line[0] = 0;
    204 		} else if (c != EOF) {
    205 			if (cfile->lpos <= 80) {
    206 				cfile->cur_line[cfile->lpos - 1] = c;
    207 				cfile->cur_line[cfile->lpos] = 0;
    208 			}
    209 			cfile->lpos++;
    210 		}
    211 	} else
    212 		cfile->ugflag = 0;
    213 	return c;
    214 }
    215 
    216 /*
    217  * Return a character to our input buffer.
    218  */
    219 static void
    220 unget_char(struct parse *cfile, int c) {
    221 	if (c != EOF) {
    222 		cfile->bufix--;
    223 		cfile->ugflag = 1;	/* do not put characters into
    224 					   our error buffer on the next
    225 					   call to get_char() */
    226 	}
    227 }
    228 
    229 /*
    230  * GENERAL NOTE ABOUT TOKENS
    231  *
    232  * We normally only want non-whitespace tokens. There are some
    233  * circumstances where we *do* want to see whitespace (for example
    234  * when parsing IPv6 addresses).
    235  *
    236  * Generally we use the next_token() function to read tokens. This
    237  * in turn calls get_next_token, which does *not* return tokens for
    238  * whitespace. Rather, it skips these.
    239  *
    240  * When we need to see whitespace, we us next_raw_token(), which also
    241  * returns the WHITESPACE token.
    242  *
    243  * The peek_token() and peek_raw_token() functions work as expected.
    244  *
    245  * Warning: if you invoke peek_token(), then if there is a whitespace
    246  * token, it will be lost, and subsequent use of next_raw_token() or
    247  * peek_raw_token() will NOT see it.
    248  */
    249 
    250 static enum dhcp_token
    251 get_raw_token(struct parse *cfile) {
    252 	int c;
    253 	enum dhcp_token ttok;
    254 	static char tb[2];
    255 	int l, p;
    256 
    257 	for (;;) {
    258 		l = cfile->line;
    259 		p = cfile->lpos;
    260 
    261 		c = get_char(cfile);
    262 		if (!((c == '\n') && cfile->eol_token) &&
    263 		    isascii(c) && isspace(c)) {
    264 		    	ttok = read_whitespace(c, cfile);
    265 			break;
    266 		}
    267 		if (c == '#') {
    268 			skip_to_eol(cfile);
    269 			continue;
    270 		}
    271 		if (c == '"') {
    272 			cfile->lexline = l;
    273 			cfile->lexchar = p;
    274 			ttok = read_string(cfile);
    275 			break;
    276 		}
    277 		if ((isascii(c) && isdigit(c)) || c == '-') {
    278 			cfile->lexline = l;
    279 			cfile->lexchar = p;
    280 			ttok = read_number(c, cfile);
    281 			break;
    282 		} else if (isascii(c) && isalpha(c)) {
    283 			cfile->lexline = l;
    284 			cfile->lexchar = p;
    285 			ttok = read_num_or_name(c, cfile);
    286 			break;
    287 		} else if (c == EOF) {
    288 			ttok = END_OF_FILE;
    289 			cfile->tlen = 0;
    290 			break;
    291 		} else {
    292 			cfile->lexline = l;
    293 			cfile->lexchar = p;
    294 			tb[0] = c;
    295 			tb[1] = 0;
    296 			cfile->tval = tb;
    297 			cfile->tlen = 1;
    298 			ttok = c;
    299 			break;
    300 		}
    301 	}
    302 	return ttok;
    303 }
    304 
    305 /*
    306  * The get_next_token() function consumes the next token and
    307  * returns it to the caller.
    308  *
    309  * Since the code is almost the same for "normal" and "raw"
    310  * input, we pass a flag to alter the way it works.
    311  */
    312 
    313 static enum dhcp_token
    314 get_next_token(const char **rval, unsigned *rlen,
    315 	       struct parse *cfile, isc_boolean_t raw) {
    316 	int rv;
    317 
    318 	if (cfile->token) {
    319 		if (cfile->lexline != cfile->tline)
    320 			cfile->token_line = cfile->cur_line;
    321 		cfile->lexchar = cfile->tlpos;
    322 		cfile->lexline = cfile->tline;
    323 		rv = cfile->token;
    324 		cfile->token = 0;
    325 	} else {
    326 		rv = get_raw_token(cfile);
    327 		cfile->token_line = cfile->cur_line;
    328 	}
    329 
    330 	if (!raw) {
    331 		while (rv == WHITESPACE) {
    332 			rv = get_raw_token(cfile);
    333 			cfile->token_line = cfile->cur_line;
    334 		}
    335 	}
    336 
    337 	if (rval)
    338 		*rval = cfile->tval;
    339 	if (rlen)
    340 		*rlen = cfile->tlen;
    341 	return rv;
    342 }
    343 
    344 /*
    345  * Get the next token from cfile and return it.
    346  *
    347  * If rval is non-NULL, set the pointer it contains to
    348  * the contents of the token.
    349  *
    350  * If rlen is non-NULL, set the integer it contains to
    351  * the length of the token.
    352  */
    353 
    354 enum dhcp_token
    355 next_token(const char **rval, unsigned *rlen, struct parse *cfile) {
    356 	return get_next_token(rval, rlen, cfile, ISC_FALSE);
    357 }
    358 
    359 
    360 /*
    361  * The same as the next_token() function above, but will return space
    362  * as the WHITESPACE token.
    363  */
    364 
    365 enum dhcp_token
    366 next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
    367 	return get_next_token(rval, rlen, cfile, ISC_TRUE);
    368 }
    369 
    370 
    371 /*
    372  * The do_peek_token() function checks the next token without
    373  * consuming it, and returns it to the caller.
    374  *
    375  * Since the code is almost the same for "normal" and "raw"
    376  * input, we pass a flag to alter the way it works. (See the
    377  * warning in the GENERAL NOTES ABOUT TOKENS above though.)
    378  */
    379 
    380 enum dhcp_token
    381 do_peek_token(const char **rval, unsigned int *rlen,
    382 	      struct parse *cfile, isc_boolean_t raw) {
    383 	int x;
    384 
    385 	if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) {
    386 		cfile->tlpos = cfile->lexchar;
    387 		cfile->tline = cfile->lexline;
    388 
    389 		do {
    390 			cfile->token = get_raw_token(cfile);
    391 		} while (!raw && (cfile->token == WHITESPACE));
    392 
    393 		if (cfile->lexline != cfile->tline)
    394 			cfile->token_line = cfile->prev_line;
    395 
    396 		x = cfile->lexchar;
    397 		cfile->lexchar = cfile->tlpos;
    398 		cfile->tlpos = x;
    399 
    400 		x = cfile->lexline;
    401 		cfile->lexline = cfile->tline;
    402 		cfile->tline = x;
    403 	}
    404 	if (rval)
    405 		*rval = cfile->tval;
    406 	if (rlen)
    407 		*rlen = cfile->tlen;
    408 	return cfile->token;
    409 }
    410 
    411 
    412 /*
    413  * Get the next token from cfile and return it, leaving it for a
    414  * subsequent call to next_token().
    415  *
    416  * Note that it WILL consume whitespace tokens.
    417  *
    418  * If rval is non-NULL, set the pointer it contains to
    419  * the contents of the token.
    420  *
    421  * If rlen is non-NULL, set the integer it contains to
    422  * the length of the token.
    423  */
    424 
    425 enum dhcp_token
    426 peek_token(const char **rval, unsigned *rlen, struct parse *cfile) {
    427 	return do_peek_token(rval, rlen, cfile, ISC_FALSE);
    428 }
    429 
    430 
    431 /*
    432  * The same as the peek_token() function above, but will return space
    433  * as the WHITESPACE token.
    434  */
    435 
    436 enum dhcp_token
    437 peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
    438 	return do_peek_token(rval, rlen, cfile, ISC_TRUE);
    439 }
    440 
    441 /*
    442  * The comment up to but not including EOL is saved.
    443  */
    444 
    445 static void
    446 skip_to_eol(struct parse *cfile)
    447 {
    448 	char buf[128];
    449 	unsigned cc = 0;
    450 	struct comment *comment;
    451 
    452 	memset(buf, 0, sizeof(buf));
    453 	buf[0] = '#';
    454 	for (;;) {
    455 		int c;
    456 
    457 		c = get_char(cfile);
    458 		if (c == EOF)
    459 			break;
    460 		if (c == EOL) {
    461 			break;
    462 		}
    463 		if (++cc < sizeof(buf) - 1)
    464 			buf[cc] = c;
    465 	}
    466 	comment = createComment(buf);
    467 	TAILQ_INSERT_TAIL(&cfile->comments, comment);
    468 }
    469 
    470 static enum dhcp_token
    471 read_whitespace(int c, struct parse *cfile) {
    472 	int ofs;
    473 
    474 	/*
    475 	 * Read as much whitespace as we have available.
    476 	 */
    477 	ofs = 0;
    478 	do {
    479 		if (ofs >= (sizeof(cfile->tokbuf) - 1)) {
    480 			/*
    481 			 * As the file includes a huge amount of whitespace,
    482 			 * it's probably broken.
    483 			 * Print out a warning and bail out.
    484 			 */
    485 			parse_error(cfile,
    486 				    "whitespace too long, buffer overflow.");
    487 		}
    488 		cfile->tokbuf[ofs++] = c;
    489 		c = get_char(cfile);
    490 		if (c == EOF)
    491 			return END_OF_FILE;
    492 	} while (!((c == '\n') && cfile->eol_token) &&
    493 		 isascii(c) && isspace(c));
    494 
    495 	/*
    496 	 * Put the last (non-whitespace) character back.
    497 	 */
    498 	unget_char(cfile, c);
    499 
    500 	/*
    501 	 * Return our token.
    502 	 */
    503 	cfile->tokbuf[ofs] = '\0';
    504 	cfile->tlen = ofs;
    505 	cfile->tval = cfile->tokbuf;
    506 	return WHITESPACE;
    507 }
    508 
    509 static enum dhcp_token
    510 read_string(struct parse *cfile)
    511 {
    512 	unsigned i;
    513 	int bs = 0;
    514 	int c;
    515 	int value = 0;
    516 	int hex = 0;
    517 
    518 	for (i = 0; i < sizeof(cfile->tokbuf); i++) {
    519 	      again:
    520 		c = get_char(cfile);
    521 		if (c == EOF)
    522 			parse_error(cfile, "eof in string constant");
    523 		if (bs == 1) {
    524 			switch (c) {
    525 			case 't':
    526 				cfile->tokbuf[i] = '\t';
    527 				break;
    528 			case 'r':
    529 				cfile->tokbuf[i] = '\r';
    530 				break;
    531 			case 'n':
    532 				cfile->tokbuf[i] = '\n';
    533 				break;
    534 			case 'b':
    535 				cfile->tokbuf[i] = '\b';
    536 				break;
    537 			case '0':
    538 			case '1':
    539 			case '2':
    540 			case '3':
    541 				hex = 0;
    542 				value = c - '0';
    543 				++bs;
    544 				goto again;
    545 			case 'x':
    546 				hex = 1;
    547 				value = 0;
    548 				++bs;
    549 				goto again;
    550 			default:
    551 				cfile->tokbuf[i] = c;
    552 				break;
    553 			}
    554 			bs = 0;
    555 		} else if (bs > 1) {
    556 			if (hex) {
    557 				if (c >= '0' && c <= '9') {
    558 					value = value * 16 + (c - '0');
    559 				} else if (c >= 'a' && c <= 'f') {
    560 					value = value * 16 + (c - 'a' + 10);
    561 				} else if (c >= 'A' && c <= 'F') {
    562 					value = value * 16 + (c - 'A' + 10);
    563 				} else
    564 					parse_error(cfile,
    565 						    "invalid hex digit: %x",
    566 						    c);
    567 				if (++bs == 4) {
    568 					cfile->tokbuf[i] = value;
    569 					bs = 0;
    570 				} else
    571 					goto again;
    572 			} else {
    573 				if (c >= '0' && c <= '7') {
    574 					value = value * 8 + (c - '0');
    575 				} else {
    576 				    if (value != 0)
    577 					parse_error(cfile,
    578 						    "invalid octal digit %x",
    579 						    c);
    580 				    else
    581 					cfile->tokbuf[i] = 0;
    582 				    bs = 0;
    583 				}
    584 				if (++bs == 4) {
    585 					cfile->tokbuf[i] = value;
    586 					bs = 0;
    587 				} else
    588 					goto again;
    589 			}
    590 		} else if (c == '\\') {
    591 			bs = 1;
    592 			goto again;
    593 		} else if (c == '"')
    594 			break;
    595 		else
    596 			cfile->tokbuf[i] = c;
    597 	}
    598 	/* Normally, I'd feel guilty about this, but we're talking about
    599 	   strings that'll fit in a DHCP packet here... */
    600 	if (i == sizeof(cfile->tokbuf))
    601 		parse_error(cfile,
    602 			    "string constant larger than internal buffer");
    603 	cfile->tokbuf[i] = 0;
    604 	cfile->tlen = i;
    605 	cfile->tval = cfile->tokbuf;
    606 	return STRING;
    607 }
    608 
    609 static enum dhcp_token
    610 read_number(int c, struct parse *cfile)
    611 {
    612 	unsigned i = 0;
    613 	int token = NUMBER;
    614 
    615 	cfile->tokbuf[i++] = c;
    616 	for (; i < sizeof(cfile->tokbuf); i++) {
    617 		c = get_char(cfile);
    618 
    619 		/* Promote NUMBER->NUMBER_OR_NAME->NAME, never demote.
    620 		 * Except in the case of '0x' syntax hex, which gets called
    621 		 * a NAME at '0x', and returned to NUMBER_OR_NAME once it's
    622 		 * verified to be at least 0xf or less.
    623 		 */
    624 		switch (isascii(c) ? token : BREAK) {
    625 		case NUMBER:
    626 			if (isdigit(c))
    627 				break;
    628 			/* FALLTHROUGH */
    629 		case NUMBER_OR_NAME:
    630 			if (isxdigit(c)) {
    631 				token = NUMBER_OR_NAME;
    632 				break;
    633 			}
    634 			/* FALLTHROUGH */
    635 		case NAME:
    636 			if ((i == 2) && isxdigit(c) &&
    637 				(cfile->tokbuf[0] == '0') &&
    638 				((cfile->tokbuf[1] == 'x') ||
    639 				 (cfile->tokbuf[1] == 'X'))) {
    640 				token = NUMBER_OR_NAME;
    641 				break;
    642 			} else if (((c == '-') || (c == '_') || isalnum(c))) {
    643 				token = NAME;
    644 				break;
    645 			}
    646 			/* FALLTHROUGH */
    647 		case BREAK:
    648 			/* At this point c is either EOF or part of the next
    649 			 * token.  If not EOF, rewind the file one byte so
    650 			 * the next token is read from there.
    651 			 */
    652 			unget_char(cfile, c);
    653 			goto end_read;
    654 
    655 		default:
    656 			parse_error(cfile,
    657 				    "read_number(): impossible case");
    658 		}
    659 
    660 		cfile->tokbuf[i] = c;
    661 	}
    662 
    663 	if (i == sizeof(cfile->tokbuf))
    664 		parse_error(cfile,
    665 			    "numeric token larger than internal buffer");
    666 
    667   end_read:
    668 	cfile->tokbuf[i] = 0;
    669 	cfile->tlen = i;
    670 	cfile->tval = cfile->tokbuf;
    671 
    672 	/*
    673 	 * If this entire token from start to finish was "-", such as
    674 	 * the middle parameter in "42 - 7", return just the MINUS token.
    675 	 */
    676 	if ((i == 1) && (cfile->tokbuf[i] == '-'))
    677 		return MINUS;
    678 	else
    679 		return token;
    680 }
    681 
    682 static enum dhcp_token
    683 read_num_or_name(int c, struct parse *cfile)
    684 {
    685 	unsigned i = 0;
    686 	enum dhcp_token rv = NUMBER_OR_NAME;
    687 
    688 	cfile->tokbuf[i++] = c;
    689 	for (; i < sizeof(cfile->tokbuf); i++) {
    690 		c = get_char(cfile);
    691 		if (!isascii(c) ||
    692 		    (c != '-' && c != '_' && !isalnum(c))) {
    693 		    	unget_char(cfile, c);
    694 			break;
    695 		}
    696 		if (!isxdigit(c))
    697 			rv = NAME;
    698 		cfile->tokbuf[i] = c;
    699 	}
    700 	if (i == sizeof(cfile->tokbuf))
    701 		parse_error(cfile, "token larger than internal buffer");
    702 	cfile->tokbuf[i] = 0;
    703 	cfile->tlen = i;
    704 	cfile->tval = cfile->tokbuf;
    705 	return intern(cfile->tval, rv);
    706 }
    707 
    708 static enum dhcp_token
    709 intern(char *atom, enum dhcp_token dfv) {
    710 	if (!isascii(atom[0]))
    711 		return dfv;
    712 
    713 	switch (tolower((unsigned char)atom[0])) {
    714 	case '-':
    715 		if (atom [1] == 0)
    716 			return MINUS;
    717 		break;
    718 
    719 	case 'a':
    720 		if (!strcasecmp(atom + 1, "bandoned"))
    721 			return TOKEN_ABANDONED;
    722 		if (!strcasecmp(atom + 1, "ctive"))
    723 			return TOKEN_ACTIVE;
    724 		if (!strncasecmp(atom + 1, "dd", 2)) {
    725 			if (atom[3] == '\0')
    726 				return TOKEN_ADD;
    727 			else if (!strcasecmp(atom + 3, "ress"))
    728 				return ADDRESS;
    729 			break;
    730 		}
    731 		if (!strcasecmp(atom + 1, "fter"))
    732 			return AFTER;
    733 		if (isascii(atom[1]) &&
    734 		    (tolower((unsigned char)atom[1]) == 'l')) {
    735 			if (!strcasecmp(atom + 2, "gorithm"))
    736 				return ALGORITHM;
    737 			if (!strcasecmp(atom + 2, "ias"))
    738 				return ALIAS;
    739 			if (isascii(atom[2]) &&
    740 			    (tolower((unsigned char)atom[2]) == 'l')) {
    741 				if (atom[3] == '\0')
    742 					return ALL;
    743 				else if (!strcasecmp(atom + 3, "ow"))
    744 					return ALLOW;
    745 				break;
    746 			}
    747 			if (!strcasecmp(atom + 2, "so"))
    748 				return TOKEN_ALSO;
    749 			break;
    750 		}
    751 		if (isascii(atom[1]) &&
    752 		    (tolower((unsigned char)atom[1]) == 'n')) {
    753 			if (!strcasecmp(atom + 2, "d"))
    754 				return AND;
    755 			if (!strcasecmp(atom + 2, "ycast-mac"))
    756 				return ANYCAST_MAC;
    757 			break;
    758 		}
    759 		if (!strcasecmp(atom + 1, "ppend"))
    760 			return APPEND;
    761 		if (!strcasecmp(atom + 1, "rray"))
    762 			return ARRAY;
    763 		if (isascii(atom[1]) &&
    764 		    (tolower((unsigned char)atom[1]) == 't')) {
    765 			if (atom[2] == '\0')
    766 				return AT;
    767 			if (!strcasecmp(atom + 2, "sfp"))
    768 				return ATSFP;
    769 			break;
    770 		}
    771 		if (!strcasecmp(atom + 1, "uthoring-byte-order"))
    772 			return AUTHORING_BYTE_ORDER;
    773 		if (!strncasecmp(atom + 1, "ut", 2)) {
    774 			if (isascii(atom[3]) &&
    775 			    (tolower((unsigned char)atom[3]) == 'h')) {
    776 				if (!strncasecmp(atom + 4, "enticat", 7)) {
    777 					if (!strcasecmp(atom + 11, "ed"))
    778 						return AUTHENTICATED;
    779 					if (!strcasecmp(atom + 11, "ion"))
    780 						return AUTHENTICATION;
    781 					break;
    782 				}
    783 				if (!strcasecmp(atom + 4, "oritative"))
    784 					return AUTHORITATIVE;
    785 				break;
    786 			}
    787 			if (!strcasecmp(atom + 3, "o-partner-down"))
    788 				return AUTO_PARTNER_DOWN;
    789 			break;
    790 		}
    791 		break;
    792 	case 'b':
    793 		if (!strcasecmp(atom + 1, "ackup"))
    794 			return TOKEN_BACKUP;
    795 		if (!strcasecmp(atom + 1, "ootp"))
    796 			return TOKEN_BOOTP;
    797 		if (!strcasecmp(atom + 1, "inding"))
    798 			return BINDING;
    799 		if (!strcasecmp(atom + 1, "inary-to-ascii"))
    800 			return BINARY_TO_ASCII;
    801 		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
    802 			return BACKOFF_CUTOFF;
    803 		if (!strcasecmp(atom + 1, "ooting"))
    804 			return BOOTING;
    805 		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
    806 			return BOOT_UNKNOWN_CLIENTS;
    807 		if (!strcasecmp(atom + 1, "reak"))
    808 			return BREAK;
    809 		if (!strcasecmp(atom + 1, "illing"))
    810 			return BILLING;
    811 		if (!strcasecmp(atom + 1, "oolean"))
    812 			return BOOLEAN;
    813 		if (!strcasecmp(atom + 1, "alance"))
    814 			return BALANCE;
    815 		if (!strcasecmp(atom + 1, "ound"))
    816 			return BOUND;
    817 		if (!strcasecmp(atom+1, "ig-endian")) {
    818 			return TOKEN_BIG_ENDIAN;
    819 		}
    820 		break;
    821 	case 'c':
    822 		if (!strcasecmp(atom + 1, "ase"))
    823 			return CASE;
    824 		if (!strcasecmp(atom + 1, "heck"))
    825 			return CHECK;
    826 		if (!strcasecmp(atom + 1, "iaddr"))
    827 			return CIADDR;
    828 		if (isascii(atom[1]) &&
    829 		    tolower((unsigned char)atom[1]) == 'l') {
    830 			if (!strcasecmp(atom + 2, "ass"))
    831 				return CLASS;
    832 			if (!strncasecmp(atom + 2, "ient", 4)) {
    833 				if (!strcasecmp(atom + 6, "s"))
    834 					return CLIENTS;
    835 				if (atom[6] == '-') {
    836 					if (!strcasecmp(atom + 7, "hostname"))
    837 						return CLIENT_HOSTNAME;
    838 					if (!strcasecmp(atom + 7, "identifier"))
    839 						return CLIENT_IDENTIFIER;
    840 					if (!strcasecmp(atom + 7, "state"))
    841 						return CLIENT_STATE;
    842 					if (!strcasecmp(atom + 7, "updates"))
    843 						return CLIENT_UPDATES;
    844 					break;
    845 				}
    846 				break;
    847 			}
    848 			if (!strcasecmp(atom + 2, "ose"))
    849 				return TOKEN_CLOSE;
    850 			if (!strcasecmp(atom + 2, "tt"))
    851 				return CLTT;
    852 			break;
    853 		}
    854 		if (isascii(atom[1]) &&
    855 		    tolower((unsigned char)atom[1]) == 'o') {
    856 			if (!strcasecmp(atom + 2, "de"))
    857 				return CODE;
    858 			if (isascii(atom[2]) &&
    859 			    tolower((unsigned char)atom[2]) == 'm') {
    860 				if (!strcasecmp(atom + 3, "mit"))
    861 					return COMMIT;
    862 				if (!strcasecmp(atom + 3,
    863 						"munications-interrupted"))
    864 					return COMMUNICATIONS_INTERRUPTED;
    865 				if (!strcasecmp(atom + 3, "pressed"))
    866 					return COMPRESSED;
    867 				break;
    868 			}
    869 			if (isascii(atom[2]) &&
    870 			    tolower((unsigned char)atom[2]) == 'n') {
    871 				if (!strcasecmp(atom + 3, "cat"))
    872 					return CONCAT;
    873 				if (!strcasecmp(atom + 3, "fig-option"))
    874 					return CONFIG_OPTION;
    875 				if (!strcasecmp(atom + 3, "flict-done"))
    876 					return CONFLICT_DONE;
    877 				if (!strcasecmp(atom + 3, "nect"))
    878 					return CONNECT;
    879 				break;
    880 			}
    881 			break;
    882 		}
    883 		if (!strcasecmp(atom + 1, "reate"))
    884 			return TOKEN_CREATE;
    885 		break;
    886 	case 'd':
    887 		if (!strcasecmp(atom + 1, "b-time-format"))
    888 			return DB_TIME_FORMAT;
    889 		if (!strcasecmp(atom + 1, "omain"))
    890 			return DOMAIN;
    891 		if (!strncasecmp(atom + 1, "omain-", 6)) {
    892 			if (!strcasecmp(atom + 7, "name"))
    893 				return DOMAIN_NAME;
    894 			if (!strcasecmp(atom + 7, "list"))
    895 				return DOMAIN_LIST;
    896 		}
    897 		if (!strcasecmp(atom + 1, "o-forward-updates"))
    898 			return DO_FORWARD_UPDATE;
    899 		/* do-forward-update is included for historical reasons */
    900 		if (!strcasecmp(atom + 1, "o-forward-update"))
    901 			return DO_FORWARD_UPDATE;
    902 		if (!strcasecmp(atom + 1, "ebug"))
    903 			return TOKEN_DEBUG;
    904 		if (!strcasecmp(atom + 1, "eny"))
    905 			return DENY;
    906 		if (!strcasecmp(atom + 1, "eleted"))
    907 			return TOKEN_DELETED;
    908 		if (!strcasecmp(atom + 1, "elete"))
    909 			return TOKEN_DELETE;
    910 		if (!strncasecmp(atom + 1, "efault", 6)) {
    911 			if (!atom [7])
    912 				return DEFAULT;
    913 			if (!strcasecmp(atom + 7, "-duid"))
    914 				return DEFAULT_DUID;
    915 			if (!strcasecmp(atom + 7, "-lease-time"))
    916 				return DEFAULT_LEASE_TIME;
    917 			break;
    918 		}
    919 		if (!strncasecmp(atom + 1, "ynamic", 6)) {
    920 			if (!atom [7])
    921 				return DYNAMIC;
    922 			if (!strncasecmp(atom + 7, "-bootp", 6)) {
    923 				if (!atom [13])
    924 					return DYNAMIC_BOOTP;
    925 				if (!strcasecmp(atom + 13, "-lease-cutoff"))
    926 					return DYNAMIC_BOOTP_LEASE_CUTOFF;
    927 				if (!strcasecmp(atom + 13, "-lease-length"))
    928 					return DYNAMIC_BOOTP_LEASE_LENGTH;
    929 				break;
    930 			}
    931 		}
    932 		if (!strcasecmp(atom + 1, "uplicates"))
    933 			return DUPLICATES;
    934 		if (!strcasecmp(atom + 1, "eclines"))
    935 			return DECLINES;
    936 		if (!strncasecmp(atom + 1, "efine", 5)) {
    937 			if (!strcasecmp(atom + 6, "d"))
    938 				return DEFINED;
    939 			if (!atom [6])
    940 				return DEFINE;
    941 		}
    942 		break;
    943 	case 'e':
    944 		if (isascii(atom [1]) &&
    945 		    tolower((unsigned char)atom[1]) == 'x') {
    946 			if (!strcasecmp(atom + 2, "tract-int"))
    947 				return EXTRACT_INT;
    948 			if (!strcasecmp(atom + 2, "ists"))
    949 				return EXISTS;
    950 			if (!strcasecmp(atom + 2, "piry"))
    951 				return EXPIRY;
    952 			if (!strcasecmp(atom + 2, "pire"))
    953 				return EXPIRE;
    954 			if (!strcasecmp(atom + 2, "pired"))
    955 				return TOKEN_EXPIRED;
    956 		}
    957 		if (!strcasecmp(atom + 1, "ncode-int"))
    958 			return ENCODE_INT;
    959 		if (!strcasecmp(atom + 1, "poch"))
    960 			return EPOCH;
    961 		if (!strcasecmp(atom + 1, "thernet"))
    962 			return ETHERNET;
    963 		if (!strcasecmp(atom + 1, "nds"))
    964 			return ENDS;
    965 		if (!strncasecmp(atom + 1, "ls", 2)) {
    966 			if (!strcasecmp(atom + 3, "e"))
    967 				return ELSE;
    968 			if (!strcasecmp(atom + 3, "if"))
    969 				return ELSIF;
    970 			break;
    971 		}
    972 		if (!strcasecmp(atom + 1, "rror"))
    973 			return ERROR;
    974 		if (!strcasecmp(atom + 1, "val"))
    975 			return EVAL;
    976 		if (!strcasecmp(atom + 1, "ncapsulate"))
    977 			return ENCAPSULATE;
    978 		if (!strcasecmp(atom + 1, "xecute"))
    979 			return EXECUTE;
    980 		if (!strcasecmp(atom+1, "n")) {
    981 			return EN;
    982 		}
    983 		break;
    984 	case 'f':
    985 		if (!strcasecmp(atom + 1, "atal"))
    986 			return FATAL;
    987 		if (!strcasecmp(atom + 1, "ilename"))
    988 			return FILENAME;
    989 		if (!strcasecmp(atom + 1, "ixed-address"))
    990 			return FIXED_ADDR;
    991 		if (!strcasecmp(atom + 1, "ixed-address6"))
    992 			return FIXED_ADDR6;
    993 		if (!strcasecmp(atom + 1, "ixed-prefix6"))
    994 			return FIXED_PREFIX6;
    995 		if (!strcasecmp(atom + 1, "ddi"))
    996 			return TOKEN_FDDI;
    997 		if (!strcasecmp(atom + 1, "ormerr"))
    998 			return NS_FORMERR;
    999 		if (!strcasecmp(atom + 1, "unction"))
   1000 			return FUNCTION;
   1001 		if (!strcasecmp(atom + 1, "ailover"))
   1002 			return FAILOVER;
   1003 		if (!strcasecmp(atom + 1, "ree"))
   1004 			return TOKEN_FREE;
   1005 		break;
   1006 	case 'g':
   1007 		if (!strncasecmp(atom + 1, "et", 2)) {
   1008 			if (!strcasecmp(atom + 3, "-lease-hostnames"))
   1009 				return GET_LEASE_HOSTNAMES;
   1010 			if (!strcasecmp(atom + 3, "hostbyname"))
   1011 				return GETHOSTBYNAME;
   1012 			if (!strcasecmp(atom + 3, "hostname"))
   1013 				return GETHOSTNAME;
   1014 			break;
   1015 		}
   1016 		if (!strcasecmp(atom + 1, "iaddr"))
   1017 			return GIADDR;
   1018 		if (!strcasecmp(atom + 1, "roup"))
   1019 			return GROUP;
   1020 		break;
   1021 	case 'h':
   1022 		if (!strcasecmp(atom + 1, "ash"))
   1023 			return HASH;
   1024 		if (!strcasecmp(atom + 1, "ba"))
   1025 			return HBA;
   1026 		if (!strcasecmp(atom + 1, "ost"))
   1027 			return HOST;
   1028 		if (!strcasecmp(atom + 1, "ost-decl-name"))
   1029 			return HOST_DECL_NAME;
   1030 		if (!strcasecmp(atom + 1, "ost-identifier"))
   1031 			return HOST_IDENTIFIER;
   1032 		if (!strcasecmp(atom + 1, "ardware"))
   1033 			return HARDWARE;
   1034 		if (!strcasecmp(atom + 1, "ostname"))
   1035 			return HOSTNAME;
   1036 		if (!strcasecmp(atom + 1, "elp"))
   1037 			return TOKEN_HELP;
   1038 		if (!strcasecmp(atom + 1, "ex")) {
   1039 			return TOKEN_HEX;
   1040 		}
   1041 		break;
   1042 	case 'i':
   1043 	      	if (!strcasecmp(atom+1, "a-na"))
   1044 			return IA_NA;
   1045 	      	if (!strcasecmp(atom+1, "a-ta"))
   1046 			return IA_TA;
   1047 	      	if (!strcasecmp(atom+1, "a-pd"))
   1048 			return IA_PD;
   1049 	      	if (!strcasecmp(atom+1, "aaddr"))
   1050 			return IAADDR;
   1051 	      	if (!strcasecmp(atom+1, "aprefix"))
   1052 			return IAPREFIX;
   1053 		if (!strcasecmp(atom + 1, "nclude"))
   1054 			return INCLUDE;
   1055 		if (!strcasecmp(atom + 1, "nteger"))
   1056 			return INTEGER;
   1057 		if (!strcasecmp(atom  + 1, "nfiniband"))
   1058 			return TOKEN_INFINIBAND;
   1059 		if (!strcasecmp(atom + 1, "nfinite"))
   1060 			return INFINITE;
   1061 		if (!strcasecmp(atom + 1, "nfo"))
   1062 			return INFO;
   1063 		if (!strcasecmp(atom + 1, "p-address"))
   1064 			return IP_ADDRESS;
   1065 		if (!strcasecmp(atom + 1, "p6-address"))
   1066 			return IP6_ADDRESS;
   1067 		if (!strcasecmp(atom + 1, "nitial-interval"))
   1068 			return INITIAL_INTERVAL;
   1069 		if (!strcasecmp(atom + 1, "nitial-delay"))
   1070 			return INITIAL_DELAY;
   1071 		if (!strcasecmp(atom + 1, "nterface"))
   1072 			return INTERFACE;
   1073 		if (!strcasecmp(atom + 1, "dentifier"))
   1074 			return IDENTIFIER;
   1075 		if (!strcasecmp(atom + 1, "f"))
   1076 			return IF;
   1077 		if (!strcasecmp(atom + 1, "s"))
   1078 			return IS;
   1079 		if (!strcasecmp(atom + 1, "gnore"))
   1080 			return IGNORE;
   1081 		break;
   1082 	case 'k':
   1083 		if (!strncasecmp(atom + 1, "nown", 4)) {
   1084 			if (!strcasecmp(atom + 5, "-clients"))
   1085 				return KNOWN_CLIENTS;
   1086 			if (!atom[5])
   1087 				return KNOWN;
   1088 			break;
   1089 		}
   1090 		if (!strcasecmp(atom + 1, "ey"))
   1091 			return KEY;
   1092 		if (!strcasecmp (atom + 1, "ey-algorithm"))
   1093 			return KEY_ALGORITHM;
   1094 		break;
   1095 	case 'l':
   1096 		if (!strcasecmp(atom + 1, "case"))
   1097 			return LCASE;
   1098 		if (!strcasecmp(atom + 1, "ease"))
   1099 			return LEASE;
   1100 		if (!strcasecmp(atom + 1, "ease6"))
   1101 			return LEASE6;
   1102 		if (!strcasecmp(atom + 1, "eased-address"))
   1103 			return LEASED_ADDRESS;
   1104 		if (!strcasecmp(atom + 1, "ease-time"))
   1105 			return LEASE_TIME;
   1106 		if (!strcasecmp(atom + 1, "easequery"))
   1107 			return LEASEQUERY;
   1108 		if (!strcasecmp(atom + 1, "ength"))
   1109 			return LENGTH;
   1110 		if (!strcasecmp(atom + 1, "imit"))
   1111 			return LIMIT;
   1112 		if (!strcasecmp(atom + 1, "et"))
   1113 			return LET;
   1114 		if (!strcasecmp(atom + 1, "oad"))
   1115 			return LOAD;
   1116 		if (!strcasecmp(atom + 1, "ocal"))
   1117 			return LOCAL;
   1118 		if (!strcasecmp(atom + 1, "og"))
   1119 			return LOG;
   1120 		if (!strcasecmp(atom+1, "lt")) {
   1121 			return LLT;
   1122 		}
   1123 		if (!strcasecmp(atom+1, "l")) {
   1124 			return LL;
   1125 		}
   1126 		if (!strcasecmp(atom+1, "ittle-endian")) {
   1127 			return TOKEN_LITTLE_ENDIAN;
   1128 		}
   1129 		if (!strcasecmp(atom + 1, "ease-id-format")) {
   1130 			return LEASE_ID_FORMAT;
   1131 		}
   1132 		break;
   1133 	case 'm':
   1134 		if (!strncasecmp(atom + 1, "ax", 2)) {
   1135 			if (!atom [3])
   1136 				return TOKEN_MAX;
   1137 			if (!strcasecmp(atom + 3, "-balance"))
   1138 				return MAX_BALANCE;
   1139 			if (!strncasecmp(atom + 3, "-lease-", 7)) {
   1140 				if (!strcasecmp(atom + 10, "misbalance"))
   1141 					return MAX_LEASE_MISBALANCE;
   1142 				if (!strcasecmp(atom + 10, "ownership"))
   1143 					return MAX_LEASE_OWNERSHIP;
   1144 				if (!strcasecmp(atom + 10, "time"))
   1145 					return MAX_LEASE_TIME;
   1146 			}
   1147 			if (!strcasecmp(atom + 3, "-life"))
   1148 				return MAX_LIFE;
   1149 			if (!strcasecmp(atom + 3, "-transmit-idle"))
   1150 				return MAX_TRANSMIT_IDLE;
   1151 			if (!strcasecmp(atom + 3, "-response-delay"))
   1152 				return MAX_RESPONSE_DELAY;
   1153 			if (!strcasecmp(atom + 3, "-unacked-updates"))
   1154 				return MAX_UNACKED_UPDATES;
   1155 		}
   1156 		if (!strncasecmp(atom + 1, "in-", 3)) {
   1157 			if (!strcasecmp(atom + 4, "balance"))
   1158 				return MIN_BALANCE;
   1159 			if (!strcasecmp(atom + 4, "lease-time"))
   1160 				return MIN_LEASE_TIME;
   1161 			if (!strcasecmp(atom + 4, "secs"))
   1162 				return MIN_SECS;
   1163 			break;
   1164 		}
   1165 		if (!strncasecmp(atom + 1, "edi", 3)) {
   1166 			if (!strcasecmp(atom + 4, "a"))
   1167 				return MEDIA;
   1168 			if (!strcasecmp(atom + 4, "um"))
   1169 				return MEDIUM;
   1170 			break;
   1171 		}
   1172 		if (!strcasecmp(atom + 1, "atch"))
   1173 			return MATCH;
   1174 		if (!strcasecmp(atom + 1, "embers"))
   1175 			return MEMBERS;
   1176 		if (!strcasecmp(atom + 1, "y"))
   1177 			return MY;
   1178 		if (!strcasecmp(atom + 1, "clt"))
   1179 			return MCLT;
   1180 		break;
   1181 	case 'n':
   1182 		if (!strcasecmp(atom + 1, "ormal"))
   1183 			return NORMAL;
   1184 		if (!strcasecmp(atom + 1, "ameserver"))
   1185 			return NAMESERVER;
   1186 		if (!strcasecmp(atom + 1, "etmask"))
   1187 			return NETMASK;
   1188 		if (!strcasecmp(atom + 1, "ever"))
   1189 			return NEVER;
   1190 		if (!strcasecmp(atom + 1, "ext-server"))
   1191 			return NEXT_SERVER;
   1192 		if (!strcasecmp(atom + 1, "ot"))
   1193 			return TOKEN_NOT;
   1194 		if (!strcasecmp(atom + 1, "o"))
   1195 			return TOKEN_NO;
   1196 		if (!strcasecmp(atom + 1, "oerror"))
   1197 			return NS_NOERROR;
   1198 		if (!strcasecmp(atom + 1, "otauth"))
   1199 			return NS_NOTAUTH;
   1200 		if (!strcasecmp(atom + 1, "otimp"))
   1201 			return NS_NOTIMP;
   1202 		if (!strcasecmp(atom + 1, "otzone"))
   1203 			return NS_NOTZONE;
   1204 		if (!strcasecmp(atom + 1, "xdomain"))
   1205 			return NS_NXDOMAIN;
   1206 		if (!strcasecmp(atom + 1, "xrrset"))
   1207 			return NS_NXRRSET;
   1208 		if (!strcasecmp(atom + 1, "ull"))
   1209 			return TOKEN_NULL;
   1210 		if (!strcasecmp(atom + 1, "ext"))
   1211 			return TOKEN_NEXT;
   1212 		if (!strcasecmp(atom + 1, "ew"))
   1213 			return TOKEN_NEW;
   1214 		break;
   1215 	case 'o':
   1216 		if (!strcasecmp(atom + 1, "mapi"))
   1217 			return OMAPI;
   1218 		if (!strcasecmp(atom + 1, "r"))
   1219 			return OR;
   1220 		if (!strcasecmp(atom + 1, "n"))
   1221 			return ON;
   1222 		if (!strcasecmp(atom + 1, "pen"))
   1223 			return TOKEN_OPEN;
   1224 		if (!strcasecmp(atom + 1, "ption"))
   1225 			return OPTION;
   1226 		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
   1227 			return ONE_LEASE_PER_CLIENT;
   1228 		if (!strcasecmp(atom + 1, "f"))
   1229 			return OF;
   1230 		if (!strcasecmp(atom + 1, "wner"))
   1231 			return OWNER;
   1232 		if (!strcasecmp(atom + 1, "ctal")) {
   1233 			return TOKEN_OCTAL;
   1234 		}
   1235 		break;
   1236 	case 'p':
   1237 		if (!strcasecmp(atom + 1, "arse-vendor-option"))
   1238 			return PARSE_VENDOR_OPT;
   1239 		if (!strcasecmp(atom + 1, "repend"))
   1240 			return PREPEND;
   1241 		if (!strcasecmp(atom + 1, "referred-life"))
   1242 			return PREFERRED_LIFE;
   1243 		if (!strcasecmp(atom + 1, "acket"))
   1244 			return PACKET;
   1245 		if (!strcasecmp(atom + 1, "ool"))
   1246 			return POOL;
   1247 		if (!strcasecmp(atom + 1, "ool6"))
   1248 			return POOL6;
   1249 		if (!strcasecmp(atom + 1, "refix6"))
   1250 			return PREFIX6;
   1251 		if (!strcasecmp(atom + 1, "seudo"))
   1252 			return PSEUDO;
   1253 		if (!strcasecmp(atom + 1, "eer"))
   1254 			return PEER;
   1255 		if (!strcasecmp(atom + 1, "rimary"))
   1256 			return PRIMARY;
   1257 		if (!strcasecmp(atom + 1, "rimary6"))
   1258 			return PRIMARY6;
   1259 		if (!strncasecmp(atom + 1, "artner", 6)) {
   1260 			if (!atom [7])
   1261 				return PARTNER;
   1262 			if (!strcasecmp(atom + 7, "-down"))
   1263 				return PARTNER_DOWN;
   1264 		}
   1265 		if (!strcasecmp(atom + 1, "ort"))
   1266 			return PORT;
   1267 		if (!strcasecmp(atom + 1, "otential-conflict"))
   1268 			return POTENTIAL_CONFLICT;
   1269 		if (!strcasecmp(atom + 1, "ick-first-value") ||
   1270 		    !strcasecmp(atom + 1, "ick"))
   1271 			return PICK;
   1272 		if (!strcasecmp(atom + 1, "aused"))
   1273 			return PAUSED;
   1274 		break;
   1275 	case 'r':
   1276 		if (!strcasecmp(atom + 1, "ange"))
   1277 			return RANGE;
   1278 		if (!strcasecmp(atom + 1, "ange6"))
   1279 			return RANGE6;
   1280 		if (isascii(atom[1]) &&
   1281 		    (tolower((unsigned char)atom[1]) == 'e')) {
   1282 			if (!strcasecmp(atom + 2, "bind"))
   1283 				return REBIND;
   1284 			if (!strcasecmp(atom + 2, "boot"))
   1285 				return REBOOT;
   1286 			if (!strcasecmp(atom + 2, "contact-interval"))
   1287 				return RECONTACT_INTERVAL;
   1288 			if (!strncasecmp(atom + 2, "cover", 5)) {
   1289 				if (atom[7] == '\0')
   1290 					return RECOVER;
   1291 				if (!strcasecmp(atom + 7, "-done"))
   1292 					return RECOVER_DONE;
   1293 				if (!strcasecmp(atom + 7, "-wait"))
   1294 					return RECOVER_WAIT;
   1295 				break;
   1296 			}
   1297 			if (!strcasecmp(atom + 2, "fresh"))
   1298 				return REFRESH;
   1299 			if (!strcasecmp(atom + 2, "fused"))
   1300 				return NS_REFUSED;
   1301 			if (!strcasecmp(atom + 2, "ject"))
   1302 				return REJECT;
   1303 			if (!strcasecmp(atom + 2, "lease"))
   1304 				return RELEASE;
   1305 			if (!strcasecmp(atom + 2, "leased"))
   1306 				return TOKEN_RELEASED;
   1307 			if (!strcasecmp(atom + 2, "move"))
   1308 				return REMOVE;
   1309 			if (!strcasecmp(atom + 2, "new"))
   1310 				return RENEW;
   1311 			if (!strcasecmp(atom + 2, "quest"))
   1312 				return REQUEST;
   1313 			if (!strcasecmp(atom + 2, "quire"))
   1314 				return REQUIRE;
   1315 			if (isascii(atom[2]) &&
   1316 			    (tolower((unsigned char)atom[2]) == 's')) {
   1317 				if (!strcasecmp(atom + 3, "erved"))
   1318 					return TOKEN_RESERVED;
   1319 				if (!strcasecmp(atom + 3, "et"))
   1320 					return TOKEN_RESET;
   1321 				if (!strcasecmp(atom + 3,
   1322 						"olution-interrupted"))
   1323 					return RESOLUTION_INTERRUPTED;
   1324 				break;
   1325 			}
   1326 			if (!strcasecmp(atom + 2, "try"))
   1327 				return RETRY;
   1328 			if (!strcasecmp(atom + 2, "turn"))
   1329 				return RETURN;
   1330 			if (!strcasecmp(atom + 2, "verse"))
   1331 				return REVERSE;
   1332 			if (!strcasecmp(atom + 2, "wind"))
   1333 				return REWIND;
   1334 			break;
   1335 		}
   1336 		break;
   1337 	case 's':
   1338 		if (!strcasecmp(atom + 1, "cript"))
   1339 			return SCRIPT;
   1340 		if (isascii(atom[1]) &&
   1341 		    tolower((unsigned char)atom[1]) == 'e') {
   1342 			if (!strcasecmp(atom + 2, "arch"))
   1343 				return SEARCH;
   1344 			if (isascii(atom[2]) &&
   1345 			    tolower((unsigned char)atom[2]) == 'c') {
   1346 				if (!strncasecmp(atom + 3, "ond", 3)) {
   1347                                         if (!strcasecmp(atom + 6, "ary"))
   1348 						return SECONDARY;
   1349                                         if (!strcasecmp(atom + 6, "ary6"))
   1350 						return SECONDARY6;
   1351                                         if (!strcasecmp(atom + 6, "s"))
   1352                                                 return SECONDS;
   1353 					break;
   1354 				}
   1355                                 if (!strcasecmp(atom + 3, "ret"))
   1356                                         return SECRET;
   1357 				break;
   1358 			}
   1359 			if (!strncasecmp(atom + 2, "lect", 4)) {
   1360                                 if (atom[6] == '\0')
   1361                                         return SELECT;
   1362                                 if (!strcasecmp(atom + 6, "-timeout"))
   1363                                         return SELECT_TIMEOUT;
   1364 				break;
   1365 			}
   1366                         if (!strcasecmp(atom + 2, "nd"))
   1367                                 return SEND;
   1368 			if (!strncasecmp(atom + 2, "rv", 2)) {
   1369 				if (!strncasecmp(atom + 4, "er", 2)) {
   1370                                         if (atom[6] == '\0')
   1371                                                 return TOKEN_SERVER;
   1372 					if (atom[6] == '-') {
   1373 						if (!strcasecmp(atom + 7,
   1374 								"duid"))
   1375 							return SERVER_DUID;
   1376                                                 if (!strcasecmp(atom + 7,
   1377 								"name"))
   1378                                                         return SERVER_NAME;
   1379                                                 if (!strcasecmp(atom + 7,
   1380 								"identifier"))
   1381                                                       return SERVER_IDENTIFIER;
   1382 						break;
   1383 					}
   1384 					break;
   1385 				}
   1386                                 if (!strcasecmp(atom + 4, "fail"))
   1387                                         return NS_SERVFAIL;
   1388 				break;
   1389 			}
   1390                         if (!strcasecmp(atom + 2, "t"))
   1391                                 return TOKEN_SET;
   1392 			break;
   1393 		}
   1394 		if (isascii(atom[1]) &&
   1395 		    tolower((unsigned char)atom[1]) == 'h') {
   1396                         if (!strcasecmp(atom + 2, "ared-network"))
   1397                                 return SHARED_NETWORK;
   1398                         if (!strcasecmp(atom + 2, "utdown"))
   1399                                 return SHUTDOWN;
   1400 			break;
   1401 		}
   1402 		if (isascii(atom[1]) &&
   1403 		    tolower((unsigned char)atom[1]) == 'i') {
   1404                         if (!strcasecmp(atom + 2, "addr"))
   1405                                 return SIADDR;
   1406                         if (!strcasecmp(atom + 2, "gned"))
   1407                                 return SIGNED;
   1408                         if (!strcasecmp(atom + 2, "ze"))
   1409                                 return SIZE;
   1410 			break;
   1411 		}
   1412 		if (isascii(atom[1]) &&
   1413 		    tolower((unsigned char)atom[1]) == 'p') {
   1414 			if (isascii(atom[2]) &&
   1415 			    tolower((unsigned char)atom[2]) == 'a') {
   1416                                 if (!strcasecmp(atom + 3, "ce"))
   1417                                         return SPACE;
   1418                                 if (!strcasecmp(atom + 3, "wn"))
   1419                                         return SPAWN;
   1420 				break;
   1421 			}
   1422                         if (!strcasecmp(atom + 2, "lit"))
   1423                                 return SPLIT;
   1424 			break;
   1425 		}
   1426 		if (isascii(atom[1]) &&
   1427 		    tolower((unsigned char)atom[1]) == 't') {
   1428 			if (isascii(atom[2]) &&
   1429 			    tolower((unsigned char)atom[2]) == 'a') {
   1430 				if (!strncasecmp(atom + 3, "rt", 2)) {
   1431                                          if (!strcasecmp(atom + 5, "s"))
   1432                                                  return STARTS;
   1433                                          if (!strcasecmp(atom + 5, "up"))
   1434                                                  return STARTUP;
   1435 					break;
   1436 				}
   1437 				if (isascii(atom[3]) &&
   1438 				    tolower((unsigned char)atom[3]) == 't') {
   1439                                         if (!strcasecmp(atom + 4, "e"))
   1440                                                 return STATE;
   1441                                         if (!strcasecmp(atom + 4, "ic"))
   1442                                                 return STATIC;
   1443 					break;
   1444 				}
   1445 			}
   1446                         if (!strcasecmp(atom + 2, "ring"))
   1447                                 return STRING_TOKEN;
   1448 			break;
   1449 		}
   1450                 if (!strncasecmp(atom + 1, "ub", 2)) {
   1451                         if (!strcasecmp(atom + 3, "class"))
   1452                                 return SUBCLASS;
   1453                         if (!strcasecmp(atom + 3, "net"))
   1454                                 return SUBNET;
   1455                         if (!strcasecmp(atom + 3, "net6"))
   1456                                 return SUBNET6;
   1457                         if (!strcasecmp(atom + 3, "string"))
   1458                                 return SUBSTRING;
   1459                         break;
   1460                 }
   1461 		if (isascii(atom[1]) &&
   1462 		    tolower((unsigned char)atom[1]) == 'u') {
   1463                         if (!strcasecmp(atom + 2, "ffix"))
   1464                                 return SUFFIX;
   1465                         if (!strcasecmp(atom + 2, "persede"))
   1466                                 return SUPERSEDE;
   1467 		}
   1468                 if (!strcasecmp(atom + 1, "witch"))
   1469                         return SWITCH;
   1470 		break;
   1471 	case 't':
   1472 		if (!strcasecmp(atom + 1, "imestamp"))
   1473 			return TIMESTAMP;
   1474 		if (!strcasecmp(atom + 1, "imeout"))
   1475 			return TIMEOUT;
   1476 		if (!strcasecmp(atom + 1, "oken-ring"))
   1477 			return TOKEN_RING;
   1478 		if (!strcasecmp(atom + 1, "ext"))
   1479 			return TEXT;
   1480 		if (!strcasecmp(atom + 1, "stp"))
   1481 			return TSTP;
   1482 		if (!strcasecmp(atom + 1, "sfp"))
   1483 			return TSFP;
   1484 		if (!strcasecmp(atom + 1, "ransmission"))
   1485 			return TRANSMISSION;
   1486 		if (!strcasecmp(atom + 1, "emporary"))
   1487 			return TEMPORARY;
   1488 		break;
   1489 	case 'u':
   1490 		if (!strcasecmp(atom + 1, "case"))
   1491 			return UCASE;
   1492 		if (!strcasecmp(atom + 1, "nset"))
   1493 			return UNSET;
   1494 		if (!strcasecmp(atom + 1, "nsigned"))
   1495 			return UNSIGNED;
   1496 		if (!strcasecmp(atom + 1, "id"))
   1497 			return UID;
   1498 		if (!strncasecmp(atom + 1, "se", 2)) {
   1499 			if (!strcasecmp(atom + 3, "r-class"))
   1500 				return USER_CLASS;
   1501 			if (!strcasecmp(atom + 3, "-host-decl-names"))
   1502 				return USE_HOST_DECL_NAMES;
   1503 			if (!strcasecmp(atom + 3,
   1504 					 "-lease-addr-for-default-route"))
   1505 				return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
   1506 			break;
   1507 		}
   1508 		if (!strncasecmp(atom + 1, "nknown", 6)) {
   1509 			if (!strcasecmp(atom + 7, "-clients"))
   1510 				return UNKNOWN_CLIENTS;
   1511 			if (!strcasecmp(atom + 7, "-state"))
   1512 				return UNKNOWN_STATE;
   1513 			if (!atom [7])
   1514 				return UNKNOWN;
   1515 			break;
   1516 		}
   1517 		if (!strcasecmp(atom + 1, "nauthenticated"))
   1518 			return UNAUTHENTICATED;
   1519 		if (!strcasecmp(atom + 1, "pdate"))
   1520 			return UPDATE;
   1521 		break;
   1522 	case 'v':
   1523 		if (!strcasecmp(atom + 1, "6relay"))
   1524 			return V6RELAY;
   1525 		if (!strcasecmp(atom + 1, "6relopt"))
   1526 			return V6RELOPT;
   1527 		if (!strcasecmp(atom + 1, "endor-class"))
   1528 			return VENDOR_CLASS;
   1529 		if (!strcasecmp(atom + 1, "endor"))
   1530 			return VENDOR;
   1531 		break;
   1532 	case 'w':
   1533 		if (!strcasecmp(atom + 1, "ith"))
   1534 			return WITH;
   1535 		if (!strcasecmp(atom + 1, "idth"))
   1536 			return WIDTH;
   1537 		break;
   1538 	case 'y':
   1539 		if (!strcasecmp(atom + 1, "iaddr"))
   1540 			return YIADDR;
   1541 		if (!strcasecmp(atom + 1, "xdomain"))
   1542 			return NS_YXDOMAIN;
   1543 		if (!strcasecmp(atom + 1, "xrrset"))
   1544 			return NS_YXRRSET;
   1545 		break;
   1546 	case 'z':
   1547 		if (!strcasecmp(atom + 1, "erolen"))
   1548 			return ZEROLEN;
   1549 		if (!strcasecmp(atom + 1, "one"))
   1550 			return ZONE;
   1551 		break;
   1552 	}
   1553 	return dfv;
   1554 }
   1555 
   1556