summaryrefslogtreecommitdiffstats
path: root/libsmartcols
diff options
context:
space:
mode:
Diffstat (limited to 'libsmartcols')
-rw-r--r--libsmartcols/Makemodule.am3
-rw-r--r--libsmartcols/docs/Makefile.in14
-rw-r--r--libsmartcols/docs/libsmartcols-docs.xml13
-rw-r--r--libsmartcols/docs/libsmartcols-sections.txt34
-rw-r--r--libsmartcols/docs/version.xml2
-rw-r--r--libsmartcols/meson.build21
-rw-r--r--libsmartcols/samples/Makemodule.am8
-rw-r--r--libsmartcols/samples/continuous-json.c80
-rw-r--r--libsmartcols/samples/continuous.c4
-rw-r--r--libsmartcols/samples/fromfile.c259
-rw-r--r--libsmartcols/samples/maxout.c2
-rw-r--r--libsmartcols/samples/wrap.c10
-rw-r--r--libsmartcols/scols-filter.5137
-rw-r--r--libsmartcols/scols-filter.5.adoc121
-rw-r--r--libsmartcols/src/Makemodule.am64
-rw-r--r--libsmartcols/src/calculate.c98
-rw-r--r--libsmartcols/src/cell.c95
-rw-r--r--libsmartcols/src/column.c383
-rw-r--r--libsmartcols/src/filter-expr.c219
-rw-r--r--libsmartcols/src/filter-param.c889
-rw-r--r--libsmartcols/src/filter-parser.c1803
-rw-r--r--libsmartcols/src/filter-parser.h110
-rw-r--r--libsmartcols/src/filter-parser.stamp0
-rw-r--r--libsmartcols/src/filter-parser.y141
-rw-r--r--libsmartcols/src/filter-scanner.c2096
-rw-r--r--libsmartcols/src/filter-scanner.h507
-rw-r--r--libsmartcols/src/filter-scanner.l62
-rw-r--r--libsmartcols/src/filter-scanner.stamp0
-rw-r--r--libsmartcols/src/filter.c520
-rw-r--r--libsmartcols/src/grouping.c2
-rw-r--r--libsmartcols/src/init.c2
-rw-r--r--libsmartcols/src/libsmartcols.h.in95
-rw-r--r--libsmartcols/src/libsmartcols.sym31
-rw-r--r--libsmartcols/src/line.c16
-rw-r--r--libsmartcols/src/print.c301
-rw-r--r--libsmartcols/src/smartcolsP.h179
-rw-r--r--libsmartcols/src/table.c53
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;
}