summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/buffer.c7
-rw-r--r--lib/c_strtod.c4
-rw-r--r--lib/caputils.c38
-rw-r--r--lib/colors.c10
-rw-r--r--lib/cpuset.c10
-rw-r--r--lib/env.c4
-rw-r--r--lib/exec_shell.c4
-rw-r--r--lib/fileeq.c2
-rw-r--r--lib/jsonwrt.c37
-rw-r--r--lib/loopdev.c165
-rw-r--r--lib/mbsalign.c21
-rw-r--r--lib/mbsedit.c13
-rw-r--r--lib/monotonic.c6
-rw-r--r--lib/pager.c38
-rw-r--r--lib/path.c53
-rw-r--r--lib/procfs.c5
-rw-r--r--lib/pty-session.c7
-rw-r--r--lib/sha1.c11
-rw-r--r--lib/sha256.c3
-rw-r--r--lib/shells.c103
-rw-r--r--lib/strutils.c60
-rw-r--r--lib/strv.c4
-rw-r--r--lib/terminal-colors.d.56
-rw-r--r--lib/timeutils.c255
-rw-r--r--lib/ttyutils.c78
-rw-r--r--libblkid/docs/Makefile.in14
-rw-r--r--libblkid/docs/libblkid-sections.txt1
-rw-r--r--libblkid/docs/version.xml2
-rw-r--r--libblkid/libblkid.36
-rw-r--r--libblkid/meson.build2
-rw-r--r--libblkid/src/Makemodule.am3
-rw-r--r--libblkid/src/blkid.h.in2
-rw-r--r--libblkid/src/blkidP.h16
-rw-r--r--libblkid/src/config.c107
-rw-r--r--libblkid/src/dev.c6
-rw-r--r--libblkid/src/libblkid.sym4
-rw-r--r--libblkid/src/partitions/bsd.c2
-rw-r--r--libblkid/src/partitions/dos.c14
-rw-r--r--libblkid/src/partitions/gpt.c6
-rw-r--r--libblkid/src/partitions/mac.c2
-rw-r--r--libblkid/src/partitions/minix.c4
-rw-r--r--libblkid/src/partitions/partitions.c5
-rw-r--r--libblkid/src/partitions/ultrix.c6
-rw-r--r--libblkid/src/probe.c213
-rw-r--r--libblkid/src/superblocks/adaptec_raid.c4
-rw-r--r--libblkid/src/superblocks/apfs.c2
-rw-r--r--libblkid/src/superblocks/bcache.c48
-rw-r--r--libblkid/src/superblocks/befs.c6
-rw-r--r--libblkid/src/superblocks/bluestore.c2
-rw-r--r--libblkid/src/superblocks/btrfs.c2
-rw-r--r--libblkid/src/superblocks/cramfs.c28
-rw-r--r--libblkid/src/superblocks/cs_fvault2.c2
-rw-r--r--libblkid/src/superblocks/ddf_raid.c6
-rw-r--r--libblkid/src/superblocks/drbd.c147
-rw-r--r--libblkid/src/superblocks/erofs.c4
-rw-r--r--libblkid/src/superblocks/exfat.c8
-rw-r--r--libblkid/src/superblocks/exfs.c6
-rw-r--r--libblkid/src/superblocks/f2fs.c6
-rw-r--r--libblkid/src/superblocks/gfs.c4
-rw-r--r--libblkid/src/superblocks/hfs.c38
-rw-r--r--libblkid/src/superblocks/highpoint_raid.c3
-rw-r--r--libblkid/src/superblocks/hpfs.c6
-rw-r--r--libblkid/src/superblocks/iso9660.c195
-rw-r--r--libblkid/src/superblocks/isw_raid.c3
-rw-r--r--libblkid/src/superblocks/jfs.c2
-rw-r--r--libblkid/src/superblocks/jmicron_raid.c3
-rw-r--r--libblkid/src/superblocks/linux_raid.c7
-rw-r--r--libblkid/src/superblocks/lsi_raid.c3
-rw-r--r--libblkid/src/superblocks/luks.c20
-rw-r--r--libblkid/src/superblocks/lvm.c10
-rw-r--r--libblkid/src/superblocks/minix.c2
-rw-r--r--libblkid/src/superblocks/mpool.c2
-rw-r--r--libblkid/src/superblocks/netware.c2
-rw-r--r--libblkid/src/superblocks/ntfs.c6
-rw-r--r--libblkid/src/superblocks/nvidia_raid.c3
-rw-r--r--libblkid/src/superblocks/ocfs.c6
-rw-r--r--libblkid/src/superblocks/promise_raid.c3
-rw-r--r--libblkid/src/superblocks/reiserfs.c4
-rw-r--r--libblkid/src/superblocks/romfs.c4
-rw-r--r--libblkid/src/superblocks/silicon_raid.c3
-rw-r--r--libblkid/src/superblocks/squashfs.c6
-rw-r--r--libblkid/src/superblocks/stratis.c6
-rw-r--r--libblkid/src/superblocks/superblocks.c9
-rw-r--r--libblkid/src/superblocks/superblocks.h16
-rw-r--r--libblkid/src/superblocks/swap.c4
-rw-r--r--libblkid/src/superblocks/sysv.c2
-rw-r--r--libblkid/src/superblocks/ubi.c2
-rw-r--r--libblkid/src/superblocks/ubifs.c2
-rw-r--r--libblkid/src/superblocks/udf.c27
-rw-r--r--libblkid/src/superblocks/vdo.c2
-rw-r--r--libblkid/src/superblocks/vfat.c17
-rw-r--r--libblkid/src/superblocks/via_raid.c3
-rw-r--r--libblkid/src/superblocks/vmfs.c6
-rw-r--r--libblkid/src/superblocks/vxfs.c26
-rw-r--r--libblkid/src/superblocks/xfs.c12
-rw-r--r--libblkid/src/superblocks/zfs.c28
-rw-r--r--libblkid/src/superblocks/zonefs.c2
-rw-r--r--libblkid/src/topology/sysfs.c2
-rw-r--r--libblkid/src/topology/topology.c6
-rw-r--r--libfdisk/docs/Makefile.in14
-rw-r--r--libfdisk/docs/libfdisk-sections.txt1
-rw-r--r--libfdisk/docs/version.xml2
-rw-r--r--libfdisk/samples/Makemodule.am3
-rw-r--r--libfdisk/samples/mkpart-fullspec.c2
-rw-r--r--libfdisk/samples/mkpart.c2
-rw-r--r--libfdisk/src/Makemodule.am4
-rw-r--r--libfdisk/src/ask.c4
-rw-r--r--libfdisk/src/bsd.c4
-rw-r--r--libfdisk/src/context.c2
-rw-r--r--libfdisk/src/dos.c18
-rw-r--r--libfdisk/src/fdiskP.h2
-rw-r--r--libfdisk/src/gpt.c27
-rw-r--r--libfdisk/src/item.c6
-rw-r--r--libfdisk/src/libfdisk.h.in3
-rw-r--r--libfdisk/src/libfdisk.sym4
-rw-r--r--libfdisk/src/partition.c52
-rw-r--r--libfdisk/src/parttype.c13
-rw-r--r--libfdisk/src/script.c32
-rw-r--r--libfdisk/src/sgi.c13
-rw-r--r--libfdisk/src/sun.c6
-rw-r--r--libfdisk/src/utils.c10
-rw-r--r--libfdisk/src/version.c4
-rw-r--r--libfdisk/src/wipe.c14
-rw-r--r--liblastlog2/COPYING24
-rw-r--r--liblastlog2/Makemodule.am10
-rw-r--r--liblastlog2/lastlog2.pc.in13
-rw-r--r--liblastlog2/man/Makemodule.am20
-rw-r--r--liblastlog2/man/lastlog2.367
-rw-r--r--liblastlog2/man/lastlog2.3.adoc51
-rw-r--r--liblastlog2/man/ll2_import_lastlog.384
-rw-r--r--liblastlog2/man/ll2_import_lastlog.3.adoc65
-rw-r--r--liblastlog2/man/ll2_read_all.393
-rw-r--r--liblastlog2/man/ll2_read_all.3.adoc74
-rw-r--r--liblastlog2/man/ll2_read_entry.391
-rw-r--r--liblastlog2/man/ll2_read_entry.3.adoc72
-rw-r--r--liblastlog2/man/ll2_remove_entry.382
-rw-r--r--liblastlog2/man/ll2_remove_entry.3.adoc63
-rw-r--r--liblastlog2/man/ll2_rename_user.385
-rw-r--r--liblastlog2/man/ll2_rename_user.3.adoc66
-rw-r--r--liblastlog2/man/ll2_update_login_time.386
-rw-r--r--liblastlog2/man/ll2_update_login_time.3.adoc67
-rw-r--r--liblastlog2/man/ll2_write_entry.388
-rw-r--r--liblastlog2/man/ll2_write_entry.3.adoc70
-rw-r--r--liblastlog2/meson.build63
-rw-r--r--liblastlog2/src/Makemodule.am99
-rw-r--r--liblastlog2/src/lastlog2.c595
-rw-r--r--liblastlog2/src/lastlog2.h91
-rw-r--r--liblastlog2/src/lastlog2P.h37
-rw-r--r--liblastlog2/src/liblastlog2.sym13
-rw-r--r--liblastlog2/src/tests/tst_dlopen.c40
-rw-r--r--liblastlog2/src/tests/tst_pam_lastlog2_output.c115
-rw-r--r--liblastlog2/src/tests/tst_remove_entry.c88
-rw-r--r--liblastlog2/src/tests/tst_rename_user.c112
-rw-r--r--liblastlog2/src/tests/tst_write_read_user.c165
-rw-r--r--liblastlog2/src/tests/tst_y2038_ll2_read_all.c156
-rw-r--r--liblastlog2/src/tests/tst_y2038_sqlite3_time.c83
-rw-r--r--libmount/Makemodule.am1
-rw-r--r--libmount/docs/Makefile.in14
-rw-r--r--libmount/docs/libmount-sections.txt3
-rw-r--r--libmount/docs/version.xml2
-rw-r--r--libmount/meson.build35
-rw-r--r--libmount/python/context.c48
-rw-r--r--libmount/python/fs.c48
-rw-r--r--libmount/python/meson.build30
-rw-r--r--libmount/python/tab.c48
-rw-r--r--libmount/samples/Makemodule.am11
-rw-r--r--libmount/samples/overwrite.c58
-rw-r--r--libmount/src/Makemodule.am2
-rw-r--r--libmount/src/cache.c14
-rw-r--r--libmount/src/context.c54
-rw-r--r--libmount/src/context_mount.c34
-rw-r--r--libmount/src/context_umount.c2
-rw-r--r--libmount/src/hook_loopdev.c20
-rw-r--r--libmount/src/hook_mount.c84
-rw-r--r--libmount/src/hook_veritydev.c39
-rw-r--r--libmount/src/libmount.h.in4
-rw-r--r--libmount/src/libmount.sym8
-rw-r--r--libmount/src/lock.c57
-rw-r--r--libmount/src/monitor.c79
-rw-r--r--libmount/src/mountP.h48
-rw-r--r--libmount/src/optlist.c66
-rw-r--r--libmount/src/optstr.c130
-rw-r--r--libmount/src/tab.c45
-rw-r--r--libmount/src/tab_diff.c6
-rw-r--r--libmount/src/tab_update.c318
-rw-r--r--libmount/src/utils.c99
-rw-r--r--libmount/src/version.c3
-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.stamp (renamed from tests/expected/mount/special)0
-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
-rw-r--r--libuuid/man/uuid.36
-rw-r--r--libuuid/man/uuid_clear.36
-rw-r--r--libuuid/man/uuid_compare.36
-rw-r--r--libuuid/man/uuid_copy.36
-rw-r--r--libuuid/man/uuid_generate.36
-rw-r--r--libuuid/man/uuid_is_null.36
-rw-r--r--libuuid/man/uuid_parse.36
-rw-r--r--libuuid/man/uuid_time.36
-rw-r--r--libuuid/man/uuid_unparse.36
-rw-r--r--libuuid/src/gen_uuid.c120
-rw-r--r--libuuid/src/libuuid.sym9
-rw-r--r--libuuid/src/test_uuid.c20
-rw-r--r--libuuid/src/uuid.h3
-rw-r--r--libuuid/src/uuidP.h3
-rw-r--r--libuuid/src/uuid_time.c59
239 files changed, 13843 insertions, 1516 deletions
diff --git a/lib/buffer.c b/lib/buffer.c
index fda2fc8..cc863fa 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -11,7 +11,7 @@
void ul_buffer_reset_data(struct ul_buffer *buf)
{
if (buf->begin)
- buf->begin[0] = '\0';
+ memset(buf->begin, 0, buf->sz);
buf->end = buf->begin;
if (buf->ptrs && buf->nptrs)
@@ -49,7 +49,7 @@ int ul_buffer_is_empty(struct ul_buffer *buf)
int ul_buffer_save_pointer(struct ul_buffer *buf, unsigned short ptr_idx)
{
if (ptr_idx >= buf->nptrs) {
- char **tmp = realloc(buf->ptrs, (ptr_idx + 1) * sizeof(char *));
+ char **tmp = reallocarray(buf->ptrs, ptr_idx + 1, sizeof(char *));
if (!tmp)
return -EINVAL;
@@ -134,12 +134,11 @@ int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz)
if (!buf)
return -EINVAL;
- if (!data || !*data)
+ if (!data)
return 0;
if (buf->begin && buf->end)
maxsz = buf->sz - (buf->end - buf->begin);
-
if (maxsz <= sz + 1) {
int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1);
if (rc)
diff --git a/lib/c_strtod.c b/lib/c_strtod.c
index d25ee27..d7d8cbe 100644
--- a/lib/c_strtod.c
+++ b/lib/c_strtod.c
@@ -50,10 +50,10 @@ double c_strtod(char const *str, char **end)
return strtod_l(str, end, cl);
#elif defined(HAVE_USELOCALE)
/*
- * B) classic strtod(), but switch to "C" locale by uselocal()
+ * B) classic strtod(), but switch to "C" locale by uselocale()
*/
if (cl) {
- locale_t org_cl = uselocale(locale);
+ locale_t org_cl = uselocale(cl);
if (!org_cl)
return 0;
diff --git a/lib/caputils.c b/lib/caputils.c
index 987533a..23866c0 100644
--- a/lib/caputils.c
+++ b/lib/caputils.c
@@ -24,6 +24,7 @@
#include "caputils.h"
#include "pathnames.h"
#include "procfs.h"
+#include "nls.h"
static int test_cap(unsigned int cap)
{
@@ -87,6 +88,43 @@ int cap_last_cap(void)
return cap;
}
+void cap_permitted_to_ambient(void)
+{
+ /* We use capabilities system calls to propagate the permitted
+ * capabilities into the ambient set because we may have
+ * already forked so be in async-signal-safe context. */
+ struct __user_cap_header_struct header = {
+ .version = _LINUX_CAPABILITY_VERSION_3,
+ .pid = 0,
+ };
+ struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }};
+ uint64_t effective, cap;
+
+ if (capget(&header, payload) < 0)
+ err(EXIT_FAILURE, _("capget failed"));
+
+ /* In order the make capabilities ambient, we first need to ensure
+ * that they are all inheritable. */
+ payload[0].inheritable = payload[0].permitted;
+ payload[1].inheritable = payload[1].permitted;
+
+ if (capset(&header, payload) < 0)
+ err(EXIT_FAILURE, _("capset failed"));
+
+ effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective;
+
+ for (cap = 0; cap < (sizeof(effective) * 8); cap++) {
+ /* This is the same check as cap_valid(), but using
+ * the runtime value for the last valid cap. */
+ if (cap > (uint64_t) cap_last_cap())
+ continue;
+
+ if ((effective & (1ULL << cap))
+ && prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0)
+ err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed"));
+ }
+}
+
#ifdef TEST_PROGRAM_CAPUTILS
int main(int argc, char *argv[])
{
diff --git a/lib/colors.c b/lib/colors.c
index 532e339..0928770 100644
--- a/lib/colors.c
+++ b/lib/colors.c
@@ -357,12 +357,10 @@ static char *colors_get_homedir(char *buf, size_t bufsz)
/*
* Adds one color sequence to array with color scheme.
- * When returning success (0) this function takes ownership of
- * @seq and @name, which have to be allocated strings.
*/
static int colors_add_scheme(struct ul_color_ctl *cc,
- char *name,
- char *seq0)
+ const char *name,
+ const char *seq0)
{
struct ul_color_scheme *cs = NULL;
char *seq = NULL;
@@ -380,8 +378,8 @@ static int colors_add_scheme(struct ul_color_ctl *cc,
/* enlarge the array */
if (cc->nschemes == cc->schemes_sz) {
- void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
- * sizeof(struct ul_color_scheme));
+ void *tmp = reallocarray(cc->schemes, cc->nschemes + 10,
+ sizeof(struct ul_color_scheme));
if (!tmp)
goto err;
cc->schemes = tmp;
diff --git a/lib/cpuset.c b/lib/cpuset.c
index 098b8e5..533b8ab 100644
--- a/lib/cpuset.c
+++ b/lib/cpuset.c
@@ -287,7 +287,7 @@ static int nextnumber(const char *str, char **end, unsigned int *result)
*/
int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
{
- size_t max = cpuset_nbits(setsize);
+ const size_t max = cpuset_nbits(setsize);
const char *p, *q;
char *end = NULL;
@@ -326,8 +326,12 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
if (!(a <= b))
return 1;
while (a <= b) {
- if (fail && (a >= max))
- return 2;
+ if (a >= max) {
+ if (fail)
+ return 2;
+ else
+ break;
+ }
CPU_SET_S(a, setsize, set);
a += s;
}
diff --git a/lib/env.c b/lib/env.c
index 2b3395c..2bdfe56 100644
--- a/lib/env.c
+++ b/lib/env.c
@@ -159,7 +159,7 @@ void __sanitize_env(struct ul_env_list **org)
if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
if (org)
*org = env_list_add(*org, *cur);
- last = remote_entry(envp, cur - envp, last);
+ last = remove_entry(envp, cur - envp, last);
cur--;
break;
}
@@ -174,7 +174,7 @@ void __sanitize_env(struct ul_env_list **org)
continue; /* OK */
if (org)
*org = env_list_add(*org, *cur);
- last = remote_entry(envp, cur - envp, last);
+ last = remove_entry(envp, cur - envp, last);
cur--;
break;
}
diff --git a/lib/exec_shell.c b/lib/exec_shell.c
index 6fef6c7..96d3e95 100644
--- a/lib/exec_shell.c
+++ b/lib/exec_shell.c
@@ -42,9 +42,7 @@ void __attribute__((__noreturn__)) exec_shell(void)
shellc = xstrdup(shell);
shell_basename = basename(shellc);
- arg0 = xmalloc(strlen(shell_basename) + 2);
- arg0[0] = '-';
- strcpy(arg0 + 1, shell_basename);
+ xasprintf(&arg0, "-%s", shell_basename);
execl(shell, arg0, (char *)NULL);
errexec(shell);
diff --git a/lib/fileeq.c b/lib/fileeq.c
index af5b4e3..2a74af8 100644
--- a/lib/fileeq.c
+++ b/lib/fileeq.c
@@ -79,9 +79,9 @@ enum {
};
struct ul_fileeq_method {
- int id;
const char *name; /* name used by applications */
const char *kname; /* name used by kernel crypto */
+ int id;
short digsiz;
};
diff --git a/lib/jsonwrt.c b/lib/jsonwrt.c
index 8ca1d4d..243ed82 100644
--- a/lib/jsonwrt.c
+++ b/lib/jsonwrt.c
@@ -26,12 +26,12 @@
* }
* }
*/
-static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
+static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t size)
{
const char *p;
fputc('"', out);
- for (p = data; p && *p; p++) {
+ for (p = data; p && *p && (!size || p < data + size); p++) {
const unsigned int c = (unsigned int) *p;
@@ -98,9 +98,9 @@ static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
fputc('"', out);
}
-#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0)
-#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
-#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
+#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0, 0)
+#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0)
+#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0)
void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
{
@@ -154,12 +154,6 @@ void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
{
- if (fmt->indent == 1) {
- fputs("\n}\n", fmt->out);
- fmt->indent--;
- fmt->after_close = 1;
- return;
- }
assert(fmt->indent > 0);
switch (type) {
@@ -168,6 +162,8 @@ void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
fputc('\n', fmt->out);
ul_jsonwrt_indent(fmt);
fputs("}", fmt->out);
+ if (fmt->indent == 0)
+ fputs("\n", fmt->out);
break;
case UL_JSON_ARRAY:
fmt->indent--;
@@ -204,6 +200,17 @@ void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
ul_jsonwrt_value_close(fmt);
}
+void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, size_t size)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ if (data && *data)
+ fputs_quoted_case_json(data, fmt->out, 0, size);
+ else
+ fputs("null", fmt->out);
+ ul_jsonwrt_value_close(fmt);
+}
+
void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
const char *name, uint64_t data)
{
@@ -212,6 +219,14 @@ void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
ul_jsonwrt_value_close(fmt);
}
+void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt,
+ const char *name, long double data)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ fprintf(fmt->out, "%Lg", data);
+ ul_jsonwrt_value_close(fmt);
+}
+
void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
const char *name, int data)
{
diff --git a/lib/loopdev.c b/lib/loopdev.c
index dd9ead3..c72fb2c 100644
--- a/lib/loopdev.c
+++ b/lib/loopdev.c
@@ -116,7 +116,9 @@ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
DBG(CXT, ul_debugobj(lc, "closing old open fd"));
}
lc->fd = -1;
- lc->mode = 0;
+ lc->is_lost = 0;
+ lc->devno = 0;
+ lc->mode = O_RDONLY;
lc->blocksize = 0;
lc->has_info = 0;
lc->info_failed = 0;
@@ -153,6 +155,28 @@ int loopcxt_has_device(struct loopdev_cxt *lc)
return lc && *lc->device;
}
+dev_t loopcxt_get_devno(struct loopdev_cxt *lc)
+{
+ if (!lc || !loopcxt_has_device(lc))
+ return 0;
+ if (!lc->devno)
+ lc->devno = sysfs_devname_to_devno(lc->device);
+ return lc->devno;
+}
+
+int loopcxt_is_lost(struct loopdev_cxt *lc)
+{
+ if (!lc || !loopcxt_has_device(lc))
+ return 0;
+ if (lc->is_lost)
+ return 1;
+
+ lc->is_lost = access(lc->device, F_OK) != 0
+ && loopcxt_get_devno(lc) != 0;
+
+ return lc->is_lost;
+}
+
/*
* @lc: context
* @flags: LOOPDEV_FL_* flags
@@ -165,12 +189,6 @@ int loopcxt_has_device(struct loopdev_cxt *lc)
*
* * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
*
- * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
- * syscall to open loop device. By default is the device open read-only.
- *
- * The exception is loopcxt_setup_device(), where the device is open read-write
- * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
- *
* Returns: <0 on error, 0 on success.
*/
int loopcxt_init(struct loopdev_cxt *lc, int flags)
@@ -271,7 +289,7 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
return NULL;
if (!lc->sysfs) {
- dev_t devno = sysfs_devname_to_devno(lc->device);
+ dev_t devno = loopcxt_get_devno(lc);
if (!devno) {
DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
return NULL;
@@ -285,28 +303,50 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
return lc->sysfs;
}
-/*
- * @lc: context
- *
- * Returns: file descriptor to the open loop device or <0 on error. The mode
- * depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is
- * read-only.
- */
-int loopcxt_get_fd(struct loopdev_cxt *lc)
+static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode)
{
+ int old = -1;
+
if (!lc || !*lc->device)
return -EINVAL;
+ /* It's okay to return a FD with read-write permissions if someone
+ * asked for read-only, but you shouldn't do the opposite.
+ *
+ * (O_RDONLY is a widely usable default.)
+ */
+ if (lc->fd >= 0 && mode == O_RDWR && lc->mode == O_RDONLY) {
+ DBG(CXT, ul_debugobj(lc, "closing already open device (mode mismatch)"));
+ old = lc->fd;
+ lc->fd = -1;
+ }
+
if (lc->fd < 0) {
- lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+ lc->mode = mode;
lc->fd = open(lc->device, lc->mode | O_CLOEXEC);
DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
- lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
+ mode == O_RDONLY ? "ro" :
+ mode == O_RDWR ? "rw" : "??"));
+
+ if (lc->fd < 0 && old >= 0) {
+ /* restore original on error */
+ lc->fd = old;
+ old = -1;
+ }
}
+
+ if (old >= 0)
+ close(old);
return lc->fd;
}
-int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+/* default is read-only file descriptor, it's enough for all ioctls */
+int loopcxt_get_fd(struct loopdev_cxt *lc)
+{
+ return __loopcxt_get_fd(lc, O_RDONLY);
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, mode_t mode)
{
if (!lc)
return -EINVAL;
@@ -400,13 +440,6 @@ static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
!(lc->iter.flags & LOOPITER_FL_FREE))
return 0; /* caller does not care about device status */
- if (!is_loopdev(lc->device)) {
- DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
- return -errno;
- }
-
- DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
-
used = loopcxt_get_offset(lc, NULL) == 0;
if ((lc->iter.flags & LOOPITER_FL_USED) && used)
@@ -479,7 +512,7 @@ static int loop_scandir(const char *dirname, int **ary, int hasprefix)
arylen += 1;
- tmp = realloc(*ary, arylen * sizeof(int));
+ tmp = reallocarray(*ary, arylen, sizeof(int));
if (!tmp) {
free(*ary);
*ary = NULL;
@@ -749,6 +782,26 @@ char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
/*
* @lc: context
+ *
+ * Returns (allocated) string with loop reference. The same as backing file by
+ * default.
+ */
+char *loopcxt_get_refname(struct loopdev_cxt *lc)
+{
+ char *res = NULL;
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo) {
+ lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+ res = strdup((char *) lo->lo_file_name);
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_refname [%s]", res));
+ return res;
+}
+
+/*
+ * @lc: context
* @offset: returns offset number for the given device
*
* Returns: <0 on error, 0 on success
@@ -840,7 +893,7 @@ int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
/*
* @lc: context
- * @devno: returns encryption type
+ * @type: returns encryption type
*
* Cryptoloop is DEPRECATED!
*
@@ -865,7 +918,6 @@ int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
/*
* @lc: context
- * @devno: returns crypt name
*
* Cryptoloop is DEPRECATED!
*
@@ -1182,6 +1234,28 @@ int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
/*
* @lc: context
+ * @refname: reference name (used to overwrite lo_file_name where is backing
+ * file by default)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_refname(struct loopdev_cxt *lc, const char *refname)
+{
+ if (!lc)
+ return -EINVAL;
+
+ memset(lc->config.info.lo_file_name, 0, sizeof(lc->config.info.lo_file_name));
+ if (refname)
+ xstrncpy((char *)lc->config.info.lo_file_name, refname, LO_NAME_SIZE);
+
+ DBG(CXT, ul_debugobj(lc, "set refname=%s", (char *)lc->config.info.lo_file_name));
+ return 0;
+}
+
+/*
+ * @lc: context
* @filename: backing file path (the path will be canonicalized)
*
* The setting is removed by loopcxt_set_device() loopcxt_next()!
@@ -1197,9 +1271,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
if (!lc->filename)
return -errno;
- xstrncpy((char *)lc->config.info.lo_file_name, lc->filename, LO_NAME_SIZE);
+ if (!lc->config.info.lo_file_name[0])
+ loopcxt_set_refname(lc, lc->filename);
- DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->config.info.lo_file_name));
+ DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->filename));
return 0;
}
@@ -1314,7 +1389,8 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
*/
int loopcxt_setup_device(struct loopdev_cxt *lc)
{
- int file_fd, dev_fd, mode = O_RDWR, flags = O_CLOEXEC;
+ int file_fd, dev_fd;
+ mode_t flags = O_CLOEXEC, mode = O_RDWR;
int rc = -1, cnt = 0;
int errsv = 0;
int fallback = 0;
@@ -1344,25 +1420,22 @@ int loopcxt_setup_device(struct loopdev_cxt *lc)
}
DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
- if (lc->fd != -1 && lc->mode != mode) {
- DBG(SETUP, ul_debugobj(lc, "closing already open device (mode mismatch)"));
- close(lc->fd);
- lc->fd = -1;
- lc->mode = 0;
- }
-
- if (mode == O_RDONLY) {
- lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */
+ if (mode == O_RDONLY)
lc->config.info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */
- } else {
- lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */
+ else
lc->config.info.lo_flags &= ~LO_FLAGS_READ_ONLY;
- lc->flags &= ~LOOPDEV_FL_RDONLY;
- }
do {
errno = 0;
- dev_fd = loopcxt_get_fd(lc);
+
+ /* For the ioctls, it's enough to use O_RDONLY, but udevd
+ * monitor devices by inotify, and udevd needs IN_CLOSE_WRITE
+ * event to trigger probing of the new device.
+ *
+ * The mode used for the device does not have to match the mode
+ * used for the backing file.
+ */
+ dev_fd = __loopcxt_get_fd(lc, O_RDWR);
if (dev_fd >= 0 || lc->control_ok == 0)
break;
if (errno != EACCES && errno != ENOENT)
diff --git a/lib/mbsalign.c b/lib/mbsalign.c
index 7b8f5a6..b4ab7be 100644
--- a/lib/mbsalign.c
+++ b/lib/mbsalign.c
@@ -310,12 +310,33 @@ char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf)
return buf;
}
+/*
+ * Guess size
+ */
size_t mbs_safe_encode_size(size_t bytes)
{
return (bytes * 4) + 1;
}
/*
+ * Count size of the original string in bytes (count \x?? as one byte)
+ */
+size_t mbs_safe_decode_size(const char *p)
+{
+ size_t bytes = 0;
+
+ while (p && *p) {
+ if (*p == '\\' && *(p + 1) == 'x' &&
+ isxdigit(*(p + 2)) && isxdigit(*(p + 3)))
+ p += 4;
+ else
+ p++;
+ bytes++;
+ }
+ return bytes;
+}
+
+/*
* Returns allocated string where all control and non-printable chars are
* replaced with \x?? hex sequence.
*/
diff --git a/lib/mbsedit.c b/lib/mbsedit.c
index ecfa9f4..9cf4f0f 100644
--- a/lib/mbsedit.c
+++ b/lib/mbsedit.c
@@ -157,13 +157,14 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
#ifdef HAVE_WIDECHAR
wchar_t wc = (wchar_t) c;
- char in_buf[MB_CUR_MAX];
+ in = malloc(MB_CUR_MAX);
+ if (!in)
+ return -1;
- n = wctomb(in_buf, wc);
+ n = wctomb(in, wc);
if (n == (size_t) -1)
- return n;
+ goto out;
*ncells = wcwidth(wc);
- in = in_buf;
#else
*ncells = 1;
in = (char *) &c;
@@ -173,6 +174,10 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
memmove(str + n, str, bytes);
memcpy(str, in, n);
str[bytes + n] = '\0';
+out:
+#ifdef HAVE_WIDECHAR
+ free(in);
+#endif
return n;
}
diff --git a/lib/monotonic.c b/lib/monotonic.c
index cd554dd..63feec0 100644
--- a/lib/monotonic.c
+++ b/lib/monotonic.c
@@ -18,10 +18,10 @@
int get_boot_time(struct timeval *boot_time)
{
#ifdef CLOCK_BOOTTIME
- struct timespec hires_uptime;
- struct timeval lores_uptime;
+ struct timespec hires_uptime = { 0 };
+ struct timeval lores_uptime = { 0 };
#endif
- struct timeval now;
+ struct timeval now = { 0 };
#ifdef HAVE_SYSINFO
struct sysinfo info;
#endif
diff --git a/lib/pager.c b/lib/pager.c
index 9429032..98814b5 100644
--- a/lib/pager.c
+++ b/lib/pager.c
@@ -85,7 +85,9 @@ static int start_command(struct child_process *cmd)
close(cmd->in);
}
- cmd->preexec_cb();
+ if (cmd->preexec_cb)
+ cmd->preexec_cb();
+
execvp(cmd->argv[0], (char *const*) cmd->argv);
errexec(cmd->argv[0]);
}
@@ -140,7 +142,7 @@ static int finish_command(struct child_process *cmd)
return wait_or_whine(cmd->pid);
}
-static void pager_preexec(void)
+static void pager_preexec_less(void)
{
/*
* Work around bug in "less" by not starting it until we
@@ -178,35 +180,45 @@ static void wait_for_pager_signal(int signo)
static int has_command(const char *cmd)
{
const char *path;
- char *p, *s;
+ char *b, *c, *p, *s;
int rc = 0;
if (!cmd)
goto done;
- if (*cmd == '/') {
- rc = access(cmd, X_OK) == 0;
+
+ c = xstrdup(cmd);
+ if (!c)
goto done;
+ b = strtok(c, " "); /* cmd may contain options */
+ if (!b)
+ goto cleanup;
+
+ if (*b == '/') {
+ rc = access(b, X_OK) == 0;
+ goto cleanup;
}
path = getenv("PATH");
if (!path)
- goto done;
+ goto cleanup;
p = xstrdup(path);
if (!p)
- goto done;
+ goto cleanup;
- for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+ for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
int fd = open(s, O_RDONLY|O_CLOEXEC);
if (fd < 0)
continue;
- rc = faccessat(fd, cmd, X_OK, 0) == 0;
+ rc = faccessat(fd, b, X_OK, 0) == 0;
close(fd);
if (rc)
break;
}
free(p);
+cleanup:
+ free(c);
done:
- /*fprintf(stderr, "has PAGER %s rc=%d\n", cmd, rc);*/
+ /*fprintf(stderr, "has PAGER '%s': rc=%d\n", cmd, rc);*/
return rc;
}
@@ -230,7 +242,11 @@ static void __setup_pager(void)
pager_argv[2] = pager;
pager_process.argv = pager_argv;
pager_process.in = -1;
- pager_process.preexec_cb = pager_preexec;
+
+ if (!strncmp(pager, "less", 4))
+ pager_process.preexec_cb = pager_preexec_less;
+ else
+ pager_process.preexec_cb = NULL;
if (start_command(&pager_process))
return;
diff --git a/lib/path.c b/lib/path.c
index 4aceda1..202f19a 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -996,38 +996,25 @@ int ul_path_next_dirent(struct path_cxt *pc, DIR **sub, const char *dirname, str
return 1;
}
-/*
- * Like fopen() but, @path is always prefixed by @prefix. This function is
- * useful in case when ul_path_* API is overkill.
- */
-FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode)
-{
- char buf[PATH_MAX];
-
- if (!path)
- return NULL;
- if (!prefix)
- return fopen(path, mode);
- if (*path == '/')
- path++;
-
- snprintf(buf, sizeof(buf), "%s/%s", prefix, path);
- return fopen(buf, mode);
-}
-
#ifdef HAVE_CPU_SET_T
static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
{
FILE *f;
size_t setsize, len = maxcpus * 7;
- char buf[len];
+ char *buf;
int rc;
*set = NULL;
+ buf = malloc(len);
+ if (!buf)
+ return -ENOMEM;
+
f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
- if (!f)
- return -errno;
+ if (!f) {
+ rc = -errno;
+ goto out;
+ }
if (fgets(buf, len, f) == NULL) {
errno = EIO;
@@ -1038,32 +1025,38 @@ static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, i
fclose(f);
if (rc)
- return rc;
+ goto out;
len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = '\0';
*set = cpuset_alloc(maxcpus, &setsize, NULL);
- if (!*set)
- return -ENOMEM;
+ if (!*set) {
+ rc = -EINVAL;
+ goto out;
+ }
if (islist) {
if (cpulist_parse(buf, *set, setsize, 0)) {
- cpuset_free(*set);
errno = EINVAL;
rc = -errno;
- return rc;
+ goto out;
}
} else {
if (cpumask_parse(buf, *set, setsize)) {
- cpuset_free(*set);
errno = EINVAL;
rc = -errno;
- return rc;
+ goto out;
}
}
- return 0;
+ rc = 0;
+
+out:
+ if (rc)
+ cpuset_free(*set);
+ free(buf);
+ return rc;
}
int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
diff --git a/lib/procfs.c b/lib/procfs.c
index aff20fb..59f40ff 100644
--- a/lib/procfs.c
+++ b/lib/procfs.c
@@ -169,6 +169,11 @@ ssize_t procfs_process_get_stat(struct path_cxt *pc, char *buf, size_t bufsz)
return procfs_process_get_data_for(pc, buf, bufsz, "stat");
}
+ssize_t procfs_process_get_syscall(struct path_cxt *pc, char *buf, size_t bufsz)
+{
+ return procfs_process_get_data_for(pc, buf, bufsz, "syscall");
+}
+
int procfs_process_get_stat_nth(struct path_cxt *pc, int n, uintmax_t *re)
{
ssize_t rc;
diff --git a/lib/pty-session.c b/lib/pty-session.c
index 3849065..815264d 100644
--- a/lib/pty-session.c
+++ b/lib/pty-session.c
@@ -241,6 +241,7 @@ int ul_pty_signals_setup(struct ul_pty *pty)
sigaddset(&ourset, SIGCHLD);
sigaddset(&ourset, SIGWINCH);
sigaddset(&ourset, SIGALRM);
+ sigaddset(&ourset, SIGHUP);
sigaddset(&ourset, SIGTERM);
sigaddset(&ourset, SIGINT);
sigaddset(&ourset, SIGQUIT);
@@ -584,6 +585,8 @@ static int handle_signal(struct ul_pty *pty, int fd)
&info, (void *) &pty->win);
}
break;
+ case SIGHUP:
+ /* fallthrough */
case SIGTERM:
/* fallthrough */
case SIGINT:
@@ -641,7 +644,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
/* note, callback usually updates @next_callback_time */
if (timerisset(&pty->next_callback_time)) {
- struct timeval now;
+ struct timeval now = { 0 };;
DBG(IO, ul_debugobj(pty, " callback requested"));
gettime_monotonic(&now);
@@ -654,7 +657,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
/* set timeout */
if (timerisset(&pty->next_callback_time)) {
- struct timeval now, rest;
+ struct timeval now = { 0 }, rest = { 0 };
gettime_monotonic(&now);
timersub(&pty->next_callback_time, &now, &rest);
diff --git a/lib/sha1.c b/lib/sha1.c
index eedeaa8..3edff12 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -1,4 +1,7 @@
/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
* SHA-1 in C by Steve Reid <steve@edmweb.com>
* 100% Public Domain
*
@@ -153,7 +156,15 @@ void ul_SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
state[3] += d;
state[4] += e;
/* Wipe variables */
+#ifdef HAVE_EXPLICIT_BZERO
+ explicit_bzero(&a, sizeof(a));
+ explicit_bzero(&b, sizeof(b));
+ explicit_bzero(&c, sizeof(c));
+ explicit_bzero(&d, sizeof(d));
+ explicit_bzero(&e, sizeof(e));
+#else
a = b = c = d = e = 0;
+#endif
#ifdef UL_SHA1HANDSOFF
memset(block, '\0', sizeof(block));
#endif
diff --git a/lib/sha256.c b/lib/sha256.c
index 934303c..10877d2 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -1,4 +1,7 @@
/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
* public domain sha256 crypt implementation
*
* original sha crypt design: http://people.redhat.com/drepper/SHA-crypt.txt
diff --git a/lib/shells.c b/lib/shells.c
index 6693ab0..18e4cca 100644
--- a/lib/shells.c
+++ b/lib/shells.c
@@ -9,20 +9,11 @@
#include "closestream.h"
#include "shells.h"
-/*
- * is_known_shell() -- if the given shell appears in /etc/shells
- * or vendor defined files.
- * Return 1 if found and return 0 if not found.
- */
-extern int is_known_shell(const char *shell_name)
-{
- int ret = 0;
-
#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
- size_t size = 0;
+static econf_file *open_etc_shells(void)
+{
econf_err error;
- char **keys;
- econf_file *key_file;
+ econf_file *key_file = NULL;
error = econf_readDirs(&key_file,
_PATH_VENDORDIR,
@@ -35,31 +26,84 @@ extern int is_known_shell(const char *shell_name)
syslog(LOG_ALERT,
_("Cannot parse shells files: %s"),
econf_errString(error));
- exit(EXIT_FAILURE);
+ return NULL;
}
- error = econf_getKeys(key_file, NULL, &size, &keys);
- if (error) {
- syslog(LOG_ALERT,
- _("Cannot evaluate entries in shells files: %s"),
- econf_errString(error));
- econf_free (key_file);
- exit(EXIT_FAILURE);
- }
+ return key_file;
+}
+#endif
- for (size_t i = 0; i < size; i++) {
- if (strcmp (keys[i], shell_name) == 0) {
- ret = 1;
- break;
- }
- }
- econf_free (key_file);
+/*
+ * print_shells () -- /etc/shells is outputted to stdout.
+ */
+extern void print_shells(FILE *out, const char *format)
+{
+#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
+ size_t size = 0;
+ econf_err error;
+ char **keys = NULL;
+ econf_file *key_file = open_etc_shells();
+
+ if (!key_file)
+ return;
+
+ error = econf_getKeys(key_file, NULL, &size, &keys);
+ if (error) {
+ econf_free(key_file);
+ errx(EXIT_FAILURE,
+ _("Cannot evaluate entries in shells files: %s"),
+ econf_errString(error));
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ fprintf(out, format, keys[i]);
+ }
+ econf_free(keys);
+ econf_free(key_file);
#else
- char *s;
+ char *s;
+
+ while ((s = getusershell()))
+ fprintf(out, format, s);
+ endusershell();
+#endif
+}
+
+
+/*
+ * is_known_shell() -- if the given shell appears in /etc/shells
+ * or vendor defined files.
+ * Return 1 if found and return 0 if not found.
+ */
+extern int is_known_shell(const char *shell_name)
+{
+ int ret = 0;
if (!shell_name)
return 0;
+#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
+ char *val = NULL;
+ econf_err error;
+ econf_file *key_file = open_etc_shells();
+
+ if (!key_file)
+ return 0;
+
+ error = econf_getStringValue (key_file, NULL, shell_name, &val);
+ if (error) {
+ if (error != ECONF_NOKEY)
+ syslog(LOG_ALERT,
+ _("Cannot evaluate entries in shells files: %s"),
+ econf_errString(error));
+ } else
+ ret = 1;
+
+ free(val);
+ econf_free(key_file);
+#else
+ char *s;
+
setusershell();
while ((s = getusershell())) {
if (*s != '#' && strcmp(shell_name, s) == 0) {
@@ -71,4 +115,3 @@ extern int is_known_shell(const char *shell_name)
#endif
return ret;
}
-
diff --git a/lib/strutils.c b/lib/strutils.c
index 6229a6e..9ea5da7 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -456,21 +456,28 @@ err:
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
-long double strtold_or_err(const char *str, const char *errmesg)
+int ul_strtold(const char *str, long double *num)
{
- double num;
char *end = NULL;
errno = 0;
if (str == NULL || *str == '\0')
- goto err;
- num = strtold(str, &end);
+ return -(errno = EINVAL);
+ *num = strtold(str, &end);
- if (errno || str == end || (end && *end))
- goto err;
+ if (errno != 0)
+ return -errno;
+ if (str == end || (end && *end))
+ return -(errno = EINVAL);
+ return 0;
+}
- return num;
-err:
+long double strtold_or_err(const char *str, const char *errmesg)
+{
+ long double num = 0;
+
+ if (ul_strtold(str, &num) == 0)
+ return num;
if (errno == ERANGE)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
@@ -1010,6 +1017,34 @@ int strappend(char **a, const char *b)
return 0;
}
+/* the hybrid version of strfconcat and strappend. */
+int strfappend(char **a, const char *format, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, format);
+ res = strvfappend(a, format, ap);
+ va_end(ap);
+
+ return res;
+}
+
+extern int strvfappend(char **a, const char *format, va_list ap)
+{
+ char *val;
+ int sz;
+ int res;
+
+ sz = vasprintf(&val, format, ap);
+ if (sz < 0)
+ return -errno;
+
+ res = strappend(a, val);
+ free(val);
+ return res;
+}
+
static size_t strcspn_escaped(const char *s, const char *reject)
{
int escaped = 0;
@@ -1386,6 +1421,15 @@ int main(int argc, char *argv[])
printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3]));
return EXIT_SUCCESS;
+ } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) {
+ char *buf = "abc\0Y\0\0xyz\0X";
+ char *end = buf + 12;
+ char *p = buf;
+
+ do {
+ printf("str: '%s'\n", p);
+ } while ((p = ul_next_string(p, end)));
+
} else {
fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
" %1$s --cmp-paths <path> <path>\n"
diff --git a/lib/strv.c b/lib/strv.c
index c306e38..fd84fe3 100644
--- a/lib/strv.c
+++ b/lib/strv.c
@@ -74,7 +74,7 @@ unsigned strv_length(char * const *l) {
return n;
}
-char **strv_new_ap(const char *x, va_list ap) {
+static char **strv_new_ap(const char *x, va_list ap) {
const char *s;
char **a;
unsigned n = 0, i = 0;
@@ -265,7 +265,7 @@ int strv_push(char ***l, char *value) {
if (m < n)
return -ENOMEM;
- c = realloc(*l, sizeof(char *) * m);
+ c = reallocarray(*l, m, sizeof(char *));
if (!c)
return -ENOMEM;
diff --git a/lib/terminal-colors.d.5 b/lib/terminal-colors.d.5
index 4b98389..1de42f4 100644
--- a/lib/terminal-colors.d.5
+++ b/lib/terminal-colors.d.5
@@ -2,12 +2,12 @@
.\" Title: terminal-colors.d
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: File formats
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "TERMINAL\-COLORS.D" "5" "2023-10-23" "util\-linux 2.39.3" "File formats"
+.TH "TERMINAL\-COLORS.D" "5" "2024-01-31" "util\-linux 2.40" "File formats"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/lib/timeutils.c b/lib/timeutils.c
index 34c7c8d..f53ec8f 100644
--- a/lib/timeutils.c
+++ b/lib/timeutils.c
@@ -161,6 +161,23 @@ static int parse_subseconds(const char *t, usec_t *usec)
return 0;
}
+static const char *parse_epoch_seconds(const char *t, struct tm *tm)
+{
+ int64_t s;
+ time_t st;
+ int f, c;
+
+ f = sscanf(t, "%"SCNd64"%n", &s, &c);
+ if (f < 1)
+ return NULL;
+ st = s;
+ if ((int64_t) st < s)
+ return NULL;
+ if (!localtime_r(&st, tm))
+ return NULL;
+ return t + c;
+}
+
static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
{
static const struct {
@@ -254,7 +271,7 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
goto finish;
} else if (t[0] == '@') {
- k = strptime(t + 1, "%s", &tm);
+ k = parse_epoch_seconds(t + 1, &tm);
if (k && *k == 0)
goto finish;
else if (k && parse_subseconds(k, &ret) == 0)
@@ -373,11 +390,13 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
ret += (usec_t) x * USEC_PER_SEC;
+ if (minus > ret)
+ return -ERANGE;
+ if ((ret + plus) < ret)
+ return -ERANGE;
+
ret += plus;
- if (ret > minus)
- ret -= minus;
- else
- ret = 0;
+ ret -= minus;
*usec = ret;
@@ -448,8 +467,9 @@ int get_gmtoff(const struct tm *tp)
#endif
}
-static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
+static int format_iso_time(const struct tm *tm, uint32_t nsec, int flags, char *buf, size_t bufsz)
{
+ uint32_t usec = nsec / NSEC_PER_USEC;
char *p = buf;
int len;
@@ -479,15 +499,28 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
p += len;
}
- if (flags & ISO_DOTUSEC) {
- len = snprintf(p, bufsz, ".%06"PRId64, (int64_t) usec);
+ if (flags & ISO_DOTNSEC) {
+ len = snprintf(p, bufsz, ".%09"PRIu32, nsec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+
+ } else if (flags & ISO_COMMANSEC) {
+ len = snprintf(p, bufsz, ",%09"PRIu32, nsec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+ } else if (flags & ISO_DOTUSEC) {
+ len = snprintf(p, bufsz, ".%06"PRIu32, usec);
if (len < 0 || (size_t) len > bufsz)
goto err;
bufsz -= len;
p += len;
} else if (flags & ISO_COMMAUSEC) {
- len = snprintf(p, bufsz, ",%06"PRId64, (int64_t) usec);
+ len = snprintf(p, bufsz, ",%06"PRIu32, usec);
if (len < 0 || (size_t) len > bufsz)
goto err;
bufsz -= len;
@@ -508,26 +541,37 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
return -1;
}
-/* timeval to ISO 8601 */
-int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
+/* timespec to ISO 8601 */
+int strtimespec_iso(const struct timespec *ts, int flags, char *buf, size_t bufsz)
{
struct tm tm;
struct tm *rc;
if (flags & ISO_GMTIME)
- rc = gmtime_r(&tv->tv_sec, &tm);
+ rc = gmtime_r(&ts->tv_sec, &tm);
else
- rc = localtime_r(&tv->tv_sec, &tm);
+ rc = localtime_r(&ts->tv_sec, &tm);
if (rc)
- return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
+ return format_iso_time(&tm, ts->tv_nsec, flags, buf, bufsz);
- warnx(_("time %"PRId64" is out of range."), (int64_t)(tv->tv_sec));
+ warnx(_("time %"PRId64" is out of range."), (int64_t)(ts->tv_sec));
return -1;
}
+/* timeval to ISO 8601 */
+int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz)
+{
+ struct timespec ts = {
+ .tv_sec = tv->tv_sec,
+ .tv_nsec = tv->tv_usec * NSEC_PER_USEC,
+ };
+
+ return strtimespec_iso(&ts, flags, buf, bufsz);
+}
+
/* struct tm to ISO 8601 */
-int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
+int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz)
{
return format_iso_time(tm, 0, flags, buf, bufsz);
}
@@ -592,6 +636,63 @@ int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, si
return rc <= 0 ? -1 : 0;
}
+int strtimespec_relative(const struct timespec *ts, char *buf, size_t bufsz)
+{
+ time_t secs = ts->tv_sec;
+ size_t i, parts = 0;
+ int rc;
+
+ if (bufsz)
+ buf[0] = '\0';
+
+ static const struct {
+ const char * const suffix;
+ int width;
+ int64_t secs;
+ } table[] = {
+ { "y", 4, NSEC_PER_YEAR / NSEC_PER_SEC },
+ { "d", 3, NSEC_PER_DAY / NSEC_PER_SEC },
+ { "h", 2, NSEC_PER_HOUR / NSEC_PER_SEC },
+ { "m", 2, NSEC_PER_MINUTE / NSEC_PER_SEC },
+ { "s", 2, NSEC_PER_SEC / NSEC_PER_SEC },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ if (secs >= table[i].secs) {
+ rc = snprintf(buf, bufsz,
+ "%*"PRId64"%s%s",
+ parts ? table[i].width : 0,
+ secs / table[i].secs, table[i].suffix,
+ secs % table[i].secs ? " " : "");
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ parts++;
+ buf += rc;
+ bufsz -= rc;
+ secs %= table[i].secs;
+ }
+ }
+
+ if (ts->tv_nsec) {
+ if (ts->tv_nsec % NSEC_PER_MSEC) {
+ rc = snprintf(buf, bufsz, "%*luns",
+ parts ? 10 : 0, ts->tv_nsec);
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ } else {
+ rc = snprintf(buf, bufsz, "%*llums",
+ parts ? 4 : 0, ts->tv_nsec / NSEC_PER_MSEC);
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ }
+ }
+
+ return 0;
+ err:
+ warnx(_("format_reltime: buffer overflow."));
+ return -1;
+}
+
#ifndef HAVE_TIMEGM
time_t timegm(struct tm *tm)
{
@@ -625,6 +726,7 @@ static int run_unittest_timestamp(void)
{ "2012-09-22 16:34:22.012", 1348331662012000 },
{ "@1348331662" , 1348331662000000 },
{ "@1348331662.234567" , 1348331662234567 },
+ { "@0" , 0 },
{ "2012-09-22 16:34" , 1348331640000000 },
{ "2012-09-22" , 1348272000000000 },
{ "16:34:22" , 1674232462000000 },
@@ -662,45 +764,142 @@ static int run_unittest_timestamp(void)
return rc;
}
+static int run_unittest_format(void)
+{
+ int rc = EXIT_SUCCESS;
+ const struct timespec ts = {
+ .tv_sec = 1674180427,
+ .tv_nsec = 12345,
+ };
+ char buf[FORMAT_TIMESTAMP_MAX];
+ static const struct testcase {
+ int flags;
+ const char * const expected;
+ } testcases[] = {
+ { ISO_DATE, "2023-01-20" },
+ { ISO_TIME, "02:07:07" },
+ { ISO_TIMEZONE, "+00:00" },
+ { ISO_TIMESTAMP_T, "2023-01-20T02:07:07+00:00" },
+ { ISO_TIMESTAMP_COMMA_G, "2023-01-20 02:07:07,000012+00:00" },
+ { ISO_TIME | ISO_DOTNSEC, "02:07:07.000012345" },
+ };
+
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct testcase t = testcases[i];
+ int r = strtimespec_iso(&ts, t.flags, buf, sizeof(buf));
+ if (r) {
+ fprintf(stderr, "Could not format '%s'\n", t.expected);
+ rc = EXIT_FAILURE;
+ }
+
+ if (strcmp(buf, t.expected)) {
+ fprintf(stderr, "#%02zu %-20s != %-20s\n", i, buf, t.expected);
+ rc = EXIT_FAILURE;
+ }
+ }
+
+ return rc;
+}
+
+static int run_unittest_format_relative(void)
+{
+ int rc = EXIT_SUCCESS;
+ char buf[FORMAT_TIMESTAMP_MAX];
+ static const struct testcase {
+ struct timespec ts;
+ const char * const expected;
+ } testcases[] = {
+ {{}, "" },
+ {{ 1 }, "1s" },
+ {{ 10 }, "10s" },
+ {{ 100 }, "1m 40s" },
+ {{ 1000 }, "16m 40s" },
+ {{ 10000 }, "2h 46m 40s" },
+ {{ 100000 }, "1d 3h 46m 40s" },
+ {{ 1000000 }, "11d 13h 46m 40s" },
+ {{ 10000000 }, "115d 17h 46m 40s" },
+ {{ 100000000 }, "3y 61d 15h 46m 40s" },
+ {{ 60 }, "1m" },
+ {{ 3600 }, "1h" },
+
+ {{ 1, 1 }, "1s 1ns" },
+ {{ 0, 1 }, "1ns" },
+ {{ 0, 1000000 }, "1ms" },
+ {{ 0, 1000001 }, "1000001ns" },
+ };
+
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct testcase t = testcases[i];
+ int r = strtimespec_relative(&t.ts, buf, sizeof(buf));
+ if (r) {
+ fprintf(stderr, "Could not format '%s'\n", t.expected);
+ rc = EXIT_FAILURE;
+ }
+
+ if (strcmp(buf, t.expected)) {
+ fprintf(stderr, "#%02zu '%-20s' != '%-20s'\n", i, buf, t.expected);
+ rc = EXIT_FAILURE;
+ }
+ }
+
+ return rc;
+}
+
int main(int argc, char *argv[])
{
- struct timeval tv = { 0 };
+ struct timespec ts = { 0 };
char buf[ISO_BUFSIZ];
+ int r;
if (argc < 2) {
fprintf(stderr, "usage: %s [<time> [<usec>]] | [--timestamp <str>] | [--unittest-timestamp]\n", argv[0]);
exit(EXIT_FAILURE);
}
- if (strcmp(argv[1], "--unittest-timestamp") == 0) {
+ if (strcmp(argv[1], "--unittest-timestamp") == 0)
return run_unittest_timestamp();
- }
+ else if (strcmp(argv[1], "--unittest-format") == 0)
+ return run_unittest_format();
+ else if (strcmp(argv[1], "--unittest-format-relative") == 0)
+ return run_unittest_format_relative();
if (strcmp(argv[1], "--timestamp") == 0) {
usec_t usec = 0;
- parse_timestamp(argv[2], &usec);
- tv.tv_sec = (time_t) (usec / 1000000);
- tv.tv_usec = usec % 1000000;
+ r = parse_timestamp(argv[2], &usec);
+ if (r)
+ errx(EXIT_FAILURE, "Can not parse '%s': %s", argv[2], strerror(-r));
+ ts.tv_sec = (time_t) (usec / USEC_PER_SEC);
+ ts.tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC;
} else {
- tv.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
+ ts.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
if (argc == 3)
- tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>");
+ ts.tv_nsec = strtos64_or_err(argv[2], "failed to parse <usec>")
+ * NSEC_PER_USEC;
}
- strtimeval_iso(&tv, ISO_DATE, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_DATE, buf, sizeof(buf));
printf("Date: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_TIME, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_TIME, buf, sizeof(buf));
printf("Time: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T,
+ strtimespec_iso(&ts, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T,
buf, sizeof(buf));
printf("Full: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_TIMESTAMP_DOT, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_TIMESTAMP_DOT, buf, sizeof(buf));
printf("Zone: '%s'\n", buf);
+ strtimespec_relative(&ts, buf, sizeof(buf));
+ printf("Rel: '%s'\n", buf);
+
return EXIT_SUCCESS;
}
diff --git a/lib/ttyutils.c b/lib/ttyutils.c
index 7064565..bacd730 100644
--- a/lib/ttyutils.c
+++ b/lib/ttyutils.c
@@ -7,9 +7,40 @@
#include <ctype.h>
#include <unistd.h>
+#ifdef HAVE_GETTTYNAM
+# include <ttyent.h>
+#endif
+
#include "c.h"
#include "ttyutils.h"
+#ifdef __linux__
+# ifndef DEFAULT_VCTERM
+# define DEFAULT_VCTERM "linux"
+# endif
+# if defined (__s390__) || defined (__s390x__)
+# define DEFAULT_TTYS0 "dumb"
+# define DEFAULT_TTY32 "ibm327x"
+# define DEFAULT_TTYS1 "vt220"
+# endif
+# ifndef DEFAULT_STERM
+# define DEFAULT_STERM "vt102"
+# endif
+#elif defined(__GNU__)
+# ifndef DEFAULT_VCTERM
+# define DEFAULT_VCTERM "hurd"
+# endif
+# ifndef DEFAULT_STERM
+# define DEFAULT_STERM "vt102"
+# endif
+#else
+# ifndef DEFAULT_VCTERM
+# define DEFAULT_VCTERM "vt100"
+# endif
+# ifndef DEFAULT_STERM
+# define DEFAULT_STERM "vt100"
+# endif
+#endif
static int get_env_int(const char *name)
{
@@ -130,6 +161,35 @@ int get_terminal_type(const char **type)
return 0;
}
+char *get_terminal_default_type(const char *ttyname, int is_serial)
+{
+ if (ttyname) {
+#ifdef HAVE_GETTTYNAM
+ struct ttyent *ent = getttynam(ttyname);
+
+ if (ent && ent->ty_type)
+ return strdup(ent->ty_type);
+#endif
+
+#if defined (__s390__) || defined (__s390x__)
+ /*
+ * Special terminal on first serial line on a S/390(x) which
+ * is due legacy reasons a block terminal of type 3270 or
+ * higher. Whereas the second serial line on a S/390(x) is
+ * a real character terminal which is compatible with VT220.
+ */
+ if (strcmp(ttyname, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */
+ return strdup(DEFAULT_TTYS0);
+ else if (strncmp(ttyname, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */
+ return strdup(DEFAULT_TTY32);
+ else if (strcmp(ttyname, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */
+ return strdup(DEFAULT_TTYS1);
+#endif
+ }
+
+ return strdup(is_serial ? DEFAULT_STERM : DEFAULT_VCTERM);
+}
+
#ifdef TEST_PROGRAM_TTYUTILS
# include <stdlib.h>
int main(void)
@@ -138,13 +198,21 @@ int main(void)
int c, l;
if (get_terminal_name(&path, &name, &num) == 0) {
- fprintf(stderr, "tty path: %s\n", path);
- fprintf(stderr, "tty name: %s\n", name);
- fprintf(stderr, "tty number: %s\n", num);
+ char *term;
+
+ fprintf(stderr, "tty path: %s\n", path);
+ fprintf(stderr, "tty name: %s\n", name);
+ fprintf(stderr, "tty number: %s\n", num);
+
+ fprintf(stderr, "tty term: %s\n", getenv("TERM"));
+
+ term = get_terminal_default_type(name, 0);
+ fprintf(stderr, "tty dflt term: %s\n", term);
+ free(term);
}
get_terminal_dimension(&c, &l);
- fprintf(stderr, "tty cols: %d\n", c);
- fprintf(stderr, "tty lines: %d\n", l);
+ fprintf(stderr, "tty cols: %d\n", c);
+ fprintf(stderr, "tty lines: %d\n", l);
return EXIT_SUCCESS;
diff --git a/libblkid/docs/Makefile.in b/libblkid/docs/Makefile.in
index f0007f2..ddb5709 100644
--- a/libblkid/docs/Makefile.in
+++ b/libblkid/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/libblkid/docs/libblkid-sections.txt b/libblkid/docs/libblkid-sections.txt
index 9297002..308f8ef 100644
--- a/libblkid/docs/libblkid-sections.txt
+++ b/libblkid/docs/libblkid-sections.txt
@@ -73,6 +73,7 @@ BLKID_PROBE_AMBIGUOUS
<FILE>lowprobe-tags</FILE>
blkid_do_fullprobe
blkid_do_wipe
+blkid_wipe_all
blkid_do_probe
blkid_do_safeprobe
<SUBSECTION>
diff --git a/libblkid/docs/version.xml b/libblkid/docs/version.xml
index a69af57..4bdd32f 100644
--- a/libblkid/docs/version.xml
+++ b/libblkid/docs/version.xml
@@ -1 +1 @@
-2.39.3
+2.40
diff --git a/libblkid/libblkid.3 b/libblkid/libblkid.3
index 42ee837..0297e27 100644
--- a/libblkid/libblkid.3
+++ b/libblkid/libblkid.3
@@ -2,12 +2,12 @@
.\" Title: libblkid
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-11-21
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "LIBBLKID" "3" "2023-11-21" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "LIBBLKID" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libblkid/meson.build b/libblkid/meson.build
index 0cb134f..5a28bdf 100644
--- a/libblkid/meson.build
+++ b/libblkid/meson.build
@@ -140,7 +140,7 @@ lib_blkid = both_libraries(
version : libblkid_version,
link_args : ['-Wl,--version-script=@0@'.format(libblkid_sym_path)],
link_with : lib_common,
- dependencies : build_libblkid ? [] : disabler(),
+ dependencies : build_libblkid ? [lib_econf] : disabler(),
install : build_libblkid)
blkid_dep = declare_dependency(link_with: lib_blkid, include_directories: '.')
diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am
index bc90eb8..3fae3f0 100644
--- a/libblkid/src/Makemodule.am
+++ b/libblkid/src/Makemodule.am
@@ -118,6 +118,9 @@ libblkid_la_SOURCES += \
endif
libblkid_la_LIBADD = libcommon.la
+if HAVE_ECONF
+libblkid_la_LIBADD += -leconf
+endif
EXTRA_libblkid_la_DEPENDENCIES = \
libblkid/src/libblkid.sym
diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in
index c232d72..8f6ec97 100644
--- a/libblkid/src/blkid.h.in
+++ b/libblkid/src/blkid.h.in
@@ -458,6 +458,8 @@ extern int blkid_probe_has_value(blkid_probe pr, const char *name)
__ul_attribute__((nonnull));
extern int blkid_do_wipe(blkid_probe pr, int dryrun)
__ul_attribute__((nonnull));
+extern int blkid_wipe_all(blkid_probe pr)
+ __ul_attribute__((nonnull));
extern int blkid_probe_step_back(blkid_probe pr)
__ul_attribute__((nonnull));
diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h
index 007cc35..ea7d81b 100644
--- a/libblkid/src/blkidP.h
+++ b/libblkid/src/blkidP.h
@@ -146,6 +146,7 @@ struct blkid_idmag
{
const char *magic; /* magic string */
unsigned int len; /* length of magic */
+ unsigned int hint; /* hint for prober */
const char *hoff; /* hint which contains byte offset to kboff */
long kboff; /* kilobyte offset of superblock */
@@ -205,6 +206,7 @@ struct blkid_struct_probe
int fd; /* device file descriptor */
uint64_t off; /* begin of data on the device */
uint64_t size; /* end of data on the device */
+ uint64_t io_size; /* optimal size of IO */
dev_t devno; /* device number (st.st_rdev) */
dev_t disk_devno; /* devno of the whole-disk or 0 */
@@ -220,6 +222,7 @@ struct blkid_struct_probe
struct blkid_chain *wipe_chain; /* superblock, partition, ... */
struct list_head buffers; /* list of buffers */
+ struct list_head prunable_buffers; /* list of prunable buffers */
struct list_head hints;
struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */
@@ -412,12 +415,12 @@ extern int blkdid_probe_is_opal_locked(blkid_probe pr)
__attribute__((nonnull))
__attribute__((warn_unused_result));
-extern unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+extern const unsigned char *blkid_probe_get_buffer(blkid_probe pr,
uint64_t off, uint64_t len)
__attribute__((nonnull))
__attribute__((warn_unused_result));
-extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+extern const unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
__attribute__((nonnull))
__attribute__((warn_unused_result));
@@ -433,10 +436,15 @@ extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
uint64_t *offset, const struct blkid_idmag **res)
__attribute__((nonnull(1)));
+extern void blkid_probe_prune_buffers(blkid_probe pr);
+
/* returns superblock according to 'struct blkid_idmag' */
-extern unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size);
+extern const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size);
#define blkid_probe_get_sb(_pr, _mag, type) \
- ((type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type)))
+ ((const type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type)))
+
+extern uint64_t blkid_probe_get_idmag_off(blkid_probe pr, const struct blkid_idmag *mag)
+ __attribute__((nonnull));
extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
__attribute__((nonnull))
diff --git a/libblkid/src/config.c b/libblkid/src/config.c
index d3f5eea..7b8b04f 100644
--- a/libblkid/src/config.c
+++ b/libblkid/src/config.c
@@ -22,6 +22,9 @@
#endif
#include <stdint.h>
#include <stdarg.h>
+#if defined (HAVE_LIBECONF)
+#include <libeconf.h>
+#endif
#include "blkidP.h"
#include "env.h"
@@ -55,6 +58,7 @@ err:
return -1;
}
+#ifndef HAVE_LIBECONF
static int parse_next(FILE *fd, struct blkid_config *conf)
{
char buf[BUFSIZ];
@@ -111,25 +115,29 @@ static int parse_next(FILE *fd, struct blkid_config *conf)
}
return 0;
}
+#endif /* !HAVE_LIBECONF */
/* return real config data or built-in default */
struct blkid_config *blkid_read_config(const char *filename)
{
struct blkid_config *conf;
- FILE *f;
-
- if (!filename)
- filename = safe_getenv("BLKID_CONF");
- if (!filename)
- filename = BLKID_CONFIG_FILE;
conf = calloc(1, sizeof(*conf));
if (!conf)
return NULL;
conf->uevent = -1;
- DBG(CONFIG, ul_debug("reading config file: %s.", filename));
+ if (!filename)
+ filename = safe_getenv("BLKID_CONF");
+
+#ifndef HAVE_LIBECONF
+
+ FILE *f;
+ if (!filename)
+ filename = BLKID_CONFIG_FILE;
+
+ DBG(CONFIG, ul_debug("reading config file: %s.", filename));
f = fopen(filename, "r" UL_CLOEXECSTR);
if (!f) {
DBG(CONFIG, ul_debug("%s: does not exist, using built-in default", filename));
@@ -141,6 +149,81 @@ struct blkid_config *blkid_read_config(const char *filename)
goto err;
}
}
+
+#else /* !HAVE_LIBECONF */
+
+ static econf_file *file = NULL;
+ econf_err error;
+
+ if (filename) {
+ DBG(CONFIG, ul_debug("reading config file: %s.", filename));
+ error = econf_readFile(&file, filename, "= \t", "#");
+ } else {
+ error = econf_readDirs(&file,
+#if USE_VENDORDIR
+ _PATH_VENDORDIR,
+#else
+ NULL,
+#endif
+ "/etc", "blkid", "conf", "= \t", "#");
+ }
+
+ if (error) {
+ if (error == ECONF_NOFILE) {
+ if (filename)
+ DBG(CONFIG,
+ ul_debug("%s: does not exist, using built-in default", filename));
+ else
+ DBG(CONFIG,
+ ul_debug("No configuration file blkid.conf found, using built-in default "));
+ goto dflt;
+ } else {
+ if (filename)
+ DBG(CONFIG, ul_debug("%s: parse error:%s", filename, econf_errString(error)));
+ else
+ DBG(CONFIG, ul_debug("parse error:%s", econf_errString(error)));
+
+ goto err;
+ }
+ }
+
+ bool uevent = false;
+ if ((error = econf_getBoolValue(file, NULL, "SEND_UEVENT", &uevent))) {
+ if (error != ECONF_NOKEY) {
+ DBG(CONFIG, ul_debug("couldn't fetch SEND_UEVENT corrently: %s", econf_errString(error)));
+ goto err;
+ } else {
+ DBG(CONFIG, ul_debug("key SEND_UEVENT not found, using built-in default "));
+ }
+ } else {
+ conf->uevent = uevent ? TRUE : FALSE;
+ }
+
+ if ((error = econf_getStringValue(file, NULL, "CACHE_FILE", &(conf->cachefile)))) {
+ conf->cachefile = NULL;
+ if (error != ECONF_NOKEY) {
+ DBG(CONFIG, ul_debug("couldn't fetch CACHE_FILE correctly: %s", econf_errString(error)));
+ goto err;
+ } else {
+ DBG(CONFIG, ul_debug("key CACHE_FILE not found, using built-in default "));
+ }
+ }
+
+ char *line = NULL;
+ if ((error = econf_getStringValue(file, NULL, "EVALUATE", &line))) {
+ conf->nevals = 0;
+ if (error != ECONF_NOKEY) {
+ DBG(CONFIG, ul_debug("couldn't fetch EVALUATE correctly: %s", econf_errString(error)));
+ goto err;
+ } else {
+ DBG(CONFIG, ul_debug("key CACHE_FILE not found, using built-in default "));
+ }
+ } else {
+ if (*line && parse_evaluate(conf, line) == -1)
+ goto err;
+ }
+
+#endif /* HAVE_LIBECONF */
dflt:
if (!conf->nevals) {
conf->eval[0] = BLKID_EVAL_UDEV;
@@ -151,13 +234,23 @@ dflt:
conf->cachefile = strdup(BLKID_CACHE_FILE);
if (conf->uevent == -1)
conf->uevent = TRUE;
+#ifndef HAVE_LIBECONF
if (f)
fclose(f);
+#else
+ econf_free (file);
+ free (line);
+#endif
return conf;
err:
free(conf->cachefile);
free(conf);
+#ifndef HAVE_LIBECONF
fclose(f);
+#else
+ econf_free (file);
+ free (line);
+#endif
return NULL;
}
diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c
index c38ec3d..ca13f39 100644
--- a/libblkid/src/dev.c
+++ b/libblkid/src/dev.c
@@ -155,15 +155,13 @@ int blkid_dev_set_search(blkid_dev_iterate iter,
if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type ||
!search_value)
return -1;
- new_type = malloc(strlen(search_type)+1);
- new_value = malloc(strlen(search_value)+1);
+ new_type = strdup(search_type);
+ new_value = strdup(search_value);
if (!new_type || !new_value) {
free(new_type);
free(new_value);
return -1;
}
- strcpy(new_type, search_type);
- strcpy(new_value, search_value);
free(iter->search_type);
free(iter->search_value);
iter->search_type = new_type;
diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym
index 775b024..7b2263a 100644
--- a/libblkid/src/libblkid.sym
+++ b/libblkid/src/libblkid.sym
@@ -187,3 +187,7 @@ BLKID_2_37 {
BLKID_2_39 {
blkid_topology_get_diskseq;
} BLKID_2_37;
+
+BLKID_2_40 {
+ blkid_wipe_all;
+} BLKID_2_39;
diff --git a/libblkid/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c
index ba12019..20dab6b 100644
--- a/libblkid/src/partitions/bsd.c
+++ b/libblkid/src/partitions/bsd.c
@@ -47,7 +47,7 @@ static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag)
blkid_partition parent;
blkid_partlist ls;
int i, nparts = BSD_MAXPARTITIONS;
- unsigned char *data;
+ const unsigned char *data;
int rc = BLKID_PROBE_NONE;
uint32_t abs_offset = 0;
diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c
index 5c7718c..d5a8aa3 100644
--- a/libblkid/src/partitions/dos.c
+++ b/libblkid/src/partitions/dos.c
@@ -34,7 +34,7 @@ static const struct dos_subtypes {
{ MBR_MINIX_PARTITION, &minix_pt_idinfo }
};
-static inline int is_extended(struct dos_partition *p)
+static inline int is_extended(const struct dos_partition *p)
{
return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION ||
p->sys_ind == MBR_W95_EXTENDED_PARTITION ||
@@ -46,7 +46,7 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
{
blkid_partlist ls = blkid_probe_get_partlist(pr);
uint32_t cur_start = ex_start, cur_size = ex_size;
- unsigned char *data;
+ const unsigned char *data;
int ct_nodata = 0; /* count ext.partitions without data partitions */
int i;
@@ -57,7 +57,7 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
}
while (1) {
- struct dos_partition *p, *p0;
+ const struct dos_partition *p, *p0;
uint32_t start = 0, size;
if (++ct_nodata > 100)
@@ -156,9 +156,9 @@ static inline int is_lvm(blkid_probe pr)
return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0);
}
-static inline int is_empty_mbr(unsigned char *mbr)
+static inline int is_empty_mbr(const unsigned char *mbr)
{
- struct dos_partition *p = mbr_get_partition(mbr, 0);
+ const struct dos_partition *p = mbr_get_partition(mbr, 0);
int i, nparts = 0;
for (i = 0; i < 4; i++) {
@@ -177,8 +177,8 @@ static int probe_dos_pt(blkid_probe pr,
int ssf;
blkid_parttable tab = NULL;
blkid_partlist ls;
- struct dos_partition *p0, *p;
- unsigned char *data;
+ const struct dos_partition *p0, *p;
+ const unsigned char *data;
uint32_t start, size, id;
char idstr[UUID_STR_LEN];
diff --git a/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c
index 89e7bb6..473bf5c 100644
--- a/libblkid/src/partitions/gpt.c
+++ b/libblkid/src/partitions/gpt.c
@@ -109,7 +109,7 @@ static inline uint32_t count_crc32(const unsigned char *buf, size_t len,
return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len) ^ ~0L);
}
-static inline unsigned char *get_lba_buffer(blkid_probe pr,
+static inline const unsigned char *get_lba_buffer(blkid_probe pr,
uint64_t lba, size_t bytes)
{
return blkid_probe_get_buffer(pr,
@@ -161,8 +161,8 @@ static int last_lba(blkid_probe pr, uint64_t *lba)
static int is_pmbr_valid(blkid_probe pr, int *has)
{
int flags = blkid_partitions_get_flags(pr);
- unsigned char *data;
- struct dos_partition *p;
+ const unsigned char *data;
+ const struct dos_partition *p;
int i;
if (has)
diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c
index 75a558b..36ce74e 100644
--- a/libblkid/src/partitions/mac.c
+++ b/libblkid/src/partitions/mac.c
@@ -56,7 +56,7 @@ struct mac_driver_desc {
/* there is more stuff after this that we don't need */
} __attribute__((packed));
-static inline unsigned char *get_mac_block(
+static inline const unsigned char *get_mac_block(
blkid_probe pr,
uint16_t block_size,
uint32_t num)
diff --git a/libblkid/src/partitions/minix.c b/libblkid/src/partitions/minix.c
index 43c9d9a..dee7df9 100644
--- a/libblkid/src/partitions/minix.c
+++ b/libblkid/src/partitions/minix.c
@@ -17,11 +17,11 @@
static int probe_minix_pt(blkid_probe pr,
const struct blkid_idmag *mag __attribute__((__unused__)))
{
- struct dos_partition *p;
+ const struct dos_partition *p;
blkid_parttable tab = NULL;
blkid_partition parent;
blkid_partlist ls;
- unsigned char *data;
+ const unsigned char *data;
int i;
data = blkid_probe_get_sector(pr, 0);
diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c
index 8ebf480..e096cf8 100644
--- a/libblkid/src/partitions/partitions.c
+++ b/libblkid/src/partitions/partitions.c
@@ -436,8 +436,8 @@ static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab)
/* Linux kernel has DISK_MAX_PARTS=256, but it's too much for
* generic Linux machine -- let start with 32 partitions.
*/
- void *tmp = realloc(ls->parts, (ls->nparts_max + 32) *
- sizeof(struct blkid_struct_partition));
+ void *tmp = reallocarray(ls->parts, ls->nparts_max + 32,
+ sizeof(struct blkid_struct_partition));
if (!tmp)
return NULL;
ls->parts = tmp;
@@ -557,6 +557,7 @@ static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
"%s: ---> call probefunc()", id->name));
errno = 0;
rc = id->probefunc(pr, mag);
+ blkid_probe_prune_buffers(pr);
if (rc < 0) {
/* reset after error */
reset_partlist(blkid_probe_get_partlist(pr));
diff --git a/libblkid/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c
index 9c060be..a85712f 100644
--- a/libblkid/src/partitions/ultrix.c
+++ b/libblkid/src/partitions/ultrix.c
@@ -37,8 +37,8 @@ struct ultrix_disklabel {
static int probe_ultrix_pt(blkid_probe pr,
const struct blkid_idmag *mag __attribute__((__unused__)))
{
- unsigned char *data;
- struct ultrix_disklabel *l;
+ const unsigned char *data;
+ const struct ultrix_disklabel *l;
blkid_parttable tab = NULL;
blkid_partlist ls;
int i;
@@ -50,7 +50,7 @@ static int probe_ultrix_pt(blkid_probe pr,
goto nothing;
}
- l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET);
+ l = (const struct ultrix_disklabel *) (data + ULTRIX_OFFSET);
if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1)
goto nothing;
diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c
index fbf504a..76905e1 100644
--- a/libblkid/src/probe.c
+++ b/libblkid/src/probe.c
@@ -90,6 +90,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
+#include <sys/mman.h>
#include <sys/types.h>
#ifdef HAVE_LINUX_CDROM_H
#include <linux/cdrom.h>
@@ -155,6 +156,7 @@ blkid_probe blkid_new_probe(void)
pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
}
INIT_LIST_HEAD(&pr->buffers);
+ INIT_LIST_HEAD(&pr->prunable_buffers);
INIT_LIST_HEAD(&pr->values);
INIT_LIST_HEAD(&pr->hints);
return pr;
@@ -182,6 +184,7 @@ blkid_probe blkid_clone_probe(blkid_probe parent)
pr->fd = parent->fd;
pr->off = parent->off;
pr->size = parent->size;
+ pr->io_size = parent->io_size;
pr->devno = parent->devno;
pr->disk_devno = parent->disk_devno;
pr->blkssz = parent->blkssz;
@@ -543,6 +546,16 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
return 0;
}
+static void remove_buffer(struct blkid_bufinfo *bf)
+{
+ list_del(&bf->bufs);
+
+ DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]",
+ bf->off, bf->len));
+ munmap(bf->data, bf->len);
+ free(bf);
+}
+
static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len)
{
ssize_t ret;
@@ -560,13 +573,20 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
}
/* allocate info and space for data by one malloc call */
- bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+ bf = calloc(1, sizeof(struct blkid_bufinfo));
if (!bf) {
errno = ENOMEM;
return NULL;
}
- bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+ bf->data = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (bf->data == MAP_FAILED) {
+ free(bf);
+ errno = ENOMEM;
+ return NULL;
+ }
+
bf->len = len;
bf->off = real_off;
INIT_LIST_HEAD(&bf->bufs);
@@ -577,7 +597,7 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
ret = read(pr->fd, bf->data, len);
if (ret != (ssize_t) len) {
DBG(LOWPROBE, ul_debug("\tread failed: %m"));
- free(bf);
+ remove_buffer(bf);
/* I/O errors on CDROMs are non-fatal to work with hybrid
* audio+data disks */
@@ -601,6 +621,9 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
return NULL;
}
+ if (mprotect(bf->data, len, PROT_READ))
+ DBG(LOWPROBE, ul_debug("\tmprotect failed: %m"));
+
return bf;
}
@@ -626,6 +649,39 @@ static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uin
}
/*
+ * Mark smaller buffers that can be satisfied by bf as prunable
+ */
+static void mark_prunable_buffers(blkid_probe pr, const struct blkid_bufinfo *bf)
+{
+ struct list_head *p, *next;
+
+ list_for_each_safe(p, next, &pr->buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ if (bf->off <= x->off && bf->off + bf->len >= x->off + x->len) {
+ list_del(&x->bufs);
+ list_add(&x->bufs, &pr->prunable_buffers);
+ }
+ }
+}
+
+/*
+ * Remove buffers that are marked as prunable
+ */
+void blkid_probe_prune_buffers(blkid_probe pr)
+{
+ struct list_head *p, *next;
+
+ list_for_each_safe(p, next, &pr->prunable_buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ remove_buffer(x);
+ }
+}
+
+/*
* Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer()
* will return modified buffer. This is usable when you want to call the same probing
* function more than once and hide previously detected magic strings.
@@ -657,7 +713,9 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len)
DBG(BUFFER, ul_debug("\thiding: off=%"PRIu64" len=%"PRIu64,
off, len));
+ mprotect(x->data, x->len, PROT_READ | PROT_WRITE);
memset(data, 0, len);
+ mprotect(x->data, x->len, PROT_READ);
ct++;
}
}
@@ -669,16 +727,29 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len)
* Note that @off is offset within probing area, the probing area is defined by
* pr->off and pr->size.
*/
-unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len)
+const unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len)
{
struct blkid_bufinfo *bf = NULL;
- uint64_t real_off = pr->off + off;
+ uint64_t real_off, bias, len_align;
+
+ bias = off % pr->io_size;
+ off -= bias;
+ len += bias;
+
+ if (len % pr->io_size) {
+ len_align = pr->io_size - (len % pr->io_size);
+
+ if (pr->off + off + len + len_align <= pr->size)
+ len += len_align;
+ }
+
+ real_off = pr->off + off;
/*
DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju",
off, real_off, pr->off, pr->off + pr->size, len));
*/
- if (pr->size == 0) {
+ if (pr->size == 0 || pr->io_size == 0) {
errno = EINVAL;
return NULL;
}
@@ -688,6 +759,11 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
return NULL;
}
+ if (len > 8388608 /* 8 Mib */ ) {
+ DBG(BUFFER, ul_debug("\t too large read request (ignore)"));
+ return NULL;
+ }
+
if (len == 0
|| (!S_ISCHR(pr->mode) && (pr->size < off || pr->size < len))
|| (!S_ISCHR(pr->mode) && (pr->off + pr->size < real_off + len))) {
@@ -718,6 +794,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
if (!bf)
return NULL;
+ mark_prunable_buffers(pr, bf);
list_add_tail(&bf->bufs, &pr->buffers);
}
@@ -725,7 +802,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
assert(bf->off + bf->len >= real_off + len);
errno = 0;
- return real_off ? bf->data + (real_off - bf->off) : bf->data;
+ return real_off ? bf->data + (real_off - bf->off + bias) : bf->data + bias;
}
/**
@@ -747,6 +824,8 @@ int blkid_probe_reset_buffers(blkid_probe pr)
pr->flags &= ~BLKID_FL_MODIF_BUFF;
+ blkid_probe_prune_buffers(pr);
+
if (list_empty(&pr->buffers))
return 0;
@@ -757,11 +836,8 @@ int blkid_probe_reset_buffers(blkid_probe pr)
struct blkid_bufinfo, bufs);
ct++;
len += bf->len;
- list_del(&bf->bufs);
- DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]",
- bf->off, bf->len));
- free(bf);
+ remove_buffer(bf);
}
DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls",
@@ -891,6 +967,24 @@ failed:
#endif
+#ifdef BLKIOOPT
+static uint64_t blkid_get_io_size(int fd)
+{
+ static const int ioctls[] = { BLKIOOPT, BLKIOMIN, BLKBSZGET };
+ unsigned int s;
+ size_t i;
+ int r;
+
+ for (i = 0; i < ARRAY_SIZE(ioctls); i++) {
+ r = ioctl(fd, ioctls[i], &s);
+ if (r == 0 && is_power_of_2(s) && s >= DEFAULT_SECTOR_SIZE)
+ return min(s, 1U << 16);
+ }
+
+ return DEFAULT_SECTOR_SIZE;
+}
+#endif
+
/**
* blkid_probe_set_device:
* @pr: probe
@@ -934,6 +1028,7 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
pr->fd = fd;
pr->off = (uint64_t) off;
pr->size = 0;
+ pr->io_size = DEFAULT_SECTOR_SIZE;
pr->devno = 0;
pr->disk_devno = 0;
pr->mode = 0;
@@ -1097,8 +1192,13 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
}
# endif
- DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64,
- pr->off, pr->size, pr->zone_size));
+#ifdef BLKIOOPT
+ if (S_ISBLK(sb.st_mode) && !is_floppy && !blkid_probe_is_tiny(pr))
+ pr->io_size = blkid_get_io_size(fd);
+#endif
+
+ DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64", iosize=%"PRIu64,
+ pr->off, pr->size, pr->zone_size, pr->io_size));
DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s",
blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
S_ISREG(pr->mode) ? "YES" : "NO"));
@@ -1136,14 +1236,28 @@ int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size)
return 0;
}
-unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
+const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
{
- uint64_t hint_offset;
+ uint64_t hint_offset, off;
+
+ if (mag->kboff >= 0) {
+ if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
+ hint_offset = 0;
- if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
- hint_offset = 0;
+ off = hint_offset + (mag->kboff << 10);
+ } else {
+ off = pr->size - (-mag->kboff << 10);
+ }
+
+ return blkid_probe_get_buffer(pr, off, size);
+}
- return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size);
+uint64_t blkid_probe_get_idmag_off(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ if (mag->kboff >= 0)
+ return mag->kboff << 10;
+ else
+ return pr->size - (-mag->kboff << 10);
}
/*
@@ -1164,8 +1278,8 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
/* try to detect by magic string */
while(mag && mag->magic) {
- unsigned char *buf;
- uint64_t kboff;
+ const unsigned char *buf;
+ long kboff;
uint64_t hint_offset;
if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
@@ -1182,19 +1296,20 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
else
kboff = ((mag->zonenum * pr->zone_size) >> 10) + mag->kboff_inzone;
- off = hint_offset + ((kboff + (mag->sboff >> 10)) << 10);
- buf = blkid_probe_get_buffer(pr, off, 1024);
+ if (kboff >= 0)
+ off = hint_offset + (kboff << 10) + mag->sboff;
+ else
+ off = pr->size - (-kboff << 10) + mag->sboff;
+ buf = blkid_probe_get_buffer(pr, off, mag->len);
if (!buf && errno)
return -errno;
- if (buf && !memcmp(mag->magic,
- buf + (mag->sboff & 0x3ff), mag->len)) {
-
- DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%" PRIu64,
+ if (buf && !memcmp(mag->magic, buf, mag->len)) {
+ DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld",
mag->sboff, kboff));
if (offset)
- *offset = off + (mag->sboff & 0x3ff);
+ *offset = off;
if (res)
*res = mag;
return BLKID_PROBE_OK;
@@ -1384,6 +1499,8 @@ static inline int is_conventional(blkid_probe pr __attribute__((__unused__)),
* See also blkid_probe_step_back() if you cannot use this built-in wipe
* function, but you want to use libblkid probing as a source for wiping.
*
+ * See also blkid_wipe_all() which works the same as the example above.
+ *
* Returns: 0 on success, and -1 in case of error.
*/
int blkid_do_wipe(blkid_probe pr, int dryrun)
@@ -1485,6 +1602,46 @@ int blkid_do_wipe(blkid_probe pr, int dryrun)
}
/**
+ * blkid_wipe_all:
+ * @pr: prober
+ *
+ * This function erases all detectable signatures from &pr.
+ * The @pr has to be open in O_RDWR mode. All other necessary configurations
+ * will be enabled automatically.
+ *
+ * <example>
+ * <title>wipe all filesystems or raids from the device</title>
+ * <programlisting>
+ * fd = open(devname, O_RDWR|O_CLOEXEC);
+ * blkid_probe_set_device(pr, fd, 0, 0);
+ *
+ * blkid_wipe_all(pr);
+ * </programlisting>
+ * </example>
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_wipe_all(blkid_probe pr)
+{
+ DBG(LOWPROBE, ul_debug("wiping all signatures"));
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC |
+ BLKID_SUBLKS_BADCSUM);
+
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
+ BLKID_PARTS_FORCE_GPT);
+
+ while (blkid_do_probe(pr) == 0) {
+ DBG(LOWPROBE, ul_debug("wiping one signature"));
+ blkid_do_wipe(pr, 0);
+ }
+
+ return BLKID_PROBE_OK;
+}
+
+/**
* blkid_probe_step_back:
* @pr: prober
*
@@ -1690,7 +1847,7 @@ done:
}
/* same sa blkid_probe_get_buffer() but works with 512-sectors */
-unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+const unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
{
return blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200);
}
diff --git a/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c
index 5fc5fc4..813d30e 100644
--- a/libblkid/src/superblocks/adaptec_raid.c
+++ b/libblkid/src/superblocks/adaptec_raid.c
@@ -79,9 +79,6 @@ static int probe_adraid(blkid_probe pr,
uint64_t off;
struct adaptec_metadata *ad;
- if (pr->size < 0x10000)
- return BLKID_PROBE_NONE;
-
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return BLKID_PROBE_NONE;
@@ -109,6 +106,7 @@ static int probe_adraid(blkid_probe pr,
const struct blkid_idinfo adraid_idinfo = {
.name = "adaptec_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_adraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/apfs.c b/libblkid/src/superblocks/apfs.c
index 048423a..b7f09f3 100644
--- a/libblkid/src/superblocks/apfs.c
+++ b/libblkid/src/superblocks/apfs.c
@@ -39,7 +39,7 @@ struct apfs_super_block {
static int probe_apfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct apfs_super_block *sb;
+ const struct apfs_super_block *sb;
sb = blkid_probe_get_sb(pr, mag, struct apfs_super_block);
if (!sb)
diff --git a/libblkid/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c
index d3afc41..47d9060 100644
--- a/libblkid/src/superblocks/bcache.c
+++ b/libblkid/src/superblocks/bcache.c
@@ -189,7 +189,7 @@ static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
return 0;
/* up to the end of bcs->d[] */
- csummed_size = offsetof(typeof(*bcs), d) +
+ csummed_size = offsetof(__typeof__(*bcs), d) +
sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys);
csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
@@ -199,7 +199,7 @@ static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
{
- struct bcache_super_block *bcs;
+ const struct bcache_super_block *bcs;
bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
if (!bcs)
@@ -211,9 +211,18 @@ static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
return BLKID_PROBE_NONE;
+ if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0)
+ return BLKID_PROBE_NONE;
+
if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
return BLKID_PROBE_NONE;
+ if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0)
+ return BLKID_PROBE_NONE;
+
+ if (blkid_probe_set_block_size(pr, le16_to_cpu(bcs->block_size) * 512))
+ return BLKID_PROBE_NONE;
+
blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
return BLKID_PROBE_OK;
@@ -229,7 +238,7 @@ static void probe_bcachefs_sb_members(blkid_probe pr,
uint64_t sectors = 0;
uint8_t i;
- if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices]))
+ if (BYTES(field) != offsetof(__typeof__(*members), members[bcs->nr_devices]))
return;
blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
@@ -249,7 +258,7 @@ static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
struct bcachefs_sb_field_disk_groups *disk_groups =
(struct bcachefs_sb_field_disk_groups *) field;
- if (BYTES(field) != offsetof(typeof(*disk_groups), disk_groups[bcs->nr_devices]))
+ if (BYTES(field) != offsetof(__typeof__(*disk_groups), disk_groups[bcs->nr_devices]))
return;
blkid_probe_set_id_label(pr, "LABEL_SUB",
@@ -257,7 +266,7 @@ static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
sizeof(disk_groups->disk_groups[dev_idx].label));
}
-static int is_within_range(void *start, uint64_t size, void *end)
+static int is_within_range(const void *start, uint64_t size, const void *end)
{
ptrdiff_t diff;
@@ -269,9 +278,9 @@ static int is_within_range(void *start, uint64_t size, void *end)
}
static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
- unsigned char *sb_start, unsigned char *sb_end)
+ const unsigned char *sb_start, const unsigned char *sb_end)
{
- unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
+ const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
while (1) {
struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
@@ -304,10 +313,10 @@ static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super
}
static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
- unsigned char *sb, unsigned char *sb_end)
+ const unsigned char *sb, const unsigned char *sb_end)
{
uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
- unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
+ const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
size_t checksummed_data_size = sb_end - checksummed_data_start;
switch (checksum_type) {
@@ -333,16 +342,17 @@ static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_supe
static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct bcachefs_super_block *bcs;
- unsigned char *sb, *sb_end;
- uint64_t sb_size, blocksize;
+ const struct bcachefs_super_block *bcs;
+ const unsigned char *sb, *sb_end;
+ uint64_t sb_size, blocksize, offset_sectors;
uint16_t version;
bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
if (!bcs)
return errno ? -errno : BLKID_PROBE_NONE;
- if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE)
+ offset_sectors = blkid_probe_get_idmag_off(pr, mag) / BCACHEFS_SECTOR_SIZE;
+ if (le64_to_cpu(bcs->offset) != offset_sectors)
return BLKID_PROBE_NONE;
if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
@@ -420,6 +430,18 @@ const struct blkid_idinfo bcachefs_idinfo =
.kboff = BCACHE_SB_KBOFF,
.sboff = BCACHE_SB_MAGIC_OFF,
},
+ {
+ .magic = BCACHEFS_SB_MAGIC,
+ .len = BCACHE_SB_MAGIC_LEN,
+ .kboff = 1 << 11,
+ .sboff = BCACHE_SB_MAGIC_OFF,
+ },
+ {
+ .magic = BCACHEFS_SB_MAGIC,
+ .len = BCACHE_SB_MAGIC_LEN,
+ .kboff = -(1 << 10),
+ .sboff = BCACHE_SB_MAGIC_OFF,
+ },
{ NULL }
}
};
diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c
index 5112d44..d501eb1 100644
--- a/libblkid/src/superblocks/befs.c
+++ b/libblkid/src/superblocks/befs.c
@@ -122,7 +122,7 @@ struct bplustree_node {
char name[0];
} __attribute__((packed));
-static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
+static const unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
const struct block_run *br, int fs_le)
{
return blkid_probe_get_buffer(pr,
@@ -135,7 +135,7 @@ static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_bloc
<< FS32_TO_CPU(bs->block_shift, fs_le));
}
-static unsigned char *get_custom_block_run(blkid_probe pr,
+static const unsigned char *get_custom_block_run(blkid_probe pr,
const struct befs_super_block *bs,
const struct block_run *br,
int64_t offset, uint32_t length, int fs_le)
@@ -154,7 +154,7 @@ static unsigned char *get_custom_block_run(blkid_probe pr,
length);
}
-static unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs,
+static const unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs,
const struct data_stream *ds,
int64_t start, uint32_t length, int fs_le)
{
diff --git a/libblkid/src/superblocks/bluestore.c b/libblkid/src/superblocks/bluestore.c
index 2ff1f35..f9178d9 100644
--- a/libblkid/src/superblocks/bluestore.c
+++ b/libblkid/src/superblocks/bluestore.c
@@ -31,7 +31,7 @@ struct bluestore_phdr {
static int probe_bluestore(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct bluestore_phdr *header;
+ const struct bluestore_phdr *header;
header = blkid_probe_get_sb(pr, mag, struct bluestore_phdr);
if (header == NULL)
diff --git a/libblkid/src/superblocks/btrfs.c b/libblkid/src/superblocks/btrfs.c
index b9cf4bd..114d348 100644
--- a/libblkid/src/superblocks/btrfs.c
+++ b/libblkid/src/superblocks/btrfs.c
@@ -250,7 +250,7 @@ static int btrfs_verify_csum(blkid_probe pr, const struct btrfs_super_block *bfs
static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct btrfs_super_block *bfs;
+ const struct btrfs_super_block *bfs;
if (pr->zone_size) {
#ifdef HAVE_LINUX_BLKZONED_H
diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c
index 2a87acd..6a8731f 100644
--- a/libblkid/src/superblocks/cramfs.c
+++ b/libblkid/src/superblocks/cramfs.c
@@ -37,12 +37,6 @@ struct cramfs_super
#define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */
-static int cramfs_is_little_endian(const struct blkid_idmag *mag)
-{
- assert(mag->len == 4);
- return memcmp(mag->magic, "\x45\x3d\xcd\x28", 4) == 0;
-}
-
static uint32_t cfs32_to_cpu(int le, uint32_t value)
{
if (le)
@@ -52,10 +46,10 @@ static uint32_t cfs32_to_cpu(int le, uint32_t value)
}
static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
- struct cramfs_super *cs, int le)
+ const struct cramfs_super *cs, int le)
{
uint32_t crc, expected, csummed_size;
- unsigned char *csummed;
+ const unsigned char *csummed;
expected = cfs32_to_cpu(le, cs->info.crc);
csummed_size = cfs32_to_cpu(le, cs->size);
@@ -67,22 +61,23 @@ static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
if (!csummed)
return 0;
- memset(csummed + offsetof(struct cramfs_super, info.crc), 0, sizeof(uint32_t));
- crc = ~ul_crc32(~0LL, csummed, csummed_size);
+ crc = ~ul_crc32_exclude_offset(~0LL, csummed, csummed_size,
+ offsetof(struct cramfs_super, info.crc),
+ sizeof_member(struct cramfs_super, info.crc));
return blkid_probe_verify_csum(pr, crc, expected);
}
static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct cramfs_super *cs;
+ const struct cramfs_super *cs;
cs = blkid_probe_get_sb(pr, mag, struct cramfs_super);
if (!cs)
return errno ? -errno : 1;
- int le = cramfs_is_little_endian(mag);
+ int le = mag->hint == BLKID_ENDIANNESS_LITTLE;
int v2 = cfs32_to_cpu(le, cs->flags) & CRAMFS_FLAG_FSID_VERSION_2;
if (v2 && !cramfs_verify_csum(pr, mag, cs, le))
@@ -91,8 +86,7 @@ static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag)
blkid_probe_set_label(pr, cs->name, sizeof(cs->name));
blkid_probe_set_fssize(pr, cfs32_to_cpu(le, cs->size));
blkid_probe_sprintf_version(pr, "%d", v2 ? 2 : 1);
- blkid_probe_set_fsendianness(pr,
- le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG);
+ blkid_probe_set_fsendianness(pr, mag->hint);
return 0;
}
@@ -103,8 +97,10 @@ const struct blkid_idinfo cramfs_idinfo =
.probefunc = probe_cramfs,
.magics =
{
- { .magic = "\x45\x3d\xcd\x28", .len = 4 },
- { .magic = "\x28\xcd\x3d\x45", .len = 4 },
+ { .magic = "\x45\x3d\xcd\x28", .len = 4,
+ .hint = BLKID_ENDIANNESS_LITTLE },
+ { .magic = "\x28\xcd\x3d\x45", .len = 4,
+ .hint = BLKID_ENDIANNESS_BIG },
{ NULL }
}
};
diff --git a/libblkid/src/superblocks/cs_fvault2.c b/libblkid/src/superblocks/cs_fvault2.c
index ef2b567..187258d 100644
--- a/libblkid/src/superblocks/cs_fvault2.c
+++ b/libblkid/src/superblocks/cs_fvault2.c
@@ -65,7 +65,7 @@ static int cs_fvault2_verify_csum(blkid_probe pr, const struct cs_fvault2_sb *sb
static int probe_cs_fvault2(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct cs_fvault2_sb *sb;
+ const struct cs_fvault2_sb *sb;
sb = blkid_probe_get_sb(pr, mag, struct cs_fvault2_sb);
if (!sb)
diff --git a/libblkid/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c
index 0b82e73..a7cf32c 100644
--- a/libblkid/src/superblocks/ddf_raid.c
+++ b/libblkid/src/superblocks/ddf_raid.c
@@ -80,9 +80,6 @@ static int probe_ddf(blkid_probe pr,
char version[DDF_REV_LENGTH + 1];
uint64_t off = 0, lba;
- if (pr->size < 0x30000)
- return 1;
-
for (i = 0; i < ARRAY_SIZE(hdrs); i++) {
off = ((pr->size / 0x200) - hdrs[i]) * 0x200;
@@ -106,7 +103,7 @@ static int probe_ddf(blkid_probe pr,
if (lba > 0) {
/* check primary header */
- unsigned char *buf;
+ const unsigned char *buf;
buf = blkid_probe_get_buffer(pr,
lba << 9, sizeof(ddf->signature));
@@ -134,6 +131,7 @@ static int probe_ddf(blkid_probe pr,
const struct blkid_idinfo ddfraid_idinfo = {
.name = "ddf_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x30000,
.probefunc = probe_ddf,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/drbd.c b/libblkid/src/superblocks/drbd.c
index f360186..96a2168 100644
--- a/libblkid/src/superblocks/drbd.c
+++ b/libblkid/src/superblocks/drbd.c
@@ -18,18 +18,24 @@
#include "superblocks.h"
+enum {
+ DRBD_VERSION_08,
+ DRBD_VERSION_09,
+};
+
/*
- * drbd/linux/drbd.h
+ * drbd/drbd_int.h
*/
-#define DRBD_MAGIC 0x83740267
+#define BM_BLOCK_SHIFT 12 /* 4k per bit */
+#define BM_BLOCK_SIZE (1<<BM_BLOCK_SHIFT)
/*
* user/drbdmeta.c
* We support v08 and v09
*/
-#define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4)
-#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5)
-#define DRBD_MD_MAGIC_09 (DRBD_MAGIC+6)
+#define DRBD_MD_MAGIC_08 "\x83\x74\x02\x6b"
+#define DRBD_MD_MAGIC_84_UNCLEAN "\x83\x74\x02\x6c"
+#define DRBD_MD_MAGIC_09 "\x83\x74\x02\x6d"
/* there is no DRBD_MD_MAGIC_09_UNCLEAN */
/*
@@ -70,9 +76,8 @@ struct md_on_disk_08 {
uint32_t bm_bytes_per_bit;
uint32_t reserved_u32[4];
- /* Unnecessary for libblkid **
- * char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)];
- */
+ unsigned char padding_start[0];
+ unsigned char padding_end[0] __attribute__((aligned(4096)));
};
/*
@@ -118,32 +123,34 @@ struct meta_data_on_disk_9 {
struct peer_dev_md_on_disk_9 peers[DRBD_PEERS_MAX];
uint64_t history_uuids[HISTORY_UUIDS];
- /* Unnecessary for libblkid **
- * char padding[0] __attribute__((aligned(4096)));
- */
+ unsigned char padding_start[0];
+ unsigned char padding_end[0] __attribute__((aligned(4096)));
} __attribute__((packed));
-static int probe_drbd_84(blkid_probe pr)
+static int is_zero_padded(const unsigned char *padding_start,
+ const unsigned char *padding_end)
{
- struct md_on_disk_08 *md;
- off_t off;
-
- off = pr->size - DRBD_MD_OFFSET;
+ for (; padding_start < padding_end; padding_start++) {
+ if (*padding_start != 0)
+ return 0;
+ }
+ return 1;
+}
- /* Small devices cannot be drbd (?) */
- if (pr->size < 0x10000)
- return 1;
+static int probe_drbd_84(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ const struct md_on_disk_08 *md;
- md = (struct md_on_disk_08 *)
- blkid_probe_get_buffer(pr,
- off,
- sizeof(struct md_on_disk_08));
+ md = blkid_probe_get_sb(pr, mag, struct md_on_disk_08);
if (!md)
return errno ? -errno : 1;
- if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 &&
- be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN)
+ if (be32_to_cpu(read_unaligned_member(md, bm_bytes_per_bit)) != BM_BLOCK_SIZE)
+ return 1;
+
+ if (!is_zero_padded(member_ptr(md, padding_start),
+ member_ptr(md, padding_end)))
return 1;
/*
@@ -151,43 +158,27 @@ static int probe_drbd_84(blkid_probe pr)
* notion of uuids (64 bit, see struct above)
*/
blkid_probe_sprintf_uuid(pr,
- (unsigned char *) &md->device_uuid, sizeof(md->device_uuid),
- "%" PRIx64, be64_to_cpu(md->device_uuid));
+ member_ptr(md, device_uuid), sizeof(md->device_uuid),
+ "%" PRIx64, be64_to_cpu(read_unaligned_member(md, device_uuid)));
blkid_probe_set_version(pr, "v08");
- if (blkid_probe_set_magic(pr,
- off + offsetof(struct md_on_disk_08, magic),
- sizeof(md->magic),
- (unsigned char *) &md->magic))
- return 1;
-
return 0;
}
-static int probe_drbd_90(blkid_probe pr)
+static int probe_drbd_90(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct meta_data_on_disk_9 *md;
- off_t off;
-
- off = pr->size - DRBD_MD_OFFSET;
-
- /*
- * Smaller ones are certainly not DRBD9 devices.
- * Recent utils even refuse to generate larger ones,
- * keep this as a sufficient lower bound.
- */
- if (pr->size < 0x10000)
- return 1;
+ const struct meta_data_on_disk_9 *md;
- md = (struct meta_data_on_disk_9 *)
- blkid_probe_get_buffer(pr,
- off,
- sizeof(struct meta_data_on_disk_9));
+ md = blkid_probe_get_sb(pr, mag, struct meta_data_on_disk_9);
if (!md)
return errno ? -errno : 1;
- if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_09)
+ if (be32_to_cpu(read_unaligned_member(md, bm_bytes_per_bit)) != BM_BLOCK_SIZE)
+ return 1;
+
+ if (!is_zero_padded(member_ptr(md, padding_start),
+ member_ptr(md, padding_end)))
return 1;
/*
@@ -195,30 +186,23 @@ static int probe_drbd_90(blkid_probe pr)
* notion of uuids (64 bit, see struct above)
*/
blkid_probe_sprintf_uuid(pr,
- (unsigned char *) &md->device_uuid, sizeof(md->device_uuid),
- "%" PRIx64, be64_to_cpu(md->device_uuid));
+ member_ptr(md, device_uuid), sizeof(md->device_uuid),
+ "%" PRIx64, be64_to_cpu(read_unaligned_member(md, device_uuid)));
blkid_probe_set_version(pr, "v09");
- if (blkid_probe_set_magic(pr,
- off + offsetof(struct meta_data_on_disk_9, magic),
- sizeof(md->magic),
- (unsigned char *) &md->magic))
- return 1;
-
return 0;
}
-static int probe_drbd(blkid_probe pr,
- const struct blkid_idmag *mag __attribute__((__unused__)))
+static int probe_drbd(blkid_probe pr, const struct blkid_idmag *mag)
{
- int ret;
+ if (mag->hint == DRBD_VERSION_08)
+ return probe_drbd_84(pr, mag);
- ret = probe_drbd_84(pr);
- if (ret <= 0) /* success or fatal (-errno) */
- return ret;
+ if (mag->hint == DRBD_VERSION_09)
+ return probe_drbd_90(pr, mag);
- return probe_drbd_90(pr);
+ return 1;
}
const struct blkid_idinfo drbd_idinfo =
@@ -226,6 +210,35 @@ const struct blkid_idinfo drbd_idinfo =
.name = "drbd",
.usage = BLKID_USAGE_RAID,
.probefunc = probe_drbd,
- .magics = BLKID_NONE_MAGIC
+ /*
+ * Smaller ones are certainly not DRBD9 devices.
+ * Recent utils even refuse to generate larger ones,
+ * keep this as a sufficient lower bound.
+ */
+ .minsz = 0x10000,
+ .magics = {
+ {
+ .magic = DRBD_MD_MAGIC_08,
+ .len = sizeof(DRBD_MD_MAGIC_08) - 1,
+ .hint = DRBD_VERSION_08,
+ .kboff = -(DRBD_MD_OFFSET >> 10),
+ .sboff = offsetof(struct md_on_disk_08, magic),
+ },
+ {
+ .magic = DRBD_MD_MAGIC_84_UNCLEAN,
+ .len = sizeof(DRBD_MD_MAGIC_84_UNCLEAN) - 1,
+ .hint = DRBD_VERSION_08,
+ .kboff = -(DRBD_MD_OFFSET >> 10),
+ .sboff = offsetof(struct md_on_disk_08, magic),
+ },
+ {
+ .magic = DRBD_MD_MAGIC_09,
+ .len = sizeof(DRBD_MD_MAGIC_09) - 1,
+ .hint = DRBD_VERSION_09,
+ .kboff = -(DRBD_MD_OFFSET >> 10),
+ .sboff = offsetof(struct meta_data_on_disk_9, magic),
+ },
+ { NULL }
+ }
};
diff --git a/libblkid/src/superblocks/erofs.c b/libblkid/src/superblocks/erofs.c
index 14d272e..0582246 100644
--- a/libblkid/src/superblocks/erofs.c
+++ b/libblkid/src/superblocks/erofs.c
@@ -46,7 +46,7 @@ static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
{
uint32_t expected, csum;
size_t csummed_size;
- unsigned char *csummed;
+ const unsigned char *csummed;
if (!(le32_to_cpu(sb->feature_compat) & EROFS_FEATURE_SB_CSUM))
return 1;
@@ -67,7 +67,7 @@ static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
static int probe_erofs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct erofs_super_block *sb;
+ const struct erofs_super_block *sb;
sb = blkid_probe_get_sb(pr, mag, struct erofs_super_block);
if (!sb)
diff --git a/libblkid/src/superblocks/exfat.c b/libblkid/src/superblocks/exfat.c
index fda1ecd..18a3e07 100644
--- a/libblkid/src/superblocks/exfat.c
+++ b/libblkid/src/superblocks/exfat.c
@@ -121,7 +121,7 @@ static struct exfat_entry_label *find_label(blkid_probe pr,
}
/* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */
-static uint32_t exfat_boot_checksum(unsigned char *sectors,
+static uint32_t exfat_boot_checksum(const unsigned char *sectors,
size_t sector_size)
{
uint32_t n_bytes = sector_size * 11;
@@ -143,7 +143,7 @@ static int exfat_validate_checksum(blkid_probe pr,
{
size_t sector_size = BLOCK_SIZE(sb);
/* 11 sectors will be checksummed, the 12th contains the expected */
- unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
+ const unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
if (!data)
return 0;
@@ -191,7 +191,7 @@ extern int blkid_probe_is_exfat(blkid_probe pr);
*/
int blkid_probe_is_exfat(blkid_probe pr)
{
- struct exfat_super_block *sb;
+ const struct exfat_super_block *sb;
const struct blkid_idmag *mag = NULL;
int rc;
@@ -213,7 +213,7 @@ int blkid_probe_is_exfat(blkid_probe pr)
static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct exfat_super_block *sb;
+ const struct exfat_super_block *sb;
struct exfat_entry_label *label;
sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
diff --git a/libblkid/src/superblocks/exfs.c b/libblkid/src/superblocks/exfs.c
index 87dc0a0..0a169bd 100644
--- a/libblkid/src/superblocks/exfs.c
+++ b/libblkid/src/superblocks/exfs.c
@@ -85,7 +85,7 @@ struct exfs_super_block {
(s)->sb_agblocks + EXFS_MIN_AG_BLOCKS)
-static void sb_from_disk(struct exfs_super_block *from,
+static void sb_from_disk(const struct exfs_super_block *from,
struct exfs_super_block *to)
{
@@ -121,7 +121,7 @@ static void sb_from_disk(struct exfs_super_block *from,
to->sb_frextents = be64_to_cpu(from->sb_frextents);
}
-static int exfs_verify_sb(struct exfs_super_block *ondisk)
+static int exfs_verify_sb(const struct exfs_super_block *ondisk)
{
struct exfs_super_block sb, *sbp = &sb;
@@ -158,7 +158,7 @@ static int exfs_verify_sb(struct exfs_super_block *ondisk)
static int probe_exfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct exfs_super_block *xs;
+ const struct exfs_super_block *xs;
xs = blkid_probe_get_sb(pr, mag, struct exfs_super_block);
if (!xs)
diff --git a/libblkid/src/superblocks/f2fs.c b/libblkid/src/superblocks/f2fs.c
index 980111e..4e0fdbf 100644
--- a/libblkid/src/superblocks/f2fs.c
+++ b/libblkid/src/superblocks/f2fs.c
@@ -67,14 +67,14 @@ static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off,
if (csum_off + sizeof(uint32_t) > 4096)
return 0;
- unsigned char *csum_data = blkid_probe_get_buffer(pr,
+ const unsigned char *csum_data = blkid_probe_get_buffer(pr,
sb_off + csum_off, sizeof(uint32_t));
if (!csum_data)
return 0;
uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data);
- unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off);
+ const unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off);
if (!csummed)
return 0;
@@ -85,7 +85,7 @@ static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off,
static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct f2fs_super_block *sb;
+ const struct f2fs_super_block *sb;
uint16_t vermaj, vermin;
sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block);
diff --git a/libblkid/src/superblocks/gfs.c b/libblkid/src/superblocks/gfs.c
index 445c0da..18d978a 100644
--- a/libblkid/src/superblocks/gfs.c
+++ b/libblkid/src/superblocks/gfs.c
@@ -59,7 +59,7 @@ struct gfs2_sb {
static int probe_gfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct gfs2_sb *sbd;
+ const struct gfs2_sb *sbd;
sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
if (!sbd)
@@ -91,7 +91,7 @@ static inline int gfs2_multiformat_is_valid(uint32_t multi)
static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct gfs2_sb *sbd;
+ const struct gfs2_sb *sbd;
sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
if (!sbd)
diff --git a/libblkid/src/superblocks/hfs.c b/libblkid/src/superblocks/hfs.c
index 68cb30e..49a0e60 100644
--- a/libblkid/src/superblocks/hfs.c
+++ b/libblkid/src/superblocks/hfs.c
@@ -154,7 +154,7 @@ static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t le
static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct hfs_mdb *hfs;
+ const struct hfs_mdb *hfs;
int size;
hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
@@ -183,11 +183,11 @@ static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
{
struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
- struct hfsplus_bnode_descriptor *descr;
- struct hfsplus_bheader_record *bnode;
- struct hfsplus_catalog_key *key;
- struct hfsplus_vol_header *hfsplus;
- struct hfs_mdb *sbd;
+ const struct hfsplus_bnode_descriptor *descr;
+ const struct hfsplus_bheader_record *bnode;
+ const struct hfsplus_catalog_key *key;
+ const struct hfsplus_vol_header *hfsplus;
+ const struct hfs_mdb *sbd;
unsigned int alloc_block_size;
unsigned int alloc_first_block;
unsigned int embed_first_block;
@@ -203,7 +203,7 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
unsigned int leaf_block;
int ext;
uint64_t leaf_off;
- unsigned char *buf;
+ const unsigned char *buf;
sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
if (!sbd)
@@ -217,6 +217,10 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
return 1;
alloc_block_size = be32_to_cpu(sbd->al_blk_size);
+ if (alloc_block_size < HFSPLUS_SECTOR_SIZE ||
+ alloc_block_size % HFSPLUS_SECTOR_SIZE)
+ return 1;
+
alloc_first_block = be16_to_cpu(sbd->al_bl_st);
embed_first_block = be16_to_cpu(sbd->embed_startblock);
off = (alloc_first_block * 512) +
@@ -225,7 +229,7 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
buf = blkid_probe_get_buffer(pr,
off + (mag->kboff * 1024),
sizeof(struct hfsplus_vol_header));
- hfsplus = (struct hfsplus_vol_header *) buf;
+ hfsplus = (const struct hfsplus_vol_header *) buf;
} else
hfsplus = blkid_probe_get_sb(pr, mag,
@@ -238,17 +242,23 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
(memcmp(hfsplus->signature, "HX", 2) != 0))
return 1;
- hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
-
+ /* Verify blocksize is initialized */
blocksize = be32_to_cpu(hfsplus->blocksize);
- if (blocksize < HFSPLUS_SECTOR_SIZE)
+ if (blocksize < HFSPLUS_SECTOR_SIZE || !is_power_of_2(blocksize))
return 1;
- blkid_probe_set_fsblocksize(pr, blocksize);
- blkid_probe_set_block_size(pr, blocksize);
-
+ /* Save extends (hfsplus buffer may be later overwritten) */
memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+
+ /* Make sure start_block is properly initialized */
cat_block = be32_to_cpu(extents[0].start_block);
+ if (off + ((uint64_t) cat_block * blocksize) > pr->size)
+ return 1;
+
+ hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
+
+ blkid_probe_set_fsblocksize(pr, blocksize);
+ blkid_probe_set_block_size(pr, blocksize);
buf = blkid_probe_get_buffer(pr,
off + ((uint64_t) cat_block * blocksize), 0x2000);
diff --git a/libblkid/src/superblocks/highpoint_raid.c b/libblkid/src/superblocks/highpoint_raid.c
index 2487930..c8b1297 100644
--- a/libblkid/src/superblocks/highpoint_raid.c
+++ b/libblkid/src/superblocks/highpoint_raid.c
@@ -29,8 +29,6 @@ static int probe_highpoint45x(blkid_probe pr,
uint64_t off;
uint32_t magic;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -62,6 +60,7 @@ static int probe_highpoint37x(blkid_probe pr,
const struct blkid_idinfo highpoint45x_idinfo = {
.name = "hpt45x_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_highpoint45x,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/hpfs.c b/libblkid/src/superblocks/hpfs.c
index 09bf975..f7ee02d 100644
--- a/libblkid/src/superblocks/hpfs.c
+++ b/libblkid/src/superblocks/hpfs.c
@@ -60,9 +60,9 @@ struct hpfs_spare_super
static int probe_hpfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct hpfs_super_block *hs;
- struct hpfs_spare_super *hss;
- struct hpfs_boot_block *hbb;
+ const struct hpfs_super_block *hs;
+ const struct hpfs_spare_super *hss;
+ const struct hpfs_boot_block *hbb;
uint8_t version;
/* super block */
diff --git a/libblkid/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c
index 536704b..a7a364f 100644
--- a/libblkid/src/superblocks/iso9660.c
+++ b/libblkid/src/superblocks/iso9660.c
@@ -22,7 +22,7 @@
#include "cctype.h"
#include "iso9660.h"
-struct iso9660_date {
+struct hs_date {
unsigned char year[4];
unsigned char month[2];
unsigned char day[2];
@@ -30,11 +30,16 @@ struct iso9660_date {
unsigned char minute[2];
unsigned char second[2];
unsigned char hundredth[2];
+} __attribute__ ((packed));
+
+struct iso9660_date {
+ struct hs_date common;
unsigned char offset;
} __attribute__ ((packed));
/* PVD - Primary volume descriptor */
struct iso_volume_descriptor {
+ /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value, those are skipped by blkid_probe_get_buffer() */
unsigned char vd_type;
unsigned char vd_id[5];
unsigned char vd_version;
@@ -43,21 +48,59 @@ struct iso_volume_descriptor {
unsigned char volume_id[32];
unsigned char unused[8];
unsigned char space_size[8];
- unsigned char escape_sequences[8];
- unsigned char unused2[32];
+ unsigned char escape_sequences[32];
+ unsigned char set_size[4];
+ unsigned char vol_seq_num[4];
unsigned char logical_block_size[4];
- unsigned char unused3[58];
- unsigned char volume_set_id[128];
- unsigned char publisher_id[128];
- unsigned char data_preparer_id[128];
- unsigned char application_id[128];
- unsigned char unused4[111];
- struct iso9660_date created;
- struct iso9660_date modified;
+ unsigned char path_table_size[8];
+ union {
+ struct {
+ unsigned char type_l_path_table[4];
+ unsigned char opt_type_l_path_table[4];
+ unsigned char type_m_path_table[4];
+ unsigned char opt_type_m_path_table[4];
+ unsigned char root_dir_record[34];
+ unsigned char volume_set_id[128];
+ unsigned char publisher_id[128];
+ unsigned char data_preparer_id[128];
+ unsigned char application_id[128];
+ unsigned char copyright_file_id[37];
+ unsigned char abstract_file_id[37];
+ unsigned char bibliographic_file_id[37];
+ struct iso9660_date created;
+ struct iso9660_date modified;
+ struct iso9660_date expiration;
+ struct iso9660_date effective;
+ unsigned char std_version;
+ } iso; /* ISO9660 */
+ struct {
+ unsigned char type_l_path_table[4];
+ unsigned char opt1_type_l_path_table[4];
+ unsigned char opt2_type_l_path_table[4];
+ unsigned char opt3_type_l_path_table[4];
+ unsigned char type_m_path_table[4];
+ unsigned char opt1_type_m_path_table[4];
+ unsigned char opt2_type_m_path_table[4];
+ unsigned char opt3_type_m_path_table[4];
+ unsigned char root_dir_record[34];
+ unsigned char volume_set_id[128];
+ unsigned char publisher_id[128];
+ unsigned char data_preparer_id[128];
+ unsigned char application_id[128];
+ unsigned char copyright_file_id[32];
+ unsigned char abstract_file_id[32];
+ struct hs_date created;
+ struct hs_date modified;
+ struct hs_date expiration;
+ struct hs_date effective;
+ unsigned char std_version;
+ } hs; /* High Sierra */
+ };
} __attribute__((packed));
/* Boot Record */
struct boot_record {
+ /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value, those are skipped by blkid_probe_get_buffer() */
unsigned char vd_type;
unsigned char vd_id[5];
unsigned char vd_version;
@@ -74,35 +117,9 @@ struct boot_record {
#define ISO_VD_END 0xff
#define ISO_VD_MAX 16
/* maximal string field size used anywhere in ISO; update if necessary */
-#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, volume_set_id)
-
-struct high_sierra_volume_descriptor {
- unsigned char foo[8];
- unsigned char type;
- unsigned char id[5];
- unsigned char version;
- unsigned char unused1;
- unsigned char system_id[32];
- unsigned char volume_id[32];
-} __attribute__((packed));
-
-/* old High Sierra format */
-static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag)
-{
- struct high_sierra_volume_descriptor *iso;
+#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, iso.volume_set_id)
- iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor);
- if (!iso)
- return errno ? -errno : 1;
-
- blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE);
- blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE);
- blkid_probe_set_version(pr, "High Sierra");
- blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id));
- return 0;
-}
-
-static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date)
+static int probe_iso9660_set_uuid (blkid_probe pr, const struct hs_date *date, unsigned char offset)
{
unsigned char buffer[16];
unsigned int i, zeros = 0;
@@ -130,7 +147,7 @@ static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *da
zeros++;
/* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */
- if (zeros == sizeof(buffer) && date->offset == 0)
+ if (zeros == sizeof(buffer) && offset == 0)
return 0;
/* generate an UUID using this date and return success */
@@ -216,9 +233,15 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
struct iso_volume_descriptor *joliet = NULL;
/* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */
unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2];
+ const struct hs_date *modified;
+ const struct hs_date *created;
+ unsigned char modified_offset;
+ unsigned char created_offset;
size_t len;
+ int is_hs;
int is_unicode_empty;
- int is_ascii_empty;
+ int is_ascii_hs_empty;
+ int is_ascii_iso_empty;
int i;
uint64_t off;
@@ -228,13 +251,12 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
if (off % ISO_SECTOR_SIZE)
return 1;
- if (strcmp(mag->magic, "CDROM") == 0)
- return probe_iso9660_hsfs(pr, mag);
+ is_hs = (strcmp(mag->magic, "CDROM") == 0);
- for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) {
- unsigned char *desc =
+ for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || (!is_hs && !joliet)); i++, off += ISO_SECTOR_SIZE) {
+ const unsigned char *desc =
blkid_probe_get_buffer(pr,
- off,
+ off + (is_hs ? 8 : 0), /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value */
max(sizeof(struct boot_record),
sizeof(struct iso_volume_descriptor)));
@@ -244,7 +266,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
boot = (struct boot_record *)desc;
else if (!pvd && desc[0] == ISO_VD_PRIMARY)
pvd = (struct iso_volume_descriptor *)desc;
- else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) {
+ else if (!is_hs && !joliet && desc[0] == ISO_VD_SUPPLEMENTARY) {
joliet = (struct iso_volume_descriptor *)desc;
if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 &&
memcmp(joliet->escape_sequences, "%/C", 3) != 0 &&
@@ -270,43 +292,66 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
else
blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id));
- if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
+ if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.volume_set_id, pvd->iso.volume_set_id, sizeof(pvd->iso.volume_set_id))) != 0)
blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE);
else if (joliet)
- blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE);
+ blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->iso.volume_set_id, sizeof(joliet->iso.volume_set_id), UL_ENCODE_UTF16BE);
+ else if (is_hs)
+ blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->hs.volume_set_id, sizeof(pvd->hs.volume_set_id));
else
- blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id));
+ blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->iso.volume_set_id, sizeof(pvd->iso.volume_set_id));
- is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_');
- is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_'));
- if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
+ is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.publisher_id, sizeof(pvd->hs.publisher_id)) || pvd->hs.publisher_id[0] == '_');
+ is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id)) || pvd->iso.publisher_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.publisher_id, sizeof(joliet->iso.publisher_id)) || (joliet->iso.publisher_id[0] == 0x00 && joliet->iso.publisher_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.publisher_id, pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id))) != 0)
blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE);
else if (!is_unicode_empty)
- blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE);
- else if (!is_ascii_empty)
- blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id));
-
- is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_');
- is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_'));
- if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->iso.publisher_id, sizeof(joliet->iso.publisher_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_hs_empty)
+ blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->hs.publisher_id, sizeof(pvd->hs.publisher_id));
+ else if (!is_ascii_iso_empty)
+ blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id));
+
+ is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.data_preparer_id, sizeof(pvd->hs.data_preparer_id)) || pvd->hs.data_preparer_id[0] == '_');
+ is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id)) || pvd->iso.data_preparer_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.data_preparer_id, sizeof(joliet->iso.data_preparer_id)) || (joliet->iso.data_preparer_id[0] == 0x00 && joliet->iso.data_preparer_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.data_preparer_id, pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id))) != 0)
blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE);
else if (!is_unicode_empty)
- blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE);
- else if (!is_ascii_empty)
- blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id));
-
- is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_');
- is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_'));
- if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->iso.data_preparer_id, sizeof(joliet->iso.data_preparer_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_hs_empty)
+ blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->hs.data_preparer_id, sizeof(pvd->hs.data_preparer_id));
+ else if (!is_ascii_iso_empty)
+ blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id));
+
+ is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.application_id, sizeof(pvd->hs.application_id)) || pvd->hs.application_id[0] == '_');
+ is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.application_id, sizeof(pvd->iso.application_id)) || pvd->iso.application_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.application_id, sizeof(joliet->iso.application_id)) || (joliet->iso.application_id[0] == 0x00 && joliet->iso.application_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.application_id, pvd->iso.application_id, sizeof(pvd->iso.application_id))) != 0)
blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE);
else if (!is_unicode_empty)
- blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE);
- else if (!is_ascii_empty)
- blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id));
+ blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->iso.application_id, sizeof(joliet->iso.application_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_hs_empty)
+ blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->hs.application_id, sizeof(pvd->hs.application_id));
+ else if (!is_ascii_iso_empty)
+ blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->iso.application_id, sizeof(pvd->iso.application_id));
+
+ if (is_hs) {
+ modified = &pvd->hs.modified;
+ created = &pvd->hs.created;
+ modified_offset = 0;
+ created_offset = 0;
+ } else {
+ modified = &pvd->iso.modified.common;
+ created = &pvd->iso.created.common;
+ modified_offset = pvd->iso.modified.offset;
+ created_offset = pvd->iso.created.offset;
+ }
/* create an UUID using the modified/created date */
- if (! probe_iso9660_set_uuid(pr, &pvd->modified))
- probe_iso9660_set_uuid(pr, &pvd->created);
+ if (! probe_iso9660_set_uuid(pr, modified, modified_offset))
+ probe_iso9660_set_uuid(pr, created, created_offset);
if (boot)
blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
@@ -315,6 +360,8 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
if (joliet)
blkid_probe_set_version(pr, "Joliet Extension");
+ else if (is_hs)
+ blkid_probe_set_version(pr, "High Sierra");
/* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is
* subset of ASCII but can contain up to the 32 characters. Non-representable characters are
@@ -340,6 +387,10 @@ const struct blkid_idinfo iso9660_idinfo =
.flags = BLKID_IDINFO_TOLERANT,
.magics =
{
+ /*
+ * Due to different location of vd_id[] in ISO9660 and High Sierra, IS9660 can match
+ * also High Sierra vd_id[]. So always check ISO9660 (CD001) before High Sierra (CDROM).
+ */
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" },
{ NULL }
diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c
index 81d53a1..aa03bcd 100644
--- a/libblkid/src/superblocks/isw_raid.c
+++ b/libblkid/src/superblocks/isw_raid.c
@@ -32,8 +32,6 @@ static int probe_iswraid(blkid_probe pr,
unsigned int sector_size;
struct isw_metadata *isw;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -60,6 +58,7 @@ static int probe_iswraid(blkid_probe pr,
const struct blkid_idinfo iswraid_idinfo = {
.name = "isw_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_iswraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/jfs.c b/libblkid/src/superblocks/jfs.c
index 7b75ecb..c277375 100644
--- a/libblkid/src/superblocks/jfs.c
+++ b/libblkid/src/superblocks/jfs.c
@@ -36,7 +36,7 @@ struct jfs_super_block {
static int probe_jfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct jfs_super_block *js;
+ const struct jfs_super_block *js;
js = blkid_probe_get_sb(pr, mag, struct jfs_super_block);
if (!js)
diff --git a/libblkid/src/superblocks/jmicron_raid.c b/libblkid/src/superblocks/jmicron_raid.c
index 65e05b6..4587075 100644
--- a/libblkid/src/superblocks/jmicron_raid.c
+++ b/libblkid/src/superblocks/jmicron_raid.c
@@ -75,8 +75,6 @@ static int probe_jmraid(blkid_probe pr,
uint64_t off;
const struct jm_metadata *jm;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -109,6 +107,7 @@ static int probe_jmraid(blkid_probe pr,
const struct blkid_idinfo jmraid_idinfo = {
.name = "jmicron_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_jmraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/linux_raid.c b/libblkid/src/superblocks/linux_raid.c
index 360cd4e..156b0bf 100644
--- a/libblkid/src/superblocks/linux_raid.c
+++ b/libblkid/src/superblocks/linux_raid.c
@@ -187,15 +187,14 @@ static int raid1_verify_csum(blkid_probe pr, off_t off,
{
size_t csummed_size = sizeof(struct mdp1_super_block)
+ le32_to_cpu(mdp1->max_dev) * sizeof(mdp1->dev_roles[0]);
- unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size);
+ const unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size);
if (!csummed)
return 1;
- memset(csummed + offsetof(struct mdp1_super_block, sb_csum), 0,
- sizeof(mdp1->sb_csum));
-
uint64_t csum = 0;
+ csum -= le32_to_cpu(*(uint32_t *) (csummed + offsetof(struct mdp1_super_block, sb_csum)));
+
while (csummed_size >= 4) {
csum += le32_to_cpu(*(uint32_t *) csummed);
csummed_size -= 4;
diff --git a/libblkid/src/superblocks/lsi_raid.c b/libblkid/src/superblocks/lsi_raid.c
index 697b0fe..2ceed9b 100644
--- a/libblkid/src/superblocks/lsi_raid.c
+++ b/libblkid/src/superblocks/lsi_raid.c
@@ -29,8 +29,6 @@ static int probe_lsiraid(blkid_probe pr,
uint64_t off;
struct lsi_metadata *lsi;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -53,6 +51,7 @@ static int probe_lsiraid(blkid_probe pr,
const struct blkid_idinfo lsiraid_idinfo = {
.name = "lsi_mega_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_lsiraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/luks.c b/libblkid/src/superblocks/luks.c
index 0230b34..4623c98 100644
--- a/libblkid/src/superblocks/luks.c
+++ b/libblkid/src/superblocks/luks.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008 Karel Zak <kzak@redhat.com>
- * Copyright (C) 2018 Milan Broz <gmazyland@gmail.com>
+ * Copyright (C) 2018-2024 Milan Broz <gmazyland@gmail.com>
*
* Inspired by libvolume_id by
* Kay Sievers <kay.sievers@vrfy.org>
@@ -15,6 +15,7 @@
#include <errno.h>
#include <ctype.h>
#include <stdint.h>
+#include <stdbool.h>
#include "superblocks.h"
@@ -96,6 +97,19 @@ static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t o
return BLKID_PROBE_OK;
}
+static bool luks_valid(struct luks2_phdr *header, const char *magic, uint64_t offset)
+{
+ if (memcmp(header->magic, magic, LUKS_MAGIC_L))
+ return false;
+
+ /* LUKS2 header is not at expected offset */
+ if (be16_to_cpu(header->version) == 2 &&
+ be64_to_cpu(header->hdr_offset) != offset)
+ return false;
+
+ return true;
+}
+
static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__)))
{
struct luks2_phdr *header;
@@ -105,7 +119,7 @@ static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute_
if (!header)
return errno ? -errno : BLKID_PROBE_NONE;
- if (!memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L)) {
+ if (luks_valid(header, LUKS_MAGIC, 0)) {
/* LUKS primary header was found. */
return luks_attributes(pr, header, 0);
}
@@ -118,7 +132,7 @@ static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute_
if (!header)
return errno ? -errno : BLKID_PROBE_NONE;
- if (!memcmp(header->magic, LUKS_MAGIC_2, LUKS_MAGIC_L))
+ if (luks_valid(header, LUKS_MAGIC_2, secondary_offsets[i]))
return luks_attributes(pr, header, secondary_offsets[i]);
}
diff --git a/libblkid/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c
index eea74d6..0a3d505 100644
--- a/libblkid/src/superblocks/lvm.c
+++ b/libblkid/src/superblocks/lvm.c
@@ -76,11 +76,11 @@ static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag)
int sector = mag->kboff << 1;
struct lvm2_pv_label_header *label;
char uuid[LVM2_ID_LEN + 7];
- unsigned char *buf;
+ const unsigned char *buf;
buf = blkid_probe_get_buffer(pr,
mag->kboff << 10,
- 512 + sizeof(struct lvm2_pv_label_header));
+ 512 + LVM2_LABEL_SIZE);
if (!buf)
return errno ? -errno : 1;
@@ -122,7 +122,7 @@ static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_lvm1(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct lvm1_pv_label_header *label;
+ const struct lvm1_pv_label_header *label;
char uuid[LVM2_ID_LEN + 7];
unsigned int version;
@@ -158,7 +158,7 @@ struct verity_sb {
static int probe_verity(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct verity_sb *sb;
+ const struct verity_sb *sb;
unsigned int version;
sb = blkid_probe_get_sb(pr, mag, struct verity_sb);
@@ -187,7 +187,7 @@ struct integrity_sb {
static int probe_integrity(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct integrity_sb *sb;
+ const struct integrity_sb *sb;
sb = blkid_probe_get_sb(pr, mag, struct integrity_sb);
if (sb == NULL)
diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
index c68ade9..dba299d 100644
--- a/libblkid/src/superblocks/minix.c
+++ b/libblkid/src/superblocks/minix.c
@@ -74,7 +74,7 @@ static int get_minix_version(const unsigned char *data, int *other_endian)
static int probe_minix(blkid_probe pr,
const struct blkid_idmag *mag __attribute__((__unused__)))
{
- unsigned char *ext;
+ const unsigned char *ext;
const unsigned char *data;
int version = 0, swabme = 0;
unsigned long zones, ninodes, imaps, zmaps;
diff --git a/libblkid/src/superblocks/mpool.c b/libblkid/src/superblocks/mpool.c
index b27569e..8a2e29c 100644
--- a/libblkid/src/superblocks/mpool.c
+++ b/libblkid/src/superblocks/mpool.c
@@ -26,7 +26,7 @@ struct omf_sb_descriptor {
static int probe_mpool(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct omf_sb_descriptor *osd;
+ const struct omf_sb_descriptor *osd;
uint32_t sb_crc;
osd = blkid_probe_get_sb(pr, mag, struct omf_sb_descriptor);
diff --git a/libblkid/src/superblocks/netware.c b/libblkid/src/superblocks/netware.c
index af81cf5..a8c20cb 100644
--- a/libblkid/src/superblocks/netware.c
+++ b/libblkid/src/superblocks/netware.c
@@ -67,7 +67,7 @@ struct netware_super_block {
static int probe_netware(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct netware_super_block *nw;
+ const struct netware_super_block *nw;
nw = blkid_probe_get_sb(pr, mag, struct netware_super_block);
if (!nw)
diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c
index ab8c921..8ce557a 100644
--- a/libblkid/src/superblocks/ntfs.c
+++ b/libblkid/src/superblocks/ntfs.c
@@ -80,13 +80,13 @@ struct file_attribute {
static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info)
{
- struct ntfs_super_block *ns;
- struct master_file_table_record *mft;
+ const struct ntfs_super_block *ns;
+ const struct master_file_table_record *mft;
uint32_t sectors_per_cluster, mft_record_size;
uint16_t sector_size;
uint64_t nr_clusters, off, attr_off;
- unsigned char *buf_mft;
+ const unsigned char *buf_mft;
ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
if (!ns)
diff --git a/libblkid/src/superblocks/nvidia_raid.c b/libblkid/src/superblocks/nvidia_raid.c
index d930f72..1a88dbd 100644
--- a/libblkid/src/superblocks/nvidia_raid.c
+++ b/libblkid/src/superblocks/nvidia_raid.c
@@ -41,8 +41,6 @@ static int probe_nvraid(blkid_probe pr,
uint64_t off;
struct nv_metadata *nv;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -71,6 +69,7 @@ static int probe_nvraid(blkid_probe pr,
const struct blkid_idinfo nvraid_idinfo = {
.name = "nvidia_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_nvraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/ocfs.c b/libblkid/src/superblocks/ocfs.c
index e213d66..f2b2ca2 100644
--- a/libblkid/src/superblocks/ocfs.c
+++ b/libblkid/src/superblocks/ocfs.c
@@ -100,7 +100,7 @@ struct oracle_asm_disk_label {
static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- unsigned char *buf;
+ const unsigned char *buf;
struct ocfs_volume_header ovh;
struct ocfs_volume_label ovl;
uint32_t maj, min;
@@ -142,7 +142,7 @@ static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct ocfs2_super_block *osb;
+ const struct ocfs2_super_block *osb;
osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block);
if (!osb)
@@ -165,7 +165,7 @@ static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_oracleasm(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct oracle_asm_disk_label *dl;
+ const struct oracle_asm_disk_label *dl;
dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label);
if (!dl)
diff --git a/libblkid/src/superblocks/promise_raid.c b/libblkid/src/superblocks/promise_raid.c
index 75f3a20..f891b37 100644
--- a/libblkid/src/superblocks/promise_raid.c
+++ b/libblkid/src/superblocks/promise_raid.c
@@ -33,8 +33,6 @@ static int probe_pdcraid(blkid_probe pr,
};
uint64_t nsectors;
- if (pr->size < 0x40000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -70,6 +68,7 @@ static int probe_pdcraid(blkid_probe pr,
const struct blkid_idinfo pdcraid_idinfo = {
.name = "promise_fasttrack_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x40000,
.probefunc = probe_pdcraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/reiserfs.c b/libblkid/src/superblocks/reiserfs.c
index 23d10e2..dac5f0c 100644
--- a/libblkid/src/superblocks/reiserfs.c
+++ b/libblkid/src/superblocks/reiserfs.c
@@ -41,7 +41,7 @@ struct reiser4_super_block {
static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct reiserfs_super_block *rs;
+ const struct reiserfs_super_block *rs;
unsigned int blocksize;
rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block);
@@ -82,7 +82,7 @@ static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct reiser4_super_block *rs4;
+ const struct reiser4_super_block *rs4;
unsigned int blocksize;
rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block);
diff --git a/libblkid/src/superblocks/romfs.c b/libblkid/src/superblocks/romfs.c
index 456cbfb..86c09ad 100644
--- a/libblkid/src/superblocks/romfs.c
+++ b/libblkid/src/superblocks/romfs.c
@@ -29,7 +29,7 @@ static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
{
uint32_t csummed_size = min((uint32_t) 512,
be32_to_cpu(ros->ros_full_size));
- unsigned char *csummed;
+ const unsigned char *csummed;
uint32_t csum;
if (csummed_size % sizeof(uint32_t) != 0)
@@ -50,7 +50,7 @@ static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
static int probe_romfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct romfs_super_block *ros;
+ const struct romfs_super_block *ros;
ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block);
if (!ros)
diff --git a/libblkid/src/superblocks/silicon_raid.c b/libblkid/src/superblocks/silicon_raid.c
index 399eea8..0fc223d 100644
--- a/libblkid/src/superblocks/silicon_raid.c
+++ b/libblkid/src/superblocks/silicon_raid.c
@@ -91,8 +91,6 @@ static int probe_silraid(blkid_probe pr,
uint64_t off;
struct silicon_metadata *sil;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -127,6 +125,7 @@ static int probe_silraid(blkid_probe pr,
const struct blkid_idinfo silraid_idinfo = {
.name = "silicon_medley_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_silraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/squashfs.c b/libblkid/src/superblocks/squashfs.c
index 92ab77a..8246583 100644
--- a/libblkid/src/superblocks/squashfs.c
+++ b/libblkid/src/superblocks/squashfs.c
@@ -40,7 +40,7 @@ struct sqsh_super_block {
static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct sqsh_super_block *sq;
+ const struct sqsh_super_block *sq;
uint16_t vermaj;
uint16_t vermin;
@@ -63,10 +63,10 @@ static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct sqsh_super_block *sq;
+ const struct sqsh_super_block *sq;
uint16_t vermaj;
uint16_t vermin;
- enum BLKID_ENDIANNESS endianness;
+ enum blkid_endianness endianness;
sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
if (!sq)
diff --git a/libblkid/src/superblocks/stratis.c b/libblkid/src/superblocks/stratis.c
index fe4a452..2651107 100644
--- a/libblkid/src/superblocks/stratis.c
+++ b/libblkid/src/superblocks/stratis.c
@@ -22,7 +22,7 @@
/* Macro to avoid error in struct declaration. */
#define STRATIS_UUID_LEN 32
/* Contains 4 hyphens and trailing null byte. */
-const int STRATIS_UUID_STR_LEN = 37;
+#define STRATIS_UUID_STR_LEN 37
struct stratis_sb {
uint32_t crc32;
@@ -49,7 +49,7 @@ const char STRATIS_MAGIC[] = "!Stra0tis\x86\xff\x02^\x41rh";
#define MAGIC_OFFSET_COPY_1 (FIRST_COPY_OFFSET + _MAGIC_OFFSET)
#define MAGIC_OFFSET_COPY_2 (SECOND_COPY_OFFSET + _MAGIC_OFFSET)
-static int stratis_valid_sb(uint8_t *p)
+static int stratis_valid_sb(const uint8_t *p)
{
const struct stratis_sb *stratis = (const struct stratis_sb *)p;
uint32_t crc = 0;
@@ -83,7 +83,7 @@ static int probe_stratis(blkid_probe pr,
const struct blkid_idmag *mag __attribute__((__unused__)))
{
const struct stratis_sb *stratis = NULL;
- uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE);
+ const uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE);
unsigned char uuid[STRATIS_UUID_STR_LEN];
if (!buf)
diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c
index 96bc0ef..dd1e6dc 100644
--- a/libblkid/src/superblocks/superblocks.c
+++ b/libblkid/src/superblocks/superblocks.c
@@ -94,11 +94,6 @@ static int blkid_probe_set_usage(blkid_probe pr, int usage);
*/
static const struct blkid_idinfo *idinfos[] =
{
- /* In case the volume is locked with OPAL we are going to get
- * an I/O error when reading past the LUKS header, so try it
- * first. */
- &luks_idinfo,
-
/* RAIDs */
&linuxraid_idinfo,
&ddfraid_idinfo,
@@ -124,6 +119,7 @@ static const struct blkid_idinfo *idinfos[] =
&snapcow_idinfo,
&verity_hash_idinfo,
&integrity_idinfo,
+ &luks_idinfo,
&vmfs_volume_idinfo,
&ubi_idinfo,
&vdo_idinfo,
@@ -423,6 +419,7 @@ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn)
DBG(LOWPROBE, ul_debug("\tcall probefunc()"));
errno = 0;
rc = id->probefunc(pr, mag);
+ blkid_probe_prune_buffers(pr);
if (rc != BLKID_PROBE_OK) {
blkid_probe_chain_reset_values(pr, chn);
if (rc < 0)
@@ -629,7 +626,7 @@ int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size)
block_size);
}
-int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness)
+int blkid_probe_set_fsendianness(blkid_probe pr, enum blkid_endianness endianness)
{
struct blkid_chain *chn = blkid_probe_get_chain(pr);
const char *value;
diff --git a/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h
index a990058..dc669e0 100644
--- a/libblkid/src/superblocks/superblocks.h
+++ b/libblkid/src/superblocks/superblocks.h
@@ -9,7 +9,7 @@
#include "blkidP.h"
-enum BLKID_ENDIANNESS {
+enum blkid_endianness {
BLKID_ENDIANNESS_LITTLE,
BLKID_ENDIANNESS_BIG,
};
@@ -128,9 +128,21 @@ int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size);
int blkid_probe_set_fssize(blkid_probe pr, uint64_t size);
int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock);
int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size);
-int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness);
+int blkid_probe_set_fsendianness(blkid_probe pr, enum blkid_endianness endianness);
extern int blkid_probe_is_bitlocker(blkid_probe pr);
extern int blkid_probe_is_ntfs(blkid_probe pr);
+/*
+ * utility functions
+ */
+static inline int blkid32_to_cpu(enum blkid_endianness e, uint32_t i)
+{
+ if (e == BLKID_ENDIANNESS_LITTLE)
+ return le32_to_cpu(i);
+ else if (e == BLKID_ENDIANNESS_BIG)
+ return be32_to_cpu(i);
+ abort();
+}
+
#endif /* _BLKID_SUPERBLOCKS_H */
diff --git a/libblkid/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c
index 67224bc..4876905 100644
--- a/libblkid/src/superblocks/swap.c
+++ b/libblkid/src/superblocks/swap.c
@@ -40,7 +40,7 @@ static void swap_set_info_swap1(blkid_probe pr,
const struct blkid_idmag *mag,
const struct swap_header_v1_2 *hdr)
{
- enum BLKID_ENDIANNESS endianness = le32_to_cpu(hdr->version) == 1 ?
+ enum blkid_endianness endianness = le32_to_cpu(hdr->version) == 1 ?
BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG;
blkid_probe_set_fsendianness(pr, endianness);
@@ -91,7 +91,7 @@ static int swap_set_info(blkid_probe pr, const struct blkid_idmag *mag,
static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag)
{
- unsigned char *buf;
+ const unsigned char *buf;
if (!mag)
return 1;
diff --git a/libblkid/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c
index 421660e..a9085c4 100644
--- a/libblkid/src/superblocks/sysv.c
+++ b/libblkid/src/superblocks/sysv.c
@@ -76,7 +76,7 @@ struct sysv_super_block
static int probe_xenix(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct xenix_super_block *sb;
+ const struct xenix_super_block *sb;
sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block);
if (!sb)
diff --git a/libblkid/src/superblocks/ubi.c b/libblkid/src/superblocks/ubi.c
index fbb7c5e..b15199b 100644
--- a/libblkid/src/superblocks/ubi.c
+++ b/libblkid/src/superblocks/ubi.c
@@ -35,7 +35,7 @@ static int ubi_verify_csum(blkid_probe pr, const struct ubi_ec_hdr *hdr)
static int probe_ubi(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct ubi_ec_hdr *hdr;
+ const struct ubi_ec_hdr *hdr;
hdr = blkid_probe_get_sb(pr, mag, struct ubi_ec_hdr);
if (!hdr)
diff --git a/libblkid/src/superblocks/ubifs.c b/libblkid/src/superblocks/ubifs.c
index 8dece28..c9733a9 100644
--- a/libblkid/src/superblocks/ubifs.c
+++ b/libblkid/src/superblocks/ubifs.c
@@ -105,7 +105,7 @@ static int ubifs_verify_csum(blkid_probe pr, const struct ubifs_sb_node *sb)
static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct ubifs_sb_node *sb;
+ const struct ubifs_sb_node *sb;
sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node);
if (!sb)
diff --git a/libblkid/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c
index 36adf60..76d4236 100644
--- a/libblkid/src/superblocks/udf.c
+++ b/libblkid/src/superblocks/udf.c
@@ -233,6 +233,7 @@ static int probe_udf(blkid_probe pr,
size_t i;
uint32_t vsd_len;
uint16_t udf_rev = 0;
+ int is_udf = 0;
int vsd_2048_valid = -1;
int have_label = 0;
int have_uuid = 0;
@@ -343,6 +344,8 @@ anchor:
return 1;
real_blksz:
+ /* At this stage we detected ISO/IEC 13346 or ECMA-167 filesystem recognition sequence, it does not have to be UDF */
+
/* Use the actual block size from here on out */
bs = pbs[i];
@@ -454,11 +457,17 @@ real_blksz:
lvid_loc = le32_to_cpu(vd->type.logical.lvid_location);
}
}
- if (!udf_rev) {
- /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document
- * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */
- if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0)
- udf_rev = le16_to_cpu(vd->type.logical.udf_rev);
+ if (!is_udf || !udf_rev) {
+ /* UDF-2.60: 2.2.4.3: This field shall indicate that the contents of
+ * this logical volume conforms to the domain defined in this document.
+ * This distinguish UDF from all other ISO/IEC 13346 and ECMA-167 filesystems. */
+ if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0) {
+ is_udf = 1;
+ /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document
+ * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */
+ if (!udf_rev)
+ udf_rev = le16_to_cpu(vd->type.logical.udf_rev);
+ }
}
if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) {
/* LogicalVolumeIdentifier in UDF 2.01 specification:
@@ -520,10 +529,15 @@ real_blksz:
vd->type.imp_use_volume.lvinfo1.c, clen, enc);
}
}
- if (have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid)
+ if (is_udf && have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid)
break;
}
+ if (!is_udf) {
+ /* We detected some other ISO/IEC 13346 or ECMA-167 filesystem, not UDF */
+ return 1;
+ }
+
/* Pick the first logical volume integrity descriptor and read UDF revision */
if (lvid_loc && lvid_len >= sizeof(*vd)) {
vd = (struct volume_descriptor *)
@@ -584,6 +598,7 @@ const struct blkid_idinfo udf_idinfo =
.flags = BLKID_IDINFO_TOLERANT,
.magics =
{
+ /* These magics are generic to all ISO/IEC 13346 and ECMA-167 filesystems, not just UDF */
{ .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
diff --git a/libblkid/src/superblocks/vdo.c b/libblkid/src/superblocks/vdo.c
index bec686f..4e9f445 100644
--- a/libblkid/src/superblocks/vdo.c
+++ b/libblkid/src/superblocks/vdo.c
@@ -25,7 +25,7 @@ struct vdo_super_block {
static int probe_vdo(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct vdo_super_block *vsb;
+ const struct vdo_super_block *vsb;
vsb = blkid_probe_get_sb(pr, mag, struct vdo_super_block);
if (!vsb)
diff --git a/libblkid/src/superblocks/vfat.c b/libblkid/src/superblocks/vfat.c
index 6b4a3ad..5644d89 100644
--- a/libblkid/src/superblocks/vfat.c
+++ b/libblkid/src/superblocks/vfat.c
@@ -183,8 +183,8 @@ static int search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries, u
static int fat_valid_superblock(blkid_probe pr,
const struct blkid_idmag *mag,
- struct msdos_super_block *ms,
- struct vfat_super_block *vs,
+ const struct msdos_super_block *ms,
+ const struct vfat_super_block *vs,
uint32_t *cluster_count, uint32_t *fat_size,
uint32_t *sect_count)
{
@@ -276,8 +276,8 @@ extern int blkid_probe_is_vfat(blkid_probe pr);
*/
int blkid_probe_is_vfat(blkid_probe pr)
{
- struct vfat_super_block *vs;
- struct msdos_super_block *ms;
+ const struct vfat_super_block *vs;
+ const struct msdos_super_block *ms;
const struct blkid_idmag *mag = NULL;
int rc;
@@ -301,11 +301,12 @@ int blkid_probe_is_vfat(blkid_probe pr)
* Sievers's volume_id library */
static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct vfat_super_block *vs;
- struct msdos_super_block *ms;
+ const struct vfat_super_block *vs;
+ const struct msdos_super_block *ms;
const unsigned char *vol_label = NULL;
const unsigned char *boot_label = NULL;
- unsigned char *vol_serno = NULL, vol_label_buf[11];
+ const unsigned char *vol_serno = NULL;
+ unsigned char vol_label_buf[11];
uint16_t sector_size = 0, reserved;
uint32_t cluster_count, fat_size, sect_count;
const char *version = NULL;
@@ -348,7 +349,7 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
version = "FAT16";
} else if (vs->vs_fat32_length) {
- unsigned char *buf;
+ const unsigned char *buf;
uint16_t fsinfo_sect;
int maxloop = 100;
diff --git a/libblkid/src/superblocks/via_raid.c b/libblkid/src/superblocks/via_raid.c
index ee3ab65..16e7fc2 100644
--- a/libblkid/src/superblocks/via_raid.c
+++ b/libblkid/src/superblocks/via_raid.c
@@ -51,8 +51,6 @@ static int probe_viaraid(blkid_probe pr,
uint64_t off;
struct via_metadata *v;
- if (pr->size < 0x10000)
- return 1;
if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
return 1;
@@ -84,6 +82,7 @@ static int probe_viaraid(blkid_probe pr,
const struct blkid_idinfo viaraid_idinfo = {
.name = "via_raid_member",
.usage = BLKID_USAGE_RAID,
+ .minsz = 0x10000,
.probefunc = probe_viaraid,
.magics = BLKID_NONE_MAGIC
};
diff --git a/libblkid/src/superblocks/vmfs.c b/libblkid/src/superblocks/vmfs.c
index fac87ab..3017768 100644
--- a/libblkid/src/superblocks/vmfs.c
+++ b/libblkid/src/superblocks/vmfs.c
@@ -24,7 +24,7 @@ struct vmfs_volume_info {
static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct vmfs_fs_info *header;
+ const struct vmfs_fs_info *header;
header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info);
if (header == NULL)
@@ -48,8 +48,8 @@ static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag)
static int probe_vmfs_volume(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct vmfs_volume_info *header;
- unsigned char *lvm_uuid;
+ const struct vmfs_volume_info *header;
+ const unsigned char *lvm_uuid;
header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info);
if (header == NULL)
diff --git a/libblkid/src/superblocks/vxfs.c b/libblkid/src/superblocks/vxfs.c
index be66831..c933861 100644
--- a/libblkid/src/superblocks/vxfs.c
+++ b/libblkid/src/superblocks/vxfs.c
@@ -25,23 +25,19 @@ struct vxfs_super_block {
static int probe_vxfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct vxfs_super_block *vxs;
+ const struct vxfs_super_block *vxs;
+ enum blkid_endianness e = mag->hint;
vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block);
if (!vxs)
return errno ? -errno : 1;
- if (le32_to_cpu(vxs->vs_magic) == 0xa501fcf5) {
- blkid_probe_sprintf_version(pr, "%u", (unsigned int)le32_to_cpu(vxs->vs_version));
- blkid_probe_set_fsblocksize(pr, le32_to_cpu(vxs->vs_bsize));
- blkid_probe_set_block_size(pr, le32_to_cpu(vxs->vs_bsize));
- blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_LITTLE);
- } else if (be32_to_cpu(vxs->vs_magic) == 0xa501fcf5) {
- blkid_probe_sprintf_version(pr, "%u", (unsigned int)be32_to_cpu(vxs->vs_version));
- blkid_probe_set_fsblocksize(pr, be32_to_cpu(vxs->vs_bsize));
- blkid_probe_set_block_size(pr, be32_to_cpu(vxs->vs_bsize));
- blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_BIG);
- }
+ blkid_probe_sprintf_version(pr, "%d",
+ (unsigned int)blkid32_to_cpu(e, vxs->vs_version));
+ blkid_probe_set_fsblocksize(pr, blkid32_to_cpu(e, vxs->vs_bsize));
+ blkid_probe_set_block_size(pr, blkid32_to_cpu(e, vxs->vs_bsize));
+ blkid_probe_set_fsendianness(pr, e);
+
return 0;
}
@@ -53,8 +49,10 @@ const struct blkid_idinfo vxfs_idinfo =
.probefunc = probe_vxfs,
.magics =
{
- { .magic = "\365\374\001\245", .len = 4, .kboff = 1 },
- { .magic = "\245\001\374\365", .len = 4, .kboff = 8 },
+ { .magic = "\xf5\xfc\x01\xa5", .len = 4, .kboff = 1,
+ .hint = BLKID_ENDIANNESS_LITTLE },
+ { .magic = "\xa5\x01\xfc\xf5", .len = 4, .kboff = 8,
+ .hint = BLKID_ENDIANNESS_BIG },
{ NULL }
}
};
diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c
index f0e099e..0c35982 100644
--- a/libblkid/src/superblocks/xfs.c
+++ b/libblkid/src/superblocks/xfs.c
@@ -111,7 +111,7 @@ struct xfs_super_block {
#define XFS_SB_VERSION2_CRCBIT 0x00000100
-static void sb_from_disk(struct xfs_super_block *from,
+static void sb_from_disk(const struct xfs_super_block *from,
struct xfs_super_block *to)
{
@@ -170,7 +170,7 @@ static void sb_from_disk(struct xfs_super_block *from,
to->sb_rrmapino = be64_to_cpu(from->sb_rrmapino);
}
-static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr,
+static int xfs_verify_sb(const struct xfs_super_block *ondisk, blkid_probe pr,
const struct blkid_idmag *mag)
{
struct xfs_super_block sb, *sbp = &sb;
@@ -206,7 +206,7 @@ static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr,
if ((sbp->sb_versionnum & 0x0f) == 5) {
uint32_t expected, crc;
- unsigned char *csummed;
+ const unsigned char *csummed;
if (!(sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT))
return 0;
@@ -230,7 +230,7 @@ static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr,
return 1;
}
-static uint64_t xfs_fssize(struct xfs_super_block *xs)
+static uint64_t xfs_fssize(const struct xfs_super_block *xs)
{
uint32_t lsize = xs->sb_logstart ? xs->sb_logblocks : 0;
uint64_t avail_blocks = be64_to_cpu(xs->sb_dblocks) - be32_to_cpu(lsize);
@@ -241,7 +241,7 @@ static uint64_t xfs_fssize(struct xfs_super_block *xs)
static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag)
{
- struct xfs_super_block *xs;
+ const struct xfs_super_block *xs;
xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block);
if (!xs)
@@ -331,7 +331,7 @@ static int probe_xfs_log(blkid_probe pr,
{
int i;
struct xlog_rec_header *rhead;
- unsigned char *buf;
+ const unsigned char *buf;
buf = blkid_probe_get_buffer(pr, 0, 256*1024);
if (!buf)
diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c
index 1fcc384..008a200 100644
--- a/libblkid/src/superblocks/zfs.c
+++ b/libblkid/src/superblocks/zfs.c
@@ -71,13 +71,13 @@ struct nvlist {
struct nvpair nvl_nvpair;
};
-static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
- void *value, size_t max_value_size, unsigned directory_level)
+static void zfs_process_value(blkid_probe pr, const char *name, size_t namelen,
+ const void *value, size_t max_value_size, unsigned directory_level)
{
if (strncmp(name, "name", namelen) == 0 &&
sizeof(struct nvstring) <= max_value_size &&
!directory_level) {
- struct nvstring *nvs = value;
+ const struct nvstring *nvs = value;
uint32_t nvs_type = be32_to_cpu(nvs->nvs_type);
uint32_t nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
@@ -92,7 +92,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
} else if (strncmp(name, "guid", namelen) == 0 &&
sizeof(struct nvuint64) <= max_value_size &&
!directory_level) {
- struct nvuint64 *nvu = value;
+ const struct nvuint64 *nvu = value;
uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
uint64_t nvu_value;
@@ -110,7 +110,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
} else if (strncmp(name, "pool_guid", namelen) == 0 &&
sizeof(struct nvuint64) <= max_value_size &&
!directory_level) {
- struct nvuint64 *nvu = value;
+ const struct nvuint64 *nvu = value;
uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
uint64_t nvu_value;
@@ -128,7 +128,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
"%"PRIu64, nvu_value);
} else if (strncmp(name, "ashift", namelen) == 0 &&
sizeof(struct nvuint64) <= max_value_size) {
- struct nvuint64 *nvu = value;
+ const struct nvuint64 *nvu = value;
uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
uint64_t nvu_value;
@@ -147,9 +147,9 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
{
- unsigned char *p;
- struct nvlist *nvl;
- struct nvpair *nvp;
+ const unsigned char *p;
+ const struct nvlist *nvl;
+ const struct nvpair *nvp;
size_t left = 4096;
unsigned directory_level = 0;
@@ -166,16 +166,16 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd",
(intmax_t)offset));
- nvl = (struct nvlist *) p;
+ nvl = (const struct nvlist *) p;
nvp = &nvl->nvl_nvpair;
- left -= (unsigned char *)nvp - p; /* Already used up 12 bytes */
+ left -= (const unsigned char *)nvp - p; /* Already used up 12 bytes */
while (left > sizeof(*nvp)) {
uint32_t nvp_size = be32_to_cpu(nvp->nvp_size);
uint32_t nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
uint64_t namesize = ((uint64_t)nvp_namelen + 3) & ~3;
size_t max_value_size;
- void *value;
+ const void *value;
if (!nvp->nvp_size) {
if (!directory_level)
@@ -201,7 +201,7 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
value = nvp->nvp_name + namesize;
if (sizeof(struct nvdirectory) <= max_value_size) {
- struct nvdirectory *nvu = value;
+ const struct nvdirectory *nvu = value;
if (be32_to_cpu(nvu->nvd_type) == DATA_TYPE_DIRECTORY) {
nvp_size = sizeof(*nvp) + namesize + sizeof(*nvu);
directory_level++;
@@ -259,7 +259,7 @@ static int probe_zfs(blkid_probe pr,
struct zfs_uberblock *ub = NULL;
loff_t offset = 0, ub_offset = 0;
int label_no, found = 0, found_in_label;
- void *label;
+ const void *label;
loff_t blk_align = (pr->size % (256 * 1024ULL));
DBG(PROBE, ul_debug("probe_zfs"));
diff --git a/libblkid/src/superblocks/zonefs.c b/libblkid/src/superblocks/zonefs.c
index 8aa45b0..e8fcab3 100644
--- a/libblkid/src/superblocks/zonefs.c
+++ b/libblkid/src/superblocks/zonefs.c
@@ -54,7 +54,7 @@ static int zonefs_verify_csum(blkid_probe pr, const struct zonefs_super *sb)
uint32_t expected = le32_to_cpu(sb->s_crc);
uint32_t crc = ul_crc32_exclude_offset(
~0LL, (unsigned char *) sb, sizeof(*sb),
- offsetof(typeof(*sb), s_crc), sizeof(sb->s_crc));
+ offsetof(__typeof__(*sb), s_crc), sizeof(sb->s_crc));
return blkid_probe_verify_csum(pr, crc, expected);
}
diff --git a/libblkid/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c
index 25debd0..d216cfc 100644
--- a/libblkid/src/topology/sysfs.c
+++ b/libblkid/src/topology/sysfs.c
@@ -26,7 +26,7 @@
static const struct topology_val {
/* /sys/dev/block/<maj>:<min>/<ATTR> */
- const char *attr;
+ const char * const attr;
/* functions to set probing result */
int (*set_ulong)(blkid_probe, unsigned long);
diff --git a/libblkid/src/topology/topology.c b/libblkid/src/topology/topology.c
index 67df3e3..f19a4f2 100644
--- a/libblkid/src/topology/topology.c
+++ b/libblkid/src/topology/topology.c
@@ -146,6 +146,7 @@ blkid_topology blkid_probe_get_topology(blkid_probe pr)
static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
{
size_t i;
+ int rc;
if (chn->idx < -1)
return -1;
@@ -182,9 +183,10 @@ static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
if (id->probefunc) {
DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name));
-
errno = 0;
- if (id->probefunc(pr, NULL) != 0)
+ rc = id->probefunc(pr, NULL);
+ blkid_probe_prune_buffers(pr);
+ if (rc != 0)
continue;
}
diff --git a/libfdisk/docs/Makefile.in b/libfdisk/docs/Makefile.in
index 2df4df0..7f4497e 100644
--- a/libfdisk/docs/Makefile.in
+++ b/libfdisk/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/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt
index 1589413..efc1385 100644
--- a/libfdisk/docs/libfdisk-sections.txt
+++ b/libfdisk/docs/libfdisk-sections.txt
@@ -182,6 +182,7 @@ fdisk_partition_get_size
fdisk_partition_get_start
fdisk_partition_get_type
fdisk_partition_get_uuid
+fdisk_partition_get_max_size
fdisk_partition_has_end
fdisk_partition_has_partno
fdisk_partition_has_size
diff --git a/libfdisk/docs/version.xml b/libfdisk/docs/version.xml
index a69af57..4bdd32f 100644
--- a/libfdisk/docs/version.xml
+++ b/libfdisk/docs/version.xml
@@ -1 +1 @@
-2.39.3
+2.40
diff --git a/libfdisk/samples/Makemodule.am b/libfdisk/samples/Makemodule.am
index b67b121..0c34436 100644
--- a/libfdisk/samples/Makemodule.am
+++ b/libfdisk/samples/Makemodule.am
@@ -3,8 +3,7 @@ check_PROGRAMS += \
sample-fdisk-mkpart \
sample-fdisk-mkpart-fullspec
-sample_fdisk_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \
- -I$(ul_libfdisk_incdir)
+sample_fdisk_cflags = $(AM_CFLAGS) -I$(ul_libfdisk_incdir)
sample_fdisk_ldadd = $(LDADD) libfdisk.la
sample_fdisk_mkpart_SOURCES = libfdisk/samples/mkpart.c
diff --git a/libfdisk/samples/mkpart-fullspec.c b/libfdisk/samples/mkpart-fullspec.c
index 821a688..0dca3f1 100644
--- a/libfdisk/samples/mkpart-fullspec.c
+++ b/libfdisk/samples/mkpart-fullspec.c
@@ -27,7 +27,7 @@
static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
struct fdisk_ask *ask,
- void *data)
+ void *data __attribute__((unused)))
{
switch(fdisk_ask_get_type(ask)) {
case FDISK_ASKTYPE_INFO:
diff --git a/libfdisk/samples/mkpart.c b/libfdisk/samples/mkpart.c
index 1e5fd99..766e751 100644
--- a/libfdisk/samples/mkpart.c
+++ b/libfdisk/samples/mkpart.c
@@ -39,7 +39,7 @@
static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
struct fdisk_ask *ask,
- void *data)
+ void *data __attribute__((unused)))
{
switch(fdisk_ask_get_type(ask)) {
case FDISK_ASKTYPE_INFO:
diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am
index 9bd64c1..073d571 100644
--- a/libfdisk/src/Makemodule.am
+++ b/libfdisk/src/Makemodule.am
@@ -67,7 +67,7 @@ check_PROGRAMS += \
test_fdisk_version \
test_fdisk_item
-libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
+libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS)
libfdisk_tests_ldflags = libuuid.la -static
libfdisk_tests_ldadd = libfdisk.la $(LDADD)
@@ -102,7 +102,7 @@ check_PROGRAMS += test_fdisk_script_fuzz
nodist_EXTRA_test_fdisk_script_fuzz_SOURCES = dummy.cxx
test_fdisk_script_fuzz_SOURCES = libfdisk/src/script.c
-test_fdisk_script_fuzz_CFLAGS = -DFUZZ_TARGET $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
+test_fdisk_script_fuzz_CFLAGS = -DFUZZ_TARGET $(libfdisk_la_CFLAGS)
test_fdisk_script_fuzz_LDFLAGS = $(libfdisk_tests_ldflags) -lpthread
test_fdisk_script_fuzz_LDADD = $(libfdisk_tests_ldadd) $(LIB_FUZZING_ENGINE)
endif
diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c
index 274f6ba..299f65b 100644
--- a/libfdisk/src/ask.c
+++ b/libfdisk/src/ask.c
@@ -1035,7 +1035,9 @@ int fdisk_info_new_partition(
}
#ifdef TEST_PROGRAM
-static int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
+static int test_ranges(struct fdisk_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
/* 1 - 3, 6, 8, 9, 11 13 */
size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
diff --git a/libfdisk/src/bsd.c b/libfdisk/src/bsd.c
index 313ae5a..cb45e3f 100644
--- a/libfdisk/src/bsd.c
+++ b/libfdisk/src/bsd.c
@@ -30,7 +30,7 @@
*
*/
-static const char *bsd_dktypenames[] = {
+static const char * const bsd_dktypenames[] = {
"unknown",
"SMD",
"MSCP",
@@ -46,7 +46,7 @@ static const char *bsd_dktypenames[] = {
};
#define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1)
-static struct fdisk_parttype bsd_fstypes[] = {
+static const struct fdisk_parttype bsd_fstypes[] = {
{BSD_FS_UNUSED, "unused"},
{BSD_FS_SWAP, "swap"},
{BSD_FS_V6, "Version 6"},
diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c
index 0d22124..463a60f 100644
--- a/libfdisk/src/context.c
+++ b/libfdisk/src/context.c
@@ -773,7 +773,7 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
cxt->dev_path);
return -errno;
}
- if (!nosync) {
+ if (S_ISBLK(cxt->dev_st.st_mode) && !nosync) {
fdisk_info(cxt, _("Syncing disks."));
sync();
}
diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c
index 7970ae1..ecf0b36 100644
--- a/libfdisk/src/dos.c
+++ b/libfdisk/src/dos.c
@@ -67,7 +67,7 @@ struct fdisk_dos_label {
/*
* Partition types
*/
-static struct fdisk_parttype dos_parttypes[] = {
+static const struct fdisk_parttype dos_parttypes[] = {
#include "pt-mbr-partnames.h"
};
@@ -792,7 +792,7 @@ static void get_partition_table_geometry(struct fdisk_context *cxt,
unsigned char *bufp = cxt->firstsector;
struct { unsigned int c, h, o, v; } t[8];
unsigned int n1, n2, n3, n4, n5, n6;
- struct dos_partition *p;
+ const struct dos_partition *p;
unsigned int c, h, s, l;
unsigned int hh, ss;
unsigned int sects;
@@ -1727,14 +1727,22 @@ static int dos_verify_disklabel(struct fdisk_context *cxt)
{
size_t i, j;
fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
- fdisk_sector_t first[cxt->label->nparts_max],
- last[cxt->label->nparts_max];
+ fdisk_sector_t *first, *last;
struct dos_partition *p;
struct fdisk_dos_label *l = self_label(cxt);
int nerrors = 0;
assert(fdisk_is_label(cxt, DOS));
+ first = calloc(cxt->label->nparts_max, sizeof(*first));
+ last = calloc(cxt->label->nparts_max, sizeof(*first));
+
+ if (!first || !last) {
+ free(first);
+ free(last);
+ return -ENOMEM;
+ }
+
fill_bounds(cxt, first, last);
for (i = 0; i < cxt->label->nparts_max; i++) {
struct pte *pe = self_pte(cxt, i);
@@ -1818,6 +1826,8 @@ static int dos_verify_disklabel(struct fdisk_context *cxt)
P_("%d error detected.", "%d errors detected.", nerrors),
nerrors);
+ free(first);
+ free(last);
return nerrors;
}
diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h
index 7c0830e..49e057f 100644
--- a/libfdisk/src/fdiskP.h
+++ b/libfdisk/src/fdiskP.h
@@ -286,7 +286,7 @@ enum {
struct fdisk_label {
const char *name; /* label name */
enum fdisk_labeltype id; /* FDISK_DISKLABEL_* */
- struct fdisk_parttype *parttypes; /* supported partitions types */
+ const struct fdisk_parttype *parttypes; /* supported partitions types */
size_t nparttypes; /* number of items in parttypes[] */
const struct fdisk_shortcut *parttype_cuts; /* partition type shortcuts */
diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c
index c3c0347..1c19d86 100644
--- a/libfdisk/src/gpt.c
+++ b/libfdisk/src/gpt.c
@@ -152,19 +152,20 @@ struct gpt_legacy_mbr {
.name = (_n), \
}
-static struct fdisk_parttype gpt_parttypes[] =
+static const struct fdisk_parttype gpt_parttypes[] =
{
#include "pt-gpt-partnames.h"
};
static const struct fdisk_shortcut gpt_parttype_cuts[] =
{
- { .shortcut = "L", .alias = "linux", .data = "0FC63DAF-8483-4772-8E79-3D69D8477DE4" }, /* Linux */
- { .shortcut = "S", .alias = "swap", .data = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" }, /* Swap */
- { .shortcut = "H", .alias = "home", .data = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915" }, /* Home */
- { .shortcut = "U", .alias = "uefi", .data = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" }, /* UEFI system */
- { .shortcut = "R", .alias = "raid", .data = "A19D880F-05FC-4D3B-A006-743F0F84911E" }, /* Linux RAID */
- { .shortcut = "V", .alias = "lvm", .data = "E6D6D379-F507-44C2-A23C-238F2A3DF928" } /* LVM */
+ { .shortcut = "L", .alias = "linux", .data = "0FC63DAF-8483-4772-8E79-3D69D8477DE4" }, /* Linux */
+ { .shortcut = "S", .alias = "swap", .data = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" }, /* Swap */
+ { .shortcut = "H", .alias = "home", .data = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915" }, /* Home */
+ { .shortcut = "U", .alias = "uefi", .data = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" }, /* UEFI system */
+ { .shortcut = "R", .alias = "raid", .data = "A19D880F-05FC-4D3B-A006-743F0F84911E" }, /* Linux RAID */
+ { .shortcut = "V", .alias = "lvm", .data = "E6D6D379-F507-44C2-A23C-238F2A3DF928" }, /* LVM */
+ { .shortcut = "X", .alias = "xbootldr", .data = "BC13C2FF-59E6-4262-A352-B275FD6F7172" }, /* Linux extended boot */
};
#define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
@@ -3322,8 +3323,12 @@ void fdisk_gpt_enable_minimize(struct fdisk_label *lb, int enable)
}
#ifdef TEST_PROGRAM
-static int test_getattr(struct fdisk_test *ts, int argc, char *argv[])
+static int test_getattr(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
const char *disk = argv[1];
size_t part = strtoul(argv[2], NULL, 0) - 1;
struct fdisk_context *cxt;
@@ -3344,8 +3349,12 @@ static int test_getattr(struct fdisk_test *ts, int argc, char *argv[])
return 0;
}
-static int test_setattr(struct fdisk_test *ts, int argc, char *argv[])
+static int test_setattr(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 4)
+ return -1;
+
const char *disk = argv[1];
size_t part = strtoul(argv[2], NULL, 0) - 1;
uint64_t atters = strtoull(argv[3], NULL, 0);
diff --git a/libfdisk/src/item.c b/libfdisk/src/item.c
index 671f9ad..f46e646 100644
--- a/libfdisk/src/item.c
+++ b/libfdisk/src/item.c
@@ -199,8 +199,12 @@ int fdisk_labelitem_is_number(struct fdisk_labelitem *li)
}
#ifdef TEST_PROGRAM
-static int test_listitems(struct fdisk_test *ts, int argc, char *argv[])
+static int test_listitems(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
const char *disk = argv[1];
struct fdisk_context *cxt;
struct fdisk_labelitem *item;
diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in
index 1c97149..9c20f44 100644
--- a/libfdisk/src/libfdisk.h.in
+++ b/libfdisk/src/libfdisk.h.in
@@ -540,6 +540,9 @@ extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
extern int fdisk_partition_has_wipe(struct fdisk_context *cxt, struct fdisk_partition *pa);
+extern int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n,
+ fdisk_sector_t *maxsz);
+
/* table.c */
extern struct fdisk_table *fdisk_new_table(void);
diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym
index 71de805..bb69e93 100644
--- a/libfdisk/src/libfdisk.sym
+++ b/libfdisk/src/libfdisk.sym
@@ -320,3 +320,7 @@ FDISK_2.36 {
FDISK_2.38 {
fdisk_dos_fix_chs;
} FDISK_2.36;
+
+FDISK_2.40 {
+ fdisk_partition_get_max_size;
+} FDISK_2.38;
diff --git a/libfdisk/src/partition.c b/libfdisk/src/partition.c
index bf3afad..1fd3176 100644
--- a/libfdisk/src/partition.c
+++ b/libfdisk/src/partition.c
@@ -1625,3 +1625,55 @@ int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
return cxt->label->op->part_is_used(cxt, n);
}
+
+/**
+ * fdisk_partition_max_size:
+ * @cxt: context
+ * @n: partition number (0 is the first partition)
+ * @maxsz: returns maximum size of partition
+ *
+ * Find maximum size the partition can be resized to.
+ * Takes into account free space between this partition and the next one.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n,
+ fdisk_sector_t *maxsz)
+{
+ struct fdisk_partition *cur = NULL;
+ struct fdisk_table *tb = NULL;
+ int rc;
+
+ rc = fdisk_get_partitions(cxt, &tb);
+ if (rc)
+ goto out;
+
+ rc = fdisk_get_freespaces(cxt, &tb);
+ if (rc)
+ goto out;
+
+ rc = fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
+ if (rc)
+ goto out;
+
+ cur = fdisk_table_get_partition_by_partno(tb, n);
+ if (!cur)
+ goto einval;
+
+ if (!fdisk_partition_has_start(cur))
+ goto einval;
+
+ if (resize_get_last_possible(tb, cur,
+ fdisk_partition_get_start(cur), maxsz))
+ goto einval;
+
+out:
+ fdisk_unref_partition(cur);
+ fdisk_unref_table(tb);
+
+ return rc;
+
+einval:
+ rc = -EINVAL;
+ goto out;
+}
diff --git a/libfdisk/src/parttype.c b/libfdisk/src/parttype.c
index 271b671..8566932 100644
--- a/libfdisk/src/parttype.c
+++ b/libfdisk/src/parttype.c
@@ -145,7 +145,7 @@ struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, si
{
if (!lb || n >= lb->nparttypes)
return NULL;
- return &lb->parttypes[n];
+ return (struct fdisk_parttype *)&lb->parttypes[n];
}
/**
@@ -237,7 +237,7 @@ struct fdisk_parttype *fdisk_label_get_parttype_from_code(
for (i = 0; i < lb->nparttypes; i++)
if (lb->parttypes[i].code == code)
- return &lb->parttypes[i];
+ return (struct fdisk_parttype *)&lb->parttypes[i];
return NULL;
}
@@ -265,7 +265,7 @@ struct fdisk_parttype *fdisk_label_get_parttype_from_string(
for (i = 0; i < lb->nparttypes; i++)
if (lb->parttypes[i].typestr
&& strcasecmp(lb->parttypes[i].typestr, str) == 0)
- return &lb->parttypes[i];
+ return (struct fdisk_parttype *)&lb->parttypes[i];
return NULL;
}
@@ -324,7 +324,8 @@ static struct fdisk_parttype *parttype_from_data(
unsigned int *xcode,
int use_seqnum)
{
- struct fdisk_parttype *types, *ret = NULL;
+ const struct fdisk_parttype *types;
+ struct fdisk_parttype *ret = NULL;
char *end = NULL;
assert(lb);
@@ -369,7 +370,7 @@ static struct fdisk_parttype *parttype_from_data(
if (use_seqnum && errno == 0
&& *end == '\0' && i > 0
&& i - 1 < (int) lb->nparttypes)
- ret = &types[i - 1];
+ ret = (struct fdisk_parttype *)&types[i - 1];
}
}
@@ -428,7 +429,7 @@ static struct fdisk_parttype *parttype_from_name(
const char *name = lb->parttypes[i].name;
if (name && *name && ul_stralnumcmp(name, str) == 0)
- return &lb->parttypes[i];
+ return (struct fdisk_parttype *)&lb->parttypes[i];
}
return NULL;
diff --git a/libfdisk/src/script.c b/libfdisk/src/script.c
index f537a7d..e357fad 100644
--- a/libfdisk/src/script.c
+++ b/libfdisk/src/script.c
@@ -1108,7 +1108,7 @@ static int parse_size_value(struct fdisk_script *dp, struct fdisk_partition *pa,
done:
DBG(SCRIPT, ul_debugobj(dp, " size parse result: rc=%d, move=%s, size=%ju, default=%s",
rc, pa->resize == FDISK_RESIZE_REDUCE ? "reduce" :
- pa->resize == FDISK_RESIZE_ENLARGE ? "enlage" : "none",
+ pa->resize == FDISK_RESIZE_ENLARGE ? "enlarge" : "none",
pa->size,
pa->end_follow_default ? "on" : "off"));
return rc;
@@ -1669,8 +1669,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
#ifdef TEST_PROGRAM
-static int test_dump(struct fdisk_test *ts, int argc, char *argv[])
+static int test_dump(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *devname = argv[1];
struct fdisk_context *cxt;
struct fdisk_script *dp;
@@ -1688,8 +1692,12 @@ static int test_dump(struct fdisk_test *ts, int argc, char *argv[])
return 0;
}
-static int test_read(struct fdisk_test *ts, int argc, char *argv[])
+static int test_read(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *filename = argv[1];
struct fdisk_script *dp;
struct fdisk_context *cxt;
@@ -1711,8 +1719,12 @@ static int test_read(struct fdisk_test *ts, int argc, char *argv[])
return 0;
}
-static int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
+static int test_stdin(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[] __attribute__((unused)))
{
+ if (argc != 1)
+ return -1;
+
char buf[BUFSIZ] = { '\0' };
struct fdisk_script *dp;
struct fdisk_context *cxt;
@@ -1746,8 +1758,12 @@ static int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
return rc;
}
-static int test_apply(struct fdisk_test *ts, int argc, char *argv[])
+static int test_apply(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *devname = argv[1], *scriptname = argv[2];
struct fdisk_context *cxt;
struct fdisk_script *dp;
@@ -1788,8 +1804,12 @@ done:
return 0;
}
-static int test_tokens(struct fdisk_test *ts, int argc, char *argv[])
+static int test_tokens(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *p, *str = argc == 2 ? strdup(argv[1]) : NULL;
int i;
diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c
index 6740535..23aafd9 100644
--- a/libfdisk/src/sgi.c
+++ b/libfdisk/src/sgi.c
@@ -41,7 +41,7 @@ struct fdisk_sgi_label {
} freelist[SGI_MAXPARTITIONS + 1];
};
-static struct fdisk_parttype sgi_parttypes[] =
+static const struct fdisk_parttype sgi_parttypes[] =
{
{SGI_TYPE_VOLHDR, N_("SGI volhdr")},
{SGI_TYPE_TRKREPL, N_("SGI trkrepl")},
@@ -123,10 +123,12 @@ static struct sgi_info *sgi_new_info(void)
info->b3 = cpu_to_be16(1);
/* You may want to replace this string !!!!!!! */
- strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30");
- strcpy((char *) info->serial, "0000");
+ strncpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30",
+ sizeof(info->scsi_string));
+ strncpy((char *) info->serial, "0000", sizeof(info->serial));
info->check1816 = cpu_to_be16(18 * 256 + 16);
- strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
+ strncpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994",
+ sizeof(info->installer));
return info;
}
@@ -1009,7 +1011,8 @@ static int sgi_create_disklabel(struct fdisk_context *cxt)
/* sizeof(sgilabel->boot_file) = 16 > 6 */
memset(sgilabel->boot_file, 0, 16);
- strcpy((char *) sgilabel->boot_file, "/unix");
+ strncpy((char *) sgilabel->boot_file, "/unix",
+ sizeof(sgilabel->boot_file));
sgilabel->devparam.skew = (0);
sgilabel->devparam.gap1 = (0);
diff --git a/libfdisk/src/sun.c b/libfdisk/src/sun.c
index dde9750..cd965ab 100644
--- a/libfdisk/src/sun.c
+++ b/libfdisk/src/sun.c
@@ -35,7 +35,7 @@ struct fdisk_sun_label {
struct sun_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */
};
-static struct fdisk_parttype sun_parttypes[] = {
+static const struct fdisk_parttype sun_parttypes[] = {
{SUN_TAG_UNASSIGNED, N_("Unassigned")},
{SUN_TAG_BOOT, N_("Boot")},
{SUN_TAG_ROOT, N_("SunOS root")},
@@ -383,6 +383,10 @@ static void fetch_sun(struct fdisk_context *cxt,
lens[i] = 0;
}
}
+ for (i = cxt->label->nparts_max; i < SUN_MAXPARTITIONS; i++) {
+ starts[i] = 0;
+ lens[i] = 0;
+ }
}
/* non-Linux qsort_r(3) has usually differently ordered arguments */
diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c
index 6b6167d..a7c8bfa 100644
--- a/libfdisk/src/utils.c
+++ b/libfdisk/src/utils.c
@@ -183,11 +183,15 @@ done:
}
#ifdef TEST_PROGRAM
-struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
-struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__((unused))) { return NULL; }
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt __attribute__((unused))) { return NULL; }
-static int test_partnames(struct fdisk_test *ts, int argc, char *argv[])
+static int test_partnames(struct fdisk_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
size_t i;
const char *disk = argv[1];
diff --git a/libfdisk/src/version.c b/libfdisk/src/version.c
index 9d84b4c..b0661e2 100644
--- a/libfdisk/src/version.c
+++ b/libfdisk/src/version.c
@@ -91,7 +91,9 @@ int fdisk_get_library_features(const char ***features)
}
#ifdef TEST_PROGRAM
-static int test_version(struct fdisk_test *ts, int argc, char *argv[])
+static int test_version(struct fdisk_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
const char *ver;
const char **features;
diff --git a/libfdisk/src/wipe.c b/libfdisk/src/wipe.c
index 54f4213..bb5f1bb 100644
--- a/libfdisk/src/wipe.c
+++ b/libfdisk/src/wipe.c
@@ -75,6 +75,7 @@ int fdisk_set_wipe_area(struct fdisk_context *cxt,
free(wp);
return 1;
}
+ DBG(WIPE, ul_debug("not requested"));
return 0;
}
@@ -133,17 +134,8 @@ int fdisk_do_wipe(struct fdisk_context *cxt)
return rc;
}
- blkid_probe_enable_superblocks(pr, 1);
- blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC |
- BLKID_SUBLKS_BADCSUM);
- blkid_probe_enable_partitions(pr, 1);
- blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
- BLKID_PARTS_FORCE_GPT);
-
- while (blkid_do_probe(pr) == 0) {
- DBG(WIPE, ul_debugobj(wp, " wiping..."));
- blkid_do_wipe(pr, FALSE);
- }
+ DBG(WIPE, ul_debugobj(wp, " wiping..."));
+ blkid_wipe_all(pr);
}
blkid_free_probe(pr);
diff --git a/liblastlog2/COPYING b/liblastlog2/COPYING
new file mode 100644
index 0000000..df71a98
--- /dev/null
+++ b/liblastlog2/COPYING
@@ -0,0 +1,24 @@
+BSD 2-Clause License
+
+Copyright (c) 2023, Thorsten Kukuk
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/liblastlog2/Makemodule.am b/liblastlog2/Makemodule.am
new file mode 100644
index 0000000..2f69f88
--- /dev/null
+++ b/liblastlog2/Makemodule.am
@@ -0,0 +1,10 @@
+if BUILD_LIBLASTLOG2
+
+include liblastlog2/man/Makemodule.am
+include liblastlog2/src/Makemodule.am
+
+pkgconfig_DATA += liblastlog2/lastlog2.pc
+PATHFILES += liblastlog2/lastlog2.pc
+EXTRA_DIST += liblastlog2/COPYING
+
+endif # BUILD_LIBLASTLOG2
diff --git a/liblastlog2/lastlog2.pc.in b/liblastlog2/lastlog2.pc.in
new file mode 100644
index 0000000..39df5da
--- /dev/null
+++ b/liblastlog2/lastlog2.pc.in
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: lastlog2
+Description: Y2038 safe version of lastlog
+Version: @LIBLASTLOG2_VERSION@
+Requires.private: sqlite3
+Cflags: -I${includedir}/liblastlog2
+Libs: -L${libdir} -llastlog2
diff --git a/liblastlog2/man/Makemodule.am b/liblastlog2/man/Makemodule.am
new file mode 100644
index 0000000..803bfb7
--- /dev/null
+++ b/liblastlog2/man/Makemodule.am
@@ -0,0 +1,20 @@
+
+MANPAGES += \
+ liblastlog2/man/lastlog2.3 \
+ liblastlog2/man/ll2_import_lastlog.3 \
+ liblastlog2/man/ll2_read_all.3 \
+ liblastlog2/man/ll2_read_entry.3 \
+ liblastlog2/man/ll2_remove_entry.3 \
+ liblastlog2/man/ll2_rename_user.3 \
+ liblastlog2/man/ll2_update_login_time.3 \
+ liblastlog2/man/ll2_write_entry.3
+
+dist_noinst_DATA += \
+ liblastlog2/man/lastlog2.3.adoc \
+ liblastlog2/man/ll2_import_lastlog.3.adoc \
+ liblastlog2/man/ll2_read_all.3.adoc \
+ liblastlog2/man/ll2_read_entry.3.adoc \
+ liblastlog2/man/ll2_remove_entry.3.adoc \
+ liblastlog2/man/ll2_rename_user.3.adoc \
+ liblastlog2/man/ll2_update_login_time.3.adoc \
+ liblastlog2/man/ll2_write_entry.3.adoc
diff --git a/liblastlog2/man/lastlog2.3 b/liblastlog2/man/lastlog2.3
new file mode 100644
index 0000000..b2ac0d7
--- /dev/null
+++ b/liblastlog2/man/lastlog2.3
@@ -0,0 +1,67 @@
+'\" t
+.\" Title: lastlog2
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LASTLOG2" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+lastlog2 \- Y2038 safe version of lastlog library.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.SH "DESCRIPTION"
+.sp
+\fBlastlog2\fP reports the last login of a given user or of all users who did ever login on a system.
+.sp
+It\(cqs using sqlite3 as database backend. Data is only collected via a PAM module, so that every
+tools can make use of it, without modifying existing packages.
+The output is as compatible as possible with the old lastlog implementation.
+By default the database will be written as \f(CR/var/lib/lastlog/lastlog2.db\fP.
+The size of the database depends on the amount of users, not how big the biggest UID is.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_write_entry\fP(3),
+\fBll2_read_all\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/lastlog2.3.adoc b/liblastlog2/man/lastlog2.3.adoc
new file mode 100644
index 0000000..05cab12
--- /dev/null
+++ b/liblastlog2/man/lastlog2.3.adoc
@@ -0,0 +1,51 @@
+//po4a: entry man manual
+= lastlog2(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+lastlog2 - Y2038 safe version of lastlog library.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+== DESCRIPTION
+
+*lastlog2* reports the last login of a given user or of all users who did ever login on a system.
+
+It's using sqlite3 as database backend. Data is only collected via a PAM module, so that every
+tools can make use of it, without modifying existing packages.
+The output is as compatible as possible with the old lastlog implementation.
+By default the database will be written as `/var/lib/lastlog/lastlog2.db`.
+The size of the database depends on the amount of users, not how big the biggest UID is.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_write_entry*(3),
+*ll2_read_all*(3),
+*ll2_read_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_remove_entry*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_import_lastlog.3 b/liblastlog2/man/ll2_import_lastlog.3
new file mode 100644
index 0000000..23231c4
--- /dev/null
+++ b/liblastlog2/man/ll2_import_lastlog.3
@@ -0,0 +1,84 @@
+'\" t
+.\" Title: ll2_import_lastlog
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_IMPORT_LASTLOG" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_import_lastlog \- Import old lastlog file.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_import_lastlog (struct ll2_context *\fIcontext\fP,
+const char *\fIlastlog_file\fP,
+char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Importing all entries from \fIlastlog_file\fP file (lastlog(8)) into
+lastlog2 database defined with \fIcontext\fP.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *lastlog_path = "/var/log/lastlog";
+
+int ret = ll2_import_lastlog (NULL, lastlog_path, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_write_entry\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_remove_entry\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_import_lastlog.3.adoc b/liblastlog2/man/ll2_import_lastlog.3.adoc
new file mode 100644
index 0000000..9ddf54b
--- /dev/null
+++ b/liblastlog2/man/ll2_import_lastlog.3.adoc
@@ -0,0 +1,65 @@
+//po4a: entry man manual
+= ll2_import_lastlog(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_import_lastlog - Import old lastlog file.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_import_lastlog (struct ll2_context *__context__,
+ const char *__lastlog_file__,
+ char **__error__);*
+
+== DESCRIPTION
+
+Importing all entries from _lastlog_file_ file (lastlog(8)) into
+lastlog2 database defined with _context_.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *lastlog_path = "/var/log/lastlog";
+
+int ret = ll2_import_lastlog (NULL, lastlog_path, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_write_entry*(3),
+*ll2_read_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_rename_user*(3),
+*ll2_remove_entry*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_read_all.3 b/liblastlog2/man/ll2_read_all.3
new file mode 100644
index 0000000..753749f
--- /dev/null
+++ b/liblastlog2/man/ll2_read_all.3
@@ -0,0 +1,93 @@
+'\" t
+.\" Title: ll2_read_all
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_READ_ALL" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_read_all \- Reads all entries from database and calls the callback function for each entry.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+\fBint ll2_read_all (struct ll2_context *\fIcontext\fP,
+int (*\fIcallback\fP)(const char *\fIuser\fP, int64_t \fIll_time\fP,
+const char *\fItty\fP, const char *\fIrhost\fP,
+const char *\fIpam_service\fP, const char *\fIcb_error\fP),
+char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Reads all entries from database, defined in \fIcontext\fP, and calls callback fuction \fIcallback\fP for each entry.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *user = "root";
+
+static int
+callback (const char *res_user, int64_t ll_time, const char *res_tty,
+ const char *res_rhost, const char *res_service, const char *cb_error)
+{
+ /* returning != 0 if no further entry has to be handled by the callback */
+ return 0;
+}
+
+int ret = ll2_read_all (NULL, callback, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+If lastlog2 database does not exist at all, the errno ENOENT has been set
+and can be checked.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_write_entry\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_read_all.3.adoc b/liblastlog2/man/ll2_read_all.3.adoc
new file mode 100644
index 0000000..fbe589d
--- /dev/null
+++ b/liblastlog2/man/ll2_read_all.3.adoc
@@ -0,0 +1,74 @@
+//po4a: entry man manual
+= ll2_read_all(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_read_all - Reads all entries from database and calls the callback function for each entry.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+*int ll2_read_all (struct ll2_context *__context__,
+ int (*__callback__)(const char *__user__, int64_t __ll_time__,
+ const char *__tty__, const char *__rhost__,
+ const char *__pam_service__, const char *__cb_error__),
+ char **__error__);*
+
+== DESCRIPTION
+
+Reads all entries from database, defined in _context_, and calls callback fuction _callback_ for each entry.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *user = "root";
+
+static int
+callback (const char *res_user, int64_t ll_time, const char *res_tty,
+ const char *res_rhost, const char *res_service, const char *cb_error)
+{
+ /* returning != 0 if no further entry has to be handled by the callback */
+ return 0;
+}
+
+int ret = ll2_read_all (NULL, callback, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+If lastlog2 database does not exist at all, the errno ENOENT has been set
+and can be checked.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_write_entry*(3),
+*ll2_read_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_remove_entry*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_read_entry.3 b/liblastlog2/man/ll2_read_entry.3
new file mode 100644
index 0000000..53f1e26
--- /dev/null
+++ b/liblastlog2/man/ll2_read_entry.3
@@ -0,0 +1,91 @@
+'\" t
+.\" Title: ll2_read_entry
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_READ_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_read_entry \- Reads one entry from database and returns that.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_read_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP,
+int64_t *\fIll_time\fP, char \fB\fItty\fP, char \fP\fIrhost\fP,
+char \fB\fIpam_service\fP, char \fP\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Reads the first entry from database, defined in \fIcontext\fP, for user \fIuser\fP.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *user = "root";
+int64_t res_time;
+char *res_tty = NULL;
+char *res_rhost = NULL;
+char *res_service = NULL;
+
+int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+If lastlog2 database does not exist at all, the errno ENOENT has been set
+and can be checked.
+.sp
+The evaluated values are returned by \fIll_time\fP, \fItty\fP, \fIrhost\fP and \fIpam_service\fP.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_write_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_read_entry.3.adoc b/liblastlog2/man/ll2_read_entry.3.adoc
new file mode 100644
index 0000000..3bef1a8
--- /dev/null
+++ b/liblastlog2/man/ll2_read_entry.3.adoc
@@ -0,0 +1,72 @@
+//po4a: entry man manual
+= ll2_read_entry(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_read_entry - Reads one entry from database and returns that.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_read_entry (struct ll2_context *__context__, const char *__user__,
+ int64_t *__ll_time__, char **__tty__, char **__rhost__,
+ char **__pam_service__, char **__error__);*
+
+== DESCRIPTION
+
+Reads the first entry from database, defined in _context_, for user _user_.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *user = "root";
+int64_t res_time;
+char *res_tty = NULL;
+char *res_rhost = NULL;
+char *res_service = NULL;
+
+int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+If lastlog2 database does not exist at all, the errno ENOENT has been set
+and can be checked.
+
+The evaluated values are returned by _ll_time_, _tty_, _rhost_ and _pam_service_.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_write_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_remove_entry*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_remove_entry.3 b/liblastlog2/man/ll2_remove_entry.3
new file mode 100644
index 0000000..3cb444b
--- /dev/null
+++ b/liblastlog2/man/ll2_remove_entry.3
@@ -0,0 +1,82 @@
+'\" t
+.\" Title: ll2_remove_entry
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_REMOVE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_remove_entry \- Remove all entries of an user.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_remove_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP,
+char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Removing all database entries, defined in \fIcontext\fP, with the user name \fIuser\fP.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *user = "root";
+
+int ret = ll2_remove_entry (NULL, user, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_write_entry\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_remove_entry.3.adoc b/liblastlog2/man/ll2_remove_entry.3.adoc
new file mode 100644
index 0000000..f38ab66
--- /dev/null
+++ b/liblastlog2/man/ll2_remove_entry.3.adoc
@@ -0,0 +1,63 @@
+//po4a: entry man manual
+= ll2_remove_entry(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_remove_entry - Remove all entries of an user.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_remove_entry (struct ll2_context *__context__, const char *__user__,
+ char **__error__);*
+
+== DESCRIPTION
+
+Removing all database entries, defined in _context_, with the user name _user_.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *user = "root";
+
+int ret = ll2_remove_entry (NULL, user, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_write_entry*(3),
+*ll2_read_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_rename_user.3 b/liblastlog2/man/ll2_rename_user.3
new file mode 100644
index 0000000..7644838
--- /dev/null
+++ b/liblastlog2/man/ll2_rename_user.3
@@ -0,0 +1,85 @@
+'\" t
+.\" Title: ll2_rename_user
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_RENAME_USER" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_rename_user \- Renames an user entry.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_rename_user (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP,
+const char *\fInewname\fP, char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Changing user name from \fIuser\fP to \fInewname\fP of one entry in
+database, which is defined by \fIcontext\fP. All other entries with the user \fIuser\fP
+will be deleted.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *user = "root";
+const char *new_user = "notroot";
+
+int ret = ll2_rename_user (NULL, user, new_user, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_write_entry\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_rename_user.3.adoc b/liblastlog2/man/ll2_rename_user.3.adoc
new file mode 100644
index 0000000..2202fe2
--- /dev/null
+++ b/liblastlog2/man/ll2_rename_user.3.adoc
@@ -0,0 +1,66 @@
+//po4a: entry man manual
+= ll2_rename_user(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_rename_user - Renames an user entry.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_rename_user (struct ll2_context *__context__, const char *__user__,
+ const char *__newname__, char **__error__);*
+
+== DESCRIPTION
+
+Changing user name from _user_ to _newname_ of one entry in
+database, which is defined by _context_. All other entries with the user _user_
+will be deleted.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *user = "root";
+const char *new_user = "notroot";
+
+int ret = ll2_rename_user (NULL, user, new_user, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_write_entry*(3),
+*ll2_read_entry*(3),
+*ll2_remove_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_update_login_time.3 b/liblastlog2/man/ll2_update_login_time.3
new file mode 100644
index 0000000..ce08bc4
--- /dev/null
+++ b/liblastlog2/man/ll2_update_login_time.3
@@ -0,0 +1,86 @@
+'\" t
+.\" Title: ll2_update_login_time
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_UPDATE_LOGIN_TIME" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_update_login_time \- Writes an *new* entry with updated login time.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_update_login_time (struct ll2_context *\fIcontext\fP,
+const char *\fIuser\fP, int64_t \fIll_time\fP,
+char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Writes an \fBnew\fP entry to database, defined in \fIcontext\fP, for user \fIuser\fP.
+Time is set by \fIll_time\fP whereas the other values are taken from
+an already existing entry.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+char *error = NULL;
+const char *user = "root";
+int64_t login_time = time(0); // Get the system time;
+
+int ret = ll2_update_login_time (NULL, user, login_time, &error);
+.fam
+.fi
+.if n .RE
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string. It could also be NULL if the return value is \-1.
+\fIerror\fP should be freed by the caller.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_write_entry\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_update_login_time.3.adoc b/liblastlog2/man/ll2_update_login_time.3.adoc
new file mode 100644
index 0000000..9cc43e1
--- /dev/null
+++ b/liblastlog2/man/ll2_update_login_time.3.adoc
@@ -0,0 +1,67 @@
+//po4a: entry man manual
+= ll2_update_login_time(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_update_login_time - Writes an *new* entry with updated login time.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_update_login_time (struct ll2_context *__context__,
+ const char *__user__, int64_t __ll_time__,
+ char **__error__);*
+
+== DESCRIPTION
+
+Writes an *new* entry to database, defined in _context_, for user _user_.
+Time is set by _ll_time_ whereas the other values are taken from
+an already existing entry.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+--------------------------------------
+char *error = NULL;
+const char *user = "root";
+int64_t login_time = time(0); // Get the system time;
+
+int ret = ll2_update_login_time (NULL, user, login_time, &error);
+--------------------------------------
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string. It could also be NULL if the return value is -1.
+_error_ should be freed by the caller.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_write_entry*(3),
+*ll2_read_entry*(3),
+*ll2_remove_entry*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/man/ll2_write_entry.3 b/liblastlog2/man/ll2_write_entry.3
new file mode 100644
index 0000000..f16294a
--- /dev/null
+++ b/liblastlog2/man/ll2_write_entry.3
@@ -0,0 +1,88 @@
+'\" t
+.\" Title: ll2_write_entry
+.\" Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2024-03-20
+.\" Manual: Programmer's Manual
+.\" Source: util-linux 2.40
+.\" Language: English
+.\"
+.TH "LL2_WRITE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual"
+.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"
+ll2_write_entry \- Write a new entry into the database.
+.SH "SYNOPSIS"
+.sp
+\fB#include <lastlog2.h>\fP
+.sp
+\fBint ll2_write_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP,
+int64_t \fIll_time\fP, const char *\fItty\fP,
+const char *\fIrhost\fP, const char *\fIpam_service\fP,
+char **\fIerror\fP);\fP
+.SH "DESCRIPTION"
+.sp
+Writes an new entry into database, which is defined in \fIcontext\fP.
+If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP,
+will be taken.
+.sp
+.if n .RS 4
+.nf
+.fam C
+time_t login_time = time(0); // Get the system time
+char *error = NULL;
+const char *user = "root";
+
+int ret = ll2_write_entry (NULL, user, login_time, "pts/0",
+ "192.168.122.1", NULL, &error);
+.fam
+.fi
+.if n .RE
+.sp
+\fIpam_service\fP is the service or instance name which has generated the entry (optional).
+.SH "RETURN VALUE"
+.sp
+Returns 0 on success, \-ENOMEM or \-1 on other failure.
+\fIerror\fP contains an error string if the return value is \-1.
+\fIerror\fP is not guaranteed to contain an error string, could also be NULL.
+\fIerror\fP should be freed by the caller.
+.SH "AUTHORS"
+.sp
+Thorsten Kukuk (\c
+.MTO "kukuk\(atsuse.de" "" ")"
+.SH "SEE ALSO"
+.sp
+\fBlastlog2\fP(3),
+\fBll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all\fP(3),
+\fBll2_read_entry\fP(3),
+\fBll2_update_login_time\fP(3),
+\fBll2_remove_entry\fP(3),
+\fBll2_rename_user\fP(3),
+\fBll2_import_lastlog\fP(3)
+.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 \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. 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/liblastlog2/man/ll2_write_entry.3.adoc b/liblastlog2/man/ll2_write_entry.3.adoc
new file mode 100644
index 0000000..6dca1b5
--- /dev/null
+++ b/liblastlog2/man/ll2_write_entry.3.adoc
@@ -0,0 +1,70 @@
+//po4a: entry man manual
+= ll2_write_entry(3)
+:doctype: manpage
+:man manual: Programmer's Manual
+:man source: util-linux {release-version}
+:page-layout: base
+:lib: liblastlog2
+:firstversion: 2.40
+
+== NAME
+
+ll2_write_entry - Write a new entry into the database.
+
+== SYNOPSIS
+
+*#include <lastlog2.h>*
+
+*int ll2_write_entry (struct ll2_context *__context__, const char *__user__,
+ int64_t __ll_time__, const char *__tty__,
+ const char *__rhost__, const char *__pam_service__,
+ char **__error__);*
+
+== DESCRIPTION
+
+Writes an new entry into database, which is defined in _context_.
+If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_,
+will be taken.
+
+
+--------------------------------------
+time_t login_time = time(0); // Get the system time
+char *error = NULL;
+const char *user = "root";
+
+int ret = ll2_write_entry (NULL, user, login_time, "pts/0",
+ "192.168.122.1", NULL, &error);
+--------------------------------------
+
+_pam_service_ is the service or instance name which has generated the entry (optional).
+
+== RETURN VALUE
+
+Returns 0 on success, -ENOMEM or -1 on other failure.
+_error_ contains an error string if the return value is -1.
+_error_ is not guaranteed to contain an error string, could also be NULL.
+_error_ should be freed by the caller.
+
+== AUTHORS
+
+Thorsten Kukuk (kukuk@suse.de)
+
+== SEE ALSO
+
+*lastlog2*(3),
+*ll2_new_context(3),
+*ll2_unref_context(3),
+*ll2_read_all*(3),
+*ll2_read_entry*(3),
+*ll2_update_login_time*(3),
+*ll2_remove_entry*(3),
+*ll2_rename_user*(3),
+*ll2_import_lastlog*(3)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer-lib.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/liblastlog2/meson.build b/liblastlog2/meson.build
new file mode 100644
index 0000000..6f8db53
--- /dev/null
+++ b/liblastlog2/meson.build
@@ -0,0 +1,63 @@
+cc = meson.get_compiler('c')
+pkg = import('pkgconfig')
+dir_liblastlog2 = include_directories('src')
+lib_lastlog2_sources = '''
+ src/lastlog2.h
+ src/lastlog2P.h
+ src/lastlog2.c
+'''.split()
+
+liblastlog2_sym = 'src/liblastlog2.sym'
+liblastlog2_sym_path = '@0@/@1@'.format(meson.current_source_dir(), liblastlog2_sym)
+
+if build_liblastlog2
+ lib_lastlog2 = both_libraries(
+ 'lastlog2',
+ lib_lastlog2_sources,
+ include_directories : [dir_include],
+ link_args : ['-Wl,--version-script=@0@'.format(liblastlog2_sym_path)],
+ link_depends : liblastlog2_sym,
+ dependencies : [lib_sqlite3],
+ install : build_liblastlog2,
+ version : liblastlog2_version,
+ )
+
+ lastlog2_dep = declare_dependency(link_with: lib_lastlog2, include_directories: dir_liblastlog2)
+
+ lastlog2_tests = [
+ 'dlopen',
+ 'pam_lastlog2_output',
+ 'remove_entry',
+ 'rename_user',
+ 'write_read_user',
+ 'y2038_ll2_read_all',
+ 'y2038_sqlite3_time',
+ ]
+ libdl = cc.find_library('dl')
+
+ pkg.generate(
+ lib_lastlog2,
+ description : 'library to manage last login data',
+ subdirs : 'lastlog2',
+ version : pc_version
+ )
+ meson.override_dependency('lastlog2', lastlog2_dep)
+
+ install_headers('src/lastlog2.h', subdir : 'liblastlog2')
+
+ foreach lastlog2_test: lastlog2_tests
+ test_name = 'test_lastlog2_' + lastlog2_test
+ exe = executable(
+ test_name,
+ 'src/tests/tst_' + lastlog2_test + '.c',
+ include_directories : [dir_include],
+ link_with : [lib_common],
+ dependencies : [libdl, lastlog2_dep],
+ )
+ # the test-setup expects the helpers in the toplevel build-directory
+ link = meson.project_build_root() / test_name
+ run_command('ln', '-srf', exe.full_path(), link,
+ check : true)
+ endforeach
+
+endif
diff --git a/liblastlog2/src/Makemodule.am b/liblastlog2/src/Makemodule.am
new file mode 100644
index 0000000..04b8e97
--- /dev/null
+++ b/liblastlog2/src/Makemodule.am
@@ -0,0 +1,99 @@
+# includes
+lastlog2incdir = $(includedir)/liblastlog2
+lastlog2inc_HEADERS = liblastlog2/src/lastlog2.h
+
+usrlib_exec_LTLIBRARIES += liblastlog2.la
+
+liblastlog2_la_SOURCES = \
+ liblastlog2/src/lastlog2.c \
+ liblastlog2/src/lastlog2P.h
+
+EXTRA_liblastlog2_la_DEPENDENCIES = \
+ liblastlog2/src/liblastlog2.sym
+
+liblastlog2_la_LIBADD = $(SQLITE3_LIBS)
+
+liblastlog2_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SOLIB_CFLAGS) \
+ -I$(ul_liblastlog2_incdir) \
+ -I$(top_srcdir)/liblastlog2/src
+
+liblastlog2_la_LDFLAGS = $(SOLIB_LDFLAGS)
+if HAVE_VSCRIPT
+liblastlog2_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/liblastlog2/src/liblastlog2.sym
+endif
+liblastlog2_la_LDFLAGS += -version-info $(LIBLASTLOG2_VERSION_INFO)
+
+EXTRA_DIST += liblastlog2/src/liblastlog2.sym
+
+
+if BUILD_LIBLASTLOG2_TESTS
+check_PROGRAMS += \
+ test_lastlog2_dlopen \
+ test_lastlog2_pam_lastlog2_output \
+ test_lastlog2_remove_entry \
+ test_lastlog2_rename_user \
+ test_lastlog2_write_read_user \
+ test_lastlog2_y2038_ll2_read_all \
+ test_lastlog2_y2038_sqlite3_time
+
+lastlog2_tests_cflags = -DTEST_PROGRAM $(liblastlog2_la_CFLAGS)
+lastlog2_tests_ldflags = -static
+lastlog2_tests_ldadd = $(LDADD) liblastlog2.la $(SOLIB_LDFLAGS) $(SQLITE3_LIBS)
+
+test_lastlog2_dlopen_SOURCES = liblastlog2/src/tests/tst_dlopen.c
+test_lastlog2_dlopen_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_dlopen_LDFLAGS = $(lastlog2_tests_ldflags) -ldl
+test_lastlog2_dlopen_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_pam_lastlog2_output_SOURCES = liblastlog2/src/tests/tst_pam_lastlog2_output.c
+test_lastlog2_pam_lastlog2_output_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_pam_lastlog2_output_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_pam_lastlog2_output_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_remove_entry_SOURCES = liblastlog2/src/tests/tst_remove_entry.c
+test_lastlog2_remove_entry_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_remove_entry_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_remove_entry_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_rename_user_SOURCES = liblastlog2/src/tests/tst_rename_user.c
+test_lastlog2_rename_user_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_rename_user_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_rename_user_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_write_read_user_SOURCES = liblastlog2/src/tests/tst_write_read_user.c
+test_lastlog2_write_read_user_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_write_read_user_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_write_read_user_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_y2038_ll2_read_all_SOURCES = liblastlog2/src/tests/tst_y2038_ll2_read_all.c
+test_lastlog2_y2038_ll2_read_all_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_y2038_ll2_read_all_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_y2038_ll2_read_all_LDADD = $(lastlog2_tests_ldadd)
+
+test_lastlog2_y2038_sqlite3_time_SOURCES = liblastlog2/src/tests/tst_y2038_sqlite3_time.c
+test_lastlog2_y2038_sqlite3_time_CFLAGS = $(lastlog2_tests_cflags)
+test_lastlog2_y2038_sqlite3_time_LDFLAGS = $(lastlog2_tests_ldflags)
+test_lastlog2_y2038_sqlite3_time_LDADD = $(lastlog2_tests_ldadd)
+
+endif #BUILD_LIBLIBLASTLOG2_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-liblastlog2:
+ if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/liblastlog2.so"; then \
+ $(MKDIR_P) $(DESTDIR)$(libdir); \
+ mv $(DESTDIR)$(usrlib_execdir)/liblastlog2.so.* $(DESTDIR)$(libdir); \
+ so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/liblastlog2.so); \
+ so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+ (cd $(DESTDIR)$(usrlib_execdir) && \
+ rm -f liblastlog2.so && \
+ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name liblastlog2.so); \
+ fi
+
+uninstall-hook-liblastlog2:
+ rm -f $(DESTDIR)$(libdir)/liblastlog2.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-liblastlog2
+UNINSTALL_HOOKS += uninstall-hook-liblastlog2
diff --git a/liblastlog2/src/lastlog2.c b/liblastlog2/src/lastlog2.c
new file mode 100644
index 0000000..744d41f
--- /dev/null
+++ b/liblastlog2/src/lastlog2.c
@@ -0,0 +1,595 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <pwd.h>
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sqlite3.h>
+#include <lastlog.h>
+
+#include "lastlog2P.h"
+#include "strutils.h"
+
+/* Set the ll2 context/environment */
+/* Returns the context or NULL if an error has happened. */
+extern struct ll2_context * ll2_new_context(const char *db_path)
+{
+ struct ll2_context *context = (struct ll2_context *)malloc(sizeof(struct ll2_context));
+
+ if (context) {
+ if (db_path) {
+ if ((context->lastlog2_path = strdup(db_path)) == NULL) {
+ free(context);
+ context = NULL;
+ }
+ } else {
+ if ((context->lastlog2_path = strdup(LL2_DEFAULT_DATABASE)) == NULL) {
+ free(context);
+ context = NULL;
+ }
+ }
+ }
+ return context;
+}
+
+/* Release ll2 context/environment */
+extern void ll2_unref_context(struct ll2_context *context)
+{
+ if (context)
+ free(context->lastlog2_path);
+ free(context);
+}
+
+/* Returns 0 on success, -ENOMEM or -1 on other failure. */
+static int
+open_database_ro(struct ll2_context *context, sqlite3 **db, char **error)
+{
+ int ret = 0;
+ char *path = LL2_DEFAULT_DATABASE;
+
+ if (context && context->lastlog2_path)
+ path = context->lastlog2_path;
+
+ if (sqlite3_open_v2(path, db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
+ ret = -1;
+ if (error)
+ if (asprintf(error, "Cannot open database (%s): %s",
+ path, sqlite3_errmsg(*db)) < 0)
+ ret = -ENOMEM;
+
+ sqlite3_close(*db);
+ }
+
+ return ret;
+}
+
+/* Returns 0 on success, -ENOMEM or -1 on other failure. */
+static int
+open_database_rw(struct ll2_context *context, sqlite3 **db, char **error)
+{
+ int ret = 0;
+ char *path = LL2_DEFAULT_DATABASE;
+
+ if (context && context->lastlog2_path)
+ path = context->lastlog2_path;
+
+ if (sqlite3_open(path, db) != SQLITE_OK) {
+ ret = -1;
+ if (error)
+ if (asprintf(error, "Cannot create/open database (%s): %s",
+ path, sqlite3_errmsg(*db)) < 0)
+ ret = -ENOMEM;
+
+ sqlite3_close(*db);
+ }
+
+ return ret;
+}
+
+/* Reads one entry from database and returns that.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+static int
+read_entry(sqlite3 *db, const char *user,
+ int64_t *ll_time, char **tty, char **rhost,
+ char **pam_service, char **error)
+{
+ int retval = 0;
+ sqlite3_stmt *res = NULL;
+ static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 WHERE Name = ?";
+
+ if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to execute statement: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+
+ if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create search query: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+
+ int step = sqlite3_step(res);
+
+ if (step == SQLITE_ROW) {
+ const unsigned char *luser = sqlite3_column_text(res, 0);
+ const unsigned char *uc;
+
+ if (strcmp((const char *)luser, user) != 0) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Returned data is for %s, not %s", luser, user) < 0)
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+
+ if (ll_time)
+ *ll_time = sqlite3_column_int64(res, 1);
+
+ if (tty) {
+ uc = sqlite3_column_text(res, 2);
+ if (uc != NULL && strlen((const char *)uc) > 0)
+ if ((*tty = strdup((const char *)uc)) == NULL) {
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+ }
+ if (rhost) {
+ uc = sqlite3_column_text(res, 3);
+ if (uc != NULL && strlen((const char *)uc) > 0)
+ if ((*rhost = strdup((const char *)uc)) == NULL) {
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+ }
+ if (pam_service) {
+ uc = sqlite3_column_text(res, 4);
+ if (uc != NULL && strlen((const char *)uc) > 0)
+ if ((*pam_service = strdup((const char *)uc)) == NULL) {
+ retval = -ENOMEM;
+ goto out_read_entry;
+ }
+ }
+ } else {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "User '%s' not found (%d)", user, step) < 0)
+ retval = -ENOMEM;
+ }
+
+out_read_entry:
+ if (res)
+ sqlite3_finalize(res);
+
+ return retval;
+}
+
+/* reads 1 entry from database and returns that. Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_read_entry(struct ll2_context *context, const char *user,
+ int64_t *ll_time, char **tty, char **rhost,
+ char **pam_service, char **error)
+{
+ sqlite3 *db;
+ int retval;
+
+ if ((retval = open_database_ro(context, &db, error)) != 0)
+ return retval;
+
+ retval = read_entry(db, user, ll_time, tty, rhost, pam_service, error);
+
+ sqlite3_close(db);
+
+ return retval;
+}
+
+/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+static int
+write_entry(sqlite3 *db, const char *user,
+ int64_t ll_time, const char *tty, const char *rhost,
+ const char *pam_service, char **error)
+{
+ int retval = 0;
+ char *err_msg = NULL;
+ sqlite3_stmt *res = NULL;
+ static const char *sql_table = "CREATE TABLE IF NOT EXISTS Lastlog2(Name TEXT PRIMARY KEY, Time INTEGER, TTY TEXT, RemoteHost TEXT, Service TEXT);";
+ static const char *sql_replace = "REPLACE INTO Lastlog2 VALUES(?,?,?,?,?);";
+
+ if (sqlite3_exec(db, sql_table, 0, 0, &err_msg) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "SQL error: %s", err_msg) < 0)
+ retval = -ENOMEM;
+
+ sqlite3_free(err_msg);
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_prepare_v2(db, sql_replace, -1, &res, 0) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to execute statement: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create replace statement for user: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_bind_int64(res, 2, ll_time) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create replace statement for ll_time: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_bind_text(res, 3, tty, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create replace statement for tty: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_bind_text(res, 4, rhost, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create replace statement for rhost: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ if (sqlite3_bind_text(res, 5, pam_service, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create replace statement for PAM service: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+
+ int step = sqlite3_step(res);
+
+ if (step != SQLITE_DONE) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d",
+ step) < 0)
+ retval = -ENOMEM;
+ goto out_ll2_read_entry;
+ }
+out_ll2_read_entry:
+ if (res)
+ sqlite3_finalize(res);
+
+ return retval;
+}
+
+/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_write_entry(struct ll2_context *context, const char *user,
+ int64_t ll_time, const char *tty, const char *rhost,
+ const char *pam_service, char **error)
+{
+ sqlite3 *db;
+ int retval;
+
+ if ((retval = open_database_rw(context, &db, error)) != 0)
+ return retval;
+
+ retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error);
+
+ sqlite3_close(db);
+
+ return retval;
+}
+
+/* Write a new entry with updated login time.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_update_login_time(struct ll2_context *context, const char *user,
+ int64_t ll_time, char **error)
+{
+ sqlite3 *db;
+ int retval;
+ char *tty;
+ char *rhost;
+ char *pam_service;
+
+ if ((retval = open_database_rw(context , &db, error)) != 0)
+ return retval;
+
+ if ((retval = read_entry(db, user, 0, &tty, &rhost, &pam_service, error)) != 0) {
+ sqlite3_close(db);
+ return retval;
+ }
+
+ retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error);
+
+ sqlite3_close(db);
+
+ free(tty);
+ free(rhost);
+ free(pam_service);
+
+ return retval;
+}
+
+
+typedef int (*callback_f)(const char *user, int64_t ll_time,
+ const char *tty, const char *rhost,
+ const char *pam_service, const char *cb_error);
+
+static int
+callback(void *cb_func, __attribute__((unused)) int argc, char **argv, __attribute__((unused)) char **azColName)
+{
+ char *endptr;
+ callback_f print_entry = cb_func;
+
+ errno = 0;
+ char *cb_error = NULL;
+ int64_t ll_time = strtoll(argv[1], &endptr, 10);
+ if ((errno == ERANGE && (ll_time == INT64_MAX || ll_time == INT64_MIN))
+ || (endptr == argv[1]) || (*endptr != '\0'))
+ if (asprintf(&cb_error, "Invalid numeric time entry for '%s': '%s'\n", argv[0], argv[1]) < 0)
+ return -1;
+
+ print_entry(argv[0], ll_time, argv[2], argv[3], argv[4], cb_error);
+ free(cb_error);
+
+ return 0;
+}
+
+/* Reads all entries from database and calls the callback function for each entry.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_read_all(struct ll2_context *context,
+ int (*cb_func)(const char *user, int64_t ll_time,
+ const char *tty, const char *rhost,
+ const char *pam_service, const char *cb_error),
+ char **error)
+{
+ sqlite3 *db;
+ char *err_msg = 0;
+ int retval = 0;
+
+ if ((retval = open_database_ro(context, &db, error)) != 0)
+ return retval;
+
+ static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 ORDER BY Name ASC";
+
+ if (sqlite3_exec(db, sql, callback, cb_func, &err_msg) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "SQL error: %s", err_msg) < 0)
+ retval = -ENOMEM;
+
+ sqlite3_free(err_msg);
+ }
+
+ sqlite3_close(db);
+
+ return retval;
+}
+
+/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+static int
+remove_entry(sqlite3 *db, const char *user, char **error)
+{
+ int retval = 0;
+ sqlite3_stmt *res = NULL;
+ static const char *sql = "DELETE FROM Lastlog2 WHERE Name = ?";
+
+ if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) {
+ if (error)
+ if (asprintf(error, "Failed to execute statement: %s",
+ sqlite3_errmsg(db)) < 0)
+ return -ENOMEM;
+
+ return -1;
+ }
+
+ if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to create delete statement: %s",
+ sqlite3_errmsg(db)) < 0)
+ retval = -ENOMEM;
+ goto out_remove_entry;
+ }
+
+ int step = sqlite3_step(res);
+
+ if (step != SQLITE_DONE) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d",
+ step) < 0)
+ retval = -ENOMEM;
+ }
+out_remove_entry:
+ if (res)
+ sqlite3_finalize(res);
+
+ return retval;
+}
+
+/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_remove_entry(struct ll2_context *context, const char *user,
+ char **error)
+{
+ sqlite3 *db;
+ int retval;
+
+ if ((retval = open_database_rw(context, &db, error)) != 0)
+ return retval;
+
+ retval = remove_entry(db, user, error);
+
+ sqlite3_close(db);
+
+ return retval;
+}
+
+/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_rename_user(struct ll2_context *context, const char *user,
+ const char *newname, char **error)
+{
+ sqlite3 *db;
+ int64_t ll_time;
+ char *tty;
+ char *rhost;
+ char *pam_service;
+ int retval;
+
+ if ((retval = open_database_rw(context, &db, error)) != 0)
+ return retval;
+
+ if ((retval = read_entry(db, user, &ll_time, &tty, &rhost, &pam_service, error) != 0)) {
+ sqlite3_close(db);
+ return retval;
+ }
+
+ if ((retval = write_entry(db, newname, ll_time, tty, rhost, pam_service, error) != 0)) {
+ sqlite3_close(db);
+ free(tty);
+ free(rhost);
+ return retval;
+ }
+
+ retval = remove_entry(db, user, error);
+
+ sqlite3_close(db);
+
+ free(tty);
+ free(rhost);
+ free(pam_service);
+
+ return retval;
+}
+
+/* Import old lastlog file.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+int
+ll2_import_lastlog(struct ll2_context *context, const char *lastlog_file,
+ char **error)
+{
+ const struct passwd *pw;
+ struct stat statll;
+ sqlite3 *db;
+ FILE *ll_fp;
+ int retval = 0;
+
+ if ((retval = open_database_rw(context, &db, error)) != 0)
+ return retval;
+
+ ll_fp = fopen(lastlog_file, "r");
+ if (ll_fp == NULL) {
+ if (error && asprintf(error, "Failed to open '%s': %s",
+ lastlog_file, strerror(errno)) < 0)
+ return -ENOMEM;
+
+ return -1;
+ }
+
+
+ if (fstat(fileno(ll_fp), &statll) != 0) {
+ retval = -1;
+ if (error && asprintf(error, "Cannot get size of '%s': %s",
+ lastlog_file, strerror(errno)) < 0)
+ retval = -ENOMEM;
+
+ goto done;
+ }
+
+ setpwent();
+ while ((pw = getpwent()) != NULL ) {
+ off_t offset;
+ struct lastlog ll;
+
+ offset = (off_t) pw->pw_uid * sizeof (ll);
+
+ if ((offset + (off_t)sizeof(ll)) <= statll.st_size) {
+ if (fseeko(ll_fp, offset, SEEK_SET) == -1)
+ continue; /* Ignore seek error */
+
+ if (fread(&ll, sizeof(ll), 1, ll_fp) != 1) {
+ retval = -1;
+ if (error)
+ if (asprintf(error, "Failed to get the entry for UID '%lu'",
+ (unsigned long int)pw->pw_uid) < 0)
+ retval = -ENOMEM;
+ goto out_import_lastlog;
+ }
+
+ if (ll.ll_time != 0) {
+ int64_t ll_time;
+ char tty[sizeof(ll.ll_line) + 1];
+ char rhost[sizeof(ll.ll_host) + 1];
+
+ ll_time = ll.ll_time;
+ mem2strcpy(tty, ll.ll_line, sizeof(ll.ll_line), sizeof(tty));
+ mem2strcpy(rhost, ll.ll_host, sizeof(ll.ll_host), sizeof(rhost));
+
+ if ((retval = write_entry(db, pw->pw_name, ll_time, tty,
+ rhost, NULL, error)) != 0)
+ goto out_import_lastlog;
+ }
+ }
+ }
+out_import_lastlog:
+ endpwent();
+ sqlite3_close(db);
+done:
+ fclose(ll_fp);
+
+ return retval;
+}
diff --git a/liblastlog2/src/lastlog2.h b/liblastlog2/src/lastlog2.h
new file mode 100644
index 0000000..280f387
--- /dev/null
+++ b/liblastlog2/src/lastlog2.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _LIBLASTLOG2_H
+#define _LIBLASTLOG2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LL2_DEFAULT_DATABASE _PATH_LOCALSTATEDIR "/lib/lastlog/lastlog2.db"
+
+#include <stdint.h>
+
+struct ll2_context;
+
+/* Set the ll2 context/environment */
+/* Returns the context or NULL if an error has happened. */
+extern struct ll2_context * ll2_new_context(const char *db_path);
+
+/* Release ll2 context/environment */
+extern void ll2_unref_context(struct ll2_context *context);
+
+/* Writes a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_write_entry (struct ll2_context *context, const char *user,
+ int64_t ll_time, const char *tty,
+ const char *rhost, const char *pam_service,
+ char **error);
+
+/* Calling a defined function for each entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_read_all (struct ll2_context *context,
+ int (*callback)(const char *user, int64_t ll_time,
+ const char *tty, const char *rhost,
+ const char *pam_service, const char *cb_error),
+ char **error);
+
+/* Reads one entry from database and returns that.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_read_entry (struct ll2_context *context, const char *user,
+ int64_t *ll_time, char **tty, char **rhost,
+ char **pam_service, char **error);
+
+/* Write a new entry with updated login time.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_update_login_time (struct ll2_context *context,
+ const char *user, int64_t ll_time,
+ char **error);
+
+/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_remove_entry (struct ll2_context *context, const char *user,
+ char **error);
+
+/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_rename_user (struct ll2_context *context, const char *user,
+ const char *newname, char **error);
+
+
+/* Import old lastlog file.
+ Returns 0 on success, -ENOMEM or -1 on other failure. */
+extern int ll2_import_lastlog (struct ll2_context *context,
+ const char *lastlog_file, char **error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBLASTLOG2_H */
diff --git a/liblastlog2/src/lastlog2P.h b/liblastlog2/src/lastlog2P.h
new file mode 100644
index 0000000..b39f660
--- /dev/null
+++ b/liblastlog2/src/lastlog2P.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2024, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _LIBLASTLOG2_P_H
+#define _LIBLASTLOG2_P_H
+
+#include "lastlog2.h"
+
+struct ll2_context {
+ char *lastlog2_path;
+};
+
+#endif /* _LIBLASTLOG2_P_H */
diff --git a/liblastlog2/src/liblastlog2.sym b/liblastlog2/src/liblastlog2.sym
new file mode 100644
index 0000000..c21cced
--- /dev/null
+++ b/liblastlog2/src/liblastlog2.sym
@@ -0,0 +1,13 @@
+LIBLASTLOG2_2_40 {
+ global:
+ ll2_new_context;
+ ll2_unref_context;
+ ll2_read_all;
+ ll2_read_entry;
+ ll2_remove_entry;
+ ll2_rename_user;
+ ll2_write_entry;
+ ll2_update_login_time;
+ ll2_import_lastlog;
+ local: *;
+};
diff --git a/liblastlog2/src/tests/tst_dlopen.c b/liblastlog2/src/tests/tst_dlopen.c
new file mode 100644
index 0000000..112564b
--- /dev/null
+++ b/liblastlog2/src/tests/tst_dlopen.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+
+ Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+*/
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+/* Simple program to see if dlopen() would succeed. */
+int main(int argc, char **argv)
+{
+ int i;
+ struct stat st;
+ char buf[PATH_MAX];
+
+ for (i = 1; i < argc; i++) {
+ if (dlopen(argv[i], RTLD_NOW)) {
+ fprintf(stdout, "dlopen() of \"%s\" succeeded.\n",
+ argv[i]);
+ } else {
+ snprintf(buf, sizeof(buf), "./%s", argv[i]);
+ if ((stat(buf, &st) == 0) && dlopen(buf, RTLD_NOW)) {
+ fprintf(stdout, "dlopen() of \"./%s\" "
+ "succeeded.\n", argv[i]);
+ } else {
+ fprintf(stdout, "dlopen() of \"%s\" failed: "
+ "%s\n", argv[i], dlerror());
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_pam_lastlog2_output.c b/liblastlog2/src/tests/tst_pam_lastlog2_output.c
new file mode 100644
index 0000000..1fd1eec
--- /dev/null
+++ b/liblastlog2/src/tests/tst_pam_lastlog2_output.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Store defined data into the database, read it, create the time
+ string like pam_lastlog2 does, compare the result.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lastlog2P.h"
+
+const char *expected = "Last login: Mon Mar 13 07:13:41 UTC 2023 from 192.168.122.1 on pts/0";
+const time_t login_time = 1678691621;
+int
+main(void)
+{
+ const char *user = "root";
+ struct ll2_context *context = ll2_new_context("pam_lastlog2-output.db");
+ int64_t ll_time = 0;
+ char *tty = NULL;
+ char *rhost = NULL;
+ char *date = NULL;
+ char the_time[256];
+ char *error = NULL;
+ char *output = NULL;
+
+ if (ll2_write_entry (context, user, login_time, "pts/0",
+ "192.168.122.1", NULL, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll2_read_entry (context, user, &ll_time, &tty, &rhost,
+ NULL, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Unknown error reading database %s", context ? context->lastlog2_path : "NULL");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll_time) {
+ struct tm *tm, tm_buf;
+ /* this is necessary if you compile this on architectures with
+ a 32bit time_t type. */
+ time_t t_time = ll_time;
+
+ if ((tm = localtime_r (&t_time, &tm_buf)) != NULL) {
+ strftime (the_time, sizeof (the_time),
+ " %a %b %e %H:%M:%S %Z %Y", tm);
+ date = the_time;
+ }
+ }
+
+ if (asprintf (&output, "Last login:%s%s%s%s%s",
+ date ? date : "",
+ rhost ? " from " : "",
+ rhost ? rhost : "",
+ tty ? " on " : "",
+ tty ? tty : "") < 0) {
+ fprintf (stderr, "Out of memory!\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (strcmp (output, expected) != 0) {
+ fprintf (stderr, "Output '%s'\n does not match '%s'\n",
+ output, expected);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ ll2_unref_context(context);
+ free (output);
+ free (tty);
+ free (rhost);
+
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_remove_entry.c b/liblastlog2/src/tests/tst_remove_entry.c
new file mode 100644
index 0000000..39079c8
--- /dev/null
+++ b/liblastlog2/src/tests/tst_remove_entry.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Create an entry, delete that entry, and try to read entry again.
+ Reading the entry should fail.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lastlog2.h"
+
+int
+main(void)
+{
+ const char *user = "user";
+ struct ll2_context *context = ll2_new_context("tst-delete-user.db");
+ int64_t ll_time = 0;
+ char *tty = NULL;
+ char *rhost = NULL;
+ char *service = NULL;
+ char *error = NULL;
+
+ if (ll2_write_entry (context, user, time (NULL), "test-tty",
+ "localhost", "sshd", &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll2_remove_entry (context, user, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "ll2_remove_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ /* this needs to fail, as the old entry shouldn't exist anymore. */
+ if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, &service, &error) == 0) {
+ fprintf (stderr, "Reading old user from database did not fail!\n");
+ fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n",
+ (long long int)ll_time, tty, rhost, service);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ ll2_unref_context(context);
+ free (error);
+ free (tty);
+ free (rhost);
+ free (service);
+
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_rename_user.c b/liblastlog2/src/tests/tst_rename_user.c
new file mode 100644
index 0000000..a1788b5
--- /dev/null
+++ b/liblastlog2/src/tests/tst_rename_user.c
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Create an entry, rename that entry, and try to read the old and
+ new entry again. Reading the old entry should fail.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lastlog2P.h"
+
+int
+main(void)
+{
+ const char *user = "user";
+ const char *newname = "new";
+ struct ll2_context *context = ll2_new_context("tst-rename-user.db");
+ int64_t ll_time = 0;
+ char *tty = NULL;
+ char *rhost = NULL;
+ char *service = NULL;
+ char *error = NULL;
+
+ if (ll2_write_entry (context, user, time (NULL), "test-tty",
+ "localhost", "test-service", &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ }
+ else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll2_rename_user (context, user, newname, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ }
+ else
+ fprintf (stderr, "ll2_rename_user failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ /* this needs to fail, as the old entry shouldn't exist anymore. */
+ if (ll2_read_entry (context, user, &ll_time, &tty, &rhost,
+ &service, &error) == 0) {
+ fprintf (stderr, "Reading old user from database did not fail!\n");
+ fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n",
+ (long long int)ll_time, tty, rhost, service);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (error) {
+ free (error);
+ error = NULL;
+ }
+
+ if (ll2_read_entry (context, newname, &ll_time, &tty, &rhost, &service,
+ &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (strcmp (tty, "test-tty") != 0 || strcmp (rhost, "localhost") != 0 ||
+ strcmp (service, "test-service") != 0) {
+ fprintf (stderr, "New entry data does not match old entry data!\n");
+ }
+
+ ll2_unref_context(context);
+ free (tty);
+ free (rhost);
+ free (service);
+
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_write_read_user.c b/liblastlog2/src/tests/tst_write_read_user.c
new file mode 100644
index 0000000..dbf1db7
--- /dev/null
+++ b/liblastlog2/src/tests/tst_write_read_user.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Create an entry, rename that entry, and try to read the old and
+ new entry again. Reading the old entry should fail.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "lastlog2P.h"
+
+static int
+test_args (struct ll2_context *context, const char *user, int64_t ll_time,
+ const char *tty, const char *rhost, const char *service)
+{
+ char *error = NULL;
+ int64_t res_time;
+ char *res_tty = NULL;
+ char *res_rhost = NULL;
+ char *res_service = NULL;
+
+ if (ll2_write_entry (context, user, ll_time, tty, rhost, service, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ return 1;
+ }
+
+ if (ll2_read_entry (context, user, &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path);
+ return 1;
+ }
+
+ if (ll_time != res_time) {
+ fprintf (stderr, "Wrong time: got %lld, expect %lld\n",
+ (long long int)res_time, (long long int)ll_time);
+ return 1;
+ }
+
+ if ((tty == NULL && res_tty != NULL) ||
+ (tty != NULL && res_tty == NULL) ||
+ (tty != NULL && res_tty != NULL && strcmp (tty, res_tty) != 0)) {
+ fprintf (stderr, "Wrong tty: got %s, expect %s\n", tty, res_tty);
+ return 1;
+ }
+
+ if ((rhost == NULL && res_rhost != NULL) ||
+ (rhost != NULL && res_rhost == NULL) ||
+ (rhost != NULL && res_rhost != NULL && strcmp (rhost, res_rhost) != 0)) {
+ fprintf (stderr, "Wrong rhost: got %s, expect %s\n", rhost, res_rhost);
+ return 1;
+ }
+
+ if ((service == NULL && res_service != NULL) ||
+ (service != NULL && res_service == NULL) ||
+ (service != NULL && res_service != NULL && strcmp (service, res_service) != 0)) {
+ fprintf (stderr, "Wrong service: got %s, expect %s\n", service, res_service);
+ return 1;
+ }
+
+
+ free (res_tty);
+ free (res_rhost);
+ free (res_service);
+
+ return 0;
+}
+
+int
+main(void)
+{
+ struct ll2_context *context = ll2_new_context("tst-write-read-user.db");
+ char *error = NULL;
+ int64_t res_time;
+ char *res_tty = NULL;
+ char *res_rhost = NULL;
+ char *res_service = NULL;
+
+ if (test_args (context, "user1", time (NULL), "test-tty", "localhost", "test") != 0) {
+ ll2_unref_context(context);
+ return 1;
+ }
+ if (test_args (context, "user2", 0, NULL, NULL, NULL) != 0) {
+ ll2_unref_context(context);
+ return 1;
+ }
+ if (test_args (context, "user3", time (NULL), NULL, NULL, NULL) != 0) {
+ ll2_unref_context(context);
+ return 1;
+ }
+ if (test_args (context, "user4", time (NULL), "test-tty", NULL, NULL) != 0) {
+ ll2_unref_context(context);
+ return 1;
+ }
+ if (test_args (context, "user5", time (NULL), NULL, "localhost", NULL) != 0) {
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ /* Checking errno if the db file does not exist */
+ struct ll2_context *context_not_found = ll2_new_context("no_file");
+ if (ll2_read_entry (context_not_found, "user", &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Couldn't read entries for all users\n");
+
+ if(errno) {
+ if (errno == ENOENT)
+ {
+ fprintf (stderr, "Returning the correct errno: %s\n",
+ strerror (errno));
+ ll2_unref_context(context_not_found);
+ return 0;
+ }
+ fprintf (stderr, "errno: %s\n",
+ strerror (errno));
+ } else {
+ fprintf (stderr, "errno: NULL\n");
+ }
+
+ ll2_unref_context(context_not_found);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ ll2_unref_context(context);
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_y2038_ll2_read_all.c b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c
new file mode 100644
index 0000000..014ae9d
--- /dev/null
+++ b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Create an entry with an 3*INT32_MAX timestamp, store that,
+ read that via ll2_read_all callback again and make sure the
+ timestamp is correct.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "lastlog2P.h"
+
+#define BIG_TIME_VALUE ((int64_t)3*INT32_MAX)
+
+const char *user = "y2038";
+const char *on_tty = "pts/test";
+const char *rhost = NULL;
+const char *service = "sshd";
+
+static int
+check_y2038 (const char *res_user, int64_t ll_time, const char *res_tty,
+ const char *res_rhost, const char *res_service, const char *error)
+{
+
+ if (strcmp (user, res_user) != 0) {
+ fprintf (stderr, "write/read entry user mismatch: written: %s, got: %s\n",
+ user, res_user);
+ exit (1);
+ }
+
+ if (ll_time != BIG_TIME_VALUE) {
+ fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n",
+ (long long int)BIG_TIME_VALUE, (long long int)ll_time);
+ exit (1);
+ }
+
+ if (strcmp (on_tty, res_tty) != 0) {
+ fprintf (stderr, "write/read entry tty mismatch: written: %s, got: %s\n",
+ on_tty, res_tty);
+ exit (1);
+ }
+
+ if (rhost != NULL) {
+ fprintf (stderr, "write/read entry rhost mismatch: written: NULL, got: %s\n",
+ res_rhost);
+ exit (1);
+ }
+
+ if (strcmp (service, res_service) != 0) {
+ fprintf (stderr, "write/read entry service mismatch: written: %s, got: %s\n",
+ service, res_service);
+ exit (1);
+ }
+
+ if (error != NULL) {
+ fprintf (stderr, "got error: %s\n",
+ error);
+ exit (1);
+ }
+
+ return 0;
+}
+
+int
+main(void)
+{
+ struct ll2_context *context = ll2_new_context("y2038-ll2_read_all.db");
+ char *error = NULL;
+
+ remove (context->lastlog2_path);
+
+ printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE);
+
+ if (ll2_write_entry (context, user, BIG_TIME_VALUE, on_tty, rhost, service,
+ &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ }
+ else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll2_read_all (context, check_y2038, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Couldn't read entries for all users\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ /* Checking errno if the db file does not exist */
+ remove (context->lastlog2_path);
+
+ if (ll2_read_all (context, check_y2038, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Couldn't read entries for all users\n");
+
+ if(errno) {
+ if (errno == ENOENT)
+ {
+ fprintf (stderr, "Returning the correct errno: %s\n",
+ strerror (errno));
+ ll2_unref_context(context);
+ return 0;
+ }
+ fprintf (stderr, "errno: %s\n",
+ strerror (errno));
+ } else {
+ fprintf (stderr, "errno: NULL\n");
+ }
+
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ ll2_unref_context(context);
+ return 0;
+}
diff --git a/liblastlog2/src/tests/tst_y2038_sqlite3_time.c b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c
new file mode 100644
index 0000000..a296634
--- /dev/null
+++ b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+
+ Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Test case:
+ Create an entry with an INT64_MAX-1000 timestamp, store that,
+ read that again and make sure the timestamp is correct.
+*/
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "lastlog2P.h"
+
+#define BIG_TIME_VALUE (INT64_MAX - 1000)
+
+int
+main(void)
+{
+ const char *user = "y2038";
+ struct ll2_context *context = ll2_new_context("y2038-sqlite3-time.db");
+ int64_t ll_time = 0;
+ char *error = NULL;
+
+ printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE);
+
+ if (ll2_write_entry (context, user, BIG_TIME_VALUE, NULL, NULL,
+ NULL, &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "ll2_write_entry failed\n");
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll2_read_entry (context, user, &ll_time, NULL, NULL, NULL,
+ &error) != 0) {
+ if (error) {
+ fprintf (stderr, "%s\n", error);
+ free (error);
+ } else
+ fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ if (ll_time != BIG_TIME_VALUE) {
+ fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n",
+ (long long int)BIG_TIME_VALUE, (long long int)ll_time);
+ ll2_unref_context(context);
+ return 1;
+ }
+
+ ll2_unref_context(context);
+ return 0;
+}
diff --git a/libmount/Makemodule.am b/libmount/Makemodule.am
index 8546c2f..02fb521 100644
--- a/libmount/Makemodule.am
+++ b/libmount/Makemodule.am
@@ -2,6 +2,7 @@ if BUILD_LIBMOUNT
include libmount/src/Makemodule.am
include libmount/python/Makemodule.am
+include libmount/samples/Makemodule.am
if ENABLE_GTK_DOC
# Docs uses separate Makefiles
diff --git a/libmount/docs/Makefile.in b/libmount/docs/Makefile.in
index bf87c32..e74b624 100644
--- a/libmount/docs/Makefile.in
+++ b/libmount/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/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt
index 86a7622..0b4adb5 100644
--- a/libmount/docs/libmount-sections.txt
+++ b/libmount/docs/libmount-sections.txt
@@ -302,6 +302,8 @@ libmnt_lock
mnt_free_lock
mnt_lock_file
mnt_new_lock
+mnt_ref_lock
+mnt_unref_lock
mnt_unlock_file
mnt_lock_block_signals
</SECTION>
@@ -454,5 +456,6 @@ mnt_monitor_get_fd
mnt_monitor_close_fd
mnt_monitor_next_change
mnt_monitor_event_cleanup
+mnt_monitor_veil_kernel
mnt_monitor_wait
</SECTION>
diff --git a/libmount/docs/version.xml b/libmount/docs/version.xml
index a69af57..4bdd32f 100644
--- a/libmount/docs/version.xml
+++ b/libmount/docs/version.xml
@@ -1 +1 @@
-2.39.3
+2.40
diff --git a/libmount/meson.build b/libmount/meson.build
index 55de27b..d1262e7 100644
--- a/libmount/meson.build
+++ b/libmount/meson.build
@@ -83,7 +83,7 @@ lib_mount_static = static_library(
lib__mount_deps = [
lib_selinux,
- get_option('cryptsetup-dlopen').enabled() ? lib_dl : lib_cryptsetup,
+ cryptsetup_dlopen ? lib_dl : lib_cryptsetup,
realtime_libs
]
lib_mount = library(
@@ -116,6 +116,7 @@ libmount_tests = [
'context',
'lock',
'optstr',
+ 'optlist',
'tab',
'tab_diff',
'monitor',
@@ -129,20 +130,22 @@ libmount_test_src_override = {
'debug': 'init',
}
-foreach libmount_test: libmount_tests
- test_name = 'test_mount_' + libmount_test
- exe = executable(
- test_name,
- 'src/' + libmount_test_src_override.get(libmount_test, libmount_test) + '.c',
- include_directories : [dir_include, dir_libblkid],
- link_with : [lib__mount, lib_common, lib_blkid_static],
- dependencies : lib__mount_deps,
- c_args : ['-DTEST_PROGRAM'],
- )
- # the test-setup expects the helpers in the toplevel build-directory
- link = meson.project_build_root() / test_name
- run_command('ln', '-srf', exe.full_path(), link,
- check : true)
-endforeach
+if program_tests
+ foreach libmount_test: libmount_tests
+ test_name = 'test_mount_' + libmount_test
+ exe = executable(
+ test_name,
+ 'src/' + libmount_test_src_override.get(libmount_test, libmount_test) + '.c',
+ include_directories : [dir_include, dir_libblkid],
+ link_with : [lib__mount, lib_common, lib_blkid_static],
+ dependencies : lib__mount_deps,
+ c_args : ['-DTEST_PROGRAM'],
+ )
+ # the test-setup expects the helpers in the toplevel build-directory
+ link = meson.project_build_root() / test_name
+ run_command('ln', '-srf', exe.full_path(), link,
+ check : true)
+ endforeach
+endif
subdir('python')
diff --git a/libmount/python/context.c b/libmount/python/context.c
index 353a893..36cf488 100644
--- a/libmount/python/context.c
+++ b/libmount/python/context.c
@@ -1175,43 +1175,17 @@ static PyObject *Context_repr(ContextObjext *self)
PyTypeObject ContextType = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libmount.Context", /*tp_name*/
- sizeof(ContextObjext), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Context_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- NULL, /*tp_getattr*/
- NULL, /*tp_setattr*/
- NULL, /*tp_compare*/
- (reprfunc) Context_repr,
- NULL, /*tp_as_number*/
- NULL, /*tp_as_sequence*/
- NULL, /*tp_as_mapping*/
- NULL, /*tp_hash */
- NULL, /*tp_call*/
- NULL, /*tp_str*/
- NULL, /*tp_getattro*/
- NULL, /*tp_setattro*/
- NULL, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- Context_HELP, /* tp_doc */
- NULL, /* tp_traverse */
- NULL, /* tp_clear */
- NULL, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- NULL, /* tp_iter */
- NULL, /* tp_iternext */
- Context_methods, /* tp_methods */
- Context_members, /* tp_members */
- Context_getseters, /* tp_getset */
- NULL, /* tp_base */
- NULL, /* tp_dict */
- NULL, /* tp_descr_get */
- NULL, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)Context_init, /* tp_init */
- NULL, /* tp_alloc */
- Context_new, /* tp_new */
+ .tp_name = "libmount.Context",
+ .tp_basicsize = sizeof(ContextObjext),
+ .tp_dealloc = (destructor)Context_dealloc,
+ .tp_repr = (reprfunc) Context_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = Context_HELP,
+ .tp_methods = Context_methods,
+ .tp_members = Context_members,
+ .tp_getset = Context_getseters,
+ .tp_init = (initproc)Context_init,
+ .tp_new = Context_new,
};
void Context_AddModuleObject(PyObject *mod)
diff --git a/libmount/python/fs.c b/libmount/python/fs.c
index e989124..ccb2460 100644
--- a/libmount/python/fs.c
+++ b/libmount/python/fs.c
@@ -832,43 +832,17 @@ static PyObject *Fs_copy_fs(FsObject *self, PyObject *args, PyObject *kwds)
PyTypeObject FsType = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libmount.Fs", /*tp_name*/
- sizeof(FsObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Fs_destructor, /*tp_dealloc*/
- 0, /*tp_print*/
- NULL, /*tp_getattr*/
- NULL, /*tp_setattr*/
- NULL, /*tp_compare*/
- (reprfunc)Fs_repr, /*tp_repr*/
- NULL, /*tp_as_number*/
- NULL, /*tp_as_sequence*/
- NULL, /*tp_as_mapping*/
- NULL, /*tp_hash */
- NULL, /*tp_call*/
- NULL, /*tp_str*/
- NULL, /*tp_getattro*/
- NULL, /*tp_setattro*/
- NULL, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- Fs_HELP, /* tp_doc */
- NULL, /* tp_traverse */
- NULL, /* tp_clear */
- NULL, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- NULL, /* tp_iter */
- NULL, /* tp_iternext */
- Fs_methods, /* tp_methods */
- Fs_members, /* tp_members */
- Fs_getseters, /* tp_getset */
- NULL, /* tp_base */
- NULL, /* tp_dict */
- NULL, /* tp_descr_get */
- NULL, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)Fs_init, /* tp_init */
- NULL, /* tp_alloc */
- Fs_new, /* tp_new */
+ .tp_name = "libmount.Fs",
+ .tp_basicsize = sizeof(FsObject),
+ .tp_dealloc = (destructor)Fs_destructor,
+ .tp_repr = (reprfunc)Fs_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = Fs_HELP,
+ .tp_methods = Fs_methods,
+ .tp_members = Fs_members,
+ .tp_getset = Fs_getseters,
+ .tp_init = (initproc)Fs_init,
+ .tp_new = Fs_new,
};
void FS_AddModuleObject(PyObject *mod)
diff --git a/libmount/python/meson.build b/libmount/python/meson.build
index c5feb7b..e1a79d1 100644
--- a/libmount/python/meson.build
+++ b/libmount/python/meson.build
@@ -17,23 +17,23 @@ if LINUX
pylibmount_sources += 'context.c'
endif
-python.extension_module(
- 'pylibmount',
- pylibmount_sources,
- include_directories : [dir_include, dir_libmount],
- subdir : 'libmount',
- link_with : lib_mount,
- dependencies : python.dependency(),
- c_args : [
- '-Wno-cast-function-type',
+if build_python
+ python.extension_module(
+ 'pylibmount',
+ pylibmount_sources,
+ include_directories : [dir_include, dir_libmount],
+ subdir : 'libmount',
+ link_with : lib_mount,
+ dependencies : python.dependency(),
+ c_args : [
+ '-Wno-cast-function-type',
- # https://github.com/util-linux/util-linux/issues/2366
- python.language_version().version_compare('>=3.12') ?
- [ '-Wno-error=redundant-decls' ] : [],
- ],
- install : true)
+ # https://github.com/util-linux/util-linux/issues/2366
+ python.language_version().version_compare('>=3.12') ?
+ [ '-Wno-error=redundant-decls' ] : [],
+ ],
+ install : true)
-if build_python
python.install_sources(
'__init__.py',
subdir : 'libmount',
diff --git a/libmount/python/tab.c b/libmount/python/tab.c
index 000bc13..d33a1fe 100644
--- a/libmount/python/tab.c
+++ b/libmount/python/tab.c
@@ -731,43 +731,17 @@ static PyObject *Table_repr(TableObject *self)
PyTypeObject TableType = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libmount.Table", /*tp_name*/
- sizeof(TableObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Table_destructor, /*tp_dealloc*/
- 0, /*tp_print*/
- NULL, /*tp_getattr*/
- NULL, /*tp_setattr*/
- NULL, /*tp_compare*/
- (reprfunc) Table_repr, /*tp_repr*/
- NULL, /*tp_as_number*/
- NULL, /*tp_as_sequence*/
- NULL, /*tp_as_mapping*/
- NULL, /*tp_hash */
- NULL, /*tp_call*/
- NULL, /*tp_str*/
- NULL, /*tp_getattro*/
- NULL, /*tp_setattro*/
- NULL, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- Table_HELP, /* tp_doc */
- NULL, /* tp_traverse */
- NULL, /* tp_clear */
- NULL, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- NULL, /* tp_iter */
- NULL, /* tp_iternext */
- Table_methods, /* tp_methods */
- Table_members, /* tp_members */
- Table_getseters, /* tp_getset */
- NULL, /* tp_base */
- NULL, /* tp_dict */
- NULL, /* tp_descr_get */
- NULL, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)Table_init, /* tp_init */
- NULL, /* tp_alloc */
- Table_new, /* tp_new */
+ .tp_name = "libmount.Table",
+ .tp_basicsize = sizeof(TableObject),
+ .tp_dealloc = (destructor)Table_destructor,
+ .tp_repr = (reprfunc) Table_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = Table_HELP,
+ .tp_methods = Table_methods,
+ .tp_members = Table_members,
+ .tp_getset = Table_getseters,
+ .tp_init = (initproc)Table_init,
+ .tp_new = Table_new,
};
void Table_AddModuleObject(PyObject *mod)
diff --git a/libmount/samples/Makemodule.am b/libmount/samples/Makemodule.am
new file mode 100644
index 0000000..e223ea6
--- /dev/null
+++ b/libmount/samples/Makemodule.am
@@ -0,0 +1,11 @@
+
+check_PROGRAMS += \
+ sample-mount-overwrite
+
+sample_mount_cflags = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+sample_mount_ldadd = libmount.la $(LDADD)
+
+
+sample_mount_overwrite_SOURCES = libmount/samples/overwrite.c
+sample_mount_overwrite_LDADD = $(sample_mount_ldadd)
+sample_mount_overwrite_CFLAGS = $(sample_mount_cflags)
diff --git a/libmount/samples/overwrite.c b/libmount/samples/overwrite.c
new file mode 100644
index 0000000..ff11b71
--- /dev/null
+++ b/libmount/samples/overwrite.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ *
+ * This sample reads the mountpoint entry from /etc/fstab and mounts it to the
+ * different (on the command line specified) mountpoint. The mount options
+ * settings are read from fstab.
+ */
+#include <stdlib.h>
+
+#include "c.h"
+
+#include "libmount.h"
+
+
+int main(int argc, char *argv[])
+{
+ char *target, *fstab_target;
+ struct libmnt_table *tab;
+ struct libmnt_fs *fs;
+ struct libmnt_context *cxt;
+ int rc;
+
+ if (argc != 3)
+ errx(EXIT_FAILURE, "usage: %s <mnt-from-fstab> <target>",
+ program_invocation_short_name);
+
+ fstab_target = argv[1];
+ target = argv[2];
+
+ printf("Mounting %s from fstab to %s\n", fstab_target, target);
+
+ tab = mnt_new_table_from_file("/etc/fstab");
+ if (!tab)
+ err(EXIT_FAILURE, "failed to parse fstab");
+
+ fs = mnt_table_find_target(tab, fstab_target, MNT_ITER_FORWARD);
+ if (!fs)
+ err(EXIT_FAILURE, "cannot found %s in fstab", argv[1]);
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ err(EXIT_FAILURE, "cannot allocate context");
+
+ mnt_context_set_fs(cxt, fs);
+ mnt_context_set_target(cxt, target);
+
+ rc = mnt_context_mount(cxt);
+
+ printf("Done: rc=%d status=%d\n", rc, mnt_context_get_status(cxt));
+
+ mnt_free_context(cxt);
+ mnt_unref_table(tab);
+ return rc == 0 && mnt_context_get_status(cxt) == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am
index 367bc46..f8cedbd 100644
--- a/libmount/src/Makemodule.am
+++ b/libmount/src/Makemodule.am
@@ -101,7 +101,7 @@ check_PROGRAMS += test_mount_context test_mount_context_mount
check_PROGRAMS += test_mount_monitor
endif
-libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
+libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS)
libmount_tests_ldflags = -static
libmount_tests_ldadd = libmount.la libblkid.la $(LDADD) $(REALTIME_LIBS)
diff --git a/libmount/src/cache.c b/libmount/src/cache.c
index 6286aeb..b795634 100644
--- a/libmount/src/cache.c
+++ b/libmount/src/cache.c
@@ -202,7 +202,7 @@ static int cache_add_entry(struct libmnt_cache *cache, char *key,
if (cache->nents == cache->nallocs) {
size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
- e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
+ e = reallocarray(cache->ents, sz, sizeof(struct mnt_cache_entry));
if (!e)
return -ENOMEM;
cache->ents = e;
@@ -748,7 +748,9 @@ char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
#ifdef TEST_PROGRAM
-static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
+static int test_resolve_path(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
@@ -771,7 +773,9 @@ static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
+static int test_resolve_spec(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
@@ -794,7 +798,9 @@ static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_read_tags(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
diff --git a/libmount/src/context.c b/libmount/src/context.c
index 0cd3201..952287a 100644
--- a/libmount/src/context.c
+++ b/libmount/src/context.c
@@ -107,7 +107,7 @@ void mnt_free_context(struct libmnt_context *cxt)
mnt_unref_optlist(cxt->optlist_saved);
mnt_unref_optlist(cxt->optlist);
- mnt_free_lock(cxt->lock);
+ mnt_unref_lock(cxt->lock);
mnt_free_update(cxt->update);
mnt_context_set_target_ns(cxt, NULL);
@@ -314,6 +314,8 @@ int mnt_context_reset_status(struct libmnt_context *cxt)
if (!cxt)
return -EINVAL;
+ reset_syscall_status(cxt);
+
cxt->syscall_status = 1; /* means not called yet */
cxt->helper_exec_status = 1;
cxt->helper_status = 0;
@@ -549,10 +551,10 @@ int mnt_context_enable_onlyonce(struct libmnt_context *cxt, int enable)
}
/**
- * mnt_context_is_lazy:
+ * mnt_context_is_onlyonce:
* @cxt: mount context
*
- * Returns: 1 if lazy umount is enabled or 0
+ * Returns: 1 if only-once mount is enabled or 0
*/
int mnt_context_is_onlyonce(struct libmnt_context *cxt)
{
@@ -2179,6 +2181,10 @@ int mnt_context_prepare_update(struct libmnt_context *cxt)
rc = mnt_update_set_fs(cxt->update, flags,
NULL, cxt->fs);
+ if (mnt_update_is_ready(cxt->update)) {
+ DBG(CXT, ul_debugobj(cxt, "update is ready"));
+ mnt_update_start(cxt->update);
+ }
return rc < 0 ? rc : 0;
}
@@ -2207,9 +2213,9 @@ int mnt_context_update_tabs(struct libmnt_context *cxt)
&& mnt_context_get_helper_status(cxt) == 0
&& mnt_context_utab_writable(cxt)) {
- if (mnt_update_already_done(cxt->update, cxt->lock)) {
+ if (mnt_update_already_done(cxt->update)) {
DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated"));
- goto end;
+ goto emit;
}
} else if (cxt->helper) {
DBG(CXT, ul_debugobj(cxt, "don't update: external helper"));
@@ -2225,8 +2231,13 @@ int mnt_context_update_tabs(struct libmnt_context *cxt)
}
rc = mnt_update_table(cxt->update, cxt->lock);
+emit:
+ if (rc == 0 && !mnt_context_within_helper(cxt))
+ mnt_update_emit_event(cxt->update);
end:
+ mnt_update_end(cxt->update);
+
if (!mnt_context_switch_ns(cxt, ns_old))
return -MNT_ERR_NAMESPACE;
return rc;
@@ -2737,7 +2748,6 @@ int mnt_context_get_excode(
return rc;
}
-
/**
* mnt_context_init_helper
* @cxt: mount context
@@ -2773,6 +2783,14 @@ int mnt_context_init_helper(struct libmnt_context *cxt, int action,
return rc;
}
+/*
+ * libmount used in /sbin/[u]mount.<type> helper
+ */
+int mnt_context_within_helper(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_HELPER);
+}
+
/**
* mnt_context_helper_setopt:
* @cxt: context
@@ -2850,7 +2868,7 @@ static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid)
if (!cxt)
return -EINVAL;
- pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1);
+ pids = reallocarray(cxt->children, cxt->nchildren + 1, sizeof(pid_t));
if (!pids)
return -ENOMEM;
@@ -3166,7 +3184,8 @@ struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt)
#ifdef TEST_PROGRAM
-static int test_search_helper(struct libmnt_test *ts, int argc, char *argv[])
+static int test_search_helper(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
const char *type;
@@ -3200,7 +3219,8 @@ static void lock_fallback(void)
mnt_unlock_file(lock);
}
-static int test_mount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3250,7 +3270,8 @@ static int test_mount(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_umount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_umount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3305,7 +3326,8 @@ err:
return rc;
}
-static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_flags(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3343,7 +3365,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[])
+static int test_cxtsync(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_fs *fs;
@@ -3387,7 +3410,8 @@ static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mountall(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_iter *itr;
@@ -3405,10 +3429,8 @@ static int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
mnt_context_set_options_pattern(cxt, argv[idx + 1]);
idx += 2;
}
- if (argv[idx] && !strcmp(argv[idx], "-t")) {
+ if (argv[idx] && !strcmp(argv[idx], "-t"))
mnt_context_set_fstype_pattern(cxt, argv[idx + 1]);
- idx += 2;
- }
}
while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index 41986e7..478a9fd 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -1571,6 +1571,12 @@ int mnt_context_get_mount_excode(
*/
syserr = mnt_context_get_syscall_errno(cxt);
+ if (buf && cxt->syscall_errmsg) {
+ snprintf(buf, bufsz, _("%s system call failed: %s"),
+ cxt->syscall_name ? : "mount",
+ cxt->syscall_errmsg);
+ return MNT_EX_FAIL;
+ }
switch(syserr) {
case EPERM:
@@ -1617,10 +1623,8 @@ int mnt_context_get_mount_excode(
return MNT_EX_SUCCESS;
if (buf)
snprintf(buf, bufsz, _("special device %s does not exist"), src);
- } else if (buf) {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ } else
+ goto generic_error;
break;
case ENOTDIR:
@@ -1633,10 +1637,8 @@ int mnt_context_get_mount_excode(
if (buf)
snprintf(buf, bufsz, _("special device %s does not exist "
"(a path prefix is not a directory)"), src);
- } else if (buf) {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ } else
+ goto generic_error;
break;
case EINVAL:
@@ -1717,10 +1719,8 @@ int mnt_context_get_mount_excode(
snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src);
else if (mflags & MS_BIND)
snprintf(buf, bufsz, _("bind %s failed"), src);
- else {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ else
+ goto generic_error;
break;
case ENOMEDIUM:
@@ -1740,9 +1740,11 @@ int mnt_context_get_mount_excode(
/* fallthrough */
default:
+ generic_error:
if (buf) {
errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
+ snprintf(buf, bufsz, _("%s system call failed: %m"),
+ cxt->syscall_name ? : "mount");
}
break;
}
@@ -1752,7 +1754,8 @@ int mnt_context_get_mount_excode(
#ifdef TEST_PROGRAM
-static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
+static int test_perms(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_optlist *ls;
@@ -1795,7 +1798,8 @@ static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_fixopts(struct libmnt_test *ts, int argc, char *argv[])
+static int test_fixopts(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_optlist *ls;
diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c
index 26394d5..79b6237 100644
--- a/libmount/src/context_umount.c
+++ b/libmount/src/context_umount.c
@@ -270,7 +270,7 @@ static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tg
*/
if (mnt_context_is_restricted(cxt)
|| *tgt != '/'
- || (cxt->flags & MNT_FL_HELPER)
+ || mnt_context_within_helper(cxt)
|| mnt_context_is_force(cxt)
|| mnt_context_is_lazy(cxt)
|| mnt_context_is_nocanonicalize(cxt)
diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
index 8c8f7f2..597b933 100644
--- a/libmount/src/hook_loopdev.c
+++ b/libmount/src/hook_loopdev.c
@@ -140,10 +140,6 @@ static int setup_loopdev(struct libmnt_context *cxt,
DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file));
- ol = mnt_context_get_optlist(cxt);
- if (!ol)
- return -ENOMEM;
-
if (mnt_optlist_is_rdonly(ol)) {
DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag"));
lo_flags |= LO_FLAGS_READ_ONLY;
@@ -356,15 +352,19 @@ success:
*/
mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux);
- /* we have to keep the device open until mount(1),
- * otherwise it will be auto-cleared by kernel
+ /*
+ * We have to keep the device open until mount(1), otherwise it
+ * will be auto-cleared by kernel. However we don't want to
+ * keep writeable fd as kernel wants to block all writers to
+ * the device being mounted (in the more hardened
+ * configurations). So grab read-only fd instead.
*/
- hd->loopdev_fd = loopcxt_get_fd(&lc);
+ hd->loopdev_fd = open(lc.device, O_RDONLY | O_CLOEXEC);
if (hd->loopdev_fd < 0) {
- DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD"));
+ DBG(LOOP,
+ ul_debugobj(cxt, "failed to reopen loopdev FD"));
rc = -errno;
- } else
- loopcxt_set_fd(&lc, -1, 0);
+ }
}
done:
loopcxt_deinit(&lc);
diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c
index dc3dfa7..f0cc381 100644
--- a/libmount/src/hook_mount.c
+++ b/libmount/src/hook_mount.c
@@ -65,6 +65,42 @@ static void close_sysapi_fds(struct libmnt_sysapi *api)
api->fd_tree = api->fd_fs = -1;
}
+static void save_fd_messages(struct libmnt_context *cxt, int fd)
+{
+ uint8_t buf[BUFSIZ];
+ int rc;
+
+ free(cxt->syscall_errmsg);
+ cxt->syscall_errmsg = NULL;
+
+ while ((rc = read(fd, buf, sizeof(buf))) != -1) {
+ if (rc > 0 && buf[rc - 1] == '\n')
+ buf[rc - 1] = '\0';
+ DBG(CXT, ul_debug("message from kernel: \"%*s\"", rc, buf));
+
+ if (rc < 3 || strncmp((char *) buf, "e ", 2) != 0)
+ continue;
+ if (cxt->syscall_errmsg)
+ strappend(&cxt->syscall_errmsg, "; ");
+
+ strappend(&cxt->syscall_errmsg, ((char *) buf) + 2);
+ }
+}
+
+static void hookset_set_syscall_status(struct libmnt_context *cxt,
+ const char *name, int x)
+{
+ struct libmnt_sysapi *api;
+
+ set_syscall_status(cxt, name, x);
+
+ if (!x) {
+ api = get_sysapi(cxt);
+ if (api && api->fd_fs >= 0)
+ save_fd_messages(cxt, api->fd_fs);
+ }
+}
+
/*
* This hookset uses 'struct libmnt_sysapi' (mountP.h) as hookset data.
*/
@@ -122,26 +158,33 @@ static inline int fsconfig_set_value(
const char *name, const char *value)
{
int rc;
- char *p = NULL;
+ char *s = NULL;
+ /* "\," is a way to use comma in values, let's remove \ escape */
if (value && strstr(value, "\\,")) {
- p = strdup(value);
- if (!p)
- return -EINVAL;
+ char *x, *p;
- strrem(p, '\\');
- value = p;
+ s = strdup(value);
+ if (!s)
+ return -EINVAL;
+ for (x = p = s; *x; p++, x++) {
+ if (*x == '\\' && *(x + 1) == ',')
+ x++;
+ *p = *x;
+ }
+ *p = '\0';
+ value = s;
}
DBG(HOOK, ul_debugobj(hs, " fsconfig(name=\"%s\" value=\"%s\")", name,
value ? : ""));
if (value) {
rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0);
- free(p);
+ free(s);
} else
rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
return rc;
}
@@ -181,6 +224,9 @@ static int configure_superblock(struct libmnt_context *cxt,
/* Ignore VFS flags, userspace and external options */
continue;
+ if (!value && mnt_opt_is_sepnodata(opt))
+ value = ""; /* force use the value as string */
+
rc = fsconfig_set_value(cxt, hs, fd, name, value);
if (rc != 0)
goto done;
@@ -206,7 +252,7 @@ static int open_fs_configuration_context(struct libmnt_context *cxt,
DBG(HOOK, ul_debug(" fsopen(%s)", type));
api->fd_fs = fsopen(type, FSOPEN_CLOEXEC);
- set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
+ hookset_set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
if (api->fd_fs < 0)
return -errno;
api->is_new_fs = 1;
@@ -245,7 +291,7 @@ static int open_mount_tree(struct libmnt_context *cxt, const char *path, unsigne
oflg & OPEN_TREE_CLONE ? " clone" : "",
oflg & AT_RECURSIVE ? " recursive" : ""));
fd = open_tree(AT_FDCWD, path, oflg);
- set_syscall_status(cxt, "open_tree", fd >= 0);
+ hookset_set_syscall_status(cxt, "open_tree", fd >= 0);
return fd;
}
@@ -285,19 +331,19 @@ static int hook_create_mount(struct libmnt_context *cxt,
DBG(HOOK, ul_debugobj(hs, "init FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_SET_STRING, "source", src, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
if (!rc)
rc = configure_superblock(cxt, hs, api->fd_fs, 0);
if (!rc) {
DBG(HOOK, ul_debugobj(hs, "create FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
}
if (!rc) {
api->fd_tree = fsmount(api->fd_fs, FSMOUNT_CLOEXEC, 0);
- set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
+ hookset_set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
if (api->fd_tree < 0)
rc = -errno;
}
@@ -346,7 +392,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt,
if (api->fd_fs < 0) {
api->fd_fs = fspick(api->fd_tree, "", FSPICK_EMPTY_PATH |
FSPICK_NO_AUTOMOUNT);
- set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
+ hookset_set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
if (api->fd_fs < 0)
return -errno;
}
@@ -355,7 +401,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt,
if (!rc) {
DBG(HOOK, ul_debugobj(hs, "re-configurate FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
}
DBG(HOOK, ul_debugobj(hs, "reconf FS done [rc=%d]", rc));
@@ -393,7 +439,7 @@ static int set_vfsflags(struct libmnt_context *cxt,
errno = 0;
rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr));
- set_syscall_status(cxt, "mount_setattr", rc == 0);
+ hookset_set_syscall_status(cxt, "mount_setattr", rc == 0);
if (rc && errno == EINVAL)
return -MNT_ERR_APPLYFLAGS;
@@ -485,7 +531,7 @@ static int hook_set_propagation(struct libmnt_context *cxt,
(uint64_t) attr.propagation));
rc = mount_setattr(api->fd_tree, "", flgs, &attr, sizeof(attr));
- set_syscall_status(cxt, "mount_setattr", rc == 0);
+ hookset_set_syscall_status(cxt, "mount_setattr", rc == 0);
if (rc && errno == EINVAL)
return -MNT_ERR_APPLYFLAGS;
@@ -527,7 +573,7 @@ static int hook_attach_target(struct libmnt_context *cxt,
}
rc = move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
- set_syscall_status(cxt, "move_mount", rc == 0);
+ hookset_set_syscall_status(cxt, "move_mount", rc == 0);
return rc == 0 ? 0 : -errno;
}
@@ -613,7 +659,7 @@ static int init_sysapi(struct libmnt_context *cxt,
else if (!fsopen_is_supported()) {
errno = ENOSYS;
rc = -errno;
- set_syscall_status(cxt, "fsopen", rc == 0);
+ hookset_set_syscall_status(cxt, "fsopen", rc == 0);
}
if (rc < 0)
goto fail;
diff --git a/libmount/src/hook_veritydev.c b/libmount/src/hook_veritydev.c
index f91778a..6a9e644 100644
--- a/libmount/src/hook_veritydev.c
+++ b/libmount/src/hook_veritydev.c
@@ -349,25 +349,8 @@ static int setup_veritydev( struct libmnt_context *cxt,
backing_file = mnt_fs_get_srcpath(cxt->fs);
if (!backing_file)
return -EINVAL;
- else {
- /* To avoid clashes, prefix libmnt_ to all mapper devices */
- char *p, *path = strdup(backing_file);
- if (!path)
- return -ENOMEM;
-
- p = stripoff_last_component(path);
- if (p)
- mapper_device = calloc(strlen(p) + sizeof("libmnt_"), sizeof(char));
- if (mapper_device) {
- strcat(mapper_device, "libmnt_");
- strcat(mapper_device, p);
- }
- free(path);
- if (!mapper_device)
- return -ENOMEM;
- }
- DBG(HOOK, ul_debugobj(hs, "verity: setup for %s [%s]", backing_file, mapper_device));
+ DBG(HOOK, ul_debugobj(hs, "verity: setup for %s", backing_file));
/* verity.hashdevice= */
if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace)))
@@ -467,6 +450,13 @@ static int setup_veritydev( struct libmnt_context *cxt,
rc = -EINVAL;
}
+ /* To avoid clashes, use the roothash as the device name. This allows us to reuse already open devices, saving
+ * a lot of time and resources when there are duplicated mounts. If the roothash is the same, then the volumes
+ * are also guaranteed to be identical. This is what systemd also does, so we can deduplicate across the whole
+ * system. */
+ if (asprintf(&mapper_device, "%s-verity", root_hash) < 0)
+ rc = -ENOMEM;
+
if (!rc)
rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) );
if (rc)
@@ -506,7 +496,9 @@ static int setup_veritydev( struct libmnt_context *cxt,
* If the mapper device already exists, and if libcryptsetup supports it, get the root
* hash associated with the existing one and compare it with the parameter passed by
* the user. If they match, then we can be sure the user intended to mount the exact
- * same device, and simply reuse it and return success.
+ * same device, and simply reuse it and return success. Although we use the roothash
+ * as the device mapper name, and root privileges are required to open them, better be
+ * safe than sorry, so double check that the actual root hash used matches.
* The kernel does the refcounting for us.
* If libcryptsetup does not support getting the root hash out of an existing device,
* then return an error and tell the user that the device is already in use.
@@ -562,15 +554,10 @@ static int setup_veritydev( struct libmnt_context *cxt,
}
if (!rc) {
- hsd->devname = calloc(strlen(mapper_device)
- + sizeof(_PATH_DEV_MAPPER) + 2, sizeof(char));
- if (!hsd->devname)
+ if (asprintf(&hsd->devname, _PATH_DEV_MAPPER "/%s", mapper_device) == -1)
rc = -ENOMEM;
- else {
- strcat(hsd->devname, _PATH_DEV_MAPPER "/");
- strcat(hsd->devname, mapper_device);
+ else
rc = mnt_fs_set_source(cxt->fs, hsd->devname);
- }
}
done:
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
index 06c2704..d893c26 100644
--- a/libmount/src/libmount.h.in
+++ b/libmount/src/libmount.h.in
@@ -444,6 +444,8 @@ extern const struct libmnt_optmap *mnt_get_builtin_optmap(int id);
/* lock.c */
extern struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
__ul_attribute__((warn_unused_result));
+extern void mnt_ref_lock(struct libmnt_lock *ml);
+extern void mnt_unref_lock(struct libmnt_lock *ml);
extern void mnt_free_lock(struct libmnt_lock *ml);
extern void mnt_unlock_file(struct libmnt_lock *ml);
@@ -697,6 +699,8 @@ extern int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable);
extern int mnt_monitor_enable_userspace(struct libmnt_monitor *mn,
int enable, const char *filename);
+extern int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable);
+
extern int mnt_monitor_get_fd(struct libmnt_monitor *mn);
extern int mnt_monitor_close_fd(struct libmnt_monitor *mn);
extern int mnt_monitor_wait(struct libmnt_monitor *mn, int timeout);
diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym
index 715bb5c..2b6b12d 100644
--- a/libmount/src/libmount.sym
+++ b/libmount/src/libmount.sym
@@ -370,8 +370,14 @@ MOUNT_2_38 {
MOUNT_2_39 {
mnt_cache_set_sbprobe;
mnt_context_enable_onlyonce;
- mnt_context_is_lazy;
+ mnt_context_is_onlyonce;
mnt_context_enable_noautofs;
mnt_table_enable_noautofs;
mnt_table_is_noautofs;
} MOUNT_2_38;
+
+MOUNT_2_40 {
+ mnt_ref_lock;
+ mnt_unref_lock;
+ mnt_monitor_veil_kernel;
+} MOUNT_2_39;
diff --git a/libmount/src/lock.c b/libmount/src/lock.c
index 4835406..8aca8a7 100644
--- a/libmount/src/lock.c
+++ b/libmount/src/lock.c
@@ -36,6 +36,7 @@
* lock handler
*/
struct libmnt_lock {
+ int refcount; /* reference counter */
char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
int lockfile_fd; /* lock file descriptor */
@@ -73,6 +74,7 @@ struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id __attribute__((_
if (!ml)
goto err;
+ ml->refcount = 1;
ml->lockfile_fd = -1;
ml->lockfile = lo;
@@ -89,18 +91,57 @@ err:
* mnt_free_lock:
* @ml: struct libmnt_lock handler
*
- * Deallocates mnt_lock.
+ * Deallocates libmnt_lock. This function does not care about reference count. Don't
+ * use this function directly -- it's better to use mnt_unref_lock().
+ *
+ * The reference counting is supported since util-linux v2.40.
*/
void mnt_free_lock(struct libmnt_lock *ml)
{
if (!ml)
return;
- DBG(LOCKS, ul_debugobj(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
+
+ DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%d]",
+ ml->locked ? " !!! LOCKED !!!" : "",
+ ml->refcount));
free(ml->lockfile);
free(ml);
}
/**
+ * mnt_ref_lock:
+ * @ml: lock pointer
+ *
+ * Increments reference counter.
+ *
+ * Since: 2.40
+ */
+void mnt_ref_lock(struct libmnt_lock *ml)
+{
+ if (ml) {
+ ml->refcount++;
+ /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/
+ }
+}
+
+/**
+ * mnt_unref_lock:
+ * @ml: lock pointer
+ *
+ * De-increments reference counter, on zero the @ml is automatically
+ * deallocated by mnt_free_lock).
+ */
+void mnt_unref_lock(struct libmnt_lock *ml)
+{
+ if (ml) {
+ ml->refcount--;
+ /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/
+ if (ml->refcount <= 0)
+ mnt_free_lock(ml);
+ }
+}
+
+/**
* mnt_lock_block_signals:
* @ml: struct libmnt_lock handler
* @enable: TRUE/FALSE
@@ -146,7 +187,7 @@ static int lock_simplelock(struct libmnt_lock *ml)
const char *lfile;
int rc;
struct stat sb;
- const mode_t lock_mask = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+ const mode_t lock_mask = S_IRUSR|S_IWUSR;
assert(ml);
@@ -161,8 +202,7 @@ static int lock_simplelock(struct libmnt_lock *ml)
sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
}
- ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC,
- S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, lock_mask);
if (ml->lockfile_fd < 0) {
rc = -errno;
goto err;
@@ -287,7 +327,7 @@ static void clean_lock(void)
if (!lock)
return;
mnt_unlock_file(lock);
- mnt_free_lock(lock);
+ mnt_unref_lock(lock);
}
static void __attribute__((__noreturn__)) sig_handler(int sig)
@@ -295,7 +335,8 @@ static void __attribute__((__noreturn__)) sig_handler(int sig)
errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
}
-static int test_lock(struct libmnt_test *ts, int argc, char *argv[])
+static int test_lock(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
time_t synctime = 0;
unsigned int usecs;
@@ -367,7 +408,7 @@ static int test_lock(struct libmnt_test *ts, int argc, char *argv[])
increment_data(datafile, verbose, l);
mnt_unlock_file(lock);
- mnt_free_lock(lock);
+ mnt_unref_lock(lock);
lock = NULL;
/* The mount command usually finishes after a mtab update. We
diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c
index f99751e..941f5d5 100644
--- a/libmount/src/monitor.c
+++ b/libmount/src/monitor.c
@@ -64,6 +64,8 @@ struct libmnt_monitor {
int fd; /* public monitor file descriptor */
struct list_head ents;
+
+ unsigned int kernel_veiled: 1;
};
struct monitor_opers {
@@ -226,18 +228,16 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
assert(me->path);
/*
- * libmount uses rename(2) to atomically update utab, monitor
- * rename changes is too tricky. It seems better to monitor utab
- * lockfile close.
+ * libmount uses utab.event file to monitor and control utab updates
*/
- if (asprintf(&filename, "%s.lock", me->path) <= 0) {
- rc = -errno;
+ if (asprintf(&filename, "%s.event", me->path) <= 0) {
+ rc = -ENOMEM;
goto done;
}
- /* try lock file if already exists */
+ /* try event file if already exists */
errno = 0;
- wd = inotify_add_watch(me->fd, filename, IN_CLOSE_NOWRITE);
+ wd = inotify_add_watch(me->fd, filename, IN_CLOSE_WRITE);
if (wd >= 0) {
DBG(MONITOR, ul_debug(" added inotify watch for %s [fd=%d]", filename, wd));
rc = 0;
@@ -256,7 +256,7 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
if (!*filename)
break;
- /* try directory where is the lock file */
+ /* try directory where is the event file */
errno = 0;
wd = inotify_add_watch(me->fd, filename, IN_CREATE|IN_ISDIR);
if (wd >= 0) {
@@ -339,10 +339,10 @@ static int userspace_event_verify(struct libmnt_monitor *mn,
e = (const struct inotify_event *) p;
DBG(MONITOR, ul_debugobj(mn, " inotify event 0x%x [%s]\n", e->mask, e->len ? e->name : ""));
- if (e->mask & IN_CLOSE_NOWRITE)
+ if (e->mask & IN_CLOSE_WRITE)
status = 1;
else {
- /* event on lock file */
+ /* add watch for the event file */
userspace_add_watch(me, &status, &fd);
if (fd != e->wd) {
@@ -473,12 +473,28 @@ err:
return rc;
}
+static int kernel_event_verify(struct libmnt_monitor *mn,
+ struct monitor_entry *me)
+{
+ int status = 1;
+
+ if (!mn || !me || me->fd < 0)
+ return 0;
+
+ if (mn->kernel_veiled && access(MNT_PATH_UTAB ".act", F_OK) == 0) {
+ status = 0;
+ DBG(MONITOR, ul_debugobj(mn, "kernel event veiled"));
+ }
+ return status;
+}
+
/*
* kernel monitor operations
*/
static const struct monitor_opers kernel_opers = {
.op_get_fd = kernel_monitor_get_fd,
.op_close_fd = kernel_monitor_close_fd,
+ .op_event_verify = kernel_event_verify
};
/**
@@ -547,6 +563,28 @@ err:
return rc;
}
+/**
+ * mnt_monitor_veil_kernel:
+ * @mn: monitor instance
+ * @enable: 1 or 0
+ *
+ * Force monitor to ignore kernel events if the same mount/umount operation
+ * will generate an userspace event later. The kernel-only mount operation will
+ * be not affected.
+ *
+ * Return: 0 on success and <0 on error.
+ *
+ * Since: 2.40
+ */
+int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable)
+{
+ if (!mn)
+ return -EINVAL;
+
+ mn->kernel_veiled = enable ? 1 : 0;
+ return 0;
+}
+
/*
* Add/Remove monitor entry to/from monitor epoll.
*/
@@ -854,6 +892,8 @@ static struct libmnt_monitor *create_test_monitor(int argc, char *argv[])
warn("failed to initialize kernel monitor");
goto err;
}
+ } else if (strcmp(argv[i], "veil") == 0) {
+ mnt_monitor_veil_kernel(mn, 1);
}
}
if (i == 1) {
@@ -870,7 +910,8 @@ err:
/*
* create a monitor and add the monitor fd to epoll
*/
-static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int cleanup)
+static int __test_epoll(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[], int cleanup)
{
int fd, efd = -1, rc = -1;
struct epoll_event ev;
@@ -900,12 +941,14 @@ static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int clea
goto done;
}
- printf("waiting for changes...\n");
do {
const char *filename = NULL;
struct epoll_event events[1];
- int n = epoll_wait(efd, events, 1, -1);
+ int n;
+
+ printf("waiting for changes...\n");
+ n = epoll_wait(efd, events, 1, -1);
if (n < 0) {
rc = -errno;
warn("polling error");
@@ -947,7 +990,8 @@ static int test_epoll_cleanup(struct libmnt_test *ts, int argc, char *argv[])
/*
* create a monitor and wait for a change
*/
-static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
+static int test_wait(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *filename;
struct libmnt_monitor *mn = create_test_monitor(argc, argv);
@@ -962,6 +1006,7 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
while (mnt_monitor_next_change(mn, &filename, NULL) == 0)
printf(" %s: change detected\n", filename);
+ printf("waiting for changes...\n");
}
mnt_unref_monitor(mn);
return 0;
@@ -970,9 +1015,9 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
- { "--epoll", test_epoll, "<userspace kernel ...> monitor in epoll" },
- { "--epoll-clean", test_epoll_cleanup, "<userspace kernel ...> monitor in epoll and clean events" },
- { "--wait", test_wait, "<userspace kernel ...> monitor wait function" },
+ { "--epoll", test_epoll, "<userspace kernel veil ...> monitor in epoll" },
+ { "--epoll-clean", test_epoll_cleanup, "<userspace kernel veil ...> monitor in epoll and clean events" },
+ { "--wait", test_wait, "<userspace kernel veil ...> monitor wait function" },
{ NULL }
};
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 339e276..fcc40bf 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -432,6 +432,7 @@ struct libmnt_context
int syscall_status; /* 1: not called yet, 0: success, <0: -errno */
const char *syscall_name; /* failed syscall name */
+ char *syscall_errmsg; /* message from kernel */
struct libmnt_ns ns_orig; /* original namespace */
struct libmnt_ns ns_tgt; /* target namespace */
@@ -479,22 +480,27 @@ struct libmnt_context
/* Flags usable with MS_BIND|MS_REMOUNT */
#define MNT_BIND_SETTABLE (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOATIME|MS_NODIRATIME|MS_RELATIME|MS_RDONLY|MS_NOSYMFOLLOW)
-#define set_syscall_status(_cxt, _name, _x) __extension__ ({ \
- if (!(_x)) { \
- DBG(CXT, ul_debug("syscall '%s' [%m]", _name)); \
- (_cxt)->syscall_status = -errno; \
- (_cxt)->syscall_name = (_name); \
- } else { \
- DBG(CXT, ul_debug("syscall '%s' [success]", _name)); \
- (_cxt)->syscall_status = 0; \
- } \
- })
-
-#define reset_syscall_status(_cxt) __extension__ ({ \
- DBG(CXT, ul_debug("reset syscall status")); \
- (_cxt)->syscall_status = 0; \
- (_cxt)->syscall_name = NULL; \
- })
+static inline void set_syscall_status(struct libmnt_context *cxt, const char *name, int x)
+{
+ if (!x) {
+ DBG(CXT, ul_debug("syscall '%s' [%m]", name));
+ cxt->syscall_status = -errno;
+ cxt->syscall_name = name;
+ } else {
+ DBG(CXT, ul_debug("syscall '%s' [success]", name));
+ cxt->syscall_status = 0;
+ }
+}
+
+static inline void reset_syscall_status(struct libmnt_context *cxt)
+{
+ DBG(CXT, ul_debug("reset syscall status"));
+ cxt->syscall_status = 0;
+ cxt->syscall_name = NULL;
+
+ free(cxt->syscall_errmsg);
+ cxt->syscall_errmsg = NULL;
+}
/* optmap.c */
extern const struct libmnt_optmap *mnt_optmap_get_entry(
@@ -506,6 +512,7 @@ extern const struct libmnt_optmap *mnt_optmap_get_entry(
/* optstr.c */
extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end);
+extern int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing);
extern int mnt_buffer_append_option(struct ul_buffer *buf,
const char *name, size_t namesz,
@@ -601,6 +608,7 @@ extern int mnt_opt_set_value(struct libmnt_opt *opt, const char *str);
extern int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num);
extern int mnt_opt_set_quoted_value(struct libmnt_opt *opt, const char *str);
extern int mnt_opt_is_external(struct libmnt_opt *opt);
+extern int mnt_opt_is_sepnodata(struct libmnt_opt *opt);
/* fs.c */
extern int mnt_fs_follow_optlist(struct libmnt_fs *fs, struct libmnt_optlist *ol);
@@ -617,6 +625,8 @@ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o);
extern int mnt_context_utab_writable(struct libmnt_context *cxt);
extern const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt);
+extern int mnt_context_within_helper(struct libmnt_context *cxt);
+
extern int mnt_context_get_mountinfo(struct libmnt_context *cxt, struct libmnt_table **tb);
extern int mnt_context_get_mountinfo_for_target(struct libmnt_context *cxt,
struct libmnt_table **mountinfo, const char *tgt);
@@ -658,9 +668,11 @@ extern int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs
extern struct libmnt_optlist *mnt_context_get_optlist(struct libmnt_context *cxt);
/* tab_update.c */
+extern int mnt_update_emit_event(struct libmnt_update *upd);
extern int mnt_update_set_filename(struct libmnt_update *upd, const char *filename);
-extern int mnt_update_already_done(struct libmnt_update *upd,
- struct libmnt_lock *lc);
+extern int mnt_update_already_done(struct libmnt_update *upd);
+extern int mnt_update_start(struct libmnt_update *upd);
+extern int mnt_update_end(struct libmnt_update *upd);
#if __linux__
/* btrfs.c */
diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c
index 0702ada..476acfd 100644
--- a/libmount/src/optlist.c
+++ b/libmount/src/optlist.c
@@ -46,6 +46,7 @@ struct libmnt_opt {
unsigned int external : 1, /* visible for external helpers only */
recursive : 1, /* recursive flag */
+ sepnodata : 1, /* value separator, but without data ("name=") */
is_linux : 1, /* defined in ls->linux_map (VFS attr) */
quoted : 1; /* name="value" */
};
@@ -438,6 +439,10 @@ static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
opt->value = strndup(value, valsz);
if (!opt->value)
goto fail;
+
+ } else if (value) {
+ /* separator specified, but empty value ("name=") */
+ opt->sepnodata = 1;
}
if (namesz) {
opt->name = strndup(name, namesz);
@@ -957,7 +962,8 @@ int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr,
continue;
rc = mnt_buffer_append_option(&buf,
opt->name, strlen(opt->name),
- opt->value,
+ opt->value ? opt->value :
+ opt->sepnodata ? "" : NULL,
opt->value ? strlen(opt->value) : 0,
opt->quoted);
if (rc)
@@ -1043,6 +1049,7 @@ struct libmnt_optlist *mnt_copy_optlist(struct libmnt_optlist *ls)
no->src = opt->src;
no->external = opt->external;
no->quoted = opt->quoted;
+ no->sepnodata = opt->sepnodata;
}
}
@@ -1184,6 +1191,11 @@ int mnt_opt_is_external(struct libmnt_opt *opt)
return opt && opt->external ? 1 : 0;
}
+int mnt_opt_is_sepnodata(struct libmnt_opt *opt)
+{
+ return opt->sepnodata;
+}
+
#ifdef TEST_PROGRAM
@@ -1241,7 +1253,8 @@ static inline unsigned long str2flg(const char *str)
return (unsigned long) strtox64_or_err(str, "connt convert string to flags");
}
-static int test_append_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1257,7 +1270,8 @@ static int test_append_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_prepend_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1273,7 +1287,8 @@ static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1289,7 +1304,8 @@ static int test_set_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1305,7 +1321,8 @@ static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1321,7 +1338,8 @@ static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_get_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
const struct libmnt_optmap *map;
@@ -1380,7 +1398,8 @@ done:
return rc;
}
-static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
unsigned long flags = 0;
@@ -1397,6 +1416,36 @@ static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
+static int test_split(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
+{
+ struct libmnt_optlist *ol;
+ int rc;
+ struct libmnt_iter itr;
+ struct libmnt_opt *opt;
+ const char *name, *value;
+
+ if (argc != 2)
+ return -EINVAL;
+ rc = mk_optlist(&ol, argv[1]);
+ if (rc)
+ goto done;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ name = mnt_opt_get_name(opt);
+ value = mnt_opt_get_value(opt);
+
+ printf("%s = %s\n", name, value ?: "(null)");
+ }
+
+done:
+ mnt_unref_optlist(ol);
+ return rc;
+}
+
+
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
@@ -1407,6 +1456,7 @@ int main(int argc, char *argv[])
{ "--set-flg", test_set_flg, "<list> <flg> linux|user set to the list" },
{ "--get-str", test_get_str, "<list> [linux|user] all options in string" },
{ "--get-flg", test_get_flg, "<list> linux|user all options by flags" },
+ { "--split", test_split, "<list> split options into key-value pairs"},
{ NULL }
};
diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c
index e86b962..7ebc295 100644
--- a/libmount/src/optstr.c
+++ b/libmount/src/optstr.c
@@ -43,13 +43,16 @@ struct libmnt_optloc {
* Locates the first option that matches @name. The @end is set to the
* char behind the option (it means ',' or \0).
*
+ * @ol is optional.
+ *
* Returns negative number on parse error, 1 when not found and 0 on success.
*/
-static int mnt_optstr_locate_option(char *optstr, const char *name,
+static int mnt_optstr_locate_option(char *optstr,
+ const char *name, size_t namesz,
struct libmnt_optloc *ol)
{
char *n;
- size_t namesz, nsz;
+ size_t nsz;
int rc;
if (!optstr)
@@ -57,27 +60,30 @@ static int mnt_optstr_locate_option(char *optstr, const char *name,
assert(name);
- namesz = strlen(name);
+ if (!namesz)
+ namesz = strlen(name);
if (!namesz)
return 1;
do {
rc = ul_optstr_next(&optstr, &n, &nsz,
- &ol->value, &ol->valsz);
+ ol ? &ol->value : NULL,
+ ol ? &ol->valsz : NULL);
if (rc)
break;
if (namesz == nsz && strncmp(n, name, nsz) == 0) {
- ol->begin = n;
- ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
- ol->namesz = nsz;
+ if (ol) {
+ ol->begin = n;
+ ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
+ ol->namesz = nsz;
+ }
return 0;
}
} while(1);
return rc;
}
-
/**
* mnt_optstr_next_option:
* @optstr: option string, returns the position of the next option
@@ -223,7 +229,7 @@ int mnt_optstr_get_option(const char *optstr, const char *name,
if (!optstr || !name)
return -EINVAL;
- rc = mnt_optstr_locate_option((char *) optstr, name, &ol);
+ rc = mnt_optstr_locate_option((char *) optstr, name, 0, &ol);
if (!rc) {
if (value)
*value = ol.value;
@@ -255,7 +261,7 @@ int mnt_optstr_deduplicate_option(char **optstr, const char *name)
do {
struct libmnt_optloc ol = MNT_INIT_OPTLOC;
- rc = mnt_optstr_locate_option(opt, name, &ol);
+ rc = mnt_optstr_locate_option(opt, name, 0, &ol);
if (!rc) {
if (begin) {
/* remove the previous instance */
@@ -368,7 +374,7 @@ int mnt_optstr_set_option(char **optstr, const char *name, const char *value)
return -EINVAL;
if (*optstr)
- rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
if (rc < 0)
return rc; /* parse error */
if (rc == 1)
@@ -411,7 +417,7 @@ int mnt_optstr_remove_option(char **optstr, const char *name)
if (!optstr || !name)
return -EINVAL;
- rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
if (rc != 0)
return rc;
@@ -571,6 +577,51 @@ int mnt_optstr_get_options(const char *optstr, char **subset,
return rc;
}
+/*
+ * @optstr: string with comma separated list of options
+ * @wanted: options expected in @optstr
+ * @missing: returns options from @wanted which missing in @optstr (optional)
+ *
+ * Retursn: <0 on error, 0 on missing options, 1 if nothing is missing
+ */
+int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing)
+{
+ char *name, *val, *str = (char *) wanted;
+ size_t namesz = 0, valsz = 0;
+ struct ul_buffer buf = UL_INIT_BUFFER;
+ int rc = 0;
+
+ if (!wanted)
+ return 1;
+ if (missing) {
+ /* caller wants data, prepare buffer */
+ ul_buffer_set_chunksize(&buf, strlen(wanted) + 3); /* to call realloc() only once */
+ *missing = NULL;
+ }
+
+ while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
+
+ rc = mnt_optstr_locate_option((char *) optstr, name, namesz, NULL);
+ if (rc == 1) { /* not found */
+ if (!missing)
+ return 0;
+ rc = mnt_buffer_append_option(&buf, name, namesz, val, valsz, 0);
+ }
+ if (rc < 0)
+ break;
+ rc = 0;
+ }
+
+ if (!rc && missing) {
+ if (ul_buffer_is_empty(&buf))
+ rc = 1;
+ else
+ *missing = ul_buffer_get_data(&buf, NULL, NULL);
+ } else
+ ul_buffer_free_data(&buf);
+
+ return rc;
+}
/**
* mnt_optstr_get_flags:
@@ -910,7 +961,8 @@ int mnt_match_options(const char *optstr, const char *pattern)
}
#ifdef TEST_PROGRAM
-static int test_append(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -933,7 +985,8 @@ static int test_append(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_prepend(struct libmnt_test *ts, int argc, char *argv[])
+static int test_prepend(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -956,7 +1009,8 @@ static int test_prepend(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_split(struct libmnt_test *ts, int argc, char *argv[])
+static int test_split(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr, *user = NULL, *fs = NULL, *vfs = NULL;
int rc;
@@ -982,7 +1036,8 @@ static int test_split(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_flags(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
int rc;
@@ -1010,7 +1065,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_apply(struct libmnt_test *ts, int argc, char *argv[])
+static int test_apply(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
int rc, map;
@@ -1042,7 +1098,8 @@ static int test_apply(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -1065,7 +1122,8 @@ static int test_set(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_get(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
const char *name;
@@ -1094,7 +1152,32 @@ static int test_get(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+static int test_missing(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
+{
+ const char *optstr;
+ const char *wanted;
+ char *missing = NULL;
+ int rc;
+
+ if (argc < 2)
+ return -EINVAL;
+ optstr = argv[1];
+ wanted = argv[2];
+
+ rc = mnt_optstr_get_missing(optstr, wanted, &missing);
+ if (rc == 0)
+ printf("missing: %s\n", missing);
+ else if (rc == 1) {
+ printf("nothing\n");
+ rc = 0;
+ } else
+ printf("parse error: %s\n", optstr);
+ return rc;
+}
+
+static int test_remove(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *name;
char *optstr;
@@ -1114,7 +1197,8 @@ static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_dedup(struct libmnt_test *ts, int argc, char *argv[])
+static int test_dedup(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *name;
char *optstr;
@@ -1134,7 +1218,8 @@ static int test_dedup(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_match(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr, *pattern;
@@ -1156,6 +1241,7 @@ int main(int argc, char *argv[])
{ "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" },
{ "--set", test_set, "<optstr> <name> [<value>] (un)set value" },
{ "--get", test_get, "<optstr> <name> search name in optstr" },
+ { "--missing",test_missing,"<optstr> <wanted> what from wanted is missing" },
{ "--remove", test_remove, "<optstr> <name> remove name in optstr" },
{ "--dedup", test_dedup, "<optstr> <name> deduplicate name in optstr" },
{ "--match", test_match, "<optstr> <pattern> compare optstr with pattern" },
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
index 9725664..526edce 100644
--- a/libmount/src/tab.c
+++ b/libmount/src/tab.c
@@ -1925,7 +1925,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
#ifdef TEST_PROGRAM
#include "pathnames.h"
-static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
+static int parser_errcb(struct libmnt_table *tb __attribute__((unused)),
+ const char *filename, int line)
{
fprintf(stderr, "%s:%d: parse error\n", filename, line);
@@ -1954,12 +1955,16 @@ err:
return NULL;
}
-static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
+static int test_copy_fs(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
int rc = -1;
+ if (argc != 2)
+ return -1;
+
tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
@@ -1984,7 +1989,8 @@ done:
return rc;
}
-static int test_parse(struct libmnt_test *ts, int argc, char *argv[])
+static int test_parse(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb = NULL;
struct libmnt_iter *itr = NULL;
@@ -2020,7 +2026,8 @@ done:
return rc;
}
-static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_idx(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs = NULL;
@@ -2065,7 +2072,8 @@ done:
return rc;
}
-static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
+static int test_find(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[], int dr)
{
struct libmnt_table *tb;
struct libmnt_fs *fs = NULL;
@@ -2107,23 +2115,29 @@ done:
return rc;
}
-static int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_bw(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
}
-static int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_fw(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
return test_find(ts, argc, argv, MNT_ITER_FORWARD);
}
-static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_pair(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
struct libmnt_cache *mpc = NULL;
int rc = -1;
+ if (argc != 4)
+ return -1;
+
tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
@@ -2144,13 +2158,17 @@ done:
return rc;
}
-static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_mountpoint(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
struct libmnt_cache *mpc = NULL;
int rc = -1;
+ if (argc != 2)
+ return -1;
+
tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
if (!tb)
return -1;
@@ -2171,13 +2189,17 @@ done:
return rc;
}
-static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
+static int test_is_mounted(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb = NULL, *fstab = NULL;
struct libmnt_fs *fs;
struct libmnt_iter *itr = NULL;
struct libmnt_cache *mpc = NULL;
+ if (argc != 2)
+ return -1;
+
tb = mnt_new_table_from_file("/proc/self/mountinfo");
if (!tb) {
fprintf(stderr, "failed to parse mountinfo\n");
@@ -2227,7 +2249,8 @@ static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)),
return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1;
}
-static int test_uniq(struct libmnt_test *ts, int argc, char *argv[])
+static int test_uniq(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
int rc = -1;
diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c
index d5fbc4d..2765caf 100644
--- a/libmount/src/tab_diff.c
+++ b/libmount/src/tab_diff.c
@@ -309,7 +309,8 @@ done:
#ifdef TEST_PROGRAM
-static int test_diff(struct libmnt_test *ts, int argc, char *argv[])
+static int test_diff(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb_old, *tb_new;
struct libmnt_tabdiff *diff;
@@ -317,6 +318,9 @@ static int test_diff(struct libmnt_test *ts, int argc, char *argv[])
struct libmnt_fs *old, *new;
int rc = -1, change;
+ if (argc != 3)
+ return -1;
+
tb_old = mnt_new_table_from_file(argv[1]);
tb_new = mnt_new_table_from_file(argv[2]);
diff = mnt_new_tabdiff();
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c
index f5e5d30..87512af 100644
--- a/libmount/src/tab_update.c
+++ b/libmount/src/tab_update.c
@@ -35,9 +35,15 @@ struct libmnt_update {
struct libmnt_fs *fs;
char *filename;
unsigned long mountflags;
- int ready;
+
+ int act_fd;
+ char *act_filename;
+
+ unsigned int ready : 1,
+ missing_options : 1;
struct libmnt_table *mountinfo;
+ struct libmnt_lock *lock;
};
static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
@@ -56,6 +62,7 @@ struct libmnt_update *mnt_new_update(void)
if (!upd)
return NULL;
+ upd->act_fd = -1;
DBG(UPDATE, ul_debugobj(upd, "allocate"));
return upd;
}
@@ -73,10 +80,14 @@ void mnt_free_update(struct libmnt_update *upd)
DBG(UPDATE, ul_debugobj(upd, "free"));
+ mnt_unref_lock(upd->lock);
mnt_unref_fs(upd->fs);
mnt_unref_table(upd->mountinfo);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
free(upd->target);
free(upd->filename);
+ free(upd->act_filename);
free(upd);
}
@@ -173,7 +184,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
mnt_unref_fs(upd->fs);
free(upd->target);
- upd->ready = FALSE;
+ upd->ready = 0;
upd->fs = NULL;
upd->target = NULL;
upd->mountflags = 0;
@@ -206,7 +217,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
}
DBG(UPDATE, ul_debugobj(upd, "ready"));
- upd->ready = TRUE;
+ upd->ready = 1;
return 0;
}
@@ -666,43 +677,42 @@ static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
return update_table(upd, tb);
}
-static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_add_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
if (tb)
rc = add_file_entry(tb, upd);
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_remove_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->target);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -714,23 +724,22 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc
rc = update_table(upd, tb);
}
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_modify_target(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
assert(upd);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -779,14 +788,13 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l
}
done:
- if (lc)
- mnt_unlock_file(lc);
-
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
+/* replaces option in the table entry by new options from @udp */
+static int update_modify_options(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
@@ -794,13 +802,13 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename));
fs = upd->fs;
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -819,11 +827,77 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
rc = add_file_entry(tb, upd); /* not found, add new */
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
+ mnt_unref_table(tb);
+ return rc;
+}
+
+/* add user options missing in the table entry by new options from @udp */
+static int update_add_options(struct libmnt_update *upd)
+{
+ struct libmnt_table *tb = NULL;
+ int rc = 0;
+ struct libmnt_fs *fs;
+
+ assert(upd);
+ assert(upd->fs);
+ assert(upd->lock);
+
+ if (!upd->fs->user_optstr)
+ return 0;
+ DBG(UPDATE, ul_debugobj(upd, "%s: add options", upd->filename));
+
+ fs = upd->fs;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
+ if (tb) {
+ struct libmnt_fs *cur = mnt_table_find_target(tb,
+ mnt_fs_get_target(fs),
+ MNT_ITER_BACKWARD);
+ if (cur) {
+ char *u = NULL;
+
+ rc = mnt_optstr_get_missing(cur->user_optstr, upd->fs->user_optstr, &u);
+ if (!rc && u) {
+ DBG(UPDATE, ul_debugobj(upd, " add missing: %s", u));
+ rc = mnt_optstr_append_option(&cur->user_optstr, u, NULL);
+ }
+ if (!rc && u)
+ rc = update_table(upd, tb);
+
+ if (rc == 1) /* nothing is missing */
+ rc = 0;
+ } else
+ rc = add_file_entry(tb, upd); /* not found, add new */
+ }
+
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
+
+}
+
+static int update_init_lock(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ assert(upd);
+
+ if (lc) {
+ mnt_unref_lock(upd->lock);
+ mnt_ref_lock(lc);
+ upd->lock = lc;
+ } else if (!upd->lock) {
+ upd->lock = mnt_new_lock(upd->filename, 0);
+ if (!upd->lock)
+ return -ENOMEM;
+ mnt_lock_block_signals(upd->lock, TRUE);
+ }
+
+ return 0;
}
/**
@@ -842,7 +916,6 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
*/
int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
{
- struct libmnt_lock *lc0 = lc;
int rc = -EINVAL;
if (!upd || !upd->filename)
@@ -854,33 +927,32 @@ int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
if (upd->fs) {
DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
}
- if (!lc) {
- lc = mnt_new_lock(upd->filename, 0);
- if (lc)
- mnt_lock_block_signals(lc, TRUE);
- }
+
+ rc = update_init_lock(upd, lc);
+ if (rc)
+ goto done;
if (!upd->fs && upd->target)
- rc = update_remove_entry(upd, lc); /* umount */
+ rc = update_remove_entry(upd); /* umount */
else if (upd->mountflags & MS_MOVE)
- rc = update_modify_target(upd, lc); /* move */
+ rc = update_modify_target(upd); /* move */
else if (upd->mountflags & MS_REMOUNT)
- rc = update_modify_options(upd, lc); /* remount */
+ rc = update_modify_options(upd); /* remount */
+ else if (upd->fs && upd->missing_options)
+ rc = update_add_options(upd); /* mount by externel helper */
else if (upd->fs)
- rc = update_add_entry(upd, lc); /* mount */
+ rc = update_add_entry(upd); /* mount */
- upd->ready = FALSE;
+ upd->ready = 1;
+done:
DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]",
upd->filename, rc));
- if (lc != lc0)
- mnt_free_lock(lc);
return rc;
}
-int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
+int mnt_update_already_done(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
- struct libmnt_lock *lc0 = lc;
int rc = 0;
if (!upd || !upd->filename || (!upd->fs && !upd->target))
@@ -888,37 +960,32 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename));
- if (!lc) {
- lc = mnt_new_lock(upd->filename, 0);
- if (lc)
- mnt_lock_block_signals(lc, TRUE);
- }
- if (lc) {
- rc = mnt_lock_file(lc);
- if (rc) {
- rc = -MNT_ERR_LOCK;
- goto done;
- }
- }
-
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
- if (lc)
- mnt_unlock_file(lc);
if (!tb)
goto done;
if (upd->fs) {
/* mount */
+ struct libmnt_fs *fs;
const char *tgt = mnt_fs_get_target(upd->fs);
const char *src = mnt_fs_get_bindsrc(upd->fs) ?
mnt_fs_get_bindsrc(upd->fs) :
mnt_fs_get_source(upd->fs);
- if (mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD)) {
+ fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
+ if (fs) {
DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s",
upd->filename, src, tgt));
+
+ /* Check if utab entry (probably writen by /sbin/mount.<type>
+ * helper) contains all options expected by this update */
+ if (mnt_optstr_get_missing(fs->user_optstr, upd->fs->user_optstr, NULL) == 0) {
+ upd->missing_options = 1;
+ DBG(UPDATE, ul_debugobj(upd, " missing options detected"));
+ }
+ } else
rc = 1;
- }
+
} else if (upd->target) {
/* umount */
if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) {
@@ -930,13 +997,133 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
mnt_unref_table(tb);
done:
- if (lc && lc != lc0)
- mnt_free_lock(lc);
DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]",
upd->filename, rc));
return rc;
}
+int mnt_update_emit_event(struct libmnt_update *upd)
+{
+ char *filename;
+ int fd;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (asprintf(&filename, "%s.event", upd->filename) <= 0)
+ return -ENOMEM;
+
+ DBG(UPDATE, ul_debugobj(upd, "emitting utab event"));
+
+ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ free(filename);
+ if (fd < 0)
+ return -errno;
+ close(fd);
+ return 0;
+}
+
+/*
+ * Let's use /run/mount/utab.act file to report to libmount monitor that
+ * libmount is working with utab. In this case, the monitor can ignore all
+ * events from kernel until entire mount (with all steps) is done.
+ *
+ * For example mount NFS with x-* options, means
+ * - create utab.act and mark it as used (by LOCK_SH)
+ * - exec /sbin/mount.nfs
+ * - call mount(2) (kernel event on /proc/self/mounts)
+ * - utab update (NFS stuff)
+ * - utab update (add x-* userspace options)
+ * - unlink utab.act (if not use anyone else)
+ * - release event by /run/mount/utab.event
+ *
+ * Note, this is used only when utab is in the game (x-* options).
+ */
+int mnt_update_start(struct libmnt_update *upd)
+{
+ int rc = 0;
+ mode_t oldmask;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (!upd->act_filename &&
+ asprintf(&upd->act_filename, "%s.act", upd->filename) <= 0)
+ return -ENOMEM;
+
+ /* Use exclusive lock to avoid some other proces will remove the the
+ * file before it's marked as used by LOCK_SH (below) */
+ rc = update_init_lock(upd, NULL);
+ if (rc)
+ return rc;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ DBG(UPDATE, ul_debugobj(upd, "creating act file"));
+
+ oldmask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+ upd->act_fd = open(upd->act_filename, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
+ umask(oldmask);
+
+ if (upd->act_fd < 0) {
+ rc = -errno;
+ goto fail;
+ }
+
+ /* mark the file as used */
+ rc = flock(upd->act_fd, LOCK_SH);
+ if (rc) {
+ rc = -errno;
+ goto fail;
+ }
+
+ mnt_unlock_file(upd->lock);
+ return 0;
+fail:
+ DBG(UPDATE, ul_debugobj(upd, "act file failed [rc=%d]", rc));
+ mnt_unlock_file(upd->lock);
+ unlink(upd->act_filename);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return rc;
+}
+
+int mnt_update_end(struct libmnt_update *upd)
+{
+ int rc;
+
+ if (!upd || upd->act_fd < 0)
+ return -EINVAL;
+
+ DBG(UPDATE, ul_debugobj(upd, "removing act file"));
+
+ /* make sure nobody else will use the file */
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ /* mark the file as unused */
+ flock(upd->act_fd, LOCK_UN);
+ errno = 0;
+
+ /* check if nobody else need the file (if yes, then the file is under LOCK_SH) )*/
+ if (flock(upd->act_fd, LOCK_EX | LOCK_NB) != 0) {
+ if (errno == EWOULDBLOCK)
+ DBG(UPDATE, ul_debugobj(upd, "act file used, no unlink"));
+ } else {
+ DBG(UPDATE, ul_debugobj(upd, "unlinking act file"));
+ unlink(upd->act_filename);
+ }
+
+ mnt_unlock_file(upd->lock);
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return 0;
+}
#ifdef TEST_PROGRAM
@@ -970,7 +1157,8 @@ done:
return rc;
}
-static int test_add(struct libmnt_test *ts, int argc, char *argv[])
+static int test_add(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -987,14 +1175,16 @@ static int test_add(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remove(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
if (argc < 2)
return -1;
return update(argv[1], NULL, 0);
}
-static int test_move(struct libmnt_test *ts, int argc, char *argv[])
+static int test_move(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1010,7 +1200,8 @@ static int test_move(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1025,7 +1216,8 @@ static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_replace(struct libmnt_test *ts, int argc, char *argv[])
+static int test_replace(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
struct libmnt_table *tb = mnt_new_table();
diff --git a/libmount/src/utils.c b/libmount/src/utils.c
index 3817b39..a2f8ea0 100644
--- a/libmount/src/utils.c
+++ b/libmount/src/utils.c
@@ -526,7 +526,7 @@ static int add_filesystem(char ***filesystems, char *name)
if (n == 0 || !((n + 1) % MYCHUNK)) {
size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
- char **x = realloc(*filesystems, items * sizeof(char *));
+ char **x = reallocarray(*filesystems, items, sizeof(char *));
if (!x)
goto err;
@@ -1032,7 +1032,7 @@ int mnt_open_uniq_filename(const char *filename, char **name)
rc = asprintf(&n, "%s.XXXXXX", filename);
if (rc <= 0)
- return -errno;
+ return -ENOMEM;
/* This is for very old glibc and for compatibility with Posix, which says
* nothing about mkstemp() mode. All sane glibc use secure mode (0600).
@@ -1134,7 +1134,7 @@ char *mnt_get_kernel_cmdline_option(const char *name)
int val = 0;
char *p, *res = NULL, *mem = NULL;
char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
- const char *path = _PATH_PROC_CMDLINE;
+ const char *path;
if (!name || !name[0])
return NULL;
@@ -1143,6 +1143,8 @@ char *mnt_get_kernel_cmdline_option(const char *name)
path = getenv("LIBMOUNT_KERNEL_CMDLINE");
if (!path)
path = _PATH_PROC_CMDLINE;
+#else
+ path = _PATH_PROC_CMDLINE;
#endif
f = fopen(path, "r" UL_CLOEXECSTR);
if (!f)
@@ -1287,8 +1289,12 @@ done:
}
#ifdef TEST_PROGRAM
-static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match_fstype(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *type = argv[1];
char *pattern = argv[2];
@@ -1296,8 +1302,12 @@ static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match_options(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1305,8 +1315,12 @@ static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
+static int test_startswith(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1314,8 +1328,12 @@ static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
+static int test_endswith(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1323,8 +1341,12 @@ static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mountpoint(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *path = canonicalize_path(argv[1]),
*mnt = path ? mnt_get_mountpoint(path) : NULL;
@@ -1334,13 +1356,17 @@ static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
+static int test_filesystems(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char **filesystems = NULL;
int rc;
- rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
- if (!rc) {
+ if (argc != 1 && argc != 2)
+ return -1;
+
+ rc = mnt_get_filesystems(&filesystems, argc == 2 ? argv[1] : NULL);
+ if (!rc && filesystems) {
char **p;
for (p = filesystems; *p; p++)
printf("%s\n", *p);
@@ -1349,8 +1375,12 @@ static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
+static int test_chdir(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
int rc;
char *path = canonicalize_path(argv[1]),
*last = NULL;
@@ -1368,8 +1398,12 @@ static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
+static int test_kernel_cmdline(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *name = argv[1];
char *res;
@@ -1387,7 +1421,8 @@ static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
}
-static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
+static int test_guess_root(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int rc;
char *real;
@@ -1413,10 +1448,14 @@ static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mkdir(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int rc;
+ if (argc != 2)
+ return -1;
+
rc = ul_mkdir_p(argv[1], S_IRWXU |
S_IRGRP | S_IXGRP |
S_IROTH | S_IXOTH);
@@ -1425,11 +1464,15 @@ static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
+static int test_statfs_type(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct statfs vfs;
int rc;
+ if (argc != 2)
+ return -1;
+
rc = statfs(argv[1], &vfs);
if (rc)
printf("%s: statfs failed: %m\n", argv[1]);
@@ -1440,8 +1483,12 @@ static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_uid(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
uid_t uid = (uid_t) -1;
int rc;
@@ -1455,8 +1502,12 @@ static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_gid(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
gid_t gid = (gid_t) -1;
int rc;
@@ -1470,8 +1521,12 @@ static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_mode(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
mode_t mod = (mode_t) -1;
int rc;
@@ -1488,8 +1543,12 @@ static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_stat(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_stat(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *path = argv[1];
struct stat st;
int rc;
diff --git a/libmount/src/version.c b/libmount/src/version.c
index 894c20c..b1e3432 100644
--- a/libmount/src/version.c
+++ b/libmount/src/version.c
@@ -121,7 +121,8 @@ int mnt_get_library_features(const char ***features)
}
#ifdef TEST_PROGRAM
-static int test_version(struct libmnt_test *ts, int argc, char *argv[])
+static int test_version(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *ver;
const char **features;
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/tests/expected/mount/special b/libsmartcols/src/filter-parser.stamp
index e69de29..e69de29 100644
--- a/tests/expected/mount/special
+++ 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;
}
diff --git a/libuuid/man/uuid.3 b/libuuid/man/uuid.3
index ce71643..0b6d8bd 100644
--- a/libuuid/man/uuid.3
+++ b/libuuid/man/uuid.3
@@ -2,12 +2,12 @@
.\" Title: uuid
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_clear.3 b/libuuid/man/uuid_clear.3
index ef242ea..0ba899c 100644
--- a/libuuid/man/uuid_clear.3
+++ b/libuuid/man/uuid_clear.3
@@ -2,12 +2,12 @@
.\" Title: uuid_clear
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_CLEAR" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_CLEAR" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_compare.3 b/libuuid/man/uuid_compare.3
index 18ee6e0..2d37b5e 100644
--- a/libuuid/man/uuid_compare.3
+++ b/libuuid/man/uuid_compare.3
@@ -2,12 +2,12 @@
.\" Title: uuid_compare
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_COMPARE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_COMPARE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_copy.3 b/libuuid/man/uuid_copy.3
index ed08bd3..ce2834f 100644
--- a/libuuid/man/uuid_copy.3
+++ b/libuuid/man/uuid_copy.3
@@ -2,12 +2,12 @@
.\" Title: uuid_copy
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_COPY" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_COPY" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_generate.3 b/libuuid/man/uuid_generate.3
index dba2eee..cb88247 100644
--- a/libuuid/man/uuid_generate.3
+++ b/libuuid/man/uuid_generate.3
@@ -2,12 +2,12 @@
.\" Title: uuid_generate
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_GENERATE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_GENERATE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_is_null.3 b/libuuid/man/uuid_is_null.3
index a4cf3cc..dc61071 100644
--- a/libuuid/man/uuid_is_null.3
+++ b/libuuid/man/uuid_is_null.3
@@ -2,12 +2,12 @@
.\" Title: uuid_is_null
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_IS_NULL" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_IS_NULL" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_parse.3 b/libuuid/man/uuid_parse.3
index c70980e..0a58da9 100644
--- a/libuuid/man/uuid_parse.3
+++ b/libuuid/man/uuid_parse.3
@@ -2,12 +2,12 @@
.\" Title: uuid_parse
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_PARSE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_PARSE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_time.3 b/libuuid/man/uuid_time.3
index 475f095..18c9f4f 100644
--- a/libuuid/man/uuid_time.3
+++ b/libuuid/man/uuid_time.3
@@ -2,12 +2,12 @@
.\" Title: uuid_time
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_TIME" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_TIME" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/man/uuid_unparse.3 b/libuuid/man/uuid_unparse.3
index a1f96fd..3b86df4 100644
--- a/libuuid/man/uuid_unparse.3
+++ b/libuuid/man/uuid_unparse.3
@@ -2,12 +2,12 @@
.\" Title: uuid_unparse
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: Programmer's Manual
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UUID_UNPARSE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual"
+.TH "UUID_UNPARSE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/libuuid/src/gen_uuid.c b/libuuid/src/gen_uuid.c
index 826cd22..59e8c23 100644
--- a/libuuid/src/gen_uuid.c
+++ b/libuuid/src/gen_uuid.c
@@ -207,6 +207,28 @@ static int get_node_id(unsigned char *node_id)
return 0;
}
+enum { STATE_FD_ERROR = -1, STATE_FD_INIT = -2 };
+
+static int state_fd_init(const char *clock_file, FILE **fp)
+{
+ mode_t save_umask;
+ int state_fd;
+ FILE *state_f;
+
+ save_umask = umask(0);
+ state_fd = open(clock_file, O_RDWR|O_CREAT|O_CLOEXEC, 0660);
+ (void) umask(save_umask);
+ if (state_fd != -1) {
+ state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR);
+ if (!state_f) {
+ close(state_fd);
+ state_fd = STATE_FD_ERROR;
+ } else
+ *fp = state_f;
+ }
+ return state_fd;
+}
+
/* Assume that the gettimeofday() has microsecond granularity */
#define MAX_ADJUSTMENT 10
/* Reserve a clock_seq value for the 'continuous clock' implementation */
@@ -223,32 +245,16 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
{
THREAD_LOCAL int adjustment = 0;
THREAD_LOCAL struct timeval last = {0, 0};
- THREAD_LOCAL int state_fd = -2;
+ THREAD_LOCAL int state_fd = STATE_FD_INIT;
THREAD_LOCAL FILE *state_f;
THREAD_LOCAL uint16_t clock_seq;
struct timeval tv;
uint64_t clock_reg;
- mode_t save_umask;
int ret = 0;
- if (state_fd == -1)
- ret = -1;
+ if (state_fd == STATE_FD_INIT)
+ state_fd = state_fd_init(LIBUUID_CLOCK_FILE, &state_f);
- if (state_fd == -2) {
- save_umask = umask(0);
- state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0660);
- (void) umask(save_umask);
- if (state_fd != -1) {
- state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR);
- if (!state_f) {
- close(state_fd);
- state_fd = -1;
- ret = -1;
- }
- }
- else
- ret = -1;
- }
if (state_fd >= 0) {
rewind(state_f);
while (flock(state_fd, LOCK_EX) < 0) {
@@ -256,11 +262,13 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
continue;
fclose(state_f);
close(state_fd);
- state_fd = -1;
+ state_fd = STATE_FD_ERROR;
ret = -1;
break;
}
- }
+ } else
+ ret = -1;
+
if (state_fd >= 0) {
unsigned int cl;
unsigned long tv1, tv2;
@@ -355,44 +363,94 @@ static uint64_t get_clock_counter(void)
/*
* Get continuous clock value.
*
- * Return -1 if there is no further clock counter available,
+ * Return -1 if there is no valid clock counter available,
* otherwise return 0.
*
* This implementation doesn't deliver clock counters based on
* the current time because last_clock_reg is only incremented
* by the number of requested UUIDs.
* max_clock_offset is used to limit the offset of last_clock_reg.
+ * used/reserved UUIDs are written to LIBUUID_CLOCK_CONT_FILE.
*/
static int get_clock_cont(uint32_t *clock_high,
uint32_t *clock_low,
int num,
uint32_t max_clock_offset)
{
- /* 100ns based time offset according to RFC 4122. 4.1.4. */
+ /* all 64bit clock_reg values in this function represent '100ns ticks'
+ * due to the combination of tv_usec + MAX_ADJUSTMENT */
+
+ /* time offset according to RFC 4122. 4.1.4. */
const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
static uint64_t last_clock_reg = 0;
- uint64_t clock_reg;
+ static uint64_t saved_clock_reg = 0;
+ static int state_fd = STATE_FD_INIT;
+ static FILE *state_f = NULL;
+ uint64_t clock_reg, next_clock_reg;
- if (last_clock_reg == 0)
- last_clock_reg = get_clock_counter();
+ if (state_fd == STATE_FD_ERROR)
+ return -1;
clock_reg = get_clock_counter();
+
+ if (state_fd == STATE_FD_INIT) {
+ struct stat st;
+
+ state_fd = state_fd_init(LIBUUID_CLOCK_CONT_FILE, &state_f);
+ if (state_fd == STATE_FD_ERROR)
+ return -1;
+
+ if (fstat(state_fd, &st))
+ goto error;
+
+ if (st.st_size) {
+ rewind(state_f);
+ if (fscanf(state_f, "cont: %"SCNu64"\n", &last_clock_reg) != 1)
+ goto error;
+ } else
+ last_clock_reg = clock_reg;
+
+ saved_clock_reg = last_clock_reg;
+ }
+
if (max_clock_offset) {
- uint64_t clock_offset = max_clock_offset * 10000000ULL;
- if (last_clock_reg < (clock_reg - clock_offset))
- last_clock_reg = clock_reg - clock_offset;
+ uint64_t co = 10000000ULL * (uint64_t)max_clock_offset; // clock_offset in [100ns]
+
+ if ((last_clock_reg + co) < clock_reg)
+ last_clock_reg = clock_reg - co;
}
clock_reg += MAX_ADJUSTMENT;
- if ((last_clock_reg + num) >= clock_reg)
+ next_clock_reg = last_clock_reg + (uint64_t)num;
+ if (next_clock_reg >= clock_reg)
return -1;
+ if (next_clock_reg >= saved_clock_reg) {
+ uint64_t cl = next_clock_reg + 100000000ULL; // 10s interval in [100ns]
+ int l;
+
+ rewind(state_f);
+ l = fprintf(state_f, "cont: %020"PRIu64" \n", cl);
+ if (l < 30 || fflush(state_f))
+ goto error;
+ saved_clock_reg = cl;
+ }
+
*clock_high = (last_clock_reg + reg_offset) >> 32;
*clock_low = last_clock_reg + reg_offset;
- last_clock_reg += num;
+ last_clock_reg = next_clock_reg;
return 0;
+
+error:
+ if (state_fd >= 0)
+ close(state_fd);
+ if (state_f)
+ fclose(state_f);
+ state_fd = STATE_FD_ERROR;
+ state_f = NULL;
+ return -1;
}
#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
diff --git a/libuuid/src/libuuid.sym b/libuuid/src/libuuid.sym
index 96372a8..0f67ede 100644
--- a/libuuid/src/libuuid.sym
+++ b/libuuid/src/libuuid.sym
@@ -52,6 +52,15 @@ global:
uuid_parse_range;
} UUID_2.31;
+/*
+ * version(s) since util-linux.2.40
+ */
+UUID_2.40 {
+global:
+ uuid_time64; /* only on 32bit architectures with 64bit time_t */
+} UUID_2.36;
+
+
/*
* __uuid_* this is not part of the official API, this is
diff --git a/libuuid/src/test_uuid.c b/libuuid/src/test_uuid.c
index 9907911..06d1589 100644
--- a/libuuid/src/test_uuid.c
+++ b/libuuid/src/test_uuid.c
@@ -66,24 +66,30 @@ static int test_uuid(const char * uuid, int isValid)
static int check_uuids_in_file(const char *file)
{
- int fd, ret = 0;
- size_t sz;
- char str[UUID_STR_LEN];
+ int ret = 0;
+ size_t alloc = 0;
+ ssize_t sz;
+ char *str = NULL;
+ FILE *f;
uuid_t uuidBits;
- if ((fd = open(file, O_RDONLY)) < 0) {
+ if ((f = fopen(file, "r")) == NULL) {
warn("%s", file);
return 1;
}
- while ((sz = read(fd, str, sizeof(str))) != 0) {
- str[sizeof(str) - 1] = '\0';
+ while ((sz = getline(&str, &alloc, f)) != -1) {
+ if (sz == 0 || str[0] == '\n')
+ continue;
+ if (str[sz - 1] == '\n')
+ str[sz - 1] = '\0';
if (uuid_parse(str, uuidBits)) {
warnx("%s: %s", file, str);
ret++;
}
}
- close(fd);
+ fclose(f);
+ free(str);
return ret;
}
diff --git a/libuuid/src/uuid.h b/libuuid/src/uuid.h
index e791abf..2e3642c 100644
--- a/libuuid/src/uuid.h
+++ b/libuuid/src/uuid.h
@@ -109,6 +109,9 @@ extern void uuid_unparse_lower(const uuid_t uu, char *out);
extern void uuid_unparse_upper(const uuid_t uu, char *out);
/* uuid_time.c */
+#if defined(__USE_TIME_BITS64) && defined(__GLIBC__)
+# define uuid_time uuid_time64
+#endif
extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
extern int uuid_type(const uuid_t uu);
extern int uuid_variant(const uuid_t uu);
diff --git a/libuuid/src/uuidP.h b/libuuid/src/uuidP.h
index 200702c..6face82 100644
--- a/libuuid/src/uuidP.h
+++ b/libuuid/src/uuidP.h
@@ -39,7 +39,8 @@
#include "uuid.h"
-#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt"
+#define LIBUUID_CLOCK_FILE _PATH_LOCALSTATEDIR "/lib/libuuid/clock.txt"
+#define LIBUUID_CLOCK_CONT_FILE _PATH_LOCALSTATEDIR "/lib/libuuid/clock-cont.txt"
/*
* Offset between 15-Oct-1582 and 1-Jan-70
diff --git a/libuuid/src/uuid_time.c b/libuuid/src/uuid_time.c
index 6f07d51..9b415b3 100644
--- a/libuuid/src/uuid_time.c
+++ b/libuuid/src/uuid_time.c
@@ -40,6 +40,7 @@
#define UUID MYUUID
#endif
+#include <errno.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -53,7 +54,15 @@
#include "uuidP.h"
-time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+#undef uuid_time
+
+/* prototype to make compiler happy */
+time_t __uuid_time(const uuid_t uu, struct timeval *ret_tv);
+
+
+/* this function could be 32bit time_t and 32bit timeval or 64bit,
+ depending on compiler flags and architecture. */
+time_t __uuid_time(const uuid_t uu, struct timeval *ret_tv)
{
struct timeval tv;
struct uuid uuid;
@@ -74,6 +83,54 @@ time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
return tv.tv_sec;
}
+#if defined(__USE_TIME_BITS64) && defined(__GLIBC__)
+extern time_t uuid_time64(const uuid_t uu, struct timeval *ret_tv) __attribute__((weak, alias("__uuid_time")));
+#else
+extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) __attribute__((weak, alias("__uuid_time")));
+#endif
+
+#if defined(__USE_TIME_BITS64) && defined(__GLIBC__)
+struct timeval32
+{
+ int32_t tv_sec;
+ int32_t tv_usec;
+};
+
+/* prototype to make compiler happy */
+int32_t __uuid_time32(const uuid_t uu, struct timeval32 *ret_tv);
+
+/* Check whether time fits in 32bit time_t. */
+static inline int
+in_time32_t_range(time_t t)
+{
+ int32_t s;
+
+ s = t;
+
+ return s == t;
+}
+
+int32_t __uuid_time32(const uuid_t uu, struct timeval32 *ret_tv)
+{
+ struct timeval tv;
+ time_t ret_time = __uuid_time(uu, &tv);
+
+ if (! in_time32_t_range(ret_time)) {
+ ret_tv->tv_sec = -1;
+ ret_tv->tv_usec = -1;
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ if (ret_tv) {
+ ret_tv->tv_sec = tv.tv_sec;
+ ret_tv->tv_usec = tv.tv_usec;
+ }
+
+ return tv.tv_sec;
+}
+extern int32_t uuid_time(const uuid_t uu, struct timeval32 *ret_tv) __attribute__((weak, alias("__uuid_time32")));
+#endif
int uuid_type(const uuid_t uu)
{