1 %{ 2 /* 3 * configlexer.lex - lexical analyzer for unbound config file 4 * 5 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved 6 * 7 * See LICENSE for the license. 8 * 9 */ 10 #include <ctype.h> 11 #include <string.h> 12 #include <strings.h> 13 #ifdef HAVE_GLOB_H 14 # include <glob.h> 15 #endif 16 17 #include "util/config_file.h" 18 #include "util/configparser.h" 19 void ub_c_error(const char *message); 20 21 #if 0 22 #define LEXOUT(s) printf s /* used ONLY when debugging */ 23 #else 24 #define LEXOUT(s) 25 #endif 26 27 /** avoid warning in about fwrite return value */ 28 #define ECHO ub_c_error_msg("syntax error at text: %s", yytext) 29 30 /** A parser variable, this is a statement in the config file which is 31 * of the form variable: value1 value2 ... nargs is the number of values. */ 32 #define YDVAR(nargs, var) \ 33 num_args=(nargs); \ 34 LEXOUT(("v(%s%d) ", yytext, num_args)); \ 35 if(num_args > 0) { BEGIN(val); } \ 36 return (var); 37 38 struct inc_state { 39 char* filename; 40 int line; 41 YY_BUFFER_STATE buffer; 42 struct inc_state* next; 43 }; 44 static struct inc_state* config_include_stack = NULL; 45 static int inc_depth = 0; 46 static int inc_prev = 0; 47 static int num_args = 0; 48 49 void init_cfg_parse(void) 50 { 51 config_include_stack = NULL; 52 inc_depth = 0; 53 inc_prev = 0; 54 num_args = 0; 55 } 56 57 static void config_start_include(const char* filename) 58 { 59 FILE *input; 60 struct inc_state* s; 61 char* nm; 62 if(inc_depth++ > 100000) { 63 ub_c_error_msg("too many include files"); 64 return; 65 } 66 if(strlen(filename) == 0) { 67 ub_c_error_msg("empty include file name"); 68 return; 69 } 70 s = (struct inc_state*)malloc(sizeof(*s)); 71 if(!s) { 72 ub_c_error_msg("include %s: malloc failure", filename); 73 return; 74 } 75 if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot, 76 strlen(cfg_parser->chroot)) == 0) { 77 filename += strlen(cfg_parser->chroot); 78 } 79 nm = strdup(filename); 80 if(!nm) { 81 ub_c_error_msg("include %s: strdup failure", filename); 82 free(s); 83 return; 84 } 85 input = fopen(filename, "r"); 86 if(!input) { 87 ub_c_error_msg("cannot open include file '%s': %s", 88 filename, strerror(errno)); 89 free(s); 90 free(nm); 91 return; 92 } 93 LEXOUT(("switch_to_include_file(%s)\n", filename)); 94 s->filename = cfg_parser->filename; 95 s->line = cfg_parser->line; 96 s->buffer = YY_CURRENT_BUFFER; 97 s->next = config_include_stack; 98 config_include_stack = s; 99 cfg_parser->filename = nm; 100 cfg_parser->line = 1; 101 yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE)); 102 } 103 104 static void config_start_include_glob(const char* filename) 105 { 106 107 /* check for wildcards */ 108 #ifdef HAVE_GLOB 109 glob_t g; 110 size_t i; 111 int r, flags; 112 if(!(!strchr(filename, '*') && !strchr(filename, '?') && !strchr(filename, '[') && 113 !strchr(filename, '{') && !strchr(filename, '~'))) { 114 flags = 0 115 #ifdef GLOB_ERR 116 | GLOB_ERR 117 #endif 118 #ifdef GLOB_NOSORT 119 | GLOB_NOSORT 120 #endif 121 #ifdef GLOB_BRACE 122 | GLOB_BRACE 123 #endif 124 #ifdef GLOB_TILDE 125 | GLOB_TILDE 126 #endif 127 ; 128 memset(&g, 0, sizeof(g)); 129 r = glob(filename, flags, NULL, &g); 130 if(r) { 131 /* some error */ 132 globfree(&g); 133 if(r == GLOB_NOMATCH) 134 return; /* no matches for pattern */ 135 config_start_include(filename); /* let original deal with it */ 136 return; 137 } 138 /* process files found, if any */ 139 for(i=0; i<(size_t)g.gl_pathc; i++) { 140 config_start_include(g.gl_pathv[i]); 141 } 142 globfree(&g); 143 return; 144 } 145 #endif /* HAVE_GLOB */ 146 147 config_start_include(filename); 148 } 149 150 static void config_end_include(void) 151 { 152 struct inc_state* s = config_include_stack; 153 --inc_depth; 154 if(!s) return; 155 free(cfg_parser->filename); 156 cfg_parser->filename = s->filename; 157 cfg_parser->line = s->line; 158 yy_delete_buffer(YY_CURRENT_BUFFER); 159 yy_switch_to_buffer(s->buffer); 160 config_include_stack = s->next; 161 free(s); 162 } 163 164 #ifndef yy_set_bol /* compat definition, for flex 2.4.6 */ 165 #define yy_set_bol(at_bol) \ 166 { \ 167 if ( ! yy_current_buffer ) \ 168 yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ 169 yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \ 170 } 171 #endif 172 173 %} 174 %option noinput 175 %option nounput 176 %{ 177 #ifndef YY_NO_UNPUT 178 #define YY_NO_UNPUT 1 179 #endif 180 #ifndef YY_NO_INPUT 181 #define YY_NO_INPUT 1 182 #endif 183 %} 184 185 SPACE [ \t] 186 LETTER [a-zA-Z] 187 UNQUOTEDLETTER [^\'\"\n\r \t\\]|\\. 188 UNQUOTEDLETTER_NOCOLON [^\:\'\"\n\r \t\\]|\\. 189 NEWLINE [\r\n] 190 COMMENT \# 191 COLON \: 192 DQANY [^\"\n\r\\]|\\. 193 SQANY [^\'\n\r\\]|\\. 194 195 %x quotedstring singlequotedstr include include_quoted val 196 197 %% 198 <INITIAL,val>{SPACE}* { 199 LEXOUT(("SP ")); /* ignore */ } 200 <INITIAL,val>{SPACE}*{COMMENT}.* { 201 /* note that flex makes the longest match and '.' is any but not nl */ 202 LEXOUT(("comment(%s) ", yytext)); /* ignore */ } 203 server{COLON} { YDVAR(0, VAR_SERVER) } 204 num-threads{COLON} { YDVAR(1, VAR_NUM_THREADS) } 205 verbosity{COLON} { YDVAR(1, VAR_VERBOSITY) } 206 port{COLON} { YDVAR(1, VAR_PORT) } 207 outgoing-range{COLON} { YDVAR(1, VAR_OUTGOING_RANGE) } 208 outgoing-port-permit{COLON} { YDVAR(1, VAR_OUTGOING_PORT_PERMIT) } 209 outgoing-port-avoid{COLON} { YDVAR(1, VAR_OUTGOING_PORT_AVOID) } 210 outgoing-num-tcp{COLON} { YDVAR(1, VAR_OUTGOING_NUM_TCP) } 211 incoming-num-tcp{COLON} { YDVAR(1, VAR_INCOMING_NUM_TCP) } 212 do-ip4{COLON} { YDVAR(1, VAR_DO_IP4) } 213 do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) } 214 do-udp{COLON} { YDVAR(1, VAR_DO_UDP) } 215 do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) } 216 tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) } 217 ssl-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) } 218 ssl-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) } 219 ssl-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) } 220 ssl-port{COLON} { YDVAR(1, VAR_SSL_PORT) } 221 do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } 222 interface{COLON} { YDVAR(1, VAR_INTERFACE) } 223 ip-address{COLON} { YDVAR(1, VAR_INTERFACE) } 224 outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) } 225 interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) } 226 so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) } 227 so-sndbuf{COLON} { YDVAR(1, VAR_SO_SNDBUF) } 228 so-reuseport{COLON} { YDVAR(1, VAR_SO_REUSEPORT) } 229 chroot{COLON} { YDVAR(1, VAR_CHROOT) } 230 username{COLON} { YDVAR(1, VAR_USERNAME) } 231 directory{COLON} { YDVAR(1, VAR_DIRECTORY) } 232 logfile{COLON} { YDVAR(1, VAR_LOGFILE) } 233 pidfile{COLON} { YDVAR(1, VAR_PIDFILE) } 234 root-hints{COLON} { YDVAR(1, VAR_ROOT_HINTS) } 235 edns-buffer-size{COLON} { YDVAR(1, VAR_EDNS_BUFFER_SIZE) } 236 msg-buffer-size{COLON} { YDVAR(1, VAR_MSG_BUFFER_SIZE) } 237 msg-cache-size{COLON} { YDVAR(1, VAR_MSG_CACHE_SIZE) } 238 msg-cache-slabs{COLON} { YDVAR(1, VAR_MSG_CACHE_SLABS) } 239 rrset-cache-size{COLON} { YDVAR(1, VAR_RRSET_CACHE_SIZE) } 240 rrset-cache-slabs{COLON} { YDVAR(1, VAR_RRSET_CACHE_SLABS) } 241 cache-max-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_TTL) } 242 cache-min-ttl{COLON} { YDVAR(1, VAR_CACHE_MIN_TTL) } 243 infra-host-ttl{COLON} { YDVAR(1, VAR_INFRA_HOST_TTL) } 244 infra-lame-ttl{COLON} { YDVAR(1, VAR_INFRA_LAME_TTL) } 245 infra-cache-slabs{COLON} { YDVAR(1, VAR_INFRA_CACHE_SLABS) } 246 infra-cache-numhosts{COLON} { YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) } 247 infra-cache-lame-size{COLON} { YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) } 248 num-queries-per-thread{COLON} { YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) } 249 jostle-timeout{COLON} { YDVAR(1, VAR_JOSTLE_TIMEOUT) } 250 delay-close{COLON} { YDVAR(1, VAR_DELAY_CLOSE) } 251 target-fetch-policy{COLON} { YDVAR(1, VAR_TARGET_FETCH_POLICY) } 252 harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) } 253 harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) } 254 harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) } 255 harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) } 256 harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) } 257 harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) } 258 use-caps-for-id{COLON} { YDVAR(1, VAR_USE_CAPS_FOR_ID) } 259 unwanted-reply-threshold{COLON} { YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) } 260 private-address{COLON} { YDVAR(1, VAR_PRIVATE_ADDRESS) } 261 private-domain{COLON} { YDVAR(1, VAR_PRIVATE_DOMAIN) } 262 prefetch-key{COLON} { YDVAR(1, VAR_PREFETCH_KEY) } 263 prefetch{COLON} { YDVAR(1, VAR_PREFETCH) } 264 stub-zone{COLON} { YDVAR(0, VAR_STUB_ZONE) } 265 name{COLON} { YDVAR(1, VAR_NAME) } 266 stub-addr{COLON} { YDVAR(1, VAR_STUB_ADDR) } 267 stub-host{COLON} { YDVAR(1, VAR_STUB_HOST) } 268 stub-prime{COLON} { YDVAR(1, VAR_STUB_PRIME) } 269 stub-first{COLON} { YDVAR(1, VAR_STUB_FIRST) } 270 forward-zone{COLON} { YDVAR(0, VAR_FORWARD_ZONE) } 271 forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) } 272 forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) } 273 forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) } 274 do-not-query-address{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) } 275 do-not-query-localhost{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_LOCALHOST) } 276 access-control{COLON} { YDVAR(2, VAR_ACCESS_CONTROL) } 277 hide-identity{COLON} { YDVAR(1, VAR_HIDE_IDENTITY) } 278 hide-version{COLON} { YDVAR(1, VAR_HIDE_VERSION) } 279 identity{COLON} { YDVAR(1, VAR_IDENTITY) } 280 version{COLON} { YDVAR(1, VAR_VERSION) } 281 module-config{COLON} { YDVAR(1, VAR_MODULE_CONF) } 282 dlv-anchor{COLON} { YDVAR(1, VAR_DLV_ANCHOR) } 283 dlv-anchor-file{COLON} { YDVAR(1, VAR_DLV_ANCHOR_FILE) } 284 trust-anchor-file{COLON} { YDVAR(1, VAR_TRUST_ANCHOR_FILE) } 285 auto-trust-anchor-file{COLON} { YDVAR(1, VAR_AUTO_TRUST_ANCHOR_FILE) } 286 trusted-keys-file{COLON} { YDVAR(1, VAR_TRUSTED_KEYS_FILE) } 287 trust-anchor{COLON} { YDVAR(1, VAR_TRUST_ANCHOR) } 288 val-override-date{COLON} { YDVAR(1, VAR_VAL_OVERRIDE_DATE) } 289 val-sig-skew-min{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MIN) } 290 val-sig-skew-max{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MAX) } 291 val-bogus-ttl{COLON} { YDVAR(1, VAR_BOGUS_TTL) } 292 val-clean-additional{COLON} { YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) } 293 val-permissive-mode{COLON} { YDVAR(1, VAR_VAL_PERMISSIVE_MODE) } 294 ignore-cd-flag{COLON} { YDVAR(1, VAR_IGNORE_CD_FLAG) } 295 val-log-level{COLON} { YDVAR(1, VAR_VAL_LOG_LEVEL) } 296 key-cache-size{COLON} { YDVAR(1, VAR_KEY_CACHE_SIZE) } 297 key-cache-slabs{COLON} { YDVAR(1, VAR_KEY_CACHE_SLABS) } 298 neg-cache-size{COLON} { YDVAR(1, VAR_NEG_CACHE_SIZE) } 299 val-nsec3-keysize-iterations{COLON} { 300 YDVAR(1, VAR_VAL_NSEC3_KEYSIZE_ITERATIONS) } 301 add-holddown{COLON} { YDVAR(1, VAR_ADD_HOLDDOWN) } 302 del-holddown{COLON} { YDVAR(1, VAR_DEL_HOLDDOWN) } 303 keep-missing{COLON} { YDVAR(1, VAR_KEEP_MISSING) } 304 use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) } 305 log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) } 306 log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) } 307 local-zone{COLON} { YDVAR(2, VAR_LOCAL_ZONE) } 308 local-data{COLON} { YDVAR(1, VAR_LOCAL_DATA) } 309 local-data-ptr{COLON} { YDVAR(1, VAR_LOCAL_DATA_PTR) } 310 unblock-lan-zones{COLON} { YDVAR(1, VAR_UNBLOCK_LAN_ZONES) } 311 statistics-interval{COLON} { YDVAR(1, VAR_STATISTICS_INTERVAL) } 312 statistics-cumulative{COLON} { YDVAR(1, VAR_STATISTICS_CUMULATIVE) } 313 extended-statistics{COLON} { YDVAR(1, VAR_EXTENDED_STATISTICS) } 314 remote-control{COLON} { YDVAR(0, VAR_REMOTE_CONTROL) } 315 control-enable{COLON} { YDVAR(1, VAR_CONTROL_ENABLE) } 316 control-interface{COLON} { YDVAR(1, VAR_CONTROL_INTERFACE) } 317 control-port{COLON} { YDVAR(1, VAR_CONTROL_PORT) } 318 server-key-file{COLON} { YDVAR(1, VAR_SERVER_KEY_FILE) } 319 server-cert-file{COLON} { YDVAR(1, VAR_SERVER_CERT_FILE) } 320 control-key-file{COLON} { YDVAR(1, VAR_CONTROL_KEY_FILE) } 321 control-cert-file{COLON} { YDVAR(1, VAR_CONTROL_CERT_FILE) } 322 python-script{COLON} { YDVAR(1, VAR_PYTHON_SCRIPT) } 323 python{COLON} { YDVAR(0, VAR_PYTHON) } 324 domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) } 325 minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } 326 rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } 327 max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } 328 dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) } 329 dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) } 330 dnstap{COLON} { YDVAR(0, VAR_DNSTAP) } 331 dnstap-enable{COLON} { YDVAR(1, VAR_DNSTAP_ENABLE) } 332 dnstap-socket-path{COLON} { YDVAR(1, VAR_DNSTAP_SOCKET_PATH) } 333 dnstap-send-identity{COLON} { YDVAR(1, VAR_DNSTAP_SEND_IDENTITY) } 334 dnstap-send-version{COLON} { YDVAR(1, VAR_DNSTAP_SEND_VERSION) } 335 dnstap-identity{COLON} { YDVAR(1, VAR_DNSTAP_IDENTITY) } 336 dnstap-version{COLON} { YDVAR(1, VAR_DNSTAP_VERSION) } 337 dnstap-log-resolver-query-messages{COLON} { 338 YDVAR(1, VAR_DNSTAP_LOG_RESOLVER_QUERY_MESSAGES) } 339 dnstap-log-resolver-response-messages{COLON} { 340 YDVAR(1, VAR_DNSTAP_LOG_RESOLVER_RESPONSE_MESSAGES) } 341 dnstap-log-client-query-messages{COLON} { 342 YDVAR(1, VAR_DNSTAP_LOG_CLIENT_QUERY_MESSAGES) } 343 dnstap-log-client-response-messages{COLON} { 344 YDVAR(1, VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES) } 345 dnstap-log-forwarder-query-messages{COLON} { 346 YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) } 347 dnstap-log-forwarder-response-messages{COLON} { 348 YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) } 349 <INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } 350 351 /* Quoted strings. Strip leading and ending quotes */ 352 <val>\" { BEGIN(quotedstring); LEXOUT(("QS ")); } 353 <quotedstring><<EOF>> { 354 yyerror("EOF inside quoted string"); 355 if(--num_args == 0) { BEGIN(INITIAL); } 356 else { BEGIN(val); } 357 } 358 <quotedstring>{DQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } 359 <quotedstring>{NEWLINE} { yyerror("newline inside quoted string, no end \""); 360 cfg_parser->line++; BEGIN(INITIAL); } 361 <quotedstring>\" { 362 LEXOUT(("QE ")); 363 if(--num_args == 0) { BEGIN(INITIAL); } 364 else { BEGIN(val); } 365 yytext[yyleng - 1] = '\0'; 366 yylval.str = strdup(yytext); 367 if(!yylval.str) 368 yyerror("out of memory"); 369 return STRING_ARG; 370 } 371 372 /* Single Quoted strings. Strip leading and ending quotes */ 373 <val>\' { BEGIN(singlequotedstr); LEXOUT(("SQS ")); } 374 <singlequotedstr><<EOF>> { 375 yyerror("EOF inside quoted string"); 376 if(--num_args == 0) { BEGIN(INITIAL); } 377 else { BEGIN(val); } 378 } 379 <singlequotedstr>{SQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } 380 <singlequotedstr>{NEWLINE} { yyerror("newline inside quoted string, no end '"); 381 cfg_parser->line++; BEGIN(INITIAL); } 382 <singlequotedstr>\' { 383 LEXOUT(("SQE ")); 384 if(--num_args == 0) { BEGIN(INITIAL); } 385 else { BEGIN(val); } 386 yytext[yyleng - 1] = '\0'; 387 yylval.str = strdup(yytext); 388 if(!yylval.str) 389 yyerror("out of memory"); 390 return STRING_ARG; 391 } 392 393 /* include: directive */ 394 <INITIAL,val>include{COLON} { 395 LEXOUT(("v(%s) ", yytext)); inc_prev = YYSTATE; BEGIN(include); } 396 <include><<EOF>> { 397 yyerror("EOF inside include directive"); 398 BEGIN(inc_prev); 399 } 400 <include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ } 401 <include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} 402 <include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); } 403 <include>{UNQUOTEDLETTER}* { 404 LEXOUT(("Iunquotedstr(%s) ", yytext)); 405 config_start_include_glob(yytext); 406 BEGIN(inc_prev); 407 } 408 <include_quoted><<EOF>> { 409 yyerror("EOF inside quoted string"); 410 BEGIN(inc_prev); 411 } 412 <include_quoted>{DQANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); } 413 <include_quoted>{NEWLINE} { yyerror("newline before \" in include name"); 414 cfg_parser->line++; BEGIN(inc_prev); } 415 <include_quoted>\" { 416 LEXOUT(("IQE ")); 417 yytext[yyleng - 1] = '\0'; 418 config_start_include_glob(yytext); 419 BEGIN(inc_prev); 420 } 421 <INITIAL,val><<EOF>> { 422 LEXOUT(("LEXEOF ")); 423 yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ 424 if (!config_include_stack) { 425 yyterminate(); 426 } else { 427 fclose(yyin); 428 config_end_include(); 429 } 430 } 431 432 <val>{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); 433 if(--num_args == 0) { BEGIN(INITIAL); } 434 yylval.str = strdup(yytext); return STRING_ARG; } 435 436 {UNQUOTEDLETTER_NOCOLON}* { 437 ub_c_error_msg("unknown keyword '%s'", yytext); 438 } 439 440 <*>. { 441 ub_c_error_msg("stray '%s'", yytext); 442 } 443 444 %% 445