text: /*	$OpenBSD: conflex.c,v 1.7 2004/09/15 19:02:38 deraadt Exp $	*/
  
  /* Lexical scanner for dhcpd config file... */
  
  /*-
   * SPDX-License-Identifier: BSD-3-Clause
   *
   * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. Neither the name of The Internet Software Consortium nor the names
   *    of its contributors may be used to endorse or promote products derived
   *    from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
   * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   *
   * This software has been written for the Internet Software Consortium
   * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
   * Enterprises.  To learn more about the Internet Software Consortium,
   * see ``http://www.vix.com/isc''.  To learn more about Vixie
   * Enterprises, see ``http://www.vix.com''.
   */
  
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
  #include <ctype.h>
  
  #include "dhcpd.h"
  #include "dhctoken.h"
  
  int lexline;
  int lexchar;
  char *token_line;
  static char *prev_line;
  static char *cur_line;
  const char *tlname;
  int eol_token;
  
  static char line1[81];
  static char line2[81];
  static unsigned lpos;
  static unsigned line;
  static int tlpos;
  static int tline;
  static int token;
  static int ugflag;
  static char *tval;
  static char tokbuf[1500];
  
  static int get_char(FILE *);
  static int get_token(FILE *);
  static void skip_to_eol(FILE *);
  static int read_string(FILE *);
  static int read_number(int, FILE *);
  static int read_num_or_name(int, FILE *);
  static int intern(char *, int);
  
  void
  new_parse(const char *name)
  {
  	tlname = name;
  	lpos = line = 1;
  	cur_line = line1;
  	prev_line = line2;
  	token_line = cur_line;
  	cur_line[0] = prev_line[0] = 0;
  	warnings_occurred = 0;
  }
  
  static int
  get_char(FILE *cfile)
  {
  	int c = getc(cfile);
  	if (!ugflag) {
  		if (c == '\n') {
  			if (cur_line == line1) {
  				cur_line = line2;
  				prev_line = line1;
  			} else {
  				cur_line = line1;
  				prev_line = line2;
  			}
  			line++;
  			lpos = 1;
  			cur_line[0] = 0;
  		} else if (c != EOF) {
  			if (lpos < sizeof(line1)) {
  				cur_line[lpos - 1] = c;
  				cur_line[lpos] = 0;
  			}
  			lpos++;
  		}
  	} else
  		ugflag = 0;
  	return (c);
  }
  
  static int
  get_token(FILE *cfile)
  {
  	int		c, ttok;
  	static char	tb[2];
  	int		l, p;
  
  	do {
  		l = line;
  		p = lpos;
  
  		c = get_char(cfile);
  
  		if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
  			continue;
  		if (c == '#') {
  			skip_to_eol(cfile);
  			continue;
  		}
  		if (c == '"') {
  			lexline = l;
  			lexchar = p;
  			ttok = read_string(cfile);
  			break;
  		}
  		if ((isascii(c) && isdigit(c)) || c == '-') {
  			lexline = l;
  			lexchar = p;
  			ttok = read_number(c, cfile);
  			break;
  		} else if (isascii(c) && isalpha(c)) {
  			lexline = l;
  			lexchar = p;
  			ttok = read_num_or_name(c, cfile);
  			break;
  		} else {
  			lexline = l;
  			lexchar = p;
  			tb[0] = c;
  			tb[1] = 0;
  			tval = tb;
  			ttok = c;
  			break;
  		}
  	} while (1);
  	return (ttok);
  }
  
  int
  next_token(char **rval, FILE *cfile)
  {
  	int	rv;
  
  	if (token) {
  		if (lexline != tline)
  			token_line = cur_line;
  		lexchar = tlpos;
  		lexline = tline;
  		rv = token;
  		token = 0;
  	} else {
  		rv = get_token(cfile);
  		token_line = cur_line;
  	}
  	if (rval)
  		*rval = tval;
  
  	return (rv);
  }
  
  int
  peek_token(char **rval, FILE *cfile)
  {
  	int	x;
  
  	if (!token) {
  		tlpos = lexchar;
  		tline = lexline;
  		token = get_token(cfile);
  		if (lexline != tline)
  			token_line = prev_line;
  		x = lexchar;
  		lexchar = tlpos;
  		tlpos = x;
  		x = lexline;
  		lexline = tline;
  		tline = x;
  	}
  	if (rval)
  		*rval = tval;
  
  	return (token);
  }
  
  static void
  skip_to_eol(FILE *cfile)
  {
  	int	c;
  
  	do {
  		c = get_char(cfile);
  		if (c == EOF)
  			return;
  		if (c == '\n')
  			return;
  	} while (1);
  }
  
  static int
  read_string(FILE *cfile)
  {
  	int	c, bs = 0;
  	unsigned i;
  
  	for (i = 0; i < sizeof(tokbuf); i++) {
  		c = get_char(cfile);
  		if (c == EOF) {
  			parse_warn("eof in string constant");
  			break;
  		}
  		if (bs) {
  			bs = 0;
  			i--;
  			tokbuf[i] = c;
  		} else if (c == '\\')
  			bs = 1;
  		else if (c == '"')
  			break;
  		else
  			tokbuf[i] = c;
  	}
  	/*
  	 * Normally, I'd feel guilty about this, but we're talking about
  	 * strings that'll fit in a DHCP packet here...
  	 */
  	if (i == sizeof(tokbuf)) {
  		parse_warn("string constant larger than internal buffer");
  		i--;
  	}
  	tokbuf[i] = 0;
  	tval = tokbuf;
  	return (STRING);
  }
  
  static int
  read_number(int c, FILE *cfile)
  {
  	int	seenx = 0, _token = NUMBER;
  	unsigned i = 0;
  
  	tokbuf[i++] = c;
  	for (; i < sizeof(tokbuf); i++) {
  		c = get_char(cfile);
  		if (!seenx && c == 'x')
  			seenx = 1;
  		else if (!isascii(c) || !isxdigit(c)) {
  			ungetc(c, cfile);
  			ugflag = 1;
  			break;
  		}
  		tokbuf[i] = c;
  	}
  	if (i == sizeof(tokbuf)) {
  		parse_warn("numeric token larger than internal buffer");
  		i--;
  	}
  	tokbuf[i] = 0;
  	tval = tokbuf;
  
  	return (_token);
  }
  
  static int
  read_num_or_name(int c, FILE *cfile)
  {
  	unsigned i = 0;
  	int	rv = NUMBER_OR_NAME;
  
  	tokbuf[i++] = c;
  	for (; i < sizeof(tokbuf); i++) {
  		c = get_char(cfile);
  		if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
  			ungetc(c, cfile);
  			ugflag = 1;
  			break;
  		}
  		if (!isxdigit(c))
  			rv = NAME;
  		tokbuf[i] = c;
  	}
  	if (i == sizeof(tokbuf)) {
  		parse_warn("token larger than internal buffer");
  		i--;
  	}
  	tokbuf[i] = 0;
  	tval = tokbuf;
  
  	return (intern(tval, rv));
  }
  
  static int
  intern(char *atom, int dfv)
  {
  	if (!isascii(atom[0]))
  		return (dfv);
  
  	switch (tolower(atom[0])) {
  	case 'a':
  		if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
  			return (ALWAYS_REPLY_RFC1048);
  		if (!strcasecmp(atom + 1, "ppend"))
  			return (APPEND);
  		if (!strcasecmp(atom + 1, "llow"))
  			return (ALLOW);
  		if (!strcasecmp(atom + 1, "lias"))
  			return (ALIAS);
  		if (!strcasecmp(atom + 1, "bandoned"))
  			return (ABANDONED);
  		if (!strcasecmp(atom + 1, "uthoritative"))
  			return (AUTHORITATIVE);
  		break;
  	case 'b':
  		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
  			return (BACKOFF_CUTOFF);
  		if (!strcasecmp(atom + 1, "ootp"))
  			return (BOOTP);
  		if (!strcasecmp(atom + 1, "ooting"))
  			return (BOOTING);
  		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
  			return (BOOT_UNKNOWN_CLIENTS);
  		break;
  	case 'c':
  		if (!strcasecmp(atom + 1, "lass"))
  			return (CLASS);
  		if (!strcasecmp(atom + 1, "iaddr"))
  			return (CIADDR);
  		if (!strcasecmp(atom + 1, "lient-identifier"))
  			return (CLIENT_IDENTIFIER);
  		if (!strcasecmp(atom + 1, "lient-hostname"))
  			return (CLIENT_HOSTNAME);
  		break;
  	case 'd':
  		if (!strcasecmp(atom + 1, "omain"))
  			return (DOMAIN);
  		if (!strcasecmp(atom + 1, "eny"))
  			return (DENY);
  		if (!strncasecmp(atom + 1, "efault", 6)) {
  			if (!atom[7])
  				return (DEFAULT);
  			if (!strcasecmp(atom + 7, "-lease-time"))
  				return (DEFAULT_LEASE_TIME);
  			break;
  		}
  		if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
  			if (!atom[13])
  				return (DYNAMIC_BOOTP);
  			if (!strcasecmp(atom + 13, "-lease-cutoff"))
  				return (DYNAMIC_BOOTP_LEASE_CUTOFF);
  			if (!strcasecmp(atom + 13, "-lease-length"))
  				return (DYNAMIC_BOOTP_LEASE_LENGTH);
  			break;
  		}
  		break;
  	case 'e':
  		if (!strcasecmp(atom + 1, "thernet"))
  			return (ETHERNET);
  		if (!strcasecmp(atom + 1, "nds"))
  			return (ENDS);
  		if (!strcasecmp(atom + 1, "xpire"))
  			return (EXPIRE);
  		break;
  	case 'f':
  		if (!strcasecmp(atom + 1, "ilename"))
  			return (FILENAME);
  		if (!strcasecmp(atom + 1, "ixed-address"))
  			return (FIXED_ADDR);
  		if (!strcasecmp(atom + 1, "ddi"))
  			return (FDDI);
  		break;
  	case 'g':
  		if (!strcasecmp(atom + 1, "iaddr"))
  			return (GIADDR);
  		if (!strcasecmp(atom + 1, "roup"))
  			return (GROUP);
  		if (!strcasecmp(atom + 1, "et-lease-hostnames"))
  			return (GET_LEASE_HOSTNAMES);
  		break;
  	case 'h':
  		if (!strcasecmp(atom + 1, "ost"))
  			return (HOST);
  		if (!strcasecmp(atom + 1, "ardware"))
  			return (HARDWARE);
  		if (!strcasecmp(atom + 1, "ostname"))
  			return (HOSTNAME);
  		break;
  	case 'i':
  		if (!strcasecmp(atom + 1, "nitial-interval"))
  			return (INITIAL_INTERVAL);
  		if (!strcasecmp(atom + 1, "nterface"))
  			return (INTERFACE);
  		break;
  	case 'l':
  		if (!strcasecmp(atom + 1, "ease"))
  			return (LEASE);
  		break;
  	case 'm':
  		if (!strcasecmp(atom + 1, "ax-lease-time"))
  			return (MAX_LEASE_TIME);
  		if (!strncasecmp(atom + 1, "edi", 3)) {
  			if (!strcasecmp(atom + 4, "a"))
  				return (MEDIA);
  			if (!strcasecmp(atom + 4, "um"))
  				return (MEDIUM);
  			break;
  		}
  		break;
  	case 'n':
  		if (!strcasecmp(atom + 1, "ameserver"))
  			return (NAMESERVER);
  		if (!strcasecmp(atom + 1, "etmask"))
  			return (NETMASK);
  		if (!strcasecmp(atom + 1, "ext-server"))
  			return (NEXT_SERVER);
  		if (!strcasecmp(atom + 1, "ot"))
  			return (TOKEN_NOT);
  		break;
  	case 'o':
  		if (!strcasecmp(atom + 1, "ption"))
  			return (OPTION);
  		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
  			return (ONE_LEASE_PER_CLIENT);
  		break;
  	case 'p':
  		if (!strcasecmp(atom + 1, "repend"))
  			return (PREPEND);
  		if (!strcasecmp(atom + 1, "acket"))
  			return (PACKET);
  		break;
  	case 'r':
  		if (!strcasecmp(atom + 1, "ange"))
  			return (RANGE);
  		if (!strcasecmp(atom + 1, "equest"))
  			return (REQUEST);
  		if (!strcasecmp(atom + 1, "equire"))
  			return (REQUIRE);
  		if (!strcasecmp(atom + 1, "etry"))
  			return (RETRY);
  		if (!strcasecmp(atom + 1, "enew"))
  			return (RENEW);
  		if (!strcasecmp(atom + 1, "ebind"))
  			return (REBIND);
  		if (!strcasecmp(atom + 1, "eboot"))
  			return (REBOOT);
  		if (!strcasecmp(atom + 1, "eject"))
  			return (REJECT);
  		break;
  	case 's':
  		if (!strcasecmp(atom + 1, "earch"))
  			return (SEARCH);
  		if (!strcasecmp(atom + 1, "tarts"))
  			return (STARTS);
  		if (!strcasecmp(atom + 1, "iaddr"))
  			return (SIADDR);
  		if (!strcasecmp(atom + 1, "ubnet"))
  			return (SUBNET);
  		if (!strcasecmp(atom + 1, "hared-network"))
  			return (SHARED_NETWORK);
  		if (!strcasecmp(atom + 1, "erver-name"))
  			return (SERVER_NAME);
  		if (!strcasecmp(atom + 1, "erver-identifier"))
  			return (SERVER_IDENTIFIER);
  		if (!strcasecmp(atom + 1, "elect-timeout"))
  			return (SELECT_TIMEOUT);
  		if (!strcasecmp(atom + 1, "end"))
  			return (SEND);
  		if (!strcasecmp(atom + 1, "cript"))
  			return (SCRIPT);
  		if (!strcasecmp(atom + 1, "upersede"))
  			return (SUPERSEDE);
  		break;
  	case 't':
  		if (!strcasecmp(atom + 1, "imestamp"))
  			return (TIMESTAMP);
  		if (!strcasecmp(atom + 1, "imeout"))
  			return (TIMEOUT);
  		if (!strcasecmp(atom + 1, "oken-ring"))
  			return (TOKEN_RING);
  		break;
  	case 'u':
  		if (!strncasecmp(atom + 1, "se", 2)) {
  			if (!strcasecmp(atom + 3, "r-class"))
  				return (USER_CLASS);
  			if (!strcasecmp(atom + 3, "-host-decl-names"))
  				return (USE_HOST_DECL_NAMES);
  			if (!strcasecmp(atom + 3,
  					 "-lease-addr-for-default-route"))
  				return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
  			break;
  		}
  		if (!strcasecmp(atom + 1, "id"))
  			return (UID);
  		if (!strcasecmp(atom + 1, "nknown-clients"))
  			return (UNKNOWN_CLIENTS);
  		break;
  	case 'v':
  		if (!strcasecmp(atom + 1, "endor-class"))
  			return (VENDOR_CLASS);
  		break;
  	case 'y':
  		if (!strcasecmp(atom + 1, "iaddr"))
  			return (YIADDR);
  		break;
  	}
  	return (dfv);
  }
  