/*
 *  ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
 *
 *  Copyright (c) 2000-2021 ircd-hybrid development team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 *  USA
 */

/*! \file conf_lexer.l
 * \brief Scans the ircd configuration file for tokens.
 * \version $Id: conf_lexer.l 9897 2021-01-17 06:18:18Z michael $
 */

%option case-insensitive
%option noyywrap
%option noinput
%option nounput
%option never-interactive

%x IN_COMMENT

%{
#include "stdinc.h"
#include "irc_string.h"
#include "conf.h"
#include "conf_parser.h" /* autogenerated header file */
#include "log.h"

#undef YY_INPUT
#define YY_FATAL_ERROR(msg) conf_yy_fatal_error(msg)
#define YY_INPUT(buf,result,max_size) \
  if ((result = conf_yy_input(buf, max_size)) == 0) \
    YY_FATAL_ERROR("input in flex scanner failed");
#define MAX_INCLUDE_DEPTH 10


unsigned int lineno = 1;
char linebuf[IRCD_BUFSIZE];
char conffilebuf[IRCD_BUFSIZE];

static struct included_file
{
  YY_BUFFER_STATE state;
  unsigned int lineno;
  FILE *file;
  char conffile[IRCD_BUFSIZE];
} include_stack[MAX_INCLUDE_DEPTH];

static unsigned int include_stack_ptr;

static void conf_include(void);
static bool conf_eof(void);

static int
conf_yy_input(char *lbuf, unsigned int max_size)
{
  return fgets(lbuf, max_size, conf_parser_ctx.conf_file) == NULL ? 0 : strlen(lbuf);
}

static int
conf_yy_fatal_error(const char *msg)
{
  return 0;
}

%}

WS        [[:blank:]]*
DIGIT     [[:digit:]]+
COMMENT   ("//"|"#").*
qstring   \"[^\"\n]*[\"\n]
include   \.include{WS}(\<.*\>|\".*\")

%%

"/*"                { BEGIN IN_COMMENT; }
<IN_COMMENT>"*/"    { BEGIN INITIAL;    }
<IN_COMMENT>.       ;  /* Eat everything but a newline */
<IN_COMMENT>\n      { ++lineno; }
<IN_COMMENT><<EOF>> { BEGIN INITIAL; if (conf_eof()) yyterminate(); }

{include}       { conf_include(); }
\n.*            { strlcpy(linebuf, yytext + 1, sizeof(linebuf)); ++lineno; yyless(1); }
{WS}            ;
{COMMENT}       ;
{DIGIT}         { yylval.number = atoi(yytext); return NUMBER; }
{qstring}       { if (yytext[yyleng - 2] == '\\')
                  {
                    yyless(yyleng - 1);  /* Return last quote */
                    yymore();  /* Append next string */
                  }
                  else
                  {
                    yylval.string = yytext + 1;

                    if (yylval.string[yyleng - 2] != '"')
                      ilog(LOG_TYPE_IRCD, "Unterminated character string");
                    else
                    {
                      unsigned int i = 0, j = 0;

                      yylval.string[yyleng - 2] = '\0';  /* Remove close quote */

                      for (; yylval.string[i] != '\0'; ++i, ++j)
                      {
                        if (yylval.string[i] != '\\')
                          yylval.string[j] = yylval.string[i];
                        else
                        {
                          ++i;

                          if (yylval.string[i] == '\0')  /* XXX: should not happen */
                          {
                            ilog(LOG_TYPE_IRCD, "Unterminated character string");
                            break;
                          }

                          yylval.string[j] = yylval.string[i];
                        }
                      }

                      yylval.string[j] = '\0';
                      return QSTRING;
                    }
                  }
                }

accept_password             { return ACCEPT_PASSWORD; }
admin                       { return ADMIN; }
administrator               { return ADMIN; }
aftype                      { return AFTYPE; }
all                         { return T_ALL; }
anti_nick_flood             { return ANTI_NICK_FLOOD; }
anti_spam_exit_message_time { return ANTI_SPAM_EXIT_MESSAGE_TIME; }
auth                        { return IRCD_AUTH; }
autoconn                    { return AUTOCONN; }
away_count                  { return AWAY_COUNT; }
away_time                   { return AWAY_TIME; }
bind                        { return T_BIND; }
bot                         { return BOT; }
caller_id_wait              { return CALLER_ID_WAIT; }
callerid                    { return T_CALLERID; }
can_flood                   { return CAN_FLOOD; }
cconn                       { return T_CCONN; }
channel                     { return CHANNEL; }
cidr_bitlen_ipv4            { return CIDR_BITLEN_IPV4; }
cidr_bitlen_ipv6            { return CIDR_BITLEN_IPV6; }
class                       { return CLASS; }
client                      { return CLIENT; }
close                       { return CLOSE; }
cluster                     { return T_CLUSTER; }
command                     { return T_COMMAND; }
connect                     { return CONNECT; }
connectfreq                 { return CONNECTFREQ; }
cycle_on_host_change        { return CYCLE_ON_HOST_CHANGE; }
deaf                        { return T_DEAF; }
debug                       { return T_DEBUG; }
default_floodcount          { return DEFAULT_FLOODCOUNT; }
default_floodtime           { return DEFAULT_FLOODTIME; }
default_join_flood_count    { return DEFAULT_JOIN_FLOOD_COUNT; }
default_join_flood_time     { return DEFAULT_JOIN_FLOOD_TIME; }
default_max_clients         { return DEFAULT_MAX_CLIENTS; }
defer                       { return DEFER; }
deny                        { return DENY; }
description                 { return DESCRIPTION; }
die                         { return DIE; }
disable_auth                { return DISABLE_AUTH; }
disable_fake_channels       { return DISABLE_FAKE_CHANNELS; }
disable_remote_commands     { return DISABLE_REMOTE_COMMANDS; }
dline                       { return T_DLINE; }
dline_min_cidr              { return DLINE_MIN_CIDR; }
dline_min_cidr6             { return DLINE_MIN_CIDR6; }
email                       { return EMAIL; }
enable_extbans              { return ENABLE_EXTBANS; }
encrypted                   { return ENCRYPTED; }
exceed_limit                { return EXCEED_LIMIT; }
exempt                      { return EXEMPT; }
expiration                  { return EXPIRATION; }
external                    { return T_EXTERNAL; }
failed_oper_notice          { return FAILED_OPER_NOTICE; }
farconnect                  { return T_FARCONNECT; }
file                        { return T_FILE; }
flags                       { return IRCD_FLAGS; }
flatten_links               { return FLATTEN_LINKS; }
flatten_links_delay         { return FLATTEN_LINKS_DELAY; }
flatten_links_file          { return FLATTEN_LINKS_FILE; }
flood                       { return T_FLOOD; }
gecos                       { return GECOS; }
general                     { return GENERAL; }
kline_min_cidr              { return KLINE_MIN_CIDR; }
kline_min_cidr6             { return KLINE_MIN_CIDR6; }
globops                     { return T_GLOBOPS; }
have_ident                  { return NEED_IDENT; }
hidden                      { return HIDDEN; }
hidden_name                 { return HIDDEN_NAME; }
hidechans                   { return HIDE_CHANS; }
hideidle                    { return HIDE_IDLE; }
hide_idle_from_opers        { return HIDE_IDLE_FROM_OPERS; }
hide_server_ips             { return HIDE_SERVER_IPS; }
hide_servers                { return HIDE_SERVERS; }
hide_services               { return HIDE_SERVICES; }
host                        { return HOST; }
hub                         { return HUB; }
hub_mask                    { return HUB_MASK; }
invisible                   { return T_INVISIBLE; }
invisible_on_connect        { return INVISIBLE_ON_CONNECT; }
invite_client_count         { return INVITE_CLIENT_COUNT; }
invite_client_time          { return INVITE_CLIENT_TIME; }
invite_delay_channel        { return INVITE_DELAY_CHANNEL; }
invite_expire_time          { return INVITE_EXPIRE_TIME; }
ip                          { return IP; }
ipv4                        { return T_IPV4; }
ipv6                        { return T_IPV6; }
join                        { return JOIN; }
kill                        { return KILL; }
kill_chase_time_limit       { return KILL_CHASE_TIME_LIMIT; }
kline                       { return KLINE; }
kline_exempt                { return KLINE_EXEMPT; }
knock_client_count          { return KNOCK_CLIENT_COUNT; }
knock_client_time           { return KNOCK_CLIENT_TIME; }
knock_delay_channel         { return KNOCK_DELAY_CHANNEL; }
leaf_mask                   { return LEAF_MASK; }
listen                      { return LISTEN; }
locops                      { return T_LOCOPS; }
log                         { return T_LOG; }
mask                        { return MASK; }
max_accept                  { return MAX_ACCEPT; }
max_bans                    { return MAX_BANS; }
max_bans_large              { return MAX_BANS_LARGE; }
max_channels                { return MAX_CHANNELS; }
max_idle                    { return MAX_IDLE; }
max_invites                 { return MAX_INVITES; }
max_monitor                 { return MAX_MONITOR; }
max_nick_changes            { return MAX_NICK_CHANGES; }
max_nick_length             { return MAX_NICK_LENGTH; }
max_nick_time               { return MAX_NICK_TIME; }
max_number                  { return MAX_NUMBER; }
max_targets                 { return MAX_TARGETS; }
max_topic_length            { return MAX_TOPIC_LENGTH; }
min_idle                    { return MIN_IDLE; }
min_nonwildcard             { return MIN_NONWILDCARD; }
min_nonwildcard_simple      { return MIN_NONWILDCARD_SIMPLE; }
module                      { return MODULE; }
modules                     { return MODULES; }
motd                        { return MOTD; }
name                        { return NAME; }
nchange                     { return T_NCHANGE; }
need_ident                  { return NEED_IDENT; }
need_password               { return NEED_PASSWORD; }
network_description         { return NETWORK_DESCRIPTION; }
network_name                { return NETWORK_NAME; }
nick                        { return NICK; }
no_oper_flood               { return NO_OPER_FLOOD; }
no_tilde                    { return NO_TILDE; }
nononreg                    { return T_NONONREG; }
number_per_cidr             { return NUMBER_PER_CIDR; }
number_per_ip_global        { return NUMBER_PER_IP_GLOBAL; }
number_per_ip_local         { return NUMBER_PER_IP_LOCAL; }
oper                        { return OPERATOR; }
oper_only_umodes            { return OPER_ONLY_UMODES; }
oper_umodes                 { return OPER_UMODES; }
operator                    { return OPERATOR; }
opers_bypass_callerid       { return OPERS_BYPASS_CALLERID; }
opme                        { return T_OPME; }
pace_wait                   { return PACE_WAIT; }
pace_wait_simple            { return PACE_WAIT_SIMPLE; }
password                    { return PASSWORD; }
path                        { return PATH; }
ping_cookie                 { return PING_COOKIE; }
ping_time                   { return PING_TIME; }
port                        { return PORT; }
prepend                     { return T_PREPEND; }
pseudo                      { return T_PSEUDO; }
random_idle                 { return RANDOM_IDLE; }
reason                      { return REASON; }
recvq                       { return T_RECVQ; }
redirport                   { return REDIRPORT; }
redirserv                   { return REDIRSERV; }
rehash                      { return REHASH; }
rej                         { return T_REJ; }
remote                      { return REMOTE; }
remoteban                   { return REMOTEBAN; }
restart                     { return T_RESTART; }
resv                        { return RESV; }
resv_exempt                 { return RESV_EXEMPT; }
rsa_private_key_file        { return RSA_PRIVATE_KEY_FILE; }
send_password               { return SEND_PASSWORD; }
sendq                       { return SENDQ; }
server                      { return T_SERVER; }
serverhide                  { return SERVERHIDE; }
serverinfo                  { return SERVERINFO; }
service                     { return T_SERVICE; }
servnotice                  { return T_SERVNOTICE; }
set                         { return T_SET; }
shared                      { return T_SHARED; }
short_motd                  { return SHORT_MOTD; }
sid                         { return IRCD_SID; }
size                        { return T_SIZE; }
skill                       { return T_SKILL; }
softcallerid                { return T_SOFTCALLERID; }
specials_in_ident           { return SPECIALS_IN_IDENT; }
spoof                       { return SPOOF; }
spy                         { return T_SPY; }
squit                       { return SQUIT; }
ssl                         { return T_TLS; }
stats_e_disabled            { return STATS_E_DISABLED; }
stats_i_oper_only           { return STATS_I_OPER_ONLY; }
stats_k_oper_only           { return STATS_K_OPER_ONLY; }
stats_m_oper_only           { return STATS_M_OPER_ONLY; }
stats_o_oper_only           { return STATS_O_OPER_ONLY; }
stats_P_oper_only           { return STATS_P_OPER_ONLY; }
stats_u_oper_only           { return STATS_U_OPER_ONLY; }
target                      { return T_TARGET; }
throttle_count              { return THROTTLE_COUNT; }
throttle_time               { return THROTTLE_TIME; }
timeout                     { return TIMEOUT; }
tls                         { return T_TLS; }
tls_certificate_file        { return TLS_CERTIFICATE_FILE; }
tls_certificate_fingerprint { return TLS_CERTIFICATE_FINGERPRINT; }
tls_cipher_list             { return TLS_CIPHER_LIST; }
tls_cipher_suites           { return TLS_CIPHER_SUITES; }
tls_connection_required     { return TLS_CONNECTION_REQUIRED; }
tls_dh_param_file           { return TLS_DH_PARAM_FILE; }
tls_message_digest_algorithm { return TLS_MESSAGE_DIGEST_ALGORITHM; }
tls_supported_groups        { return TLS_SUPPORTED_GROUPS; }
ts_max_delta                { return TS_MAX_DELTA; }
ts_warn_delta               { return TS_WARN_DELTA; }
type                        { return TYPE; }
umodes                      { return T_UMODES; }
undline                     { return T_UNDLINE; }
unkline                     { return UNKLINE; }
unlimited                   { return T_UNLIMITED; }
unresv                      { return T_UNRESV; }
unxline                     { return T_UNXLINE; }
use_logging                 { return USE_LOGGING; }
user                        { return USER; }
wallop                      { return T_WALLOP; }
wallops                     { return T_WALLOPS; }
warn_no_connect_block       { return WARN_NO_CONNECT_BLOCK; }
webirc                      { return T_WEBIRC; }
whois                       { return WHOIS; }
whowas_history_length       { return WHOWAS_HISTORY_LENGTH; }
xline                       { return XLINE; }
xline_exempt                { return XLINE_EXEMPT; }

yes                         { yylval.number = 1; return TBOOL; }
no                          { yylval.number = 0; return TBOOL; }

years                       { return YEARS; }
year                        { return YEARS; }
months                      { return MONTHS; }
month                       { return MONTHS; }
weeks                       { return WEEKS; }
week                        { return WEEKS; }
days                        { return DAYS; }
day                         { return DAYS; }
hours                       { return HOURS; }
hour                        { return HOURS; }
minutes                     { return MINUTES; }
minute                      { return MINUTES; }
seconds                     { return SECONDS; }
second                      { return SECONDS; }

bytes                       { return BYTES; }
byte                        { return BYTES; }
kilobytes                   { return KBYTES; }
kilobyte                    { return KBYTES; }
kbytes                      { return KBYTES; }
kbyte                       { return KBYTES; }
kb                          { return KBYTES; }
megabytes                   { return MBYTES; }
megabyte                    { return MBYTES; }
mbytes                      { return MBYTES; }
mbyte                       { return MBYTES; }
mb                          { return MBYTES; }
\.\.                        { return TWODOTS; }

.                           { return yytext[0]; }
<<EOF>>                     { if (conf_eof() == true) yyterminate(); }

%%

/* C-style .includes. This function will properly swap input conf buffers,
 * and lineno -kre */
static void
conf_include(void)
{
  char *p;
  char filenamebuf[IRCD_BUFSIZE];

  if ((p = strchr(yytext, '<')) == NULL)
    *strchr(p = strchr(yytext, '"') + 1, '"') = '\0';
  else
    *strchr(++p, '>') = '\0';

  /* do stacking and co. */
  if (include_stack_ptr >= MAX_INCLUDE_DEPTH)
  {
    ilog(LOG_TYPE_IRCD, "Includes nested too deep in %s", p);
    return;
  }

  if (*p == '/')  /* if it is an absolute path */
    snprintf(filenamebuf, sizeof(filenamebuf), "%s", p);
  else
    snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ETCPATH, p);

  FILE *tmp_fbfile_in = fopen(filenamebuf, "r");
  if (tmp_fbfile_in == NULL)
  {
    ilog(LOG_TYPE_IRCD, "Unable to read configuration file '%s': %s",
         filenamebuf, strerror(errno));
    return;
  }

  struct included_file *file = &include_stack[include_stack_ptr++];
  file->lineno = lineno;
  file->file = conf_parser_ctx.conf_file;
  file->state = YY_CURRENT_BUFFER;
  strlcpy(file->conffile, conffilebuf, sizeof(file->conffile));

  lineno = 1;
  conf_parser_ctx.conf_file = tmp_fbfile_in;
  strlcpy(conffilebuf, filenamebuf, sizeof(conffilebuf));

  yy_switch_to_buffer(yy_create_buffer(NULL, YY_BUF_SIZE));
}

/* This is function that will be called on EOF in conf file. It will
 * apropriately close conf if it not main conf and swap input buffers -kre
 * */
static bool
conf_eof(void)
{
  if (include_stack_ptr == 0)
  {
    lineno = 1;
    return true;
  }

  /* switch buffer */
  struct included_file *file = &include_stack[--include_stack_ptr];

  /* close current file */
  fclose(conf_parser_ctx.conf_file);

  /* switch buffers */
  yy_delete_buffer(YY_CURRENT_BUFFER);
  yy_switch_to_buffer(file->state);

  /* switch lineno */
  lineno = file->lineno;

  /* switch file */
  conf_parser_ctx.conf_file = file->file;

  strlcpy(conffilebuf, file->conffile, sizeof(conffilebuf));
  return false;
}
