diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:33:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:33:30 +0000 |
commit | c61e14d3a8412cd50d98aab604e607692c844c8a (patch) | |
tree | 4925aca0e6b64c8664ea2f3fdfa99a52dc93d5da /libsmartcols | |
parent | Adding upstream version 2.39.3. (diff) | |
download | util-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.tar.xz util-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.zip |
Adding upstream version 2.40.upstream/2.40
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libsmartcols')
37 files changed, 7996 insertions, 378 deletions
diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am index 012848b..77e87b5 100644 --- a/libsmartcols/Makemodule.am +++ b/libsmartcols/Makemodule.am @@ -12,4 +12,7 @@ pkgconfig_DATA += libsmartcols/smartcols.pc PATHFILES += libsmartcols/smartcols.pc EXTRA_DIST += libsmartcols/COPYING +MANPAGES += libsmartcols/scols-filter.5 +dist_noinst_DATA += libsmartcols/scols-filter.5.adoc + endif # BUILD_LIBSMARTCOLS diff --git a/libsmartcols/docs/Makefile.in b/libsmartcols/docs/Makefile.in index de740f1..df70f76 100644 --- a/libsmartcols/docs/Makefile.in +++ b/libsmartcols/docs/Makefile.in @@ -117,7 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \ - $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ul.m4 $(top_srcdir)/m4/year2038.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -159,10 +160,12 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BISON = @BISON@ BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@ @@ -192,6 +195,7 @@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ +FLEX = @FLEX@ FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ @@ -219,6 +223,8 @@ LIBFDISK_VERSION = @LIBFDISK_VERSION@ LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ +LIBLASTLOG2_VERSION = @LIBLASTLOG2_VERSION@ +LIBLASTLOG2_VERSION_INFO = @LIBLASTLOG2_VERSION_INFO@ LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@ LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@ LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@ @@ -244,6 +250,7 @@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MATH_LIBS = @MATH_LIBS@ MKDIR_P = @MKDIR_P@ +MQ_LIBS = @MQ_LIBS@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ @@ -257,7 +264,6 @@ NCURSES_CFLAGS = @NCURSES_CFLAGS@ NCURSES_LIBS = @NCURSES_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ -NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -297,6 +303,8 @@ SHELL = @SHELL@ SOCKET_LIBS = @SOCKET_LIBS@ SOLIB_CFLAGS = @SOLIB_CFLAGS@ SOLIB_LDFLAGS = @SOLIB_LDFLAGS@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ STRIP = @STRIP@ SUID_CFLAGS = @SUID_CFLAGS@ SUID_LDFLAGS = @SUID_LDFLAGS@ @@ -382,6 +390,7 @@ sysconfdir = @sysconfdir@ sysconfstaticdir = @sysconfstaticdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -391,6 +400,7 @@ usrsbin_execdir = @usrsbin_execdir@ vendordir = @vendordir@ with_bashcompletiondir = @with_bashcompletiondir@ with_systemdsystemunitdir = @with_systemdsystemunitdir@ +with_tmpfilesdir = @with_tmpfilesdir@ # We require automake 1.10 at least. AUTOMAKE_OPTIONS = 1.10 diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml index 9ded584..bd55182 100644 --- a/libsmartcols/docs/libsmartcols-docs.xml +++ b/libsmartcols/docs/libsmartcols-docs.xml @@ -35,6 +35,7 @@ available from https://www.kernel.org/pub/linux/utils/util-linux/. <xi:include href="xml/cell.xml"/> <xi:include href="xml/symbols.xml"/> <xi:include href="xml/grouping.xml"/> + <xi:include href="xml/filter.xml"/> </part> <part> <title>Printing</title> @@ -82,4 +83,16 @@ available from https://www.kernel.org/pub/linux/utils/util-linux/. <title>Index of new symbols in 2.35</title> <xi:include href="xml/api-index-2.35.xml"><xi:fallback /></xi:include> </index> + <index role="2.38"> + <title>Index of new symbols in 2.38</title> + <xi:include href="xml/api-index-2.38.xml"><xi:fallback /></xi:include> + </index> + <index role="2.39"> + <title>Index of new symbols in 2.39</title> + <xi:include href="xml/api-index-2.39.xml"><xi:fallback /></xi:include> + </index> + <index role="2.40"> + <title>Index of new symbols in 2.40</title> + <xi:include href="xml/api-index-2.40.xml"><xi:fallback /></xi:include> + </index> </book> diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index fd6f2ba..5b343d0 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -5,9 +5,11 @@ scols_cell_copy_content scols_cell_get_alignment scols_cell_get_color scols_cell_get_data +scols_cell_get_datasiz scols_cell_get_flags scols_cell_get_userdata scols_cell_refer_data +scols_cell_refer_memory scols_cell_set_color scols_cell_set_data scols_cell_set_flags @@ -20,6 +22,7 @@ scols_reset_cell <FILE>column</FILE> libscols_column scols_column_get_color +scols_column_get_data_type scols_column_get_flags scols_column_get_header scols_column_get_json_type @@ -29,6 +32,8 @@ scols_column_get_safechars scols_column_get_table scols_column_get_whint scols_column_get_width +scols_column_get_wrap_data +scols_column_has_data_func scols_column_is_customwrap scols_column_is_hidden scols_column_is_noextremes @@ -39,6 +44,8 @@ scols_column_is_trunc scols_column_is_wrap scols_column_set_cmpfunc scols_column_set_color +scols_column_set_data_func +scols_column_set_data_type scols_column_set_flags scols_column_set_json_type scols_column_set_name @@ -50,8 +57,10 @@ scols_copy_column scols_new_column scols_ref_column scols_unref_column +scols_shellvar_name scols_wrapnl_chunksize scols_wrapnl_nextchunk +scols_wrapzero_nextchunk </SECTION> <SECTION> @@ -64,6 +73,30 @@ scols_reset_iter </SECTION> <SECTION> +<FILE>filter</FILE> +libscols_filter +libscols_counter +scols_counter_get_name +scols_counter_get_result +scols_counter_set_func +scols_counter_set_name +scols_counter_set_param +scols_dump_filter +scols_filter_assign_column +scols_filter_get_errmsg +scols_filter_new_counter +scols_filter_next_counter +scols_filter_next_holder +scols_filter_parse_string +scols_filter_set_filler_cb +scols_line_apply_filter +scols_line_is_filled +scols_new_filter +scols_ref_filter +scols_unref_filter +</SECTION> + +<SECTION> <FILE>line</FILE> libscols_line scols_copy_line @@ -146,6 +179,7 @@ scols_table_enable_shellvar scols_table_get_column scols_table_get_column_by_name scols_table_get_column_separator +scols_table_get_cursor scols_table_get_line scols_table_get_line_separator scols_table_get_name diff --git a/libsmartcols/docs/version.xml b/libsmartcols/docs/version.xml index a69af57..4bdd32f 100644 --- a/libsmartcols/docs/version.xml +++ b/libsmartcols/docs/version.xml @@ -1 +1 @@ -2.39.3 +2.40 diff --git a/libsmartcols/meson.build b/libsmartcols/meson.build index 122b1e8..bedd6da 100644 --- a/libsmartcols/meson.build +++ b/libsmartcols/meson.build @@ -11,6 +11,18 @@ configure_file( install_dir : join_paths(get_option('includedir'), 'libsmartcols'), ) +scols_bison = generator( + bison, + output : ['@BASENAME@.c', '@BASENAME@.h'], + arguments : ['@INPUT@', '--output=@OUTPUT0@', '--defines=@OUTPUT1@']) +scols_parser_c = scols_bison.process('src/filter-parser.y') + +scols_flex = generator( + flex, + output : ['@BASENAME@.c', '@BASENAME@.h'], + arguments : ['--outfile=@OUTPUT0@', '--header-file=@OUTPUT1@', '@INPUT@']) +scols_scanner_c = scols_flex.process('src/filter-scanner.l') + lib_smartcols_sources = ''' src/smartcolsP.h src/iter.c @@ -26,7 +38,12 @@ lib_smartcols_sources = ''' src/grouping.c src/walk.c src/init.c -'''.split() + src/filter.c + src/filter-param.c + src/filter-expr.c +'''.split() \ + + scols_parser_c + scols_scanner_c + libsmartcols_sym = 'src/libsmartcols.sym' libsmartcols_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libsmartcols_sym) @@ -55,3 +72,5 @@ if build_libsmartcols meson.override_dependency('smartcols', smartcols_dep) endif endif + +manadocs += ['libsmartcols/scols-filter.5.adoc'] diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am index c0130b9..b192eba 100644 --- a/libsmartcols/samples/Makemodule.am +++ b/libsmartcols/samples/Makemodule.am @@ -4,13 +4,13 @@ check_PROGRAMS += \ sample-scols-title \ sample-scols-wrap \ sample-scols-continuous \ + sample-scols-continuous-json \ sample-scols-fromfile \ sample-scols-grouping-simple \ sample-scols-grouping-overlay \ sample-scols-maxout -sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \ - -I$(ul_libsmartcols_incdir) +sample_scols_cflags = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) sample_scols_ldadd = libsmartcols.la $(LDADD) if HAVE_OPENAT @@ -36,6 +36,10 @@ sample_scols_continuous_SOURCES = libsmartcols/samples/continuous.c sample_scols_continuous_LDADD = $(sample_scols_ldadd) libcommon.la sample_scols_continuous_CFLAGS = $(sample_scols_cflags) +sample_scols_continuous_json_SOURCES = libsmartcols/samples/continuous-json.c +sample_scols_continuous_json_LDADD = $(sample_scols_ldadd) libcommon.la +sample_scols_continuous_json_CFLAGS = $(sample_scols_cflags) + sample_scols_maxout_SOURCES = libsmartcols/samples/maxout.c sample_scols_maxout_LDADD = $(sample_scols_ldadd) sample_scols_maxout_CFLAGS = $(sample_scols_cflags) diff --git a/libsmartcols/samples/continuous-json.c b/libsmartcols/samples/continuous-json.c new file mode 100644 index 0000000..ae1f405 --- /dev/null +++ b/libsmartcols/samples/continuous-json.c @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright (C) 2016 Karel Zak <kzak@redhat.com> + * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de> + */ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "c.h" +#include "xalloc.h" + +#include "libsmartcols.h" + +/* add columns to the @tb */ +static void setup_columns(struct libscols_table *tb) +{ + scols_table_enable_maxout(tb, 1); + if (!scols_table_new_column(tb, "COUNT", 0.1, SCOLS_FL_RIGHT)) + goto fail; + if (!scols_table_new_column(tb, "TEXT", 0.9, 0)) + goto fail; + return; +fail: + scols_unref_table(tb); + err(EXIT_FAILURE, "failed to create output columns"); +} + +static struct libscols_line *add_line(struct libscols_table *tb, int i) +{ + char *p; + struct libscols_line *ln = scols_table_new_line(tb, NULL); + + if (!ln) + err(EXIT_FAILURE, "failed to create output line"); + + xasprintf(&p, "%d", i); + if (scols_line_refer_data(ln, 0, p)) + goto fail; + + xasprintf(&p, "text%d", i); + if (scols_line_refer_data(ln, 1, p)) + goto fail; + + return ln; +fail: + scols_unref_table(tb); + err(EXIT_FAILURE, "failed to create output line"); +} + +int main(void) +{ + struct libscols_table *tb; + size_t i; + + scols_init_debug(0); + + tb = scols_new_table(); + if (!tb) + err(EXIT_FAILURE, "failed to create output table"); + scols_table_enable_json(tb, 1); + + setup_columns(tb); + + for (i = 0; i < 10; i++) { + struct libscols_line *line; + + line = add_line(tb, i); + + /* print the line */ + scols_table_print_range(tb, line, NULL); + + fflush(scols_table_get_stream(tb)); + } + + scols_unref_table(tb); + return EXIT_SUCCESS; +} diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c index 6bdba6e..432b027 100644 --- a/libsmartcols/samples/continuous.c +++ b/libsmartcols/samples/continuous.c @@ -65,7 +65,7 @@ fail: err(EXIT_FAILURE, "failed to create output line"); } -int main(int argc, char *argv[]) +int main(void) { struct libscols_table *tb; size_t i; @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) struct libscols_line *line; struct timeval now; int done = 0; - char *timecell = xmalloc( timecellsz ); + char *timecell = xcalloc(1, timecellsz ); line = add_line(tb, i); diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c index 0fdc929..cf77cc4 100644 --- a/libsmartcols/samples/fromfile.c +++ b/libsmartcols/samples/fromfile.c @@ -18,136 +18,63 @@ #include "strutils.h" #include "xalloc.h" #include "optutils.h" +#include "mangle.h" +#include "path.h" #include "libsmartcols.h" -struct column_flag { - const char *name; - int mask; -}; - -static const struct column_flag flags[] = { - { "trunc", SCOLS_FL_TRUNC }, - { "tree", SCOLS_FL_TREE }, - { "right", SCOLS_FL_RIGHT }, - { "strictwidth",SCOLS_FL_STRICTWIDTH }, - { "noextremes", SCOLS_FL_NOEXTREMES }, - { "hidden", SCOLS_FL_HIDDEN }, - { "wrap", SCOLS_FL_WRAP }, - { "wrapnl", SCOLS_FL_WRAP }, - { "none", 0 } -}; - -static long name_to_flag(const char *name, size_t namesz) +static struct libscols_column *parse_column(const char *path) { - size_t i; + char buf[BUFSIZ]; + struct libscols_column *cl; - for (i = 0; i < ARRAY_SIZE(flags); i++) { - const char *cn = flags[i].name; + if (ul_path_read_buffer(NULL, buf, sizeof(buf), path) < 0) + err(EXIT_FAILURE, "failed to read column: %s", path); - if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) - return flags[i].mask; - } - warnx("unknown flag: %s", name); - return -1; -} - -static int parse_column_flags(char *str) -{ - unsigned long num_flags = 0; - - if (string_to_bitmask(str, &num_flags, name_to_flag)) - err(EXIT_FAILURE, "failed to parse column flags"); - - return num_flags; -} - -static struct libscols_column *parse_column(FILE *f) -{ - size_t len = 0; - char *line = NULL; - int nlines = 0; + cl = scols_new_column(); + if (!cl) + err(EXIT_FAILURE, "failed to allocate column"); - struct libscols_column *cl = NULL; + if (scols_column_set_properties(cl, buf) != 0) + err(EXIT_FAILURE, "failed to set column properties"); - while (getline(&line, &len, f) != -1) { - - char *p = strrchr(line, '\n'); - if (p) - *p = '\0'; - - switch (nlines) { - case 0: /* NAME */ - cl = scols_new_column(); - if (!cl) - goto fail; - if (scols_column_set_name(cl, line) != 0) - goto fail; - break; - - case 1: /* WIDTH-HINT */ - { - double whint = strtod_or_err(line, "failed to parse column whint"); - if (scols_column_set_whint(cl, whint)) - goto fail; - break; - } - case 2: /* FLAGS */ - { - int num_flags = parse_column_flags(line); - if (scols_column_set_flags(cl, num_flags)) - goto fail; - if (strcmp(line, "wrapnl") == 0) { - scols_column_set_wrapfunc(cl, - scols_wrapnl_chunksize, - scols_wrapnl_nextchunk, - NULL); - scols_column_set_safechars(cl, "\n"); - } - break; - } - case 3: /* COLOR */ - if (scols_column_set_color(cl, line)) - goto fail; - break; - default: - break; - } - - nlines++; - } - - free(line); return cl; -fail: - free(line); - scols_unref_column(cl); - return NULL; } static int parse_column_data(FILE *f, struct libscols_table *tb, int col) { size_t len = 0, nlines = 0; - int i; - char *str = NULL; + ssize_t i; + char *str = NULL, *p; while ((i = getline(&str, &len, f)) != -1) { - struct libscols_line *ln; - char *p = strrchr(str, '\n'); - if (p) - *p = '\0'; - - while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') { - *p = '\n'; - memmove(p + 1, p + 2, i - (p + 2 - str)); - } + int rc = 0; ln = scols_table_get_line(tb, nlines++); if (!ln) break; - if (*str && scols_line_set_data(ln, col, str) != 0) + p = strrchr(str, '\n'); + if (p) + *p = '\0'; + if (!*str) + continue; + + /* convert \x?? to real bytes */ + if (strstr(str, "\\x")) { + struct libscols_cell *ce = scols_line_get_cell(ln, col); + size_t sz = i + 1; + char *buf = xcalloc(1, sz); + + sz = unhexmangle_to_buffer(str, buf, sz); + if (sz) + rc = scols_cell_refer_memory(ce, buf, sz); + else + free(buf); + } else + rc = scols_line_set_data(ln, col, str); + if (rc) err(EXIT_FAILURE, "failed to add output data"); } @@ -193,6 +120,83 @@ static void compose_tree(struct libscols_table *tb, int parent_col, int id_col) scols_free_iter(itr); } +static struct libscols_filter *init_filter( + struct libscols_table *tb, + const char *query, int dump) +{ + struct libscols_iter *itr; + struct libscols_filter *f = scols_new_filter(NULL); + const char *name = NULL; + int rc = 0; + + if (!f) + err(EXIT_FAILURE, "failed to allocate filter"); + if (scols_filter_parse_string(f, query) != 0) { + warnx("failed to parse filter: %s", scols_filter_get_errmsg(f)); + scols_unref_filter(f); + return NULL; + } + + itr = scols_new_iter(SCOLS_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, "failed to allocate iterator"); + + while (scols_filter_next_holder(f, itr, &name, 0) == 0) { + struct libscols_column *col; + + col = scols_table_get_column_by_name(tb, name); + if (!col) { + warnx("unknown column '%s' in filter", name); + rc++; + continue; + } + scols_filter_assign_column(f, itr, name, col); + } + + scols_free_iter(itr); + if (dump && f) + scols_dump_filter(f, stdout); + if (rc) { + scols_unref_filter(f); + errx(EXIT_FAILURE, "failed to initialize filter"); + } + + return f; +} + +/* Note: This is a simple (naive) way to use the filter, employed here for + * testing functionality. + * + * A more effective approach to using the filter is demonstrated in lsblk.c, + * where data manipulation is divided into two steps. The initial step prepares + * only the data necessary for evaluating the filter, and the remaining data is + * gathered later, only if necessary. + */ +static void apply_filter(struct libscols_table *tb, struct libscols_filter *fltr) +{ + struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); + struct libscols_line *ln; + + if (!itr) + err(EXIT_FAILURE, "failed to allocate iterator"); + + while (scols_table_next_line(tb, itr, &ln) == 0) { + int status = 0; + + if (scols_line_apply_filter(ln, fltr, &status) != 0) + err(EXIT_FAILURE, "failed to apply filter"); + if (status == 0) { + struct libscols_line *x = scols_line_get_parent(ln); + + if (x) + scols_line_remove_child(x, ln); + scols_table_remove_line(tb, ln); + ln = NULL; + } + } + + scols_free_iter(itr); +} static void __attribute__((__noreturn__)) usage(void) { @@ -211,6 +215,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(" -w, --width <num> hardcode terminal width\n", out); fputs(" -p, --tree-parent-column <n> parent column\n", out); fputs(" -i, --tree-id-column <n> id column\n", out); + fputs(" -Q, --filter <expr> filter\n", out); fputs(" -h, --help this help\n", out); fputs("\n", out); @@ -220,8 +225,11 @@ static void __attribute__((__noreturn__)) usage(void) int main(int argc, char *argv[]) { struct libscols_table *tb; - int c, n, nlines = 0; + int c, n, nlines = 0, rc; int parent_col = -1, id_col = -1; + int fltr_dump = 0; + const char *fltr_str = NULL; + struct libscols_filter *fltr = NULL; static const struct option longopts[] = { { "maxout", 0, NULL, 'm' }, @@ -235,6 +243,8 @@ int main(int argc, char *argv[]) { "raw", 0, NULL, 'r' }, { "export", 0, NULL, 'E' }, { "colsep", 1, NULL, 'C' }, + { "filter", 1, NULL, 'Q' }, + { "filter-dump", 0, NULL, 'd' }, { "help", 0, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; @@ -253,25 +263,23 @@ int main(int argc, char *argv[]) if (!tb) err(EXIT_FAILURE, "failed to create output table"); - while((c = getopt_long(argc, argv, "hCc:Ei:JMmn:p:rw:", longopts, NULL)) != -1) { + while((c = getopt_long(argc, argv, "hCc:dEi:JMmn:p:Q:rw:", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch(c) { case 'c': /* add column from file */ { - struct libscols_column *cl; - FILE *f = fopen(optarg, "r"); + struct libscols_column *cl = parse_column(optarg); - if (!f) - err(EXIT_FAILURE, "%s: open failed", optarg); - cl = parse_column(f); if (cl && scols_table_add_column(tb, cl)) err(EXIT_FAILURE, "%s: failed to add column", optarg); scols_unref_column(cl); - fclose(f); break; } + case 'd': + fltr_dump = 1; + break; case 'p': parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column"); break; @@ -300,6 +308,9 @@ int main(int argc, char *argv[]) case 'n': nlines = strtou32_or_err(optarg, "failed to parse number of lines"); break; + case 'Q': + fltr_str = optarg; + break; case 'w': scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS); scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width")); @@ -323,6 +334,14 @@ int main(int argc, char *argv[]) scols_unref_line(ln); } + if (fltr_str) { + fltr = init_filter(tb, fltr_str, fltr_dump); + if (!fltr) { + rc = EXIT_FAILURE; + goto done; + } + } + n = 0; while (optind < argc) { @@ -341,7 +360,13 @@ int main(int argc, char *argv[]) scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + if (fltr) + apply_filter(tb, fltr); + scols_print_table(tb); + rc = EXIT_SUCCESS; +done: + scols_unref_filter(fltr); scols_unref_table(tb); - return EXIT_SUCCESS; + return rc; } diff --git a/libsmartcols/samples/maxout.c b/libsmartcols/samples/maxout.c index 20c6424..a867697 100644 --- a/libsmartcols/samples/maxout.c +++ b/libsmartcols/samples/maxout.c @@ -19,7 +19,7 @@ enum { COL_LEFT, COL_FOO, COL_RIGHT }; -int main(int argc, char *argv[]) +int main(void) { struct libscols_table *tb; int rc = -1, nlines = 3; diff --git a/libsmartcols/samples/wrap.c b/libsmartcols/samples/wrap.c index 795bef7..8368304 100644 --- a/libsmartcols/samples/wrap.c +++ b/libsmartcols/samples/wrap.c @@ -92,7 +92,15 @@ int main(int argc, char *argv[]) if (!tb) err(EXIT_FAILURE, "failed to create output table"); - scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + if (argc > 1 && strcmp(argv[1], "--export") == 0) + scols_table_enable_export(tb, 1); + else if (argc > 1 && strcmp(argv[1], "--raw") == 0) + scols_table_enable_raw(tb, 1); + else if (argc > 1 && strcmp(argv[1], "--json") == 0) + scols_table_enable_json(tb, 1); + else + scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + setup_columns(tb); ln = add_line(tb, NULL, "A"); diff --git a/libsmartcols/scols-filter.5 b/libsmartcols/scols-filter.5 new file mode 100644 index 0000000..b7cb826 --- /dev/null +++ b/libsmartcols/scols-filter.5 @@ -0,0 +1,137 @@ +'\" t +.\" Title: scols-filter +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: File formats and conventions +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "SCOLS\-FILTER" "5" "2024-03-20" "util\-linux 2.40" "File formats and conventions" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +scols-filter \- syntax for libsmartcols filter expressions +.SH "SYNTAX" +.sp +.if n .RS 4 +.nf +.fam C +expr: param + | ( expr ) + | expr && expr | expr AND expr + | expr || expr | expr OR expr + | !expr | NOT expr + | expr == expr | expr EQ expr + | expr != expr | expr NE expr + | expr >= expr | expr GE expr + | expr <= expr | expr LE expr + | expr > expr | expr GT expr + | expr < expr | expr LT expr + | expr =~ string + | expr !~ string + +param: integer + | float + | string + | boolean + | holder + +integer: [0\-9]* + +float: integer.integer + +boolean: "true" | "false" | "TRUE" | "FALSE" + +string: "[^\(rsn\(rs"]*" | \*(Aq[^\(rsn\(rs\*(Aq]*\*(Aq + +holder: [a\-zA\-Z][a\-zA\-Z_.%:/\(rs\-0\-9]* +.fam +.fi +.if n .RE +.SH "DESCRIPTION" +.sp +The filter expression can be used by application linked with libsmartcols to filter +output data. The application can use the filter before it gathers all data for the +output to reduce resources and improve performance. This makes scols filter more +effective than grep(1) on the complete output. For example +.sp +.if n .RS 4 +.nf +.fam C + lsblk \-\-output NAME,LABEL,FSTYPE \-\-filter \*(AqNAME=="sda1"\*(Aq +.fam +.fi +.if n .RE +.sp +helps lsblk(1) to not read LABELs for all block device from udevd or libblkid, +but read it only for device sda1. +.sp +The filter can be also used for columns which are not used in the output. +.SH "SYNTAX NOTES" +.sp +An expression consists of holders, params, and operators. +.sp +The currently supported \f(CRholder\fP type is column name only. The name has to be +used without quotes. Before evaluation, application map column names in the +given expression to the output table columns and assign column data type to the +holder. The default type is "string". +.sp +The \f(CRparam\fP is for representing a value directly. The currenly supported data +types are integer, float, string and boolean. +.sp +An operator works with one or two operand(s). An operator has an expectation +about the data type(s) of its operands. Giving an unexpected data type to an +operator causes a syntax error. The library can cast between data types, the +prefferred is always the type as specified by \f(CRparam\fP and in case of expression with +number and float the preferred is the float. +.sp +Operators taking two operands are \f(CRand\fP, \f(CRor\fP, \f(CReq\fP, \f(CRne\fP, \f(CRle\fP, \f(CRlt\fP, \f(CRge\fP, \f(CRgt\fP, \f(CR=~\fP, \f(CR!~\fP. +Alphabetically named operators have C\-language +flavored aliases: \f(CR&&\fP, \f(CR||\fP, \f(CR==\fP, \f(CR!=\fP, \f(CR<\fP, \f(CR\(lA\fP, \f(CR>=\fP, and \f(CR>\fP. +.sp +\f(CR!\fP is the only operator that takes one operand. If no operator is specified then +expression is true if param or holder are not empty. For example \f(CR\-\-filter NAME\fP will +return lines where column NAME is not empty. +.sp +\f(CR=~\fP and \f(CR!~\fP is for regular expression matching; if a string at the right side +matches (or not matches for \f(CR!~\fP a regular expression at the left side, the result +is true. The right side operand must be a string literal. +.sp +The precedences within operators is \f(CRor\fP, \f(CRand\fP, and \f(CReq\fP, \f(CRne\fP, \f(CRle\fP, \f(CRgt\fP, \f(CRge\fP, \f(CR=~\fP, \f(CR!~\fP, \f(CRnot\fP. +.SH "LIMITATIONS" +.sp +About \f(CRfloat\fP and \f(CRinteger\fP typed values, the filter engine supports only +non\-negative numbers. The \f(CRinteger\fP is unsigned 64\-bit number, and \f(CRfloat\fP is +long double. +.SH "AUTHORS" +.sp +.MTO "kzak\(atredhat.com" "Karel Zak" "" +.sp +Based on original implementation from \c +.MTO "yamato\(atredhat.com" "Masatake YAMATO" "." +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBlibsmartcols\fP library is part of the util\-linux package since version 2.25. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/libsmartcols/scols-filter.5.adoc b/libsmartcols/scols-filter.5.adoc new file mode 100644 index 0000000..ec1d95c --- /dev/null +++ b/libsmartcols/scols-filter.5.adoc @@ -0,0 +1,121 @@ +//po4a: entry man manual +//// +Copyright (C) 2023 Karel Zak <kzak@redhat.com> + +This file may be copied under the terms of the GNU Public License. +//// += scols-filter(5) +:doctype: manpage +:man manual: File formats and conventions +:man source: util-linux {release-version} +:page-layout: base +:lib: libsmartcols +:firstversion: 2.25 +:colon: : + +== NAME + +scols-filter - syntax for libsmartcols filter expressions + +== SYNTAX +// Simplified https://en.wikipedia.org/wiki/Wirth_syntax_notation +// TRANSLATORS: don't translate the expressions syntax, please. +[verse] +---- +expr: param + | ( expr ) + | expr && expr | expr AND expr + | expr || expr | expr OR expr + | !expr | NOT expr + | expr == expr | expr EQ expr + | expr != expr | expr NE expr + | expr >= expr | expr GE expr + | expr <= expr | expr LE expr + | expr > expr | expr GT expr + | expr < expr | expr LT expr + | expr =~ string + | expr !~ string + +param: integer + | float + | string + | boolean + | holder + +integer: [0-9]* + +float: integer.integer + +boolean: "true" | "false" | "TRUE" | "FALSE" + +string: "[^\n\"]*" | '[^\n\']*' + +holder: [a-zA-Z][a-zA-Z_.%:/\-0-9]* +---- + +== DESCRIPTION + +The filter expression can be used by application linked with libsmartcols to filter +output data. The application can use the filter before it gathers all data for the +output to reduce resources and improve performance. This makes scols filter more +effective than grep(1) on the complete output. For example +.... + lsblk --output NAME,LABEL,FSTYPE --filter 'NAME=="sda1"' +.... +helps lsblk(1) to not read LABELs for all block device from udevd or libblkid, +but read it only for device sda1. + +The filter can be also used for columns which are not used in the output. + +== SYNTAX NOTES + +An expression consists of holders, params, and operators. + +The currently supported `holder` type is column name only. The name has to be +used without quotes. Before evaluation, application map column names in the +given expression to the output table columns and assign column data type to the +holder. The default type is "string". + +The `param` is for representing a value directly. The currenly supported data +types are integer, float, string and boolean. + +An operator works with one or two operand(s). An operator has an expectation +about the data type(s) of its operands. Giving an unexpected data type to an +operator causes a syntax error. The library can cast between data types, the +prefferred is always the type as specified by `param` and in case of expression with +number and float the preferred is the float. + +Operators taking two operands are `and`, `or`, `eq`, `ne`, `le`, `lt`, `ge`, `gt`, `=~`, `!~`. +Alphabetically named operators have C-language +flavored aliases: `&&`, `||`, `==`, `!=`, `<`, `<=`, `>=`, and `>`. + +`!` is the only operator that takes one operand. If no operator is specified then +expression is true if param or holder are not empty. For example `--filter NAME` will +return lines where column NAME is not empty. + +`=~` and `!~` is for regular expression matching; if a string at the right side +matches (or not matches for `!~` a regular expression at the left side, the result +is true. The right side operand must be a string literal. + +The precedences within operators is `or`, `and`, and `eq`, `ne`, `le`, `gt`, `ge`, `=~`, `!~`, `not`. + +== LIMITATIONS + +About `float` and `integer` typed values, the filter engine supports only +non-negative numbers. The `integer` is unsigned 64-bit number, and `float` is +long double. + + +== AUTHORS + +mailto:kzak@redhat.com[Karel Zak] + +Based on original implementation from mailto:yamato@redhat.com[Masatake YAMATO]. + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am index 2bb19fd..377888e 100644 --- a/libsmartcols/src/Makemodule.am +++ b/libsmartcols/src/Makemodule.am @@ -1,5 +1,4 @@ - # smartcols.h is generated, so it's stored in builddir! smartcolsincdir = $(includedir)/libsmartcols nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h @@ -21,7 +20,68 @@ libsmartcols_la_SOURCES= \ libsmartcols/src/calculate.c \ libsmartcols/src/grouping.c \ libsmartcols/src/walk.c \ - libsmartcols/src/init.c + libsmartcols/src/init.c \ + \ + libsmartcols/src/filter-parser.c \ + libsmartcols/src/filter-scanner.c \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-scanner.h \ + \ + libsmartcols/src/filter.c \ + libsmartcols/src/filter-param.c \ + libsmartcols/src/filter-expr.c + +BUILT_SOURCES += libsmartcols/src/filter-parser.c \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-scanner.c \ + libsmartcols/src/filter-scanner.h + +EXTRA_DIST += libsmartcols/src/filter-parser.y \ + libsmartcols/src/filter-scanner.l + +## Generate filter parser (YACC) and tokenizer (LEX) .c and .h files. +# +# Note that we need advanced bison and flex features and configuration +# directives to generated thread safe parser (usable in stared library), so +# it's probably a bad idea to use "-y" (posix compatible) as default in +# autotools. We also need to generate .c and .h files in the same time to avoid +# duplicate stuff. +# +SCOLS_YACC_BASENAME = libsmartcols/src/filter-parser +SCOLS_YACC_STEMP = $(SCOLS_YACC_BASENAME).stamp + +SCOLS_LEX_BASENAME = libsmartcols/src/filter-scanner +SCOLS_LEX_STEMP = $(SCOLS_LEX_BASENAME).stamp + + +$(SCOLS_YACC_STEMP): $(SCOLS_YACC_BASENAME).y + @rm -f $(SCOLS_YACC_STEMP).tmp + @touch -f $(SCOLS_YACC_STEMP).tmp + $(AM_V_YACC) $(BISON) $< --output=${basename $@}.c --defines=${basename $@}.h + @mv -f $(SCOLS_YACC_STEMP).tmp $@ + +$(SCOLS_YACC_BASENAME).c $(SCOLS_YACC_BASENAME).h: $(SCOLS_YACC_STEMP) + @test -f $@ || rm -f $(SCOLS_YACC_STEMP) + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $(SCOLS_YACC_STEMP) + + +$(SCOLS_LEX_STEMP): $(SCOLS_LEX_BASENAME).l + @rm -f $(SCOLS_LEX_STEMP).tmp + @touch -f $(SCOLS_LEX_STEMP).tmp + $(AM_V_GEN) $(FLEX) --header-file=${basename $@}.h --outfile=${basename $@}.c $< + @mv -f $(SCOLS_LEX_STEMP).tmp $@ + +$(SCOLS_LEX_BASENAME).c $(SCOLS_LEX_BASENAME).h: $(SCOLS_LEX_STEMP) + @test -f $@ || rm -f $(SCOLS_LEX_STEMP) + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $(SCOLS_LEX_STEMP) + +# Each part of the parser depends on the header files +$(SCOLS_LEX_BASENAME).c: $(SCOLS_YACC_BASENAME).h +$(SCOLS_YACC_BASENAME).c: $(SCOLS_LEX_BASENAME).h + +# Don't re-generate parser when use sources from tarball +EXTRA_DIST += $(SCOLS_YACC_STEMP) $(SCOLS_LEX_STEMP) + libsmartcols_la_LIBADD = $(LDADD) libcommon.la diff --git a/libsmartcols/src/calculate.c b/libsmartcols/src/calculate.c index ad0b15d..84198da 100644 --- a/libsmartcols/src/calculate.c +++ b/libsmartcols/src/calculate.c @@ -12,19 +12,19 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) st = &cl->wstat; - DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, " - "hint=%d, max=%zu, min=%zu, " + DBG(COL, ul_debugobj(cl, "#%zu %12s: width=%zd " + "hint=%d max=%zu min=%zu " "0x04%x [%s%s%s]", - cl->header.data, cl->seqnum, cl->width, + cl->seqnum, cl->header.data, cl->width, cl->width_hint >= 1.0 ? (int) cl->width_hint : (int) (cl->width_hint * tb->termwidth), st->width_max, st->width_min, cl->flags, - cl->flags & SCOLS_FL_TRUNC ? "trunc" : "", - scols_column_is_right(cl) ? " right" : "", - scols_column_is_noextremes(cl) ? " noextrem" : "")); + cl->flags & SCOLS_FL_TRUNC ? "trunc " : "", + scols_column_is_right(cl) ? "right " : "", + scols_column_is_noextremes(cl) ? "noextrem " : "")); } static void dbg_columns(struct libscols_table *tb) @@ -42,41 +42,38 @@ static int count_cell_width(struct libscols_table *tb, struct libscols_column *cl, struct ul_buffer *buf) { - size_t len; - char *data; - int rc; + size_t len = 0; + int rc = 0; struct libscols_cell *ce; - struct libscols_wstat *st; + char *data; - rc = __cell_to_buffer(tb, ln, cl, buf); + ce = scols_line_get_cell(ln, cl->seqnum); + scols_table_set_cursor(tb, ln, cl, ce); + + rc = __cursor_to_buffer(tb, buf, 1); if (rc) - return rc; + goto done; data = ul_buffer_get_data(buf, NULL, NULL); - if (!data) - len = 0; - else if (scols_column_is_customwrap(cl)) - len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data); - else if (scols_table_is_noencoding(tb)) - len = mbs_width(data); - else - len = mbs_safe_width(data); - - if (len == (size_t) -1) /* ignore broken multibyte strings */ - len = 0; - - ce = scols_line_get_cell(ln, cl->seqnum); - ce->width = len; + if (data) { + len = scols_table_is_noencoding(tb) ? + mbs_width(data) : + mbs_safe_width(data); - st = &cl->wstat; - st->width_max = max(len, st->width_max); + if (len == (size_t) -1) /* ignore broken multibyte strings */ + len = 0; + } if (scols_column_is_tree(cl)) { size_t treewidth = ul_buffer_get_safe_pointer_width(buf, SCOLS_BUFPTR_TREEEND); cl->width_treeart = max(cl->width_treeart, treewidth); } - return 0; + ce->width = len; + cl->wstat.width_max = max(len, cl->wstat.width_max); +done: + scols_table_reset_cursor(tb); + return rc; } static int walk_count_cell_width(struct libscols_table *tb, @@ -121,7 +118,7 @@ static void count_column_deviation(struct libscols_table *tb, struct libscols_co } if (n) - st->width_avg = sum / n; + st->width_avg = (double) sum / (double) n; /* count deviation */ if (n > 1) { @@ -136,7 +133,7 @@ static void count_column_deviation(struct libscols_table *tb, struct libscols_co st->width_sqr_sum += diff * diff; /* aka pow(x, 2) */ } - variance = st->width_sqr_sum / (n - 1); + variance = st->width_sqr_sum / (double) (n - 1); st->width_deviation = sqrtroot(variance); } @@ -175,8 +172,9 @@ static int count_column_width(struct libscols_table *tb, } /* set minimal width according to header width */ - data = scols_cell_get_data(&cl->header); - if (data) { + if (!scols_table_is_noheadings(tb) && + (data = scols_cell_get_data(&cl->header))) { + size_t len = scols_table_is_noencoding(tb) ? mbs_width(data) : mbs_safe_width(data); @@ -242,8 +240,8 @@ static int cmp_deviation(struct list_head *a, struct list_head *b, struct libscols_column *ca = list_entry(a, struct libscols_column, cl_columns); struct libscols_column *cb = list_entry(b, struct libscols_column, cl_columns); - double xa = ca->wstat.width_avg + (3*ca->wstat.width_deviation); - double xb = cb->wstat.width_avg + (3*cb->wstat.width_deviation); + double xa = ca->wstat.width_avg + (3.0 * ca->wstat.width_deviation); + double xb = cb->wstat.width_avg + (3.0 * cb->wstat.width_deviation); return cmp_numbers(xa, xb); } @@ -282,9 +280,15 @@ static void reduce_to_68(struct libscols_column *cl, size_t wanted) if (st->width_deviation < 1.0) return; - new = st->width_avg + st->width_deviation; + new = (size_t) (st->width_avg + st->width_deviation); + if (new < st->width_min) new = st->width_min; + else if (new > st->width_max) + new = st->width_max; + + if (new >= cl->width) + return; if (cl->width - new > wanted) cl->width -= wanted; @@ -509,6 +513,8 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) /* enlarge */ if (width < tb->termwidth) { + DBG(TAB, ul_debugobj(tb, " enlarge (extreme, avalable %zu)", + tb->termwidth - width)); if (ignore_extremes) { if (!sorted) { sort_columns(tb, cmp_deviation); @@ -523,14 +529,17 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) continue; if (cl->wstat.width_min == 0 && cl->width == 0) continue; + if (cl->width >= cl->wstat.width_max) + continue; add = tb->termwidth - width; - if (add && cl->wstat.width_max && - cl->width + add > cl->wstat.width_max) + if (add && cl->wstat.width_max + && cl->width + add > cl->wstat.width_max) add = cl->wstat.width_max - cl->width; + if (!add) continue; - DBG(TAB, ul_debugobj(tb, " add +%zd (extreme %s)", + DBG(COL, ul_debugobj(cl, " add +%zd (%s)", add, cl->header.data)); cl->width += add; width += add; @@ -541,7 +550,8 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } if (width < tb->termwidth && scols_table_is_maxout(tb)) { - DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)")); + DBG(TAB, ul_debugobj(tb, " enlarge (max-out, avalable %zu)", + tb->termwidth - width)); /* try enlarging all columns */ while (width < tb->termwidth) { @@ -549,7 +559,7 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) while (scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - DBG(TAB, ul_debugobj(tb, " enlarge (max-out %s)", + DBG(COL, ul_debugobj(cl, " add +1 (%s)", cl->header.data)); cl->width++; width++; @@ -559,9 +569,13 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } } else if (width < tb->termwidth) { /* enlarge the last column */ - DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); + DBG(TAB, ul_debugobj(tb, " enlarge (last column, avalable %zu)", + tb->termwidth - width)); if (!scols_column_is_right(last_cl)) { + DBG(COL, ul_debugobj(last_cl, " add +%zu (%s)", + tb->termwidth - width, + last_cl->header.data)); last_cl->width += tb->termwidth - width; width = tb->termwidth; } diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c index 5b83123..df45667 100644 --- a/libsmartcols/src/cell.c +++ b/libsmartcols/src/cell.c @@ -56,24 +56,32 @@ int scols_reset_cell(struct libscols_cell *ce) * @ce: a pointer to a struct libscols_cell instance * @data: data (used for scols_print_table()) * - * Stores a copy of the @str in @ce, the old data are deallocated by free(). + * Stores a copy of the @data in @ce, the old data are deallocated by free(). * * Returns: 0, a negative value in case of an error. */ int scols_cell_set_data(struct libscols_cell *ce, const char *data) { - return strdup_to_struct_member(ce, data, data); + int rc; + + if (!ce) + return -EINVAL; + + ce->is_filled = 1; + rc = strdup_to_struct_member(ce, data, data); + ce->datasiz = ce->data && *ce->data ? strlen(ce->data) + 1: 0; + return rc; } /** * scols_cell_refer_data: * @ce: a pointer to a struct libscols_cell instance - * @data: data (used for scols_print_table()) + * @data: string (used for scols_print_table()) * - * Adds a reference to @str to @ce. The pointer is deallocated by - * scols_reset_cell() or scols_unref_line(). This function is mostly designed - * for situations when the data for the cell are already composed in allocated - * memory (e.g. asprintf()) to avoid extra unnecessary strdup(). + * Adds a reference to @data to @ce. The pointer is deallocated by + * scols_reset_cell() or scols_unref_line() by free(). This function is mostly + * designed for situations when the data for the cell are already composed in + * allocated memory (e.g. asprintf()) to avoid extra unnecessary strdup(). * * Returns: 0, a negative value in case of an error. */ @@ -83,10 +91,52 @@ int scols_cell_refer_data(struct libscols_cell *ce, char *data) return -EINVAL; free(ce->data); ce->data = data; + ce->datasiz = ce->data && *ce->data ? strlen(ce->data) + 1: 0; + ce->is_filled = 1; return 0; } /** + * scols_cell_refer_memory: + * @ce: a pointer to a struct libscols_cell instance + * @data: data + * @datasiz: size of the data + * + * Same like scols_cell_refer_data, but @data does not have to be zero terminated. + * The pointer is deallocated by scols_reset_cell() or scols_unref_line() by free(). + * + * The column (for the cell) has to define wrap function which converts the + * data to zero terminated string, otherwise library will work with the data as + * with string! + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_cell_refer_memory(struct libscols_cell *ce, char *data, size_t datasiz) +{ + if (!ce) + return -EINVAL; + free(ce->data); + ce->data = data; + ce->datasiz = datasiz; + return 0; +} + +/** + * scols_cell_get_datasiz: + * @ce: a pointer to a struct libscols_cell instance + * + * Returns: the current set data size. + * + * Since: 2.40 + */ +size_t scols_cell_get_datasiz(struct libscols_cell *ce) +{ + return ce ? ce->datasiz : 0; +} + +/** * scols_cell_get_data: * @ce: a pointer to a struct libscols_cell instance * @@ -120,7 +170,7 @@ int scols_cell_set_userdata(struct libscols_cell *ce, void *data) */ void *scols_cell_get_userdata(struct libscols_cell *ce) { - return ce->userdata; + return ce ? ce->userdata : NULL; } /** @@ -166,6 +216,9 @@ int scols_cmpstr_cells(struct libscols_cell *a, */ int scols_cell_set_color(struct libscols_cell *ce, const char *color) { + if (!ce) + return -EINVAL; + if (color && !color_is_sequence(color)) { char *seq = color_get_sequence(color); if (!seq) @@ -185,6 +238,9 @@ int scols_cell_set_color(struct libscols_cell *ce, const char *color) */ const char *scols_cell_get_color(const struct libscols_cell *ce) { + if (!ce) + return NULL; + return ce->color; } @@ -214,7 +270,7 @@ int scols_cell_set_flags(struct libscols_cell *ce, int flags) */ int scols_cell_get_flags(const struct libscols_cell *ce) { - return ce->flags; + return ce ? ce->flags : 0; } /** @@ -227,9 +283,11 @@ int scols_cell_get_flags(const struct libscols_cell *ce) */ int scols_cell_get_alignment(const struct libscols_cell *ce) { - if (ce->flags & SCOLS_CELL_FL_RIGHT) + int flags = scols_cell_get_flags(ce); + + if (flags & SCOLS_CELL_FL_RIGHT) return SCOLS_CELL_FL_RIGHT; - if (ce->flags & SCOLS_CELL_FL_CENTER) + if (flags & SCOLS_CELL_FL_CENTER) return SCOLS_CELL_FL_CENTER; return SCOLS_CELL_FL_LEFT; /* default */ @@ -240,7 +298,7 @@ int scols_cell_get_alignment(const struct libscols_cell *ce) * @dest: a pointer to a struct libscols_cell instance * @src: a pointer to an immutable struct libscols_cell instance * - * Copy the contents of @src into @dest. + * Copy the contents (data, usewrdata, colors) of @src into @dest. * * Returns: 0, a negative value in case of an error. */ @@ -248,8 +306,19 @@ int scols_cell_copy_content(struct libscols_cell *dest, const struct libscols_cell *src) { int rc; + char *data = NULL; + + if (!dest || !src) + return -EINVAL; + + if (src->datasiz) { + data = malloc(src->datasiz); + if (!data) + return -ENOMEM; + memcpy(data, src->data, src->datasiz); + } - rc = scols_cell_set_data(dest, scols_cell_get_data(src)); + rc = scols_cell_refer_memory(dest, data, src->datasiz); if (!rc) rc = scols_cell_set_color(dest, scols_cell_get_color(src)); if (!rc) diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c index db4c357..5700bac 100644 --- a/libsmartcols/src/column.c +++ b/libsmartcols/src/column.c @@ -73,7 +73,7 @@ void scols_unref_column(struct libscols_column *cl) scols_reset_cell(&cl->header); free(cl->color); free(cl->safechars); - free(cl->pending_data_buf); + free(cl->wrap_data); free(cl->shellvar); free(cl); } @@ -211,6 +211,42 @@ int scols_column_get_json_type(const struct libscols_column *cl) /** + * scols_column_set_data_type: + * @cl: a pointer to a struct libscols_column instance + * @type: SCOLS_DATA_* + * + * The table always keep data in strings in form that is printed on output, but + * for some internal operations (like filters or counters) it needs to convert + * the strings to usable data format. This data format is possible to specify, + * by this function. If the format is not specified then filter and counters + * try to use SCOLS_JSON_* types, if also not define than defaults to string. + * + * If a simple string conversion is not possible then application (which want + * to use filters and counters) needs to define data function to do the + * conversion. See scols_column_set_data_func(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_set_data_type(struct libscols_column *cl, int type) +{ + return cl->data_type = type; +} + +/** + * scols_column_get_data_type: + * @cl: a pointer to a struct libscols_column instance + * + * Returns: The current datatype setting of the column @cl. + * + * Since: 2.40 + */ +int scols_column_get_data_type(const struct libscols_column *cl) +{ + return cl->data_type; +} +/** * scols_column_get_table: * @cl: a pointer to a struct libscols_column instance * @@ -280,6 +316,59 @@ const char *scols_column_get_name(struct libscols_column *cl) } /** + * scols_shellvar_name: + * @name: raw (column) name + * @buf: buffer to returns normalized name + * @bufsz: size of the buffer + * + * Converts @name to a name compatible with shell. The buffer is reallocated if + * not large enough. + * + * Returns: 0 in case of conversion, 1 if conversion unnecessary, <0 on error. + * + * Since: 2.40 + */ +int scols_shellvar_name(const char *name, char **buf, size_t *bufsz) +{ + char *p; + const char *s; + size_t sz; + + if (!name || !*name || !buf || !bufsz) + return -EINVAL; + + /* size to convert "1FOO%" --> "_1FOO_PCT */ + sz = strlen(name) + 1 + 3; + if (sz + 1 > *bufsz) { + char *tmp; + + *bufsz = sz + 1; + tmp = realloc(*buf, *bufsz); + if (!tmp) + return -ENOMEM; + *buf = tmp; + } + memset(*buf, 0, *bufsz); + p = *buf; + + /* convert "1FOO" to "_1FOO" */ + if (!isalpha(*name)) + *p++ = '_'; + + /* replace all "bad" chars with "_" */ + for (s = name; *s; s++) + *p++ = !isalnum(*s) ? '_' : *s; + + if (!*s && *(s - 1) == '%') { + *p++ = 'P'; + *p++ = 'C'; + *p++ = 'T'; + } + + return strcmp(name, *buf) == 0; +} + +/** * scols_column_get_name_as_shellvar * @cl: a pointer to a struct libscols_column instance * @@ -291,32 +380,13 @@ const char *scols_column_get_name(struct libscols_column *cl) const char *scols_column_get_name_as_shellvar(struct libscols_column *cl) { if (!cl->shellvar) { - const char *s, *name = scols_column_get_name(cl); - char *p; - size_t sz; + const char *name = scols_column_get_name(cl); + size_t sz = 0; if (!name || !*name) return NULL; - - /* "1FOO%" --> "_1FOO_PCT */ - sz = strlen(name) + 1 + 3; - p = cl->shellvar = calloc(1, sz + 1); - if (!cl->shellvar) + if (scols_shellvar_name(name, &cl->shellvar, &sz) < 0) return NULL; - - /* convert "1FOO" to "_1FOO" */ - if (!isalpha(*name)) - *p++ = '_'; - - /* replace all "bad" chars with "_" */ - for (s = name; *s; s++) - *p++ = !isalnum(*s) ? '_' : *s; - - if (!*s && *(s - 1) == '%') { - *p++ = 'P'; - *p++ = 'C'; - *p++ = 'T'; - } } return cl->shellvar; } @@ -361,6 +431,7 @@ const char *scols_column_get_color(const struct libscols_column *cl) return cl->color; } + /** * scols_wrapnl_nextchunk: * @cl: a pointer to a struct libscols_column instance @@ -391,6 +462,37 @@ char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unu } /** + * scols_wrapzero_nextchunk: + * @cl: a pointer to a struct libscols_column instance + * @data: string + * @userdata: callback private data + * + * This is built-in function for scols_column_set_wrapfunc(). This function + * walk string separated by \0. + * + * For example for data "AAA\0BBB\0CCC" the next chunk is "BBB". + * + * Returns: next chunk + * + * Since: 2.40 + */ +char *scols_wrapzero_nextchunk(const struct libscols_column *cl, + char *data, + void *userdata __attribute__((unused))) +{ + char *start = NULL; + size_t sz = 0; + + if (!data) + return NULL; + scols_column_get_wrap_data(cl, &start, &sz, NULL, NULL); + if (!start || !sz) + return NULL; + return ul_next_string(data, start + sz); +} + + +/** * scols_wrapnl_chunksize: * @cl: a pointer to a struct libscols_column instance * @data: string @@ -402,6 +504,8 @@ char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unu * Note that the size has to be based on number of terminal cells rather than * bytes to support multu-byte output. * + * Deprecated since 2.40. + * * Returns: size of the largest chunk. * * Since: 2.29 @@ -459,7 +563,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, /** * scols_column_set_wrapfunc: * @cl: a pointer to a struct libscols_column instance - * @wrap_chunksize: function to return size of the largest chink of data + * @wrap_chunksize: function to return size of the largest chink of data (deprecated) * @wrap_nextchunk: function to return next zero terminated data * @userdata: optional stuff for callbacks * @@ -467,6 +571,13 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, * is to wrap by column size, but you can create functions to wrap for example * after \n or after words, etc. * + * Note that since 2.40 the @wrap_chunksize is unnecessary. The library calculates + * the size itself. + * + * The wrap functions do not work directly with cell data, but with buffer used + * by library to compose output data. The wrap_nextchunk() function can access + * additional details about wrap data by scols_column_get_wrap_data(). + * * Returns: 0, a negative value in case of an error. * * Since: 2.29 @@ -474,7 +585,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, int scols_column_set_wrapfunc(struct libscols_column *cl, size_t (*wrap_chunksize)(const struct libscols_column *, const char *, - void *), + void *) __attribute__((__unused__)), char * (*wrap_nextchunk)(const struct libscols_column *, char *, void *), @@ -484,12 +595,88 @@ int scols_column_set_wrapfunc(struct libscols_column *cl, return -EINVAL; cl->wrap_nextchunk = wrap_nextchunk; - cl->wrap_chunksize = wrap_chunksize; cl->wrapfunc_data = userdata; return 0; } /** + * scols_column_get_wrap_data: + * @cl: column + * @data: return wrap data + * @datasiz: return wrap buffer size + * @cur: the current pozition in the buffer + * @next: the next pozition + * + * This function returns the current status of wrapping cell data (for multi-line cells). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_get_wrap_data(const struct libscols_column *cl, + char **data, size_t *datasiz, char **cur, char **next) +{ + if (!cl) + return -EINVAL; + if (data) + *data = cl->wrap_data; + if (datasiz) + *datasiz = cl->wrap_datasz; + if (cur) + *cur = cl->wrap_cur; + if (next) + *next = cl->wrap_next; + return 0; +} + +/* + * scols_column_set_data_func: + * @cl: a pointer to a struct libscols_column instance + * @datafunc: function to return data + * @userdata: optional stuff for callbacks + * + * The table always keep data in strings in form that is printed on output, but + * for some internal operations (like filters or counters) it needs to convert + * the strings to usable data format. If this converion is not possible then + * application can define datafunc() callback to provide data for filters and counters. + + * The callback needs to return the data as pointer to void, and the data type + * is defined by scols_column_set_data_type(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_set_data_func(struct libscols_column *cl, + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *), + void *userdata) +{ + if (!cl) + return -EINVAL; + + cl->datafunc = datafunc; + cl->datafunc_data = userdata; + return 0; +} + +/** + * scols_column_has_data_func: + * @cl: a pointer to a struct libscols_column instance + * + * See scols_column_set_data_func() for more details. + * + * Returns: 1 if data function defined, or 0 + * + * Since: 2.40 + */ +int scols_column_has_data_func(struct libscols_column *cl) +{ + return cl && cl->datafunc != NULL ? 1 : 0; +} + +/** * scols_column_set_safechars: * @cl: a pointer to a struct libscols_column instance * @safe: safe characters (e.g. "\n\t") @@ -639,7 +826,6 @@ int scols_column_is_wrap(const struct libscols_column *cl) int scols_column_is_customwrap(const struct libscols_column *cl) { return (cl->flags & SCOLS_FL_WRAP) - && cl->wrap_chunksize && cl->wrap_nextchunk ? 1 : 0; } @@ -681,7 +867,7 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) flags |= SCOLS_FL_STRICTWIDTH; else if (strncmp(name, "noextremes", namesz) == 0) - flags |= SCOLS_FL_STRICTWIDTH; + flags |= SCOLS_FL_NOEXTREMES; else if (strncmp(name, "hidden", namesz) == 0) flags |= SCOLS_FL_HIDDEN; @@ -689,12 +875,29 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) else if (strncmp(name, "wrap", namesz) == 0) flags |= SCOLS_FL_WRAP; - else if (value && strncmp(name, "json", namesz) == 0) { + else if (strncmp(name, "wrapnl", namesz) == 0) { + flags |= SCOLS_FL_WRAP; + scols_column_set_wrapfunc(cl, + NULL, + scols_wrapnl_nextchunk, + NULL); + scols_column_set_safechars(cl, "\n"); + + } else if (strncmp(name, "wrapzero", namesz) == 0) { + flags |= SCOLS_FL_WRAP; + scols_column_set_wrapfunc(cl, + NULL, + scols_wrapzero_nextchunk, + NULL); + + } else if (value && strncmp(name, "json", namesz) == 0) { if (strncmp(value, "string", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_STRING); else if (strncmp(value, "number", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); + else if (strncmp(value, "float", valuesz) == 0) + rc = scols_column_set_json_type(cl, SCOLS_JSON_FLOAT); else if (strncmp(value, "array-string", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING); else if (strncmp(value, "array-number", valuesz) == 0) @@ -706,9 +909,8 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) char *end = NULL; double x = strtod(value, &end); - if (errno || str == end) + if (errno || value == end) return -EINVAL; - rc = scols_column_set_whint(cl, x); } else if (value && strncmp(name, "color", namesz) == 0) { @@ -735,3 +937,122 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) return rc; } +void scols_column_reset_wrap(struct libscols_column *cl) +{ + if (!cl) + return; + + if (cl->wrap_data) + memset(cl->wrap_data, 0, cl->wrap_datamax); + cl->wrap_cell = NULL; + cl->wrap_datasz = 0; + cl->wrap_cur = NULL; + cl->wrap_next = NULL; +} + +static int scols_column_init_wrap( + struct libscols_column *cl, + struct libscols_cell *ce) +{ + const char *data = scols_cell_get_data(ce); + + if (!cl || !ce) + return -EINVAL; + + assert(cl->table->cur_column == cl); + assert(cl->table->cur_cell == ce); + + scols_column_reset_wrap(cl); + + cl->wrap_cell = ce; + if (data) { + void *tmp; + cl->wrap_datasz = scols_cell_get_datasiz(ce); + + if (cl->wrap_datasz > cl->wrap_datamax) { + cl->wrap_datamax = cl->wrap_datasz; + tmp = realloc(cl->wrap_data, cl->wrap_datamax); + if (!tmp) + return -ENOMEM; + cl->wrap_data = tmp; + } + memcpy(cl->wrap_data, data, cl->wrap_datasz); + cl->wrap_cur = cl->wrap_data; + cl->wrap_next = NULL; + } + + return 0; +} + +/* Returns the next chunk of cell data in multi-line cells */ +int scols_column_next_wrap( + struct libscols_column *cl, + struct libscols_cell *ce, + char **data) +{ + if (!cl || !data || (!cl->wrap_cell && !ce)) + return -EINVAL; + + *data = NULL; + + if (ce && cl->wrap_cell != ce) + scols_column_init_wrap(cl, ce); /* init */ + else { + cl->wrap_cur = cl->wrap_next; /* next step */ + cl->wrap_next = NULL; + } + + if (!cl->wrap_cur) + return 1; /* no more data */ + if (scols_column_is_customwrap(cl)) + cl->wrap_next = cl->wrap_nextchunk(cl, cl->wrap_cur, cl->wrapfunc_data); + + *data = cl->wrap_cur; + return 0; +} + +int scols_column_greatest_wrap( + struct libscols_column *cl, + struct libscols_cell *ce, + char **data) +{ + size_t maxsz = 0; + char *res = NULL;; + + if (!scols_column_is_customwrap(cl)) + return scols_column_next_wrap(cl, ce, data); + + while (scols_column_next_wrap(cl, ce, data) == 0) { + size_t sz = strlen(*data); + + maxsz = max(maxsz, sz); + if (maxsz == sz) + res = *data; + } + + *data = res; + return 0; +} + +/* Set the "next" chunk in multi-line cell to offset specified by @bytes. + * Don't use it for columns with custom wrapfunc(). + */ +int scols_column_move_wrap(struct libscols_column *cl, size_t bytes) +{ + size_t x; /* remaining bytes */ + + if (!cl->wrap_cur) + return -EINVAL; /* scols_column_init_wrap() not called */ + + x = cl->wrap_datasz - (cl->wrap_cur - cl->wrap_data); + if (bytes >= x) + cl->wrap_next = NULL; /* done */ + else + cl->wrap_next = cl->wrap_cur + bytes; + return 0; +} + +int scols_column_has_pending_wrap(struct libscols_column *cl) +{ + return cl && cl->wrap_next; +} diff --git a/libsmartcols/src/filter-expr.c b/libsmartcols/src/filter-expr.c new file mode 100644 index 0000000..7e559c4 --- /dev/null +++ b/libsmartcols/src/filter-expr.c @@ -0,0 +1,219 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smartcolsP.h" + +struct filter_expr { + struct filter_node node; + enum filter_etype type; + + struct filter_node *left; + struct filter_node *right; +}; + +struct filter_node *filter_new_expr( + struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_etype type, + struct filter_node *left, + struct filter_node *right) +{ + struct filter_expr *n = (struct filter_expr *) __filter_new_node( + F_NODE_EXPR, sizeof(struct filter_expr)); + + if (!n) + return NULL; + + n->type = type; + + switch (type) { + case F_EXPR_AND: + case F_EXPR_OR: + case F_EXPR_EQ: + case F_EXPR_NE: + case F_EXPR_LE: + case F_EXPR_LT: + case F_EXPR_GE: + case F_EXPR_GT: + case F_EXPR_REG: + case F_EXPR_NREG: + n->left = left; + n->right = right; + break; + case F_EXPR_NEG: + n->right = right; + break; + } + + return (struct filter_node *) n; +} + +void filter_free_expr(struct filter_expr *n) +{ + filter_unref_node(n->left); + filter_unref_node(n->right); + free(n); +} + +static const char *expr_type_as_string(struct filter_expr *n) +{ + switch (n->type) { + case F_EXPR_AND: + return "AND"; + case F_EXPR_OR: + return "OR"; + case F_EXPR_EQ: + return "EQ"; + case F_EXPR_NE: + return "NE"; + case F_EXPR_LE: + return "LE"; + case F_EXPR_LT: + return "LT"; + case F_EXPR_GE: + return "GE"; + case F_EXPR_GT: + return "GT"; + case F_EXPR_REG: + return "REG"; + case F_EXPR_NREG: + return "NREG"; + case F_EXPR_NEG: + return "NOT"; + } + return ""; +} + +void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n) +{ + ul_jsonwrt_object_open(json, "expr"); + ul_jsonwrt_value_s(json, "type", expr_type_as_string(n)); + + if (n->left) + filter_dump_node(json, n->left); + if (n->right) + filter_dump_node(json, n->right); + + ul_jsonwrt_object_close(json); +} + +static int cast_node(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_node *n, + struct filter_param **result) +{ + struct filter_node *pr; + int status = 0, rc; + bool x; + + switch (n->type) { + case F_NODE_EXPR: + /* convert expression to a boolean param */ + rc = filter_eval_expr(fltr, ln, (struct filter_expr *) n, &status); + if (rc) + return rc; + x = status != 0 ? true : false; + pr = filter_new_param(NULL, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + if (!pr) + return -ENOMEM; + rc = filter_cast_param(fltr, ln, type, (struct filter_param *) pr, result); + filter_unref_node(pr); + break; + case F_NODE_PARAM: + rc = filter_cast_param(fltr, ln, type, (struct filter_param *) n, result); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int node_get_datatype(struct filter_node *n) +{ + switch (n->type) { + case F_NODE_EXPR: + return SCOLS_DATA_BOOLEAN; + case F_NODE_PARAM: + return filter_param_get_datatype((struct filter_param *) n); + } + return SCOLS_DATA_NONE; +} + +static int guess_expr_datatype(struct filter_expr *n) +{ + int type; + int l = node_get_datatype(n->left), + r = node_get_datatype(n->right); + + if (l == r) + type = l; + else { + bool l_holder, r_holder; + + /* for expression like "FOO > 5.5" preffer type defined by a real param + * rather than by holder (FOO) */ + l_holder = is_filter_holder_node(n->left); + r_holder = is_filter_holder_node(n->right); + + if (l_holder && !r_holder) + type = r; + else if (r_holder && !l_holder) + type = l; + else + type = l; + + /* Always prefer float before number */ + if (type == SCOLS_DATA_U64 + && (r == SCOLS_DATA_FLOAT || l == SCOLS_DATA_FLOAT)) + type = SCOLS_DATA_FLOAT; + } + + DBG(FPARAM, ul_debugobj(n, " expr datatype: %d", type)); + return type; +} + +int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_expr *n, int *status) +{ + int rc = 0; + struct filter_param *l = NULL, *r = NULL; + enum filter_etype oper = n->type; + int type; + + /* logical operators */ + switch (oper) { + case F_EXPR_AND: + rc = filter_eval_node(fltr, ln, n->left, status); + if (rc == 0 && *status) + rc = filter_eval_node(fltr, ln, n->right, status); + return rc; + case F_EXPR_OR: + rc = filter_eval_node(fltr, ln, n->left, status); + if (rc == 0 && !*status) + rc = filter_eval_node(fltr, ln, n->right, status); + return rc; + case F_EXPR_NEG: + rc = filter_eval_node(fltr, ln, n->right, status); + if (rc == 0) + *status = !*status; + return rc; + default: + break; + } + + type = guess_expr_datatype(n); + + /* compare data */ + rc = cast_node(fltr, ln, type, n->left, &l); + if (!rc) + rc = cast_node(fltr, ln, type, n->right, &r); + if (!rc) + rc = filter_compare_params(fltr, oper, l, r, status); + + filter_unref_node((struct filter_node *) l); + filter_unref_node((struct filter_node *) r); + return rc; +} diff --git a/libsmartcols/src/filter-param.c b/libsmartcols/src/filter-param.c new file mode 100644 index 0000000..f7d243d --- /dev/null +++ b/libsmartcols/src/filter-param.c @@ -0,0 +1,889 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <regex.h> + +#include "rpmatch.h" +#include "smartcolsP.h" + +struct filter_param { + struct filter_node node; + int type; + enum filter_holder holder; + + union { + char *str; + unsigned long long num; + long double fnum; + bool boolean; + } val; + + struct list_head pr_params; + struct libscols_column *col; + char *holder_name; + regex_t *re; + + unsigned int fetched :1, /* holder requested */ + empty : 1; +}; + +static int cast_param(int type, struct filter_param *n); + +static inline const char *datatype2str(int type) +{ + static const char *types[] = { + [SCOLS_DATA_NONE] = "none", + [SCOLS_DATA_STRING] = "string", + [SCOLS_DATA_U64] = "u64", + [SCOLS_DATA_FLOAT] = "float", + [SCOLS_DATA_BOOLEAN] = "boolean" + }; + return types[type]; +} + +static char *rem_quotation(const char *p, int c) +{ + size_t len = strlen(p); + + if (*(p + (len - 1)) == c) + len -= 2; + return strndup(p + 1, len); +} + +static int param_set_data(struct filter_param *n, int type, const void *data) +{ + const char *p; + + /*DBG(FPARAM, ul_debugobj(n, " set %s data", datatype2str(type)));*/ + + switch (type) { + case SCOLS_DATA_STRING: + p = data; + if (p && (*p == '"' || *p == '\'')) + n->val.str = rem_quotation(p, *p); + else if (data) + n->val.str = strdup((char *) data); + if (data && !n->val.str) + return -ENOMEM; + if (data) { + rtrim_whitespace((unsigned char *) n->val.str); + ltrim_whitespace((unsigned char *) n->val.str); + } + break; + case SCOLS_DATA_U64: + n->val.num = data ? *((unsigned long long *) data) : 0; + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = data ? *((long double *) data) : 0; + break; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = data ? (*((bool *) data) == 0 ? 0 : 1) : 0; + break; + default: + return 0; + } + + n->type = type; + n->empty = data == NULL; + return 0; +} + +struct filter_node *filter_new_param( + struct libscols_filter *fltr, + int type, + enum filter_holder holder, + void *data) +{ + struct filter_param *n = (struct filter_param *) __filter_new_node( + F_NODE_PARAM, + sizeof(struct filter_param)); + if (!n) + return NULL; + + n->type = type; + n->holder = holder; + INIT_LIST_HEAD(&n->pr_params); + + if (param_set_data(n, type, data) != 0) { + filter_free_param(n); + return NULL; + } + + if (holder == F_HOLDER_COLUMN) { + n->holder_name = strdup((char *) data); + DBG(FLTR, ul_debugobj(fltr, "new %s holder", n->holder_name)); + } + + if (fltr) + list_add_tail(&n->pr_params, &fltr->params); + + return (struct filter_node *) n; +} + +int filter_compile_param(struct libscols_filter *fltr, struct filter_param *n) +{ + int rc; + + if (n->re) + return 0; + if (!n->val.str) + return -EINVAL; + + n->re = calloc(1, sizeof(regex_t)); + if (!n->re) + return -ENOMEM; + + rc = regcomp(n->re, n->val.str, REG_NOSUB | REG_EXTENDED); + if (rc) { + size_t size = regerror(rc, n->re, NULL, 0); + + fltr->errmsg = malloc(size + 1); + if (!fltr->errmsg) + return -ENOMEM; + regerror(rc, n->re, fltr->errmsg, size); + return -EINVAL; + } + return 0; +} + +static struct filter_param *copy_param(struct filter_param *n) +{ + void *data = NULL; + + switch (n->type) { + case SCOLS_DATA_STRING: + data = n->val.str; + break; + case SCOLS_DATA_U64: + data = &n->val.num; + break; + case SCOLS_DATA_FLOAT: + data = &n->val.fnum; + break; + case SCOLS_DATA_BOOLEAN: + data = &n->val.boolean; + break; + } + + DBG(FPARAM, ul_debugobj(n, "copying")); + return (struct filter_param *) filter_new_param(NULL, n->type, F_HOLDER_NONE, data); +} + +static void param_reset_data(struct filter_param *n) +{ + if (n->type == SCOLS_DATA_STRING) + free(n->val.str); + + memset(&n->val, 0, sizeof(n->val)); + n->fetched = 0; + n->empty = 1; + + if (n->re) { + regfree(n->re); + free(n->re); + n->re = NULL; + } +} + +void filter_free_param(struct filter_param *n) +{ + param_reset_data(n); + + free(n->holder_name); + list_del_init(&n->pr_params); + scols_unref_column(n->col); + free(n); +} + +int filter_param_get_datatype(struct filter_param *n) +{ + return n ? n->type : SCOLS_DATA_NONE; +} + +int is_filter_holder_node(struct filter_node *n) +{ + return n && filter_node_get_type(n) == F_NODE_PARAM + && ((struct filter_param *)(n))->holder; +} + +void filter_dump_param(struct ul_jsonwrt *json, struct filter_param *n) +{ + ul_jsonwrt_object_open(json, "param"); + + if (n->empty) { + ul_jsonwrt_value_boolean(json, "empty", true); + ul_jsonwrt_value_s(json, "type", datatype2str(n->type)); + } else { + switch (n->type) { + case SCOLS_DATA_STRING: + ul_jsonwrt_value_s(json, "string", n->val.str); + break; + case SCOLS_DATA_U64: + ul_jsonwrt_value_u64(json, "number", n->val.num); + break; + case SCOLS_DATA_FLOAT: + ul_jsonwrt_value_double(json, "float", n->val.fnum); + break; + case SCOLS_DATA_BOOLEAN: + ul_jsonwrt_value_boolean(json, "bool", n->val.boolean); + break; + default: + break; + } + } + + if (n->holder == F_HOLDER_COLUMN) + ul_jsonwrt_value_s(json, "column", n->holder_name); + + ul_jsonwrt_object_close(json); +} + +int filter_param_reset_holder(struct filter_param *n) +{ + if (!n->holder || !n->col) + return 0; + + param_reset_data(n); + + if (n->type != SCOLS_DATA_NONE) + return 0; /* already set */ + + if (scols_column_get_data_type(n->col)) + /* use by application defined type */ + n->type = scols_column_get_data_type(n->col); + else { + /* use by JSON defined type, default to string if not specified */ + switch (n->col->json_type) { + case SCOLS_JSON_NUMBER: + n->type = SCOLS_DATA_U64; + break; + case SCOLS_JSON_BOOLEAN: + n->type = SCOLS_DATA_BOOLEAN; + break; + case SCOLS_JSON_FLOAT: + n->type = SCOLS_DATA_FLOAT; + break; + case SCOLS_JSON_STRING: + default: + n->type = SCOLS_DATA_STRING; + break; + } + } + + DBG(FPARAM, ul_debugobj(n, "holder %s type: %s", n->holder_name, datatype2str(n->type))); + return 0; +} + +static int fetch_holder_data(struct libscols_filter *fltr __attribute__((__unused__)), + struct filter_param *n, struct libscols_line *ln) +{ + const char *data = NULL; + struct libscols_column *cl = n->col; + int type = n->type; + int rc = 0; + + if (n->fetched || n->holder != F_HOLDER_COLUMN) + return 0; + if (!cl) { + DBG(FPARAM, ul_debugobj(n, "no column for %s holder", n->holder_name)); + return -EINVAL; + } + DBG(FPARAM, ul_debugobj(n, "fetching %s data", n->holder_name)); + + if (fltr->filler_cb && !scols_line_is_filled(ln, cl->seqnum)) { + DBG(FPARAM, ul_debugobj(n, " by callback")); + rc = fltr->filler_cb(fltr, ln, cl->seqnum, fltr->filler_data); + if (rc) + return rc; + } + + n->fetched = 1; + + if (scols_column_has_data_func(cl)) { + struct libscols_cell *ce = scols_line_get_column_cell(ln, cl); + + DBG(FPARAM, ul_debugobj(n, " using datafunc()")); + if (ce) + data = cl->datafunc(n->col, ce, cl->datafunc_data); + if (data) + rc = param_set_data(n, scols_column_get_data_type(cl), data); + } else { + DBG(FPARAM, ul_debugobj(n, " using as string")); + data = scols_line_get_column_data(ln, n->col); + rc = param_set_data(n, SCOLS_DATA_STRING, data); + } + + /* cast to the wanted type */ + if (rc == 0 && type != SCOLS_DATA_NONE) + rc = cast_param(type, n); + return rc; +} + +int filter_eval_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct filter_param *n, + int *status) +{ + int rc = 0; + + DBG(FLTR, ul_debugobj(fltr, "eval param")); + + rc = fetch_holder_data(fltr, n, ln); + if (n->empty || rc) { + *status = 0; + goto done; + } + + switch (n->type) { + case SCOLS_DATA_STRING: + *status = n->val.str != NULL && *n->val.str != '\0'; + break; + case SCOLS_DATA_U64: + *status = n->val.num != 0; + break; + case SCOLS_DATA_FLOAT: + *status = n->val.fnum != 0.0; + break; + case SCOLS_DATA_BOOLEAN: + *status = n->val.boolean != false; + break; + default: + rc = -EINVAL; + break; + } +done: + if (rc) + DBG(FLTR, ul_debugobj(fltr, "failed eval param [rc=%d]", rc)); + return rc; +} + +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct) +{ + unsigned long long num = 0; + + if (ct->func == SCOLS_COUNTER_COUNT) { + ct->result++; + return 0; + } + + if (ct->param) { + int rc; + + ct->param->type = SCOLS_DATA_U64; + rc = fetch_holder_data(fltr, ct->param, ln); + if (rc) + return rc; + + if (ct->param->empty) + return -EINVAL; + + num = ct->param->val.num; + } + + switch (ct->func) { + case SCOLS_COUNTER_MAX: + if (!ct->has_result) + ct->result = num; + else if (num > ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_MIN: + if (!ct->has_result) + ct->result = num; + else if (num < ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_SUM: + ct->result += num; + break; + default: + return -EINVAL; + } + + ct->has_result = 1; + DBG(FLTR, ul_debugobj(fltr, "counted '%s' [result: %llu]", ct->name, ct->result)); + return 0; +} + +static int xstrcmp(char *a, char *b) +{ + if (!a && !b) + return 0; + if (!a && b) + return -1; + if (a && !b) + return 1; + return strcmp(a, b); +} + +static int string_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + switch (oper) { + case F_EXPR_EQ: + *status = xstrcmp(l->val.str, r->val.str) == 0; + break; + case F_EXPR_NE: + *status = xstrcmp(l->val.str, r->val.str) != 0; + break; + case F_EXPR_LE: + *status = xstrcmp(l->val.str, r->val.str) <= 0; + break; + case F_EXPR_LT: + *status = xstrcmp(l->val.str, r->val.str) < 0; + break; + case F_EXPR_GE: + *status = xstrcmp(l->val.str, r->val.str) >= 0; + break; + case F_EXPR_GT: + *status = xstrcmp(l->val.str, r->val.str) > 0; + break; + case F_EXPR_REG: + if (!r->re) + return -EINVAL; + *status = regexec(r->re, l->val.str ? : "", 0, NULL, 0) == 0; + break; + case F_EXPR_NREG: + if (!r->re) + return -EINVAL; + *status = regexec(r->re, l->val.str ? : "", 0, NULL, 0) != 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int u64_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.num == r->val.num; + break; + case F_EXPR_NE: + *status = l->val.num != r->val.num; + break; + case F_EXPR_LE: + *status = l->val.num <= r->val.num; + break; + case F_EXPR_LT: + *status = l->val.num < r->val.num; + break; + case F_EXPR_GE: + *status = l->val.num >= r->val.num; + break; + case F_EXPR_GT: + *status = l->val.num > r->val.num; + break; + default: + return -EINVAL; + } + return 0; +} + +static int float_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.fnum == r->val.fnum; + break; + case F_EXPR_NE: + *status = l->val.fnum != r->val.fnum; + break; + case F_EXPR_LE: + *status = l->val.fnum <= r->val.fnum; + break; + case F_EXPR_LT: + *status = l->val.fnum < r->val.fnum; + break; + case F_EXPR_GE: + *status = l->val.fnum >= r->val.fnum; + break; + case F_EXPR_GT: + *status = l->val.fnum > r->val.fnum; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bool_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.boolean == r->val.boolean; + break; + case F_EXPR_NE: + *status = l->val.boolean != r->val.boolean; + break; + case F_EXPR_LE: + *status = l->val.boolean <= r->val.boolean; + break; + case F_EXPR_LT: + *status = l->val.boolean < r->val.boolean; + break; + case F_EXPR_GE: + *status = l->val.boolean >= r->val.boolean; + break; + case F_EXPR_GT: + *status = l->val.boolean > r->val.boolean; + break; + default: + return -EINVAL; + } + return 0; +} + +/* call filter_cast_param() to be sure that param data are ready (fetched from + * holder, etc.) */ +int filter_compare_params(struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_etype oper, + struct filter_param *l, + struct filter_param *r, + int *status) +{ + int rc; + + if (!l || !r || l->type != r->type) + return -EINVAL; + + *status = 0; + + switch (l->type) { + case SCOLS_DATA_STRING: + rc = string_opers(oper, l, r, status); + break; + case SCOLS_DATA_U64: + rc = u64_opers(oper, l, r, status); + break; + case SCOLS_DATA_FLOAT: + rc = float_opers(oper, l, r, status); + break; + case SCOLS_DATA_BOOLEAN: + rc = bool_opers(oper, l, r, status); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int string_cast(int type, struct filter_param *n) +{ + char *str = n->val.str; + + if (type == SCOLS_DATA_STRING) + return 0; + + n->val.str = NULL; + + switch (type) { + case SCOLS_DATA_U64: + { + uint64_t num = 0; + if (str) { + int rc = ul_strtou64(str, &num, 10); + if (rc) + return rc; + } + n->val.num = num; + break; + } + case SCOLS_DATA_FLOAT: + { + long double num = 0; + if (str) { + int rc = ul_strtold(str, &num); + if (rc) + return rc; + } + n->val.fnum = num; + break; + } + case SCOLS_DATA_BOOLEAN: + { + bool x = str && *str + && (strcasecmp(str, "1") == 0 + || strcasecmp(str, "true") == 0 + || rpmatch(str) == RPMATCH_YES); + n->val.boolean = x; + break; + } + default: + return -EINVAL; + } + + free(str); + return 0; +} + +static int u64_cast(int type, struct filter_param *n) +{ + unsigned long long num = n->val.num; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%llu", num) <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = num; + break; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = num > 0 ? true : false; + break; + default: + return -EINVAL; + } + return 0; +} + +static int float_cast(int type, struct filter_param *n) +{ + long double fnum = n->val.fnum; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%Lg", fnum) <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + n->val.num = fnum; + break; + case SCOLS_DATA_FLOAT: + break;; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = fnum > 0.0 ? true : false; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bool_cast(int type, struct filter_param *n) +{ + bool x = n->val.boolean; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%s", x ? "true" : "false") <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + n->val.num = x ? 1 : 0; + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = x ? 1.0 : 0.0; + break; + case SCOLS_DATA_BOOLEAN: + break;; + default: + return -EINVAL; + } + return 0; +} + +static int cast_param(int type, struct filter_param *n) +{ + int rc; + int orgtype = n->type; + + if (type == orgtype) + return 0; + + if (orgtype == SCOLS_DATA_STRING) + DBG(FPARAM, ul_debugobj(n, " casting \"%s\" to %s", n->val.str, datatype2str(type))); + else + DBG(FPARAM, ul_debugobj(n, " casting %s to %s", datatype2str(orgtype), datatype2str(type))); + + switch (orgtype) { + case SCOLS_DATA_STRING: + rc = string_cast(type, n); + break; + case SCOLS_DATA_U64: + rc = u64_cast(type, n); + break; + case SCOLS_DATA_FLOAT: + rc = float_cast(type, n); + break; + case SCOLS_DATA_BOOLEAN: + rc = bool_cast(type, n); + break; + default: + rc = -EINVAL; + break; + } + + if (rc == 0) + n->type = type; + + if (rc) + DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc)); + return rc; +} + +int filter_cast_param(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_param *n, + struct filter_param **result) +{ + int rc; + int orgtype = n->type; + + DBG(FPARAM, ul_debugobj(n, "casting param to %s", datatype2str(type))); + rc = fetch_holder_data(fltr, n, ln); + if (rc) + return rc; + + if (type == orgtype) { + filter_ref_node((struct filter_node *) n); /* caller wants to call filter_unref_node() for the result */ + *result = n; + return 0; + } + + *result = copy_param(n); + if (!*result) + return -ENOMEM; + rc = cast_param(type, *result); + + DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc)); + return rc; +} + +int filter_next_param(struct libscols_filter *fltr, + struct libscols_iter *itr, struct filter_param **prm) +{ + int rc = 1; + + if (!fltr || !itr || !prm) + return -EINVAL; + *prm = NULL; + + if (!itr->head) + SCOLS_ITER_INIT(itr, &fltr->params); + if (itr->p != itr->head) { + SCOLS_ITER_ITERATE(itr, *prm, struct filter_param, pr_params); + rc = 0; + } + + return rc; +} + +/** + * scols_filter_assign_column: + * @fltr: pointer to filter + * @itr: iterator + * @name: holder name + * @col: column + * + * Assign @col to filter parametr. The parametr is addressed by @itr or by + * @name. See scols_filter_next_holder(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_filter_assign_column(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char *name, struct libscols_column *col) +{ + struct filter_param *n = NULL; + + if (itr && itr->p) { + struct list_head *p = IS_ITER_FORWARD(itr) ? + itr->p->prev : itr->p->next; + n = list_entry(p, struct filter_param, pr_params); + } else if (name) { + struct libscols_iter xitr; + struct filter_param *x = NULL; + + scols_reset_iter(&xitr, SCOLS_ITER_FORWARD); + while (filter_next_param(fltr, &xitr, &x) == 0) { + if (x->col + || x->holder != F_HOLDER_COLUMN + || strcmp(name, x->holder_name) != 0) + continue; + n = x; + break; + } + } + + if (n) { + if (n->col) + scols_unref_column(n->col); + + DBG(FPARAM, ul_debugobj(n, "assing %s to column %s", name, + scols_column_get_name(col))); + n->col = col; + scols_ref_column(col); + } + + return n ? 0 : -EINVAL; +} + +/** + * scols_filter_next_holder: + * @fltr: filter instance + * @itr: a pointer to a struct libscols_iter instance + * @name: returns the next column name + * @type: 0 (not implemented yet) + * + * Finds the next holder used in the expression and and returns a name via + * @name. The currently supported holder type is only column name. + * + * Returns: 0, a negative value in case of an error, and 1 at the end. + * + * Since: 2.40 + */ +int scols_filter_next_holder(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char **name, + int type) +{ + struct filter_param *prm = NULL; + int rc = 0; + + *name = NULL; + if (!type) + type = F_HOLDER_COLUMN; /* default */ + + do { + rc = filter_next_param(fltr, itr, &prm); + if (rc == 0 && (int) prm->holder == type) { + *name = prm->holder_name; + } + } while (rc == 0 && !*name); + + return rc; +} diff --git a/libsmartcols/src/filter-parser.c b/libsmartcols/src/filter-parser.c new file mode 100644 index 0000000..cf9ed9b --- /dev/null +++ b/libsmartcols/src/filter-parser.c @@ -0,0 +1,1803 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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 3 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, see <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 2 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 1 "libsmartcols/src/filter-parser.y" + +#ifdef __clang__ +/* clang detects yynerrs as unused. + * Will be fixed in future versions of bison. + */ +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif + +#include <stdio.h> + +#include "smartcolsP.h" +#include "filter-parser.h" +#include "filter-scanner.h" + +void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); + + +#line 89 "libsmartcols/src/filter-parser.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "filter-parser.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_T_NUMBER = 3, /* T_NUMBER */ + YYSYMBOL_T_STRING = 4, /* T_STRING */ + YYSYMBOL_T_HOLDER = 5, /* T_HOLDER */ + YYSYMBOL_T_FLOAT = 6, /* T_FLOAT */ + YYSYMBOL_T_OR = 7, /* T_OR */ + YYSYMBOL_T_AND = 8, /* T_AND */ + YYSYMBOL_T_EQ = 9, /* T_EQ */ + YYSYMBOL_T_NE = 10, /* T_NE */ + YYSYMBOL_T_LT = 11, /* T_LT */ + YYSYMBOL_T_LE = 12, /* T_LE */ + YYSYMBOL_T_GT = 13, /* T_GT */ + YYSYMBOL_T_GE = 14, /* T_GE */ + YYSYMBOL_T_REG = 15, /* T_REG */ + YYSYMBOL_T_NREG = 16, /* T_NREG */ + YYSYMBOL_T_TRUE = 17, /* T_TRUE */ + YYSYMBOL_T_FALSE = 18, /* T_FALSE */ + YYSYMBOL_T_NEG = 19, /* T_NEG */ + YYSYMBOL_20_ = 20, /* '(' */ + YYSYMBOL_21_ = 21, /* ')' */ + YYSYMBOL_YYACCEPT = 22, /* $accept */ + YYSYMBOL_filter = 23, /* filter */ + YYSYMBOL_expr = 24, /* expr */ + YYSYMBOL_param = 25 /* param */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 14 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 54 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 22 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 4 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 21 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 36 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 274 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 20, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 72, 72, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 88, 94, 102, 103, 104, 105, + 106, 110 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "T_NUMBER", "T_STRING", + "T_HOLDER", "T_FLOAT", "T_OR", "T_AND", "T_EQ", "T_NE", "T_LT", "T_LE", + "T_GT", "T_GE", "T_REG", "T_NREG", "T_TRUE", "T_FALSE", "T_NEG", "'('", + "')'", "$accept", "filter", "expr", "param", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-8) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 1, -8, -8, -8, -8, -8, -8, 1, 1, 2, + 30, -8, -8, 15, -8, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -8, 38, 38, -8, -8, + -8, -8, -8, -8, -8, -8 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 16, 19, 18, 17, 20, 21, 0, 0, 0, + 2, 3, 7, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 6, 5, 8, 9, + 11, 10, 13, 12, 14, 15 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -8, -8, -7, -8 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 9, 10, 11 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 12, 13, 14, 0, 1, 2, 3, 4, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 5, 6, + 7, 8, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 0, 0, 0, 0, 25, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 17, 18, 19, + 20, 21, 22, 23, 24 +}; + +static const yytype_int8 yycheck[] = +{ + 7, 8, 0, -1, 3, 4, 5, 6, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 17, 18, + 19, 20, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, -1, -1, -1, -1, 21, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 9, 10, 11, + 12, 13, 14, 15, 16 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 4, 5, 6, 17, 18, 19, 20, 23, + 24, 25, 24, 24, 0, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 21, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 22, 23, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, + 25, 25 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 3, 3, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, + 1, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (scanner, fltr, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, scanner, fltr); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (scanner); + YY_USE (fltr); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, scanner, fltr); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, void *scanner, struct libscols_filter *fltr) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], scanner, fltr); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, scanner, fltr); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + YY_USE (yyvaluep); + YY_USE (scanner); + YY_USE (fltr); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yykind) + { + case YYSYMBOL_expr: /* expr */ +#line 59 "libsmartcols/src/filter-parser.y" + { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != ((*yyvaluep).param)) + filter_unref_node(((*yyvaluep).param)); + } +#line 1133 "libsmartcols/src/filter-parser.c" + break; + + case YYSYMBOL_param: /* param */ +#line 59 "libsmartcols/src/filter-parser.y" + { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != ((*yyvaluep).param)) + filter_unref_node(((*yyvaluep).param)); + } +#line 1145 "libsmartcols/src/filter-parser.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void *scanner, struct libscols_filter *fltr) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, scanner); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* filter: expr */ +#line 72 "libsmartcols/src/filter-parser.y" + { fltr->root = (yyvsp[0].param); } +#line 1424 "libsmartcols/src/filter-parser.c" + break; + + case 3: /* expr: param */ +#line 76 "libsmartcols/src/filter-parser.y" + { (yyval.param) = (yyvsp[0].param); } +#line 1430 "libsmartcols/src/filter-parser.c" + break; + + case 4: /* expr: '(' expr ')' */ +#line 77 "libsmartcols/src/filter-parser.y" + { (yyval.param) = (yyvsp[-1].param); } +#line 1436 "libsmartcols/src/filter-parser.c" + break; + + case 5: /* expr: expr T_AND expr */ +#line 78 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_AND, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1442 "libsmartcols/src/filter-parser.c" + break; + + case 6: /* expr: expr T_OR expr */ +#line 79 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_OR, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1448 "libsmartcols/src/filter-parser.c" + break; + + case 7: /* expr: T_NEG expr */ +#line 80 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_NEG, NULL, (yyvsp[0].param)); } +#line 1454 "libsmartcols/src/filter-parser.c" + break; + + case 8: /* expr: expr T_EQ expr */ +#line 81 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_EQ, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1460 "libsmartcols/src/filter-parser.c" + break; + + case 9: /* expr: expr T_NE expr */ +#line 82 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_NE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1466 "libsmartcols/src/filter-parser.c" + break; + + case 10: /* expr: expr T_LE expr */ +#line 83 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_LE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1472 "libsmartcols/src/filter-parser.c" + break; + + case 11: /* expr: expr T_LT expr */ +#line 84 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_LT, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1478 "libsmartcols/src/filter-parser.c" + break; + + case 12: /* expr: expr T_GE expr */ +#line 85 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_GE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1484 "libsmartcols/src/filter-parser.c" + break; + + case 13: /* expr: expr T_GT expr */ +#line 86 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_GT, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1490 "libsmartcols/src/filter-parser.c" + break; + + case 14: /* expr: expr T_REG expr */ +#line 88 "libsmartcols/src/filter-parser.y" + { + if (filter_compile_param(fltr, (struct filter_param *) (yyvsp[0].param)) != 0) + YYERROR; + (yyval.param) = filter_new_expr(fltr, F_EXPR_REG, (yyvsp[-2].param), (yyvsp[0].param)); + } +#line 1500 "libsmartcols/src/filter-parser.c" + break; + + case 15: /* expr: expr T_NREG expr */ +#line 94 "libsmartcols/src/filter-parser.y" + { + if (filter_compile_param(fltr, (struct filter_param *) (yyvsp[0].param)) != 0) + YYERROR; + (yyval.param) = filter_new_expr(fltr, F_EXPR_NREG, (yyvsp[-2].param), (yyvsp[0].param)); + } +#line 1510 "libsmartcols/src/filter-parser.c" + break; + + case 16: /* param: T_NUMBER */ +#line 102 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_U64, 0, (void *) (&(yyvsp[0].param_number))); } +#line 1516 "libsmartcols/src/filter-parser.c" + break; + + case 17: /* param: T_FLOAT */ +#line 103 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_FLOAT, 0, (void *) (&(yyvsp[0].param_float))); } +#line 1522 "libsmartcols/src/filter-parser.c" + break; + + case 18: /* param: T_HOLDER */ +#line 104 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_NONE, F_HOLDER_COLUMN, (void *) (yyvsp[0].param_name)); } +#line 1528 "libsmartcols/src/filter-parser.c" + break; + + case 19: /* param: T_STRING */ +#line 105 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_STRING, 0, (void *) (yyvsp[0].param_string)); } +#line 1534 "libsmartcols/src/filter-parser.c" + break; + + case 20: /* param: T_TRUE */ +#line 106 "libsmartcols/src/filter-parser.y" + { + bool x = true; + (yyval.param) = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } +#line 1543 "libsmartcols/src/filter-parser.c" + break; + + case 21: /* param: T_FALSE */ +#line 110 "libsmartcols/src/filter-parser.y" + { + bool x = false; + (yyval.param) = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } +#line 1552 "libsmartcols/src/filter-parser.c" + break; + + +#line 1556 "libsmartcols/src/filter-parser.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (scanner, fltr, yymsgp); + if (yysyntax_error_status == YYENOMEM) + YYNOMEM; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, scanner, fltr); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, scanner, fltr); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (scanner, fltr, YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, scanner, fltr); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, scanner, fltr); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 118 "libsmartcols/src/filter-parser.y" + + +void yyerror (yyscan_t *locp __attribute__((__unused__)), + struct libscols_filter *fltr, + char const *msg) +{ + if (msg && fltr) { + char *p; + + if (fltr->errmsg) + free(fltr->errmsg); + + fltr->errmsg = strdup(msg); + if (!fltr->errmsg) + return; + + p = strstr(fltr->errmsg, "T_"); + if (p) { + size_t sz = strlen(fltr->errmsg); + memmove(p, p + 2, sz - 1 - (p - fltr->errmsg)); + } + } + errno = EINVAL; +} diff --git a/libsmartcols/src/filter-parser.h b/libsmartcols/src/filter-parser.h new file mode 100644 index 0000000..45666f1 --- /dev/null +++ b/libsmartcols/src/filter-parser.h @@ -0,0 +1,110 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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 3 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, see <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED +# define YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +#line 27 "libsmartcols/src/filter-parser.y" + + +#line 52 "libsmartcols/src/filter-parser.h" + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + T_NUMBER = 258, /* T_NUMBER */ + T_STRING = 259, /* T_STRING */ + T_HOLDER = 260, /* T_HOLDER */ + T_FLOAT = 261, /* T_FLOAT */ + T_OR = 262, /* T_OR */ + T_AND = 263, /* T_AND */ + T_EQ = 264, /* T_EQ */ + T_NE = 265, /* T_NE */ + T_LT = 266, /* T_LT */ + T_LE = 267, /* T_LE */ + T_GT = 268, /* T_GT */ + T_GE = 269, /* T_GE */ + T_REG = 270, /* T_REG */ + T_NREG = 271, /* T_NREG */ + T_TRUE = 272, /* T_TRUE */ + T_FALSE = 273, /* T_FALSE */ + T_NEG = 274 /* T_NEG */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 40 "libsmartcols/src/filter-parser.y" + + unsigned long long param_number; + const char* param_string; + const char* param_name; + long double param_float; + struct filter_node *param; + struct filter_node *expr; + +#line 97 "libsmartcols/src/filter-parser.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + + +int yyparse (void *scanner, struct libscols_filter *fltr); + + +#endif /* !YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED */ diff --git a/libsmartcols/src/filter-parser.stamp b/libsmartcols/src/filter-parser.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libsmartcols/src/filter-parser.stamp diff --git a/libsmartcols/src/filter-parser.y b/libsmartcols/src/filter-parser.y new file mode 100644 index 0000000..ce245f3 --- /dev/null +++ b/libsmartcols/src/filter-parser.y @@ -0,0 +1,141 @@ +%{ +#ifdef __clang__ +/* clang detects yynerrs as unused. + * Will be fixed in future versions of bison. + */ +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif + +#include <stdio.h> + +#include "smartcolsP.h" +#include "filter-parser.h" +#include "filter-scanner.h" + +void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); + +%} + +%define api.pure full + +%lex-param {void *scanner} +%parse-param {void *scanner}{struct libscols_filter *fltr} + +%define parse.error verbose + +%code requires +{ +} + +/* Elegant way, but not compatible with biron -y (autotools): +%define api.value.type union +%token <unsigned long long> param_number +%token <const char*> param_string +%token <const char*> param_name +%token <long double> param_float +%type <struct filter_node*> param +%type <struct filter_node*> expr +*/ + +%union { + unsigned long long param_number; + const char* param_string; + const char* param_name; + long double param_float; + struct filter_node *param; + struct filter_node *expr; +} +%token <param_number> T_NUMBER +%token <param_string> T_STRING +%token <param_name> T_HOLDER +%token <param_float> T_FLOAT +%type <param> param expr + +%token T_OR T_AND T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG +%left T_OR T_AND +%left T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG + + +%destructor { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != $$) + filter_unref_node($$); + } <param> + +%% + +%start filter; + +filter: + expr { fltr->root = $1; } +; + +expr: + param { $$ = $1; } + | '(' expr ')' { $$ = $2; } + | expr T_AND expr { $$ = filter_new_expr(fltr, F_EXPR_AND, $1, $3); } + | expr T_OR expr { $$ = filter_new_expr(fltr, F_EXPR_OR, $1, $3); } + | T_NEG expr { $$ = filter_new_expr(fltr, F_EXPR_NEG, NULL, $2); } + | expr T_EQ expr { $$ = filter_new_expr(fltr, F_EXPR_EQ, $1, $3); } + | expr T_NE expr { $$ = filter_new_expr(fltr, F_EXPR_NE, $1, $3); } + | expr T_LE expr { $$ = filter_new_expr(fltr, F_EXPR_LE, $1, $3); } + | expr T_LT expr { $$ = filter_new_expr(fltr, F_EXPR_LT, $1, $3); } + | expr T_GE expr { $$ = filter_new_expr(fltr, F_EXPR_GE, $1, $3); } + | expr T_GT expr { $$ = filter_new_expr(fltr, F_EXPR_GT, $1, $3); } + + | expr T_REG expr { + if (filter_compile_param(fltr, (struct filter_param *) $3) != 0) + YYERROR; + $$ = filter_new_expr(fltr, F_EXPR_REG, $1, $3); + } + + | expr T_NREG expr { + if (filter_compile_param(fltr, (struct filter_param *) $3) != 0) + YYERROR; + $$ = filter_new_expr(fltr, F_EXPR_NREG, $1, $3); + } +; + +param: + T_NUMBER { $$ = filter_new_param(fltr, SCOLS_DATA_U64, 0, (void *) (&$1)); } + | T_FLOAT { $$ = filter_new_param(fltr, SCOLS_DATA_FLOAT, 0, (void *) (&$1)); } + | T_HOLDER { $$ = filter_new_param(fltr, SCOLS_DATA_NONE, F_HOLDER_COLUMN, (void *) $1); } + | T_STRING { $$ = filter_new_param(fltr, SCOLS_DATA_STRING, 0, (void *) $1); } + | T_TRUE { + bool x = true; + $$ = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } + | T_FALSE { + bool x = false; + $$ = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } + +; + + +%% + +void yyerror (yyscan_t *locp __attribute__((__unused__)), + struct libscols_filter *fltr, + char const *msg) +{ + if (msg && fltr) { + char *p; + + if (fltr->errmsg) + free(fltr->errmsg); + + fltr->errmsg = strdup(msg); + if (!fltr->errmsg) + return; + + p = strstr(fltr->errmsg, "T_"); + if (p) { + size_t sz = strlen(fltr->errmsg); + memmove(p, p + 2, sz - 1 - (p - fltr->errmsg)); + } + } + errno = EINVAL; +} diff --git a/libsmartcols/src/filter-scanner.c b/libsmartcols/src/filter-scanner.c new file mode 100644 index 0000000..fdebb46 --- /dev/null +++ b/libsmartcols/src/filter-scanner.c @@ -0,0 +1,2096 @@ +#line 1 "libsmartcols/src/filter-scanner.c" + +#line 3 "libsmartcols/src/filter-scanner.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yyget_lval +#define yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval yyget_lval +#endif + +#ifdef yyset_lval +#define yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval yyset_lval +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +#define yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 23 +#define YY_END_OF_BUFFER 24 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[78] = + { 0, + 0, 0, 24, 23, 1, 2, 8, 23, 23, 5, + 3, 4, 20, 12, 23, 14, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 23, 1, 2, 10, 16, 0, 22, + 6, 0, 0, 20, 11, 9, 15, 13, 21, 21, + 9, 21, 13, 14, 11, 12, 10, 21, 7, 21, + 21, 21, 21, 21, 7, 19, 6, 21, 8, 21, + 21, 21, 21, 18, 21, 17, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 6, 7, 8, 9, + 10, 1, 1, 1, 6, 11, 6, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 6, 1, 13, + 14, 15, 1, 1, 16, 17, 17, 18, 19, 20, + 21, 17, 17, 17, 17, 22, 17, 23, 24, 17, + 25, 26, 27, 28, 29, 17, 17, 17, 17, 17, + 1, 1, 1, 1, 6, 1, 30, 17, 17, 31, + + 32, 33, 34, 17, 17, 17, 17, 35, 17, 36, + 37, 17, 38, 39, 40, 41, 42, 17, 17, 17, + 17, 17, 1, 43, 1, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[45] = + { 0, + 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, + 3, 3, 1, 1, 1, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1 + } ; + +static const flex_int16_t yy_base[81] = + { 0, + 0, 0, 108, 109, 105, 103, 31, 100, 97, 95, + 109, 109, 35, 88, 34, 87, 77, 0, 74, 82, + 30, 31, 32, 71, 70, 59, 56, 63, 20, 21, + 23, 53, 52, 47, 87, 85, 109, 109, 82, 109, + 109, 78, 73, 52, 109, 109, 109, 109, 0, 65, + 0, 58, 0, 0, 0, 0, 0, 49, 0, 47, + 43, 38, 31, 29, 109, 58, 0, 42, 0, 49, + 27, 34, 46, 0, 25, 0, 109, 78, 81, 51 + } ; + +static const flex_int16_t yy_def[81] = + { 0, + 77, 1, 77, 77, 77, 77, 77, 78, 77, 79, + 77, 77, 77, 77, 77, 77, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 77, 77, 77, 77, 77, 78, 77, + 77, 79, 77, 77, 77, 77, 77, 77, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 77, 77, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 0, 77, 77, 77 + } ; + +static const flex_int16_t yy_nxt[154] = + { 0, + 4, 5, 6, 7, 8, 4, 9, 10, 11, 12, + 4, 13, 14, 15, 16, 17, 18, 18, 19, 20, + 21, 22, 23, 24, 18, 18, 18, 25, 18, 26, + 18, 27, 28, 29, 30, 31, 32, 18, 18, 18, + 33, 18, 34, 4, 37, 43, 44, 46, 53, 55, + 57, 53, 55, 49, 57, 58, 76, 54, 56, 63, + 54, 56, 43, 44, 76, 74, 75, 74, 73, 66, + 72, 69, 71, 67, 38, 70, 69, 47, 39, 68, + 39, 42, 67, 42, 66, 40, 40, 36, 35, 65, + 64, 59, 62, 51, 61, 60, 59, 52, 51, 50, + + 48, 45, 40, 41, 40, 36, 35, 77, 3, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77 + } ; + +static const flex_int16_t yy_chk[154] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7, 13, 13, 15, 21, 22, + 23, 29, 30, 80, 31, 23, 75, 21, 22, 31, + 29, 30, 44, 44, 73, 72, 71, 70, 68, 66, + 64, 63, 62, 61, 7, 60, 58, 15, 78, 52, + 78, 79, 50, 79, 43, 42, 39, 36, 35, 34, + 33, 32, 28, 27, 26, 25, 24, 20, 19, 17, + + 16, 14, 10, 9, 8, 6, 5, 3, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "libsmartcols/src/filter-scanner.l" +#line 2 "libsmartcols/src/filter-scanner.l" +#include "smartcolsP.h" +#include "filter-parser.h" /* define tokens (T_*) */ +#line 491 "libsmartcols/src/filter-scanner.c" +#define YY_NO_INPUT 1 +#line 493 "libsmartcols/src/filter-scanner.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 14 "libsmartcols/src/filter-scanner.l" + + +#line 768 "libsmartcols/src/filter-scanner.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 109 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 16 "libsmartcols/src/filter-scanner.l" +; /* ignore */ + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 17 "libsmartcols/src/filter-scanner.l" +; /* ignore */ + YY_BREAK +case 3: +YY_RULE_SETUP +#line 19 "libsmartcols/src/filter-scanner.l" +return '('; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 20 "libsmartcols/src/filter-scanner.l" +return ')'; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 21 "libsmartcols/src/filter-scanner.l" +return '\''; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 23 "libsmartcols/src/filter-scanner.l" +return T_AND; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 24 "libsmartcols/src/filter-scanner.l" +return T_OR; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 25 "libsmartcols/src/filter-scanner.l" +return T_NEG; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 27 "libsmartcols/src/filter-scanner.l" +return T_EQ; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 28 "libsmartcols/src/filter-scanner.l" +return T_NE; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 30 "libsmartcols/src/filter-scanner.l" +return T_LE; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 31 "libsmartcols/src/filter-scanner.l" +return T_LT; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 33 "libsmartcols/src/filter-scanner.l" +return T_GE; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 34 "libsmartcols/src/filter-scanner.l" +return T_GT; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 36 "libsmartcols/src/filter-scanner.l" +return T_REG; + YY_BREAK +case 16: +YY_RULE_SETUP +#line 37 "libsmartcols/src/filter-scanner.l" +return T_NREG; + YY_BREAK +case 17: +YY_RULE_SETUP +#line 39 "libsmartcols/src/filter-scanner.l" +return T_FALSE; + YY_BREAK +case 18: +YY_RULE_SETUP +#line 40 "libsmartcols/src/filter-scanner.l" +return T_TRUE; + YY_BREAK +case 19: +YY_RULE_SETUP +#line 42 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_float = strtold(yytext, NULL); + return T_FLOAT; +} + YY_BREAK +case 20: +YY_RULE_SETUP +#line 47 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); + return T_NUMBER; +} + YY_BREAK +case 21: +YY_RULE_SETUP +#line 52 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_name = yytext; + return T_HOLDER; +} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 57 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_string = yytext; + return T_STRING; +} + YY_BREAK +case 23: +YY_RULE_SETUP +#line 63 "libsmartcols/src/filter-scanner.l" +ECHO; + YY_BREAK +#line 953 "libsmartcols/src/filter-scanner.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 77); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 63 "libsmartcols/src/filter-scanner.l" diff --git a/libsmartcols/src/filter-scanner.h b/libsmartcols/src/filter-scanner.h new file mode 100644 index 0000000..e56f09b --- /dev/null +++ b/libsmartcols/src/filter-scanner.h @@ -0,0 +1,507 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 5 "libsmartcols/src/filter-scanner.h" + +#line 7 "libsmartcols/src/filter-scanner.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yyget_lval +#define yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval yyget_lval +#endif + +#ifdef yyset_lval +#define yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval yyset_lval +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 63 "libsmartcols/src/filter-scanner.l" + +#line 505 "libsmartcols/src/filter-scanner.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/libsmartcols/src/filter-scanner.l b/libsmartcols/src/filter-scanner.l new file mode 100644 index 0000000..501b603 --- /dev/null +++ b/libsmartcols/src/filter-scanner.l @@ -0,0 +1,62 @@ +%{ +#include "smartcolsP.h" +#include "filter-parser.h" /* define tokens (T_*) */ +%} + +%option reentrant bison-bridge noyywrap noinput nounput + +id [a-zA-Z][a-zA-Z_.%:/\-0-9]* +int [0-9]+ +blank [ \t] +str_qu \"[^\"\n]*\" +str_ap \'[^\'\n]*\' + +%% + +{blank}+ ; /* ignore */ +[\n]+ ; /* ignore */ + +"(" return '('; +")" return ')'; +"'" return '\''; + +and|AND|"&&" return T_AND; +or|OR|"||" return T_OR; +"!"|not|NOT return T_NEG; + +eq|EQ|"==" return T_EQ; +ne|NE|"!=" return T_NE; + +le|LE|"<=" return T_LE; +lt|LT|"<" return T_LT; + +ge|GE|">=" return T_GE; +gt|GT|">" return T_GT; + +"=~" return T_REG; +"!~" return T_NREG; + +false|FALSE return T_FALSE; +true|TRUE return T_TRUE; + +{int}+\.{int}+ { + yylval->param_float = strtold(yytext, NULL); + return T_FLOAT; +} + +{int}+ { + yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); + return T_NUMBER; +} + +{id} { + yylval->param_name = yytext; + return T_HOLDER; +} + +{str_ap}|{str_qu} { + yylval->param_string = yytext; + return T_STRING; +} + + diff --git a/libsmartcols/src/filter-scanner.stamp b/libsmartcols/src/filter-scanner.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libsmartcols/src/filter-scanner.stamp diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c new file mode 100644 index 0000000..dccf05c --- /dev/null +++ b/libsmartcols/src/filter.c @@ -0,0 +1,520 @@ +/* + * filter.c - functions for lines filtering + * + * Copyright (C) 2023 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: filter + * @title: Filters and counters + * @short_description: defines lines filter and counter + * + * An API to define and use filter and counters. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smartcolsP.h" + +#include "filter-parser.h" +#include "filter-scanner.h" + +/** + * scols_new_filter: + * @str: filter expression or NULL + * + * Allocated and optionally parses a new filter. + * + * Returns: new filter instance or NULL in case of error. + * + * Since: 2.40 + */ +struct libscols_filter *scols_new_filter(const char *str) +{ + struct libscols_filter *fltr = calloc(1, sizeof(*fltr)); + + if (!fltr) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc")); + fltr->refcount = 1; + INIT_LIST_HEAD(&fltr->params); + INIT_LIST_HEAD(&fltr->counters); + + if (str && scols_filter_parse_string(fltr, str) != 0) { + scols_unref_filter(fltr); + return NULL; + } + + return fltr; +} + +/** + * scols_ref_filter: + * @fltr: filter instance + * + * Increment filter reference counter. + * + * Since: 2.40 + */ +void scols_ref_filter(struct libscols_filter *fltr) +{ + if (fltr) + fltr->refcount++; +} + +static void reset_filter(struct libscols_filter *fltr) +{ + if (!fltr) + return; + filter_unref_node(fltr->root); + fltr->root = NULL; + + if (fltr->src) + fclose(fltr->src); + fltr->src = NULL; + + free(fltr->errmsg); + fltr->errmsg = NULL; +} + +static void remove_counters(struct libscols_filter *fltr) +{ + if (!fltr) + return; + + DBG(FLTR, ul_debugobj(fltr, "remove all counters")); + while (!list_empty(&fltr->counters)) { + struct libscols_counter *ct = list_entry(fltr->counters.next, + struct libscols_counter, counters); + + filter_unref_node((struct filter_node *) ct->param); + list_del_init(&ct->counters); + free(ct->name); + free(ct); + } +} + +/** + * scols_unref_filter: + * @fltr: filter instance + * + * Deincrements reference counter, unallocates the filter for the last + * reference. + * + * Since: 2.40 + */ +void scols_unref_filter(struct libscols_filter *fltr) +{ + if (fltr && --fltr->refcount <= 0) { + DBG(FLTR, ul_debugobj(fltr, "dealloc")); + reset_filter(fltr); + remove_counters(fltr); + free(fltr); + } +} + +/* This is generic allocater for a new node, always use the node type specific + * functions (e.g. filter_new_param() */ +struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz) +{ + struct filter_node *n = calloc(1, sz); + + if (!n) + return NULL; + + n->type = type; + n->refcount = 1; + return n; +} + +void filter_unref_node(struct filter_node *n) +{ + if (!n || --n->refcount > 0) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_free_expr((struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_free_param((struct filter_param *) n); + break; + } +} + +void filter_ref_node(struct filter_node *n) +{ + if (n) + n->refcount++; +} + +void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n) +{ + if (!n) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_dump_expr(json, (struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_dump_param(json, (struct filter_param *) n); + break; + } +} + +/** + * scols_filter_parse_string: + * @fltr: filter instance + * @str: string with filter expression + * + * Parses filter, see scols_filter_get_errmsg() for errors. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_parse_string(struct libscols_filter *fltr, const char *str) +{ + yyscan_t sc; + int rc; + + reset_filter(fltr); + + if (!str || !*str) + return 0; /* empty filter is not error */ + + fltr->src = fmemopen((void *) str, strlen(str), "r"); + if (!fltr->src) + return -errno; + + yylex_init(&sc); + yyset_in(fltr->src, sc); + + rc = yyparse(sc, fltr); + yylex_destroy(sc); + + fclose(fltr->src); + fltr->src = NULL; + + ON_DBG(FLTR, scols_dump_filter(fltr, stderr)); + + return rc; +} + +/** + * scols_dump_filter: + * @fltr: filter instance + * @out: output stream + * + * Dumps internal filter nodes in JSON format. This function is mostly designed + * for debugging purpose. The fileds in the output are subject to change. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_dump_filter(struct libscols_filter *fltr, FILE *out) +{ + struct ul_jsonwrt json; + + if (!fltr || !out) + return -EINVAL; + + ul_jsonwrt_init(&json, out, 0); + ul_jsonwrt_root_open(&json); + + filter_dump_node(&json, fltr->root); + ul_jsonwrt_root_close(&json); + return 0; +} + +/** + * scols_filter_get_errmsg: + * @fltr: filter instance + * + * Returns: string with parse-error message of NULL (if no error) + * + * Since: 2.40 + */ +const char *scols_filter_get_errmsg(struct libscols_filter *fltr) +{ + return fltr ? fltr->errmsg : NULL; +} + +int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_node *n, int *status) +{ + switch (n->type) { + case F_NODE_PARAM: + return filter_eval_param(fltr, ln, (struct filter_param *) n, status); + case F_NODE_EXPR: + return filter_eval_expr(fltr, ln, (struct filter_expr *) n, status); + default: + break; + } + return -EINVAL; +} + +/** + * scols_line_apply_filter: + * @ln: apply filter to the line + * @fltr: filter instance + * @status: return 1 or 0 as result of the expression + * + * Applies filter (and also counters assisiated with the filter). + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_line_apply_filter(struct libscols_line *ln, + struct libscols_filter *fltr, int *status) +{ + int rc, res = 0; + struct libscols_iter itr; + struct filter_param *prm = NULL; + + if (!ln || !fltr) + return -EINVAL; + + /* reset column data and types stored in the filter */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (filter_next_param(fltr, &itr, &prm) == 0) { + filter_param_reset_holder(prm); + } + + if (fltr->root) + rc = filter_eval_node(fltr, ln, fltr->root, &res); + else + rc = 0, res = 1; /* empty filter matches all lines */ + + if (rc == 0) { + struct libscols_counter *ct = NULL; + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_filter_next_counter(fltr, &itr, &ct) == 0) { + if ((ct->neg && res == 0) || res == 1) + filter_count_param(fltr, ln, ct); + } + } + + if (status) + *status = res; + DBG(FLTR, ul_debugobj(fltr, "filter done [rc=%d, status=%d]", rc, res)); + return rc; +} + +/** + * scols_filter_set_filler_cb: + * @fltr: filter instance + * @cb: application defined callback + * @userdata: pointer to private callback data + * + * The application can apply filter for empty lines to avoid filling the table + * with unnecessary data (for example if the line will be later removed from + * the table due to filter). + * + * This callback is used by filter to ask application to fill to the line data + * which are necessary to evaluate the filter expression. The callback + * arguments are filter, column number and userdata. + * + * <informalexample> + * <programlisting> + * ln = scols_table_new_line(tab, NULL); + * + * scols_filter_set_filler_cb(filter, my_filler, NULL); + * + * scols_line_apply_filter(line, filter, &status); + * if (status == 0) + * scols_table_remove_line(tab, line); + * else for (i = 0; i < ncolumns; i++) { + * if (scols_line_is_filled(line, i)) + * continue; + * my_filler(NULL, ln, i, NULL); + * } + * </programlisting> + * </informalexample> + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_set_filler_cb(struct libscols_filter *fltr, + int (*cb)(struct libscols_filter *, + struct libscols_line *, size_t, void *), + void *userdata) +{ + if (!fltr) + return -EINVAL; + fltr->filler_cb = cb; + fltr->filler_data = userdata; + + return 0; +} + +/** + * scols_filter_new_counter: + * @fltr: filter instance + * + * Alocates a new counter instance into the filter. + * + * Returns: new counter or NULL in case of an error. + * + * Since: 2.40 + */ +struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr) +{ + struct libscols_counter *ct; + + if (!fltr) + return NULL; + + ct = calloc(1, sizeof(*ct)); + if (!ct) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc counter")); + + ct->filter = fltr; /* don't use ref.counting here */ + INIT_LIST_HEAD(&ct->counters); + list_add_tail(&ct->counters, &fltr->counters); + + + return ct; +} + +/** + * scols_counter_set_name: + * @ct: counter instance + * @name: something for humans + * + * The name is not use by library, it's just description usable for application + * when prints results from countes. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_name(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + return strdup_to_struct_member(ct, name, name); +} + +/** + * scols_counter_set_param: + * @ct: counter instance + * @name: holder (column) name + * + * Assigns a counter to the column. The name is used in the same way as names + * in the filter expression. This is usable for counter that calcuate with data + * from table cells (e.g. max, sum, etc.) + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_param(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + + if (ct->param) { + filter_unref_node((struct filter_node *) ct->param); + ct->param = NULL; + } + if (name) { + ct->param = (struct filter_param *) + filter_new_param(ct->filter, SCOLS_DATA_U64, + F_HOLDER_COLUMN, (void *) name); + if (!ct->param) + return -ENOMEM; + } + return 0; +} + +/** + * scols_counter_set_func: + * @ct: counter instance + * @func: SCOLS_COUNTER_{COUNT,MAX,MIN,SUM} + * + * Defines function to calculate data. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_func(struct libscols_counter *ct, int func) +{ + if (!ct || func < 0 || func >= __SCOLS_NCOUNTES) + return -EINVAL; + + ct->func = func; + return 0; +} + +/** + * scols_counter_get_result: + * @ct: counter instance + * + * Returns: result from the counter + * + * Since: 2.40 + */ +unsigned long long scols_counter_get_result(struct libscols_counter *ct) +{ + return ct ? ct->result : 0; +} + +/** + * scols_counter_get_name: + * @ct: counter instance + * + * Returns: name of the counter. + * + * Since: 2.40 + */ +const char *scols_counter_get_name(struct libscols_counter *ct) +{ + return ct ? ct->name : NULL;; +} + +/** + * scols_filter_next_counter: + * @fltr: filter instance + * @itr: a pointer to a struct libscols_iter instance + * @ct: returns the next counter + * + * Finds the next counter and returns a pointer to it via @ct. + * + * Returns: 0, a negative value in case of an error, and 1 at the end. + * + * Since: 2.40 + */ +int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct) +{ + int rc = 1; + + if (!fltr || !itr || !ct) + return -EINVAL; + *ct = NULL; + + if (!itr->head) + SCOLS_ITER_INIT(itr, &fltr->counters); + if (itr->p != itr->head) { + SCOLS_ITER_ITERATE(itr, *ct, struct libscols_counter, counters); + rc = 0; + } + + return rc; +} diff --git a/libsmartcols/src/grouping.c b/libsmartcols/src/grouping.c index 0b27cb2..0f6fe78 100644 --- a/libsmartcols/src/grouping.c +++ b/libsmartcols/src/grouping.c @@ -278,7 +278,7 @@ static struct libscols_group **grpset_locate_freespace(struct libscols_table *tb DBG(TAB, ul_debugobj(tb, " realocate grpset [sz: old=%zu, new=%zu, new_chunks=%d]", tb->grpset_size, tb->grpset_size + wanted, chunks)); - tmp = realloc(tb->grpset, (tb->grpset_size + wanted) * sizeof(struct libscols_group *)); + tmp = reallocarray(tb->grpset, tb->grpset_size + wanted, sizeof(struct libscols_group *)); if (!tmp) return NULL; diff --git a/libsmartcols/src/init.c b/libsmartcols/src/init.c index dfd7510..5d51691 100644 --- a/libsmartcols/src/init.c +++ b/libsmartcols/src/init.c @@ -28,6 +28,8 @@ UL_DEBUG_DEFINE_MASKNAMES(libsmartcols) = { "group", SCOLS_DEBUG_GROUP, "lines grouping utils" }, { "line", SCOLS_DEBUG_LINE, "table line utils" }, { "tab", SCOLS_DEBUG_TAB, "table utils" }, + { "filter", SCOLS_DEBUG_FLTR, "lines filter" }, + { "fparam", SCOLS_DEBUG_FPARAM, "filter params" }, { NULL, 0, NULL } }; diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index f5820e9..c97651f 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -67,6 +67,29 @@ struct libscols_table; */ struct libscols_column; +/** + * libscols_filter: + * + * A filter - defines the filtering + */ +struct libscols_filter; + +/** + * libscols_counter: + * + * A filter counter + */ +struct libscols_counter; + +enum { + SCOLS_COUNTER_COUNT = 0, + SCOLS_COUNTER_MAX, + SCOLS_COUNTER_MIN, + SCOLS_COUNTER_SUM, + + __SCOLS_NCOUNTES +}; + /* iter.c */ enum { @@ -97,6 +120,18 @@ enum { SCOLS_JSON_ARRAY_STRING = 3, /* e.g. for multi-line (SCOLS_FL_WRAP) cells */ SCOLS_JSON_ARRAY_NUMBER = 4, SCOLS_JSON_BOOLEAN_OPTIONAL = 5, + SCOLS_JSON_FLOAT = 6 +}; + +/* + * Types used by filters and counters + */ +enum { + SCOLS_DATA_NONE = 0, /* default */ + SCOLS_DATA_U64, /* uint64_t */ + SCOLS_DATA_BOOLEAN, /* 0 or 1 */ + SCOLS_DATA_FLOAT, /* long double */ + SCOLS_DATA_STRING }; /* @@ -146,7 +181,11 @@ extern int scols_cell_copy_content(struct libscols_cell *dest, const struct libscols_cell *src); extern int scols_cell_set_data(struct libscols_cell *ce, const char *data); extern int scols_cell_refer_data(struct libscols_cell *ce, char *data); +extern int scols_cell_refer_memory(struct libscols_cell *ce, char *data, size_t datasiz); + extern const char *scols_cell_get_data(const struct libscols_cell *ce); +extern size_t scols_cell_get_datasiz(struct libscols_cell *ce); + extern int scols_cell_set_color(struct libscols_cell *ce, const char *color); extern const char *scols_cell_get_color(const struct libscols_cell *ce); @@ -159,6 +198,7 @@ extern int scols_cell_set_userdata(struct libscols_cell *ce, void *data); extern int scols_cmpstr_cells(struct libscols_cell *a, struct libscols_cell *b, void *data); + /* column.c */ extern int scols_column_is_tree(const struct libscols_column *cl); extern int scols_column_is_trunc(const struct libscols_column *cl); @@ -177,6 +217,9 @@ extern const char *scols_column_get_safechars(const struct libscols_column *cl); extern int scols_column_set_json_type(struct libscols_column *cl, int type); extern int scols_column_get_json_type(const struct libscols_column *cl); +extern int scols_column_set_data_type(struct libscols_column *cl, int type); +extern int scols_column_get_data_type(const struct libscols_column *cl); + extern int scols_column_set_flags(struct libscols_column *cl, int flags); extern int scols_column_get_flags(const struct libscols_column *cl); extern struct libscols_column *scols_new_column(void); @@ -193,6 +236,7 @@ extern struct libscols_table *scols_column_get_table(const struct libscols_colum extern int scols_column_set_name(struct libscols_column *cl, const char *name); extern const char *scols_column_get_name(struct libscols_column *cl); extern const char *scols_column_get_name_as_shellvar(struct libscols_column *cl); +extern int scols_shellvar_name(const char *name, char **buf, size_t *bufsz); extern int scols_column_set_properties(struct libscols_column *cl, const char *opts); @@ -208,9 +252,21 @@ extern int scols_column_set_wrapfunc(struct libscols_column *cl, char *, void *), void *userdata); +extern int scols_column_set_data_func(struct libscols_column *cl, + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *), + void *userdata); +extern int scols_column_has_data_func(struct libscols_column *cl); + extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata); extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata); +extern char *scols_wrapzero_nextchunk(const struct libscols_column *cl, char *data, void *userdata); + +extern int scols_column_get_wrap_data(const struct libscols_column *cl, + char **data, size_t *datasiz, char **cur, char **next); + /* line.c */ extern struct libscols_line *scols_new_line(void); extern void scols_ref_line(struct libscols_line *ln); @@ -235,6 +291,7 @@ extern struct libscols_cell *scols_line_get_column_cell( struct libscols_column *cl); extern int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data); extern int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data); +extern int scols_line_is_filled(struct libscols_line *ln, size_t n); extern int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data); extern const char *scols_line_get_column_data(struct libscols_line *ln, struct libscols_column *cl); extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data); @@ -310,6 +367,12 @@ extern int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce extern int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl); extern int scols_sort_table_by_tree(struct libscols_table *tb); + +extern int scols_table_get_cursor(struct libscols_table *tb, + struct libscols_line **ln, + struct libscols_column **cl, + struct libscols_cell **ce); + /* * */ @@ -342,6 +405,38 @@ extern int scols_table_print_range_to_string( struct libscols_table *tb, int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member, int id); int scols_table_group_lines(struct libscols_table *tb, struct libscols_line *ln, struct libscols_line *member, int id); + +/* filter.c */ +extern int scols_filter_parse_string(struct libscols_filter *fltr, const char *str); +extern struct libscols_filter *scols_new_filter(const char *str); +extern void scols_ref_filter(struct libscols_filter *fltr); +extern void scols_unref_filter(struct libscols_filter *fltr); +extern int scols_dump_filter(struct libscols_filter *fltr, FILE *out); +extern const char *scols_filter_get_errmsg(struct libscols_filter *fltr); + +extern int scols_line_apply_filter(struct libscols_line *ln, + struct libscols_filter *fltr, int *status); + +extern int scols_filter_next_holder(struct libscols_filter *fltr, + struct libscols_iter *itr, const char **name, int type); +extern int scols_filter_assign_column(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char *name, struct libscols_column *col); +extern int scols_filter_set_filler_cb(struct libscols_filter *fltr, + int (*cb)(struct libscols_filter *, + struct libscols_line *, size_t, void *), + void *userdata); + +extern struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr); +extern int scols_counter_set_name(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_param(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_func(struct libscols_counter *ct, int func); + +extern unsigned long long scols_counter_get_result(struct libscols_counter *ct); +extern const char *scols_counter_get_name(struct libscols_counter *ct); +extern int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct); + #ifdef __cplusplus } #endif diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index 4499908..41c7455 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -210,8 +210,37 @@ SMARTCOLS_2.38 { scols_table_enable_shellvar; } SMARTCOLS_2.35; - SMARTCOLS_2.39 { scols_column_set_properties; scols_table_get_column_by_name; } SMARTCOLS_2.38; + +SMARTCOLS_2.40 { + scols_table_get_cursor; + scols_cell_refer_memory; + scols_cell_get_datasiz; + scols_wrapzero_nextchunk; + scols_column_get_wrap_data; + scols_dump_filter; + scols_filter_parse_string; + scols_new_filter; + scols_unref_filter; + scols_filter_get_errmsg; + scols_filter_next_holder; + scols_filter_assign_column; + scols_line_apply_filter; + scols_filter_set_filler_cb; + scols_line_is_filled; + scols_filter_new_counter; + scols_counter_set_name; + scols_counter_set_param; + scols_counter_set_func; + scols_counter_get_result; + scols_counter_get_name; + scols_filter_next_counter; + scols_shellvar_name; + scols_column_set_data_func; + scols_column_has_data_func; + scols_column_set_data_type; + scols_column_get_data_type; +} SMARTCOLS_2.39; diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c index cab99c5..2289db0 100644 --- a/libsmartcols/src/line.c +++ b/libsmartcols/src/line.c @@ -136,7 +136,7 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n) DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n)); - ce = realloc(ln->cells, n * sizeof(struct libscols_cell)); + ce = reallocarray(ln->cells, n, sizeof(struct libscols_cell)); if (!ce) return -errno; @@ -505,6 +505,20 @@ int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data) } /** + * scols_line_is_filled: + * @ln: a pointer to a struct libscols_line instance + * @n: number of the cell + * + * Returns: 0 or 1 if cell was already filled (note that NULL is also valid filler) + */ +int scols_line_is_filled(struct libscols_line *ln, size_t n) +{ + struct libscols_cell *ce = scols_line_get_cell(ln, n); + + return ce ? ce->is_filled : 0; +} + +/** * scols_line_refer_column_data: * @ln: a pointer to a struct libscols_line instance * @cl: column, whose data is to be set diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index 6a7e6da..88ab5a2 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -232,21 +232,6 @@ static int groups_ascii_art_to_buffer( struct libscols_table *tb, return 0; } -static int has_pending_data(struct libscols_table *tb) -{ - struct libscols_column *cl; - struct libscols_iter itr; - - scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (scols_table_next_column(tb, &itr, &cl) == 0) { - if (scols_column_is_hidden(cl)) - continue; - if (cl->pending_data) - return 1; - } - return 0; -} - static void fputs_color_reset(struct libscols_table *tb) { if (tb->cur_color) { @@ -349,7 +334,7 @@ static void print_empty_cell(struct libscols_table *tb, tree_ascii_art_to_buffer(tb, ln, &art); - if (!list_empty(&ln->ln_branch) && has_pending_data(tb)) + if (!list_empty(&ln->ln_branch)) ul_buffer_append_string(&art, vertical_symbol(tb)); if (scols_table_is_noencoding(tb)) @@ -423,95 +408,43 @@ static void print_newline_padding(struct libscols_table *tb, fputs_color_line_close(tb); } -/* - * Pending data - * - * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is - * printed as usually and output is truncated to match column width. - * - * The rest of the long text is printed on next extra line(s). The extra lines - * don't exist in the table (not represented by libscols_line). The data for - * the extra lines are stored in libscols_column->pending_data_buf and the - * function print_line() adds extra lines until the buffer is not empty in all - * columns. - */ - -/* set data that will be printed by extra lines */ -static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz) +static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf) { - char *p = NULL; - - if (data && *data) { - DBG(COL, ul_debugobj(cl, "setting pending data")); - assert(sz); - p = strdup(data); - if (!p) - return -ENOMEM; - } - - free(cl->pending_data_buf); - cl->pending_data_buf = p; - cl->pending_data_sz = sz; - cl->pending_data = cl->pending_data_buf; - return 0; -} - -/* the next extra line has been printed, move pending data cursor */ -static int step_pending_data(struct libscols_column *cl, size_t bytes) -{ - DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes)); - - if (bytes >= cl->pending_data_sz) - return set_pending_data(cl, NULL, 0); - - cl->pending_data += bytes; - cl->pending_data_sz -= bytes; - return 0; -} - -/* print next pending data for the column @cl */ -static int print_pending_data( - struct libscols_table *tb, - struct libscols_column *cl, - struct libscols_line *ln, /* optional */ - struct libscols_cell *ce) -{ - size_t width = cl->width, bytes; - size_t len = width, i; + struct libscols_line *ln; + struct libscols_column *cl; + struct libscols_cell *ce; char *data; - char *nextchunk = NULL; + size_t i, width = 0, len = 0, bytes = 0; - if (!cl->pending_data) - return 0; + scols_table_get_cursor(tb, &ln, &cl, &ce); + + width = cl->width; if (!width) return -EINVAL; DBG(COL, ul_debugobj(cl, "printing pending data")); - data = strdup(cl->pending_data); + if (scols_table_is_noencoding(tb)) + data = ul_buffer_get_data(buf, &bytes, &len); + else + data = ul_buffer_get_safe_data(buf, &bytes, &len, scols_column_get_safechars(cl)); + if (!data) - goto err; + return 0; - if (scols_column_is_customwrap(cl) - && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { - bytes = nextchunk - data; + /* standard multi-line cell */ + if (len > width && scols_column_is_wrap(cl) + && !scols_column_is_customwrap(cl)) { - len = scols_table_is_noencoding(tb) ? - mbs_nwidth(data, bytes) : - mbs_safe_nwidth(data, bytes, NULL); - } else + len = width; bytes = mbs_truncate(data, &len); - if (bytes == (size_t) -1) - goto err; - - if (bytes) - step_pending_data(cl, bytes); + if (bytes != (size_t) -1 && bytes > 0) + scols_column_move_wrap(cl, mbs_safe_decode_size(data)); + } fputs_color_cell_open(tb, cl, ln, ce); - fputs(data, tb->out); - free(data); /* minout -- don't fill */ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) { @@ -535,9 +468,6 @@ static int print_pending_data( fputs(colsep(tb), tb->out); return 0; -err: - free(data); - return -errno; } static void print_json_data(struct libscols_table *tb, @@ -551,6 +481,7 @@ static void print_json_data(struct libscols_table *tb, ul_jsonwrt_value_s(&tb->json, name, data); break; case SCOLS_JSON_NUMBER: + case SCOLS_JSON_FLOAT: /* name: 123 */ ul_jsonwrt_value_raw(&tb->json, name, data); break; @@ -574,47 +505,45 @@ static void print_json_data(struct libscols_table *tb, if (!scols_column_is_customwrap(cl)) ul_jsonwrt_value_s(&tb->json, NULL, data); else do { - char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data); - - if (cl->json_type == SCOLS_JSON_ARRAY_STRING) - ul_jsonwrt_value_s(&tb->json, NULL, data); - else - ul_jsonwrt_value_raw(&tb->json, NULL, data); - data = next; - } while (data); + if (cl->json_type == SCOLS_JSON_ARRAY_STRING) + ul_jsonwrt_value_s(&tb->json, NULL, data); + else + ul_jsonwrt_value_raw(&tb->json, NULL, data); + } while (scols_column_next_wrap(cl, NULL, &data) == 0); ul_jsonwrt_array_close(&tb->json); break; } } -static int print_data(struct libscols_table *tb, - struct libscols_column *cl, - struct libscols_line *ln, /* optional */ - struct libscols_cell *ce, /* optional */ - struct ul_buffer *buf) +static int print_data(struct libscols_table *tb, struct ul_buffer *buf) { + struct libscols_line *ln; /* NULL for header line! */ + struct libscols_column *cl; + struct libscols_cell *ce; size_t len = 0, i, width, bytes; - char *data, *nextchunk; + char *data = NULL; const char *name = NULL; int is_last; assert(tb); - assert(cl); - data = ul_buffer_get_data(buf, NULL, NULL); - if (!data) - data = ""; + scols_table_get_cursor(tb, &ln, &cl, &ce); + assert(cl); if (tb->format != SCOLS_FMT_HUMAN) { name = scols_table_is_shellvar(tb) ? scols_column_get_name_as_shellvar(cl) : scols_column_get_name(cl); + + data = ul_buffer_get_data(buf, NULL, NULL); + if (!data) + data = ""; } is_last = is_last_column(cl); - if (is_last && scols_table_is_json(tb) && + if (ln && is_last && scols_table_is_json(tb) && scols_table_is_tree(tb) && has_children(ln)) /* "children": [] is the real last value */ is_last = 0; @@ -642,7 +571,7 @@ static int print_data(struct libscols_table *tb, break; /* continue below */ } - /* Encode. Note that 'len' and 'width' are number of cells, not bytes. + /* Encode. Note that 'len' and 'width' are number of glyphs not bytes. */ if (scols_table_is_noencoding(tb)) data = ul_buffer_get_data(buf, &bytes, &len); @@ -653,17 +582,6 @@ static int print_data(struct libscols_table *tb, data = ""; width = cl->width; - /* custom multi-line cell based */ - if (*data && scols_column_is_customwrap(cl) - && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { - set_pending_data(cl, nextchunk, bytes - (nextchunk - data)); - bytes = nextchunk - data; - - len = scols_table_is_noencoding(tb) ? - mbs_nwidth(data, bytes) : - mbs_safe_nwidth(data, bytes, NULL); - } - if (is_last && len < width && !scols_table_is_maxout(tb) @@ -679,12 +597,12 @@ static int print_data(struct libscols_table *tb, /* standard multi-line cell */ if (len > width && scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)) { - set_pending_data(cl, data, bytes); len = width; bytes = mbs_truncate(data, &len); - if (bytes != (size_t) -1 && bytes > 0) - step_pending_data(cl, bytes); + + if (bytes != (size_t) -1 && bytes > 0) + scols_column_move_wrap(cl, mbs_safe_decode_size(data)); } if (bytes == (size_t) -1) { @@ -701,7 +619,6 @@ static int print_data(struct libscols_table *tb, len = width; } fputs(data, tb->out); - } /* minout -- don't fill */ @@ -732,16 +649,24 @@ static int print_data(struct libscols_table *tb, return 0; } -int __cell_to_buffer(struct libscols_table *tb, - struct libscols_line *ln, - struct libscols_column *cl, - struct ul_buffer *buf) +/* + * Copy current cell data to buffer. The @cal means "calculation" phase. + */ +int __cursor_to_buffer(struct libscols_table *tb, + struct ul_buffer *buf, + int cal) { - const char *data; + const char *data = NULL; + size_t datasiz = 0; struct libscols_cell *ce; + struct libscols_line *ln; + struct libscols_column *cl; int rc = 0; assert(tb); + + scols_table_get_cursor(tb, &ln, &cl, &ce); + assert(ln); assert(cl); assert(buf); @@ -749,11 +674,8 @@ int __cell_to_buffer(struct libscols_table *tb, ul_buffer_reset_data(buf); - ce = scols_line_get_cell(ln, cl->seqnum); - data = ce ? scols_cell_get_data(ce) : NULL; - if (!scols_column_is_tree(cl)) - return data ? ul_buffer_append_string(buf, data) : 0; + goto notree; /* * Group stuff @@ -775,9 +697,79 @@ int __cell_to_buffer(struct libscols_table *tb, if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb)) ul_buffer_save_pointer(buf, SCOLS_BUFPTR_TREEEND); +notree: + if (!rc && ce) { + int do_wrap = scols_column_is_wrap(cl); + + /* Disable multi-line cells for "raw" and "export" formats. + * JSON uses data wrapping to generate arrays */ + if (do_wrap && (tb->format == SCOLS_FMT_RAW || + tb->format == SCOLS_FMT_EXPORT)) + do_wrap = 0; + + /* Wrapping enabled; append the next chunk if cell data */ + if (do_wrap) { + char *x = NULL; + + rc = cal ? scols_column_greatest_wrap(cl, ce, &x) : + scols_column_next_wrap(cl, ce, &x); + /* rc: error: <0; nodata: 1; success: 0 */ + if (rc < 0) + goto done; + data = x; + rc = 0; + if (data && *data) + datasiz = strlen(data); + if (data && datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + + /* Wrapping disabled, but data maintained by custom wrapping + * callback. Try to use data as a string, if not possible, + * append all chunks separated by \n (backward compatibility). + * */ + } else if (scols_column_is_customwrap(cl)) { + size_t len; + int i = 0; + char *x = NULL; + + data = scols_cell_get_data(ce); + datasiz = scols_cell_get_datasiz(ce); + len = data ? strnlen(data, datasiz) : 0; + + if (len && len + 1 == datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + + else while (scols_column_next_wrap(cl, ce, &x) == 0) { + /* non-string data in cell, use a nextchunk callback */ + if (!x) + continue; + datasiz = strlen(x); + if (i) + rc = ul_buffer_append_data(buf, "\n", 1); + if (!rc) + rc = ul_buffer_append_data(buf, x, datasiz); + i++; + } + + /* Wrapping disabled; let's use data as a classic string. */ + } else { + data = scols_cell_get_data(ce); + datasiz = scols_cell_get_datasiz(ce); + + if (data && *data && !datasiz) + datasiz = strlen(data); /* cell content may be updated */ - if (!rc && data) - rc = ul_buffer_append_string(buf, data); + if (data && datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + } + } + + /* reset wrapping after greatest chunk calculation */ + if (cal && scols_column_is_wrap(cl)) + scols_column_reset_wrap(cl); + +done: + DBG(COL, ul_debugobj(cl, "__cursor_to_buffer rc=%d", rc)); return rc; } @@ -801,16 +793,18 @@ static int print_line(struct libscols_table *tb, /* regular line */ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - rc = __cell_to_buffer(tb, ln, cl, buf); - if (rc == 0) - rc = print_data(tb, cl, ln, - scols_line_get_cell(ln, cl->seqnum), - buf); - if (rc == 0 && cl->pending_data) + + scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum)); + rc = __cursor_to_buffer(tb, buf, 0); + if (!rc) + rc = print_data(tb, buf); + if (!rc && scols_column_has_pending_wrap(cl)) pending = 1; + scols_table_reset_cursor(tb); } fputs_color_line_close(tb); @@ -821,16 +815,25 @@ static int print_line(struct libscols_table *tb, fputs(linesep(tb), tb->out); fputs_color_line_open(tb, ln); tb->termlines_used++; + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - if (cl->pending_data) { - rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum)); - if (rc == 0 && cl->pending_data) + + scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum)); + if (scols_column_has_pending_wrap(cl)) { + rc = __cursor_to_buffer(tb, buf, 0); + if (!rc) + rc = print_pending_data(tb, buf); + if (!rc && scols_column_has_pending_wrap(cl)) pending = 1; + if (!rc && !pending) + scols_column_reset_wrap(cl); } else print_empty_cell(tb, cl, ln, NULL, ul_buffer_get_bufsiz(buf)); + scols_table_reset_cursor(tb); } fputs_color_line_close(tb); } @@ -963,6 +966,7 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf) continue; ul_buffer_reset_data(buf); + scols_table_set_cursor(tb, NULL, cl, &cl->header); if (cl->is_groups && scols_table_is_tree(tb) && scols_column_is_tree(cl)) { @@ -979,7 +983,8 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf) scols_column_get_name_as_shellvar(cl) : scols_column_get_name(cl)); if (!rc) - rc = print_data(tb, cl, NULL, &cl->header, buf); + rc = print_data(tb, buf); + scols_table_reset_cursor(tb); } if (rc == 0) { diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 8a7ee9b..75fb7ff 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -19,6 +19,8 @@ #include "debug.h" #include "buffer.h" +#include <stdbool.h> + #include "libsmartcols.h" /* @@ -32,6 +34,8 @@ #define SCOLS_DEBUG_COL (1 << 5) #define SCOLS_DEBUG_BUFF (1 << 6) #define SCOLS_DEBUG_GROUP (1 << 7) +#define SCOLS_DEBUG_FLTR (1 << 8) +#define SCOLS_DEBUG_FPARAM (1 << 9) #define SCOLS_DEBUG_ALL 0xFFFF UL_DEBUG_DECLARE_MASK(libsmartcols); @@ -80,10 +84,13 @@ struct libscols_symbols { */ struct libscols_cell { char *data; + size_t datasiz; char *color; void *userdata; int flags; size_t width; + + unsigned int is_filled : 1; }; extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn); @@ -112,26 +119,32 @@ struct libscols_column { struct libscols_wstat wstat; /* private __scols_calculate() data */ int json_type; /* SCOLS_JSON_* */ + int data_type; /* SCOLS_DATA_* */ int flags; char *color; /* default column color */ char *safechars; /* do not encode this bytes */ - char *pending_data; - size_t pending_data_sz; - char *pending_data_buf; - int (*cmpfunc)(struct libscols_cell *, struct libscols_cell *, void *); /* cells comparison function */ void *cmpfunc_data; - size_t (*wrap_chunksize)(const struct libscols_column *, - const char *, void *); - char *(*wrap_nextchunk)(const struct libscols_column *, - char *, void *); + /* multi-line cell data wrapping */ + char *(*wrap_nextchunk)(const struct libscols_column *, char *, void *); void *wrapfunc_data; + size_t wrap_datasz; + size_t wrap_datamax; + char *wrap_data; + char *wrap_cur; + char *wrap_next; + struct libscols_cell *wrap_cell; + + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *); + void *datafunc_data; struct libscols_cell header; /* column name with color etc. */ char *shellvar; /* raw colum name in shell compatible format */ @@ -247,6 +260,10 @@ struct libscols_table { const char *cur_color; /* current active color when printing */ + struct libscols_cell *cur_cell; /* currently used cell */ + struct libscols_line *cur_line; /* currently used line */ + struct libscols_column *cur_column; /* currently used column */ + /* flags */ unsigned int ascii :1, /* don't use unicode */ colors_wanted :1, /* enable colors */ @@ -299,6 +316,18 @@ int scols_line_next_group_child(struct libscols_line *ln, struct libscols_iter *itr, struct libscols_line **chld); +/* + * column.c + */ +void scols_column_reset_wrap(struct libscols_column *cl); +int scols_column_next_wrap( struct libscols_column *cl, + struct libscols_cell *ce, + char **data); +int scols_column_greatest_wrap( struct libscols_column *cl, + struct libscols_cell *ce, + char **data); +int scols_column_has_pending_wrap(struct libscols_column *cl); +int scols_column_move_wrap(struct libscols_column *cl, size_t bytes); /* * table.c @@ -306,6 +335,13 @@ int scols_line_next_group_child(struct libscols_line *ln, int scols_table_next_group(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_group **gr); +int scols_table_set_cursor(struct libscols_table *tb, + struct libscols_line *ln, + struct libscols_column *cl, + struct libscols_cell *ce); + +#define scols_table_reset_cursor(_t) scols_table_set_cursor((_t), NULL, NULL, NULL) + /* * grouping.c @@ -339,10 +375,9 @@ extern int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf); /* * print.c */ -extern int __cell_to_buffer(struct libscols_table *tb, - struct libscols_line *ln, - struct libscols_column *cl, - struct ul_buffer *buf); +int __cursor_to_buffer(struct libscols_table *tb, + struct ul_buffer *buf, + int cal); void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf); int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf); @@ -452,4 +487,124 @@ static inline int has_group_children(struct libscols_line *ln) return ln && ln->group && !list_empty(&ln->group->gr_children); } +/* + * Filter stuff + */ +enum filter_holder { + F_HOLDER_NONE, + F_HOLDER_COLUMN /* column name */ +}; + +/* node types */ +enum filter_ntype { + F_NODE_PARAM, + F_NODE_EXPR +}; + +/* expresion types */ +enum filter_etype { + F_EXPR_AND, + F_EXPR_OR, + F_EXPR_NEG, + + F_EXPR_EQ, + F_EXPR_NE, + + F_EXPR_LT, + F_EXPR_LE, + F_EXPR_GT, + F_EXPR_GE, + + F_EXPR_REG, + F_EXPR_NREG, +}; + +struct filter_node { + enum filter_ntype type; + int refcount; +}; + +#define filter_node_get_type(n) (((struct filter_node *)(n))->type) + +struct filter_param; +struct filter_expr; + +struct libscols_counter { + char *name; + struct list_head counters; + struct filter_param *param; + struct libscols_filter *filter; + + int func; + unsigned long long result; + + unsigned int neg : 1, + has_result : 1; +}; + +struct libscols_filter { + int refcount; + char *errmsg; + struct filter_node *root; + FILE *src; + + int (*filler_cb)(struct libscols_filter *, struct libscols_line *, size_t, void *); + void *filler_data; + + struct list_head params; + struct list_head counters; +}; + +struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz); +void filter_ref_node(struct filter_node *n); +void filter_unref_node(struct filter_node *n); + +void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n); +int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_node *n, int *status); +/* param */ +int filter_compile_param(struct libscols_filter *fltr, struct filter_param *n); +void filter_dump_param(struct ul_jsonwrt *json, struct filter_param *n); +int filter_eval_param(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_param *n, int *status); +void filter_free_param(struct filter_param *n); +int filter_param_reset_holder(struct filter_param *n); +int filter_param_get_datatype(struct filter_param *n); + +int filter_next_param(struct libscols_filter *fltr, + struct libscols_iter *itr, struct filter_param **prm); + +int filter_compare_params(struct libscols_filter *fltr, + enum filter_etype oper, + struct filter_param *l, + struct filter_param *r, + int *status); +int filter_cast_param(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_param *n, + struct filter_param **result); + +int is_filter_holder_node(struct filter_node *n); + +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct); + +/* expr */ +void filter_free_expr(struct filter_expr *n); +void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n); +int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_expr *n, int *status); + +/* required by parser */ +struct filter_node *filter_new_param(struct libscols_filter *filter, + int type, + enum filter_holder holder, + void *data); +struct filter_node *filter_new_expr(struct libscols_filter *filter, + enum filter_etype type, + struct filter_node *left, + struct filter_node *right); + #endif /* _LIBSMARTCOLS_PRIVATE_H */ diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index 8449c4f..3d23da8 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -521,6 +521,50 @@ size_t scols_table_get_nlines(const struct libscols_table *tb) return tb->nlines; } + +int scols_table_set_cursor(struct libscols_table *tb, + struct libscols_line *ln, + struct libscols_column *cl, + struct libscols_cell *ce) +{ + if (!tb) + return -EINVAL; + + tb->cur_line = ln; + tb->cur_column = cl; + tb->cur_cell = ce; + + return 0; +} + +/** + * scols_table_get_cursor: + * @tb: table + * @ln: returns current line (optional) + * @cl: returns current column (optional) + * @ce: returns current cell (optional) + * + * Returns: 0 on success, negative number in case of error. + * + * Since: 2.40 + */ +int scols_table_get_cursor(struct libscols_table *tb, + struct libscols_line **ln, + struct libscols_column **cl, + struct libscols_cell **ce) +{ + if (!tb) + return -EINVAL; + + if (ln) + *ln = tb->cur_line; + if (cl) + *cl = tb->cur_column; + if (ce) + *ce = tb->cur_cell; + return 0; +} + /** * scols_table_set_stream: * @tb: table @@ -632,6 +676,15 @@ struct libscols_column *scols_table_get_column_by_name( if (cn && strcmp(cn, name) == 0) return cl; } + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_column(tb, &itr, &cl) == 0) { + const char *cn = scols_column_get_name_as_shellvar(cl); + + if (cn && strcmp(cn, name) == 0) + return cl; + } + return NULL; } |