PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/syslog-ng-3.2.5/lib/filter-expr-grammar.y

#
Happy | 639 lines | 532 code | 107 blank | 0 comment | 0 complexity | abd5fea1c36d82c325f10e608226e840 MD5 | raw file
  1. /*
  2. * Copyright (c) 2002-2010 BalaBit IT Ltd, Budapest, Hungary
  3. * Copyright (c) 1998-2010 Bal??zs Scheidler
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  18. *
  19. * As an additional exemption you are allowed to compile & link against the
  20. * OpenSSL libraries as published by the OpenSSL project. See the file
  21. * COPYING for details.
  22. *
  23. */
  24. %code requires {
  25. #include "filter-expr-parser.h"
  26. }
  27. %code {
  28. #include "syslog-names.h"
  29. #include "filter-expr-grammar.h"
  30. #include "plugin.h"
  31. FilterRE *last_re_filter;
  32. }
  33. %name-prefix "filter_expr_"
  34. %lex-param {CfgLexer *lexer}
  35. %parse-param {CfgLexer *lexer}
  36. %parse-param {FilterExprNode **result}
  37. %require "2.4.1"
  38. %locations
  39. %define api.pure
  40. %pure-parser
  41. %error-verbose
  42. %code {
  43. # define YYLLOC_DEFAULT(Current, Rhs, N) \
  44. do { \
  45. if (YYID (N)) \
  46. { \
  47. (Current).level = YYRHSLOC(Rhs, 1).level; \
  48. (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
  49. (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
  50. (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
  51. (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
  52. } \
  53. else \
  54. { \
  55. (Current).level = YYRHSLOC(Rhs, 0).level; \
  56. (Current).first_line = (Current).last_line = \
  57. YYRHSLOC (Rhs, 0).last_line; \
  58. (Current).first_column = (Current).last_column = \
  59. YYRHSLOC (Rhs, 0).last_column; \
  60. } \
  61. } while (YYID (0))
  62. #define CHECK_ERROR(val, token, errorfmt, ...) do { \
  63. if (!(val)) \
  64. { \
  65. if (errorfmt) \
  66. { \
  67. gchar __buf[256]; \
  68. g_snprintf(__buf, sizeof(__buf), errorfmt ? errorfmt : "x", ## __VA_ARGS__); \
  69. yyerror(& (token), lexer, NULL, __buf); \
  70. } \
  71. YYERROR; \
  72. } \
  73. } while (0)
  74. #define YYMAXDEPTH 20000
  75. }
  76. /* plugin types, must be equal to the numerical values of the plugin type in plugin.h */
  77. %token LL_CONTEXT_ROOT 1
  78. %token LL_CONTEXT_DESTINATION 2
  79. %token LL_CONTEXT_SOURCE 3
  80. %token LL_CONTEXT_PARSER 4
  81. %token LL_CONTEXT_REWRITE 5
  82. %token LL_CONTEXT_FILTER 6
  83. %token LL_CONTEXT_LOG 7
  84. %token LL_CONTEXT_BLOCK_DEF 8
  85. %token LL_CONTEXT_BLOCK_REF 9
  86. %token LL_CONTEXT_BLOCK_CONTENT 10
  87. %token LL_CONTEXT_PRAGMA 11
  88. %token LL_CONTEXT_FORMAT 12
  89. %token LL_CONTEXT_TEMPLATE_FUNC 13
  90. /* statements */
  91. %token KW_SOURCE 10000
  92. %token KW_FILTER 10001
  93. %token KW_PARSER 10002
  94. %token KW_DESTINATION 10003
  95. %token KW_LOG 10004
  96. %token KW_OPTIONS 10005
  97. %token KW_INCLUDE 10006
  98. %token KW_BLOCK 10007
  99. /* source & destination items */
  100. %token KW_INTERNAL 10010
  101. %token KW_FILE 10011
  102. %token KW_SQL 10030
  103. %token KW_TYPE 10031
  104. %token KW_COLUMNS 10032
  105. %token KW_INDEXES 10033
  106. %token KW_VALUES 10034
  107. %token KW_PASSWORD 10035
  108. %token KW_DATABASE 10036
  109. %token KW_USERNAME 10037
  110. %token KW_TABLE 10038
  111. %token KW_ENCODING 10039
  112. %token KW_SESSION_STATEMENTS 10040
  113. %token KW_DELIMITERS 10050
  114. %token KW_QUOTES 10051
  115. %token KW_QUOTE_PAIRS 10052
  116. %token KW_NULL 10053
  117. %token KW_SYSLOG 10060
  118. /* option items */
  119. %token KW_MARK_FREQ 10071
  120. %token KW_STATS_FREQ 10072
  121. %token KW_STATS_LEVEL 10073
  122. %token KW_FLUSH_LINES 10074
  123. %token KW_SUPPRESS 10075
  124. %token KW_FLUSH_TIMEOUT 10076
  125. %token KW_LOG_MSG_SIZE 10077
  126. %token KW_FILE_TEMPLATE 10078
  127. %token KW_PROTO_TEMPLATE 10079
  128. %token KW_CHAIN_HOSTNAMES 10090
  129. %token KW_NORMALIZE_HOSTNAMES 10091
  130. %token KW_KEEP_HOSTNAME 10092
  131. %token KW_CHECK_HOSTNAME 10093
  132. %token KW_BAD_HOSTNAME 10094
  133. %token KW_KEEP_TIMESTAMP 10100
  134. %token KW_USE_DNS 10110
  135. %token KW_USE_FQDN 10111
  136. %token KW_DNS_CACHE 10120
  137. %token KW_DNS_CACHE_SIZE 10121
  138. %token KW_DNS_CACHE_EXPIRE 10130
  139. %token KW_DNS_CACHE_EXPIRE_FAILED 10131
  140. %token KW_DNS_CACHE_HOSTS 10132
  141. %token KW_PERSIST_ONLY 10140
  142. %token KW_TZ_CONVERT 10150
  143. %token KW_TS_FORMAT 10151
  144. %token KW_FRAC_DIGITS 10152
  145. %token KW_LOG_FIFO_SIZE 10160
  146. %token KW_LOG_FETCH_LIMIT 10162
  147. %token KW_LOG_IW_SIZE 10163
  148. %token KW_LOG_PREFIX 10164
  149. %token KW_PROGRAM_OVERRIDE 10165
  150. %token KW_HOST_OVERRIDE 10166
  151. %token KW_THROTTLE 10170
  152. /* log statement options */
  153. %token KW_FLAGS 10190
  154. /* reader options */
  155. %token KW_PAD_SIZE 10200
  156. %token KW_TIME_ZONE 10201
  157. %token KW_RECV_TIME_ZONE 10202
  158. %token KW_SEND_TIME_ZONE 10203
  159. %token KW_LOCAL_TIME_ZONE 10204
  160. %token KW_FORMAT 10205
  161. /* timers */
  162. %token KW_TIME_REOPEN 10210
  163. %token KW_TIME_REAP 10211
  164. %token KW_TIME_SLEEP 10212
  165. /* destination options */
  166. %token KW_TMPL_ESCAPE 10220
  167. /* driver specific options */
  168. %token KW_OPTIONAL 10230
  169. /* file related options */
  170. %token KW_CREATE_DIRS 10240
  171. %token KW_OWNER 10250
  172. %token KW_GROUP 10251
  173. %token KW_PERM 10252
  174. %token KW_DIR_OWNER 10260
  175. %token KW_DIR_GROUP 10261
  176. %token KW_DIR_PERM 10262
  177. %token KW_TEMPLATE 10270
  178. %token KW_TEMPLATE_ESCAPE 10271
  179. %token KW_DEFAULT_FACILITY 10300
  180. %token KW_DEFAULT_LEVEL 10301
  181. %token KW_PORT 10323
  182. /* misc options */
  183. %token KW_USE_TIME_RECVD 10340
  184. /* filter items*/
  185. %token KW_FACILITY 10350
  186. %token KW_LEVEL 10351
  187. %token KW_HOST 10352
  188. %token KW_MATCH 10353
  189. %token KW_MESSAGE 10354
  190. %token KW_NETMASK 10355
  191. %token KW_TAGS 10356
  192. /* parser items */
  193. %token KW_VALUE 10361
  194. /* rewrite items */
  195. %token KW_REWRITE 10370
  196. %token KW_SET 10371
  197. %token KW_SUBST 10372
  198. /* yes/no switches */
  199. %token KW_YES 10380
  200. %token KW_NO 10381
  201. %token KW_IFDEF 10410
  202. %token KW_ENDIF 10411
  203. %token LL_DOTDOT 10420
  204. %token <cptr> LL_IDENTIFIER 10421
  205. %token <num> LL_NUMBER 10422
  206. %token <fnum> LL_FLOAT 10423
  207. %token <cptr> LL_STRING 10424
  208. %token <token> LL_TOKEN 10425
  209. %token <cptr> LL_BLOCK 10426
  210. %token LL_PRAGMA 10427
  211. %token LL_EOL 10428
  212. %token LL_ERROR 10429
  213. %type <num> yesno
  214. %type <num> dnsmode
  215. %type <num> regexp_option_flags
  216. %type <num> dest_writer_options_flags
  217. %type <cptr> string
  218. %type <cptr> string_or_number
  219. %type <ptr> string_list
  220. %type <ptr> string_list_build
  221. %type <num> facility_string
  222. %type <num> level_string
  223. %token KW_PROGRAM
  224. %left ';'
  225. %left KW_OR
  226. %left KW_AND
  227. %left KW_NOT
  228. %left KW_LT KW_LE KW_EQ KW_NE KW_GE KW_GT
  229. %left KW_NUM_LT KW_NUM_LE KW_NUM_EQ KW_NUM_NE KW_NUM_GE KW_NUM_GT
  230. %type <node> filter_expr
  231. %type <node> filter_simple_expr
  232. %type <num> filter_fac_list
  233. %type <num> filter_level_list
  234. %type <num> filter_level
  235. %type <token> operator
  236. %%
  237. start
  238. : filter_expr { *result = $1; if (yychar != YYEMPTY) { cfg_lexer_unput_token(lexer, &yylval); } YYACCEPT; }
  239. ;
  240. filter_expr
  241. : filter_simple_expr { $$ = $1; if (!$1) YYERROR; }
  242. | KW_NOT filter_expr { ((FilterExprNode *) $2)->comp = !(((FilterExprNode *) $2)->comp); $$ = $2; }
  243. | filter_expr KW_OR filter_expr { $$ = fop_or_new($1, $3); }
  244. | filter_expr KW_AND filter_expr { $$ = fop_and_new($1, $3); }
  245. | filter_expr ';' filter_expr { $$ = fop_and_new($1, $3); }
  246. | filter_expr ';' { $$ = $1; }
  247. | '(' filter_expr ')' { $$ = $2; }
  248. ;
  249. filter_simple_expr
  250. : KW_FACILITY '(' filter_fac_list ')' { $$ = filter_facility_new($3); }
  251. | KW_FACILITY '(' LL_NUMBER ')' { $$ = filter_facility_new(0x80000000 | $3); }
  252. | KW_LEVEL '(' filter_level_list ')' { $$ = filter_level_new($3); }
  253. | KW_FILTER '(' string ')' { $$ = filter_call_new($3, configuration); free($3); }
  254. | KW_NETMASK '(' string ')' { $$ = filter_netmask_new($3); free($3); }
  255. | KW_TAGS '(' string_list ')' { $$ = filter_tags_new($3); }
  256. | KW_PROGRAM '(' string
  257. {
  258. last_re_filter = (FilterRE *) filter_re_new(LM_V_PROGRAM);
  259. }
  260. filter_re_opts ')'
  261. {
  262. if(!filter_re_set_regexp(last_re_filter, $3))
  263. YYERROR;
  264. free($3);
  265. $$ = &last_re_filter->super;
  266. }
  267. | KW_HOST '(' string
  268. {
  269. last_re_filter = (FilterRE *) filter_re_new(LM_V_HOST);
  270. }
  271. filter_re_opts ')'
  272. {
  273. if(!filter_re_set_regexp(last_re_filter, $3))
  274. YYERROR;
  275. free($3);
  276. $$ = &last_re_filter->super;
  277. }
  278. | KW_MATCH '(' string
  279. {
  280. last_re_filter = (FilterRE *) filter_match_new();
  281. }
  282. filter_match_opts ')'
  283. {
  284. if(!filter_re_set_regexp(last_re_filter, $3))
  285. YYERROR;
  286. free($3);
  287. $$ = &last_re_filter->super;
  288. if (last_re_filter->value_handle == 0)
  289. {
  290. static gboolean warn_written = FALSE;
  291. if (!warn_written)
  292. {
  293. msg_warning("WARNING: the match() filter without the use of the value() option is deprecated and hinders performance, please update your configuration",
  294. NULL);
  295. warn_written = TRUE;
  296. }
  297. }
  298. }
  299. | KW_MESSAGE '(' string
  300. {
  301. last_re_filter = (FilterRE *) filter_re_new(LM_V_MESSAGE);
  302. }
  303. filter_re_opts ')'
  304. {
  305. if(!filter_re_set_regexp(last_re_filter, $3))
  306. YYERROR;
  307. free($3);
  308. $$ = &last_re_filter->super;
  309. }
  310. | KW_SOURCE '(' string
  311. {
  312. last_re_filter = (FilterRE *) filter_re_new(LM_V_SOURCE);
  313. filter_re_set_matcher(last_re_filter, log_matcher_string_new());
  314. }
  315. filter_re_opts ')'
  316. {
  317. if(!filter_re_set_regexp(last_re_filter, $3))
  318. YYERROR;
  319. free($3);
  320. $$ = &last_re_filter->super;
  321. }
  322. | LL_IDENTIFIER
  323. {
  324. Plugin *p;
  325. gint context = LL_CONTEXT_FILTER;
  326. FilterExprNode *node;
  327. p = plugin_find(configuration, context, $1);
  328. CHECK_ERROR(p, @1, "%s plugin %s not found", cfg_lexer_lookup_context_name_by_type(context), $1);
  329. node = (FilterExprNode *) plugin_parse_config(p, configuration, &@1);
  330. free($1);
  331. if (!node)
  332. {
  333. YYERROR;
  334. }
  335. $$ = node;
  336. }
  337. | string operator string
  338. {
  339. LogTemplate *left, *right;
  340. GError *error = NULL;
  341. left = log_template_new(configuration, NULL, $1);
  342. right = log_template_new(configuration, NULL, $3);
  343. free($1);
  344. free($3);
  345. CHECK_ERROR(log_template_compile(left, &error), @1, "error compiling template: %s", error->message);
  346. CHECK_ERROR(log_template_compile(right, &error), @3, "error compiling template: %s", error->message);
  347. $$ = fop_cmp_new(left, right, $2);
  348. }
  349. ;
  350. operator
  351. : KW_NUM_LT { $$ = yylval.token; }
  352. | KW_NUM_LE { $$ = yylval.token; }
  353. | KW_NUM_EQ { $$ = yylval.token; }
  354. | KW_NUM_NE { $$ = yylval.token; }
  355. | KW_NUM_GE { $$ = yylval.token; }
  356. | KW_NUM_GT { $$ = yylval.token; }
  357. | KW_LT { $$ = yylval.token; }
  358. | KW_LE { $$ = yylval.token; }
  359. | KW_EQ { $$ = yylval.token; }
  360. | KW_NE { $$ = yylval.token; }
  361. | KW_GE { $$ = yylval.token; }
  362. | KW_GT { $$ = yylval.token; }
  363. ;
  364. filter_match_opts
  365. : filter_match_opt filter_match_opts
  366. |
  367. ;
  368. filter_match_opt
  369. : filter_re_opt
  370. | KW_VALUE '(' string ')'
  371. {
  372. const gchar *p = $3;
  373. if (p[0] == '$')
  374. {
  375. msg_warning("Value references in filters should not use the '$' prefix, those are only needed in templates",
  376. evt_tag_str("value", $3),
  377. NULL);
  378. p++;
  379. }
  380. last_re_filter->value_handle = log_msg_get_value_handle(p);
  381. free($3);
  382. }
  383. ;
  384. filter_re_opts
  385. : filter_re_opt filter_re_opts
  386. |
  387. ;
  388. filter_re_opt
  389. : KW_TYPE '(' string ')'
  390. {
  391. filter_re_set_matcher(last_re_filter, log_matcher_new($3));
  392. free($3);
  393. }
  394. | KW_FLAGS '(' regexp_option_flags ')' { filter_re_set_flags(last_re_filter, $3); }
  395. ;
  396. filter_fac_list
  397. : facility_string filter_fac_list { $$ = (1 << ($1 >> 3)) | $2; }
  398. | facility_string { $$ = (1 << ($1 >> 3)); }
  399. ;
  400. filter_level_list
  401. : filter_level filter_level_list { $$ = $1 | $2; }
  402. | filter_level { $$ = $1; }
  403. ;
  404. filter_level
  405. : level_string LL_DOTDOT level_string
  406. {
  407. $$ = syslog_make_range($1, $3);
  408. }
  409. | level_string
  410. {
  411. $$ = 1 << $1;
  412. }
  413. ;
  414. string
  415. : LL_IDENTIFIER
  416. | LL_STRING
  417. ;
  418. yesno
  419. : KW_YES { $$ = 1; }
  420. | KW_NO { $$ = 0; }
  421. | LL_NUMBER { $$ = $1; }
  422. ;
  423. dnsmode
  424. : yesno { $$ = $1; }
  425. | KW_PERSIST_ONLY { $$ = 2; }
  426. ;
  427. string_or_number
  428. : string { $$ = $1; }
  429. | LL_NUMBER { $$ = strdup(lexer->token_text->str); }
  430. | LL_FLOAT { $$ = strdup(lexer->token_text->str); }
  431. ;
  432. string_list
  433. : string_list_build { $$ = g_list_reverse($1); }
  434. ;
  435. string_list_build
  436. : string string_list_build { $$ = g_list_append($2, g_strdup($1)); free($1); }
  437. | { $$ = NULL; }
  438. ;
  439. level_string
  440. : string
  441. {
  442. /* return the numeric value of the "level" */
  443. int n = syslog_name_lookup_level_by_name($1);
  444. CHECK_ERROR((n != -1), @1, "Unknown priority level\"%s\"", $1);
  445. free($1);
  446. $$ = n;
  447. }
  448. ;
  449. facility_string
  450. : string
  451. {
  452. /* return the numeric value of facility */
  453. int n = syslog_name_lookup_facility_by_name($1);
  454. CHECK_ERROR((n != -1), @1, "Unknown facility \"%s\"", $1);
  455. free($1);
  456. $$ = n;
  457. }
  458. | KW_SYSLOG { $$ = LOG_SYSLOG; }
  459. ;
  460. regexp_option_flags
  461. : string regexp_option_flags { $$ = log_matcher_lookup_flag($1) | $2; free($1); }
  462. | { $$ = 0; }
  463. ;
  464. source_reader_options
  465. : source_reader_option source_reader_options
  466. |
  467. ;
  468. source_reader_option
  469. : KW_LOG_IW_SIZE '(' LL_NUMBER ')' { last_reader_options->super.init_window_size = $3; }
  470. | KW_CHAIN_HOSTNAMES '(' yesno ')' { last_reader_options->super.chain_hostnames = $3; }
  471. | KW_NORMALIZE_HOSTNAMES '(' yesno ')' { last_reader_options->super.normalize_hostnames = $3; }
  472. | KW_KEEP_HOSTNAME '(' yesno ')' { last_reader_options->super.keep_hostname = $3; }
  473. | KW_USE_FQDN '(' yesno ')' { last_reader_options->super.use_fqdn = $3; }
  474. | KW_USE_DNS '(' dnsmode ')' { last_reader_options->super.use_dns = $3; }
  475. | KW_DNS_CACHE '(' yesno ')' { last_reader_options->super.use_dns_cache = $3; }
  476. | KW_PROGRAM_OVERRIDE '(' string ')' { last_reader_options->super.program_override = g_strdup($3); free($3); }
  477. | KW_HOST_OVERRIDE '(' string ')' { last_reader_options->super.host_override = g_strdup($3); free($3); }
  478. | KW_LOG_PREFIX '(' string ')' { gchar *p = strrchr($3, ':'); if (p) *p = 0; last_reader_options->super.program_override = g_strdup($3); free($3); }
  479. | KW_TIME_ZONE '(' string ')' { last_reader_options->parse_options.recv_time_zone = g_strdup($3); free($3); }
  480. | KW_CHECK_HOSTNAME '(' yesno ')' { last_reader_options->check_hostname = $3; }
  481. | KW_FLAGS '(' source_reader_option_flags ')'
  482. | KW_LOG_MSG_SIZE '(' LL_NUMBER ')' { last_reader_options->msg_size = $3; }
  483. | KW_LOG_FETCH_LIMIT '(' LL_NUMBER ')' { last_reader_options->fetch_limit = $3; }
  484. | KW_PAD_SIZE '(' LL_NUMBER ')' { last_reader_options->padding = $3; }
  485. | KW_KEEP_TIMESTAMP '(' yesno ')' { last_reader_options->super.keep_timestamp = $3; }
  486. | KW_ENCODING '(' string ')' { last_reader_options->text_encoding = g_strdup($3); free($3); }
  487. | KW_TAGS '(' string_list ')' { log_reader_options_set_tags(last_reader_options, $3); }
  488. | KW_FORMAT '(' string ')' { last_reader_options->parse_options.format = g_strdup($3); free($3); }
  489. | KW_DEFAULT_LEVEL '(' level_string ')'
  490. {
  491. if (last_reader_options->parse_options.default_pri == 0xFFFF)
  492. last_reader_options->parse_options.default_pri = LOG_USER;
  493. last_reader_options->parse_options.default_pri = (last_reader_options->parse_options.default_pri & ~7) | $3;
  494. }
  495. | KW_DEFAULT_FACILITY '(' facility_string ')'
  496. {
  497. if (last_reader_options->parse_options.default_pri == 0xFFFF)
  498. last_reader_options->parse_options.default_pri = LOG_NOTICE;
  499. last_reader_options->parse_options.default_pri = (last_reader_options->parse_options.default_pri & 7) | $3;
  500. }
  501. ;
  502. source_reader_option_flags
  503. : string source_reader_option_flags { CHECK_ERROR(log_reader_options_process_flag(last_reader_options, $1), @1, "Unknown flag %s", $1); free($1); }
  504. |
  505. ;
  506. dest_writer_options
  507. : dest_writer_option dest_writer_options
  508. |
  509. ;
  510. dest_writer_option
  511. : KW_FLAGS '(' dest_writer_options_flags ')' { last_writer_options->options = $3; }
  512. | KW_LOG_FIFO_SIZE '(' LL_NUMBER ')' { last_writer_options->mem_fifo_size = $3; }
  513. | KW_FLUSH_LINES '(' LL_NUMBER ')' { last_writer_options->flush_lines = $3; }
  514. | KW_FLUSH_TIMEOUT '(' LL_NUMBER ')' { last_writer_options->flush_timeout = $3; }
  515. | KW_SUPPRESS '(' LL_NUMBER ')' { last_writer_options->suppress = $3; }
  516. | KW_TEMPLATE '(' string ')' {
  517. GError *error = NULL;
  518. last_writer_options->template = cfg_check_inline_template(configuration, $3);
  519. CHECK_ERROR(log_template_compile(last_writer_options->template, &error), @3, "Error compiling template (%s)", error->message);
  520. free($3);
  521. }
  522. | KW_TEMPLATE_ESCAPE '(' yesno ')' { log_writer_options_set_template_escape(last_writer_options, $3); }
  523. | KW_TIME_ZONE '(' string ')' { last_writer_options->template_options.time_zone[LTZ_SEND] = g_strdup($3); free($3); }
  524. | KW_TS_FORMAT '(' string ')' { last_writer_options->template_options.ts_format = cfg_ts_format_value($3); free($3); }
  525. | KW_FRAC_DIGITS '(' LL_NUMBER ')' { last_writer_options->template_options.frac_digits = $3; }
  526. | KW_THROTTLE '(' LL_NUMBER ')' { last_writer_options->throttle = $3; }
  527. ;
  528. dest_writer_options_flags
  529. : string dest_writer_options_flags { $$ = log_writer_options_lookup_flag($1) | $2; free($1); }
  530. | { $$ = 0; }
  531. ;
  532. %%