#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "ntpd.h"
#include "ntp_config.h"
#include "ntpsim.h"
#include "ntp_scanner.h"
#include "ntp_parser.h"
#include "ntp_keyword.h"
#define MAX_LEXEME (1024 + 1)
char yytext[MAX_LEXEME];
u_int32 conf_file_sum;
static struct FILE_INFO * lex_stack = NULL;
const char special_chars[] = "{}(),;|=";
static int is_keyword(char *lexeme, follby *pfollowedby);
const char *
keyword(
int token
)
{
size_t i;
const char *text;
i = token - LOWEST_KEYWORD_ID;
if (i < COUNTOF(keyword_text))
text = keyword_text[i];
else
text = NULL;
return (text != NULL)
? text
: "(keyword not found)";
}
static struct FILE_INFO *
lex_open(
const char *path,
const char *mode
)
{
struct FILE_INFO *stream;
size_t nnambuf;
nnambuf = strlen(path);
stream = emalloc_zero(sizeof(*stream) + nnambuf);
stream->curpos.nline = 1;
stream->backch = EOF;
memcpy(stream->fname, path, nnambuf);
if (NULL != mode) {
stream->fpi = fopen(path, mode);
if (NULL == stream->fpi) {
free(stream);
stream = NULL;
}
}
return stream;
}
static int
lex_getch(
struct FILE_INFO *stream
)
{
int ch;
if (NULL == stream || stream->force_eof)
return EOF;
if (EOF != stream->backch) {
ch = stream->backch;
stream->backch = EOF;
if (stream->fpi)
conf_file_sum += ch;
} else if (stream->fpi) {
while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX)
stream->curpos.ncol++;
if (EOF != ch) {
conf_file_sum += ch;
stream->curpos.ncol++;
}
} else {
const char * scan;
scan = &remote_config.buffer[remote_config.pos];
while ((ch = (u_char)*scan) > SCHAR_MAX) {
scan++;
stream->curpos.ncol++;
}
if ('\0' != ch) {
scan++;
stream->curpos.ncol++;
} else {
ch = EOF;
}
remote_config.pos = (int)(scan - remote_config.buffer);
}
if (EOF == ch && stream->curpos.ncol != 0)
ch = '\n';
if (ch == '\n') {
stream->bakpos = stream->curpos;
stream->curpos.nline++;
stream->curpos.ncol = 0;
}
return ch;
}
static int
lex_ungetch(
int ch,
struct FILE_INFO *stream
)
{
if (NULL == stream || stream->force_eof)
return EOF;
if (EOF != stream->backch || EOF == ch)
return EOF;
stream->backch = (u_char)ch;
if (stream->fpi)
conf_file_sum -= stream->backch;
if (stream->backch == '\n') {
stream->curpos = stream->bakpos;
stream->bakpos.ncol = -1;
}
stream->curpos.ncol--;
return stream->backch;
}
static void
lex_close(
struct FILE_INFO *stream
)
{
if (NULL != stream) {
if (NULL != stream->fpi)
fclose(stream->fpi);
free(stream);
}
}
static struct FILE_INFO *
_drop_stack_do(
struct FILE_INFO * head
)
{
struct FILE_INFO * tail;
while (NULL != head) {
tail = head->st_next;
lex_close(head);
head = tail;
}
return head;
}
int
lex_init_stack(
const char * path,
const char * mode
)
{
if (NULL != lex_stack || NULL == path)
return FALSE;
lex_stack = lex_open(path, mode);
return (NULL != lex_stack);
}
void
lex_drop_stack()
{
lex_stack = _drop_stack_do(lex_stack);
}
int
lex_flush_stack()
{
int retv = FALSE;
if (NULL != lex_stack) {
retv = !lex_stack->force_eof;
lex_stack->force_eof = TRUE;
lex_stack->st_next = _drop_stack_do(
lex_stack->st_next);
}
return retv;
}
int lex_push_file(
const char * path,
const char * mode
)
{
struct FILE_INFO * next = NULL;
if (NULL != path) {
next = lex_open(path, mode);
if (NULL != next) {
next->st_next = lex_stack;
lex_stack = next;
}
}
return (NULL != next);
}
int
lex_pop_file(void)
{
struct FILE_INFO * head = lex_stack;
struct FILE_INFO * tail = NULL;
if (NULL != head) {
tail = head->st_next;
if (NULL != tail) {
lex_stack = tail;
lex_close(head);
}
}
return (NULL != tail);
}
size_t
lex_level(void)
{
size_t cnt = 0;
struct FILE_INFO *ipf = lex_stack;
while (NULL != ipf) {
cnt++;
ipf = ipf->st_next;
}
return cnt;
}
int
lex_from_file(void)
{
return (NULL != lex_stack) && (NULL != lex_stack->fpi);
}
struct FILE_INFO *
lex_current()
{
return lex_stack;
}
static int
is_keyword(
char *lexeme,
follby *pfollowedby
)
{
follby fb;
int curr_s;
int token;
int i;
curr_s = SCANNER_INIT_S;
token = 0;
for (i = 0; lexeme[i]; i++) {
while (curr_s && (lexeme[i] != SS_CH(sst[curr_s])))
curr_s = SS_OTHER_N(sst[curr_s]);
if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) {
if ('\0' == lexeme[i + 1]
&& FOLLBY_NON_ACCEPTING
!= SS_FB(sst[curr_s])) {
fb = SS_FB(sst[curr_s]);
*pfollowedby = fb;
token = curr_s;
break;
}
curr_s = SS_MATCH_N(sst[curr_s]);
} else
break;
}
return token;
}
static int
is_integer(
char *lexeme
)
{
int i;
int is_neg;
u_int u_val;
i = 0;
if (lexeme[i] == '-') {
i++;
is_neg = TRUE;
} else {
is_neg = FALSE;
}
for (; lexeme[i] != '\0'; i++) {
if (!isdigit((u_char)lexeme[i]))
return FALSE;
}
if (is_neg)
return TRUE;
if (1 == sscanf(lexeme, "%u", &u_val))
return (u_val <= INT_MAX);
else
return FALSE;
}
static int
is_u_int(
char *lexeme
)
{
int i;
int is_hex;
i = 0;
if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) {
i += 2;
is_hex = TRUE;
} else {
is_hex = FALSE;
}
for (; lexeme[i] != '\0'; i++) {
if (is_hex && !isxdigit((u_char)lexeme[i]))
return FALSE;
if (!is_hex && !isdigit((u_char)lexeme[i]))
return FALSE;
}
return TRUE;
}
static int
is_double(
char *lexeme
)
{
u_int num_digits = 0;
u_int i;
i = 0;
if ('+' == lexeme[i] || '-' == lexeme[i])
i++;
for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++)
num_digits++;
if ('.' == lexeme[i]) {
i++;
for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++)
num_digits++;
}
if (!num_digits)
return 0;
if (!lexeme[i])
return 1;
if ('e' == tolower((u_char)lexeme[i]))
i++;
else
return 0;
if ('+' == lexeme[i] || '-' == lexeme[i])
i++;
while (lexeme[i] && isdigit((u_char)lexeme[i]))
i++;
if (!lexeme[i])
return 1;
else
return 0;
}
static inline int
is_special(
int ch
)
{
return strchr(special_chars, ch) != NULL;
}
static int
is_EOC(
int ch
)
{
if ((old_config_style && (ch == '\n')) ||
(!old_config_style && (ch == ';')))
return 1;
return 0;
}
char *
quote_if_needed(char *str)
{
char *ret;
size_t len;
size_t octets;
len = strlen(str);
octets = len + 2 + 1;
ret = emalloc(octets);
if ('"' != str[0]
&& (strcspn(str, special_chars) < len
|| strchr(str, ' ') != NULL)) {
snprintf(ret, octets, "\"%s\"", str);
} else
strlcpy(ret, str, octets);
return ret;
}
static int
create_string_token(
char *lexeme
)
{
char *pch;
pch = lexeme;
while (*pch && isspace((u_char)*pch))
pch++;
if (!*pch) {
yylval.Integer = T_EOC;
return yylval.Integer;
}
yylval.String = estrdup(lexeme);
return T_String;
}
int
yylex(void)
{
static follby followedby = FOLLBY_TOKEN;
size_t i;
int instring;
int yylval_was_set;
int converted;
int token;
int ch;
instring = FALSE;
yylval_was_set = FALSE;
do {
while (EOF != (ch = lex_getch(lex_stack)) &&
isspace(ch) &&
!is_EOC(ch))
;
if (EOF == ch) {
if ( ! lex_pop_file())
return 0;
token = T_EOC;
goto normal_return;
} else if (is_EOC(ch)) {
followedby = FOLLBY_TOKEN;
token = T_EOC;
goto normal_return;
} else if (is_special(ch) && FOLLBY_TOKEN == followedby) {
token = ch;
if ('=' == ch && old_config_style)
followedby = FOLLBY_STRING;
yytext[0] = (char)ch;
yytext[1] = '\0';
goto normal_return;
} else
lex_ungetch(ch, lex_stack);
lex_stack->tokpos = lex_stack->curpos;
i = 0;
while (EOF != (ch = lex_getch(lex_stack))) {
yytext[i] = (char)ch;
if (isspace(ch) || is_EOC(ch)
|| '"' == ch
|| (FOLLBY_TOKEN == followedby
&& is_special(ch)))
break;
if ('#' == ch) {
while (EOF != (ch = lex_getch(lex_stack))
&& '\n' != ch)
;
break;
}
i++;
if (i >= COUNTOF(yytext))
goto lex_too_long;
}
if ('"' == ch) {
instring = TRUE;
while (EOF != (ch = lex_getch(lex_stack)) &&
ch != '"' && ch != '\n') {
yytext[i++] = (char)ch;
if (i >= COUNTOF(yytext))
goto lex_too_long;
}
if ('"' == ch)
ch = lex_getch(lex_stack);
}
lex_ungetch(ch, lex_stack);
yytext[i] = '\0';
} while (i == 0);
if (followedby == FOLLBY_TOKEN && !instring) {
token = is_keyword(yytext, &followedby);
if (token) {
if (T_Server == token && !old_config_style)
followedby = FOLLBY_TOKEN;
goto normal_return;
} else if (is_integer(yytext)) {
yylval_was_set = TRUE;
errno = 0;
if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0
&& ((errno == EINVAL) || (errno == ERANGE))) {
msyslog(LOG_ERR,
"Integer cannot be represented: %s",
yytext);
if (lex_from_file()) {
exit(1);
} else {
yylval.Integer = 0;
return 0;
}
}
token = T_Integer;
goto normal_return;
} else if (is_u_int(yytext)) {
yylval_was_set = TRUE;
if ('0' == yytext[0] &&
'x' == tolower((unsigned long)yytext[1]))
converted = sscanf(&yytext[2], "%x",
&yylval.U_int);
else
converted = sscanf(yytext, "%u",
&yylval.U_int);
if (1 != converted) {
msyslog(LOG_ERR,
"U_int cannot be represented: %s",
yytext);
if (lex_from_file()) {
exit(1);
} else {
yylval.Integer = 0;
return 0;
}
}
token = T_U_int;
goto normal_return;
} else if (is_double(yytext)) {
yylval_was_set = TRUE;
errno = 0;
if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) {
msyslog(LOG_ERR,
"Double too large to represent: %s",
yytext);
exit(1);
} else {
token = T_Double;
goto normal_return;
}
} else {
yylval_was_set = TRUE;
token = create_string_token(yytext);
goto normal_return;
}
}
if ('-' == yytext[0]) {
if ('4' == yytext[1]) {
token = T_Ipv4_flag;
goto normal_return;
} else if ('6' == yytext[1]) {
token = T_Ipv6_flag;
goto normal_return;
}
}
if (FOLLBY_STRING == followedby)
followedby = FOLLBY_TOKEN;
yylval_was_set = TRUE;
token = create_string_token(yytext);
normal_return:
if (T_EOC == token)
DPRINTF(4,("\t<end of command>\n"));
else
DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext,
token_name(token)));
if (!yylval_was_set)
yylval.Integer = token;
return token;
lex_too_long:
yytext[min(sizeof(yytext) - 1, 50)] = 0;
msyslog(LOG_ERR,
"configuration item on line %d longer than limit of %lu, began with '%s'",
lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50),
yytext);
if (lex_from_file())
exit(sizeof(yytext) - 1);
yylval.Integer = 0;
return 0;
}