/* ntp_scanner.h
 *
 * The header file for a simple lexical analyzer.
 *
 * Written By:	Sachin Kambojq
 *		University of Delaware
 *		Newark, DE 19711
 * Copyright Sachin Kamboj
 * Copyright the NTPsec project contributors
 * SPDX-License-Identifier: BSD-2-Clause
 */

#ifndef GUARD_NTP_SCANNER_H
#define GUARD_NTP_SCANNER_H

#include "ntp_config.h"

/*
 * ntp.conf syntax is slightly irregular in that some tokens such as
 * hostnames do not require quoting even if they might otherwise be
 * recognized as T_ terminal tokens.  This hand-crafted lexical scanner
 * uses a "followed by" value associated with each keyword to indicate
 * normal scanning of the next token, forced scanning of the next token
 * alone as a T_String, or forced scanning of all tokens to the end of
 * the command as T_String.
 * In the past the identifiers for this functionality ended in _ARG:
 *
 * NO_ARG	->	FOLLBY_TOKEN
 * SINGLE_ARG	->	FOLLBY_STRING
 * MULTIPLE_ARG	->	FOLLBY_STRINGS_TO_EOC
 *
 * Note that some tokens use FOLLBY_TOKEN even though they sometimes
 * are followed by strings.  FOLLBY_STRING is used only when needed to
 * avoid the keyword scanner matching a token where a string is needed.
 *
 * FOLLBY_NON_ACCEPT is an overloading of this field to distinguish
 * non-accepting states (where the state number does not match a T_
 * value).
 */
typedef enum {
	FOLLBY_TOKEN = 0,
	FOLLBY_STRING,
	FOLLBY_STRINGS_TO_EOC,
	FOLLBY_NON_ACCEPTING
} follby;

#define MAXINCLUDELEVEL	5	/* maximum include file levels */

/* STRUCTURES
 * ----------
 */

/*
 * Define a structure to hold the FSA for the keywords.
 * The structure is actually a tree.
 *
 * To save space, a single uint32_t encodes four fields, and a fifth
 * (the token completed for terminal states) is implied by the index of
 * the rule within the scan state array, taking advantage of the fact
 * there are more scan states than the highest T_ token number.
 *
 * The lowest 8 bits hold the character the state matches on.
 * Bits 8 and 9 hold the followedby value (0 - 3).  For non-accepting
 *   states (which do not match a completed token) the followedby
 *   value 3 (FOLLBY_NONACCEPTING) denotes that fact.  For accepting
 *   states, values 0 - 2 control whether the scanner forces the
 *   following token(s) to strings.
 * Bits 10 through 20 hold the next state to check not matching
 * this state's character.
 * Bits 21 through 31 hold the next state to check matching the char.
 */

#define S_ST(ch, fb, match_n, other_n) (			\
	(uint8_t)((ch) & 0xff) |					\
	((uint32_t)(fb) << 8) |					\
	((uint32_t)(match_n) << 10) |				\
	((uint32_t)(other_n) << 21)				\
)

#define SS_CH(ss)	((char)(uint8_t)((ss) & 0xff))
#define SS_FB(ss)	(((unsigned int)(ss) >>  8) & 0x3)
#define SS_MATCH_N(ss)	(((unsigned int)(ss) >> 10) & 0x7ff)
#define SS_OTHER_N(ss)	(((unsigned int)(ss) >> 21) & 0x7ff)

typedef uint32_t scan_state;

struct LCPOS {
	int nline;
	int ncol;
};

/* Structure to hold a filename, file pointer and positional info.
 * Instances are dynamically allocated, and the file name is copied by
 * value into a dynamic extension of the 'fname' array. (Which *must* be
 * the last field for that reason!)
 */
struct FILE_INFO {
	struct FILE_INFO * st_next;	/* next on stack */
	FILE *		   fpi;		/* File Descriptor */
	bool               force_eof;	/* locked or not */
	int                backch;	/* ungetch buffer */

	struct LCPOS       curpos;	/* current scan position */
	struct LCPOS       bakpos;	/* last line end for ungetc */
	struct LCPOS       tokpos;	/* current token position */
	struct LCPOS       errpos;	/* error position */

	char               fname[1];	/* (formal only) buffered name */
};


/* SCANNER GLOBAL VARIABLES
 * ------------------------
 */
extern config_tree cfgt;	  /* Parser output stored here */

/* VARIOUS SUBROUTINE DECLARATIONS
 * -------------------------------
 */
extern const char *keyword(int token);
extern char *quote_if_needed(char *str);
int yylex(void);

/* managing the input source stack itself */
extern bool lex_init_stack(const char * path, const char * mode);
extern void        lex_drop_stack(void);
extern bool lex_flush_stack(void);

/* path management for config directories */
extern void reparent(char *, size_t, const char *, const char *);
extern bool is_directory(const char *);

/* add/remove a nested input source */
extern bool lex_push_file(const char * path);
extern bool lex_pop_file(void);

/* input stack state query functions */
extern size_t      lex_level(void);
extern bool lex_from_file(void);
extern struct FILE_INFO * lex_current(void);
extern const char * const keyword_text[];

#endif	/* GUARD_NTP_SCANNER_H */
