summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel Bentele2020-09-08 15:07:31 +0200
committerManuel Bentele2020-09-16 07:37:56 +0200
commitefc492d327ea6a9658674eb9e971aff3742818cd (patch)
tree72a3e3e61ee3cbc7df4059ee24ae95487c265b60
parentAdded file format file format subsystem for loop devices (diff)
downloadxloop-efc492d327ea6a9658674eb9e971aff3742818cd.tar.gz
xloop-efc492d327ea6a9658674eb9e971aff3742818cd.tar.xz
xloop-efc492d327ea6a9658674eb9e971aff3742818cd.zip
Added patched losetup utility to configure xloop devices
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt8
-rw-r--r--kernel/CMakeLists.txt4
-rw-r--r--kernel/Kbuild.in (renamed from Kbuild.in)0
-rw-r--r--kernel/Kconfig (renamed from Kconfig)0
-rw-r--r--kernel/Makefile (renamed from Makefile)0
-rw-r--r--kernel/loop_file_fmt.c (renamed from loop_file_fmt.c)0
-rw-r--r--kernel/loop_file_fmt.h (renamed from loop_file_fmt.h)0
-rw-r--r--kernel/loop_file_fmt_qcow_cache.c (renamed from loop_file_fmt_qcow_cache.c)0
-rw-r--r--kernel/loop_file_fmt_qcow_cache.h (renamed from loop_file_fmt_qcow_cache.h)0
-rw-r--r--kernel/loop_file_fmt_qcow_cluster.c (renamed from loop_file_fmt_qcow_cluster.c)0
-rw-r--r--kernel/loop_file_fmt_qcow_cluster.h (renamed from loop_file_fmt_qcow_cluster.h)0
-rw-r--r--kernel/loop_file_fmt_qcow_main.c (renamed from loop_file_fmt_qcow_main.c)0
-rw-r--r--kernel/loop_file_fmt_qcow_main.h (renamed from loop_file_fmt_qcow_main.h)0
-rw-r--r--kernel/loop_file_fmt_raw.c (renamed from loop_file_fmt_raw.c)0
-rw-r--r--kernel/loop_main.c (renamed from loop_main.c)0
-rw-r--r--kernel/loop_main.h (renamed from loop_main.h)0
-rw-r--r--kernel/uapi/linux/loop.h (renamed from uapi/linux/loop.h)0
-rw-r--r--utils/CMakeLists.txt15
-rw-r--r--utils/bash-completion/losetup85
-rw-r--r--utils/config.h897
-rw-r--r--utils/include/all-io.h81
-rw-r--r--utils/include/bitops.h150
-rw-r--r--utils/include/blkdev.h151
-rw-r--r--utils/include/c.h427
-rw-r--r--utils/include/canonicalize.h32
-rw-r--r--utils/include/caputils.h34
-rw-r--r--utils/include/carefulputc.h155
-rw-r--r--utils/include/cctype.h325
-rw-r--r--utils/include/closestream.h110
-rw-r--r--utils/include/color-names.h44
-rw-r--r--utils/include/colors.h73
-rw-r--r--utils/include/cpuset.h99
-rw-r--r--utils/include/crc32.h12
-rw-r--r--utils/include/crc32c.h9
-rw-r--r--utils/include/debug.h181
-rw-r--r--utils/include/debugobj.h22
-rw-r--r--utils/include/encode.h14
-rw-r--r--utils/include/env.h35
-rw-r--r--utils/include/exec_shell.h6
-rw-r--r--utils/include/exitcodes.h25
-rw-r--r--utils/include/fileutils.h77
-rw-r--r--utils/include/fuzz.h9
-rw-r--r--utils/include/idcache.h28
-rw-r--r--utils/include/ismounted.h14
-rw-r--r--utils/include/iso9660.h58
-rw-r--r--utils/include/linux_version.h14
-rw-r--r--utils/include/list.h361
-rw-r--r--utils/include/loopdev.h222
-rw-r--r--utils/include/mangle.h28
-rw-r--r--utils/include/match.h12
-rw-r--r--utils/include/mbsalign.h66
-rw-r--r--utils/include/mbsedit.h32
-rw-r--r--utils/include/md5.h24
-rw-r--r--utils/include/minix.h85
-rw-r--r--utils/include/monotonic.h22
-rw-r--r--utils/include/namespace.h56
-rw-r--r--utils/include/nls.h153
-rw-r--r--utils/include/optutils.h107
-rw-r--r--utils/include/pager.h9
-rw-r--r--utils/include/partx.h63
-rw-r--r--utils/include/path.h135
-rw-r--r--utils/include/pathnames.h210
-rw-r--r--utils/include/pidfd-utils.h28
-rw-r--r--utils/include/plymouth-ctrl.h65
-rw-r--r--utils/include/procutils.h34
-rw-r--r--utils/include/pt-bsd.h156
-rw-r--r--utils/include/pt-gpt-partnames.h160
-rw-r--r--utils/include/pt-mbr-partnames.h112
-rw-r--r--utils/include/pt-mbr.h187
-rw-r--r--utils/include/pt-sgi.h115
-rw-r--r--utils/include/pt-sun.h90
-rw-r--r--utils/include/pty-session.h110
-rw-r--r--utils/include/pwdutils.h14
-rw-r--r--utils/include/randutils.h17
-rw-r--r--utils/include/rpmatch.h13
-rw-r--r--utils/include/setproctitle.h7
-rw-r--r--utils/include/sha1.h27
-rw-r--r--utils/include/signames.h8
-rw-r--r--utils/include/statfs_magic.h100
-rw-r--r--utils/include/strutils.h333
-rw-r--r--utils/include/strv.h55
-rw-r--r--utils/include/swapheader.h23
-rw-r--r--utils/include/swapprober.h9
-rw-r--r--utils/include/sysfs.h113
-rw-r--r--utils/include/timer.h22
-rw-r--r--utils/include/timeutils.h92
-rw-r--r--utils/include/ttyutils.h205
-rw-r--r--utils/include/widechar.h47
-rw-r--r--utils/include/xalloc.h139
-rw-r--r--utils/lib/CMakeLists.txt45
-rw-r--r--utils/lib/blkdev.c452
-rw-r--r--utils/lib/canonicalize.c250
-rw-r--r--utils/lib/caputils.c45
-rw-r--r--utils/lib/color-names.c64
-rw-r--r--utils/lib/colors.c907
-rw-r--r--utils/lib/cpuset.c413
-rw-r--r--utils/lib/crc32.c142
-rw-r--r--utils/lib/crc32c.c102
-rw-r--r--utils/lib/encode.c79
-rw-r--r--utils/lib/env.c238
-rw-r--r--utils/lib/exec_shell.c51
-rw-r--r--utils/lib/fileutils.c246
-rw-r--r--utils/lib/idcache.c117
-rw-r--r--utils/lib/ismounted.c396
-rw-r--r--utils/lib/langinfo.c124
-rw-r--r--utils/lib/linux_version.c71
-rw-r--r--utils/lib/loopdev.c1914
-rw-r--r--utils/lib/mangle.c169
-rw-r--r--utils/lib/match.c53
-rw-r--r--utils/lib/mbsalign.c627
-rw-r--r--utils/lib/mbsedit.c225
-rw-r--r--utils/lib/md5.c257
-rw-r--r--utils/lib/monotonic.c81
-rw-r--r--utils/lib/pager.c317
-rw-r--r--utils/lib/path.c1248
-rw-r--r--utils/lib/plymouth-ctrl.c144
-rw-r--r--utils/lib/procutils.c308
-rw-r--r--utils/lib/pty-session.c725
-rw-r--r--utils/lib/pwdutils.c156
-rw-r--r--utils/lib/randutils.c238
-rw-r--r--utils/lib/setproctitle.c75
-rw-r--r--utils/lib/sha1.c256
-rw-r--r--utils/lib/signames.c204
-rw-r--r--utils/lib/strutils.c1135
-rw-r--r--utils/lib/strv.c403
-rw-r--r--utils/lib/sysfs.c1127
-rw-r--r--utils/lib/timer.c95
-rw-r--r--utils/lib/timeutils.c611
-rw-r--r--utils/lib/ttyutils.c152
-rw-r--r--utils/libsmartcols/CMakeLists.txt22
-rw-r--r--utils/libsmartcols/src/buffer.c152
-rw-r--r--utils/libsmartcols/src/calculate.c454
-rw-r--r--utils/libsmartcols/src/cell.c257
-rw-r--r--utils/libsmartcols/src/column.c564
-rw-r--r--utils/libsmartcols/src/fput.c97
-rw-r--r--utils/libsmartcols/src/grouping.c575
-rw-r--r--utils/libsmartcols/src/init.c62
-rw-r--r--utils/libsmartcols/src/iter.c74
-rw-r--r--utils/libsmartcols/src/libsmartcols.h336
-rw-r--r--utils/libsmartcols/src/line.c540
-rw-r--r--utils/libsmartcols/src/print-api.c211
-rw-r--r--utils/libsmartcols/src/print.c1089
-rw-r--r--utils/libsmartcols/src/smartcolsP.h468
-rw-r--r--utils/libsmartcols/src/symbols.c293
-rw-r--r--utils/libsmartcols/src/table.c1691
-rw-r--r--utils/libsmartcols/src/version.c62
-rw-r--r--utils/libsmartcols/src/walk.c152
-rw-r--r--utils/sys-utils/CMakeLists.txt9
-rw-r--r--utils/sys-utils/losetup.8215
-rw-r--r--utils/sys-utils/losetup.c955
151 files changed, 29601 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index b2d1740..4dafafe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ modules.order
*.o.cmd
*.mod*
*.ko.cmd
+build
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..b136eca
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop)
+
+# add subprojects
+add_subdirectory(kernel)
+add_subdirectory(utils)
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
new file mode 100644
index 0000000..3c91af4
--- /dev/null
+++ b/kernel/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop-kernel)
diff --git a/Kbuild.in b/kernel/Kbuild.in
index b61f7a0..b61f7a0 100644
--- a/Kbuild.in
+++ b/kernel/Kbuild.in
diff --git a/Kconfig b/kernel/Kconfig
index 2fe8cb5..2fe8cb5 100644
--- a/Kconfig
+++ b/kernel/Kconfig
diff --git a/Makefile b/kernel/Makefile
index b7f2191..b7f2191 100644
--- a/Makefile
+++ b/kernel/Makefile
diff --git a/loop_file_fmt.c b/kernel/loop_file_fmt.c
index 062ea0d..062ea0d 100644
--- a/loop_file_fmt.c
+++ b/kernel/loop_file_fmt.c
diff --git a/loop_file_fmt.h b/kernel/loop_file_fmt.h
index 38d6a3b..38d6a3b 100644
--- a/loop_file_fmt.h
+++ b/kernel/loop_file_fmt.h
diff --git a/loop_file_fmt_qcow_cache.c b/kernel/loop_file_fmt_qcow_cache.c
index 4ef772a..4ef772a 100644
--- a/loop_file_fmt_qcow_cache.c
+++ b/kernel/loop_file_fmt_qcow_cache.c
diff --git a/loop_file_fmt_qcow_cache.h b/kernel/loop_file_fmt_qcow_cache.h
index d2f1010..d2f1010 100644
--- a/loop_file_fmt_qcow_cache.h
+++ b/kernel/loop_file_fmt_qcow_cache.h
diff --git a/loop_file_fmt_qcow_cluster.c b/kernel/loop_file_fmt_qcow_cluster.c
index 593a173..593a173 100644
--- a/loop_file_fmt_qcow_cluster.c
+++ b/kernel/loop_file_fmt_qcow_cluster.c
diff --git a/loop_file_fmt_qcow_cluster.h b/kernel/loop_file_fmt_qcow_cluster.h
index 5078f29..5078f29 100644
--- a/loop_file_fmt_qcow_cluster.h
+++ b/kernel/loop_file_fmt_qcow_cluster.h
diff --git a/loop_file_fmt_qcow_main.c b/kernel/loop_file_fmt_qcow_main.c
index 7c3e360..7c3e360 100644
--- a/loop_file_fmt_qcow_main.c
+++ b/kernel/loop_file_fmt_qcow_main.c
diff --git a/loop_file_fmt_qcow_main.h b/kernel/loop_file_fmt_qcow_main.h
index 54b94c3..54b94c3 100644
--- a/loop_file_fmt_qcow_main.h
+++ b/kernel/loop_file_fmt_qcow_main.h
diff --git a/loop_file_fmt_raw.c b/kernel/loop_file_fmt_raw.c
index 11cc8cd..11cc8cd 100644
--- a/loop_file_fmt_raw.c
+++ b/kernel/loop_file_fmt_raw.c
diff --git a/loop_main.c b/kernel/loop_main.c
index 9cd9c99..9cd9c99 100644
--- a/loop_main.c
+++ b/kernel/loop_main.c
diff --git a/loop_main.h b/kernel/loop_main.h
index 1b5851a..1b5851a 100644
--- a/loop_main.h
+++ b/kernel/loop_main.h
diff --git a/uapi/linux/loop.h b/kernel/uapi/linux/loop.h
index f93f6ad..f93f6ad 100644
--- a/uapi/linux/loop.h
+++ b/kernel/uapi/linux/loop.h
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 0000000..d1e359d
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop-utils)
+
+# include global headers
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+# add include of config.h to each source file
+add_compile_options(-include ${CMAKE_CURRENT_SOURCE_DIR}/config.h)
+
+add_subdirectory(lib)
+add_subdirectory(libsmartcols)
+add_subdirectory(sys-utils)
diff --git a/utils/bash-completion/losetup b/utils/bash-completion/losetup
new file mode 100644
index 0000000..e085abe
--- /dev/null
+++ b/utils/bash-completion/losetup
@@ -0,0 +1,85 @@
+_losetup_module()
+{
+ local cur prev OPTS ARG
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case $prev in
+ '-d'|'--detach')
+ ARG="$($1 --output NAME | awk '{if (1 < NR) {print}}')"
+ COMPREPLY=( $(compgen -W "$ARG" -- $cur) )
+ return 0
+ ;;
+ '-j'|'--associated')
+ ARG="$($1 --output BACK-FILE | awk '{if (1 < NR) {print}}')"
+ COMPREPLY=( $(compgen -W "$ARG" -- $cur) )
+ return 0
+ ;;
+ '-c'|'--set-capacity')
+ ARG="$(for I in /dev/loop[0-9]*; do if [ -e $I ]; then echo $I; fi; done)"
+ COMPREPLY=( $(compgen -W "$ARG" -- $cur) )
+ return 0
+ ;;
+ '-o'|'--offset'|'--sizelimit')
+ COMPREPLY=( $(compgen -W "number" -- $cur) )
+ return 0
+ ;;
+ '-t'|'--type')
+ ARG="RAW QCOW VDI VMDK"
+ COMPREPLY=( $(compgen -W "$ARG" -- $cur) )
+ return 0
+ ;;
+ '-O'|'--output')
+ local prefix realcur OUTPUT_ALL OUTPUT
+ realcur="${cur##*,}"
+ prefix="${cur%$realcur}"
+ OUTPUT_ALL="NAME AUTOCLEAR BACK-FILE BACK-INO
+ BACK-MAJ:MIN FILE-FORMAT MAJ:MIN OFFSET PARTSCAN RO
+ SIZELIMIT DIO"
+ for WORD in $OUTPUT_ALL; do
+ if ! [[ $prefix == *"$WORD"* ]]; then
+ OUTPUT="$WORD ${OUTPUT:-""}"
+ fi
+ done
+ compopt -o nospace
+ COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) )
+ return 0
+ ;;
+ '-h'|'--help'|'-V'|'--version')
+ return 0
+ ;;
+ esac
+ case $cur in
+ -*)
+ OPTS="--all
+ --detach
+ --detach-all
+ --find
+ --set-capacity
+ --associated
+ --nooverlap
+ --offset
+ --sizelimit
+ --partscan
+ --read-only
+ --show
+ --type
+ --verbose
+ --json
+ --list
+ --noheadings
+ --output
+ --output-all
+ --raw
+ --help
+ --version"
+ COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
+ return 0
+ ;;
+ esac
+ local IFS=$'\n'
+ compopt -o filenames
+ COMPREPLY=( $(compgen -f -- $cur) )
+ return 0
+}
+complete -F _losetup_module losetup
diff --git a/utils/config.h b/utils/config.h
new file mode 100644
index 0000000..f74c162
--- /dev/null
+++ b/utils/config.h
@@ -0,0 +1,897 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Enable agetty --reload feature */
+#define AGETTY_RELOAD 1
+
+/* Should chfn and chsh require the user to enter the password? */
+#define CHFN_CHSH_PASSWORD 1
+
+/* Path to hwclock adjtime file */
+#define CONFIG_ADJTIME_PATH "/etc/adjtime"
+
+/* Define if cryptsetup is to be loaded via dlopen */
+/* #undef CRYPTSETUP_VIA_DLOPEN */
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#define ENABLE_NLS 1
+
+/* search path for fs helpers */
+#define FS_SEARCH_PATH "/sbin:/sbin/fs.d:/sbin/fs"
+
+/* Define to 1 if you have the <asm/io.h> header file. */
+/* #undef HAVE_ASM_IO_H */
+
+/* Define if btrfs stuff is available */
+#define HAVE_BTRFS_SUPPORT 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define HAVE_BYTESWAP_H 1
+
+/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+/* #undef HAVE_CFLOCALECOPYCURRENT */
+
+/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
+
+/* Define to 1 if you have the `clearenv' function. */
+#define HAVE_CLEARENV 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if the system has the type `cpu_set_t'. */
+#define HAVE_CPU_SET_T 1
+
+/* Define if cryptsetup is available */
+/* #undef HAVE_CRYPTSETUP */
+
+/* Define if crypt_activate_by_signed_key exist in -lcryptsetup */
+/* #undef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY */
+
+/* Define to 1 if you have the <crypt.h> header file. */
+#define HAVE_CRYPT_H 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the declaration of `BLK_ZONE_REP_CAPACITY', and to
+ 0 if you don't. */
+#define HAVE_DECL_BLK_ZONE_REP_CAPACITY 0
+
+/* Define to 1 if you have the declaration of `CPU_ALLOC', and to 0 if you
+ don't. */
+#define HAVE_DECL_CPU_ALLOC 1
+
+/* Define to 1 if you have the declaration of `dirfd', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_DIRFD */
+
+/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_TZNAME */
+
+/* Define to 1 if you have the declaration of `_NL_TIME_WEEK_1STDAY', and to 0
+ if you don't. */
+#define HAVE_DECL__NL_TIME_WEEK_1STDAY 1
+
+/* Define to 1 if you have the `dirfd' function. */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if `dd_fd' is a member of `DIR'. */
+/* #undef HAVE_DIR_DD_FD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `eaccess' function. */
+#define HAVE_EACCESS 1
+
+/* Define to 1 if you have the <endian.h> header file. */
+#define HAVE_ENDIAN_H 1
+
+/* Define to 1 if have **environ prototype */
+#define HAVE_ENVIRON_DECL 1
+
+/* Define to 1 if you have the `err' function. */
+#define HAVE_ERR 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `errx' function. */
+#define HAVE_ERRX 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the `explicit_bzero' function. */
+#define HAVE_EXPLICIT_BZERO 1
+
+/* Have valid fallocate() function */
+#define HAVE_FALLOCATE 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fpurge' function. */
+/* #undef HAVE_FPURGE */
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fsync' function. */
+#define HAVE_FSYNC 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `getdomainname' function. */
+#define HAVE_GETDOMAINNAME 1
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define to 1 if you have the `getexecname' function. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define to 1 if you have the `getmntinfo' function. */
+/* #undef HAVE_GETMNTINFO */
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getrandom' function. */
+#define HAVE_GETRANDOM 1
+
+/* Define to 1 if you have the `getrlimit' function. */
+#define HAVE_GETRLIMIT 1
+
+/* Define to 1 if you have the `getsgnam' function. */
+#define HAVE_GETSGNAM 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define to 1 if you have the `getusershell' function. */
+#define HAVE_GETUSERSHELL 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the `inotify_init' function. */
+#define HAVE_INOTIFY_INIT 1
+
+/* Define to 1 if you have the `inotify_init1' function. */
+#define HAVE_INOTIFY_INIT1 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `ioperm' function. */
+#define HAVE_IOPERM 1
+
+/* Define to 1 if you have the `iopl' function. */
+#define HAVE_IOPL 1
+
+/* Define to 1 if you have the `isnan' function. */
+#define HAVE_ISNAN 1
+
+/* Define to 1 if you have the `jrand48' function. */
+#define HAVE_JRAND48 1
+
+/* Define if langinfo.h defines ALTMON_x constants */
+#define HAVE_LANGINFO_ALTMON 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define if langinfo.h defines _NL_ABALTMON_x constants */
+#define HAVE_LANGINFO_NL_ABALTMON 1
+
+/* Define to 1 if you have the <lastlog.h> header file. */
+#define HAVE_LASTLOG_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `audit' library (-laudit). */
+/* #undef HAVE_LIBAUDIT */
+
+/* Define to 1 if you have the -lblkid. */
+#define HAVE_LIBBLKID 1
+
+/* Define to 1 if you have the `cap-ng' library (-lcap-ng). */
+#define HAVE_LIBCAP_NG 1
+
+/* Do we need -lcrypt? */
+#define HAVE_LIBCRYPT 1
+
+/* Define if libeconf is available */
+/* #undef HAVE_LIBECONF */
+
+/* Define if libmount available. */
+#define HAVE_LIBMOUNT 1
+
+/* Define if ncurses library available */
+/* #undef HAVE_LIBNCURSES */
+
+/* Define if ncursesw library available */
+#define HAVE_LIBNCURSESW 1
+
+/* Define to 1 if you have the `readline' library (-lreadline). */
+#define HAVE_LIBREADLINE 1
+
+/* Define if librtas exists */
+/* #undef HAVE_LIBRTAS */
+
+/* Define if SELinux is available */
+/* #undef HAVE_LIBSELINUX */
+
+/* Define if libsystemd is available */
+#define HAVE_LIBSYSTEMD 1
+
+/* Define if libtinfo or libtinfow available. */
+#define HAVE_LIBTINFO 1
+
+/* Define to 1 if you have the `udev' library (-ludev). */
+#define HAVE_LIBUDEV 1
+
+/* Define if libuser is available */
+/* #undef HAVE_LIBUSER */
+
+/* Define to 1 if you have the `utempter' library (-lutempter). */
+/* #undef HAVE_LIBUTEMPTER */
+
+/* Define to 1 if you have the `util' library (-lutil). */
+#define HAVE_LIBUTIL 1
+
+/* Define to 1 if you have the <libutil.h> header file. */
+/* #undef HAVE_LIBUTIL_H */
+
+/* Define to 1 if you have the -luuid. */
+#define HAVE_LIBUUID 1
+
+/* Define to 1 if you have the <linux/blkpg.h> header file. */
+#define HAVE_LINUX_BLKPG_H 1
+
+/* Define to 1 if you have the <linux/blkzoned.h> header file. */
+#define HAVE_LINUX_BLKZONED_H 1
+
+/* Define to 1 if you have the <linux/btrfs.h> header file. */
+#define HAVE_LINUX_BTRFS_H 1
+
+/* Define to 1 if you have the <linux/capability.h> header file. */
+#define HAVE_LINUX_CAPABILITY_H 1
+
+/* Define to 1 if you have the <linux/cdrom.h> header file. */
+#define HAVE_LINUX_CDROM_H 1
+
+/* Define to 1 if you have the <linux/compiler.h> header file. */
+/* #undef HAVE_LINUX_COMPILER_H */
+
+/* Define to 1 if you have the <linux/falloc.h> header file. */
+#define HAVE_LINUX_FALLOC_H 1
+
+/* Define to 1 if you have the <linux/fd.h> header file. */
+#define HAVE_LINUX_FD_H 1
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/gsmmux.h> header file. */
+#define HAVE_LINUX_GSMMUX_H 1
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+#define HAVE_LINUX_MAJOR_H 1
+
+/* Define to 1 if you have the <linux/net_namespace.h> header file. */
+#define HAVE_LINUX_NET_NAMESPACE_H 1
+
+/* Define to 1 if you have the <linux/raw.h> header file. */
+#define HAVE_LINUX_RAW_H 1
+
+/* Define to 1 if you have the <linux/securebits.h> header file. */
+#define HAVE_LINUX_SECUREBITS_H 1
+
+/* Define to 1 if you have the <linux/tiocl.h> header file. */
+#define HAVE_LINUX_TIOCL_H 1
+
+/* Define to 1 if you have the <linux/version.h> header file. */
+#define HAVE_LINUX_VERSION_H 1
+
+/* Define to 1 if you have the <linux/watchdog.h> header file. */
+#define HAVE_LINUX_WATCHDOG_H 1
+
+/* Define to 1 if you have the `llseek' function. */
+/* #undef HAVE_LLSEEK */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if the system has the type `loff_t'. */
+#define HAVE_LOFF_T 1
+
+/* Define to 1 if you have the libmagic present. */
+#define HAVE_MAGIC 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have the `mkostemp' function. */
+#define HAVE_MKOSTEMP 1
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#define HAVE_MNTENT_H 1
+
+/* Define to 1 if you have the `nanosleep' function. */
+#define HAVE_NANOSLEEP 1
+
+/* Define to 1 if you have the <ncursesw/ncurses.h> header file. */
+/* #undef HAVE_NCURSESW_NCURSES_H */
+
+/* Define to 1 if you have the <ncursesw/term.h> header file. */
+/* #undef HAVE_NCURSESW_TERM_H */
+
+/* Define to 1 if you have the <ncurses.h> header file. */
+#define HAVE_NCURSES_H 1
+
+/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
+/* #undef HAVE_NCURSES_NCURSES_H */
+
+/* Define to 1 if you have the <ncurses/term.h> header file. */
+/* #undef HAVE_NCURSES_TERM_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+/* #undef HAVE_NET_IF_DL_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the `ntp_gettime' function. */
+#define HAVE_NTP_GETTIME 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the `open_memstream' function. */
+#define HAVE_OPEN_MEMSTREAM 1
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define if libpcre2 is available */
+#define HAVE_PCRE 1
+
+/* Define to 1 if you have the `personality' function. */
+#define HAVE_PERSONALITY 1
+
+/* Define to 1 if you have the `pidfd_open' function. */
+/* #undef HAVE_PIDFD_OPEN */
+
+/* Define to 1 if you have the `pidfd_send_signal' function. */
+/* #undef HAVE_PIDFD_SEND_SIGNAL */
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#define HAVE_POSIX_FADVISE 1
+
+/* Have valid posix_fallocate() function */
+#define HAVE_POSIX_FALLOCATE 1
+
+/* Define to 1 if you have the `prctl' function. */
+#define HAVE_PRCTL 1
+
+/* Define to 1 if you have the `prlimit' function. */
+#define HAVE_PRLIMIT 1
+
+/* Define if program_invocation_short_name is defined */
+#define HAVE_PROGRAM_INVOCATION_SHORT_NAME 1
+
+/* have PTY support */
+#define HAVE_PTY 1
+
+/* Define to 1 if you have the <pty.h> header file. */
+#define HAVE_PTY_H 1
+
+/* Define to 1 if you have the `qsort_r' function. */
+#define HAVE_QSORT_R 1
+
+/* Define to 1 if you have the `reboot' function. */
+#define HAVE_REBOOT 1
+
+/* Define if curses library has the resizeterm(). */
+#define HAVE_RESIZETERM 1
+
+/* Define to 1 if you have the `rpmatch' function. */
+#define HAVE_RPMATCH 1
+
+/* Define if struct sockaddr contains sa_len */
+/* #undef HAVE_SA_LEN */
+
+/* Define to 1 if you have the `scandirat' function. */
+#define HAVE_SCANDIRAT 1
+
+/* Define to 1 if you have the `sched_setattr' function. */
+/* #undef HAVE_SCHED_SETATTR */
+
+/* Define to 1 if you have the `sched_setscheduler' function. */
+#define HAVE_SCHED_SETSCHEDULER 1
+
+/* Define to 1 if you have the `secure_getenv' function. */
+#define HAVE_SECURE_GETENV 1
+
+/* Define to 1 if you have the `security_get_initial_context' function. */
+/* #undef HAVE_SECURITY_GET_INITIAL_CONTEXT */
+
+/* Define to 1 if you have the <security/openpam.h> header file. */
+/* #undef HAVE_SECURITY_OPENPAM_H */
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+#define HAVE_SECURITY_PAM_APPL_H 1
+
+/* Define to 1 if you have the <security/pam_misc.h> header file. */
+#define HAVE_SECURITY_PAM_MISC_H 1
+
+/* Define to 1 if you have the `setitimer' function. */
+/* #undef HAVE_SETITIMER */
+
+/* Define to 1 if you have the `setns' function. */
+#define HAVE_SETNS 1
+
+/* Define to 1 if you have the `setprogname' function. */
+/* #undef HAVE_SETPROGNAME */
+
+/* Define to 1 if you have the `setresgid' function. */
+#define HAVE_SETRESGID 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if you have the <shadow.h> header file. */
+#define HAVE_SHADOW_H 1
+
+/* Define to 1 if the system has the type `sighandler_t'. */
+#define HAVE_SIGHANDLER_T 1
+
+/* Define to 1 if you have the `sigqueue' function. */
+#define HAVE_SIGQUEUE 1
+
+/* Define to 1 if you have the <slang.h> header file. */
+/* #undef HAVE_SLANG_H */
+
+/* Define to 1 if you have the <slang/slang.h> header file. */
+/* #undef HAVE_SLANG_SLANG_H */
+
+/* Define to 1 if you have the <slang/slcurses.h> header file. */
+/* #undef HAVE_SLANG_SLCURSES_H */
+
+/* Define to 1 if you have the <slcurses.h> header file. */
+/* #undef HAVE_SLCURSES_H */
+
+/* Add SMACK support */
+/* #undef HAVE_SMACK */
+
+/* Define to 1 if you have the `srandom' function. */
+#define HAVE_SRANDOM 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio_ext.h> header file. */
+#define HAVE_STDIO_EXT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strnchr' function. */
+/* #undef HAVE_STRNCHR */
+
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if have strsignal function prototype */
+#define HAVE_STRSIGNAL_DECL 1
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `c_line' is a member of `struct termios'. */
+#define HAVE_STRUCT_TERMIOS_C_LINE 1
+
+/* Define to 1 if `tm_zone' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_ZONE 1
+
+/* Define to 1 if you have the `swapoff' function. */
+#define HAVE_SWAPOFF 1
+
+/* Define to 1 if you have the `swapon' function. */
+#define HAVE_SWAPON 1
+
+/* Define to 1 if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define to 1 if you have the `sysinfo' function. */
+#define HAVE_SYSINFO 1
+
+/* Define to 1 if you have the <sys/disklabel.h> header file. */
+/* #undef HAVE_SYS_DISKLABEL_H */
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+/* #undef HAVE_SYS_DISK_H */
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/ioccom.h> header file. */
+/* #undef HAVE_SYS_IOCCOM_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+#define HAVE_SYS_IO_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+#define HAVE_SYS_PRCTL_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/signalfd.h> header file. */
+#define HAVE_SYS_SIGNALFD_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/swap.h> header file. */
+#define HAVE_SYS_SWAP_H 1
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/timex.h> header file. */
+#define HAVE_SYS_TIMEX_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/ttydefaults.h> header file. */
+#define HAVE_SYS_TTYDEFAULTS_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/ucred.h> header file. */
+/* #undef HAVE_SYS_UCRED_H */
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if you have the <term.h> header file. */
+#define HAVE_TERM_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define if timer_create exist in -lrt -lpthread */
+#define HAVE_TIMER_CREATE 1
+
+/* Define to 1 if the target supports thread-local storage. */
+#define HAVE_TLS 1
+
+/* Does struct tm have a field tm_gmtoff? */
+#define HAVE_TM_GMTOFF 1
+
+/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use
+ `HAVE_STRUCT_TM_TM_ZONE' instead. */
+#define HAVE_TM_ZONE 1
+
+/* Define to 1 if you don't have `tm_zone' but do have the external array
+ `tzname'. */
+/* #undef HAVE_TZNAME */
+
+/* Define to 1 if the system has the type `union semun'. */
+/* #undef HAVE_UNION_SEMUN */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unshare' function. */
+#define HAVE_UNSHARE 1
+
+/* Define to 1 if you have the `updwtmpx' function. */
+#define HAVE_UPDWTMPX 1
+
+/* Define if curses library has the use_default_colors(). */
+#define HAVE_USE_DEFAULT_COLORS 1
+
+/* Define to 1 if you have the `usleep' function. */
+#define HAVE_USLEEP 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the <utmpx.h> header file. */
+#define HAVE_UTMPX_H 1
+
+/* Define to 1 if you have the <utmp.h> header file. */
+#define HAVE_UTMP_H 1
+
+/* Define to 1 if you want to use uuid daemon. */
+#define HAVE_UUIDD 1
+
+/* Define to 1 if you have the `vwarnx' function. */
+#define HAVE_VWARNX 1
+
+/* Define to 1 if you have the `warn' function. */
+#define HAVE_WARN 1
+
+/* Define to 1 if you have the `warnx' function. */
+#define HAVE_WARNX 1
+
+/* Do we have wide character support? */
+#define HAVE_WIDECHAR 1
+
+/* Define to 1 if you have the `__fpending' function. */
+#define HAVE___FPENDING 1
+
+/* Define to 1 if you have the `__fpurge' function. */
+#define HAVE___FPURGE 1
+
+/* Define if __progname is defined */
+#define HAVE___PROGNAME 1
+
+/* Define to 1 if you have the `__secure_getenv' function. */
+/* #undef HAVE___SECURE_GETENV */
+
+/* libblkid date string */
+#define LIBBLKID_DATE "23-Jul-2020"
+
+/* libblkid version string */
+#define LIBBLKID_VERSION "2.36.97"
+
+/* libfdisk version string */
+#define LIBFDISK_VERSION "2.36.97"
+
+/* libmount version string */
+#define LIBMOUNT_VERSION "2.36.97"
+
+/* libsmartcols version string */
+#define LIBSMARTCOLS_VERSION "2.36.97"
+
+/* Should login chown /dev/vcsN? */
+/* #undef LOGIN_CHOWN_VCS */
+
+/* Should login stat() the mailbox? */
+/* #undef LOGIN_STAT_MAIL */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* "Multi-arch triplet for whereis library search path" */
+/* #undef MULTIARCHTRIPLET */
+
+/* Define to 1 if assertions should be disabled. */
+/* #undef NDEBUG */
+
+/* Should chsh allow only shells in /etc/shells? */
+#define ONLY_LISTED_SHELLS 1
+
+/* Name of package */
+#define PACKAGE "util-linux"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "kzak@redhat.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "util-linux"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "util-linux 2.36.97-87ac5-dirty"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "util-linux"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "http://www.kernel.org/pub/linux/utils/util-linux/"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.36.97-87ac5"
+
+/* Should pg ring the bell on invalid keys? */
+#define PG_BELL 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Fallback syscall number for fallocate */
+/* #undef SYS_fallocate */
+
+/* Fallback syscall number for ioprio_get */
+/* #undef SYS_ioprio_get */
+
+/* Fallback syscall number for ioprio_set */
+/* #undef SYS_ioprio_set */
+
+/* Fallback syscall number for pidfd_open */
+/* #undef SYS_pidfd_open */
+
+/* Fallback syscall number for pidfd_send_signal */
+/* #undef SYS_pidfd_send_signal */
+
+/* Fallback syscall number for pivot_root */
+/* #undef SYS_pivot_root */
+
+/* Fallback syscall number for prlimit64 */
+/* #undef SYS_prlimit64 */
+
+/* Fallback syscall number for sched_getaffinity */
+/* #undef SYS_sched_getaffinity */
+
+/* Fallback syscall number for sched_setattr */
+/* #undef SYS_sched_setattr */
+
+/* Fallback syscall number for setns */
+/* #undef SYS_setns */
+
+/* Fallback syscall number for swapoff */
+/* #undef SYS_swapoff */
+
+/* Fallback syscall number for swapon */
+/* #undef SYS_swapon */
+
+/* Fallback syscall number for unshare */
+/* #undef SYS_unshare */
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Enables colorized output from utils by default */
+#define USE_COLORS_BY_DEFAULT 1
+
+/* Define to 1 if want to use CMOS clock. */
+#define USE_HWCLOCK_CMOS 1
+
+/* use datetime parsing GPLv3 code to hwclock */
+#define USE_HWCLOCK_GPLv3_DATETIME 1
+
+/* Define to 1 if want to support mtab. */
+/* #undef USE_LIBMOUNT_SUPPORT_MTAB */
+
+/* Define to 1 if want to support namepaces. */
+#define USE_LIBMOUNT_SUPPORT_NAMESPACES 1
+
+/* Enable plymouth support feature for sulogin and aggety */
+#define USE_PLYMOUTH_SUPPORT 1
+
+/* Should sulogin use a emergency mount of /dev and /proc? */
+/* #undef USE_SULOGIN_EMERGENCY_MOUNT */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Should wall and write be installed setgid tty? */
+#define USE_TTY_GROUP 1
+
+/* Define to 1 to remove /bin and /sbin from PATH env.variable */
+/* #undef USE_USRDIR_PATHS_ONLY */
+
+/* Define to 1 to use vendordir */
+/* #undef USE_VENDORDIR */
+
+/* Version number of package */
+#define VERSION "2.36.97-87ac5"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Enable MAP_ANON in sys/mman.h on Mac OS X */
+/* #undef _DARWIN_C_SOURCE */
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
diff --git a/utils/include/all-io.h b/utils/include/all-io.h
new file mode 100644
index 0000000..8ffa9cf
--- /dev/null
+++ b/utils/include/all-io.h
@@ -0,0 +1,81 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ * Petr Uzel <petr.uzel@suse.cz>
+ */
+
+#ifndef UTIL_LINUX_ALL_IO_H
+#define UTIL_LINUX_ALL_IO_H
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "c.h"
+
+static inline int write_all(int fd, const void *buf, size_t count)
+{
+ while (count) {
+ ssize_t tmp;
+
+ errno = 0;
+ tmp = write(fd, buf, count);
+ if (tmp > 0) {
+ count -= tmp;
+ if (count)
+ buf = (const void *) ((const char *) buf + tmp);
+ } else if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ if (errno == EAGAIN) /* Try later, *sigh* */
+ xusleep(250000);
+ }
+ return 0;
+}
+
+static inline int fwrite_all(const void *ptr, size_t size,
+ size_t nmemb, FILE *stream)
+{
+ while (nmemb) {
+ size_t tmp;
+
+ errno = 0;
+ tmp = fwrite(ptr, size, nmemb, stream);
+ if (tmp > 0) {
+ nmemb -= tmp;
+ if (nmemb)
+ ptr = (const void *) ((const char *) ptr + (tmp * size));
+ } else if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ if (errno == EAGAIN) /* Try later, *sigh* */
+ xusleep(250000);
+ }
+ return 0;
+}
+
+static inline ssize_t read_all(int fd, char *buf, size_t count)
+{
+ ssize_t ret;
+ ssize_t c = 0;
+ int tries = 0;
+
+ memset(buf, 0, count);
+ while (count > 0) {
+ ret = read(fd, buf, count);
+ if (ret <= 0) {
+ if (ret < 0 && (errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
+ xusleep(250000);
+ continue;
+ }
+ return c ? c : -1;
+ }
+ tries = 0;
+ count -= ret;
+ buf += ret;
+ c += ret;
+ }
+ return c;
+}
+
+#endif /* UTIL_LINUX_ALL_IO_H */
diff --git a/utils/include/bitops.h b/utils/include/bitops.h
new file mode 100644
index 0000000..287d4af
--- /dev/null
+++ b/utils/include/bitops.h
@@ -0,0 +1,150 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#include <stdint.h>
+#include <sys/param.h>
+
+#if defined(HAVE_BYTESWAP_H)
+# include <byteswap.h>
+#endif
+
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+#elif defined(HAVE_SYS_ENDIAN_H) /* BSDs have them here */
+# include <sys/endian.h>
+#endif
+
+#if defined(__OpenBSD__)
+# include <sys/types.h>
+# define be16toh(x) betoh16(x)
+# define be32toh(x) betoh32(x)
+# define be64toh(x) betoh64(x)
+#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+# define bswap_16(x) bswap16(x)
+# define bswap_32(x) bswap32(x)
+# define bswap_64(x) bswap64(x)
+#elif defined(__APPLE__)
+# include <libkern/OSByteOrder.h>
+# define htobe16(x) OSSwapHostToBigInt16(x)
+# define htole16(x) OSSwapHostToLittleInt16(x)
+# define be16toh(x) OSSwapBigToHostInt16(x)
+# define le16toh(x) OSSwapLittleToHostInt16(x)
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define htole32(x) OSSwapHostToLittleInt32(x)
+# define be32toh(x) OSSwapBigToHostInt32(x)
+# define le32toh(x) OSSwapLittleToHostInt32(x)
+# define htobe64(x) OSSwapHostToBigInt64(x)
+# define htole64(x) OSSwapHostToLittleInt64(x)
+# define be64toh(x) OSSwapBigToHostInt64(x)
+# define le64toh(x) OSSwapLittleToHostInt64(x)
+# define bswap_16(x) OSSwapInt16(x)
+# define bswap_32(x) OSSwapInt32(x)
+# define bswap_64(x) OSSwapInt64(x)
+#endif
+
+/*
+ * Fallbacks
+ * casts are necessary for constants, because we never know how for sure
+ * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way.
+ */
+#ifndef bswap_16
+# define bswap_16(x) ((uint16_t)( \
+ (((uint16_t)(x) & 0x00FF) << 8) | \
+ (((uint16_t)(x) & 0xFF00) >> 8)))
+#endif
+
+#ifndef bswap_32
+# define bswap_32(x) ((uint32_t)( \
+ (((uint32_t)(x) & 0x000000FF) << 24) | \
+ (((uint32_t)(x) & 0x0000FF00) << 8) | \
+ (((uint32_t)(x) & 0x00FF0000) >> 8) | \
+ (((uint32_t)(x) & 0xFF000000) >> 24)))
+#endif
+
+#ifndef bswap_64
+# define bswap_64(x) ((uint64_t)( \
+ (((uint64_t)(x) & 0x00000000000000FFULL) << 56) | \
+ (((uint64_t)(x) & 0x000000000000FF00ULL) << 40) | \
+ (((uint64_t)(x) & 0x0000000000FF0000ULL) << 24) | \
+ (((uint64_t)(x) & 0x00000000FF000000ULL) << 8) | \
+ (((uint64_t)(x) & 0x000000FF00000000ULL) >> 8) | \
+ (((uint64_t)(x) & 0x0000FF0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & 0x00FF000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & 0xFF00000000000000ULL) >> 56)))
+#endif
+
+#ifndef htobe16
+# if !defined(WORDS_BIGENDIAN)
+# define htobe16(x) bswap_16 (x)
+# define htole16(x) (x)
+# define be16toh(x) bswap_16 (x)
+# define le16toh(x) (x)
+# define htobe32(x) bswap_32 (x)
+# define htole32(x) (x)
+# define be32toh(x) bswap_32 (x)
+# define le32toh(x) (x)
+# define htobe64(x) bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe16(x) (x)
+# define htole16(x) bswap_16 (x)
+# define be16toh(x) (x)
+# define le16toh(x) bswap_16 (x)
+# define htobe32(x) (x)
+# define htole32(x) bswap_32 (x)
+# define be32toh(x) (x)
+# define le32toh(x) bswap_32 (x)
+# define htobe64(x) (x)
+# define htole64(x) bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) bswap_64 (x)
+# endif
+#endif
+
+/*
+ * Byte swab macros (based on linux/byteorder/swab.h)
+ */
+#define swab16(x) bswap_16(x)
+#define swab32(x) bswap_32(x)
+#define swab64(x) bswap_64(x)
+
+#define cpu_to_le16(x) ((uint16_t) htole16(x))
+#define cpu_to_le32(x) ((uint32_t) htole32(x))
+#define cpu_to_le64(x) ((uint64_t) htole64(x))
+
+#define cpu_to_be16(x) ((uint16_t) htobe16(x))
+#define cpu_to_be32(x) ((uint32_t) htobe32(x))
+#define cpu_to_be64(x) ((uint64_t) htobe64(x))
+
+#define le16_to_cpu(x) ((uint16_t) le16toh(x))
+#define le32_to_cpu(x) ((uint32_t) le32toh(x))
+#define le64_to_cpu(x) ((uint64_t) le64toh(x))
+
+#define be16_to_cpu(x) ((uint16_t) be16toh(x))
+#define be32_to_cpu(x) ((uint32_t) be32toh(x))
+#define be64_to_cpu(x) ((uint64_t) be64toh(x))
+
+/*
+ * Bit map related macros. Usually provided by libc.
+ */
+#ifndef NBBY
+# define NBBY CHAR_BIT
+#endif
+
+#ifndef setbit
+# define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+# define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+# define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+# define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif
+
+#endif /* BITOPS_H */
+
diff --git a/utils/include/blkdev.h b/utils/include/blkdev.h
new file mode 100644
index 0000000..6cbecbb
--- /dev/null
+++ b/utils/include/blkdev.h
@@ -0,0 +1,151 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BLKDEV_H
+#define BLKDEV_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCCOM_H
+# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* major and minor on Solaris */
+#endif
+
+#define DEFAULT_SECTOR_SIZE 512
+
+#ifdef __linux__
+/* very basic ioctls, should be available everywhere */
+# ifndef BLKROSET
+# define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
+# define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
+# define BLKRRPART _IO(0x12,95) /* re-read partition table */
+# define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */
+# define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+# define BLKRASET _IO(0x12,98) /* set read ahead for block device */
+# define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
+# define BLKFRASET _IO(0x12,100) /* set filesystem (mm/filemap.c) read-ahead */
+# define BLKFRAGET _IO(0x12,101) /* get filesystem (mm/filemap.c) read-ahead */
+# define BLKSECTSET _IO(0x12,102) /* set max sectors per request (ll_rw_blk.c) */
+# define BLKSECTGET _IO(0x12,103) /* get max sectors per request (ll_rw_blk.c) */
+# define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+
+/* ioctls introduced in 2.2.16, removed in 2.5.58 */
+# define BLKELVGET _IOR(0x12,106,size_t) /* elevator get */
+# define BLKELVSET _IOW(0x12,107,size_t) /* elevator set */
+
+# define BLKBSZGET _IOR(0x12,112,size_t)
+# define BLKBSZSET _IOW(0x12,113,size_t)
+# endif /* !BLKROSET */
+
+# ifndef BLKGETSIZE64
+# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+# endif
+
+/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */
+# ifndef BLKIOMIN
+# define BLKIOMIN _IO(0x12,120)
+# define BLKIOOPT _IO(0x12,121)
+# define BLKALIGNOFF _IO(0x12,122)
+# define BLKPBSZGET _IO(0x12,123)
+# endif
+
+/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */
+# ifndef BLKDISCARDZEROES
+# define BLKDISCARDZEROES _IO(0x12,124)
+# endif
+
+/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */
+# ifndef FIFREEZE
+# define FIFREEZE _IOWR('X', 119, int) /* Freeze */
+# define FITHAW _IOWR('X', 120, int) /* Thaw */
+# endif
+
+/* uniform CD-ROM information */
+# ifndef CDROM_GET_CAPABILITY
+# define CDROM_GET_CAPABILITY 0x5331
+# endif
+
+#endif /* __linux */
+
+
+#ifdef APPLE_DARWIN
+# define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif
+
+#ifndef HDIO_GETGEO
+# ifdef __linux__
+# define HDIO_GETGEO 0x0301
+# endif
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders; /* truncated */
+ unsigned long start;
+};
+#endif /* HDIO_GETGEO */
+
+
+/* are we working with block device? */
+int is_blkdev(int fd);
+
+/* open block device or file */
+int open_blkdev_or_file(const struct stat *st, const char *name, const int oflag);
+
+/* Determine size in bytes */
+off_t blkdev_find_size (int fd);
+
+/* get size in bytes */
+int blkdev_get_size(int fd, unsigned long long *bytes);
+
+/* get 512-byte sector count */
+int blkdev_get_sectors(int fd, unsigned long long *sectors);
+
+/* get hardware sector size */
+int blkdev_get_sector_size(int fd, int *sector_size);
+
+/* specifies whether or not the device is misaligned */
+int blkdev_is_misaligned(int fd);
+
+/* get physical block device size */
+int blkdev_get_physector_size(int fd, int *sector_size);
+
+/* is the device cdrom capable? */
+int blkdev_is_cdrom(int fd);
+
+/* get device's geometry - legacy */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s);
+
+/* SCSI device types. Copied almost as-is from kernel header.
+ * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/scsi/scsi.h */
+#define SCSI_TYPE_DISK 0x00
+#define SCSI_TYPE_TAPE 0x01
+#define SCSI_TYPE_PRINTER 0x02
+#define SCSI_TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define SCSI_TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define SCSI_TYPE_ROM 0x05
+#define SCSI_TYPE_SCANNER 0x06
+#define SCSI_TYPE_MOD 0x07 /* Magneto-optical disk - treated as SCSI_TYPE_DISK */
+#define SCSI_TYPE_MEDIUM_CHANGER 0x08
+#define SCSI_TYPE_COMM 0x09 /* Communications device */
+#define SCSI_TYPE_RAID 0x0c
+#define SCSI_TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define SCSI_TYPE_RBC 0x0e
+#define SCSI_TYPE_OSD 0x11
+#define SCSI_TYPE_NO_LUN 0x7f
+
+/* convert scsi type code to name */
+const char *blkdev_scsi_type_to_name(int type);
+
+int blkdev_lock(int fd, const char *devname, const char *lockmode);
+
+#endif /* BLKDEV_H */
diff --git a/utils/include/c.h b/utils/include/c.h
new file mode 100644
index 0000000..ae08131
--- /dev/null
+++ b/utils/include/c.h
@@ -0,0 +1,427 @@
+/*
+ * Fundamental C definitions.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
+#ifndef UTIL_LINUX_C_H
+#define UTIL_LINUX_C_H
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <assert.h>
+
+#ifdef HAVE_ERR_H
+# include <err.h>
+#endif
+
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h> /* for major, minor */
+#endif
+
+#ifndef LOGIN_NAME_MAX
+# define LOGIN_NAME_MAX 256
+#endif
+
+#ifndef NAME_MAX
+# define NAME_MAX PATH_MAX
+#endif
+
+/*
+ * __GNUC_PREREQ is deprecated in favour of __has_attribute() and
+ * __has_feature(). The __has macros are supported by clang and gcc>=5.
+ */
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+# define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#ifdef __GNUC__
+
+/* &a[0] degrades to a pointer: a different type from an array */
+# define __must_be_array(a) \
+ UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
+
+# define ignore_result(x) __extension__ ({ \
+ __typeof__(x) __dummy __attribute__((__unused__)) = (x); (void) __dummy; \
+})
+
+#else /* !__GNUC__ */
+# define __must_be_array(a) 0
+# define __attribute__(_arg_)
+# define ignore_result(x) ((void) (x))
+#endif /* !__GNUC__ */
+
+/*
+ * It evaluates to 1 if the attribute/feature is supported by the current
+ * compilation target. Fallback for old compilers.
+ */
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+
+#ifndef __has_feature
+ #define __has_feature(x) 0
+#endif
+
+/*
+ * Function attributes
+ */
+#ifndef __ul_alloc_size
+# if (__has_attribute(alloc_size) && __has_attribute(warn_unused_result)) || __GNUC_PREREQ (4, 3)
+# define __ul_alloc_size(s) __attribute__((alloc_size(s), warn_unused_result))
+# else
+# define __ul_alloc_size(s)
+# endif
+#endif
+
+#ifndef __ul_calloc_size
+# if (__has_attribute(alloc_size) && __has_attribute(warn_unused_result)) || __GNUC_PREREQ (4, 3)
+# define __ul_calloc_size(n, s) __attribute__((alloc_size(n, s), warn_unused_result))
+# else
+# define __ul_calloc_size(n, s)
+# endif
+#endif
+
+#if __has_attribute(returns_nonnull) || __GNUC_PREREQ (4, 9)
+# define __ul_returns_nonnull __attribute__((returns_nonnull))
+#else
+# define __ul_returns_nonnull
+#endif
+
+/*
+ * Force a compilation error if condition is true, but also produce a
+ * result (of value 0 and type size_t), so the expression can be used
+ * e.g. in a structure initializer (or wherever else comma expressions
+ * aren't permitted).
+ */
+#define UL_BUILD_BUG_ON_ZERO(e) __extension__ (sizeof(struct { int:-!!(e); }))
+#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef min
+# define min(x, y) __extension__ ({ \
+ __typeof__(x) _min1 = (x); \
+ __typeof__(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef max
+# define max(x, y) __extension__ ({ \
+ __typeof__(x) _max1 = (x); \
+ __typeof__(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef cmp_numbers
+# define cmp_numbers(x, y) __extension__ ({ \
+ __typeof__(x) _a = (x); \
+ __typeof__(y) _b = (y); \
+ (void) (&_a == &_b); \
+ _a == _b ? 0 : _a > _b ? 1 : -1; })
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ */
+#ifndef container_of
+#define container_of(ptr, type, member) __extension__ ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+# ifdef HAVE___PROGNAME
+extern char *__progname;
+# define program_invocation_short_name __progname
+# else
+# ifdef HAVE_GETEXECNAME
+# define program_invocation_short_name \
+ prog_inv_sh_nm_from_file(getexecname(), 0)
+# else
+# define program_invocation_short_name \
+ prog_inv_sh_nm_from_file(__FILE__, 1)
+# endif
+static char prog_inv_sh_nm_buf[256];
+static inline char *
+prog_inv_sh_nm_from_file(char *f, char stripext)
+{
+ char *t;
+
+ if ((t = strrchr(f, '/')) != NULL)
+ t++;
+ else
+ t = f;
+
+ strncpy(prog_inv_sh_nm_buf, t, sizeof(prog_inv_sh_nm_buf) - 1);
+ prog_inv_sh_nm_buf[sizeof(prog_inv_sh_nm_buf) - 1] = '\0';
+
+ if (stripext && (t = strrchr(prog_inv_sh_nm_buf, '.')) != NULL)
+ *t = '\0';
+
+ return prog_inv_sh_nm_buf;
+}
+# endif
+#endif
+
+
+#ifndef HAVE_ERR_H
+static inline void
+errmsg(char doexit, int excode, char adderr, const char *fmt, ...)
+{
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ if (fmt != NULL) {
+ va_list argp;
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+ if (adderr)
+ fprintf(stderr, ": ");
+ }
+ if (adderr)
+ fprintf(stderr, "%m");
+ fprintf(stderr, "\n");
+ if (doexit)
+ exit(excode);
+}
+
+#ifndef HAVE_ERR
+# define err(E, FMT...) errmsg(1, E, 1, FMT)
+#endif
+
+#ifndef HAVE_ERRX
+# define errx(E, FMT...) errmsg(1, E, 0, FMT)
+#endif
+
+#ifndef HAVE_WARN
+# define warn(FMT...) errmsg(0, 0, 1, FMT)
+#endif
+
+#ifndef HAVE_WARNX
+# define warnx(FMT...) errmsg(0, 0, 0, FMT)
+#endif
+#endif /* !HAVE_ERR_H */
+
+
+/* Don't use inline function to avoid '#include "nls.h"' in c.h
+ */
+#define errtryhelp(eval) __extension__ ({ \
+ fprintf(stderr, _("Try '%s --help' for more information.\n"), \
+ program_invocation_short_name); \
+ exit(eval); \
+})
+
+/* After failed execvp() */
+#define EX_EXEC_FAILED 126 /* Program located, but not usable. */
+#define EX_EXEC_ENOENT 127 /* Could not find program to exec. */
+#define errexec(name) err(errno == ENOENT ? EX_EXEC_ENOENT : EX_EXEC_FAILED, \
+ _("failed to execute %s"), name)
+
+
+static inline __attribute__((const)) int is_power_of_2(unsigned long num)
+{
+ return (num != 0 && ((num & (num - 1)) == 0));
+}
+
+#ifndef HAVE_LOFF_T
+typedef int64_t loff_t;
+#endif
+
+#if !defined(HAVE_DIRFD) && (!defined(HAVE_DECL_DIRFD) || HAVE_DECL_DIRFD == 0) && defined(HAVE_DIR_DD_FD)
+#include <sys/types.h>
+#include <dirent.h>
+static inline int dirfd(DIR *d)
+{
+ return d->dd_fd;
+}
+#endif
+
+/*
+ * Fallback defines for old versions of glibc
+ */
+#include <fcntl.h>
+
+#ifdef O_CLOEXEC
+#define UL_CLOEXECSTR "e"
+#else
+#define UL_CLOEXECSTR ""
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifdef __FreeBSD_kernel__
+#ifndef F_DUPFD_CLOEXEC
+#define F_DUPFD_CLOEXEC 17 /* Like F_DUPFD, but FD_CLOEXEC is set */
+#endif
+#endif
+
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0x0020
+#endif
+
+#ifndef IUTF8
+#define IUTF8 0040000
+#endif
+
+/*
+ * MAXHOSTNAMELEN replacement
+ */
+static inline size_t get_hostname_max(void)
+{
+ long len = sysconf(_SC_HOST_NAME_MAX);
+
+ if (0 < len)
+ return len;
+
+#ifdef MAXHOSTNAMELEN
+ return MAXHOSTNAMELEN;
+#elif HOST_NAME_MAX
+ return HOST_NAME_MAX;
+#endif
+ return 64;
+}
+
+/*
+ * The usleep function was marked obsolete in POSIX.1-2001 and was removed
+ * in POSIX.1-2008. It was replaced with nanosleep() that provides more
+ * advantages (like no interaction with signals and other timer functions).
+ */
+#include <time.h>
+
+static inline int xusleep(useconds_t usec)
+{
+#ifdef HAVE_NANOSLEEP
+ struct timespec waittime = {
+ .tv_sec = usec / 1000000L,
+ .tv_nsec = (usec % 1000000L) * 1000
+ };
+ return nanosleep(&waittime, NULL);
+#elif defined(HAVE_USLEEP)
+ return usleep(usec);
+#else
+# error "System with usleep() or nanosleep() required!"
+#endif
+}
+
+/*
+ * Constant strings for usage() functions. For more info see
+ * Documentation/{howto-usage-function.txt,boilerplate.c}
+ */
+#define USAGE_HEADER _("\nUsage:\n")
+#define USAGE_OPTIONS _("\nOptions:\n")
+#define USAGE_FUNCTIONS _("\nFunctions:\n")
+#define USAGE_COMMANDS _("\nCommands:\n")
+#define USAGE_ARGUMENTS _("\nArguments:\n")
+#define USAGE_COLUMNS _("\nAvailable output columns:\n")
+#define USAGE_SEPARATOR "\n"
+
+#define USAGE_OPTSTR_HELP _("display this help")
+#define USAGE_OPTSTR_VERSION _("display version")
+
+#define USAGE_HELP_OPTIONS(marg_dsc) \
+ "%-" #marg_dsc "s%s\n" \
+ "%-" #marg_dsc "s%s\n" \
+ , " -h, --help", USAGE_OPTSTR_HELP \
+ , " -V, --version", USAGE_OPTSTR_VERSION
+
+#define USAGE_ARG_SEPARATOR "\n"
+#define USAGE_ARG_SIZE(_name) \
+ _(" %s arguments may be followed by the suffixes for\n" \
+ " GiB, TiB, PiB, EiB, ZiB, and YiB (the \"iB\" is optional)\n"), _name
+
+#define USAGE_MAN_TAIL(_man) _("\nFor more details see %s.\n"), _man
+
+#define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING
+
+#define print_version(eval) __extension__ ({ \
+ printf(UTIL_LINUX_VERSION); \
+ exit(eval); \
+})
+
+/*
+ * seek stuff
+ */
+#ifndef SEEK_DATA
+# define SEEK_DATA 3
+#endif
+#ifndef SEEK_HOLE
+# define SEEK_HOLE 4
+#endif
+
+
+/*
+ * Macros to convert #define'itions to strings, for example
+ * #define XYXXY 42
+ * printf ("%s=%s\n", stringify(XYXXY), stringify_value(XYXXY));
+ */
+#define stringify_value(s) stringify(s)
+#define stringify(s) #s
+
+/*
+ * UL_ASAN_BLACKLIST is a macro to tell AddressSanitizer (a compile-time
+ * instrumentation shipped with Clang and GCC) to not instrument the
+ * annotated function. Furthermore, it will prevent the compiler from
+ * inlining the function because inlining currently breaks the blacklisting
+ * mechanism of AddressSanitizer.
+ */
+#if __has_feature(address_sanitizer) && __has_attribute(no_sanitize_memory) && __has_attribute(no_sanitize_address)
+# define UL_ASAN_BLACKLIST __attribute__((noinline)) __attribute__((no_sanitize_memory)) __attribute__((no_sanitize_address))
+#else
+# define UL_ASAN_BLACKLIST /* nothing */
+#endif
+
+/*
+ * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for
+ * pwd buffer and in some cases it is not large enough. See POSIX and
+ * getpwnam_r man page for more details.
+ */
+#define UL_GETPW_BUFSIZ (16 * 1024)
+
+/*
+ * Darwin or other BSDs may only have MAP_ANON. To get it on Darwin we must
+ * define _DARWIN_C_SOURCE before including sys/mman.h. We do this in config.h.
+ */
+#if !defined MAP_ANONYMOUS && defined MAP_ANON
+# define MAP_ANONYMOUS (MAP_ANON)
+#endif
+
+#endif /* UTIL_LINUX_C_H */
diff --git a/utils/include/canonicalize.h b/utils/include/canonicalize.h
new file mode 100644
index 0000000..ff6ef0d
--- /dev/null
+++ b/utils/include/canonicalize.h
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, 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 Library Public License for more details.
+ */
+#ifndef CANONICALIZE_H
+#define CANONICALIZE_H
+
+#include "c.h" /* for PATH_MAX */
+#include "strutils.h"
+
+extern char *canonicalize_path(const char *path);
+extern char *canonicalize_path_restricted(const char *path);
+extern char *canonicalize_dm_name(const char *ptname);
+extern char *__canonicalize_dm_name(const char *prefix, const char *ptname);
+
+extern char *absolute_path(const char *path);
+
+static inline int is_relative_path(const char *path)
+{
+ if (!path || *path == '/')
+ return 0;
+ return 1;
+}
+
+#endif /* CANONICALIZE_H */
diff --git a/utils/include/caputils.h b/utils/include/caputils.h
new file mode 100644
index 0000000..852903a
--- /dev/null
+++ b/utils/include/caputils.h
@@ -0,0 +1,34 @@
+/*
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CAPUTILS_H
+#define CAPUTILS_H
+
+#include <linux/capability.h>
+
+#ifndef PR_CAP_AMBIENT
+# define PR_CAP_AMBIENT 47
+# define PR_CAP_AMBIENT_IS_SET 1
+# define PR_CAP_AMBIENT_RAISE 2
+# define PR_CAP_AMBIENT_LOWER 3
+#endif
+
+extern int capset(cap_user_header_t header, cap_user_data_t data);
+extern int capget(cap_user_header_t header, const cap_user_data_t data);
+
+extern int cap_last_cap(void);
+
+#endif /* CAPUTILS_H */
diff --git a/utils/include/carefulputc.h b/utils/include/carefulputc.h
new file mode 100644
index 0000000..f1c0356
--- /dev/null
+++ b/utils/include/carefulputc.h
@@ -0,0 +1,155 @@
+#ifndef UTIL_LINUX_CAREFULPUTC_H
+#define UTIL_LINUX_CAREFULPUTC_H
+
+/*
+ * A putc() for use in write and wall (that sometimes are sgid tty).
+ * It avoids control characters in our locale, and also ASCII control
+ * characters. Note that the locale of the recipient is unknown.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "cctype.h"
+
+static inline int fputc_careful(int c, FILE *fp, const char fail)
+{
+ int ret;
+
+ if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
+ ret = putc(c, fp);
+ else if (!c_isascii(c))
+ ret = fprintf(fp, "\\%3o", (unsigned char)c);
+ else {
+ ret = putc(fail, fp);
+ if (ret != EOF)
+ ret = putc(c ^ 0x40, fp);
+ }
+ return (ret < 0) ? EOF : 0;
+}
+
+/*
+ * Requirements enumerated via testing (V8, Firefox, IE11):
+ *
+ * var charsToEscape = [];
+ * for (var i = 0; i < 65535; i += 1) {
+ * try {
+ * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
+ * } catch (e) {
+ * charsToEscape.push(i);
+ * }
+ * }
+ */
+static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir)
+{
+ const char *p;
+
+ fputc('"', out);
+ for (p = data; p && *p; p++) {
+
+ const unsigned char c = (unsigned char) *p;
+
+ /* From http://www.json.org
+ *
+ * The double-quote and backslashes would break out a string or
+ * init an escape sequence if not escaped.
+ *
+ * Note that single-quotes and forward slashes, while they're
+ * in the JSON spec, don't break double-quoted strings.
+ */
+ if (c == '"' || c == '\\') {
+ fputc('\\', out);
+ fputc(c, out);
+ continue;
+ }
+
+ /* All non-control characters OK; do the case swap as required. */
+ if (c >= 0x20) {
+ fputc(dir == 1 ? toupper(c) :
+ dir == -1 ? tolower(c) : *p, out);
+ continue;
+ }
+
+ /* In addition, all chars under ' ' break Node's/V8/Chrome's, and
+ * Firefox's JSON.parse function
+ */
+ switch (c) {
+ /* Handle short-hand cases to reduce output size. C
+ * has most of the same stuff here, so if there's an
+ * "Escape for C" function somewhere in the STL, we
+ * should probably be using it.
+ */
+ case '\b':
+ fputs("\\b", out);
+ break;
+ case '\t':
+ fputs("\\t", out);
+ break;
+ case '\n':
+ fputs("\\n", out);
+ break;
+ case '\f':
+ fputs("\\f", out);
+ break;
+ case '\r':
+ fputs("\\r", out);
+ break;
+ default:
+ /* Other assorted control characters */
+ fprintf(out, "\\u00%02x", c);
+ break;
+ }
+ }
+ fputc('"', out);
+}
+
+
+static inline void fputs_quoted_case(const char *data, FILE *out, int dir)
+{
+ const char *p;
+
+ fputc('"', out);
+ for (p = data; p && *p; p++) {
+ if ((unsigned char) *p == 0x22 || /* " */
+ (unsigned char) *p == 0x5c || /* \ */
+ (unsigned char) *p == 0x60 || /* ` */
+ (unsigned char) *p == 0x24 || /* $ */
+ !isprint((unsigned char) *p) ||
+ iscntrl((unsigned char) *p)) {
+
+ fprintf(out, "\\x%02x", (unsigned char) *p);
+ } else
+ fputc(dir == 1 ? toupper(*p) :
+ dir == -1 ? tolower(*p) :
+ *p, out);
+ }
+ fputc('"', out);
+}
+
+#define fputs_quoted(_d, _o) fputs_quoted_case(_d, _o, 0)
+#define fputs_quoted_upper(_d, _o) fputs_quoted_case(_d, _o, 1)
+#define fputs_quoted_lower(_d, _o) fputs_quoted_case(_d, _o, -1)
+
+#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)
+
+static inline void fputs_nonblank(const char *data, FILE *out)
+{
+ const char *p;
+
+ for (p = data; p && *p; p++) {
+ if (isblank((unsigned char) *p) ||
+ (unsigned char) *p == 0x5c || /* \ */
+ !isprint((unsigned char) *p) ||
+ iscntrl((unsigned char) *p)) {
+
+ fprintf(out, "\\x%02x", (unsigned char) *p);
+
+ } else
+ fputc(*p, out);
+ }
+}
+
+
+#endif /* _CAREFULPUTC_H */
diff --git a/utils/include/cctype.h b/utils/include/cctype.h
new file mode 100644
index 0000000..6ab644c
--- /dev/null
+++ b/utils/include/cctype.h
@@ -0,0 +1,325 @@
+/**
+ * Character handling in C locale.
+ *
+ * This file is based on gnulib c-ctype.h-dd7a871 with the
+ * other gnulib dependencies removed for use in util-linux.
+ *
+ * These functions work like the corresponding functions in <ctype.h>,
+ * except that they have the C (POSIX) locale hardwired, whereas the
+ * <ctype.h> functions' behaviour depends on the current locale set via
+ * setlocale.
+ *
+ * Copyright (C) 2000-2003, 2006, 2008-2017 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 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTIL_LINUX_CCTYPE_H
+#define UTIL_LINUX_CCTYPE_H
+
+/**
+ * The functions defined in this file assume the "C" locale and a character
+ * set without diacritics (ASCII-US or EBCDIC-US or something like that).
+ * Even if the "C" locale on a particular system is an extension of the ASCII
+ * character set (like on BeOS, where it is UTF-8, or on AmigaOS, where it
+ * is ISO-8859-1), the functions in this file recognize only the ASCII
+ * characters.
+ */
+
+#if (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)
+
+/*
+ * The character set is ASCII or one of its variants or extensions, not EBCDIC.
+ * Testing the value of '\n' and '\r' is not relevant.
+ */
+# define C_CTYPE_ASCII 1
+#elif ! (' ' == '\x40' && '0' == '\xf0' \
+ && 'A' == '\xc1' && 'J' == '\xd1' && 'S' == '\xe2' \
+ && 'a' == '\x81' && 'j' == '\x91' && 's' == '\xa2')
+# error "Only ASCII and EBCDIC are supported"
+#endif
+
+#if 'A' < 0
+# error "EBCDIC and char is signed -- not supported"
+#endif
+
+/* Cases for control characters. */
+#define _C_CTYPE_CNTRL \
+ case '\a': case '\b': case '\f': case '\n': \
+ case '\r': case '\t': case '\v': \
+ _C_CTYPE_OTHER_CNTRL
+
+/* ASCII control characters other than those with \-letter escapes. */
+#if C_CTYPE_ASCII
+# define _C_CTYPE_OTHER_CNTRL \
+ case '\x00': case '\x01': case '\x02': case '\x03': \
+ case '\x04': case '\x05': case '\x06': case '\x0e': \
+ case '\x0f': case '\x10': case '\x11': case '\x12': \
+ case '\x13': case '\x14': case '\x15': case '\x16': \
+ case '\x17': case '\x18': case '\x19': case '\x1a': \
+ case '\x1b': case '\x1c': case '\x1d': case '\x1e': \
+ case '\x1f': case '\x7f'
+#else
+
+/*
+ * Use EBCDIC code page 1047's assignments for ASCII control chars;
+ * assume all EBCDIC code pages agree about these assignments.
+ */
+# define _C_CTYPE_OTHER_CNTRL \
+ case '\x00': case '\x01': case '\x02': case '\x03': \
+ case '\x07': case '\x0e': case '\x0f': case '\x10': \
+ case '\x11': case '\x12': case '\x13': case '\x18': \
+ case '\x19': case '\x1c': case '\x1d': case '\x1e': \
+ case '\x1f': case '\x26': case '\x27': case '\x2d': \
+ case '\x2e': case '\x32': case '\x37': case '\x3c': \
+ case '\x3d': case '\x3f'
+#endif
+
+/* Cases for lowercase hex letters, and lowercase letters, all offset by N. */
+#define _C_CTYPE_LOWER_A_THRU_F_N(N) \
+ case 'a' + (N): case 'b' + (N): case 'c' + (N): case 'd' + (N): \
+ case 'e' + (N): case 'f' + (N)
+#define _C_CTYPE_LOWER_N(N) \
+ _C_CTYPE_LOWER_A_THRU_F_N(N): \
+ case 'g' + (N): case 'h' + (N): case 'i' + (N): case 'j' + (N): \
+ case 'k' + (N): case 'l' + (N): case 'm' + (N): case 'n' + (N): \
+ case 'o' + (N): case 'p' + (N): case 'q' + (N): case 'r' + (N): \
+ case 's' + (N): case 't' + (N): case 'u' + (N): case 'v' + (N): \
+ case 'w' + (N): case 'x' + (N): case 'y' + (N): case 'z' + (N)
+
+/* Cases for hex letters, digits, lower, punct, and upper. */
+#define _C_CTYPE_A_THRU_F \
+ _C_CTYPE_LOWER_A_THRU_F_N (0): \
+ _C_CTYPE_LOWER_A_THRU_F_N ('A' - 'a')
+#define _C_CTYPE_DIGIT \
+ case '0': case '1': case '2': case '3': \
+ case '4': case '5': case '6': case '7': \
+ case '8': case '9'
+#define _C_CTYPE_LOWER _C_CTYPE_LOWER_N (0)
+#define _C_CTYPE_PUNCT \
+ case '!': case '"': case '#': case '$': \
+ case '%': case '&': case '\'': case '(': \
+ case ')': case '*': case '+': case ',': \
+ case '-': case '.': case '/': case ':': \
+ case ';': case '<': case '=': case '>': \
+ case '?': case '@': case '[': case '\\': \
+ case ']': case '^': case '_': case '`': \
+ case '{': case '|': case '}': case '~'
+#define _C_CTYPE_UPPER _C_CTYPE_LOWER_N ('A' - 'a')
+
+/**
+ * Function definitions.
+ *
+ * Unlike the functions in <ctype.h>, which require an argument in the range
+ * of the 'unsigned char' type, the functions here operate on values that are
+ * in the 'unsigned char' range or in the 'char' range. In other words,
+ * when you have a 'char' value, you need to cast it before using it as
+ * argument to a <ctype.h> function:
+ *
+ * const char *s = ...;
+ * if (isalpha ((unsigned char) *s)) ...
+ *
+ * but you don't need to cast it for the functions defined in this file:
+ *
+ * const char *s = ...;
+ * if (c_isalpha (*s)) ...
+ */
+
+static inline int c_isalnum (int c)
+{
+ switch (c) {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isalpha (int c)
+{
+ switch (c) {
+ _C_CTYPE_LOWER:
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* The function isascii is not locale dependent.
+ * Its use in EBCDIC is questionable.
+ */
+static inline int c_isascii (int c)
+{
+ switch (c) {
+ case ' ':
+ _C_CTYPE_CNTRL:
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isblank (int c)
+{
+ return c == ' ' || c == '\t';
+}
+
+static inline int c_iscntrl (int c)
+{
+ switch (c) {
+ _C_CTYPE_CNTRL:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isdigit (int c)
+{
+ switch (c) {
+ _C_CTYPE_DIGIT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isgraph (int c)
+{
+ switch (c) {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_islower (int c)
+{
+ switch (c) {
+ _C_CTYPE_LOWER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isprint (int c)
+{
+ switch (c) {
+ case ' ':
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_ispunct (int c)
+{
+ switch (c) {
+ _C_CTYPE_PUNCT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isspace (int c)
+{
+ switch (c) {
+ case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isupper (int c)
+{
+ switch (c) {
+ _C_CTYPE_UPPER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_isxdigit (int c)
+{
+ switch (c) {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_A_THRU_F:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int c_tolower (int c)
+{
+ switch (c) {
+ _C_CTYPE_UPPER:
+ return c - 'A' + 'a';
+ default:
+ return c;
+ }
+}
+
+static inline int c_toupper (int c)
+{
+ switch (c) {
+ _C_CTYPE_LOWER:
+ return c - 'a' + 'A';
+ default:
+ return c;
+ }
+}
+
+#endif /* UTIL_LINUX_CCTYPE_H */
diff --git a/utils/include/closestream.h b/utils/include/closestream.h
new file mode 100644
index 0000000..41afbe2
--- /dev/null
+++ b/utils/include/closestream.h
@@ -0,0 +1,110 @@
+#ifndef UTIL_LINUX_CLOSESTREAM_H
+#define UTIL_LINUX_CLOSESTREAM_H
+
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#ifndef CLOSE_EXIT_CODE
+# define CLOSE_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline int
+close_stream(FILE * stream)
+{
+#ifdef HAVE___FPENDING
+ const int some_pending = (__fpending(stream) != 0);
+#endif
+ const int prev_fail = (ferror(stream) != 0);
+ const int fclose_fail = (fclose(stream) != 0);
+
+ if (prev_fail || (fclose_fail && (
+#ifdef HAVE___FPENDING
+ some_pending ||
+#endif
+ errno != EBADF))) {
+ if (!fclose_fail && !(errno == EPIPE))
+ errno = 0;
+ return EOF;
+ }
+ return 0;
+}
+
+static inline int
+flush_standard_stream(FILE *stream)
+{
+ int fd;
+
+ errno = 0;
+
+ if (ferror(stream) != 0 || fflush(stream) != 0)
+ goto error;
+
+ /*
+ * Calling fflush is not sufficient on some filesystems
+ * like e.g. NFS, which may defer the actual flush until
+ * close. Calling fsync would help solve this, but would
+ * probably result in a performance hit. Thus, we work
+ * around this issue by calling close on a dup'd file
+ * descriptor from the stream.
+ */
+ if ((fd = fileno(stream)) < 0 || (fd = dup(fd)) < 0 || close(fd) != 0)
+ goto error;
+
+ return 0;
+error:
+ return (errno == EBADF) ? 0 : EOF;
+}
+
+/* Meant to be used atexit(close_stdout); */
+static inline void
+close_stdout(void)
+{
+ if (flush_standard_stream(stdout) != 0 && !(errno == EPIPE)) {
+ if (errno)
+ warn(_("write error"));
+ else
+ warnx(_("write error"));
+ _exit(CLOSE_EXIT_CODE);
+ }
+
+ if (flush_standard_stream(stderr) != 0)
+ _exit(CLOSE_EXIT_CODE);
+}
+
+static inline void
+close_stdout_atexit(void)
+{
+ /*
+ * Note that close stdout at exit disables ASAN to report memory leaks
+ */
+#if !defined(__SANITIZE_ADDRESS__)
+ atexit(close_stdout);
+#endif
+}
+
+#ifndef HAVE_FSYNC
+static inline int
+fsync(int fd __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+static inline int
+close_fd(int fd)
+{
+ const int fsync_fail = (fsync(fd) != 0);
+ const int close_fail = (close(fd) != 0);
+
+ if (fsync_fail || close_fail)
+ return EOF;
+ return 0;
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/utils/include/color-names.h b/utils/include/color-names.h
new file mode 100644
index 0000000..42f6f8f
--- /dev/null
+++ b/utils/include/color-names.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012-2015 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_COLOR_NAMES_H
+#define UTIL_LINUX_COLOR_NAMES_H
+
+#define UL_COLOR_RESET "\033[0m"
+#define UL_COLOR_BOLD "\033[1m"
+#define UL_COLOR_HALFBRIGHT "\033[2m"
+#define UL_COLOR_UNDERSCORE "\033[4m"
+#define UL_COLOR_BLINK "\033[5m"
+#define UL_COLOR_REVERSE "\033[7m"
+
+/* Standard colors */
+#define UL_COLOR_BLACK "\033[30m"
+#define UL_COLOR_RED "\033[31m"
+#define UL_COLOR_GREEN "\033[32m"
+#define UL_COLOR_BROWN "\033[33m" /* well, brown */
+#define UL_COLOR_BLUE "\033[34m"
+#define UL_COLOR_MAGENTA "\033[35m"
+#define UL_COLOR_CYAN "\033[36m"
+#define UL_COLOR_GRAY "\033[37m"
+
+/* Bold variants */
+#define UL_COLOR_DARK_GRAY "\033[1;30m"
+#define UL_COLOR_BOLD_RED "\033[1;31m"
+#define UL_COLOR_BOLD_GREEN "\033[1;32m"
+#define UL_COLOR_BOLD_YELLOW "\033[1;33m"
+#define UL_COLOR_BOLD_BLUE "\033[1;34m"
+#define UL_COLOR_BOLD_MAGENTA "\033[1;35m"
+#define UL_COLOR_BOLD_CYAN "\033[1;36m"
+
+#define UL_COLOR_WHITE "\033[1;37m"
+
+
+/* maximal length of human readable name of ESC seq. */
+#define UL_COLORNAME_MAXSZ 32
+
+extern const char *color_sequence_from_colorname(const char *str);
+
+#endif /* UTIL_LINUX_COLOR_NAMES_H */
diff --git a/utils/include/colors.h b/utils/include/colors.h
new file mode 100644
index 0000000..d4ae4e4
--- /dev/null
+++ b/utils/include/colors.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_COLORS_H
+#define UTIL_LINUX_COLORS_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "color-names.h"
+
+/* --color[=WHEN] */
+enum colortmode {
+ UL_COLORMODE_AUTO = 0,
+ UL_COLORMODE_NEVER,
+ UL_COLORMODE_ALWAYS,
+ UL_COLORMODE_UNDEF,
+
+ __UL_NCOLORMODES /* last */
+};
+
+#ifdef USE_COLORS_BY_DEFAULT
+# define USAGE_COLORS_DEFAULT _("colors are enabled by default")
+#else
+# define USAGE_COLORS_DEFAULT _("colors are disabled by default")
+#endif
+
+extern int colormode_from_string(const char *str);
+extern int colormode_or_err(const char *str, const char *errmsg);
+
+/* Initialize the global variable UL_COLOR_TERM_OK */
+extern int colors_init(int mode, const char *util_name);
+
+/* Returns 1 or 0 */
+extern int colors_wanted(void);
+
+/* Returns UL_COLORMODE_* */
+extern int colors_mode(void);
+
+/* temporary enable/disable colors */
+extern void colors_off(void);
+extern void colors_on(void);
+
+
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
+extern const char *color_scheme_get_sequence(const char *name, const char *dflt);
+
+static inline void color_enable(const char *seq)
+{
+ color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+ color_scheme_fenable(name, dflt, stdout);
+}
+
+/* Reset colors to default */
+extern void color_fdisable(FILE *f);
+
+static inline void color_disable(void)
+{
+ color_fdisable(stdout);
+}
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/utils/include/cpuset.h b/utils/include/cpuset.h
new file mode 100644
index 0000000..5a531bf
--- /dev/null
+++ b/utils/include/cpuset.h
@@ -0,0 +1,99 @@
+/*
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_CPUSET_H
+#define UTIL_LINUX_CPUSET_H
+
+#include <sched.h>
+
+/*
+ * Fallback for old or obscure libcs without dynamically allocated cpusets
+ *
+ * The following macros are based on code from glibc.
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+#if !HAVE_DECL_CPU_ALLOC
+
+# define CPU_ZERO_S(setsize, cpusetp) \
+ do { \
+ size_t __i; \
+ size_t __imax = (setsize) / sizeof (__cpu_mask); \
+ __cpu_mask *__bits = (cpusetp)->__bits; \
+ for (__i = 0; __i < __imax; ++__i) \
+ __bits[__i] = 0; \
+ } while (0)
+
+# define CPU_SET_S(cpu, setsize, cpusetp) \
+ ({ size_t __cpu = (cpu); \
+ __cpu < 8 * (setsize) \
+ ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
+ |= __CPUMASK (__cpu)) \
+ : 0; })
+
+# define CPU_ISSET_S(cpu, setsize, cpusetp) \
+ ({ size_t __cpu = (cpu); \
+ __cpu < 8 * (setsize) \
+ ? ((((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
+ & __CPUMASK (__cpu))) != 0 \
+ : 0; })
+
+# define CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \
+ ({ __cpu_mask *__arr1 = (cpusetp1)->__bits; \
+ __cpu_mask *__arr2 = (cpusetp2)->__bits; \
+ size_t __imax = (setsize) / sizeof (__cpu_mask); \
+ size_t __i; \
+ for (__i = 0; __i < __imax; ++__i) \
+ if (__arr1[__i] != __arr2[__i]) \
+ break; \
+ __i == __imax; })
+
+extern int __cpuset_count_s(size_t setsize, const cpu_set_t *set);
+# define CPU_COUNT_S(setsize, cpusetp) __cpuset_count_s(setsize, cpusetp)
+
+# define CPU_ALLOC_SIZE(count) \
+ ((((count) + __NCPUBITS - 1) / __NCPUBITS) * sizeof (__cpu_mask))
+# define CPU_ALLOC(count) (malloc(CPU_ALLOC_SIZE(count)))
+# define CPU_FREE(cpuset) (free(cpuset))
+
+#endif /* !HAVE_DECL_CPU_ALLOC */
+
+
+#define cpuset_nbits(setsize) (8 * (setsize))
+
+/*
+ * The @idx parameter returns an index of the first mask from @ary array where
+ * the @cpu is set.
+ *
+ * Returns: 0 if found, otherwise 1.
+ */
+static inline int cpuset_ary_isset(size_t cpu, cpu_set_t **ary, size_t nmemb,
+ size_t setsize, size_t *idx)
+{
+ size_t i;
+
+ for (i = 0; i < nmemb; i++) {
+ if (CPU_ISSET_S(cpu, setsize, ary[i])) {
+ *idx = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+extern int get_max_number_of_cpus(void);
+
+extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits);
+extern void cpuset_free(cpu_set_t *set);
+
+extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail);
+
+extern char *cpumask_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize);
+
+#endif /* UTIL_LINUX_CPUSET_H */
diff --git a/utils/include/crc32.h b/utils/include/crc32.h
new file mode 100644
index 0000000..2551f50
--- /dev/null
+++ b/utils/include/crc32.h
@@ -0,0 +1,12 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t ul_crc32(uint32_t seed, const unsigned char *buf, size_t len);
+extern uint32_t ul_crc32_exclude_offset(uint32_t seed, const unsigned char *buf, size_t len,
+ size_t exclude_off, size_t exclude_len);
+
+#endif
+
diff --git a/utils/include/crc32c.h b/utils/include/crc32c.h
new file mode 100644
index 0000000..1c50839
--- /dev/null
+++ b/utils/include/crc32c.h
@@ -0,0 +1,9 @@
+#ifndef UL_NG_CRC32C_H
+#define UL_NG_CRC32C_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t crc32c(uint32_t crc, const void *buf, size_t size);
+
+#endif /* UL_NG_CRC32C_H */
diff --git a/utils/include/debug.h b/utils/include/debug.h
new file mode 100644
index 0000000..39c21d5
--- /dev/null
+++ b/utils/include/debug.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_DEBUG_H
+#define UTIL_LINUX_DEBUG_H
+
+
+/*
+ * util-linux debug macros
+ *
+ * The debug stuff is based on <name>_debug_mask that controls what outputs is
+ * expected. The mask is usually initialized by <NAME>_DEBUG= env.variable
+ *
+ * After successful initialization the flag <PREFIX>_DEBUG_INIT is always set
+ * to the mask (this flag is required). The <PREFIX> is usually library API
+ * prefix (e.g. MNT_) or program name (e.g. CFDISK_)
+ *
+ * In the code is possible to use
+ *
+ * DBG(FOO, ul_debug("this is output for foo"));
+ *
+ * where for the FOO has to be defined <PREFIX>_DEBUG_FOO.
+ *
+ * It's possible to initialize the mask by comma delimited strings with
+ * subsystem names (e.g. "LIBMOUNT_DEBUG=options,tab"). In this case is
+ * necessary to define mask names array. This functionality is optional.
+ *
+ * It's strongly recommended to use UL_* macros to define/declare/use
+ * the debug stuff.
+ *
+ * See disk-utils/cfdisk.c: cfdisk_init_debug() for programs debug
+ * or libmount/src/init.c: mnt_init_debug() for library debug
+ *
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+struct ul_debug_maskname {
+ const char *name;
+ int mask;
+ const char *help;
+};
+#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0, NULL }}
+#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct ul_debug_maskname m ## _masknames[]
+#define UL_DEBUG_MASKNAMES(m) m ## _masknames
+
+#define UL_DEBUG_MASK(m) m ## _debug_mask
+#define UL_DEBUG_DEFINE_MASK(m) int UL_DEBUG_MASK(m)
+#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+
+/*
+ * Internal mask flags (above 0xffffff)
+ */
+#define __UL_DEBUG_FL_NOADDR (1 << 24) /* Don't print object address */
+
+
+/* l - library name, p - flag prefix, m - flag postfix, x - function */
+#define __UL_DBG(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_CALL(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_FLUSH(l, p) \
+ do { \
+ if (l ## _debug_mask && \
+ l ## _debug_mask != p ## INIT) { \
+ fflush(stderr); \
+ } \
+ } while (0)
+
+#define __UL_INIT_DEBUG_FROM_STRING(lib, pref, mask, str) \
+ do { \
+ if (lib ## _debug_mask & pref ## INIT) \
+ ; \
+ else if (!mask && str) { \
+ lib ## _debug_mask = ul_debug_parse_mask(lib ## _masknames, str); \
+ } else \
+ lib ## _debug_mask = mask; \
+ if (lib ## _debug_mask) { \
+ if (getuid() != geteuid() || getgid() != getegid()) { \
+ lib ## _debug_mask |= __UL_DEBUG_FL_NOADDR; \
+ fprintf(stderr, "%d: %s: don't print memory addresses (SUID executable).\n", getpid(), # lib); \
+ } \
+ } \
+ lib ## _debug_mask |= pref ## INIT; \
+ } while (0)
+
+
+#define __UL_INIT_DEBUG_FROM_ENV(lib, pref, mask, env) \
+ do { \
+ const char *envstr = mask ? NULL : getenv(# env); \
+ __UL_INIT_DEBUG_FROM_STRING(lib, pref, mask, envstr); \
+ } while (0)
+
+
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+ul_debug(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline int ul_debug_parse_mask(
+ const struct ul_debug_maskname flagnames[],
+ const char *mask)
+{
+ int res;
+ char *ptr;
+
+ /* let's check for a numeric mask first */
+ res = strtoul(mask, &ptr, 0);
+
+ /* perhaps it's a comma-separated string? */
+ if (ptr && *ptr && flagnames && flagnames[0].name) {
+ char *msbuf, *ms, *name;
+ res = 0;
+
+ ms = msbuf = strdup(mask);
+ if (!ms)
+ return res;
+
+ while ((name = strtok_r(ms, ",", &ptr))) {
+ const struct ul_debug_maskname *d;
+ ms = ptr;
+
+ for (d = flagnames; d && d->name; d++) {
+ if (strcmp(name, d->name) == 0) {
+ res |= d->mask;
+ break;
+ }
+ }
+ /* nothing else we can do by OR-ing the mask */
+ if (res == 0xffff)
+ break;
+ }
+ free(msbuf);
+ } else if (ptr && strcmp(ptr, "all") == 0)
+ res = 0xffff;
+
+ return res;
+}
+
+static inline void ul_debug_print_masks(
+ const char *env,
+ const struct ul_debug_maskname flagnames[])
+{
+ const struct ul_debug_maskname *d;
+
+ if (!flagnames)
+ return;
+
+ fprintf(stderr, "Available \"%s=<name>[,...]|<mask>\" debug masks:\n",
+ env);
+ for (d = flagnames; d && d->name; d++) {
+ if (!d->help)
+ continue;
+ fprintf(stderr, " %-8s [0x%04x] : %s\n",
+ d->name, d->mask, d->help);
+ }
+}
+
+#endif /* UTIL_LINUX_DEBUG_H */
diff --git a/utils/include/debugobj.h b/utils/include/debugobj.h
new file mode 100644
index 0000000..73b70b8
--- /dev/null
+++ b/utils/include/debugobj.h
@@ -0,0 +1,22 @@
+#ifndef UTIL_LINUX_DEBUGOBJ_H
+#define UTIL_LINUX_DEBUGOBJ_H
+
+/*
+ * Include *after* debug.h and after UL_DEBUG_CURRENT_MASK define.
+ */
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+ul_debugobj(const void *handler, const char *mesg, ...)
+{
+ va_list ap;
+
+ if (handler && !(UL_DEBUG_CURRENT_MASK & __UL_DEBUG_FL_NOADDR))
+ fprintf(stderr, "[%p]: ", handler);
+
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+#endif /* UTIL_LINUX_DEBUGOBJ_H */
diff --git a/utils/include/encode.h b/utils/include/encode.h
new file mode 100644
index 0000000..b259ab5
--- /dev/null
+++ b/utils/include/encode.h
@@ -0,0 +1,14 @@
+#ifndef UTIL_LINUX_ENCODE_H
+#define UTIL_LINUX_ENCODE_H
+
+extern size_t ul_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+ const unsigned char *src, size_t count)
+ __attribute__((nonnull));
+
+enum {
+ UL_ENCODE_UTF16BE = 0,
+ UL_ENCODE_UTF16LE,
+ UL_ENCODE_LATIN1
+};
+
+#endif
diff --git a/utils/include/env.h b/utils/include/env.h
new file mode 100644
index 0000000..c2caecb
--- /dev/null
+++ b/utils/include/env.h
@@ -0,0 +1,35 @@
+#ifndef UTIL_LINUX_ENV_H
+#define UTIL_LINUX_ENV_H
+
+#include "c.h"
+#include "nls.h"
+
+struct ul_env_list;
+
+extern void sanitize_env(void);
+extern void __sanitize_env(struct ul_env_list **org);
+
+extern int env_list_setenv(struct ul_env_list *ls);
+extern void env_list_free(struct ul_env_list *ls);
+
+extern char *safe_getenv(const char *arg);
+
+
+#ifndef XSETENV_EXIT_CODE
+# define XSETENV_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline void xsetenv(char const *name, char const *val, int overwrite)
+{
+ if (setenv(name, val, overwrite) != 0)
+ err(XSETENV_EXIT_CODE, _("failed to set the %s environment variable"), name);
+}
+
+static inline int remote_entry(char **argv, int remove, int last)
+{
+ memmove(argv + remove, argv + remove + 1, sizeof(char *) * (last - remove));
+ return last - 1;
+}
+
+#endif /* UTIL_LINUX_ENV_H */
+
diff --git a/utils/include/exec_shell.h b/utils/include/exec_shell.h
new file mode 100644
index 0000000..04aa089
--- /dev/null
+++ b/utils/include/exec_shell.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_EXEC_SHELL_H
+#define UTIL_LINUX_EXEC_SHELL_H
+
+extern void exec_shell(void) __attribute__((__noreturn__));
+
+#endif /* UTIL_LINUX_EXEC_SHELL_H */
diff --git a/utils/include/exitcodes.h b/utils/include/exitcodes.h
new file mode 100644
index 0000000..f28f68e
--- /dev/null
+++ b/utils/include/exitcodes.h
@@ -0,0 +1,25 @@
+#ifndef UTIL_LINUX_EXITCODES_H
+#define UTIL_LINUX_EXITCODES_H
+/*
+ * BE CAREFUL
+ *
+ * These exit codes are part of the official interface for mount,
+ * fsck, mkfs, etc. wrappers.
+ */
+
+/* Exit codes used by mkfs-type programs */
+#define MKFS_EX_OK 0 /* No errors */
+#define MKFS_EX_ERROR 8 /* Operational error */
+#define MKFS_EX_USAGE 16 /* Usage or syntax error */
+
+/* Exit codes used by fsck-type programs */
+#define FSCK_EX_OK 0 /* No errors */
+#define FSCK_EX_NONDESTRUCT 1 /* File system errors corrected */
+#define FSCK_EX_REBOOT 2 /* System should be rebooted */
+#define FSCK_EX_DESTRUCT FSCK_EX_REBOOT /* Alias */
+#define FSCK_EX_UNCORRECTED 4 /* File system errors left uncorrected */
+#define FSCK_EX_ERROR 8 /* Operational error */
+#define FSCK_EX_USAGE 16 /* Usage or syntax error */
+#define FSCK_EX_LIBRARY 128 /* Shared library error */
+
+#endif /* UTIL_LINUX_EXITCODES_H */
diff --git a/utils/include/fileutils.h b/utils/include/fileutils.h
new file mode 100644
index 0000000..479ad15
--- /dev/null
+++ b/utils/include/fileutils.h
@@ -0,0 +1,77 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "c.h"
+
+extern int mkstemp_cloexec(char *template);
+
+extern int xmkstemp(char **tmpname, const char *dir, const char *prefix);
+
+static inline FILE *xfmkstemp(char **tmpname, const char *dir, const char *prefix)
+{
+ int fd;
+ FILE *ret;
+
+ fd = xmkstemp(tmpname, dir, prefix);
+ if (fd == -1)
+ return NULL;
+
+ if (!(ret = fdopen(fd, "w+" UL_CLOEXECSTR))) {
+ close(fd);
+ return NULL;
+ }
+ return ret;
+}
+
+#ifdef HAVE_OPENAT
+static inline FILE *fopen_at(int dir, const char *filename,
+ int flags, const char *mode)
+{
+ int fd = openat(dir, filename, flags);
+ if (fd < 0)
+ return NULL;
+
+ return fdopen(fd, mode);
+}
+#endif
+
+static inline int is_same_inode(const int fd, const struct stat *st)
+{
+ struct stat f;
+
+ if (fstat(fd, &f) < 0)
+ return 0;
+ else if (f.st_dev != st->st_dev || f.st_ino != st->st_ino)
+ return 0;
+ return 1;
+}
+
+extern int dup_fd_cloexec(int oldfd, int lowfd);
+extern int get_fd_tabsize(void);
+
+extern int mkdir_p(const char *path, mode_t mode);
+extern char *stripoff_last_component(char *path);
+
+/* This is readdir()-like function, but skips "." and ".." directory entries */
+static inline struct dirent *xreaddir(DIR *dp)
+{
+ struct dirent *d;
+
+ while ((d = readdir(dp))) {
+ if (!strcmp(d->d_name, ".") ||
+ !strcmp(d->d_name, ".."))
+ continue;
+ break;
+ }
+ return d;
+}
+
+extern void close_all_fds(const int exclude[], size_t exsz);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/utils/include/fuzz.h b/utils/include/fuzz.h
new file mode 100644
index 0000000..1b0dbd2
--- /dev/null
+++ b/utils/include/fuzz.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_FUZZ_H
+#define UTIL_LINUX_FUZZ_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+#endif /* UTIL_LINUX_FUZZ_H */
diff --git a/utils/include/idcache.h b/utils/include/idcache.h
new file mode 100644
index 0000000..912edd5
--- /dev/null
+++ b/utils/include/idcache.h
@@ -0,0 +1,28 @@
+#ifndef UTIL_LINUX_IDCACHE_H
+#define UTIL_LINUX_IDCACHE_H
+
+#include <sys/types.h>
+#include <pwd.h>
+
+#define IDCACHE_FLAGS_NAMELEN (1 << 1)
+
+struct identry {
+ unsigned long int id;
+ char *name;
+ struct identry *next;
+};
+
+struct idcache {
+ struct identry *ent; /* first entry */
+ int width; /* name width */
+};
+
+
+extern struct idcache *new_idcache(void);
+extern void add_gid(struct idcache *cache, unsigned long int id);
+extern void add_uid(struct idcache *cache, unsigned long int id);
+
+extern void free_idcache(struct idcache *ic);
+extern struct identry *get_id(struct idcache *ic, unsigned long int id);
+
+#endif /* UTIL_LINUX_IDCACHE_H */
diff --git a/utils/include/ismounted.h b/utils/include/ismounted.h
new file mode 100644
index 0000000..57918cb
--- /dev/null
+++ b/utils/include/ismounted.h
@@ -0,0 +1,14 @@
+#ifndef IS_MOUNTED_H
+#define IS_MOUNTED_H
+
+#define MF_MOUNTED 1
+#define MF_ISROOT 2
+#define MF_READONLY 4
+#define MF_SWAP 8
+#define MF_BUSY 16
+
+extern int is_mounted(const char *file);
+extern int check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen);
+
+#endif /* IS_MOUNTED_H */
diff --git a/utils/include/iso9660.h b/utils/include/iso9660.h
new file mode 100644
index 0000000..73decd9
--- /dev/null
+++ b/utils/include/iso9660.h
@@ -0,0 +1,58 @@
+#ifndef UTIL_LINUX_ISO_H
+#define UTIL_LINUX_ISO_H
+
+#include <stdbool.h>
+
+#include "c.h"
+
+static inline int isonum_721(unsigned char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8));
+}
+
+static inline int isonum_722(unsigned char *p)
+{
+ return ((p[1] & 0xff)
+ | ((p[0] & 0xff) << 8));
+}
+
+static inline int isonum_723(unsigned char *p, bool check_match)
+{
+ int le = isonum_721(p);
+ int be = isonum_722(p + 2);
+
+ if (check_match && le != be)
+ /* translation is useless */
+ warnx("723error: le=%d be=%d", le, be);
+ return (le);
+}
+
+static inline int isonum_731(unsigned char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8)
+ | ((p[2] & 0xff) << 16)
+ | ((p[3] & 0xff) << 24));
+}
+
+static inline int isonum_732(unsigned char *p)
+{
+ return ((p[3] & 0xff)
+ | ((p[2] & 0xff) << 8)
+ | ((p[1] & 0xff) << 16)
+ | ((p[0] & 0xff) << 24));
+}
+
+static inline int isonum_733(unsigned char *p, bool check_match)
+{
+ int le = isonum_731(p);
+ int be = isonum_732(p + 4);
+
+ if (check_match && le != be)
+ /* translation is useless */
+ warnx("733error: le=%d be=%d", le, be);
+ return(le);
+}
+
+#endif
diff --git a/utils/include/linux_version.h b/utils/include/linux_version.h
new file mode 100644
index 0000000..a6a1e99
--- /dev/null
+++ b/utils/include/linux_version.h
@@ -0,0 +1,14 @@
+#ifndef LINUX_VERSION_H
+#define LINUX_VERSION_H
+
+#ifdef HAVE_LINUX_VERSION_H
+# include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+int get_linux_version(void);
+
+#endif /* LINUX_VERSION_H */
diff --git a/utils/include/list.h b/utils/include/list.h
new file mode 100644
index 0000000..96c84e5
--- /dev/null
+++ b/utils/include/list.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 1999-2008 by Theodore Ts'o
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * (based on list.h from e2fsprogs)
+ * Merge sort based on kernel's implementation.
+ */
+
+#ifndef UTIL_LINUX_LIST_H
+#define UTIL_LINUX_LIST_H
+
+#include "c.h"
+
+/* TODO: use AC_C_INLINE */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_add(struct list_head * add,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+_INLINE_ void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+_INLINE_ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+_INLINE_ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+_INLINE_ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_entry_is_last - tests whether is entry last in the list
+ * @entry: the entry to test.
+ * @head: the list to test.
+ */
+_INLINE_ int list_entry_is_last(struct list_head *entry, struct list_head *head)
+{
+ return head->prev == entry;
+}
+
+/**
+ * list_entry_is_first - tests whether is entry first in the list
+ * @entry: the entry to test.
+ * @head: the list to test.
+ */
+_INLINE_ int list_entry_is_first(struct list_head *entry, struct list_head *head)
+{
+ return head->next == entry;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+_INLINE_ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
+
+#define list_first_entry(head, type, member) \
+ ((head) && (head)->next != (head) ? list_entry((head)->next, type, member) : NULL)
+
+#define list_last_entry(head, type, member) \
+ ((head) && (head)->prev != (head) ? list_entry((head)->prev, type, member) : NULL)
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_backwardly - iterate over elements in a list in reverse
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_backwardly(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ * pos after the body is done (in case it is freed)
+ * @pos: the &struct list_head to use as a loop counter.
+ * @pnext: the &struct list_head to use as a pointer to the next item.
+ * @head: the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+ for (pos = (head)->next, pnext = pos->next; pos != (head); \
+ pos = pnext, pnext = pos->next)
+
+_INLINE_ size_t list_count_entries(struct list_head *head)
+{
+ struct list_head *pos;
+ size_t ct = 0;
+
+ list_for_each(pos, head)
+ ct++;
+
+ return ct;
+}
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+_INLINE_ struct list_head *merge(int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head head, *tail = &head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b, data) <= 0) {
+ tail->next = a;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+ return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure. This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+_INLINE_ void merge_and_restore_back_links(int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data,
+ struct list_head *head,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head *tail = head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b, data) <= 0) {
+ tail->next = a;
+ a->prev = tail;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b->prev = tail;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+
+ do {
+ /*
+ * In worst cases this loop may run many iterations.
+ * Continue callbacks to the client even though no
+ * element comparison is needed, so the client's cmp()
+ * routine can invoke cond_resched() periodically.
+ */
+ (*cmp)(tail->next, tail->next, data);
+
+ tail->next->prev = tail;
+ tail = tail->next;
+ } while (tail->next);
+
+ tail->next = head;
+ head->prev = tail;
+}
+
+
+/**
+ * list_sort - sort a list
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+_INLINE_ void list_sort(struct list_head *head,
+ int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data)
+{
+ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+ -- last slot is a sentinel */
+ size_t lev; /* index into part[] */
+ size_t max_lev = 0;
+ struct list_head *list;
+
+ if (list_empty(head))
+ return;
+
+ memset(part, 0, sizeof(part));
+
+ head->prev->next = NULL;
+ list = head->next;
+
+ while (list) {
+ struct list_head *cur = list;
+ list = list->next;
+ cur->next = NULL;
+
+ for (lev = 0; part[lev]; lev++) {
+ cur = merge(cmp, data, part[lev], cur);
+ part[lev] = NULL;
+ }
+ if (lev > max_lev) {
+ /* list passed to list_sort() too long for efficiency */
+ if (lev >= ARRAY_SIZE(part) - 1)
+ lev--;
+ max_lev = lev;
+ }
+ part[lev] = cur;
+ }
+
+ for (lev = 0; lev < max_lev; lev++)
+ if (part[lev])
+ list = merge(cmp, data, part[lev], list);
+
+ merge_and_restore_back_links(cmp, data, head, part[max_lev], list);
+}
+
+#undef _INLINE_
+
+#endif /* UTIL_LINUX_LIST_H */
diff --git a/utils/include/loopdev.h b/utils/include/loopdev.h
new file mode 100644
index 0000000..bc68e52
--- /dev/null
+++ b/utils/include/loopdev.h
@@ -0,0 +1,222 @@
+#ifndef UTIL_LINUX_LOOPDEV_H
+#define UTIL_LINUX_LOOPDEV_H
+
+#include "sysfs.h"
+
+/*
+ * loop_info.lo_encrypt_type
+ */
+#define LO_CRYPT_NONE 0
+#define LO_CRYPT_XOR 1
+#define LO_CRYPT_DES 2
+#define LO_CRYPT_CRYPTOAPI 18
+
+/*
+ * loop_info.lo_file_fmt_type
+ */
+#define LO_FILE_FMT_RAW 0
+#define LO_FILE_FMT_QCOW 1
+#define LO_FILE_FMT_VDI 2
+#define LO_FILE_FMT_VMDK 3
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+/*
+ * Obsolete (kernel < 2.6)
+ *
+ * #define LOOP_SET_STATUS 0x4C02
+ * #define LOOP_GET_STATUS 0x4C03
+ */
+#define LOOP_SET_STATUS64 0x4C04
+#define LOOP_GET_STATUS64 0x4C05
+/* #define LOOP_CHANGE_FD 0x4C06 */
+#define LOOP_SET_CAPACITY 0x4C07
+#define LOOP_SET_DIRECT_IO 0x4C08
+#define LOOP_SET_BLOCK_SIZE 0x4C09
+
+/* /dev/loop-control interface */
+#ifndef LOOP_CTL_ADD
+# define LOOP_CTL_ADD 0x4C80
+# define LOOP_CTL_REMOVE 0x4C81
+# define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+/*
+ * loop_info.lo_flags
+ */
+enum {
+ LO_FLAGS_READ_ONLY = 1,
+ LO_FLAGS_USE_AOPS = 2,
+ LO_FLAGS_AUTOCLEAR = 4, /* kernel >= 2.6.25 */
+ LO_FLAGS_PARTSCAN = 8, /* kernel >= 3.2 */
+ LO_FLAGS_DIRECT_IO = 16, /* kernel >= 4.2 */
+};
+
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+
+/*
+ * Linux LOOP_{SET,GET}_STATUS64 ioctl struct
+ */
+struct loop_info64 {
+ uint64_t lo_device;
+ uint64_t lo_inode;
+ uint64_t lo_rdevice;
+ uint64_t lo_offset;
+ uint64_t lo_sizelimit; /* bytes, 0 == max available */
+ uint32_t lo_number;
+ uint32_t lo_encrypt_type;
+ uint32_t lo_encrypt_key_size;
+ uint32_t lo_flags;
+ uint8_t lo_file_name[LO_NAME_SIZE];
+ uint8_t lo_crypt_name[LO_NAME_SIZE];
+ uint8_t lo_encrypt_key[LO_KEY_SIZE];
+ uint64_t lo_init[2];
+ uint32_t lo_file_fmt_type;
+};
+
+#define LOOPDEV_MAJOR 7 /* loop major number */
+#define LOOPDEV_DEFAULT_NNODES 8 /* default number of loop devices */
+
+struct loopdev_iter {
+ FILE *proc; /* /proc/partitions */
+ DIR *sysblock; /* /sys/block */
+ int ncur; /* current position */
+ int *minors; /* ary of minor numbers (when scan whole /dev) */
+ int nminors; /* number of items in *minors */
+ int ct_perm; /* count permission problems */
+ int ct_succ; /* count number of detected devices */
+
+ unsigned int done:1; /* scanning done */
+ unsigned int default_check:1;/* check first LOOPDEV_NLOOPS */
+ int flags; /* LOOPITER_FL_* flags */
+};
+
+enum {
+ LOOPITER_FL_FREE = (1 << 0),
+ LOOPITER_FL_USED = (1 << 1)
+};
+
+/*
+ * handler for work with loop devices
+ */
+struct loopdev_cxt {
+ char device[128]; /* device path (e.g. /dev/loop<N>) */
+ char *filename; /* backing file for loopcxt_set_... */
+ int fd; /* open(/dev/looo<N>) */
+ int mode; /* fd mode O_{RDONLY,RDWR} */
+ uint64_t blocksize; /* used by loopcxt_setup_device() */
+
+ int flags; /* LOOPDEV_FL_* flags */
+ unsigned int has_info:1; /* .info contains data */
+ unsigned int extra_check:1; /* unusual stuff for iterator */
+ unsigned int info_failed:1; /* LOOP_GET_STATUS ioctl failed */
+ unsigned int control_ok:1; /* /dev/loop-control success */
+
+ struct path_cxt *sysfs; /* pointer to /sys/dev/block/<maj:min>/ */
+ struct loop_info64 info; /* for GET/SET ioctl */
+ struct loopdev_iter iter; /* scans /sys or /dev for used/free devices */
+};
+
+#define UL_LOOPDEVCXT_EMPTY { .fd = -1 }
+
+/*
+ * loopdev_cxt.flags
+ */
+enum {
+ LOOPDEV_FL_RDONLY = (1 << 0), /* open(/dev/loop) mode; default */
+ LOOPDEV_FL_RDWR = (1 << 1), /* necessary for loop setup only */
+ LOOPDEV_FL_OFFSET = (1 << 4),
+ LOOPDEV_FL_NOSYSFS = (1 << 5),
+ LOOPDEV_FL_NOIOCTL = (1 << 6),
+ LOOPDEV_FL_DEVSUBDIR = (1 << 7),
+ LOOPDEV_FL_CONTROL = (1 << 8), /* system with /dev/loop-control */
+ LOOPDEV_FL_SIZELIMIT = (1 << 9)
+};
+
+/*
+ * High-level
+ */
+extern int loopmod_supports_partscan(void);
+
+extern int is_loopdev(const char *device);
+extern int loopdev_is_autoclear(const char *device);
+
+extern char *loopdev_get_backing_file(const char *device);
+extern int loopdev_is_used(const char *device, const char *filename,
+ uint64_t offset, uint64_t sizelimit, int flags);
+extern char *loopdev_find_by_backing_file(const char *filename,
+ uint64_t offset, uint64_t sizelimit, int flags);
+extern int loopcxt_find_unused(struct loopdev_cxt *lc);
+extern int loopdev_delete(const char *device);
+extern int loopdev_count_by_backing_file(const char *filename, char **loopdev);
+
+/*
+ * Low-level
+ */
+extern int loopcxt_init(struct loopdev_cxt *lc, int flags)
+ __attribute__ ((warn_unused_result));
+extern void loopcxt_deinit(struct loopdev_cxt *lc);
+
+extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+ __attribute__ ((warn_unused_result));
+extern int loopcxt_has_device(struct loopdev_cxt *lc);
+extern int loopcxt_add_device(struct loopdev_cxt *lc);
+extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
+extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
+extern struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc);
+
+extern int loopcxt_get_fd(struct loopdev_cxt *lc);
+extern int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode);
+
+extern int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags);
+extern int loopcxt_deinit_iterator(struct loopdev_cxt *lc);
+extern int loopcxt_next(struct loopdev_cxt *lc);
+
+extern int loopcxt_setup_device(struct loopdev_cxt *lc);
+extern int loopcxt_delete_device(struct loopdev_cxt *lc);
+
+extern int loopcxt_ioctl_status(struct loopdev_cxt *lc);
+extern int loopcxt_ioctl_capacity(struct loopdev_cxt *lc);
+extern int loopcxt_ioctl_dio(struct loopdev_cxt *lc, unsigned long use_dio);
+extern int loopcxt_ioctl_blocksize(struct loopdev_cxt *lc, uint64_t blocksize);
+
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, uint64_t blocksize);
+int loopcxt_set_file_fmt_type(struct loopdev_cxt *lc, uint32_t file_fmt_type);
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+
+extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
+extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
+extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
+extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize);
+extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
+extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
+extern int loopcxt_get_file_fmt_type(struct loopdev_cxt *lc, uint32_t* file_fmt_type);
+extern char *loopcxt_get_file_fmt_type_string(struct loopdev_cxt *lc);
+extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
+extern int loopcxt_is_autoclear(struct loopdev_cxt *lc);
+extern int loopcxt_is_readonly(struct loopdev_cxt *lc);
+extern int loopcxt_is_dio(struct loopdev_cxt *lc);
+extern int loopcxt_is_partscan(struct loopdev_cxt *lc);
+extern int loopcxt_find_by_backing_file(struct loopdev_cxt *lc,
+ const char *filename,
+ uint64_t offset, uint64_t sizelimit,
+ int flags);
+extern int loopcxt_find_overlap(struct loopdev_cxt *lc,
+ const char *filename,
+ uint64_t offset, uint64_t sizelimit);
+
+extern int loopcxt_is_used(struct loopdev_cxt *lc,
+ struct stat *st,
+ const char *backing_file,
+ uint64_t offset,
+ uint64_t sizelimit,
+ int flags);
+
+extern int parse_file_fmt_type(const char *file_fmt_type_str, uint32_t *file_fmt_type);
+
+#endif /* UTIL_LINUX_LOOPDEV_H */
diff --git a/utils/include/mangle.h b/utils/include/mangle.h
new file mode 100644
index 0000000..08c66cb
--- /dev/null
+++ b/utils/include/mangle.h
@@ -0,0 +1,28 @@
+#ifndef UTIL_LINUX_MANGLE_H
+#define UTIL_LINUX_MANGLE_H
+
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ */
+
+extern char *mangle(const char *s);
+
+extern void unmangle_to_buffer(const char *s, char *buf, size_t len);
+extern size_t unhexmangle_to_buffer(const char *s, char *buf, size_t len);
+
+extern char *unmangle(const char *s, const char **end);
+
+static inline void unmangle_string(char *s)
+{
+ if (s)
+ unmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+static inline void unhexmangle_string(char *s)
+{
+ if (s)
+ unhexmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+#endif /* UTIL_LINUX_MANGLE_H */
+
diff --git a/utils/include/match.h b/utils/include/match.h
new file mode 100644
index 0000000..94440c2
--- /dev/null
+++ b/utils/include/match.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_MATCH_H
+#define UTIL_LINUX_MATCH_H
+
+extern int match_fstype(const char *type, const char *pattern);
+
+#endif /* UTIL_LINUX_MATCH_H */
diff --git a/utils/include/mbsalign.h b/utils/include/mbsalign.h
new file mode 100644
index 0000000..4f5add8
--- /dev/null
+++ b/utils/include/mbsalign.h
@@ -0,0 +1,66 @@
+/* Align/Truncate a string in a given screen width
+ Copyright (C) 2009-2010 Free Software Foundation, Inc.
+ Copyright (C) 2010-2013 Karel Zak <kzak@redhat.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2.1 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 <http://www.gnu.org/licenses/>. */
+#ifndef UTIL_LINUX_MBSALIGN_H
+# define UTIL_LINUX_MBSALIGN_H
+# include <stddef.h>
+
+typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
+
+enum {
+ /* Use unibyte mode for invalid multibyte strings or
+ or when heap memory is exhausted. */
+ MBA_UNIBYTE_FALLBACK = 0x0001,
+
+#if 0 /* Other possible options. */
+ /* Skip invalid multibyte chars rather than failing */
+ MBA_IGNORE_INVALID = 0x0002,
+
+ /* Align multibyte strings using "figure space" (\u2007) */
+ MBA_USE_FIGURE_SPACE = 0x0004,
+
+ /* Don't add any padding */
+ MBA_TRUNCATE_ONLY = 0x0008,
+
+ /* Don't truncate */
+ MBA_PAD_ONLY = 0x0010,
+#endif
+};
+
+extern size_t mbs_truncate(char *str, size_t *width);
+
+extern size_t mbsalign (const char *src, char *dest,
+ size_t dest_size, size_t *width,
+ mbs_align_t align, int flags);
+
+extern size_t mbsalign_with_padding (const char *src, char *dest, size_t dest_size,
+ size_t *width, mbs_align_t align, int flags,
+ int padchar);
+
+extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+extern size_t mbs_safe_width(const char *s);
+
+extern size_t mbs_nwidth(const char *buf, size_t bufsz);
+extern size_t mbs_width(const char *s);
+
+extern char *mbs_safe_encode(const char *s, size_t *width);
+extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars);
+extern size_t mbs_safe_encode_size(size_t bytes);
+
+extern char *mbs_invalid_encode(const char *s, size_t *width);
+extern char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf);
+
+#endif /* UTIL_LINUX_MBSALIGN_H */
diff --git a/utils/include/mbsedit.h b/utils/include/mbsedit.h
new file mode 100644
index 0000000..8d1c6c2
--- /dev/null
+++ b/utils/include/mbsedit.h
@@ -0,0 +1,32 @@
+#ifndef UTIL_LINUX_MBSEDIT_H
+# define UTIL_LINUX_MBSEDIT_H
+
+#include "mbsalign.h"
+#include "widechar.h"
+
+struct mbs_editor {
+ char *buf; /* buffer */
+ size_t max_bytes; /* size of the buffer */
+ size_t max_cells; /* maximal allowed number of cells */
+ size_t cur_cells; /* number of cells to print the buffer */
+ size_t cur_bytes; /* number of chars in bytes */
+ size_t cursor; /* cursor position in bytes */
+ size_t cursor_cells; /* cursor position in cells */
+};
+
+enum {
+ MBS_EDIT_LEFT,
+ MBS_EDIT_RIGHT,
+ MBS_EDIT_END,
+ MBS_EDIT_HOME
+};
+
+struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells);
+char *mbs_free_edit(struct mbs_editor *edit);
+
+int mbs_edit_goto(struct mbs_editor *edit, int where);
+int mbs_edit_delete(struct mbs_editor *edit);
+int mbs_edit_backspace(struct mbs_editor *edit);
+int mbs_edit_insert(struct mbs_editor *edit, wint_t c);
+
+#endif /* UTIL_LINUX_MBSEDIT_H */
diff --git a/utils/include/md5.h b/utils/include/md5.h
new file mode 100644
index 0000000..02e627b
--- /dev/null
+++ b/utils/include/md5.h
@@ -0,0 +1,24 @@
+#ifndef UTIL_LINUX_MD5_H
+#define UTIL_LINUX_MD5_H
+
+#include <stdint.h>
+
+#define UL_MD5LENGTH 16
+
+struct UL_MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+void ul_MD5Init(struct UL_MD5Context *ctx);
+void ul_MD5Update(struct UL_MD5Context *ctx, unsigned char const *buf, unsigned len);
+void ul_MD5Final(unsigned char digest[UL_MD5LENGTH], struct UL_MD5Context *ctx);
+void ul_MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct UL_MD5Context UL_MD5_CTX;
+
+#endif /* !UTIL_LINUX_MD5_H */
diff --git a/utils/include/minix.h b/utils/include/minix.h
new file mode 100644
index 0000000..f28991c
--- /dev/null
+++ b/utils/include/minix.h
@@ -0,0 +1,85 @@
+#ifndef UTIL_LINUX_MINIX_H
+#define UTIL_LINUX_MINIX_H
+
+#include <stdint.h>
+
+struct minix_inode {
+ uint16_t i_mode;
+ uint16_t i_uid;
+ uint32_t i_size;
+ uint32_t i_time;
+ uint8_t i_gid;
+ uint8_t i_nlinks;
+ uint16_t i_zone[9];
+};
+
+struct minix2_inode {
+ uint16_t i_mode;
+ uint16_t i_nlinks;
+ uint16_t i_uid;
+ uint16_t i_gid;
+ uint32_t i_size;
+ uint32_t i_atime;
+ uint32_t i_mtime;
+ uint32_t i_ctime;
+ uint32_t i_zone[10];
+};
+
+struct minix_super_block {
+ uint16_t s_ninodes;
+ uint16_t s_nzones;
+ uint16_t s_imap_blocks;
+ uint16_t s_zmap_blocks;
+ uint16_t s_firstdatazone;
+ uint16_t s_log_zone_size;
+ uint32_t s_max_size;
+ uint16_t s_magic;
+ uint16_t s_state;
+ uint32_t s_zones;
+};
+
+/* V3 minix super-block data on disk */
+struct minix3_super_block {
+ uint32_t s_ninodes;
+ uint16_t s_pad0;
+ uint16_t s_imap_blocks;
+ uint16_t s_zmap_blocks;
+ uint16_t s_firstdatazone;
+ uint16_t s_log_zone_size;
+ uint16_t s_pad1;
+ uint32_t s_max_size;
+ uint32_t s_zones;
+ uint16_t s_magic;
+ uint16_t s_pad2;
+ uint16_t s_blocksize;
+ uint8_t s_disk_version;
+};
+
+/*
+ * Minix subpartitions are always within primary dos partition.
+ */
+#define MINIX_MAXPARTITIONS 4
+
+#define MINIX_BLOCK_SIZE_BITS 10
+#define MINIX_BLOCK_SIZE (1 << MINIX_BLOCK_SIZE_BITS)
+
+#define MINIX_NAME_MAX 255 /* # chars in a file name */
+#define MINIX_MAX_INODES 65535
+
+#define MINIX_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+/* minix_super_block.s_state */
+#define MINIX_VALID_FS 0x0001 /* Clean fs. */
+#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
+
+
+#define MINIX_SUPER_MAGIC 0x137F /* minix V1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix V1 fs, 30 char names */
+
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs, 14 char names */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix V3 fs (60 char names) */
+
+#endif /* UTIL_LINUX_MINIX_H */
diff --git a/utils/include/monotonic.h b/utils/include/monotonic.h
new file mode 100644
index 0000000..380e59c
--- /dev/null
+++ b/utils/include/monotonic.h
@@ -0,0 +1,22 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
+#ifndef UTIL_LINUX_MONOTONIC_H
+#define UTIL_LINUX_MONOTONIC_H
+
+# ifdef CLOCK_MONOTONIC_RAW
+# define UL_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW
+# else
+# define UL_CLOCK_MONOTONIC CLOCK_MONOTONIC
+# endif
+
+#include <sys/time.h>
+
+extern int get_boot_time(struct timeval *boot_time);
+
+extern time_t get_suspended_time(void);
+
+extern int gettime_monotonic(struct timeval *tv);
+
+#endif /* UTIL_LINUX_MONOTONIC_H */
diff --git a/utils/include/namespace.h b/utils/include/namespace.h
new file mode 100644
index 0000000..2d0a56e
--- /dev/null
+++ b/utils/include/namespace.h
@@ -0,0 +1,56 @@
+
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Compat code so unshare and setns can be used with older libcs
+ */
+#ifndef UTIL_LINUX_NAMESPACE_H
+# define UTIL_LINUX_NAMESPACE_H
+
+# include <sched.h>
+
+# ifndef CLONE_NEWNS
+# define CLONE_NEWNS 0x00020000
+# endif
+# ifndef CLONE_NEWCGROUP
+# define CLONE_NEWCGROUP 0x02000000
+# endif
+# ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+# endif
+# ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+# endif
+# ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+# endif
+# ifndef CLONE_NEWUSER
+# define CLONE_NEWUSER 0x10000000
+# endif
+# ifndef CLONE_NEWPID
+# define CLONE_NEWPID 0x20000000
+# endif
+# ifndef CLONE_NEWTIME
+# define CLONE_NEWTIME 0x00000080
+# endif
+
+# if !defined(HAVE_UNSHARE) || !defined(HAVE_SETNS)
+# include <sys/syscall.h>
+# endif
+
+# if !defined(HAVE_UNSHARE) && defined(SYS_unshare)
+static inline int unshare(int flags)
+{
+ return syscall(SYS_unshare, flags);
+}
+# endif
+
+# if !defined(HAVE_SETNS) && defined(SYS_setns)
+static inline int setns(int fd, int nstype)
+{
+ return syscall(SYS_setns, fd, nstype);
+}
+# endif
+
+#endif /* UTIL_LINUX_NAMESPACE_H */
diff --git a/utils/include/nls.h b/utils/include/nls.h
new file mode 100644
index 0000000..5566908
--- /dev/null
+++ b/utils/include/nls.h
@@ -0,0 +1,153 @@
+#ifndef UTIL_LINUX_NLS_H
+#define UTIL_LINUX_NLS_H
+
+#ifndef LOCALEDIR
+#define LOCALEDIR "/usr/share/locale"
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(Category, Locale) /* empty */
+struct lconv
+{
+ char *decimal_point;
+};
+# undef localeconv
+# define localeconv() NULL
+#endif
+
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+/*
+ * For NLS support in the public shared libraries we have to specify text
+ * domain name to be independent on the main program. For this purpose define
+ * UL_TEXTDOMAIN_EXPLICIT before you include nls.h to your shared library code.
+ */
+# ifdef UL_TEXTDOMAIN_EXPLICIT
+# define _(Text) dgettext (UL_TEXTDOMAIN_EXPLICIT, Text)
+# else
+# define _(Text) gettext (Text)
+# endif
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+# define P_(Singular, Plural, n) ngettext (Singular, Plural, n)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+# define P_(Singular, Plural, n) ((n) == 1 ? (Singular) : (Plural))
+#endif /* ENABLE_NLS */
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#else
+
+typedef int nl_item;
+extern char *langinfo_fallback(nl_item item);
+
+# define nl_langinfo langinfo_fallback
+
+enum {
+ CODESET = 1,
+ RADIXCHAR,
+ THOUSEP,
+ D_T_FMT,
+ D_FMT,
+ T_FMT,
+ T_FMT_AMPM,
+ AM_STR,
+ PM_STR,
+
+ DAY_1,
+ DAY_2,
+ DAY_3,
+ DAY_4,
+ DAY_5,
+ DAY_6,
+ DAY_7,
+
+ ABDAY_1,
+ ABDAY_2,
+ ABDAY_3,
+ ABDAY_4,
+ ABDAY_5,
+ ABDAY_6,
+ ABDAY_7,
+
+ MON_1,
+ MON_2,
+ MON_3,
+ MON_4,
+ MON_5,
+ MON_6,
+ MON_7,
+ MON_8,
+ MON_9,
+ MON_10,
+ MON_11,
+ MON_12,
+
+ ABMON_1,
+ ABMON_2,
+ ABMON_3,
+ ABMON_4,
+ ABMON_5,
+ ABMON_6,
+ ABMON_7,
+ ABMON_8,
+ ABMON_9,
+ ABMON_10,
+ ABMON_11,
+ ABMON_12,
+
+ ERA_D_FMT,
+ ERA_D_T_FMT,
+ ERA_T_FMT,
+ ALT_DIGITS,
+ CRNCYSTR,
+ YESEXPR,
+ NOEXPR
+};
+
+#endif /* !HAVE_LANGINFO_H */
+
+#ifndef HAVE_LANGINFO_ALTMON
+# define ALTMON_1 MON_1
+# define ALTMON_2 MON_2
+# define ALTMON_3 MON_3
+# define ALTMON_4 MON_4
+# define ALTMON_5 MON_5
+# define ALTMON_6 MON_6
+# define ALTMON_7 MON_7
+# define ALTMON_8 MON_8
+# define ALTMON_9 MON_9
+# define ALTMON_10 MON_10
+# define ALTMON_11 MON_11
+# define ALTMON_12 MON_12
+#endif /* !HAVE_LANGINFO_ALTMON */
+
+#ifndef HAVE_LANGINFO_NL_ABALTMON
+# define _NL_ABALTMON_1 ABMON_1
+# define _NL_ABALTMON_2 ABMON_2
+# define _NL_ABALTMON_3 ABMON_3
+# define _NL_ABALTMON_4 ABMON_4
+# define _NL_ABALTMON_5 ABMON_5
+# define _NL_ABALTMON_6 ABMON_6
+# define _NL_ABALTMON_7 ABMON_7
+# define _NL_ABALTMON_8 ABMON_8
+# define _NL_ABALTMON_9 ABMON_9
+# define _NL_ABALTMON_10 ABMON_10
+# define _NL_ABALTMON_11 ABMON_11
+# define _NL_ABALTMON_12 ABMON_12
+#endif /* !HAVE_LANGINFO_NL_ABALTMON */
+
+#endif /* UTIL_LINUX_NLS_H */
diff --git a/utils/include/optutils.h b/utils/include/optutils.h
new file mode 100644
index 0000000..0dc127b
--- /dev/null
+++ b/utils/include/optutils.h
@@ -0,0 +1,107 @@
+#ifndef UTIL_LINUX_OPTUTILS_H
+#define UTIL_LINUX_OPTUTILS_H
+
+#include <assert.h>
+
+#include "c.h"
+#include "nls.h"
+#include "cctype.h"
+
+static inline const char *option_to_longopt(int c, const struct option *opts)
+{
+ const struct option *o;
+
+ assert(!(opts == NULL));
+ for (o = opts; o->name; o++)
+ if (o->val == c)
+ return o->name;
+ return NULL;
+}
+
+#ifndef OPTUTILS_EXIT_CODE
+# define OPTUTILS_EXIT_CODE EXIT_FAILURE
+#endif
+
+/*
+ * Check collisions between options.
+ *
+ * The conflicts between options are described in ul_excl_t array. The
+ * array contains groups of mutually exclusive options. For example
+ *
+ * static const ul_excl_t excl[] = {
+ * { 'Z','b','c' }, // first group
+ * { 'b','x' }, // second group
+ * { 0 }
+ * };
+ *
+ * int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ *
+ * while ((c = getopt_long(argc, argv, "Zbcx", longopts, NULL)) != -1) {
+ *
+ * err_exclusive_options(c, longopts, excl, excl_st);
+ *
+ * switch (c) {
+ * case 'Z':
+ * ....
+ * }
+ * }
+ *
+ * The array excl[] defines two groups of the mutually exclusive options. The
+ * option '-b' is in the both groups.
+ *
+ * Note that the options in the group have to be in ASCII order (ABC..abc..) and
+ * groups have to be also in ASCII order.
+ *
+ * The maximal number of the options in the group is 15 (size of the array is
+ * 16, last is zero).
+ *
+ * The current status of options is stored in excl_st array. The size of the array
+ * must be the same as number of the groups in the ul_excl_t array.
+ *
+ * If you're unsure then see sys-utils/mount.c or misc-utils/findmnt.c.
+ */
+#define UL_EXCL_STATUS_INIT { 0 }
+typedef int ul_excl_t[16];
+
+static inline void err_exclusive_options(
+ int c,
+ const struct option *opts,
+ const ul_excl_t *excl,
+ int *status)
+{
+ int e;
+
+ for (e = 0; excl[e][0] && excl[e][0] <= c; e++) {
+ const int *op = excl[e];
+
+ for (; *op && *op <= c; op++) {
+ if (*op != c)
+ continue;
+ if (status[e] == 0)
+ status[e] = c;
+ else if (status[e] != c) {
+ size_t ct = 0;
+
+ fprintf(stderr, _("%s: mutually exclusive "
+ "arguments:"),
+ program_invocation_short_name);
+
+ for (op = excl[e];
+ ct + 1 < ARRAY_SIZE(excl[0]) && *op;
+ op++, ct++) {
+ const char *n = option_to_longopt(*op, opts);
+ if (n)
+ fprintf(stderr, " --%s", n);
+ else if (c_isgraph(*op))
+ fprintf(stderr, " -%c", *op);
+ }
+ fputc('\n', stderr);
+ exit(OPTUTILS_EXIT_CODE);
+ }
+ break;
+ }
+ }
+}
+
+#endif
+
diff --git a/utils/include/pager.h b/utils/include/pager.h
new file mode 100644
index 0000000..0a7140d
--- /dev/null
+++ b/utils/include/pager.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_PAGER
+#define UTIL_LINUX_PAGER
+
+void pager_redirect(void);
+
+void pager_open(void);
+void pager_close(void);
+
+#endif
diff --git a/utils/include/partx.h b/utils/include/partx.h
new file mode 100644
index 0000000..618a0a4
--- /dev/null
+++ b/utils/include/partx.h
@@ -0,0 +1,63 @@
+#ifndef UTIL_LINUX_PARTX_H
+#define UTIL_LINUX_PARTX_H
+
+#include <sys/ioctl.h>
+#include <linux/blkpg.h>
+#include <stdint.h>
+
+#ifndef BLKPG_ADD_PARTITION
+# define BLKPG_ADD_PARTITION 1
+#endif
+
+#ifndef BLKPG_DEL_PARTITION
+# define BLKPG_DEL_PARTITION 2
+#endif
+
+#ifndef BLKPG_RESIZE_PARTITION
+# define BLKPG_RESIZE_PARTITION 3 /* since Linux 3.6 */
+#endif
+
+
+#define INIT_BLKPG_PARTITION(_partno, _start, _size) { \
+ .pno = (_partno), \
+ .start = (_start) << 9, \
+ .length = (_size) << 9, \
+ .devname[0] = 0, \
+ .volname[0] = 0 \
+}
+
+#define INIT_BLKPG_ARG(_action, _part) { \
+ .op = (_action), \
+ .flags = 0, \
+ .datalen = sizeof(*(_part)), \
+ .data = (_part) \
+}
+
+
+static inline int partx_del_partition(int fd, unsigned int partno)
+{
+ struct blkpg_partition p = INIT_BLKPG_PARTITION(partno, 0, 0);
+ struct blkpg_ioctl_arg a = INIT_BLKPG_ARG(BLKPG_DEL_PARTITION, &p);
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+static inline int partx_add_partition(int fd, int partno,
+ uint64_t start, uint64_t size)
+{
+ struct blkpg_partition p = INIT_BLKPG_PARTITION(partno, start, size);
+ struct blkpg_ioctl_arg a = INIT_BLKPG_ARG(BLKPG_ADD_PARTITION, &p);
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+static inline int partx_resize_partition(int fd, int partno,
+ uint64_t start, uint64_t size)
+{
+ struct blkpg_partition p = INIT_BLKPG_PARTITION(partno, start, size);
+ struct blkpg_ioctl_arg a = INIT_BLKPG_ARG(BLKPG_RESIZE_PARTITION, &p);
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+#endif /* UTIL_LINUX_PARTX_H */
diff --git a/utils/include/path.h b/utils/include/path.h
new file mode 100644
index 0000000..2a4f80e
--- /dev/null
+++ b/utils/include/path.h
@@ -0,0 +1,135 @@
+#ifndef UTIL_LINUX_PATH_H
+#define UTIL_LINUX_PATH_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "c.h"
+
+struct path_cxt {
+ int dir_fd;
+ char *dir_path;
+
+ int refcount;
+
+ char *prefix;
+ char path_buffer[PATH_MAX];
+
+ void *dialect;
+ void (*free_dialect)(struct path_cxt *);
+ int (*redirect_on_enoent)(struct path_cxt *, const char *, int *);
+};
+
+struct path_cxt *ul_new_path(const char *dir, ...);
+void ul_unref_path(struct path_cxt *pc);
+void ul_ref_path(struct path_cxt *pc);
+
+void ul_path_init_debug(void);
+
+int ul_path_set_prefix(struct path_cxt *pc, const char *prefix);
+const char *ul_path_get_prefix(struct path_cxt *pc);
+
+int ul_path_set_dir(struct path_cxt *pc, const char *dir);
+const char *ul_path_get_dir(struct path_cxt *pc);
+
+int ul_path_set_dialect(struct path_cxt *pc, void *data, void free_data(struct path_cxt *));
+void *ul_path_get_dialect(struct path_cxt *pc);
+
+int ul_path_set_enoent_redirect(struct path_cxt *pc, int (*func)(struct path_cxt *, const char *, int *));
+int ul_path_get_dirfd(struct path_cxt *pc);
+void ul_path_close_dirfd(struct path_cxt *pc);
+int ul_path_isopen_dirfd(struct path_cxt *pc);
+int ul_path_is_accessible(struct path_cxt *pc);
+
+char *ul_path_get_abspath(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+int ul_path_stat(struct path_cxt *pc, struct stat *sb, const char *path);
+int ul_path_access(struct path_cxt *pc, int mode, const char *path);
+int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_open(struct path_cxt *pc, int flags, const char *path);
+int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap);
+
+FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path);
+FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap);
+
+DIR *ul_path_opendir(struct path_cxt *pc, const char *path);
+DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap);
+DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+
+ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path);
+ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path);
+int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap);
+int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+int ul_path_read_string(struct path_cxt *pc, char **str, const char *path);
+int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path);
+int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...);
+int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
+ __attribute__ ((__format__ (__scanf__, 4, 5)));
+
+int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path);
+int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_read_u32(struct path_cxt *pc, uint32_t *res, const char *path);
+int ul_path_readf_u32(struct path_cxt *pc, uint32_t *res, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_read_s32(struct path_cxt *pc, int32_t *res, const char *path);
+int ul_path_readf_s32(struct path_cxt *pc, int32_t *res, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path);
+int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path);
+int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path);
+int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path);
+int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path);
+int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+int ul_path_count_dirents(struct path_cxt *pc, const char *path);
+int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+
+FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode);
+
+
+#ifdef HAVE_CPU_SET_T
+# include "cpuset.h"
+int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+#endif /* HAVE_CPU_SET_T */
+#endif /* UTIL_LINUX_PATH_H */
diff --git a/utils/include/pathnames.h b/utils/include/pathnames.h
new file mode 100644
index 0000000..8f62337
--- /dev/null
+++ b/utils/include/pathnames.h
@@ -0,0 +1,210 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+/* used by kernel in /proc (e.g. /proc/swaps) for deleted files */
+#define PATH_DELETED_SUFFIX " (deleted)"
+
+/* DEFPATHs from <paths.h> don't include /usr/local */
+#undef _PATH_DEFPATH
+
+#ifdef USE_USRDIR_PATHS_ONLY
+# define _PATH_DEFPATH "/usr/local/bin:/usr/bin"
+#else
+# define _PATH_DEFPATH "/usr/local/bin:/bin:/usr/bin"
+#endif
+
+#undef _PATH_DEFPATH_ROOT
+
+#ifdef USE_USRDIR_PATHS_ONLY
+# define _PATH_DEFPATH_ROOT "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
+#else
+# define _PATH_DEFPATH_ROOT "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+#endif
+
+#define _PATH_HUSHLOGIN ".hushlogin"
+#define _PATH_HUSHLOGINS "/etc/hushlogins"
+
+#define _PATH_NOLOGIN_TXT "/etc/nologin.txt"
+
+#ifndef _PATH_MAILDIR
+# define _PATH_MAILDIR "/var/spool/mail"
+#endif
+#define _PATH_MOTDFILE "/usr/share/misc/motd:/run/motd:/etc/motd"
+#ifndef _PATH_NOLOGIN
+# define _PATH_NOLOGIN "/etc/nologin"
+#endif
+#define _PATH_VAR_NOLOGIN "/var/run/nologin"
+
+#ifndef _PATH_LOGIN
+# define _PATH_LOGIN "/bin/login"
+#endif
+#define _PATH_SHUTDOWN "/sbin/shutdown"
+#define _PATH_POWEROFF "/sbin/poweroff"
+
+#define _PATH_TERMCOLORS_DIRNAME "terminal-colors.d"
+#define _PATH_TERMCOLORS_DIR "/etc/" _PATH_TERMCOLORS_DIRNAME
+
+/* login paths */
+#define _PATH_PASSWD "/etc/passwd"
+#define _PATH_GSHADOW "/etc/gshadow"
+#define _PATH_GROUP "/etc/group"
+#define _PATH_SHADOW_PASSWD "/etc/shadow"
+#define _PATH_SHELLS "/etc/shells"
+
+#ifndef _PATH_TMP
+# define _PATH_TMP "/tmp/"
+#endif
+
+#ifndef _PATH_BTMP
+# define _PATH_BTMP "/var/log/btmp"
+#endif
+
+#define _PATH_ISSUE_FILENAME "issue"
+#define _PATH_ISSUE_DIRNAME _PATH_ISSUE_FILENAME ".d"
+
+#define _PATH_ISSUE "/etc/" _PATH_ISSUE_FILENAME
+#define _PATH_ISSUEDIR "/etc/" _PATH_ISSUE_DIRNAME
+
+#define _PATH_OS_RELEASE_ETC "/etc/os-release"
+#define _PATH_OS_RELEASE_USR "/usr/lib/os-release"
+#define _PATH_NUMLOCK_ON _PATH_RUNSTATEDIR "/numlock-on"
+#define _PATH_LOGINDEFS "/etc/login.defs"
+
+/* misc paths */
+#define _PATH_WORDS "/usr/share/dict/words"
+#define _PATH_WORDS_ALT "/usr/share/dict/web2"
+
+/* mount paths */
+#define _PATH_FILESYSTEMS "/etc/filesystems"
+#define _PATH_PROC_SWAPS "/proc/swaps"
+#define _PATH_PROC_FILESYSTEMS "/proc/filesystems"
+#define _PATH_PROC_MOUNTS "/proc/mounts"
+#define _PATH_PROC_PARTITIONS "/proc/partitions"
+#define _PATH_PROC_DEVICES "/proc/devices"
+#define _PATH_PROC_MOUNTINFO "/proc/self/mountinfo"
+#define _PATH_PROC_LOCKS "/proc/locks"
+#define _PATH_PROC_CDROMINFO "/proc/sys/dev/cdrom/info"
+
+#define _PATH_PROC_UIDMAP "/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP "/proc/self/gid_map"
+#define _PATH_PROC_SETGROUPS "/proc/self/setgroups"
+
+#define _PATH_PROC_FDDIR "/proc/self/fd"
+
+#define _PATH_PROC_ATTR_CURRENT "/proc/self/attr/current"
+#define _PATH_PROC_ATTR_EXEC "/proc/self/attr/exec"
+#define _PATH_PROC_CAPLASTCAP "/proc/sys/kernel/cap_last_cap"
+
+
+#define _PATH_SYS_BLOCK "/sys/block"
+#define _PATH_SYS_DEVBLOCK "/sys/dev/block"
+#define _PATH_SYS_DEVCHAR "/sys/dev/char"
+#define _PATH_SYS_CLASS "/sys/class"
+#define _PATH_SYS_SCSI "/sys/bus/scsi"
+
+#define _PATH_SYS_SELINUX "/sys/fs/selinux"
+#define _PATH_SYS_APPARMOR "/sys/kernel/security/apparmor"
+
+#ifndef _PATH_MOUNTED
+# ifdef MOUNTED /* deprecated */
+# define _PATH_MOUNTED MOUNTED
+# else
+# define _PATH_MOUNTED "/etc/mtab"
+# endif
+#endif
+
+#ifndef _PATH_MNTTAB
+# ifdef MNTTAB /* deprecated */
+# define _PATH_MNTTAB MNTTAB
+# else
+# define _PATH_MNTTAB "/etc/fstab"
+# endif
+#endif
+
+#ifndef _PATH_DEV
+ /*
+ * The tailing '/' in _PATH_DEV is there for compatibility with libc.
+ */
+# define _PATH_DEV "/dev/"
+#endif
+
+#define _PATH_DEV_MAPPER "/dev/mapper"
+
+#define _PATH_DEV_MEM "/dev/mem"
+
+#define _PATH_DEV_LOOP "/dev/loop"
+#define _PATH_DEV_LOOPCTL "/dev/loop-control"
+
+/* udev paths */
+#define _PATH_DEV_BYLABEL "/dev/disk/by-label"
+#define _PATH_DEV_BYUUID "/dev/disk/by-uuid"
+#define _PATH_DEV_BYID "/dev/disk/by-id"
+#define _PATH_DEV_BYPATH "/dev/disk/by-path"
+#define _PATH_DEV_BYPARTLABEL "/dev/disk/by-partlabel"
+#define _PATH_DEV_BYPARTUUID "/dev/disk/by-partuuid"
+
+/* hwclock paths */
+#ifdef CONFIG_ADJTIME_PATH
+# define _PATH_ADJTIME CONFIG_ADJTIME_PATH
+#else
+# define _PATH_ADJTIME "/etc/adjtime"
+#endif
+
+#ifdef __ia64__
+# define _PATH_RTC_DEV "/dev/efirtc"
+#else
+# define _PATH_RTC_DEV "/dev/rtc0"
+#endif
+
+/* raw paths*/
+#define _PATH_RAWDEVDIR "/dev/raw/"
+#define _PATH_RAWDEVCTL _PATH_RAWDEVDIR "rawctl"
+/* deprecated */
+#define _PATH_RAWDEVCTL_OLD "/dev/rawctl"
+
+/* ipc paths */
+#define _PATH_PROC_SYSV_MSG "/proc/sysvipc/msg"
+#define _PATH_PROC_SYSV_SEM "/proc/sysvipc/sem"
+#define _PATH_PROC_SYSV_SHM "/proc/sysvipc/shm"
+#define _PATH_PROC_IPC_MSGMAX "/proc/sys/kernel/msgmax"
+#define _PATH_PROC_IPC_MSGMNB "/proc/sys/kernel/msgmnb"
+#define _PATH_PROC_IPC_MSGMNI "/proc/sys/kernel/msgmni"
+#define _PATH_PROC_IPC_SEM "/proc/sys/kernel/sem"
+#define _PATH_PROC_IPC_SHMALL "/proc/sys/kernel/shmall"
+#define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax"
+#define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni"
+
+/* irqtop paths */
+#define _PATH_PROC_INTERRUPTS "/proc/interrupts"
+#define _PATH_PROC_SOFTIRQS "/proc/softirqs"
+#define _PATH_PROC_UPTIME "/proc/uptime"
+
+/* kernel command line */
+#define _PATH_PROC_CMDLINE "/proc/cmdline"
+
+/* logger paths */
+#define _PATH_DEVLOG "/dev/log"
+
+/* ctrlaltdel paths */
+#define _PATH_PROC_CTRL_ALT_DEL "/proc/sys/kernel/ctrl-alt-del"
+
+/* lscpu paths */
+#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
+
+/* rfkill paths */
+#define _PATH_DEV_RFKILL "/dev/rfkill"
+#define _PATH_SYS_RFKILL "/sys/class/rfkill"
+
+#endif /* PATHNAMES_H */
diff --git a/utils/include/pidfd-utils.h b/utils/include/pidfd-utils.h
new file mode 100644
index 0000000..4a6c3a6
--- /dev/null
+++ b/utils/include/pidfd-utils.h
@@ -0,0 +1,28 @@
+#ifndef UTIL_LINUX_PIDFD_UTILS
+#define UTIL_LINUX_PIDFD_UTILS
+
+#if defined(__linux__)
+# include <sys/syscall.h>
+# if defined(SYS_pidfd_send_signal) && defined(SYS_pidfd_open)
+# include <sys/types.h>
+
+# ifndef HAVE_PIDFD_SEND_SIGNAL
+static inline int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
+ unsigned int flags)
+{
+ return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
+}
+# endif
+
+# ifndef HAVE_PIDFD_OPEN
+static inline int pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(SYS_pidfd_open, pid, flags);
+}
+# endif
+
+# define UL_HAVE_PIDFD 1
+
+# endif /* SYS_pidfd_send_signal */
+#endif /* __linux__ */
+#endif /* UTIL_LINUX_PIDFD_UTILS */
diff --git a/utils/include/plymouth-ctrl.h b/utils/include/plymouth-ctrl.h
new file mode 100644
index 0000000..b6f1299
--- /dev/null
+++ b/utils/include/plymouth-ctrl.h
@@ -0,0 +1,65 @@
+/*
+ * plymouth-ctrl.h Header file for communications with plymouthd
+ *
+ * Copyright (c) 2016 SUSE Linux GmbH, All rights reserved.
+ * Copyright (c) 2016 Werner Fink <werner@suse.de>
+ *
+ * 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, 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 (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Author: Werner Fink <werner@suse.de>
+ */
+
+/*
+ * Taken from plymouth 0.9.0 src/ply-boot-protocol.h
+ */
+
+#ifndef UTIL_LINUX_PLYMOUTH_CTRL_H
+#define UTIL_LINUX_PLYMOUTH_CTRL_H
+
+#define PLYMOUTH_SOCKET_PATH "\0/org/freedesktop/plymouthd"
+#define ANSWER_TYP '\x2'
+#define ANSWER_ENQ '\x5'
+#define ANSWER_ACK '\x6'
+#define ANSWER_MLT '\t'
+#define ANSWER_NCK '\x15'
+
+#define MAGIC_PRG_STOP 'A'
+#define MAGIC_PRG_CONT 'a'
+#define MAGIC_UPDATE 'U'
+#define MAGIC_SYS_UPDATE 'u'
+#define MAGIC_SYS_INIT 'S'
+#define MAGIC_DEACTIVATE 'D'
+#define MAGIC_REACTIVATE 'r'
+#define MAGIC_SHOW_SPLASH '$'
+#define MAGIC_HIDE_SPLASH 'H'
+#define MAGIC_CHMOD 'C'
+#define MAGIC_CHROOT 'R'
+#define MAGIC_ACTIVE_VT 'V'
+#define MAGIC_QUESTION 'W'
+#define MAGIC_SHOW_MSG 'M'
+#define MAGIC_HIDE_MSG 'm'
+#define MAGIC_KEYSTROKE 'K'
+#define MAGIC_KEYSTROKE_RM 'L'
+#define MAGIC_PING 'P'
+#define MAGIC_QUIT 'Q'
+#define MAGIC_CACHED_PWD 'c'
+#define MAGIC_ASK_PWD '*'
+#define MAGIC_DETAILS '!'
+
+#define PLYMOUTH_TERMIOS_FLAGS_DELAY 30
+extern int plymouth_command(int cmd, ...);
+
+#endif /* UTIL_LINUX_PLYMOUTH_CTRL_H */
diff --git a/utils/include/procutils.h b/utils/include/procutils.h
new file mode 100644
index 0000000..9f8dd76
--- /dev/null
+++ b/utils/include/procutils.h
@@ -0,0 +1,34 @@
+#ifndef UTIL_LINUX_PROCUTILS
+#define UTIL_LINUX_PROCUTILS
+
+#include <dirent.h>
+
+struct proc_tasks {
+ DIR *dir;
+};
+
+extern struct proc_tasks *proc_open_tasks(pid_t pid);
+extern void proc_close_tasks(struct proc_tasks *tasks);
+extern int proc_next_tid(struct proc_tasks *tasks, pid_t *tid);
+
+struct proc_processes {
+ DIR *dir;
+
+ const char *fltr_name;
+ uid_t fltr_uid;
+
+ unsigned int has_fltr_name : 1,
+ has_fltr_uid : 1;
+};
+
+extern struct proc_processes *proc_open_processes(void);
+extern void proc_close_processes(struct proc_processes *ps);
+
+extern void proc_processes_filter_by_name(struct proc_processes *ps, const char *name);
+extern void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid);
+extern int proc_next_pid(struct proc_processes *ps, pid_t *pid);
+
+extern char *proc_get_command(pid_t pid);
+extern char *proc_get_command_name(pid_t pid);
+
+#endif /* UTIL_LINUX_PROCUTILS */
diff --git a/utils/include/pt-bsd.h b/utils/include/pt-bsd.h
new file mode 100644
index 0000000..9bf47a5
--- /dev/null
+++ b/utils/include/pt-bsd.h
@@ -0,0 +1,156 @@
+#ifndef UTIL_LINUX_PT_BSD_H
+#define UTIL_LINUX_PT_BSD_H
+
+#define BSD_MAXPARTITIONS 16
+#define BSD_FS_UNUSED 0
+
+#ifndef BSD_DISKMAGIC
+# define BSD_DISKMAGIC ((uint32_t) 0x82564557)
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__alpha__) || defined (__powerpc__) || \
+ defined (__ia64__) || defined (__hppa__)
+# define BSD_LABELSECTOR 0
+# define BSD_LABELOFFSET 64
+#else
+# define BSD_LABELSECTOR 1
+# define BSD_LABELOFFSET 0
+#endif
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+struct bsd_disklabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+
+ /* disk geometry: */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+ uint32_t d_drivedata[5]; /* drive-type specific information */
+ uint32_t d_spare[5]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+
+ struct bsd_partition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* filesystem basic fragment size */
+ uint8_t p_fstype; /* filesystem type, see below */
+ uint8_t p_frag; /* filesystem fragments per block */
+ uint16_t p_cpg; /* filesystem cylinders per group */
+ } __attribute__((packed)) d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+} __attribute__((packed));
+
+
+/* d_type values: */
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED 0 /* unused */
+#define BSD_FS_SWAP 1 /* swap */
+#define BSD_FS_V6 2 /* Sixth Edition */
+#define BSD_FS_V7 3 /* Seventh Edition */
+#define BSD_FS_SYSV 4 /* System V */
+#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS BSD_FS_ISO9660
+#define BSD_FS_BOOT 13 /* partition contains bootstrap */
+#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
+#define BSD_FS_HFS 15 /* Macintosh HFS */
+#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2 8 /* ext2 file system */
+#else
+#define BSD_FS_MSDOS 8 /* MS-DOS file system */
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01 /* removable media */
+#define BSD_D_ECC 0x02 /* supports ECC */
+#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
+#define BSD_D_RAMDISK 0x08 /* disk emulator */
+#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
+#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
+
+#endif /* UTIL_LINUX_PT_BSD_H */
diff --git a/utils/include/pt-gpt-partnames.h b/utils/include/pt-gpt-partnames.h
new file mode 100644
index 0000000..604f2c6
--- /dev/null
+++ b/utils/include/pt-gpt-partnames.h
@@ -0,0 +1,160 @@
+
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * Probably the most complete list of the GUIDs are at:
+ * https://wikipedia.org/wiki/GUID_Partition_Table
+ *
+ * The macro DEF_GUID() has to be defined in your code according to array where
+ * you want to include this file.
+ */
+
+/* Generic OS */
+DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")),
+
+DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")),
+DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")),
+
+/* Hah!IdontneedEFI */
+DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")),
+
+/* NIH syndrome */
+DEF_GUID("F4019732-066E-4E12-8273-346C5641494F", N_("Sony boot partition")),
+DEF_GUID("BFBFAFE7-A34F-448A-9A5B-6213EB736C22", N_("Lenovo boot partition")),
+
+/* PowerPC reference platform boot partition */
+DEF_GUID("9E1A2D38-C612-4316-AA26-8B49521E5A8B", N_("PowerPC PReP boot")),
+
+/* Open Network Install Environment */
+DEF_GUID("7412F7D5-A156-4B13-81DC-867174929325", N_("ONIE boot")),
+DEF_GUID("D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", N_("ONIE config")),
+
+/* Windows */
+DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")),
+DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")),
+DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")),
+DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")),
+DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")),
+DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")),
+DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")),
+
+/* HP-UX */
+DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")),
+DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")),
+
+/* Linux (https://systemd.io/DISCOVERABLE_PARTITIONS/) */
+DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")),
+DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")),
+DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")),
+DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")),
+DEF_GUID("69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", N_("Linux root (ARM)")),
+DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")),
+DEF_GUID("B921B045-1DF0-41C3-AF44-4C6F280D3FAE", N_("Linux root (ARM-64)")),
+DEF_GUID("993D8D3D-F80E-4225-855A-9DAF8ED7EA97", N_("Linux root (IA-64)")),
+DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")),
+DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")),
+DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
+DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
+DEF_GUID("4D21B016-B534-45C2-A9FB-5C16E091FD2D", N_("Linux variable data")),
+DEF_GUID("7EC6F557-3BC5-4ACA-B293-16EF5DF639D1", N_("Linux temporary data")),
+DEF_GUID("D13C5D3B-B5D1-422A-B29F-9454FDC89D76", N_("Linux root verity (x86)")),
+DEF_GUID("7386CDF2-203C-47A9-A498-F2ECCE45A2D6", N_("Linux root verity (ARM)")),
+DEF_GUID("2C7357ED-EBD2-46D9-AEC1-23D437EC2BF5", N_("Linux root verity (x86-64)")),
+DEF_GUID("DF3300CE-D69F-4C92-978C-9BFB0F38D820", N_("Linux root verity (ARM-64)")),
+DEF_GUID("86ED10D5-B607-45BB-8957-D350F23D0571", N_("Linux root verity (IA-64)")),
+/* ... too crazy, ignore for now:
+DEF_GUID("7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", N_("Linux plain dm-crypt")),
+DEF_GUID("CA7D7CCB-63ED-4C53-861C-1742536059CC", N_("Linux LUKS")),
+*/
+/* Linux https://systemd.io/BOOT_LOADER_SPECIFICATION/ */
+DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")),
+
+/* Linux https://systemd.io/HOME_DIRECTORY/ */
+DEF_GUID("773f91ef-66d4-49b5-bd83-d683bf40ad16", N_("Linux user's home")),
+
+/* FreeBSD */
+DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")),
+DEF_GUID("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", N_("FreeBSD boot")),
+DEF_GUID("516E7CB5-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD swap")),
+DEF_GUID("516E7CB6-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD UFS")),
+DEF_GUID("516E7CBA-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD ZFS")),
+DEF_GUID("516E7CB8-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD Vinum")),
+
+/* Apple OSX */
+DEF_GUID("48465300-0000-11AA-AA11-00306543ECAC", N_("Apple HFS/HFS+")),
+DEF_GUID("7C3457EF-0000-11AA-AA11-00306543ECAC", N_("Apple APFS")),
+DEF_GUID("55465300-0000-11AA-AA11-00306543ECAC", N_("Apple UFS")),
+DEF_GUID("52414944-0000-11AA-AA11-00306543ECAC", N_("Apple RAID")),
+DEF_GUID("52414944-5F4F-11AA-AA11-00306543ECAC", N_("Apple RAID offline")),
+DEF_GUID("426F6F74-0000-11AA-AA11-00306543ECAC", N_("Apple boot")),
+DEF_GUID("4C616265-6C00-11AA-AA11-00306543ECAC", N_("Apple label")),
+DEF_GUID("5265636F-7665-11AA-AA11-00306543ECAC", N_("Apple TV recovery")),
+DEF_GUID("53746F72-6167-11AA-AA11-00306543ECAC", N_("Apple Core storage")),
+
+/* Solaris */
+DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")),
+DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")),
+/* same as Apple ZFS */
+DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")),
+DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")),
+DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")),
+DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")),
+DEF_GUID("6A90BA39-1DD2-11B2-99A6-080020736631", N_("Solaris /home")),
+DEF_GUID("6A9283A5-1DD2-11B2-99A6-080020736631", N_("Solaris alternate sector")),
+DEF_GUID("6A945A3B-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 1")),
+DEF_GUID("6A9630D1-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 2")),
+DEF_GUID("6A980767-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 3")),
+DEF_GUID("6A96237F-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 4")),
+DEF_GUID("6A8D2AC7-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 5")),
+
+/* NetBSD */
+DEF_GUID("49F48D32-B10E-11DC-B99B-0019D1879648", N_("NetBSD swap")),
+DEF_GUID("49F48D5A-B10E-11DC-B99B-0019D1879648", N_("NetBSD FFS")),
+DEF_GUID("49F48D82-B10E-11DC-B99B-0019D1879648", N_("NetBSD LFS")),
+DEF_GUID("2DB519C4-B10E-11DC-B99B-0019D1879648", N_("NetBSD concatenated")),
+DEF_GUID("2DB519EC-B10E-11DC-B99B-0019D1879648", N_("NetBSD encrypted")),
+DEF_GUID("49F48DAA-B10E-11DC-B99B-0019D1879648", N_("NetBSD RAID")),
+
+/* ChromeOS */
+DEF_GUID("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", N_("ChromeOS kernel")),
+DEF_GUID("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", N_("ChromeOS root fs")),
+DEF_GUID("2E0A753D-9E48-43B0-8337-B15192CB1B5E", N_("ChromeOS reserved")),
+
+/* MidnightBSD */
+DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")),
+DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")),
+DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")),
+DEF_GUID("0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")),
+DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")),
+DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")),
+
+/* Ceph */
+DEF_GUID("45B0969E-9B03-4F30-B4C6-B4B80CEFF106", N_("Ceph Journal")),
+DEF_GUID("45B0969E-9B03-4F30-B4C6-5EC00CEFF106", N_("Ceph Encrypted Journal")),
+DEF_GUID("4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", N_("Ceph OSD")),
+DEF_GUID("4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", N_("Ceph crypt OSD")),
+DEF_GUID("89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", N_("Ceph disk in creation")),
+DEF_GUID("89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", N_("Ceph crypt disk in creation")),
+
+/* VMware */
+DEF_GUID("AA31E02A-400F-11DB-9590-000C2911D1B8", N_("VMware VMFS")),
+DEF_GUID("9D275380-40AD-11DB-BF97-000C2911D1B8", N_("VMware Diagnostic")),
+DEF_GUID("381CFCCC-7288-11E0-92EE-000C2911D0B2", N_("VMware Virtual SAN")),
+DEF_GUID("77719A0C-A4A0-11E3-A47E-000C29745A24", N_("VMware Virsto")),
+DEF_GUID("9198EFFC-31C0-11DB-8F78-000C2911D1B8", N_("VMware Reserved")),
+
+/* OpenBSD */
+DEF_GUID("824CC7A0-36A8-11E3-890A-952519AD3F61", N_("OpenBSD data")),
+
+/* QNX */
+DEF_GUID("CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", N_("QNX6 file system")),
+
+/* Plan 9 */
+DEF_GUID("C91818F9-8025-47AF-89D2-F030D7000C2C", N_("Plan 9 partition")),
+
+/* HiFive Unleased bootloaders */
+DEF_GUID("5B193300-FC78-40CD-8002-E86C45580B47", N_("HiFive Unleashed FSBL")),
+DEF_GUID("2E54B353-1271-4842-806F-E436D6AF6985", N_("HiFive Unleashed BBL")),
diff --git a/utils/include/pt-mbr-partnames.h b/utils/include/pt-mbr-partnames.h
new file mode 100644
index 0000000..19a3450
--- /dev/null
+++ b/utils/include/pt-mbr-partnames.h
@@ -0,0 +1,112 @@
+ {0x00, N_("Empty")},
+ {0x01, N_("FAT12")},
+ {0x02, N_("XENIX root")},
+ {0x03, N_("XENIX usr")},
+ {0x04, N_("FAT16 <32M")},
+ {0x05, N_("Extended")}, /* DOS 3.3+ extended partition */
+ {0x06, N_("FAT16")}, /* DOS 16-bit >=32M */
+ {0x07, N_("HPFS/NTFS/exFAT")}, /* OS/2 IFS, eg, HPFS or NTFS or QNX or exFAT */
+ {0x08, N_("AIX")}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ {0x09, N_("AIX bootable")}, /* AIX data or Coherent */
+ {0x0a, N_("OS/2 Boot Manager")},/* OS/2 Boot Manager */
+ {0x0b, N_("W95 FAT32")},
+ {0x0c, N_("W95 FAT32 (LBA)")},/* LBA really is `Extended Int 13h' */
+ {0x0e, N_("W95 FAT16 (LBA)")},
+ {0x0f, N_("W95 Ext'd (LBA)")},
+ {0x10, N_("OPUS")},
+ {0x11, N_("Hidden FAT12")},
+ {0x12, N_("Compaq diagnostics")},
+ {0x14, N_("Hidden FAT16 <32M")},
+ {0x16, N_("Hidden FAT16")},
+ {0x17, N_("Hidden HPFS/NTFS")},
+ {0x18, N_("AST SmartSleep")},
+ {0x1b, N_("Hidden W95 FAT32")},
+ {0x1c, N_("Hidden W95 FAT32 (LBA)")},
+ {0x1e, N_("Hidden W95 FAT16 (LBA)")},
+ {0x24, N_("NEC DOS")},
+ {0x27, N_("Hidden NTFS WinRE")},
+ {0x39, N_("Plan 9")},
+ {0x3c, N_("PartitionMagic recovery")},
+ {0x40, N_("Venix 80286")},
+ {0x41, N_("PPC PReP Boot")},
+ {0x42, N_("SFS")},
+ {0x4d, N_("QNX4.x")},
+ {0x4e, N_("QNX4.x 2nd part")},
+ {0x4f, N_("QNX4.x 3rd part")},
+ {0x50, N_("OnTrack DM")},
+ {0x51, N_("OnTrack DM6 Aux1")}, /* (or Novell) */
+ {0x52, N_("CP/M")}, /* CP/M or Microport SysV/AT */
+ {0x53, N_("OnTrack DM6 Aux3")},
+ {0x54, N_("OnTrackDM6")},
+ {0x55, N_("EZ-Drive")},
+ {0x56, N_("Golden Bow")},
+ {0x5c, N_("Priam Edisk")},
+ {0x61, N_("SpeedStor")},
+ {0x63, N_("GNU HURD or SysV")}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ {0x64, N_("Novell Netware 286")},
+ {0x65, N_("Novell Netware 386")},
+ {0x70, N_("DiskSecure Multi-Boot")},
+ {0x75, N_("PC/IX")},
+ {0x80, N_("Old Minix")}, /* Minix 1.4a and earlier */
+ {0x81, N_("Minix / old Linux")},/* Minix 1.4b and later */
+ {0x82, N_("Linux swap / Solaris")},
+ {0x83, N_("Linux")},
+ {0x84, N_("OS/2 hidden or Intel hibernation")},/* OS/2 hidden C: drive,
+ hibernation type Microsoft APM
+ or hibernation Intel Rapid Start */
+ {0x85, N_("Linux extended")},
+ {0x86, N_("NTFS volume set")},
+ {0x87, N_("NTFS volume set")},
+ {0x88, N_("Linux plaintext")},
+ {0x8e, N_("Linux LVM")},
+ {0x93, N_("Amoeba")},
+ {0x94, N_("Amoeba BBT")}, /* (bad block table) */
+ {0x9f, N_("BSD/OS")}, /* BSDI */
+ {0xa0, N_("IBM Thinkpad hibernation")},
+ {0xa5, N_("FreeBSD")}, /* various BSD flavours */
+ {0xa6, N_("OpenBSD")},
+ {0xa7, N_("NeXTSTEP")},
+ {0xa8, N_("Darwin UFS")},
+ {0xa9, N_("NetBSD")},
+ {0xab, N_("Darwin boot")},
+ {0xaf, N_("HFS / HFS+")},
+ {0xb7, N_("BSDI fs")},
+ {0xb8, N_("BSDI swap")},
+ {0xbb, N_("Boot Wizard hidden")},
+ {0xbc, N_("Acronis FAT32 LBA")},/* hidden (+0xb0) Acronis Secure Zone (backup software) */
+ {0xbe, N_("Solaris boot")},
+ {0xbf, N_("Solaris")},
+ {0xc1, N_("DRDOS/sec (FAT-12)")},
+ {0xc4, N_("DRDOS/sec (FAT-16 < 32M)")},
+ {0xc6, N_("DRDOS/sec (FAT-16)")},
+ {0xc7, N_("Syrinx")},
+ {0xda, N_("Non-FS data")},
+ {0xdb, N_("CP/M / CTOS / ...")},/* CP/M or Concurrent CP/M or
+ Concurrent DOS or CTOS */
+ {0xde, N_("Dell Utility")}, /* Dell PowerEdge Server utilities */
+ {0xdf, N_("BootIt")}, /* BootIt EMBRM */
+ {0xe1, N_("DOS access")}, /* DOS access or SpeedStor 12-bit FAT
+ extended partition */
+ {0xe3, N_("DOS R/O")}, /* DOS R/O or SpeedStor */
+ {0xe4, N_("SpeedStor")}, /* SpeedStor 16-bit FAT extended
+ partition < 1024 cyl. */
+
+ /* Linux https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ */
+ {0xea, N_("Linux extended boot")},
+
+ {0xeb, N_("BeOS fs")},
+ {0xee, N_("GPT")}, /* Intel EFI GUID Partition Table */
+ {0xef, N_("EFI (FAT-12/16/32)")},/* Intel EFI System Partition */
+ {0xf0, N_("Linux/PA-RISC boot")},/* Linux/PA-RISC boot loader */
+ {0xf1, N_("SpeedStor")},
+ {0xf4, N_("SpeedStor")}, /* SpeedStor large partition */
+ {0xf2, N_("DOS secondary")}, /* DOS 3.3+ secondary */
+ {0xfb, N_("VMware VMFS")},
+ {0xfc, N_("VMware VMKCORE")}, /* VMware kernel dump partition */
+ {0xfd, N_("Linux raid autodetect")},/* Linux raid partition with
+ autodetect using persistent
+ superblock */
+ {0xfe, N_("LANstep")}, /* SpeedStor >1024 cyl. or LANstep */
+ {0xff, N_("BBT")}, /* Xenix Bad Block Table */
+
+ { 0, NULL }
diff --git a/utils/include/pt-mbr.h b/utils/include/pt-mbr.h
new file mode 100644
index 0000000..1a38246
--- /dev/null
+++ b/utils/include/pt-mbr.h
@@ -0,0 +1,187 @@
+#ifndef UTIL_LINUX_PT_MBR_H
+#define UTIL_LINUX_PT_MBR_H
+
+#include <assert.h>
+
+struct dos_partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char bh, bs, bc; /* begin CHS */
+ unsigned char sys_ind;
+ unsigned char eh, es, ec; /* end CHS */
+ unsigned char start_sect[4];
+ unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define MBR_PT_OFFSET 0x1be
+#define MBR_PT_BOOTBITS_SIZE 440
+
+static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
+{
+ return (struct dos_partition *)
+ (mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
+}
+
+/* assemble badly aligned little endian integer */
+static inline uint32_t __dos_assemble_4le(const unsigned char *p)
+{
+ uint32_t last_byte = p[3];
+
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (last_byte << 24);
+}
+
+static inline void __dos_store_4le(unsigned char *p, unsigned int val)
+{
+ assert(!(p == NULL));
+ p[0] = (val & 0xff);
+ p[1] = ((val >> 8) & 0xff);
+ p[2] = ((val >> 16) & 0xff);
+ p[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int dos_partition_get_start(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->start_sect[0]));
+}
+
+static inline void dos_partition_set_start(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->start_sect, n);
+}
+
+static inline unsigned int dos_partition_get_size(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->nr_sects[0]));
+}
+
+static inline void dos_partition_set_size(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->nr_sects, n);
+}
+
+static inline int mbr_is_valid_magic(const unsigned char *mbr)
+{
+ return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline void mbr_set_magic(unsigned char *b)
+{
+ b[510] = 0x55;
+ b[511] = 0xaa;
+}
+
+static inline unsigned int mbr_get_id(const unsigned char *mbr)
+{
+ return __dos_assemble_4le(&mbr[440]);
+}
+
+static inline void mbr_set_id(unsigned char *b, unsigned int id)
+{
+ __dos_store_4le(&b[440], id);
+}
+
+enum {
+ MBR_EMPTY_PARTITION = 0x00,
+ MBR_FAT12_PARTITION = 0x01,
+ MBR_XENIX_ROOT_PARTITION = 0x02,
+ MBR_XENIX_USR_PARTITION = 0x03,
+ MBR_FAT16_LESS32M_PARTITION = 0x04,
+ MBR_DOS_EXTENDED_PARTITION = 0x05,
+ MBR_FAT16_PARTITION = 0x06, /* DOS 16-bit >=32M */
+ MBR_HPFS_NTFS_PARTITION = 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+ MBR_AIX_PARTITION = 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ MBR_AIX_BOOTABLE_PARTITION = 0x09, /* AIX data or Coherent */
+ MBR_OS2_BOOTMNGR_PARTITION = 0x0a, /* OS/2 Boot Manager */
+ MBR_W95_FAT32_PARTITION = 0x0b,
+ MBR_W95_FAT32_LBA_PARTITION = 0x0c, /* LBA really is `Extended Int 13h' */
+ MBR_W95_FAT16_LBA_PARTITION = 0x0e,
+ MBR_W95_EXTENDED_PARTITION = 0x0f,
+ MBR_OPUS_PARTITION = 0x10,
+ MBR_HIDDEN_FAT12_PARTITION = 0x11,
+ MBR_COMPAQ_DIAGNOSTICS_PARTITION = 0x12,
+ MBR_HIDDEN_FAT16_L32M_PARTITION = 0x14,
+ MBR_HIDDEN_FAT16_PARTITION = 0x16,
+ MBR_HIDDEN_HPFS_NTFS_PARTITION = 0x17,
+ MBR_AST_SMARTSLEEP_PARTITION = 0x18,
+ MBR_HIDDEN_W95_FAT32_PARTITION = 0x1b,
+ MBR_HIDDEN_W95_FAT32LBA_PARTITION = 0x1c,
+ MBR_HIDDEN_W95_FAT16LBA_PARTITION = 0x1e,
+ MBR_NEC_DOS_PARTITION = 0x24,
+ MBR_PLAN9_PARTITION = 0x39,
+ MBR_PARTITIONMAGIC_PARTITION = 0x3c,
+ MBR_VENIX80286_PARTITION = 0x40,
+ MBR_PPC_PREP_BOOT_PARTITION = 0x41,
+ MBR_SFS_PARTITION = 0x42,
+ MBR_QNX_4X_PARTITION = 0x4d,
+ MBR_QNX_4X_2ND_PARTITION = 0x4e,
+ MBR_QNX_4X_3RD_PARTITION = 0x4f,
+ MBR_DM_PARTITION = 0x50,
+ MBR_DM6_AUX1_PARTITION = 0x51, /* (or Novell) */
+ MBR_CPM_PARTITION = 0x52, /* CP/M or Microport SysV/AT */
+ MBR_DM6_AUX3_PARTITION = 0x53,
+ MBR_DM6_PARTITION = 0x54,
+ MBR_EZ_DRIVE_PARTITION = 0x55,
+ MBR_GOLDEN_BOW_PARTITION = 0x56,
+ MBR_PRIAM_EDISK_PARTITION = 0x5c,
+ MBR_SPEEDSTOR_PARTITION = 0x61,
+ MBR_GNU_HURD_PARTITION = 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ MBR_UNIXWARE_PARTITION = MBR_GNU_HURD_PARTITION,
+ MBR_NETWARE_286_PARTITION = 0x64,
+ MBR_NETWARE_386_PARTITION = 0x65,
+ MBR_DISKSECURE_MULTIBOOT_PARTITION = 0x70,
+ MBR_PC_IX_PARTITION = 0x75,
+ MBR_OLD_MINIX_PARTITION = 0x80, /* Minix 1.4a and earlier */
+ MBR_MINIX_PARTITION = 0x81, /* Minix 1.4b and later */
+ MBR_LINUX_SWAP_PARTITION = 0x82,
+ MBR_SOLARIS_X86_PARTITION = MBR_LINUX_SWAP_PARTITION,
+ MBR_LINUX_DATA_PARTITION = 0x83,
+ MBR_OS2_HIDDEN_DRIVE_PARTITION = 0x84, /* also hibernation MS APM, Intel Rapid Start */
+ MBR_INTEL_HIBERNATION_PARTITION = MBR_OS2_HIDDEN_DRIVE_PARTITION,
+ MBR_LINUX_EXTENDED_PARTITION = 0x85,
+ MBR_NTFS_VOL_SET1_PARTITION = 0x86,
+ MBR_NTFS_VOL_SET2_PARTITION = 0x87,
+ MBR_LINUX_PLAINTEXT_PARTITION = 0x88,
+ MBR_LINUX_LVM_PARTITION = 0x8e,
+ MBR_AMOEBA_PARTITION = 0x93,
+ MBR_AMOEBA_BBT_PARTITION = 0x94, /* (bad block table) */
+ MBR_BSD_OS_PARTITION = 0x9f, /* BSDI */
+ MBR_THINKPAD_HIBERNATION_PARTITION = 0xa0,
+ MBR_FREEBSD_PARTITION = 0xa5, /* various BSD flavours */
+ MBR_OPENBSD_PARTITION = 0xa6,
+ MBR_NEXTSTEP_PARTITION = 0xa7,
+ MBR_DARWIN_UFS_PARTITION = 0xa8,
+ MBR_NETBSD_PARTITION = 0xa9,
+ MBR_DARWIN_BOOT_PARTITION = 0xab,
+ MBR_HFS_HFS_PARTITION = 0xaf,
+ MBR_BSDI_FS_PARTITION = 0xb7,
+ MBR_BSDI_SWAP_PARTITION = 0xb8,
+ MBR_BOOTWIZARD_HIDDEN_PARTITION = 0xbb,
+ MBR_ACRONIS_FAT32LBA_PARTITION = 0xbc, /* Acronis Secure Zone with ipl for loader F11.SYS */
+ MBR_SOLARIS_BOOT_PARTITION = 0xbe,
+ MBR_SOLARIS_PARTITION = 0xbf,
+ MBR_DRDOS_FAT12_PARTITION = 0xc1,
+ MBR_DRDOS_FAT16_L32M_PARTITION = 0xc4,
+ MBR_DRDOS_FAT16_PARTITION = 0xc6,
+ MBR_SYRINX_PARTITION = 0xc7,
+ MBR_NONFS_DATA_PARTITION = 0xda,
+ MBR_CPM_CTOS_PARTITION = 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+ MBR_DELL_UTILITY_PARTITION = 0xde, /* Dell PowerEdge Server utilities */
+ MBR_BOOTIT_PARTITION = 0xdf, /* BootIt EMBRM */
+ MBR_DOS_ACCESS_PARTITION = 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+ MBR_DOS_RO_PARTITION = 0xe3, /* DOS R/O or SpeedStor */
+ MBR_SPEEDSTOR_EXTENDED_PARTITION = 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+ MBR_RUFUS_EXTRA_PARTITION = 0xea, /* Rufus extra partition for alignment */
+ MBR_BEOS_FS_PARTITION = 0xeb,
+ MBR_GPT_PARTITION = 0xee, /* Intel EFI GUID Partition Table */
+ MBR_EFI_SYSTEM_PARTITION = 0xef, /* Intel EFI System Partition */
+ MBR_LINUX_PARISC_BOOT_PARTITION = 0xf0, /* Linux/PA-RISC boot loader */
+ MBR_SPEEDSTOR1_PARTITION = 0xf1,
+ MBR_SPEEDSTOR2_PARTITION = 0xf4, /* SpeedStor large partition */
+ MBR_DOS_SECONDARY_PARTITION = 0xf2, /* DOS 3.3+ secondary */
+ MBR_VMWARE_VMFS_PARTITION = 0xfb,
+ MBR_VMWARE_VMKCORE_PARTITION = 0xfc, /* VMware kernel dump partition */
+ MBR_LINUX_RAID_PARTITION = 0xfd, /* Linux raid partition with autodetect using persistent superblock */
+ MBR_LANSTEP_PARTITION = 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+ MBR_XENIX_BBT_PARTITION = 0xff, /* Xenix Bad Block Table */
+};
+
+#endif /* UTIL_LINUX_PT_MBR_H */
diff --git a/utils/include/pt-sgi.h b/utils/include/pt-sgi.h
new file mode 100644
index 0000000..6d512ee
--- /dev/null
+++ b/utils/include/pt-sgi.h
@@ -0,0 +1,115 @@
+#ifndef UTIL_LINUX_PT_SGI_H
+#define UTIL_LINUX_PT_SGI_H
+
+#include <stdint.h>
+
+#define SGI_LABEL_MAGIC 0x0be5a941
+
+#define SGI_MAXPARTITIONS 16
+#define SGI_MAXVOLUMES 15
+
+/* partition types */
+enum {
+ SGI_TYPE_VOLHDR = 0x00,
+ SGI_TYPE_TRKREPL = 0x01,
+ SGI_TYPE_SECREPL = 0x02,
+ SGI_TYPE_SWAP = 0x03,
+ SGI_TYPE_BSD = 0x04,
+ SGI_TYPE_SYSV = 0x05,
+ SGI_TYPE_ENTIRE_DISK = 0x06,
+ SGI_TYPE_EFS = 0x07,
+ SGI_TYPE_LVOL = 0x08,
+ SGI_TYPE_RLVOL = 0x09,
+ SGI_TYPE_XFS = 0x0a,
+ SGI_TYPE_XFSLOG = 0x0b,
+ SGI_TYPE_XLV = 0x0c,
+ SGI_TYPE_XVM = 0x0d
+};
+
+struct sgi_device_parameter {
+ unsigned char skew;
+ unsigned char gap1;
+ unsigned char gap2;
+ unsigned char sparecyl;
+
+ uint16_t pcylcount;
+ uint16_t head_vol0;
+ uint16_t ntrks; /* tracks in cyl 0 or vol 0 */
+
+ unsigned char cmd_tag_queue_depth;
+ unsigned char unused0;
+
+ uint16_t unused1;
+ uint16_t nsect; /* sectors/tracks in cyl 0 or vol 0 */
+ uint16_t bytes;
+ uint16_t ilfact;
+ uint32_t flags; /* SGI_DEVPARAM_* controller flags */
+ uint32_t datarate;
+ uint32_t retries_on_error;
+ uint32_t ms_per_word;
+ uint16_t xylogics_gap1;
+ uint16_t xylogics_syncdelay;
+ uint16_t xylogics_readdelay;
+ uint16_t xylogics_gap2;
+ uint16_t xylogics_readgate;
+ uint16_t xylogics_writecont;
+} __attribute__((packed));
+
+enum {
+ SGI_DEVPARAM_SECTOR_SLIP = 0x01,
+ SGI_DEVPARAM_SECTOR_FWD = 0x02,
+ SGI_DEVPARAM_TRACK_FWD = 0x04,
+ SGI_DEVPARAM_TRACK_MULTIVOL = 0x08,
+ SGI_DEVPARAM_IGNORE_ERRORS = 0x10,
+ SGI_DEVPARAM_RESEEK = 0x20,
+ SGI_DEVPARAM_CMDTAGQ_ENABLE = 0x40
+};
+
+
+struct sgi_disklabel {
+ uint32_t magic; /* magic number */
+ uint16_t root_part_num; /* # root partition */
+ uint16_t swap_part_num; /* # swap partition */
+ unsigned char boot_file[16]; /* name of boot file */
+
+ struct sgi_device_parameter devparam; /* not used now */
+
+ struct sgi_volume {
+ unsigned char name[8]; /* name of volume */
+ uint32_t block_num; /* logical block number */
+ uint32_t num_bytes; /* how big, in bytes */
+ } __attribute__((packed)) volume[SGI_MAXVOLUMES];
+
+ struct sgi_partition {
+ uint32_t num_blocks; /* size in logical blocks */
+ uint32_t first_block; /* first logical block */
+ uint32_t type; /* type of this partition */
+ } __attribute__((packed)) partitions[SGI_MAXPARTITIONS];
+
+ /* checksum is the 32bit 2's complement sum of the disklabel */
+ uint32_t csum; /* disk label checksum */
+ uint32_t padding; /* padding */
+} __attribute__((packed));
+
+static inline uint32_t sgi_pt_checksum(struct sgi_disklabel *label)
+{
+ int count;
+ uint32_t sum = 0;
+ unsigned char *ptr = (unsigned char *) label;
+
+ count = sizeof(*label) / sizeof(uint32_t);
+ ptr += sizeof(uint32_t) * (count - 1);
+
+ while (count--) {
+ uint32_t val;
+
+ memcpy(&val, ptr, sizeof(uint32_t));
+ sum -= be32_to_cpu(val);
+
+ ptr -= sizeof(uint32_t);
+ }
+
+ return sum;
+}
+
+#endif /* UTIL_LINUX_PT_SGI_H */
diff --git a/utils/include/pt-sun.h b/utils/include/pt-sun.h
new file mode 100644
index 0000000..8bb5d95
--- /dev/null
+++ b/utils/include/pt-sun.h
@@ -0,0 +1,90 @@
+#ifndef UTIL_LINUX_PT_SUN_H
+#define UTIL_LINUX_PT_SUN_H
+
+#include <stdint.h>
+
+#define SUN_LABEL_MAGIC 0xDABE
+
+/* Supported VTOC setting */
+#define SUN_VTOC_SANITY 0x600DDEEE /* magic number */
+#define SUN_VTOC_VERSION 1
+#define SUN_MAXPARTITIONS 8
+
+struct sun_disklabel {
+ unsigned char label_id[128]; /* Informative text string */
+
+ struct sun_vtoc {
+ uint32_t version; /* version */
+ char volume_id[8];/* volume name */
+ uint16_t nparts; /* num of partitions */
+
+ struct sun_info { /* partition information */
+ uint16_t id; /* SUN_TAG_* */
+ uint16_t flags; /* SUN_FLAG_* */
+ } __attribute__ ((packed)) infos[8];
+
+ uint16_t padding; /* padding */
+ uint32_t bootinfo[3]; /* info needed by mboot */
+ uint32_t sanity; /* magic number */
+ uint32_t reserved[10]; /* padding */
+ uint32_t timestamp[8]; /* partition timestamp */
+ } __attribute__ ((packed)) vtoc;
+
+ uint32_t write_reinstruct; /* sectors to skip, writes */
+ uint32_t read_reinstruct; /* sectors to skip, reads */
+ unsigned char spare[148]; /* padding */
+ uint16_t rpm; /* disk rotational speed */
+ uint16_t pcyl; /* physical cylinder count */
+ uint16_t apc; /* extra sects per cylinder */
+ uint16_t obs1;
+ uint16_t obs2;
+ uint16_t intrlv; /* interleave factor */
+ uint16_t ncyl; /* data cylinder count */
+ uint16_t acyl; /* alt. cylinder count */
+ uint16_t nhead; /* tracks per cylinder <---- */
+ uint16_t nsect; /* sectors per track <---- */
+ uint16_t obs3;
+ uint16_t obs4;
+
+ struct sun_partition { /* partitions */
+ uint32_t start_cylinder;
+ uint32_t num_sectors;
+ } __attribute__ ((packed)) partitions[8];
+
+ uint16_t magic; /* magic number */
+ uint16_t csum; /* label xor'd checksum */
+} __attribute__ ((packed));
+
+
+#define SUN_TAG_UNASSIGNED 0x00 /* Unassigned partition */
+#define SUN_TAG_BOOT 0x01 /* Boot partition */
+#define SUN_TAG_ROOT 0x02 /* Root filesystem */
+#define SUN_TAG_SWAP 0x03 /* Swap partition */
+#define SUN_TAG_USR 0x04 /* /usr filesystem */
+#define SUN_TAG_WHOLEDISK 0x05 /* Full-disk slice */
+#define SUN_TAG_STAND 0x06 /* Stand partition */
+#define SUN_TAG_VAR 0x07 /* /var filesystem */
+#define SUN_TAG_HOME 0x08 /* /home filesystem */
+#define SUN_TAG_ALTSCTR 0x09 /* Alt sector partition */
+#define SUN_TAG_CACHE 0x0a /* Cachefs partition */
+#define SUN_TAG_RESERVED 0x0b /* SMI reserved data */
+#define SUN_TAG_LINUX_SWAP 0x82 /* Linux SWAP */
+#define SUN_TAG_LINUX_NATIVE 0x83 /* Linux filesystem */
+#define SUN_TAG_LINUX_LVM 0x8e /* Linux LVM */
+#define SUN_TAG_LINUX_RAID 0xfd /* LInux RAID */
+
+#define SUN_FLAG_UNMNT 0x01 /* Unmountable partition*/
+#define SUN_FLAG_RONLY 0x10 /* Read only */
+
+static inline uint16_t sun_pt_checksum(const struct sun_disklabel *label)
+{
+ const uint16_t *ptr = ((const uint16_t *) (label + 1)) - 1;
+ uint16_t sum;
+
+ for (sum = 0; ptr >= ((const uint16_t *) label);)
+ sum ^= *ptr--;
+
+ return sum;
+}
+
+#endif /* UTIL_LINUX_PT_SUN_H */
diff --git a/utils/include/pty-session.h b/utils/include/pty-session.h
new file mode 100644
index 0000000..0c9ccc6
--- /dev/null
+++ b/utils/include/pty-session.h
@@ -0,0 +1,110 @@
+/*
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com> in Jul 2019
+ */
+#ifndef UTIL_LINUX_PTY_SESSION_H
+#define UTIL_LINUX_PTY_SESSION_H
+
+#include <pty.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include <sys/signalfd.h>
+
+/*
+ * Callbacks -- the first argument is always callback data, see
+ * ul_pty_set_callback_data().
+ */
+struct ul_pty_callbacks {
+ /*
+ * Optional. Executed on SIGCHLD when ssi_code is EXITED, KILLED or
+ * DUMPED; The callback has to call ul_pty_set_child(pty, (pid_t) -1)
+ * if child is no more alive.
+ */
+ void (*child_wait)(void *, pid_t);
+
+ /*
+ * Used when child_wait() undefined to informa about child status
+ */
+ void (*child_die)(void *, pid_t, int);
+
+ /*
+ * Executed on SIGCHLD when ssi_status is SIGSTOP
+ */
+ void (*child_sigstop)(void *, pid_t);
+
+ /*
+ * Executed in master loop before ul_pty enter poll() and in time set by
+ * ul_pty_set_mainloop_time(). The callback is no used when time is not set.
+ */
+ int (*mainloop)(void *);
+
+ /*
+ * Executed on master or stdin activity, arguments:
+ * 2nd - file descriptor
+ * 3rd - buffer with data
+ * 4th - size of the data
+ */
+ int (*log_stream_activity)(void *, int, char *, size_t);
+
+ /*
+ * Executed on signal, arguments:
+ * 2nd - signal info
+ * 3rd - NULL or signal specific data (e.g. struct winsize on SIGWINCH
+ */
+ int (*log_signal)(void *, struct signalfd_siginfo *, void *);
+
+ /*
+ * Executed on SIGUSR1
+ */
+ int (*flush_logs)(void *);
+};
+
+struct ul_pty {
+ struct termios stdin_attrs; /* stdin and slave terminal runtime attributes */
+ int master; /* parent side */
+ int slave; /* child side */
+ int sigfd; /* signalfd() */
+ int poll_timeout;
+ struct winsize win; /* terminal window size */
+ sigset_t orgsig; /* original signal mask */
+
+ int delivered_signal;
+
+ struct ul_pty_callbacks callbacks;
+ void *callback_data;
+
+ pid_t child;
+
+ struct timeval next_callback_time;
+
+ unsigned int isterm:1, /* is stdin terminal? */
+ slave_echo:1; /* keep ECHO on pty slave */
+};
+
+void ul_pty_init_debug(int mask);
+struct ul_pty *ul_new_pty(int is_stdin_tty);
+void ul_free_pty(struct ul_pty *pty);
+
+void ul_pty_slave_echo(struct ul_pty *pty, int enable);
+int ul_pty_get_delivered_signal(struct ul_pty *pty);
+
+void ul_pty_set_callback_data(struct ul_pty *pty, void *data);
+void ul_pty_set_child(struct ul_pty *pty, pid_t child);
+
+struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty);
+int ul_pty_is_running(struct ul_pty *pty);
+int ul_pty_setup(struct ul_pty *pty);
+void ul_pty_cleanup(struct ul_pty *pty);
+void ul_pty_init_slave(struct ul_pty *pty);
+int ul_pty_proxy_master(struct ul_pty *pty);
+
+void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv);
+int ul_pty_get_childfd(struct ul_pty *pty);
+void ul_pty_wait_for_child(struct ul_pty *pty);
+pid_t ul_pty_get_child(struct ul_pty *pty);
+void ul_pty_write_eof_to_child(struct ul_pty *pty);
+
+#endif /* UTIL_LINUX_PTY_H */
diff --git a/utils/include/pwdutils.h b/utils/include/pwdutils.h
new file mode 100644
index 0000000..b58268d
--- /dev/null
+++ b/utils/include/pwdutils.h
@@ -0,0 +1,14 @@
+#ifndef UTIL_LINUX_PWDUTILS_H
+#define UTIL_LINUX_PWDUTILS_H
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+extern struct passwd *xgetpwnam(const char *username, char **pwdbuf);
+extern struct group *xgetgrnam(const char *groupname, char **grpbuf);
+extern struct passwd *xgetpwuid(uid_t uid, char **pwdbuf);
+extern char *xgetlogin(void);
+
+#endif /* UTIL_LINUX_PWDUTILS_H */
+
diff --git a/utils/include/randutils.h b/utils/include/randutils.h
new file mode 100644
index 0000000..86e35f3
--- /dev/null
+++ b/utils/include/randutils.h
@@ -0,0 +1,17 @@
+#ifndef UTIL_LINUX_RANDUTILS
+#define UTIL_LINUX_RANDUTILS
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+/* rand() based */
+extern int rand_get_number(int low_n, int high_n);
+
+/* /dev/urandom based with fallback to rand() */
+extern int random_get_fd(void);
+extern void random_get_bytes(void *buf, size_t nbytes);
+extern const char *random_tell_source(void);
+
+#endif
diff --git a/utils/include/rpmatch.h b/utils/include/rpmatch.h
new file mode 100644
index 0000000..f64d52e
--- /dev/null
+++ b/utils/include/rpmatch.h
@@ -0,0 +1,13 @@
+#ifndef UTIL_LINUX_RPMATCH_H
+#define UTIL_LINUX_RPMATCH_H
+
+#ifndef HAVE_RPMATCH
+#define rpmatch(r) \
+ (*r == 'y' || *r == 'Y' ? 1 : *r == 'n' || *r == 'N' ? 0 : -1)
+#endif
+
+#define RPMATCH_YES 1
+#define RPMATCH_NO 0
+#define RPMATCH_INVALID -1
+
+#endif /* UTIL_LINUX_RPMATCH_H */
diff --git a/utils/include/setproctitle.h b/utils/include/setproctitle.h
new file mode 100644
index 0000000..70a9efa
--- /dev/null
+++ b/utils/include/setproctitle.h
@@ -0,0 +1,7 @@
+#ifndef UTIL_LINUX_SETPROCTITLE_H
+#define UTIL_LINUX_SETPROCTITLE_H
+
+extern void initproctitle (int argc, char **argv);
+extern void setproctitle (const char *prog, const char *txt);
+
+#endif
diff --git a/utils/include/sha1.h b/utils/include/sha1.h
new file mode 100644
index 0000000..62af1da
--- /dev/null
+++ b/utils/include/sha1.h
@@ -0,0 +1,27 @@
+#ifndef UTIL_LINUX_SHA1_H
+#define UTIL_LINUX_SHA1_H
+
+/*
+ SHA-1 in C
+ By Steve Reid <steve@edmweb.com>
+ 100% Public Domain
+ */
+
+#include "stdint.h"
+
+#define UL_SHA1LENGTH 20
+
+typedef struct
+{
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} UL_SHA1_CTX;
+
+void ul_SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
+void ul_SHA1Init(UL_SHA1_CTX *context);
+void ul_SHA1Update(UL_SHA1_CTX *context, const unsigned char *data, uint32_t len);
+void ul_SHA1Final(unsigned char digest[UL_SHA1LENGTH], UL_SHA1_CTX *context);
+void ul_SHA1(char *hash_out, const char *str, unsigned len);
+
+#endif /* UTIL_LINUX_SHA1_H */
diff --git a/utils/include/signames.h b/utils/include/signames.h
new file mode 100644
index 0000000..a4fd1bc
--- /dev/null
+++ b/utils/include/signames.h
@@ -0,0 +1,8 @@
+#ifndef SIGNAMES_H
+#define SIGNAMES_H
+
+int signame_to_signum(const char *sig);
+const char *signum_to_signame(int signum);
+int get_signame_by_idx(size_t idx, const char **signame, int *signum);
+
+#endif /* SIGNAMES_H */
diff --git a/utils/include/statfs_magic.h b/utils/include/statfs_magic.h
new file mode 100644
index 0000000..b6b0225
--- /dev/null
+++ b/utils/include/statfs_magic.h
@@ -0,0 +1,100 @@
+#ifndef UTIL_LINUX_STATFS_MAGIC_H
+#define UTIL_LINUX_STATFS_MAGIC_H
+
+#include <sys/statfs.h>
+
+/*
+ * If possible then don't depend on internal libc __SWORD_TYPE type.
+ */
+#ifdef __GNUC__
+#define F_TYPE_EQUAL(a, b) (a == (__typeof__(a)) b)
+#else
+#define F_TYPE_EQUAL(a, b) (a == (__SWORD_TYPE) b)
+#endif
+
+/*
+ * Unfortunately, Linux kernel header file <linux/magic.h> is incomplete
+ * mess and kernel returns by statfs f_type many numbers that are nowhere
+ * specified (in API).
+ *
+ * This is collection of the magic numbers.
+ */
+#define STATFS_ADFS_MAGIC 0xadf5
+#define STATFS_AFFS_MAGIC 0xadff
+#define STATFS_AFS_MAGIC 0x5346414F
+#define STATFS_AUTOFS_MAGIC 0x0187
+#define STATFS_BDEVFS_MAGIC 0x62646576
+#define STATFS_BEFS_MAGIC 0x42465331
+#define STATFS_BFS_MAGIC 0x1BADFACE
+#define STATFS_BINFMTFS_MAGIC 0x42494e4d
+#define STATFS_BTRFS_MAGIC 0x9123683E
+#define STATFS_CEPH_MAGIC 0x00c36400
+#define STATFS_CGROUP_MAGIC 0x27e0eb
+#define STATFS_CGROUP2_MAGIC 0x63677270
+#define STATFS_CIFS_MAGIC 0xff534d42
+#define STATFS_CODA_MAGIC 0x73757245
+#define STATFS_CONFIGFS_MAGIC 0x62656570
+#define STATFS_CRAMFS_MAGIC 0x28cd3d45
+#define STATFS_DEBUGFS_MAGIC 0x64626720
+#define STATFS_DEVPTS_MAGIC 0x1cd1
+#define STATFS_ECRYPTFS_MAGIC 0xf15f
+#define STATFS_EFIVARFS_MAGIC 0xde5e81e4
+#define STATFS_EFS_MAGIC 0x414A53
+#define STATFS_EXOFS_MAGIC 0x5DF5
+#define STATFS_EXT2_MAGIC 0xEF53
+#define STATFS_EXT3_MAGIC 0xEF53
+#define STATFS_EXT4_MAGIC 0xEF53
+#define STATFS_F2FS_MAGIC 0xF2F52010
+#define STATFS_FUSE_MAGIC 0x65735546
+#define STATFS_FUTEXFS_MAGIC 0xBAD1DEA
+#define STATFS_GFS2_MAGIC 0x01161970
+#define STATFS_HFSPLUS_MAGIC 0x482b
+#define STATFS_HOSTFS_MAGIC 0x00c0ffee
+#define STATFS_HPFS_MAGIC 0xf995e849
+#define STATFS_HPPFS_MAGIC 0xb00000ee
+#define STATFS_HUGETLBFS_MAGIC 0x958458f6
+#define STATFS_ISOFS_MAGIC 0x9660
+#define STATFS_JFFS2_MAGIC 0x72b6
+#define STATFS_JFS_MAGIC 0x3153464a
+#define STATFS_LOGFS_MAGIC 0xc97e8168
+#define STATFS_MINIX2_MAGIC 0x2468
+#define STATFS_MINIX2_MAGIC2 0x2478
+#define STATFS_MINIX3_MAGIC 0x4d5a
+#define STATFS_MINIX_MAGIC 0x137F
+#define STATFS_MINIX_MAGIC2 0x138F
+#define STATFS_MQUEUE_MAGIC 0x19800202
+#define STATFS_MSDOS_MAGIC 0x4d44
+#define STATFS_NCP_MAGIC 0x564c
+#define STATFS_NFS_MAGIC 0x6969
+#define STATFS_NILFS_MAGIC 0x3434
+#define STATFS_NTFS_MAGIC 0x5346544e
+#define STATFS_OCFS2_MAGIC 0x7461636f
+#define STATFS_OMFS_MAGIC 0xC2993D87
+#define STATFS_OPENPROMFS_MAGIC 0x9fa1
+#define STATFS_PIPEFS_MAGIC 0x50495045
+#define STATFS_PROC_MAGIC 0x9fa0
+#define STATFS_PSTOREFS_MAGIC 0x6165676C
+#define STATFS_QNX4_MAGIC 0x002f
+#define STATFS_QNX6_MAGIC 0x68191122
+#define STATFS_RAMFS_MAGIC 0x858458f6
+#define STATFS_REISERFS_MAGIC 0x52654973
+#define STATFS_ROMFS_MAGIC 0x7275
+#define STATFS_SECURITYFS_MAGIC 0x73636673
+#define STATFS_SELINUXFS_MAGIC 0xf97cff8c
+#define STATFS_SMACKFS_MAGIC 0x43415d53
+#define STATFS_SMB_MAGIC 0x517B
+#define STATFS_SOCKFS_MAGIC 0x534F434B
+#define STATFS_SQUASHFS_MAGIC 0x73717368
+#define STATFS_SYSFS_MAGIC 0x62656572
+#define STATFS_TMPFS_MAGIC 0x01021994
+#define STATFS_UBIFS_MAGIC 0x24051905
+#define STATFS_UDF_MAGIC 0x15013346
+#define STATFS_UFS2_MAGIC 0x19540119
+#define STATFS_UFS_MAGIC 0x00011954
+#define STATFS_V9FS_MAGIC 0x01021997
+#define STATFS_VXFS_MAGIC 0xa501FCF5
+#define STATFS_XENFS_MAGIC 0xabba1974
+#define STATFS_XFS_MAGIC 0x58465342
+
+#endif /* UTIL_LINUX_STATFS_MAGIC_H */
+
diff --git a/utils/include/strutils.h b/utils/include/strutils.h
new file mode 100644
index 0000000..4b3182f
--- /dev/null
+++ b/utils/include/strutils.h
@@ -0,0 +1,333 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* initialize a custom exit code for all *_or_err functions */
+extern void strutils_set_exitcode(int exit_code);
+
+extern int parse_size(const char *str, uintmax_t *res, int *power);
+extern int strtosize(const char *str, uintmax_t *res);
+extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
+
+extern int16_t strtos16_or_err(const char *str, const char *errmesg);
+extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
+extern uint16_t strtox16_or_err(const char *str, const char *errmesg);
+
+extern int32_t strtos32_or_err(const char *str, const char *errmesg);
+extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
+extern uint32_t strtox32_or_err(const char *str, const char *errmesg);
+
+extern int64_t strtos64_or_err(const char *str, const char *errmesg);
+extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
+extern uint64_t strtox64_or_err(const char *str, const char *errmesg);
+
+extern double strtod_or_err(const char *str, const char *errmesg);
+
+extern long strtol_or_err(const char *str, const char *errmesg);
+extern unsigned long strtoul_or_err(const char *str, const char *errmesg);
+
+extern void strtotimeval_or_err(const char *str, struct timeval *tv,
+ const char *errmesg);
+
+extern int isdigit_strend(const char *str, const char **end);
+#define isdigit_string(_s) isdigit_strend(_s, NULL)
+
+extern int isxdigit_strend(const char *str, const char **end);
+#define isxdigit_string(_s) isxdigit_strend(_s, NULL)
+
+
+extern int parse_switch(const char *arg, const char *errmesg, ...);
+
+#ifndef HAVE_MEMPCPY
+extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
+#endif
+#ifndef HAVE_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#endif
+#ifndef HAVE_STRNDUP
+extern char *strndup(const char *s, size_t n);
+#endif
+#ifndef HAVE_STRNCHR
+extern char *strnchr(const char *s, size_t maxlen, int c);
+#endif
+
+/* caller guarantees n > 0 */
+static inline void xstrncpy(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n-1);
+ dest[n-1] = 0;
+}
+
+/* This is like strncpy(), but based on memcpy(), so compilers and static
+ * analyzers do not complain when sizeof(destination) is the same as 'n' and
+ * result is not terminated by zero.
+ *
+ * Use this function to copy string to logs with fixed sizes (wtmp/utmp. ...)
+ * where string terminator is optional.
+ */
+static inline void *str2memcpy(void *dest, const char *src, size_t n)
+{
+ size_t bytes = strlen(src) + 1;
+
+ if (bytes > n)
+ bytes = n;
+
+ memcpy(dest, src, bytes);
+ return dest;
+}
+
+static inline char *mem2strcpy(char *dest, const void *src, size_t n, size_t nmax)
+{
+ if (n + 1 > nmax)
+ n = nmax - 1;
+
+ memcpy(dest, src, n);
+ dest[nmax-1] = '\0';
+ return dest;
+}
+
+/* Reallocate @str according to @newstr and copy @newstr to @str; returns new @str.
+ * The @str is not modified if reallocation failed (like classic realloc()).
+ */
+static inline char * __attribute__((warn_unused_result))
+strrealloc(char *str, const char *newstr)
+{
+ size_t nsz, osz;
+
+ if (!str)
+ return newstr ? strdup(newstr) : NULL;
+ if (!newstr)
+ return NULL;
+
+ osz = strlen(str);
+ nsz = strlen(newstr);
+
+ if (nsz > osz)
+ str = realloc(str, nsz + 1);
+ if (str)
+ memcpy(str, newstr, nsz + 1);
+ return str;
+}
+
+/* Copy string @str to struct @stru to member addressed by @offset */
+static inline int strdup_to_offset(void *stru, size_t offset, const char *str)
+{
+ char **o;
+ char *p = NULL;
+
+ if (!stru)
+ return -EINVAL;
+
+ o = (char **) ((char *) stru + offset);
+ if (str) {
+ p = strdup(str);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(*o);
+ *o = p;
+ return 0;
+}
+
+/* Copy string __str to struct member _m of the struct _s */
+#define strdup_to_struct_member(_s, _m, _str) \
+ strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
+
+/* Copy string addressed by @offset between two structs */
+static inline int strdup_between_offsets(void *stru_dst, void *stru_src, size_t offset)
+{
+ char **src;
+ char **dst;
+ char *p = NULL;
+
+ if (!stru_src || !stru_dst)
+ return -EINVAL;
+
+ src = (char **) ((char *) stru_src + offset);
+ dst = (char **) ((char *) stru_dst + offset);
+
+ if (*src) {
+ p = strdup(*src);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(*dst);
+ *dst = p;
+ return 0;
+}
+
+/* Copy string addressed by struct member between two instances of the same
+ * struct type */
+#define strdup_between_structs(_dst, _src, _m) \
+ strdup_between_offsets((void *)_dst, (void *)_src, offsetof(__typeof__(*(_src)), _m))
+
+
+extern char *xstrmode(mode_t mode, char *str);
+
+/* Options for size_to_human_string() */
+enum
+{
+ SIZE_SUFFIX_1LETTER = 0,
+ SIZE_SUFFIX_3LETTER = (1 << 0),
+ SIZE_SUFFIX_SPACE = (1 << 1),
+ SIZE_DECIMAL_2DIGITS = (1 << 2)
+};
+
+extern char *size_to_human_string(int options, uint64_t bytes);
+
+extern int string_to_idarray(const char *list, int ary[], size_t arysz,
+ int (name2id)(const char *, size_t));
+extern int string_add_to_idarray(const char *list, int ary[],
+ size_t arysz, size_t *ary_pos,
+ int (name2id)(const char *, size_t));
+
+extern int string_to_bitarray(const char *list, char *ary,
+ int (*name2bit)(const char *, size_t));
+
+extern int string_to_bitmask(const char *list,
+ unsigned long *mask,
+ long (*name2flag)(const char *, size_t));
+extern int parse_range(const char *str, int *lower, int *upper, int def);
+
+extern int streq_paths(const char *a, const char *b);
+
+/*
+ * Match string beginning.
+ */
+static inline const char *startswith(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+static inline const char *startswith_no_case(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncasecmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Match string ending.
+ */
+static inline const char *endswith(const char *s, const char *postfix)
+{
+ size_t sl = s ? strlen(s) : 0;
+ size_t pl = postfix ? strlen(postfix) : 0;
+
+ if (pl == 0)
+ return s + sl;
+ if (sl < pl)
+ return NULL;
+ if (memcmp(s + sl - pl, postfix, pl) != 0)
+ return NULL;
+ return s + sl - pl;
+}
+
+/*
+ * Skip leading white space.
+ */
+static inline const char *skip_space(const char *p)
+{
+ while (isspace(*p))
+ ++p;
+ return p;
+}
+
+static inline const char *skip_blank(const char *p)
+{
+ while (isblank(*p))
+ ++p;
+ return p;
+}
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t rtrim_whitespace(unsigned char *str)
+{
+ size_t i;
+
+ if (!str)
+ return 0;
+ i = strlen((char *) str);
+ while (i) {
+ i--;
+ if (!isspace(str[i])) {
+ i++;
+ break;
+ }
+ }
+ str[i] = '\0';
+ return i;
+}
+
+/* Removes whitespace from the left-hand side of a string.
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t ltrim_whitespace(unsigned char *str)
+{
+ size_t len;
+ unsigned char *p;
+
+ if (!str)
+ return 0;
+ for (p = str; *p && isspace(*p); p++);
+
+ len = strlen((char *) p);
+
+ if (p > str)
+ memmove(str, p, len + 1);
+
+ return len;
+}
+
+static inline void strrep(char *s, int find, int replace)
+{
+ while (s && *s && (s = strchr(s, find)) != NULL)
+ *s++ = replace;
+}
+
+static inline void strrem(char *s, int rem)
+{
+ char *p;
+
+ if (!s)
+ return;
+ for (p = s; *s; s++) {
+ if (*s != rem)
+ *p++ = *s;
+ }
+ *p = '\0';
+}
+
+extern char *strnappend(const char *s, const char *suffix, size_t b);
+extern char *strappend(const char *s, const char *suffix);
+extern char *strfappend(const char *s, const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 2, 0)));
+extern const char *split(const char **state, size_t *l, const char *separator, int quoted);
+
+extern int skip_fline(FILE *fp);
+
+#endif
diff --git a/utils/include/strv.h b/utils/include/strv.h
new file mode 100644
index 0000000..260ad12
--- /dev/null
+++ b/utils/include/strv.h
@@ -0,0 +1,55 @@
+#ifndef UTIL_LINUX_STRV
+#define UTIL_LINUX_STRV
+
+#include <stdarg.h>
+
+#include "c.h"
+
+char **strv_free(char **l);
+void strv_clear(char **l);
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l);
+
+int strv_extend_strv(char ***a, char **b);
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
+int strv_extend(char ***l, const char *value);
+int strv_extendv(char ***l, const char *format, va_list ap);
+int strv_extendf(char ***l, const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 2, 0)));
+int strv_push(char ***l, char *value);
+int strv_push_prepend(char ***l, char *value);
+int strv_consume(char ***l, char *value);
+int strv_consume_prepend(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+
+char **strv_new(const char *x, ...);
+char **strv_new_ap(const char *x, va_list ap);
+
+static inline const char* STRV_IFNOTNULL(const char *x) {
+ return x ? x : (const char *) -1;
+}
+
+static inline int strv_isempty(char * const *l) {
+ return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator);
+char *strv_join(char **l, const char *separator);
+
+#define STRV_FOREACH(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ STRV_FOREACH(s, l) \
+ ; \
+ for ((s)--; (l) && ((s) >= (l)); (s)--)
+
+
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+char **strv_reverse(char **l);
+
+#endif /* UTIL_LINUX_STRV */
+
+
diff --git a/utils/include/swapheader.h b/utils/include/swapheader.h
new file mode 100644
index 0000000..3fce0d0
--- /dev/null
+++ b/utils/include/swapheader.h
@@ -0,0 +1,23 @@
+#ifndef _SWAPHEADER_H
+#define _SWAPHEADER_H
+
+#define SWAP_VERSION 1
+#define SWAP_UUID_LENGTH 16
+#define SWAP_LABEL_LENGTH 16
+#define SWAP_SIGNATURE "SWAPSPACE2"
+#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
+
+#include <stdint.h>
+
+struct swap_header_v1_2 {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ uint32_t version;
+ uint32_t last_page;
+ uint32_t nr_badpages;
+ unsigned char uuid[SWAP_UUID_LENGTH];
+ char volume_name[SWAP_LABEL_LENGTH];
+ uint32_t padding[117];
+ uint32_t badpages[1];
+};
+
+#endif /* _SWAPHEADER_H */
diff --git a/utils/include/swapprober.h b/utils/include/swapprober.h
new file mode 100644
index 0000000..5107700
--- /dev/null
+++ b/utils/include/swapprober.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_SWAP_PROBER_H
+#define UTIL_LINUX_SWAP_PROBER_H
+
+#include <blkid.h>
+
+blkid_probe get_swap_prober(const char *devname);
+
+#endif /* UTIL_LINUX_SWAP_PROBER_H */
+
diff --git a/utils/include/sysfs.h b/utils/include/sysfs.h
new file mode 100644
index 0000000..dcd2a14
--- /dev/null
+++ b/utils/include/sysfs.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_SYSFS_H
+#define UTIL_LINUX_SYSFS_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+#include "path.h"
+
+/**
+ * sysfs_devname_sys_to_dev:
+ * @name: devname to be converted in place
+ *
+ * Linux kernel linux/drivers/base/core.c: device_get_devnode()
+ * defines a replacement of '!' in the /sys device name by '/' in the
+ * /dev device name. This helper replaces all occurrences of '!' in
+ * @name by '/' to convert from /sys to /dev.
+ */
+static inline void sysfs_devname_sys_to_dev(char *name)
+{
+ char *c;
+
+ if (name)
+ while ((c = strchr(name, '!')))
+ c[0] = '/';
+}
+
+/**
+ * sysfs_devname_dev_to_sys:
+ * @name: devname to be converted in place
+ *
+ * See sysfs_devname_sys_to_dev().
+ */
+static inline void sysfs_devname_dev_to_sys(char *name)
+{
+ char *c;
+
+ if (name)
+ while ((c = strchr(name, '/')))
+ c[0] = '!';
+}
+
+struct sysfs_blkdev {
+ dev_t devno;
+ struct path_cxt *parent;
+
+ unsigned int scsi_host,
+ scsi_channel,
+ scsi_target,
+ scsi_lun;
+
+ unsigned int has_hctl : 1,
+ hctl_error : 1 ;
+};
+
+void ul_sysfs_init_debug(void);
+
+struct path_cxt *ul_new_sysfs_path(dev_t devno, struct path_cxt *parent, const char *prefix);
+int sysfs_blkdev_init_path(struct path_cxt *pc, dev_t devno, struct path_cxt *parent);
+
+int sysfs_blkdev_set_parent(struct path_cxt *pc, struct path_cxt *parent);
+struct path_cxt *sysfs_blkdev_get_parent(struct path_cxt *pc);
+
+char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz);
+int sysfs_blkdev_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name);
+int sysfs_blkdev_count_partitions(struct path_cxt *pc, const char *devname);
+dev_t sysfs_blkdev_partno_to_devno(struct path_cxt *pc, int partno);
+char *sysfs_blkdev_get_slave(struct path_cxt *pc);
+char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz);
+dev_t sysfs_blkdev_get_devno(struct path_cxt *pc);
+
+char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz);
+int sysfs_blkdev_next_subsystem(struct path_cxt *pc __attribute__((unused)), char *devchain, char **subsys);
+
+int sysfs_blkdev_is_hotpluggable(struct path_cxt *pc);
+int sysfs_blkdev_get_wholedisk( struct path_cxt *pc,
+ char *diskname,
+ size_t len,
+ dev_t *diskdevno);
+
+int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+ size_t len, dev_t *diskdevno);
+int sysfs_devno_is_dm_private(dev_t devno, char **uuid);
+int sysfs_devno_is_wholedisk(dev_t devno);
+
+dev_t sysfs_devname_to_devno(const char *name);
+dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent);
+int sysfs_devname_is_hidden(const char *prefix, const char *name);
+
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
+char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz);
+int sysfs_devno_count_partitions(dev_t devno);
+
+int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l);
+char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc,
+ const char *type, const char *attr);
+int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type);
+int sysfs_blkdev_scsi_has_attribute(struct path_cxt *pc, const char *attr);
+int sysfs_blkdev_scsi_path_contains(struct path_cxt *pc, const char *pattern);
+
+
+#endif /* UTIL_LINUX_SYSFS_H */
diff --git a/utils/include/timer.h b/utils/include/timer.h
new file mode 100644
index 0000000..70da1ba
--- /dev/null
+++ b/utils/include/timer.h
@@ -0,0 +1,22 @@
+#ifndef UTIL_LINUX_TIMER_H
+#define UTIL_LINUX_TIMER_H
+
+#include <signal.h>
+#include <sys/time.h>
+
+#ifdef HAVE_TIMER_CREATE
+struct ul_timer {
+ timer_t t_id;
+};
+#else
+struct ul_timer {
+ struct itimerval old_timer;
+ struct sigaction old_sa;
+};
+#endif
+
+extern int setup_timer(struct ul_timer *timer, struct itimerval *timeout,
+ void (*timeout_handler)(int, siginfo_t *, void *));
+extern void cancel_timer(struct ul_timer *timer);
+
+#endif /* UTIL_LINUX_TIMER_H */
diff --git a/utils/include/timeutils.h b/utils/include/timeutils.h
new file mode 100644
index 0000000..e452e55
--- /dev/null
+++ b/utils/include/timeutils.h
@@ -0,0 +1,92 @@
+/***
+ First set of functions in this file are part of systemd, and were
+ copied to util-linux at August 2013.
+
+ Copyright 2010 Lennart Poettering
+ Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+#ifndef UTIL_LINUX_TIME_UTIL_H
+#define UTIL_LINUX_TIME_UTIL_H
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <sys/time.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#define MSEC_PER_SEC 1000ULL
+#define USEC_PER_SEC 1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC 1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE (60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR (60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY (24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK (7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH (2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR (31557600ULL*NSEC_PER_SEC)
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+int parse_timestamp(const char *t, usec_t *usec);
+int get_gmtoff(const struct tm *tp);
+
+/* flags and masks for strxxx_iso() functions */
+enum {
+ ISO_DATE = (1 << 0),
+ ISO_TIME = (1 << 1),
+ ISO_TIMEZONE = (1 << 2),
+ ISO_DOTUSEC = (1 << 3),
+ ISO_COMMAUSEC = (1 << 4),
+ ISO_T = (1 << 5),
+ ISO_GMTIME = (1 << 6),
+ ISO_TIMESTAMP = ISO_DATE | ISO_TIME | ISO_TIMEZONE,
+ ISO_TIMESTAMP_T = ISO_TIMESTAMP | ISO_T,
+ ISO_TIMESTAMP_DOT = ISO_TIMESTAMP | ISO_DOTUSEC,
+ ISO_TIMESTAMP_DOT_T = ISO_TIMESTAMP_DOT | ISO_T,
+ ISO_TIMESTAMP_COMMA = ISO_TIMESTAMP | ISO_COMMAUSEC,
+ ISO_TIMESTAMP_COMMA_T = ISO_TIMESTAMP_COMMA | ISO_T,
+ ISO_TIMESTAMP_COMMA_G = ISO_TIMESTAMP_COMMA | ISO_GMTIME,
+ ISO_TIMESTAMP_COMMA_GT = ISO_TIMESTAMP_COMMA_G | ISO_T
+};
+
+#define CTIME_BUFSIZ 26
+#define ISO_BUFSIZ 42
+
+int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz);
+int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz);
+int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz);
+
+#define UL_SHORTTIME_THISYEAR_HHMM (1 << 1)
+
+int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, size_t bufsz);
+
+#ifndef HAVE_TIMEGM
+extern time_t timegm(struct tm *tm);
+#endif
+
+#endif /* UTIL_LINUX_TIME_UTIL_H */
diff --git a/utils/include/ttyutils.h b/utils/include/ttyutils.h
new file mode 100644
index 0000000..f164a58
--- /dev/null
+++ b/utils/include/ttyutils.h
@@ -0,0 +1,205 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_TTYUTILS_H
+#define UTIL_LINUX_TTYUTILS_H
+
+#include <stdlib.h>
+#include <termios.h>
+#include <limits.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_TTYDEFAULTS_H
+#include <sys/ttydefaults.h>
+#endif
+
+/* Some shorthands for control characters. */
+#define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */
+#define CR CTL('M') /* carriage return */
+#define NL CTL('J') /* line feed */
+#define BS CTL('H') /* back space */
+#define DEL CTL('?') /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change these. */
+#define DEF_ERASE DEL /* default erase character */
+#define DEF_INTR CTL('C') /* default interrupt character */
+#define DEF_QUIT CTL('\\') /* default quit char */
+#define DEF_KILL CTL('U') /* default kill char */
+#define DEF_EOF CTL('D') /* default EOF char */
+#define DEF_EOL 0
+#define DEF_SWITCH 0 /* default switch char */
+
+/* Fallback for termios->c_cc[] */
+#ifndef CREPRINT
+# define CREPRINT ('r' & 037)
+#endif
+#ifndef CDISCARD
+# define CDISCARD ('o' & 037)
+#endif
+
+/* Default termios->iflag */
+#ifndef TTYDEF_IFLAG
+# define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY)
+#endif
+
+/* Default termios->oflag */
+#ifndef TTYDEF_OFLAG
+# define TTYDEF_OFLAG (OPOST | ONLCR /*| OXTABS*/)
+#endif
+
+/* Default termios->lflag */
+#ifndef TTYDEF_LFLAG
+# define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)
+#endif
+
+/* Default termios->cflag */
+#ifndef TTYDEF_CFLAG
+# define TTYDEF_CFLAG (CREAD | CS8 | HUPCL)
+#endif
+
+/* Storage for things detected while the login name was read. */
+struct chardata {
+ int erase; /* erase character */
+ int kill; /* kill character */
+ int eol; /* end-of-line character */
+ int parity; /* what parity did we see */
+ int capslock; /* upper case without lower case */
+};
+
+#define INIT_CHARDATA(ptr) do { \
+ (ptr)->erase = DEF_ERASE; \
+ (ptr)->kill = DEF_KILL; \
+ (ptr)->eol = CTRL('r'); \
+ (ptr)->parity = 0; \
+ (ptr)->capslock = 0; \
+ } while (0)
+
+extern int get_terminal_dimension(int *cols, int *lines);
+extern int get_terminal_width(int default_width);
+extern int get_terminal_type(const char **type);
+extern int get_terminal_stdfd(void);
+extern int get_terminal_name(const char **path, const char **name,
+ const char **number);
+
+#define UL_TTY_KEEPCFLAGS (1 << 1)
+#define UL_TTY_UTF8 (1 << 2)
+
+static inline void reset_virtual_console(struct termios *tp, int flags)
+{
+ /* Use defaults of <sys/ttydefaults.h> for base settings */
+ tp->c_iflag |= TTYDEF_IFLAG;
+ tp->c_oflag |= TTYDEF_OFLAG;
+ tp->c_lflag |= TTYDEF_LFLAG;
+
+ if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+#ifdef CBAUD
+ tp->c_lflag &= ~CBAUD;
+#endif
+ tp->c_cflag |= (B38400 | TTYDEF_CFLAG);
+ }
+
+ /* Sane setting, allow eight bit characters, no carriage return delay
+ * the same result as `stty sane cr0 pass8'
+ */
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+#ifndef NL0
+# define NL0 0
+#endif
+#ifndef CR0
+# define CR0 0
+#endif
+#ifndef BS0
+# define BS0 0
+#endif
+#ifndef VT0
+# define VT0 0
+#endif
+#ifndef FF0
+# define FF0 0
+#endif
+#ifndef OLCUC
+# define OLCUC 0
+#endif
+#ifndef OFILL
+# define OFILL 0
+#endif
+#ifndef NLDLY
+# define NLDLY 0
+#endif
+#ifndef CRDLY
+# define CRDLY 0
+#endif
+#ifndef BSDLY
+# define BSDLY 0
+#endif
+#ifndef VTDLY
+# define VTDLY 0
+#endif
+#ifndef FFDLY
+# define FFDLY 0
+#endif
+#ifndef TAB0
+# define TAB0 0
+#endif
+#ifndef TABDLY
+# define TABDLY 0
+#endif
+
+ tp->c_iflag |= (BRKINT | ICRNL | IMAXBEL);
+ tp->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP);
+ tp->c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
+ tp->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | \
+ NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
+ tp->c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE|ECHOCTL);
+ tp->c_lflag &= ~(ECHONL|ECHOPRT | NOFLSH | TOSTOP);
+
+ if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+ tp->c_cflag |= (CREAD | CS8 | HUPCL);
+ tp->c_cflag &= ~(PARODD | PARENB);
+ }
+#ifdef OFDEL
+ tp->c_oflag &= ~OFDEL;
+#endif
+#ifdef XCASE
+ tp->c_lflag &= ~XCASE;
+#endif
+#ifdef IUTF8
+ if (flags & UL_TTY_UTF8)
+ tp->c_iflag |= IUTF8; /* Set UTF-8 input flag */
+ else
+ tp->c_iflag &= ~IUTF8;
+#endif
+ /* VTIME and VMIN can overlap with VEOF and VEOL since they are
+ * only used for non-canonical mode. We just set the at the
+ * beginning, so nothing bad should happen.
+ */
+ tp->c_cc[VTIME] = 0;
+ tp->c_cc[VMIN] = 1;
+ tp->c_cc[VINTR] = CINTR;
+ tp->c_cc[VQUIT] = CQUIT;
+ tp->c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
+ tp->c_cc[VKILL] = CKILL;
+ tp->c_cc[VEOF] = CEOF;
+#ifdef VSWTC
+ tp->c_cc[VSWTC] = _POSIX_VDISABLE;
+#elif defined(VSWTCH)
+ tp->c_cc[VSWTCH] = _POSIX_VDISABLE;
+#endif
+ tp->c_cc[VSTART] = CSTART;
+ tp->c_cc[VSTOP] = CSTOP;
+ tp->c_cc[VSUSP] = CSUSP;
+ tp->c_cc[VEOL] = _POSIX_VDISABLE;
+ tp->c_cc[VREPRINT] = CREPRINT;
+ tp->c_cc[VDISCARD] = CDISCARD;
+ tp->c_cc[VWERASE] = CWERASE;
+ tp->c_cc[VLNEXT] = CLNEXT;
+ tp->c_cc[VEOL2] = _POSIX_VDISABLE;
+}
+
+#endif /* UTIL_LINUX_TTYUTILS_H */
diff --git a/utils/include/widechar.h b/utils/include/widechar.h
new file mode 100644
index 0000000..c1f2cf2
--- /dev/null
+++ b/utils/include/widechar.h
@@ -0,0 +1,47 @@
+/* Declarations for wide characters */
+/* This file must be included last because the redefinition of wchar_t may
+ cause conflicts when system include files were included after it. */
+
+#ifdef HAVE_WIDECHAR
+
+# include <wchar.h>
+# include <wctype.h>
+
+#else /* !HAVE_WIDECHAR */
+
+# include <ctype.h>
+ /* Fallback for types */
+# define wchar_t char
+# define wint_t int
+# ifndef WEOF
+# define WEOF EOF
+# endif
+
+ /* Fallback for input operations */
+# define fgetwc fgetc
+# define getwc getc
+# define getwchar getchar
+# define fgetws fgets
+
+ /* Fallback for output operations */
+# define fputwc fputc
+# define putwc putc
+# define putwchar putchar
+# define fputws fputs
+
+ /* Fallback for character classification */
+# define iswgraph isgraph
+# define iswprint isprint
+# define iswspace isspace
+
+ /* Fallback for string functions */
+# define wcschr strchr
+# define wcsdup strdup
+# define wcslen strlen
+# define wcspbrk strpbrk
+
+# define wcwidth(c) (1)
+# define wmemset memset
+# define ungetwc ungetc
+
+#endif /* HAVE_WIDECHAR */
diff --git a/utils/include/xalloc.h b/utils/include/xalloc.h
new file mode 100644
index 0000000..c4124cb
--- /dev/null
+++ b/utils/include/xalloc.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * General memory allocation wrappers for malloc, realloc, calloc and strdup
+ */
+
+#ifndef UTIL_LINUX_XALLOC_H
+#define UTIL_LINUX_XALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "c.h"
+
+#ifndef XALLOC_EXIT_CODE
+# define XALLOC_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline
+__attribute__((__noreturn__))
+void __err_oom(const char *file, unsigned int line)
+{
+ err(XALLOC_EXIT_CODE, "%s: %u: cannot allocate memory", file, line);
+}
+
+#define err_oom() __err_oom(__FILE__, __LINE__)
+
+static inline
+__ul_alloc_size(1)
+__ul_returns_nonnull
+void *xmalloc(const size_t size)
+{
+ void *ret = malloc(size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline
+__ul_alloc_size(2)
+__ul_returns_nonnull
+void *xrealloc(void *ptr, const size_t size)
+{
+ void *ret = realloc(ptr, size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline
+__ul_calloc_size(1, 2)
+__ul_returns_nonnull
+void *xcalloc(const size_t nelems, const size_t size)
+{
+ void *ret = calloc(nelems, size);
+
+ if (!ret && size && nelems)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline
+__attribute__((warn_unused_result))
+__ul_returns_nonnull
+char *xstrdup(const char *str)
+{
+ char *ret;
+
+ assert(str);
+ ret = strdup(str);
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+static inline
+__attribute__((warn_unused_result))
+__ul_returns_nonnull
+char *xstrndup(const char *str, size_t size)
+{
+ char *ret;
+
+ assert(str);
+ ret = strndup(str, size);
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+
+static inline
+__attribute__((__format__(printf, 2, 3)))
+int xasprintf(char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = vasprintf(&(*strp), fmt, args);
+ va_end(args);
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+static inline
+__attribute__((__format__(printf, 2, 0)))
+int xvasprintf(char **strp, const char *fmt, va_list ap)
+{
+ int ret = vasprintf(&(*strp), fmt, ap);
+
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+
+static inline
+__attribute__((warn_unused_result))
+char *xgethostname(void)
+{
+ char *name;
+ size_t sz = get_hostname_max() + 1;
+
+ name = xmalloc(sizeof(char) * sz);
+ if (gethostname(name, sz) != 0) {
+ free(name);
+ return NULL;
+ }
+ name[sz - 1] = '\0';
+ return name;
+}
+
+#endif
diff --git a/utils/lib/CMakeLists.txt b/utils/lib/CMakeLists.txt
new file mode 100644
index 0000000..b84dc3f
--- /dev/null
+++ b/utils/lib/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop-utils-lib)
+
+add_library(libcommon STATIC ${CMAKE_CURRENT_SOURCE_DIR}/blkdev.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/canonicalize.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/caputils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/color-names.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/colors.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/cpuset.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/crc32.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/crc32c.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/encode.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/env.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/exec_shell.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/fileutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/idcache.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ismounted.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/langinfo.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/linux_version.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/loopdev.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mangle.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/match.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mbsalign.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mbsedit.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/md5.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/monotonic.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/pager.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/path.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/plymouth-ctrl.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/procutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/pty-session.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/pwdutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/randutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/setproctitle.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/sha1.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/signames.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/strutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/strv.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/sysfs.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/timer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/timeutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ttyutils.c)
+target_include_directories(libcommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
diff --git a/utils/lib/blkdev.c b/utils/lib/blkdev.c
new file mode 100644
index 0000000..c22853d
--- /dev/null
+++ b/utils/lib/blkdev.c
@@ -0,0 +1,452 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+
+#ifdef HAVE_SYS_DISK_H
+# include <sys/disk.h>
+#endif
+
+#ifndef EBADFD
+# define EBADFD 77 /* File descriptor in bad state */
+#endif
+
+#include "blkdev.h"
+#include "c.h"
+#include "linux_version.h"
+#include "fileutils.h"
+#include "nls.h"
+
+static long
+blkdev_valid_offset (int fd, off_t offset) {
+ char ch;
+
+ if (lseek (fd, offset, 0) < 0)
+ return 0;
+ if (read (fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+int is_blkdev(int fd)
+{
+ struct stat st;
+ return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
+}
+
+off_t
+blkdev_find_size (int fd) {
+ uintmax_t high, low = 0;
+
+ for (high = 1024; blkdev_valid_offset (fd, high); ) {
+ if (high == UINTMAX_MAX)
+ return -1;
+
+ low = high;
+
+ if (high >= UINTMAX_MAX/2)
+ high = UINTMAX_MAX;
+ else
+ high *= 2;
+ }
+
+ while (low < high - 1)
+ {
+ uintmax_t mid = (low + high) / 2;
+
+ if (blkdev_valid_offset (fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ blkdev_valid_offset (fd, 0);
+ return (low + 1);
+}
+
+/* get size in bytes */
+int
+blkdev_get_size(int fd, unsigned long long *bytes)
+{
+#ifdef DKIOCGETBLOCKCOUNT
+ /* Apple Darwin */
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
+ *bytes <<= 9;
+ return 0;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+ if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
+ return 0;
+#endif
+
+#ifdef BLKGETSIZE
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ *bytes = ((unsigned long long)size << 9);
+ return 0;
+ }
+ }
+
+#endif /* BLKGETSIZE */
+
+#ifdef DIOCGMEDIASIZE
+ /* FreeBSD */
+ if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
+ return 0;
+#endif
+
+#ifdef FDGETPRM
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+ *bytes = ((unsigned long long) this_floppy.size) << 9;
+ return 0;
+ }
+ }
+#endif /* FDGETPRM */
+
+#if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO)
+ {
+ /*
+ * This code works for FreeBSD 4.11 i386, except for the full device
+ * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
+ * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
+ * above however.
+ *
+ * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+ * character) devices, so we need to check for S_ISCHR, too.
+ */
+ int part = -1;
+ struct disklabel lab;
+ struct partition *pp;
+ struct stat st;
+
+ if ((fstat(fd, &st) >= 0) &&
+ (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
+ part = st.st_rdev & 7;
+
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size) {
+ *bytes = pp->p_size << 9;
+ return 0;
+ }
+ }
+ }
+#endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */
+
+ {
+ struct stat st;
+
+ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
+ *bytes = st.st_size;
+ return 0;
+ }
+ if (!S_ISBLK(st.st_mode))
+ return -1;
+ }
+
+ *bytes = blkdev_find_size(fd);
+ return 0;
+}
+
+/* get 512-byte sector count */
+int
+blkdev_get_sectors(int fd, unsigned long long *sectors)
+{
+ unsigned long long bytes;
+
+ if (blkdev_get_size(fd, &bytes) == 0) {
+ *sectors = (bytes >> 9);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Get logical sector size.
+ *
+ * This is the smallest unit the storage device can
+ * address. It is typically 512 bytes.
+ */
+#ifdef BLKSSZGET
+int blkdev_get_sector_size(int fd, int *sector_size)
+{
+ if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
+ return 0;
+ return -1;
+}
+#else
+int blkdev_get_sector_size(int fd __attribute__((__unused__)), int *sector_size)
+{
+ *sector_size = DEFAULT_SECTOR_SIZE;
+ return 0;
+}
+#endif
+
+/*
+ * Get physical block device size. The BLKPBSZGET is supported since Linux
+ * 2.6.32. For old kernels is probably the best to assume that physical sector
+ * size is the same as logical sector size.
+ *
+ * Example:
+ *
+ * rc = blkdev_get_physector_size(fd, &physec);
+ * if (rc || physec == 0) {
+ * rc = blkdev_get_sector_size(fd, &physec);
+ * if (rc)
+ * physec = DEFAULT_SECTOR_SIZE;
+ * }
+ */
+#ifdef BLKPBSZGET
+int blkdev_get_physector_size(int fd, int *sector_size)
+{
+ if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
+ return 0;
+ return -1;
+}
+#else
+int blkdev_get_physector_size(int fd __attribute__((__unused__)), int *sector_size)
+{
+ *sector_size = DEFAULT_SECTOR_SIZE;
+ return 0;
+}
+#endif
+
+/*
+ * Return the alignment status of a device
+ */
+#ifdef BLKALIGNOFF
+int blkdev_is_misaligned(int fd)
+{
+ int aligned;
+
+ if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
+ return 0; /* probably kernel < 2.6.32 */
+ /*
+ * Note that kernel returns -1 as alignment offset if no compatible
+ * sizes and alignments exist for stacked devices
+ */
+ return aligned != 0 ? 1 : 0;
+}
+#else
+int blkdev_is_misaligned(int fd __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+int open_blkdev_or_file(const struct stat *st, const char *name, const int oflag)
+{
+ int fd;
+
+ if (S_ISBLK(st->st_mode)) {
+ fd = open(name, oflag | O_EXCL);
+ } else
+ fd = open(name, oflag);
+ if (-1 < fd && !is_same_inode(fd, st)) {
+ close(fd);
+ errno = EBADFD;
+ return -1;
+ }
+ if (-1 < fd && S_ISBLK(st->st_mode) && blkdev_is_misaligned(fd))
+ warnx(_("warning: %s is misaligned"), name);
+ return fd;
+}
+
+#ifdef CDROM_GET_CAPABILITY
+int blkdev_is_cdrom(int fd)
+{
+ int ret;
+
+ if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
+ return 0;
+
+ return ret;
+}
+#else
+int blkdev_is_cdrom(int fd __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+/*
+ * Get kernel's interpretation of the device's geometry.
+ *
+ * Returns the heads and sectors - but not cylinders
+ * as it's truncated for disks with more than 65535 tracks.
+ *
+ * Note that this is deprecated in favor of LBA addressing.
+ */
+#ifdef HDIO_GETGEO
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
+{
+ struct hd_geometry geometry;
+
+ if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
+ *h = geometry.heads;
+ *s = geometry.sectors;
+ return 0;
+ }
+#else
+int blkdev_get_geometry(int fd __attribute__((__unused__)),
+ unsigned int *h, unsigned int *s)
+{
+ *h = 0;
+ *s = 0;
+#endif
+ return -1;
+}
+
+/*
+ * Convert scsi type to human readable string.
+ */
+const char *blkdev_scsi_type_to_name(int type)
+{
+ switch (type) {
+ case SCSI_TYPE_DISK:
+ return "disk";
+ case SCSI_TYPE_TAPE:
+ return "tape";
+ case SCSI_TYPE_PRINTER:
+ return "printer";
+ case SCSI_TYPE_PROCESSOR:
+ return "processor";
+ case SCSI_TYPE_WORM:
+ return "worm";
+ case SCSI_TYPE_ROM:
+ return "rom";
+ case SCSI_TYPE_SCANNER:
+ return "scanner";
+ case SCSI_TYPE_MOD:
+ return "mo-disk";
+ case SCSI_TYPE_MEDIUM_CHANGER:
+ return "changer";
+ case SCSI_TYPE_COMM:
+ return "comm";
+ case SCSI_TYPE_RAID:
+ return "raid";
+ case SCSI_TYPE_ENCLOSURE:
+ return "enclosure";
+ case SCSI_TYPE_RBC:
+ return "rbc";
+ case SCSI_TYPE_OSD:
+ return "osd";
+ case SCSI_TYPE_NO_LUN:
+ return "no-lun";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/* return 0 on success */
+int blkdev_lock(int fd, const char *devname, const char *lockmode)
+{
+ int oper, rc, msg = 0;
+
+ if (!lockmode)
+ lockmode = getenv("LOCK_BLOCK_DEVICE");
+ if (!lockmode)
+ return 0;
+
+ if (strcasecmp(lockmode, "yes") == 0 ||
+ strcmp(lockmode, "1") == 0)
+ oper = LOCK_EX;
+
+ else if (strcasecmp(lockmode, "nonblock") == 0)
+ oper = LOCK_EX | LOCK_NB;
+
+ else if (strcasecmp(lockmode, "no") == 0 ||
+ strcmp(lockmode, "0") == 0)
+ return 0;
+ else {
+ warnx(_("unsupported lock mode: %s"), lockmode);
+ return -EINVAL;
+ }
+
+ if (!(oper & LOCK_NB)) {
+ /* Try non-block first to provide message */
+ rc = flock(fd, oper | LOCK_NB);
+ if (rc == 0)
+ return 0;
+ if (rc != 0 && errno == EWOULDBLOCK) {
+ fprintf(stderr, _("%s: %s: device already locked, waiting to get lock ... "),
+ program_invocation_short_name, devname);
+ msg = 1;
+ }
+ }
+ rc = flock(fd, oper);
+ if (rc != 0) {
+ switch (errno) {
+ case EWOULDBLOCK: /* LOCK_NB */
+ warnx(_("%s: device already locked"), devname);
+ break;
+ default:
+ warn(_("%s: failed to get lock"), devname);
+ }
+ } else if (msg)
+ fprintf(stderr, _("OK\n"));
+ return rc;
+}
+
+
+#ifdef TEST_PROGRAM_BLKDEV
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+int
+main(int argc, char **argv)
+{
+ unsigned long long bytes;
+ unsigned long long sectors;
+ int sector_size, phy_sector_size;
+ int fd;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s device\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0)
+ err(EXIT_FAILURE, "open %s failed", argv[1]);
+
+ if (blkdev_get_size(fd, &bytes) < 0)
+ err(EXIT_FAILURE, "blkdev_get_size() failed");
+ if (blkdev_get_sectors(fd, &sectors) < 0)
+ err(EXIT_FAILURE, "blkdev_get_sectors() failed");
+ if (blkdev_get_sector_size(fd, &sector_size) < 0)
+ err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
+ if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
+ err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
+
+ printf(" bytes: %llu\n", bytes);
+ printf(" sectors: %llu\n", sectors);
+ printf(" sector size: %d\n", sector_size);
+ printf("phy-sector size: %d\n", phy_sector_size);
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_BLKDEV */
diff --git a/utils/lib/canonicalize.c b/utils/lib/canonicalize.c
new file mode 100644
index 0000000..e101c5b
--- /dev/null
+++ b/utils/lib/canonicalize.c
@@ -0,0 +1,250 @@
+/*
+ * canonicalize.c -- canonicalize pathname by removing symlinks
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "canonicalize.h"
+#include "pathnames.h"
+#include "all-io.h"
+
+/*
+ * Converts private "dm-N" names to "/dev/mapper/<name>"
+ *
+ * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
+ * provides the real DM device names in /sys/block/<ptname>/dm/name
+ */
+char *__canonicalize_dm_name(const char *prefix, const char *ptname)
+{
+ FILE *f;
+ size_t sz;
+ char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
+
+ if (!ptname || !*ptname)
+ return NULL;
+
+ if (!prefix)
+ prefix = "";
+
+ snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
+ if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
+ return NULL;
+
+ /* read "<name>\n" from sysfs */
+ if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
+ name[sz - 1] = '\0';
+ snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
+
+ if ((prefix && *prefix) || access(path, F_OK) == 0)
+ res = strdup(path);
+ }
+ fclose(f);
+ return res;
+}
+
+char *canonicalize_dm_name(const char *ptname)
+{
+ return __canonicalize_dm_name(NULL, ptname);
+}
+
+static int is_dm_devname(char *canonical, char **name)
+{
+ struct stat sb;
+ char *p = strrchr(canonical, '/');
+
+ *name = NULL;
+
+ if (!p
+ || strncmp(p, "/dm-", 4) != 0
+ || !isdigit(*(p + 4))
+ || stat(canonical, &sb) != 0
+ || !S_ISBLK(sb.st_mode))
+ return 0;
+
+ *name = p + 1;
+ return 1;
+}
+
+/*
+ * This function does not canonicalize the path! It just prepends CWD before a
+ * relative path. If the path is no relative than returns NULL. The path does
+ * not have to exist.
+ */
+char *absolute_path(const char *path)
+{
+ char cwd[PATH_MAX], *res, *p;
+ size_t psz, csz;
+
+ if (!is_relative_path(path)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!getcwd(cwd, sizeof(cwd)))
+ return NULL;
+
+ /* simple clean up */
+ if (startswith(path, "./"))
+ path += 2;
+ else if (strcmp(path, ".") == 0)
+ path = NULL;
+
+ if (!path || !*path)
+ return strdup(cwd);
+
+ csz = strlen(cwd);
+ psz = strlen(path);
+
+ p = res = malloc(csz + 1 + psz + 1);
+ if (!res)
+ return NULL;
+
+ memcpy(p, cwd, csz);
+ p += csz;
+ *p++ = '/';
+ memcpy(p, path, psz + 1);
+
+ return res;
+}
+
+char *canonicalize_path(const char *path)
+{
+ char *canonical, *dmname;
+
+ if (!path || !*path)
+ return NULL;
+
+ canonical = realpath(path, NULL);
+ if (!canonical)
+ return strdup(path);
+
+ if (is_dm_devname(canonical, &dmname)) {
+ char *dm = canonicalize_dm_name(dmname);
+ if (dm) {
+ free(canonical);
+ return dm;
+ }
+ }
+
+ return canonical;
+}
+
+char *canonicalize_path_restricted(const char *path)
+{
+ char *canonical = NULL;
+ int errsv = 0;
+ int pipes[2];
+ ssize_t len;
+ pid_t pid;
+
+ if (!path || !*path)
+ return NULL;
+
+ if (pipe(pipes) != 0)
+ return NULL;
+
+ /*
+ * To accurately assume identity of getuid() we must use setuid()
+ * but if we do that, we lose ability to reassume euid of 0, so
+ * we fork to do the check to keep euid intact.
+ */
+ pid = fork();
+ switch (pid) {
+ case -1:
+ close(pipes[0]);
+ close(pipes[1]);
+ return NULL; /* fork error */
+ case 0:
+ close(pipes[0]); /* close unused end */
+ pipes[0] = -1;
+ errno = 0;
+
+ /* drop permissions */
+ if (setgid(getgid()) < 0 || setuid(getuid()) < 0)
+ canonical = NULL; /* failed */
+ else {
+ char *dmname = NULL;
+
+ canonical = realpath(path, NULL);
+ if (canonical && is_dm_devname(canonical, &dmname)) {
+ char *dm = canonicalize_dm_name(dmname);
+ if (dm) {
+ free(canonical);
+ canonical = dm;
+ }
+ }
+ }
+
+ len = canonical ? (ssize_t) strlen(canonical) :
+ errno ? -errno : -EINVAL;
+
+ /* send length or errno */
+ write_all(pipes[1], (char *) &len, sizeof(len));
+ if (canonical)
+ write_all(pipes[1], canonical, len);
+ exit(0);
+ default:
+ break;
+ }
+
+ close(pipes[1]); /* close unused end */
+ pipes[1] = -1;
+
+ /* read size or -errno */
+ if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
+ goto done;
+ if (len < 0) {
+ errsv = -len;
+ goto done;
+ }
+
+ canonical = malloc(len + 1);
+ if (!canonical) {
+ errsv = ENOMEM;
+ goto done;
+ }
+ /* read path */
+ if (read_all(pipes[0], canonical, len) != len) {
+ errsv = errno;
+ goto done;
+ }
+ canonical[len] = '\0';
+done:
+ if (errsv) {
+ free(canonical);
+ canonical = NULL;
+ }
+ close(pipes[0]);
+
+ /* We make a best effort to reap child */
+ waitpid(pid, NULL, 0);
+
+ errno = errsv;
+ return canonical;
+}
+
+
+#ifdef TEST_PROGRAM_CANONICALIZE
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stdout, "orig: %s\n", argv[1]);
+ fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
+ exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/utils/lib/caputils.c b/utils/lib/caputils.c
new file mode 100644
index 0000000..17e9c01
--- /dev/null
+++ b/utils/lib/caputils.c
@@ -0,0 +1,45 @@
+/*
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+
+#include "caputils.h"
+#include "pathnames.h"
+
+int cap_last_cap(void)
+{
+ /* CAP_LAST_CAP is untrustworthy. */
+ static int ret = -1;
+ int matched;
+ FILE *f;
+
+ if (ret != -1)
+ return ret;
+
+ f = fopen(_PATH_PROC_CAPLASTCAP, "r");
+ if (!f) {
+ ret = CAP_LAST_CAP; /* guess */
+ return ret;
+ }
+
+ matched = fscanf(f, "%d", &ret);
+ fclose(f);
+
+ if (matched != 1)
+ ret = CAP_LAST_CAP; /* guess */
+
+ return ret;
+}
diff --git a/utils/lib/color-names.c b/utils/lib/color-names.c
new file mode 100644
index 0000000..9b1505e
--- /dev/null
+++ b/utils/lib/color-names.c
@@ -0,0 +1,64 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include "c.h"
+#include "color-names.h"
+
+struct ul_color_name {
+ const char *name;
+ const char *seq;
+};
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_color_name(const void *a0, const void *b0)
+{
+ const struct ul_color_name
+ *a = (const struct ul_color_name *) a0,
+ *b = (const struct ul_color_name *) b0;
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * Maintains human readable color names
+ */
+const char *color_sequence_from_colorname(const char *str)
+{
+ static const struct ul_color_name basic_schemes[] = {
+ { "black", UL_COLOR_BLACK },
+ { "blink", UL_COLOR_BLINK },
+ { "blue", UL_COLOR_BLUE },
+ { "bold", UL_COLOR_BOLD },
+ { "brown", UL_COLOR_BROWN },
+ { "cyan", UL_COLOR_CYAN },
+ { "darkgray", UL_COLOR_DARK_GRAY },
+ { "gray", UL_COLOR_GRAY },
+ { "green", UL_COLOR_GREEN },
+ { "halfbright", UL_COLOR_HALFBRIGHT },
+ { "lightblue", UL_COLOR_BOLD_BLUE },
+ { "lightcyan", UL_COLOR_BOLD_CYAN },
+ { "lightgray,", UL_COLOR_GRAY },
+ { "lightgreen", UL_COLOR_BOLD_GREEN },
+ { "lightmagenta", UL_COLOR_BOLD_MAGENTA },
+ { "lightred", UL_COLOR_BOLD_RED },
+ { "magenta", UL_COLOR_MAGENTA },
+ { "red", UL_COLOR_RED },
+ { "reset", UL_COLOR_RESET, },
+ { "reverse", UL_COLOR_REVERSE },
+ { "yellow", UL_COLOR_BOLD_YELLOW },
+ { "white", UL_COLOR_WHITE }
+ };
+ struct ul_color_name key = { .name = str }, *res;
+
+ if (!str)
+ return NULL;
+
+ res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
+ sizeof(struct ul_color_name),
+ cmp_color_name);
+ return res ? res->seq : NULL;
+}
diff --git a/utils/lib/colors.c b/utils/lib/colors.c
new file mode 100644
index 0000000..e317519
--- /dev/null
+++ b/utils/lib/colors.c
@@ -0,0 +1,907 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
+# if defined(HAVE_NCURSESW_NCURSES_H)
+# include <ncursesw/ncurses.h>
+# elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+# elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+# endif
+# if defined(HAVE_NCURSESW_TERM_H)
+# include <ncursesw/term.h>
+# elif defined(HAVE_NCURSES_TERM_H)
+# include <ncurses/term.h>
+# elif defined(HAVE_TERM_H)
+# include <term.h>
+# endif
+#endif
+
+#include "c.h"
+#include "colors.h"
+#include "pathnames.h"
+#include "strutils.h"
+
+#include "debug.h"
+
+/*
+ * Default behavior, may be overridden by terminal-colors.d/{enable,disable}.
+ */
+#ifdef USE_COLORS_BY_DEFAULT
+# define UL_COLORMODE_DEFAULT UL_COLORMODE_AUTO /* check isatty() */
+#else
+# define UL_COLORMODE_DEFAULT UL_COLORMODE_NEVER /* no colors by default */
+#endif
+
+/*
+ * terminal-colors.d debug stuff
+ */
+static UL_DEBUG_DEFINE_MASK(termcolors);
+UL_DEBUG_DEFINE_MASKNAMES(termcolors) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define TERMCOLORS_DEBUG_INIT (1 << 1)
+#define TERMCOLORS_DEBUG_CONF (1 << 2)
+#define TERMCOLORS_DEBUG_SCHEME (1 << 3)
+#define TERMCOLORS_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(termcolors, TERMCOLORS_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(termcolors, TERMCOLORS_DEBUG_, m, x)
+
+/*
+ * terminal-colors.d file types
+ */
+enum {
+ UL_COLORFILE_DISABLE, /* .disable */
+ UL_COLORFILE_ENABLE, /* .enable */
+ UL_COLORFILE_SCHEME, /* .scheme */
+
+ __UL_COLORFILE_COUNT
+};
+
+struct ul_color_scheme {
+ char *name;
+ char *seq;
+};
+
+/*
+ * Global colors control struct
+ *
+ * The terminal-colors.d/ evaluation is based on "scores":
+ *
+ * filename score
+ * ---------------------------------------
+ * type 1
+ * @termname.type 10 + 1
+ * utilname.type 20 + 1
+ * utilname@termname.type 20 + 10 + 1
+ *
+ * the match with higher score wins. The score is per type.
+ */
+struct ul_color_ctl {
+ const char *utilname; /* util name */
+ const char *termname; /* terminal name ($TERM) */
+
+ char *sfile; /* path to scheme */
+
+ struct ul_color_scheme *schemes; /* array with color schemes */
+ size_t nschemes; /* number of the items */
+ size_t schemes_sz; /* number of the allocated items */
+
+ int mode; /* UL_COLORMODE_* */
+ unsigned int has_colors : 1, /* based on mode and scores[] */
+ disabled : 1, /* disable colors */
+ cs_configured : 1, /* color schemes read */
+ configured : 1; /* terminal-colors.d parsed */
+
+ int scores[__UL_COLORFILE_COUNT]; /* the best match */
+};
+
+/*
+ * Control struct, globally shared.
+ */
+static struct ul_color_ctl ul_colors;
+
+static void colors_free_schemes(struct ul_color_ctl *cc);
+static int colors_read_schemes(struct ul_color_ctl *cc);
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_scheme_name(const void *a0, const void *b0)
+{
+ const struct ul_color_scheme *a = (const struct ul_color_scheme *) a0,
+ *b = (const struct ul_color_scheme *) b0;
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * Resets control struct (note that we don't allocate the struct)
+ */
+static void colors_reset(struct ul_color_ctl *cc)
+{
+ if (!cc)
+ return;
+
+ colors_free_schemes(cc);
+
+ free(cc->sfile);
+
+ cc->sfile = NULL;
+ cc->utilname = NULL;
+ cc->termname = NULL;
+ cc->mode = UL_COLORMODE_UNDEF;
+
+ memset(cc->scores, 0, sizeof(cc->scores));
+}
+
+static void colors_debug(struct ul_color_ctl *cc)
+{
+ size_t i;
+
+ if (!cc)
+ return;
+
+ printf("Colors:\n");
+ printf("\tutilname = '%s'\n", cc->utilname);
+ printf("\ttermname = '%s'\n", cc->termname);
+ printf("\tscheme file = '%s'\n", cc->sfile);
+ printf("\tmode = %s\n",
+ cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
+ cc->mode == UL_COLORMODE_AUTO ? "auto" :
+ cc->mode == UL_COLORMODE_NEVER ? "never" :
+ cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
+ printf("\thas_colors = %d\n", cc->has_colors);
+ printf("\tdisabled = %d\n", cc->disabled);
+ printf("\tconfigured = %d\n", cc->configured);
+ printf("\tcs configured = %d\n", cc->cs_configured);
+
+ fputc('\n', stdout);
+
+ for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
+ printf("\tscore %s = %d\n",
+ i == UL_COLORFILE_DISABLE ? "disable" :
+ i == UL_COLORFILE_ENABLE ? "enable" :
+ i == UL_COLORFILE_SCHEME ? "scheme" : "???",
+ cc->scores[i]);
+
+ fputc('\n', stdout);
+
+ for (i = 0; i < cc->nschemes; i++) {
+ printf("\tscheme #%02zu ", i);
+ color_scheme_enable(cc->schemes[i].name, NULL);
+ fputs(cc->schemes[i].name, stdout);
+ color_disable();
+ fputc('\n', stdout);
+ }
+ fputc('\n', stdout);
+}
+
+/*
+ * Parses [[<utilname>][@<termname>].]<type>
+ */
+static int filename_to_tokens(const char *str,
+ const char **name, size_t *namesz,
+ const char **term, size_t *termsz,
+ int *filetype)
+{
+ const char *type_start, *term_start, *p;
+
+ if (!str || !*str || *str == '.' || strlen(str) > PATH_MAX)
+ return -EINVAL;
+
+ /* parse .type */
+ p = strrchr(str, '.');
+ type_start = p ? p + 1 : str;
+
+ if (strcmp(type_start, "disable") == 0)
+ *filetype = UL_COLORFILE_DISABLE;
+ else if (strcmp(type_start, "enable") == 0)
+ *filetype = UL_COLORFILE_ENABLE;
+ else if (strcmp(type_start, "scheme") == 0)
+ *filetype = UL_COLORFILE_SCHEME;
+ else {
+ DBG(CONF, ul_debug("unknown type '%s'", type_start));
+ return 1; /* unknown type */
+ }
+
+ if (type_start == str)
+ return 0; /* "type" only */
+
+ /* parse @termname */
+ p = strchr(str, '@');
+ term_start = p ? p + 1 : NULL;
+ if (term_start) {
+ *term = term_start;
+ *termsz = type_start - term_start - 1;
+ if (term_start - 1 == str)
+ return 0; /* "@termname.type" */
+ }
+
+ /* parse utilname */
+ p = term_start ? term_start : type_start;
+ *name = str;
+ *namesz = p - str - 1;
+
+ return 0;
+}
+
+/*
+ * Scans @dirname and select the best matches for UL_COLORFILE_* types.
+ * The result is stored to cc->scores. The path to the best "scheme"
+ * file is stored to cc->scheme.
+ */
+static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
+{
+ DIR *dir;
+ int rc = 0;
+ struct dirent *d;
+ char sfile[PATH_MAX] = { '\0' };
+ size_t namesz, termsz;
+
+ if (!dirname || !cc || !cc->utilname || !*cc->utilname)
+ return -EINVAL;
+
+ DBG(CONF, ul_debug("reading dir: '%s'", dirname));
+
+ dir = opendir(dirname);
+ if (!dir)
+ return -errno;
+
+ namesz = strlen(cc->utilname);
+ termsz = cc->termname ? strlen(cc->termname) : 0;
+
+ while ((d = readdir(dir))) {
+ int type, score = 1;
+ const char *tk_name = NULL, *tk_term = NULL;
+ size_t tk_namesz = 0, tk_termsz = 0;
+
+ if (*d->d_name == '.')
+ continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK &&
+ d->d_type != DT_REG)
+ continue;
+#endif
+ if (filename_to_tokens(d->d_name,
+ &tk_name, &tk_namesz,
+ &tk_term, &tk_termsz, &type) != 0)
+ continue;
+
+ /* count theoretical score before we check names to avoid
+ * unnecessary strcmp() */
+ if (tk_name)
+ score += 20;
+ if (tk_term)
+ score += 10;
+
+ DBG(CONF, ul_debug("item '%s': score=%d "
+ "[cur: %d, name(%zu): %s, term(%zu): %s]",
+ d->d_name, score, cc->scores[type],
+ tk_namesz, tk_name,
+ tk_termsz, tk_term));
+
+
+ if (score < cc->scores[type])
+ continue;
+
+ /* filter out by names */
+ if (tk_namesz && (tk_namesz != namesz ||
+ strncmp(tk_name, cc->utilname, namesz) != 0))
+ continue;
+
+ if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
+ strncmp(tk_term, cc->termname, termsz) != 0))
+ continue;
+
+ DBG(CONF, ul_debug("setting '%s' from %d -to-> %d",
+ type == UL_COLORFILE_SCHEME ? "scheme" :
+ type == UL_COLORFILE_DISABLE ? "disable" :
+ type == UL_COLORFILE_ENABLE ? "enable" : "???",
+ cc->scores[type], score));
+ cc->scores[type] = score;
+ if (type == UL_COLORFILE_SCHEME)
+ strncpy(sfile, d->d_name, sizeof(sfile));
+ }
+
+ if (*sfile) {
+ sfile[sizeof(sfile) - 1] = '\0';
+ if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
+ rc = -ENOMEM;
+ }
+
+ closedir(dir);
+ return rc;
+}
+
+/* atexit() wrapper */
+static void colors_deinit(void)
+{
+ colors_reset(&ul_colors);
+}
+
+/*
+ * Returns path to $XDG_CONFIG_HOME/terminal-colors.d
+ */
+static char *colors_get_homedir(char *buf, size_t bufsz)
+{
+ char *p = getenv("XDG_CONFIG_HOME");
+
+ if (p) {
+ snprintf(buf, bufsz, "%s/" _PATH_TERMCOLORS_DIRNAME, p);
+ return buf;
+ }
+
+ p = getenv("HOME");
+ if (p) {
+ snprintf(buf, bufsz, "%s/.config/" _PATH_TERMCOLORS_DIRNAME, p);
+ return buf;
+ }
+
+ return NULL;
+}
+
+/* canonicalize sequence */
+static int cn_sequence(const char *str, char **seq)
+{
+ char *in, *out;
+ int len;
+
+ if (!str)
+ return -EINVAL;
+
+ *seq = NULL;
+
+ /* convert logical names like "red" to the real sequence */
+ if (*str != '\\' && isalpha(*str)) {
+ const char *s = color_sequence_from_colorname(str);
+ *seq = strdup(s ? s : str);
+
+ return *seq ? 0 : -ENOMEM;
+ }
+
+ /* convert xx;yy sequences to "\033[xx;yy" */
+ if ((len = asprintf(seq, "\033[%sm", str)) < 1)
+ return -ENOMEM;
+
+ for (in = *seq, out = *seq; in && *in; in++) {
+ if (*in != '\\') {
+ *out++ = *in;
+ continue;
+ }
+ switch(*(in + 1)) {
+ case 'a':
+ *out++ = '\a'; /* Bell */
+ break;
+ case 'b':
+ *out++ = '\b'; /* Backspace */
+ break;
+ case 'e':
+ *out++ = '\033'; /* Escape */
+ break;
+ case 'f':
+ *out++ = '\f'; /* Form Feed */
+ break;
+ case 'n':
+ *out++ = '\n'; /* Newline */
+ break;
+ case 'r':
+ *out++ = '\r'; /* Carriage Return */
+ break;
+ case 't':
+ *out++ = '\t'; /* Tab */
+ break;
+ case 'v':
+ *out++ = '\v'; /* Vertical Tab */
+ break;
+ case '\\':
+ *out++ = '\\'; /* Backslash */
+ break;
+ case '_':
+ *out++ = ' '; /* Space */
+ break;
+ case '#':
+ *out++ = '#'; /* Hash mark */
+ break;
+ case '?':
+ *out++ = '?'; /* Question mark */
+ break;
+ default:
+ *out++ = *in;
+ *out++ = *(in + 1);
+ break;
+ }
+ in++;
+ }
+
+ if (out) {
+ assert ((out - *seq) <= len);
+ *out = '\0';
+ }
+
+ return 0;
+}
+
+
+/*
+ * 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)
+{
+ struct ul_color_scheme *cs = NULL;
+ char *seq = NULL;
+ int rc;
+
+ if (!cc || !name || !*name || !seq0 || !*seq0)
+ return -EINVAL;
+
+ DBG(SCHEME, ul_debug("add '%s'", name));
+
+ rc = cn_sequence(seq0, &seq);
+ if (rc)
+ return rc;
+
+ rc = -ENOMEM;
+
+ /* convert logical name (e.g. "red") to real ESC code */
+ if (isalpha(*seq)) {
+ const char *s = color_sequence_from_colorname(seq);
+ char *p;
+
+ if (!s) {
+ DBG(SCHEME, ul_debug("unknown logical name: %s", seq));
+ rc = -EINVAL;
+ goto err;
+ }
+
+ p = strdup(s);
+ if (!p)
+ goto err;
+ free(seq);
+ seq = p;
+ }
+
+ /* enlarge the array */
+ if (cc->nschemes == cc->schemes_sz) {
+ void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
+ * sizeof(struct ul_color_scheme));
+ if (!tmp)
+ goto err;
+ cc->schemes = tmp;
+ cc->schemes_sz = cc->nschemes + 10;
+ }
+
+ /* add a new item */
+ cs = &cc->schemes[cc->nschemes];
+ cs->seq = seq;
+ cs->name = strdup(name);
+ if (!cs->name)
+ goto err;
+
+ cc->nschemes++;
+ return 0;
+err:
+ if (cs) {
+ free(cs->seq);
+ free(cs->name);
+ cs->seq = cs->name = NULL;
+ } else
+ free(seq);
+ return rc;
+}
+
+/*
+ * Deallocates all regards to color schemes
+ */
+static void colors_free_schemes(struct ul_color_ctl *cc)
+{
+ size_t i;
+
+ DBG(SCHEME, ul_debug("free scheme"));
+
+ for (i = 0; i < cc->nschemes; i++) {
+ free(cc->schemes[i].name);
+ free(cc->schemes[i].seq);
+ }
+
+ free(cc->schemes);
+ cc->schemes = NULL;
+ cc->nschemes = 0;
+ cc->schemes_sz = 0;
+}
+
+/*
+ * The scheme configuration has to be sorted for bsearch
+ */
+static void colors_sort_schemes(struct ul_color_ctl *cc)
+{
+ if (!cc->nschemes)
+ return;
+
+ DBG(SCHEME, ul_debug("sort scheme"));
+
+ qsort(cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme), cmp_scheme_name);
+}
+
+/*
+ * Returns just one color scheme
+ */
+static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
+ const char *name)
+{
+ struct ul_color_scheme key = { .name = (char *) name}, *res;
+
+ if (!cc || !name || !*name)
+ return NULL;
+
+ if (!cc->cs_configured) {
+ int rc = colors_read_schemes(cc);
+ if (rc)
+ return NULL;
+ }
+ if (!cc->nschemes)
+ return NULL;
+
+ DBG(SCHEME, ul_debug("search '%s'", name));
+
+ res = bsearch(&key, cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme),
+ cmp_scheme_name);
+
+ return res && res->seq ? res : NULL;
+}
+
+/*
+ * Parses filenames in terminal-colors.d
+ */
+static int colors_read_configuration(struct ul_color_ctl *cc)
+{
+ int rc = -ENOENT;
+ char *dirname, buf[PATH_MAX];
+
+ cc->termname = getenv("TERM");
+
+ dirname = colors_get_homedir(buf, sizeof(buf));
+ if (dirname)
+ rc = colors_readdir(cc, dirname); /* ~/.config */
+ if (rc == -EPERM || rc == -EACCES || rc == -ENOENT)
+ rc = colors_readdir(cc, _PATH_TERMCOLORS_DIR); /* /etc */
+
+ cc->configured = 1;
+ return rc;
+}
+
+/*
+ * Reads terminal-colors.d/ scheme file into array schemes
+ */
+static int colors_read_schemes(struct ul_color_ctl *cc)
+{
+ int rc = 0;
+ FILE *f = NULL;
+ char buf[BUFSIZ],
+ cn[129], seq[129];
+
+ if (!cc->configured)
+ rc = colors_read_configuration(cc);
+
+ cc->cs_configured = 1;
+
+ if (rc || !cc->sfile)
+ goto done;
+
+ DBG(SCHEME, ul_debug("reading file '%s'", cc->sfile));
+
+ f = fopen(cc->sfile, "r");
+ if (!f) {
+ rc = -errno;
+ goto done;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ char *p = strchr(buf, '\n');
+
+ if (!p) {
+ if (feof(f))
+ p = strchr(buf, '\0');
+ else {
+ rc = -errno;
+ goto done;
+ }
+ }
+ *p = '\0';
+ p = (char *) skip_blank(buf);
+ if (*p == '\0' || *p == '#')
+ continue;
+
+ rc = sscanf(p, "%128[^ ] %128[^\n ]", cn, seq);
+ if (rc == 2 && *cn && *seq) {
+ rc = colors_add_scheme(cc, cn, seq); /* set rc=0 on success */
+ if (rc)
+ goto done;
+ }
+ }
+ rc = 0;
+
+done:
+ if (f)
+ fclose(f);
+ colors_sort_schemes(cc);
+
+ return rc;
+}
+
+
+static void termcolors_init_debug(void)
+{
+ __UL_INIT_DEBUG_FROM_ENV(termcolors, TERMCOLORS_DEBUG_, 0, TERMINAL_COLORS_DEBUG);
+}
+
+static int colors_terminal_is_ready(void)
+{
+ int ncolors = -1;
+
+#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
+ {
+ int ret;
+
+ if (setupterm(NULL, STDOUT_FILENO, &ret) == 0 && ret == 1)
+ ncolors = tigetnum("colors");
+ }
+#endif
+ if (1 < ncolors) {
+ DBG(CONF, ul_debug("terminal is ready (supports %d colors)", ncolors));
+ return 1;
+ }
+
+ DBG(CONF, ul_debug("terminal is NOT ready (no colors)"));
+ return 0;
+}
+
+/**
+ * colors_init:
+ * @mode: UL_COLORMODE_*
+ * @name: util argv[0]
+ *
+ * Initialize private color control struct and initialize the colors
+ * status. The color schemes are parsed on demand by colors_get_scheme().
+ *
+ * Returns: >0 on success.
+ */
+int colors_init(int mode, const char *name)
+{
+ int ready = -1;
+ struct ul_color_ctl *cc = &ul_colors;
+
+ cc->utilname = name;
+
+ termcolors_init_debug();
+
+ if (mode != UL_COLORMODE_ALWAYS && !isatty(STDOUT_FILENO))
+ cc->mode = UL_COLORMODE_NEVER;
+ else
+ cc->mode = mode;
+
+ if (cc->mode == UL_COLORMODE_UNDEF
+ && (ready = colors_terminal_is_ready())) {
+ int rc = colors_read_configuration(cc);
+ if (rc)
+ cc->mode = UL_COLORMODE_DEFAULT;
+ else {
+
+ /* evaluate scores */
+ if (cc->scores[UL_COLORFILE_DISABLE] >
+ cc->scores[UL_COLORFILE_ENABLE])
+ cc->mode = UL_COLORMODE_NEVER;
+ else
+ cc->mode = UL_COLORMODE_DEFAULT;
+
+ atexit(colors_deinit);
+ }
+ }
+
+ switch (cc->mode) {
+ case UL_COLORMODE_AUTO:
+ cc->has_colors = ready == -1 ? colors_terminal_is_ready() : ready;
+ break;
+ case UL_COLORMODE_ALWAYS:
+ cc->has_colors = 1;
+ break;
+ case UL_COLORMODE_NEVER:
+ default:
+ cc->has_colors = 0;
+ }
+
+ ON_DBG(CONF, colors_debug(cc));
+
+ return cc->has_colors;
+}
+
+/*
+ * Temporary disable colors (this setting is independent on terminal-colors.d/)
+ */
+void colors_off(void)
+{
+ ul_colors.disabled = 1;
+}
+
+/*
+ * Enable colors
+ */
+void colors_on(void)
+{
+ ul_colors.disabled = 0;
+}
+
+/*
+ * Is terminal-colors.d/ configured to use colors?
+ */
+int colors_wanted(void)
+{
+ return ul_colors.has_colors;
+}
+
+/*
+ * Returns mode
+ */
+int colors_mode(void)
+{
+ return ul_colors.mode;
+}
+
+/*
+ * Enable @seq color
+ */
+void color_fenable(const char *seq, FILE *f)
+{
+ if (!ul_colors.disabled && ul_colors.has_colors && seq)
+ fputs(seq, f);
+}
+
+/*
+ * Returns escape sequence by logical @name, if undefined then returns @dflt.
+ */
+const char *color_scheme_get_sequence(const char *name, const char *dflt)
+{
+ struct ul_color_scheme *cs;
+
+ if (ul_colors.disabled || !ul_colors.has_colors)
+ return NULL;
+
+ cs = colors_get_scheme(&ul_colors, name);
+ return cs && cs->seq ? cs->seq : dflt;
+}
+
+/*
+ * Enable color by logical @name, if undefined enable @dflt.
+ */
+void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
+{
+ const char *seq = color_scheme_get_sequence(name, dflt);
+
+ if (!seq)
+ return;
+ color_fenable(seq, f);
+}
+
+
+/*
+ * Disable previously enabled color
+ */
+void color_fdisable(FILE *f)
+{
+ if (!ul_colors.disabled && ul_colors.has_colors)
+ fputs(UL_COLOR_RESET, f);
+}
+
+/*
+ * Parses @str to return UL_COLORMODE_*
+ */
+int colormode_from_string(const char *str)
+{
+ size_t i;
+ static const char *modes[] = {
+ [UL_COLORMODE_AUTO] = "auto",
+ [UL_COLORMODE_NEVER] = "never",
+ [UL_COLORMODE_ALWAYS] = "always",
+ [UL_COLORMODE_UNDEF] = ""
+ };
+
+ if (!str || !*str)
+ return -EINVAL;
+
+ assert(ARRAY_SIZE(modes) == __UL_NCOLORMODES);
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ if (strcasecmp(str, modes[i]) == 0)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Parses @str and exit(EXIT_FAILURE) on error
+ */
+int colormode_or_err(const char *str, const char *errmsg)
+{
+ const char *p = str && *str == '=' ? str + 1 : str;
+ int colormode;
+
+ colormode = colormode_from_string(p);
+ if (colormode < 0)
+ errx(EXIT_FAILURE, "%s: '%s'", errmsg, p);
+
+ return colormode;
+}
+
+#ifdef TEST_PROGRAM_COLORS
+# include <getopt.h>
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "color", required_argument, NULL, 'c' },
+ { "color-scheme", required_argument, NULL, 'C' },
+ { "name", required_argument, NULL, 'n' },
+ { NULL, 0, NULL, 0 }
+ };
+ int c, mode = UL_COLORMODE_UNDEF; /* default */
+ const char *color = "red", *name = NULL, *color_scheme = NULL;
+ const char *seq = NULL;
+
+ while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ color = optarg;
+ break;
+ case 'C':
+ color_scheme = optarg;
+ break;
+ case 'm':
+ mode = colormode_or_err(optarg, "unsupported color mode");
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [options]\n"
+ " -m, --mode <auto|never|always> default is undefined\n"
+ " -c, --color <red|blue|...> color for the test message\n"
+ " -C, --color-scheme <name> color for the test message\n"
+ " -n, --name <utilname> util name\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ colors_init(mode, name ? name : program_invocation_short_name);
+
+ seq = color_sequence_from_colorname(color);
+
+ if (color_scheme)
+ color_scheme_enable(color_scheme, seq);
+ else
+ color_enable(seq);
+ printf("Hello World!");
+ color_disable();
+ fputc('\n', stdout);
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_COLORS */
+
diff --git a/utils/lib/cpuset.c b/utils/lib/cpuset.c
new file mode 100644
index 0000000..2847db8
--- /dev/null
+++ b/utils/lib/cpuset.c
@@ -0,0 +1,413 @@
+/*
+ * Terminology:
+ *
+ * cpuset - (libc) cpu_set_t data structure represents set of CPUs
+ * cpumask - string with hex mask (e.g. "0x00000001")
+ * cpulist - string with CPU ranges (e.g. "0-3,5,7,8")
+ *
+ * Based on code from taskset.c and Linux kernel.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/syscall.h>
+
+#include "cpuset.h"
+#include "c.h"
+
+static inline int val_to_char(int v)
+{
+ if (v >= 0 && v < 10)
+ return '0' + v;
+ if (v >= 10 && v < 16)
+ return ('a' - 10) + v;
+ return -1;
+}
+
+static inline int char_to_val(int c)
+{
+ int cl;
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ cl = tolower(c);
+ if (cl >= 'a' && cl <= 'f')
+ return cl + (10 - 'a');
+ return -1;
+}
+
+static const char *nexttoken(const char *q, int sep)
+{
+ if (q)
+ q = strchr(q, sep);
+ if (q)
+ q++;
+ return q;
+}
+
+/*
+ * Number of bits in a CPU bitmask on current system
+ */
+int get_max_number_of_cpus(void)
+{
+#ifdef SYS_sched_getaffinity
+ int n, cpus = 2048;
+ size_t setsize;
+ cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
+
+ if (!set)
+ return -1; /* error */
+
+ for (;;) {
+ CPU_ZERO_S(setsize, set);
+
+ /* the library version does not return size of cpumask_t */
+ n = syscall(SYS_sched_getaffinity, 0, setsize, set);
+
+ if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
+ cpuset_free(set);
+ cpus *= 2;
+ set = cpuset_alloc(cpus, &setsize, NULL);
+ if (!set)
+ return -1; /* error */
+ continue;
+ }
+ cpuset_free(set);
+ return n * 8;
+ }
+#endif
+ return -1;
+}
+
+/*
+ * Allocates a new set for ncpus and returns size in bytes and size in bits
+ */
+cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits)
+{
+ cpu_set_t *set = CPU_ALLOC(ncpus);
+
+ if (!set)
+ return NULL;
+ if (setsize)
+ *setsize = CPU_ALLOC_SIZE(ncpus);
+ if (nbits)
+ *nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus));
+ return set;
+}
+
+void cpuset_free(cpu_set_t *set)
+{
+ CPU_FREE(set);
+}
+
+#if !HAVE_DECL_CPU_ALLOC
+/* Please, use CPU_COUNT_S() macro. This is fallback */
+int __cpuset_count_s(size_t setsize, const cpu_set_t *set)
+{
+ int s = 0;
+ const __cpu_mask *p = set->__bits;
+ const __cpu_mask *end = &set->__bits[setsize / sizeof (__cpu_mask)];
+
+ while (p < end) {
+ __cpu_mask l = *p++;
+
+ if (l == 0)
+ continue;
+# if LONG_BIT > 32
+ l = (l & 0x5555555555555555ul) + ((l >> 1) & 0x5555555555555555ul);
+ l = (l & 0x3333333333333333ul) + ((l >> 2) & 0x3333333333333333ul);
+ l = (l & 0x0f0f0f0f0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0f0f0f0f0ful);
+ l = (l & 0x00ff00ff00ff00fful) + ((l >> 8) & 0x00ff00ff00ff00fful);
+ l = (l & 0x0000ffff0000fffful) + ((l >> 16) & 0x0000ffff0000fffful);
+ l = (l & 0x00000000fffffffful) + ((l >> 32) & 0x00000000fffffffful);
+# else
+ l = (l & 0x55555555ul) + ((l >> 1) & 0x55555555ul);
+ l = (l & 0x33333333ul) + ((l >> 2) & 0x33333333ul);
+ l = (l & 0x0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0ful);
+ l = (l & 0x00ff00fful) + ((l >> 8) & 0x00ff00fful);
+ l = (l & 0x0000fffful) + ((l >> 16) & 0x0000fffful);
+# endif
+ s += l;
+ }
+ return s;
+}
+#endif
+
+/*
+ * Returns human readable representation of the cpuset. The output format is
+ * a list of CPUs with ranges (for example, "0,1,3-9").
+ */
+char *cpulist_create(char *str, size_t len,
+ cpu_set_t *set, size_t setsize)
+{
+ size_t i;
+ char *ptr = str;
+ int entry_made = 0;
+ size_t max = cpuset_nbits(setsize);
+
+ for (i = 0; i < max; i++) {
+ if (CPU_ISSET_S(i, setsize, set)) {
+ int rlen;
+ size_t j, run = 0;
+ entry_made = 1;
+ for (j = i + 1; j < max; j++) {
+ if (CPU_ISSET_S(j, setsize, set))
+ run++;
+ else
+ break;
+ }
+ if (!run)
+ rlen = snprintf(ptr, len, "%zu,", i);
+ else if (run == 1) {
+ rlen = snprintf(ptr, len, "%zu,%zu,", i, i + 1);
+ i++;
+ } else {
+ rlen = snprintf(ptr, len, "%zu-%zu,", i, i + run);
+ i += run;
+ }
+ if (rlen < 0 || (size_t) rlen >= len)
+ return NULL;
+ ptr += rlen;
+ len -= rlen;
+ }
+ }
+ ptr -= entry_made;
+ *ptr = '\0';
+
+ return str;
+}
+
+/*
+ * Returns string with CPU mask.
+ */
+char *cpumask_create(char *str, size_t len,
+ cpu_set_t *set, size_t setsize)
+{
+ char *ptr = str;
+ char *ret = NULL;
+ int cpu;
+
+ for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) {
+ char val = 0;
+
+ if (len == (size_t) (ptr - str))
+ break;
+
+ if (CPU_ISSET_S(cpu, setsize, set))
+ val |= 1;
+ if (CPU_ISSET_S(cpu + 1, setsize, set))
+ val |= 2;
+ if (CPU_ISSET_S(cpu + 2, setsize, set))
+ val |= 4;
+ if (CPU_ISSET_S(cpu + 3, setsize, set))
+ val |= 8;
+
+ if (!ret && val)
+ ret = ptr;
+ *ptr++ = val_to_char(val);
+ }
+ *ptr = '\0';
+ return ret ? ret : ptr - 1;
+}
+
+/*
+ * Parses string with CPUs mask.
+ */
+int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
+{
+ int len = strlen(str);
+ const char *ptr = str + len - 1;
+ int cpu = 0;
+
+ /* skip 0x, it's all hex anyway */
+ if (len > 1 && !memcmp(str, "0x", 2L))
+ str += 2;
+
+ CPU_ZERO_S(setsize, set);
+
+ while (ptr >= str) {
+ char val;
+
+ /* cpu masks in /sys uses comma as a separator */
+ if (*ptr == ',')
+ ptr--;
+
+ val = char_to_val(*ptr);
+ if (val == (char) -1)
+ return -1;
+ if (val & 1)
+ CPU_SET_S(cpu, setsize, set);
+ if (val & 2)
+ CPU_SET_S(cpu + 1, setsize, set);
+ if (val & 4)
+ CPU_SET_S(cpu + 2, setsize, set);
+ if (val & 8)
+ CPU_SET_S(cpu + 3, setsize, set);
+ ptr--;
+ cpu += 4;
+ }
+
+ return 0;
+}
+
+static int nextnumber(const char *str, char **end, unsigned int *result)
+{
+ errno = 0;
+ if (str == NULL || *str == '\0' || !isdigit(*str))
+ return -EINVAL;
+ *result = (unsigned int) strtoul(str, end, 10);
+ if (errno)
+ return -errno;
+ if (str == *end)
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * Parses string with list of CPU ranges.
+ * Returns 0 on success.
+ * Returns 1 on error.
+ * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
+ * into the cpu_set. If fail is not set cpu numbers that do not fit are
+ * ignored and 0 is returned instead.
+ */
+int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
+{
+ size_t max = cpuset_nbits(setsize);
+ const char *p, *q;
+ char *end = NULL;
+
+ q = str;
+ CPU_ZERO_S(setsize, set);
+
+ while (p = q, q = nexttoken(q, ','), p) {
+ unsigned int a; /* beginning of range */
+ unsigned int b; /* end of range */
+ unsigned int s; /* stride */
+ const char *c1, *c2;
+
+ if (nextnumber(p, &end, &a) != 0)
+ return 1;
+ b = a;
+ s = 1;
+ p = end;
+
+ c1 = nexttoken(p, '-');
+ c2 = nexttoken(p, ',');
+
+ if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+ if (nextnumber(c1, &end, &b) != 0)
+ return 1;
+
+ c1 = end && *end ? nexttoken(end, ':') : NULL;
+
+ if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+ if (nextnumber(c1, &end, &s) != 0)
+ return 1;
+ if (s == 0)
+ return 1;
+ }
+ }
+
+ if (!(a <= b))
+ return 1;
+ while (a <= b) {
+ if (fail && (a >= max))
+ return 2;
+ CPU_SET_S(a, setsize, set);
+ a += s;
+ }
+ }
+
+ if (end && *end)
+ return 1;
+ return 0;
+}
+
+#ifdef TEST_PROGRAM_CPUSET
+
+#include <getopt.h>
+
+int main(int argc, char *argv[])
+{
+ cpu_set_t *set;
+ size_t setsize, buflen, nbits;
+ char *buf, *mask = NULL, *range = NULL;
+ int ncpus = 2048, rc, c;
+
+ static const struct option longopts[] = {
+ { "ncpus", 1, NULL, 'n' },
+ { "mask", 1, NULL, 'm' },
+ { "range", 1, NULL, 'r' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "n:m:r:", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'n':
+ ncpus = atoi(optarg);
+ break;
+ case 'm':
+ mask = strdup(optarg);
+ break;
+ case 'r':
+ range = strdup(optarg);
+ break;
+ default:
+ goto usage_err;
+ }
+ }
+
+ if (!mask && !range)
+ goto usage_err;
+
+ set = cpuset_alloc(ncpus, &setsize, &nbits);
+ if (!set)
+ err(EXIT_FAILURE, "failed to allocate cpu set");
+
+ /*
+ fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
+ ncpus, nbits, setsize);
+ */
+
+ buflen = 7 * nbits;
+ buf = malloc(buflen);
+ if (!buf)
+ err(EXIT_FAILURE, "failed to allocate cpu set buffer");
+
+ if (mask)
+ rc = cpumask_parse(mask, set, setsize);
+ else
+ rc = cpulist_parse(range, set, setsize, 0);
+
+ if (rc)
+ errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
+
+ printf("%-15s = %15s ", mask ? : range,
+ cpumask_create(buf, buflen, set, setsize));
+ printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
+
+ free(buf);
+ free(mask);
+ free(range);
+ cpuset_free(set);
+
+ return EXIT_SUCCESS;
+
+usage_err:
+ fprintf(stderr,
+ "usage: %s [--ncpus <num>] --mask <mask> | --range <list>\n",
+ program_invocation_short_name);
+ exit(EXIT_FAILURE);
+}
+#endif
diff --git a/utils/lib/crc32.c b/utils/lib/crc32.c
new file mode 100644
index 0000000..824693d
--- /dev/null
+++ b/utils/lib/crc32.c
@@ -0,0 +1,142 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1.
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way,
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to high-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly.
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera-
+ * tions for all combinations of data and CRC register values.
+ *
+ * The values must be right-shifted by eight bits by the "updcrc"
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions.
+ * polynomial $edb88320
+ *
+ */
+
+#include <stdio.h>
+
+#include "crc32.h"
+
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+static inline uint32_t crc32_add_char(uint32_t crc, unsigned char c)
+{
+ return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);
+}
+
+/*
+ * This a generic crc32() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint32_t ul_crc32(uint32_t seed, const unsigned char *buf, size_t len)
+{
+ uint32_t crc = seed;
+ const unsigned char *p = buf;
+
+ while (len) {
+ crc = crc32_add_char(crc, *p++);
+ len--;
+ }
+
+ return crc;
+}
+
+uint32_t ul_crc32_exclude_offset(uint32_t seed, const unsigned char *buf, size_t len,
+ size_t exclude_off, size_t exclude_len)
+{
+ uint32_t crc = seed;
+ const unsigned char *p = buf;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ unsigned char x = *p++;
+
+ if (i >= exclude_off && i < exclude_off + exclude_len)
+ x = 0;
+
+ crc = crc32_add_char(crc, x);
+ }
+
+ return crc;
+}
+
diff --git a/utils/lib/crc32c.c b/utils/lib/crc32c.c
new file mode 100644
index 0000000..49e7543
--- /dev/null
+++ b/utils/lib/crc32c.c
@@ -0,0 +1,102 @@
+/*
+ * This code is from freebsd/sys/libkern/crc32.c
+ *
+ * Simplest table-based crc32c. Performance is not important
+ * for checking crcs on superblocks
+ */
+
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+#include "crc32c.h"
+
+static const uint32_t crc32Table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+/*
+ *This was singletable_crc32c() in bsd
+ *
+ * If you will not be passing crc back into this function to process more bytes,
+ * the answer is:
+ *
+ * crc = crc32c(~0L, buf, size);
+ * [ crc = crc32c(crc, buf, size); ]
+ * crc ^= ~0L
+ *
+ */
+uint32_t
+crc32c(uint32_t crc, const void *buf, size_t size)
+{
+ const uint8_t *p = buf;
+
+ while (size--)
+ crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+
+ return crc;
+}
diff --git a/utils/lib/encode.c b/utils/lib/encode.c
new file mode 100644
index 0000000..10b5971
--- /dev/null
+++ b/utils/lib/encode.c
@@ -0,0 +1,79 @@
+/*
+ * Based on code from libblkid,
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2020 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "c.h"
+#include "encode.h"
+
+size_t ul_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+ const unsigned char *src, size_t count)
+{
+ size_t i, j;
+ uint32_t c;
+ uint16_t c2;
+
+ for (j = i = 0; i < count; i++) {
+ if (enc == UL_ENCODE_UTF16LE) {
+ if (i+2 > count)
+ break;
+ c = (src[i+1] << 8) | src[i];
+ i++;
+ } else if (enc == UL_ENCODE_UTF16BE) {
+ if (i+2 > count)
+ break;
+ c = (src[i] << 8) | src[i+1];
+ i++;
+ } else if (enc == UL_ENCODE_LATIN1) {
+ c = src[i];
+ } else {
+ return 0;
+ }
+ if ((enc == UL_ENCODE_UTF16LE || enc == UL_ENCODE_UTF16BE) &&
+ c >= 0xD800 && c <= 0xDBFF && i+2 < count) {
+ if (enc == UL_ENCODE_UTF16LE)
+ c2 = (src[i+2] << 8) | src[i+1];
+ else
+ c2 = (src[i+1] << 8) | src[i+2];
+ if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
+ c = 0x10000 + ((c - 0xD800) << 10) + (c2 - 0xDC00);
+ i += 2;
+ }
+ }
+ if (c == 0) {
+ dest[j] = '\0';
+ break;
+ }
+
+ if (c < 0x80) {
+ if (j+1 >= len)
+ break;
+ dest[j++] = (uint8_t) c;
+ } else if (c < 0x800) {
+ if (j+2 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ } else if (c < 0x10000) {
+ if (j+3 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+ dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ } else {
+ if (j+4 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xf0 | (c >> 18));
+ dest[j++] = (uint8_t) (0x80 | ((c >> 12) & 0x3f));
+ dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ }
+ }
+ dest[j] = '\0';
+ return j;
+}
diff --git a/utils/lib/env.c b/utils/lib/env.c
new file mode 100644
index 0000000..c26a5be
--- /dev/null
+++ b/utils/lib/env.c
@@ -0,0 +1,238 @@
+/*
+ * Security checks of environment
+ * Added from shadow-utils package
+ * by Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "env.h"
+
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+static char * const forbid[] = {
+ "BASH_ENV=", /* GNU creeping featurism strikes again... */
+ "ENV=",
+ "HOME=",
+ "IFS=",
+ "KRB_CONF=",
+ "LD_", /* anything with the LD_ prefix */
+ "LIBPATH=",
+ "MAIL=",
+ "NLSPATH=",
+ "PATH=",
+ "SHELL=",
+ "SHLIB_PATH=",
+ (char *) 0
+};
+
+/* these are allowed, but with no slashes inside
+ (to work around security problems in GNU gettext) */
+static char * const noslash[] = {
+ "LANG=",
+ "LANGUAGE=",
+ "LC_", /* anything with the LC_ prefix */
+ (char *) 0
+};
+
+
+struct ul_env_list {
+ char *env;
+ struct ul_env_list *next;
+};
+
+/*
+ * Saves @name env.varable to @ls, returns pointer to the new head of the list.
+ */
+static struct ul_env_list *env_list_add(struct ul_env_list *ls0, const char *str)
+{
+ struct ul_env_list *ls;
+ char *p;
+ size_t sz = 0;
+
+ if (!str || !*str)
+ return ls0;
+
+ sz = strlen(str) + 1;
+ p = malloc(sizeof(struct ul_env_list) + sz);
+
+ ls = (struct ul_env_list *) p;
+ p += sizeof(struct ul_env_list);
+ memcpy(p, str, sz);
+ ls->env = p;
+
+ ls->next = ls0;
+ return ls;
+}
+
+/*
+ * Use setenv() for all stuff in @ls.
+ *
+ * It would be possible to use putenv(), but we want to keep @ls free()-able.
+ */
+int env_list_setenv(struct ul_env_list *ls)
+{
+ int rc = 0;
+
+ while (ls && rc == 0) {
+ if (ls->env) {
+ char *val = strchr(ls->env, '=');
+ if (!val)
+ continue;
+ *val = '\0';
+ rc = setenv(ls->env, val + 1, 0);
+ *val = '=';
+ }
+ ls = ls->next;
+ }
+ return rc;
+}
+
+void env_list_free(struct ul_env_list *ls)
+{
+ while (ls) {
+ struct ul_env_list *x = ls;
+ ls = ls->next;
+ free(x);
+ }
+}
+
+/*
+ * Removes unwanted variables from environ[]. If @ls is not NULL than stores
+ * unwnated variables to the list.
+ */
+void __sanitize_env(struct ul_env_list **org)
+{
+ char **envp = environ;
+ char * const *bad;
+ char **cur;
+ int last = 0;
+
+ for (cur = envp; *cur; cur++)
+ last++;
+
+ for (cur = envp; *cur; cur++) {
+ for (bad = forbid; *bad; bad++) {
+ if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
+ if (org)
+ *org = env_list_add(*org, *cur);
+ last = remote_entry(envp, cur - envp, last);
+ cur--;
+ break;
+ }
+ }
+ }
+
+ for (cur = envp; *cur; cur++) {
+ for (bad = noslash; *bad; bad++) {
+ if (strncmp(*cur, *bad, strlen(*bad)) != 0)
+ continue;
+ if (!strchr(*cur, '/'))
+ continue; /* OK */
+ if (org)
+ *org = env_list_add(*org, *cur);
+ last = remote_entry(envp, cur - envp, last);
+ cur--;
+ break;
+ }
+ }
+}
+
+void sanitize_env(void)
+{
+ __sanitize_env(NULL);
+}
+
+char *safe_getenv(const char *arg)
+{
+ uid_t ruid = getuid();
+
+ if (ruid != 0 || (ruid != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#ifdef HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+#ifdef HAVE_SECURE_GETENV
+return secure_getenv(arg);
+#elif HAVE___SECURE_GETENV
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+#ifdef TEST_PROGRAM
+int main(void)
+{
+ char *const *bad;
+ char copy[32];
+ char *p;
+ int retval = EXIT_SUCCESS;
+ struct ul_env_list *removed = NULL;
+
+ for (bad = forbid; *bad; bad++) {
+ strcpy(copy, *bad);
+ p = strchr(copy, '=');
+ if (p)
+ *p = '\0';
+ setenv(copy, copy, 1);
+ }
+
+ /* removed */
+ __sanitize_env(&removed);
+
+ /* check removal */
+ for (bad = forbid; *bad; bad++) {
+ strcpy(copy, *bad);
+ p = strchr(copy, '=');
+ if (p)
+ *p = '\0';
+ p = getenv(copy);
+ if (p) {
+ warnx("%s was not removed", copy);
+ retval = EXIT_FAILURE;
+ }
+ }
+
+ /* restore removed */
+ env_list_setenv(removed);
+
+ /* check restore */
+ for (bad = forbid; *bad; bad++) {
+ strcpy(copy, *bad);
+ p = strchr(copy, '=');
+ if (p)
+ *p = '\0';
+ p = getenv(copy);
+ if (!p) {
+ warnx("%s was not restored", copy);
+ retval = EXIT_FAILURE;
+ }
+ }
+
+ env_list_free(removed);
+
+ return retval;
+}
+#endif
diff --git a/utils/lib/exec_shell.c b/utils/lib/exec_shell.c
new file mode 100644
index 0000000..18798eb
--- /dev/null
+++ b/utils/lib/exec_shell.c
@@ -0,0 +1,51 @@
+/*
+ * exec_shell() - launch a shell, else exit!
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <libgen.h>
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+
+#include "exec_shell.h"
+
+#define DEFAULT_SHELL "/bin/sh"
+
+void __attribute__((__noreturn__)) exec_shell(void)
+{
+ const char *shell = getenv("SHELL");
+ char *shellc;
+ const char *shell_basename;
+ char *arg0;
+
+ if (!shell)
+ shell = DEFAULT_SHELL;
+
+ shellc = xstrdup(shell);
+ shell_basename = basename(shellc);
+ arg0 = xmalloc(strlen(shell_basename) + 2);
+ arg0[0] = '-';
+ strcpy(arg0 + 1, shell_basename);
+
+ execl(shell, arg0, NULL);
+ errexec(shell);
+}
diff --git a/utils/lib/fileutils.c b/utils/lib/fileutils.c
new file mode 100644
index 0000000..3ca43c1
--- /dev/null
+++ b/utils/lib/fileutils.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "pathnames.h"
+
+int mkstemp_cloexec(char *template)
+{
+#ifdef HAVE_MKOSTEMP
+ return mkostemp(template, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
+#else
+ int fd, old_flags, errno_save;
+
+ fd = mkstemp(template);
+ if (fd < 0)
+ return fd;
+
+ old_flags = fcntl(fd, F_GETFD, 0);
+ if (old_flags < 0)
+ goto unwind;
+ if (fcntl(fd, F_SETFD, old_flags | O_CLOEXEC) < 0)
+ goto unwind;
+
+ return fd;
+
+unwind:
+ errno_save = errno;
+ unlink(template);
+ close(fd);
+ errno = errno_save;
+
+ return -1;
+#endif
+}
+
+/* Create open temporary file in safe way. Please notice that the
+ * file permissions are -rw------- by default. */
+int xmkstemp(char **tmpname, const char *dir, const char *prefix)
+{
+ char *localtmp;
+ const char *tmpenv;
+ mode_t old_mode;
+ int fd, rc;
+
+ /* Some use cases must be capable of being moved atomically
+ * with rename(2), which is the reason why dir is here. */
+ tmpenv = dir ? dir : getenv("TMPDIR");
+ if (!tmpenv)
+ tmpenv = _PATH_TMP;
+
+ rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix);
+ if (rc < 0)
+ return -1;
+
+ old_mode = umask(077);
+ fd = mkstemp_cloexec(localtmp);
+ umask(old_mode);
+ if (fd == -1) {
+ free(localtmp);
+ localtmp = NULL;
+ }
+ *tmpname = localtmp;
+ return fd;
+}
+
+int dup_fd_cloexec(int oldfd, int lowfd)
+{
+ int fd, flags, errno_save;
+
+#ifdef F_DUPFD_CLOEXEC
+ fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
+ if (fd >= 0)
+ return fd;
+#endif
+
+ fd = dup(oldfd);
+ if (fd < 0)
+ return fd;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0)
+ goto unwind;
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+ goto unwind;
+
+ return fd;
+
+unwind:
+ errno_save = errno;
+ close(fd);
+ errno = errno_save;
+
+ return -1;
+}
+
+/*
+ * portable getdtablesize()
+ */
+int get_fd_tabsize(void)
+{
+ int m;
+
+#if defined(HAVE_GETDTABLESIZE)
+ m = getdtablesize();
+#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
+ struct rlimit rl;
+
+ getrlimit(RLIMIT_NOFILE, &rl);
+ m = rl.rlim_cur;
+#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ m = sysconf(_SC_OPEN_MAX);
+#else
+ m = OPEN_MAX;
+#endif
+ return m;
+}
+
+static inline int in_set(int x, const int set[], size_t setsz)
+{
+ size_t i;
+
+ for (i = 0; i < setsz; i++) {
+ if (set[i] == x)
+ return 1;
+ }
+ return 0;
+}
+
+void close_all_fds(const int exclude[], size_t exsz)
+{
+ struct dirent *d;
+ DIR *dir;
+
+ dir = opendir(_PATH_PROC_FDDIR);
+ if (dir) {
+ while ((d = xreaddir(dir))) {
+ char *end;
+ int fd;
+
+ errno = 0;
+ fd = strtol(d->d_name, &end, 10);
+
+ if (errno || end == d->d_name || !end || *end)
+ continue;
+ if (dirfd(dir) == fd)
+ continue;
+ if (in_set(fd, exclude, exsz))
+ continue;
+ close(fd);
+ }
+ closedir(dir);
+ } else {
+ int fd, tbsz = get_fd_tabsize();
+
+ for (fd = 0; fd < tbsz; fd++) {
+ if (!in_set(fd, exclude, exsz))
+ close(fd);
+ }
+ }
+}
+
+#ifdef TEST_PROGRAM_FILEUTILS
+int main(int argc, char *argv[])
+{
+ if (argc < 2)
+ errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds}", argv[0]);
+
+ if (strcmp(argv[1], "--mkstemp") == 0) {
+ FILE *f;
+ char *tmpname;
+ f = xfmkstemp(&tmpname, NULL, "test");
+ unlink(tmpname);
+ free(tmpname);
+ fclose(f);
+
+ } else if (strcmp(argv[1], "--close-fds") == 0) {
+ static const int wanted_fds[] = {
+ STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
+ };
+
+ ignore_result( dup(STDIN_FILENO) );
+ ignore_result( dup(STDIN_FILENO) );
+ ignore_result( dup(STDIN_FILENO) );
+
+ close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds));
+ }
+ return EXIT_SUCCESS;
+}
+#endif
+
+
+int mkdir_p(const char *path, mode_t mode)
+{
+ char *p, *dir;
+ int rc = 0;
+
+ if (!path || !*path)
+ return -EINVAL;
+
+ dir = p = strdup(path);
+ if (!dir)
+ return -ENOMEM;
+
+ if (*p == '/')
+ p++;
+
+ while (p && *p) {
+ char *e = strchr(p, '/');
+ if (e)
+ *e = '\0';
+ if (*p) {
+ rc = mkdir(dir, mode);
+ if (rc && errno != EEXIST)
+ break;
+ rc = 0;
+ }
+ if (!e)
+ break;
+ *e = '/';
+ p = e + 1;
+ }
+
+ free(dir);
+ return rc;
+}
+
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+char *stripoff_last_component(char *path)
+{
+ char *p = path ? strrchr(path, '/') : NULL;
+
+ if (!p)
+ return NULL;
+ *p = '\0';
+ return p + 1;
+}
diff --git a/utils/lib/idcache.c b/utils/lib/idcache.c
new file mode 100644
index 0000000..5550223
--- /dev/null
+++ b/utils/lib/idcache.c
@@ -0,0 +1,117 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <wchar.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "c.h"
+#include "idcache.h"
+
+struct identry *get_id(struct idcache *ic, unsigned long int id)
+{
+ struct identry *ent;
+
+ if (!ic)
+ return NULL;
+
+ for (ent = ic->ent; ent; ent = ent->next) {
+ if (ent->id == id)
+ return ent;
+ }
+
+ return NULL;
+}
+
+struct idcache *new_idcache(void)
+{
+ return calloc(1, sizeof(struct idcache));
+}
+
+void free_idcache(struct idcache *ic)
+{
+ struct identry *ent = ic->ent;
+
+ while (ent) {
+ struct identry *next = ent->next;
+ free(ent->name);
+ free(ent);
+ ent = next;
+ }
+
+ free(ic);
+}
+
+static void add_id(struct idcache *ic, char *name, unsigned long int id)
+{
+ struct identry *ent, *x;
+ int w = 0;
+
+ ent = calloc(1, sizeof(struct identry));
+ if (!ent)
+ return;
+ ent->id = id;
+
+ if (name) {
+#ifdef HAVE_WIDECHAR
+ wchar_t wc[LOGIN_NAME_MAX + 1];
+
+ if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
+ wc[LOGIN_NAME_MAX] = '\0';
+ w = wcswidth(wc, LOGIN_NAME_MAX);
+ }
+ else
+#endif
+ w = strlen(name);
+ }
+
+ /* note, we ignore names with non-printable widechars */
+ if (w > 0) {
+ ent->name = strdup(name);
+ if (!ent->name) {
+ free(ent);
+ return;
+ }
+ } else {
+ if (asprintf(&ent->name, "%lu", id) < 0) {
+ free(ent);
+ return;
+ }
+ }
+
+ for (x = ic->ent; x && x->next; x = x->next);
+
+ if (x)
+ x->next = ent;
+ else
+ ic->ent = ent;
+
+ if (w <= 0)
+ w = ent->name ? strlen(ent->name) : 0;
+ ic->width = ic->width < w ? w : ic->width;
+}
+
+void add_uid(struct idcache *cache, unsigned long int id)
+{
+ struct identry *ent= get_id(cache, id);
+
+ if (!ent) {
+ struct passwd *pw = getpwuid((uid_t) id);
+ add_id(cache, pw ? pw->pw_name : NULL, id);
+ }
+}
+
+void add_gid(struct idcache *cache, unsigned long int id)
+{
+ struct identry *ent = get_id(cache, id);
+
+ if (!ent) {
+ struct group *gr = getgrgid((gid_t) id);
+ add_id(cache, gr ? gr->gr_name : NULL, id);
+ }
+}
+
diff --git a/utils/lib/ismounted.c b/utils/lib/ismounted.c
new file mode 100644
index 0000000..9a20b23
--- /dev/null
+++ b/utils/lib/ismounted.c
@@ -0,0 +1,396 @@
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000,2008 Theodore Ts'o.
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+#ifndef __linux__
+# ifdef HAVE_SYS_UCRED_H
+# include <sys/ucred.h>
+# endif
+# ifdef HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+# endif
+#endif
+
+#include "pathnames.h"
+#include "strutils.h"
+#include "ismounted.h"
+#include "c.h"
+#ifdef __linux__
+# include "loopdev.h"
+#endif
+
+
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted. Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static int check_mntent_file(const char *mtab_file, const char *file,
+ int *mount_flags, char *mtpt, int mtlen)
+{
+ struct mntent *mnt;
+ struct stat st_buf;
+ int retval = 0;
+ dev_t file_dev=0, file_rdev=0;
+ ino_t file_ino=0;
+ FILE *f;
+ int fd;
+
+ *mount_flags = 0;
+ if ((f = setmntent (mtab_file, "r")) == NULL)
+ return errno;
+
+ if (stat(file, &st_buf) == 0) {
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ file_rdev = st_buf.st_rdev;
+#endif /* __GNU__ */
+ } else {
+ file_dev = st_buf.st_dev;
+ file_ino = st_buf.st_ino;
+ }
+ }
+
+ while ((mnt = getmntent (f)) != NULL) {
+ if (mnt->mnt_fsname[0] != '/')
+ continue;
+ if (strcmp(file, mnt->mnt_fsname) == 0)
+ break;
+ if (stat(mnt->mnt_fsname, &st_buf) != 0)
+ continue;
+
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+ if (file_rdev && file_rdev == st_buf.st_rdev)
+ break;
+#ifdef __linux__
+ /* maybe the file is loopdev backing file */
+ if (file_dev
+ && major(st_buf.st_rdev) == LOOPDEV_MAJOR
+ && loopdev_is_used(mnt->mnt_fsname, file, 0, 0, 0))
+ break;
+#endif /* __linux__ */
+#endif /* __GNU__ */
+ } else {
+ if (file_dev && ((file_dev == st_buf.st_dev) &&
+ (file_ino == st_buf.st_ino)))
+ break;
+ }
+ }
+
+ if (mnt == NULL) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ /*
+ * Do an extra check to see if this is the root device. We
+ * can't trust /etc/mtab, and /proc/mounts will only list
+ * /dev/root for the root filesystem. Argh. Instead we
+ * check if the given device has the same major/minor number
+ * as the device that the root directory is on.
+ */
+ if (file_rdev && stat("/", &st_buf) == 0 &&
+ st_buf.st_dev == file_rdev) {
+ *mount_flags = MF_MOUNTED;
+ if (mtpt)
+ xstrncpy(mtpt, "/", mtlen);
+ goto is_root;
+ }
+#endif /* __GNU__ */
+ goto errout;
+ }
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+ /* Validate the entry in case /etc/mtab is out of date */
+ /*
+ * We need to be paranoid, because some broken distributions
+ * (read: Slackware) don't initialize /etc/mtab before checking
+ * all of the non-root filesystems on the disk.
+ */
+ if (stat(mnt->mnt_dir, &st_buf) < 0) {
+ retval = errno;
+ if (retval == ENOENT) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s does not exist)\n",
+ mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+ retval = 0;
+ }
+ goto errout;
+ }
+ if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s not mounted on %s)\n",
+ mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+ goto errout;
+ }
+#endif /* __GNU__ */
+ *mount_flags = MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+ /* Check to see if the ro option is set */
+ if (hasmntopt(mnt, MNTOPT_RO))
+ *mount_flags |= MF_READONLY;
+#endif
+
+ if (mtpt)
+ xstrncpy(mtpt, mnt->mnt_dir, mtlen);
+ /*
+ * Check to see if we're referring to the root filesystem.
+ * If so, do a manual check to see if we can open /etc/mtab
+ * read/write, since if the root is mounted read/only, the
+ * contents of /etc/mtab may not be accurate.
+ */
+ if (!strcmp(mnt->mnt_dir, "/")) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+ *mount_flags |= MF_ISROOT;
+ fd = open(TEST_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0600);
+ if (fd < 0) {
+ if (errno == EROFS)
+ *mount_flags |= MF_READONLY;
+ } else
+ close(fd);
+ (void) unlink(TEST_FILE);
+ }
+ retval = 0;
+errout:
+ endmntent (f);
+ return retval;
+}
+
+static int check_mntent(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ int retval;
+
+#ifdef DEBUG
+ retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0)
+ return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+ retval = check_mntent_file("/proc/mounts", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0 && (*mount_flags != 0))
+ return 0;
+ if (access("/proc/mounts", R_OK) == 0) {
+ *mount_flags = 0;
+ return retval;
+ }
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+ return retval;
+#else
+ *mount_flags = 0;
+ return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static int check_getmntinfo(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ struct statfs *mp;
+ int len, n;
+ const char *s1;
+ char *s2;
+
+ n = getmntinfo(&mp, MNT_NOWAIT);
+ if (n == 0)
+ return errno;
+
+ len = sizeof(_PATH_DEV) - 1;
+ s1 = file;
+ if (strncmp(_PATH_DEV, s1, len) == 0)
+ s1 += len;
+
+ *mount_flags = 0;
+ while (--n >= 0) {
+ s2 = mp->f_mntfromname;
+ if (strncmp(_PATH_DEV, s2, len) == 0) {
+ s2 += len - 1;
+ *s2 = 'r';
+ }
+ if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+ *mount_flags = MF_MOUNTED;
+ break;
+ }
+ ++mp;
+ }
+ if (mtpt)
+ xstrncpy(mtpt, mp->f_mntonname, mtlen);
+ return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+ FILE *f;
+ char buf[1024], *cp;
+ dev_t file_dev;
+ struct stat st_buf;
+ int ret = 0;
+
+ file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ if ((stat(file, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode))
+ file_dev = st_buf.st_rdev;
+#endif /* __GNU__ */
+
+ if (!(f = fopen("/proc/swaps", "r" UL_CLOEXECSTR)))
+ return 0;
+ /* Skip the first line */
+ if (!fgets(buf, sizeof(buf), f))
+ goto leave;
+ if (*buf && strncmp(buf, "Filename\t", 9) != 0)
+ /* Linux <=2.6.19 contained a bug in the /proc/swaps
+ * code where the header would not be displayed
+ */
+ goto valid_first_line;
+
+ while (fgets(buf, sizeof(buf), f)) {
+valid_first_line:
+ if ((cp = strchr(buf, ' ')) != NULL)
+ *cp = 0;
+ if ((cp = strchr(buf, '\t')) != NULL)
+ *cp = 0;
+ if (strcmp(buf, file) == 0) {
+ ret++;
+ break;
+ }
+#ifndef __GNU__
+ if (file_dev && (stat(buf, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode) &&
+ file_dev == st_buf.st_rdev) {
+ ret++;
+ break;
+ }
+#endif /* __GNU__ */
+ }
+
+leave:
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * check_mount_point() fills determines if the device is mounted or otherwise
+ * busy, and fills in mount_flags with one or more of the following flags:
+ * MF_MOUNTED, MF_ISROOT, MF_READONLY, MF_SWAP, and MF_BUSY. If mtpt is
+ * non-NULL, the directory where the device is mounted is copied to where mtpt
+ * is pointing, up to mtlen characters.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+int check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ int retval = 0;
+
+ if (is_swap_device(device)) {
+ *mount_flags = MF_MOUNTED | MF_SWAP;
+ if (mtpt && mtlen)
+ xstrncpy(mtpt, "[SWAP]", mtlen);
+ } else {
+#ifdef HAVE_MNTENT_H
+ retval = check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+ retval = check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+ *mount_flags = 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+ }
+ if (retval)
+ return retval;
+
+#ifdef __linux__ /* This only works on Linux 2.6+ systems */
+ {
+ struct stat st_buf;
+ int fd;
+ if ((stat(device, &st_buf) != 0) ||
+ !S_ISBLK(st_buf.st_mode))
+ return 0;
+ fd = open(device, O_RDONLY|O_EXCL|O_CLOEXEC);
+ if (fd < 0) {
+ if (errno == EBUSY)
+ *mount_flags |= MF_BUSY;
+ } else
+ close(fd);
+ }
+#endif
+
+ return 0;
+}
+
+int is_mounted(const char *file)
+{
+ int retval;
+ int mount_flags = 0;
+
+ retval = check_mount_point(file, &mount_flags, NULL, 0);
+ if (retval)
+ return 0;
+ return mount_flags & MF_MOUNTED;
+}
+
+#ifdef TEST_PROGRAM_ISMOUNTED
+int main(int argc, char **argv)
+{
+ int flags = 0;
+ char devname[PATH_MAX];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (check_mount_point(argv[1], &flags, devname, sizeof(devname)) == 0 &&
+ (flags & MF_MOUNTED)) {
+ if (flags & MF_SWAP)
+ printf("used swap device\n");
+ else
+ printf("mounted on %s\n", devname);
+ return EXIT_SUCCESS;
+ }
+
+ printf("not mounted\n");
+ return EXIT_FAILURE;
+}
+#endif /* DEBUG */
diff --git a/utils/lib/langinfo.c b/utils/lib/langinfo.c
new file mode 100644
index 0000000..a200085
--- /dev/null
+++ b/utils/lib/langinfo.c
@@ -0,0 +1,124 @@
+/*
+ * This is callback solution for systems without nl_langinfo(), this function
+ * returns hardcoded and on locale setting indepndent value.
+ *
+ * See langinfo.h man page for more details.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include "nls.h"
+
+char *langinfo_fallback(nl_item item)
+{
+ switch (item) {
+ case CODESET:
+ return "ISO-8859-1";
+ case THOUSEP:
+ return ",";
+ case D_T_FMT:
+ case ERA_D_T_FMT:
+ return "%a %b %e %H:%M:%S %Y";
+ case D_FMT:
+ case ERA_D_FMT:
+ return "%m/%d/%y";
+ case T_FMT:
+ case ERA_T_FMT:
+ return "%H:%M:%S";
+ case T_FMT_AMPM:
+ return "%I:%M:%S %p";
+ case AM_STR:
+ return "AM";
+ case PM_STR:
+ return "PM";
+ case DAY_1:
+ return "Sunday";
+ case DAY_2:
+ return "Monday";
+ case DAY_3:
+ return "Tuesday";
+ case DAY_4:
+ return "Wednesday";
+ case DAY_5:
+ return "Thursday";
+ case DAY_6:
+ return "Friday";
+ case DAY_7:
+ return "Saturday";
+ case ABDAY_1:
+ return "Sun";
+ case ABDAY_2:
+ return "Mon";
+ case ABDAY_3:
+ return "Tue";
+ case ABDAY_4:
+ return "Wed";
+ case ABDAY_5:
+ return "Thu";
+ case ABDAY_6:
+ return "Fri";
+ case ABDAY_7:
+ return "Sat";
+ case MON_1:
+ return "January";
+ case MON_2:
+ return "February";
+ case MON_3:
+ return "March";
+ case MON_4:
+ return "April";
+ case MON_5:
+ return "May";
+ case MON_6:
+ return "June";
+ case MON_7:
+ return "July";
+ case MON_8:
+ return "August";
+ case MON_9:
+ return "September";
+ case MON_10:
+ return "October";
+ case MON_11:
+ return "November";
+ case MON_12:
+ return "December";
+ case ABMON_1:
+ return "Jan";
+ case ABMON_2:
+ return "Feb";
+ case ABMON_3:
+ return "Mar";
+ case ABMON_4:
+ return "Apr";
+ case ABMON_5:
+ return "May";
+ case ABMON_6:
+ return "Jun";
+ case ABMON_7:
+ return "Jul";
+ case ABMON_8:
+ return "Aug";
+ case ABMON_9:
+ return "Sep";
+ case ABMON_10:
+ return "Oct";
+ case ABMON_11:
+ return "Nov";
+ case ABMON_12:
+ return "Dec";
+ case ALT_DIGITS:
+ return "\0\0\0\0\0\0\0\0\0\0";
+ case CRNCYSTR:
+ return "-";
+ case YESEXPR:
+ return "^[yY]";
+ case NOEXPR:
+ return "^[nN]";
+ default:
+ return "";
+ }
+}
+
diff --git a/utils/lib/linux_version.c b/utils/lib/linux_version.c
new file mode 100644
index 0000000..137bbe7
--- /dev/null
+++ b/utils/lib/linux_version.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#include "c.h"
+#include "linux_version.h"
+
+int get_linux_version (void)
+{
+ static int kver = -1;
+ struct utsname uts;
+ int x = 0, y = 0, z = 0;
+ int n;
+
+ if (kver != -1)
+ return kver;
+ if (uname(&uts))
+ return kver = 0;
+
+ n = sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
+ if (n < 1 || n > 3)
+ return kver = 0;
+
+ return kver = KERNEL_VERSION(x, y, z);
+}
+
+#ifdef TEST_PROGRAM_LINUXVERSION
+# include <stdlib.h>
+int main(int argc, char *argv[])
+{
+ int rc = EXIT_FAILURE;
+
+ if (argc == 1) {
+ printf("Linux version: %d\n", get_linux_version());
+ rc = EXIT_SUCCESS;
+
+ } else if (argc == 5) {
+ const char *oper = argv[1];
+
+ int x = atoi(argv[2]),
+ y = atoi(argv[3]),
+ z = atoi(argv[4]);
+ int kver = get_linux_version();
+ int uver = KERNEL_VERSION(x, y, z);
+
+ if (strcmp(oper, "==") == 0)
+ rc = kver == uver;
+ else if (strcmp(oper, "<=") == 0)
+ rc = kver <= uver;
+ else if (strcmp(oper, ">=") == 0)
+ rc = kver >= uver;
+ else
+ errx(EXIT_FAILURE, "unsupported operator");
+
+ if (rc)
+ printf("match\n");
+ else
+ printf("not-match [%d %s %d, x.y.z: %d.%d.%d]\n",
+ kver, oper, uver, x, y, z);
+
+ rc = rc ? EXIT_SUCCESS : EXIT_FAILURE;
+
+ } else
+ fprintf(stderr, "Usage:\n"
+ " %s [<oper> <x> <y> <z>]\n"
+ "supported operators:\n"
+ " ==, <=, >=\n",
+ program_invocation_short_name);
+
+ return rc;
+}
+#endif
diff --git a/utils/lib/loopdev.c b/utils/lib/loopdev.c
new file mode 100644
index 0000000..686be53
--- /dev/null
+++ b/utils/lib/loopdev.c
@@ -0,0 +1,1914 @@
+
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * -- based on mount/losetup.c
+ *
+ * Simple library for work with loop devices.
+ *
+ * - requires kernel 2.6.x
+ * - reads info from /sys/block/loop<N>/loop/<attr> (new kernels)
+ * - reads info by ioctl
+ * - supports *unlimited* number of loop devices
+ * - supports /dev/loop<N> as well as /dev/loop/<N>
+ * - minimize overhead (fd, loopinfo, ... are shared for all operations)
+ * - setup (associate device and backing file)
+ * - delete (dis-associate file)
+ * - old LOOP_{SET,GET}_STATUS (32bit) ioctls are unsupported
+ * - extendible
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+#include "linux_version.h"
+#include "c.h"
+#include "sysfs.h"
+#include "pathnames.h"
+#include "loopdev.h"
+#include "canonicalize.h"
+#include "blkdev.h"
+#include "debug.h"
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+static UL_DEBUG_DEFINE_MASK(loopdev);
+UL_DEBUG_DEFINE_MASKNAMES(loopdev) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define LOOPDEV_DEBUG_INIT (1 << 1)
+#define LOOPDEV_DEBUG_CXT (1 << 2)
+#define LOOPDEV_DEBUG_ITER (1 << 3)
+#define LOOPDEV_DEBUG_SETUP (1 << 4)
+
+#define DBG(m, x) __UL_DBG(loopdev, LOOPDEV_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(loopdev, LOOPDEV_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(loopdev)
+#include "debugobj.h"
+
+static void loopdev_init_debug(void)
+{
+ if (loopdev_debug_mask)
+ return;
+ __UL_INIT_DEBUG_FROM_ENV(loopdev, LOOPDEV_DEBUG_, 0, LOOPDEV_DEBUG);
+}
+
+/*
+ * see loopcxt_init()
+ */
+#define loopcxt_ioctl_enabled(_lc) (!((_lc)->flags & LOOPDEV_FL_NOIOCTL))
+#define loopcxt_sysfs_available(_lc) (!((_lc)->flags & LOOPDEV_FL_NOSYSFS)) \
+ && !loopcxt_ioctl_enabled(_lc)
+
+/*
+ * @lc: context
+ * @device: device name, absolute device path or NULL to reset the current setting
+ *
+ * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device
+ * names ("loop<N>") are converted to the path (/dev/loop<N> or to
+ * /dev/loop/<N>)
+ *
+ * This sets the device name, but does not check if the device exists!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+{
+ if (!lc)
+ return -EINVAL;
+
+ if (lc->fd >= 0) {
+ close(lc->fd);
+ DBG(CXT, ul_debugobj(lc, "closing old open fd"));
+ }
+ lc->fd = -1;
+ lc->mode = 0;
+ lc->blocksize = 0;
+ lc->has_info = 0;
+ lc->info_failed = 0;
+ *lc->device = '\0';
+ memset(&lc->info, 0, sizeof(lc->info));
+
+ /* set new */
+ if (device) {
+ if (*device != '/') {
+ const char *dir = _PATH_DEV;
+
+ /* compose device name for /dev/loop<n> or /dev/loop/<n> */
+ if (lc->flags & LOOPDEV_FL_DEVSUBDIR) {
+ if (strlen(device) < 5)
+ return -1;
+ device += 4;
+ dir = _PATH_DEV_LOOP "/"; /* _PATH_DEV uses tailing slash */
+ }
+ snprintf(lc->device, sizeof(lc->device), "%s%s",
+ dir, device);
+ } else
+ xstrncpy(lc->device, device, sizeof(lc->device));
+
+ DBG(CXT, ul_debugobj(lc, "%s name assigned", device));
+ }
+
+ ul_unref_path(lc->sysfs);
+ lc->sysfs = NULL;
+ return 0;
+}
+
+int loopcxt_has_device(struct loopdev_cxt *lc)
+{
+ return lc && *lc->device;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPDEV_FL_* flags
+ *
+ * Initialize loop handler.
+ *
+ * We have two sets of the flags:
+ *
+ * * LOOPDEV_FL_* flags control loopcxt_* API behavior
+ *
+ * * 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)
+{
+ int rc;
+ struct stat st;
+ struct loopdev_cxt dummy = UL_LOOPDEVCXT_EMPTY;
+
+ if (!lc)
+ return -EINVAL;
+
+ loopdev_init_debug();
+ DBG(CXT, ul_debugobj(lc, "initialize context"));
+
+ memcpy(lc, &dummy, sizeof(dummy));
+ lc->flags = flags;
+
+ rc = loopcxt_set_device(lc, NULL);
+ if (rc)
+ return rc;
+
+ if (stat(_PATH_SYS_BLOCK, &st) || !S_ISDIR(st.st_mode)) {
+ lc->flags |= LOOPDEV_FL_NOSYSFS;
+ lc->flags &= ~LOOPDEV_FL_NOIOCTL;
+ DBG(CXT, ul_debugobj(lc, "init: disable /sys usage"));
+ }
+
+ if (!(lc->flags & LOOPDEV_FL_NOSYSFS) &&
+ get_linux_version() >= KERNEL_VERSION(2,6,37)) {
+ /*
+ * Use only sysfs for basic information about loop devices
+ */
+ lc->flags |= LOOPDEV_FL_NOIOCTL;
+ DBG(CXT, ul_debugobj(lc, "init: ignore ioctls"));
+ }
+
+ if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) {
+ lc->flags |= LOOPDEV_FL_CONTROL;
+ DBG(CXT, ul_debugobj(lc, "init: loop-control detected "));
+ }
+
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Deinitialize loop context
+ */
+void loopcxt_deinit(struct loopdev_cxt *lc)
+{
+ int errsv = errno;
+
+ if (!lc)
+ return;
+
+ DBG(CXT, ul_debugobj(lc, "de-initialize"));
+
+ free(lc->filename);
+ lc->filename = NULL;
+
+ ignore_result( loopcxt_set_device(lc, NULL) );
+ loopcxt_deinit_iterator(lc);
+
+ errno = errsv;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns newly allocated device path.
+ */
+char *loopcxt_strdup_device(struct loopdev_cxt *lc)
+{
+ if (!lc || !*lc->device)
+ return NULL;
+ return strdup(lc->device);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer device name in the @lc struct.
+ */
+const char *loopcxt_get_device(struct loopdev_cxt *lc)
+{
+ return lc && *lc->device ? lc->device : NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer to the sysfs context (see lib/sysfs.c)
+ */
+static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
+{
+ if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS))
+ return NULL;
+
+ if (!lc->sysfs) {
+ dev_t devno = sysfs_devname_to_devno(lc->device);
+ if (!devno) {
+ DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
+ return NULL;
+ }
+
+ lc->sysfs = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!lc->sysfs)
+ DBG(CXT, ul_debugobj(lc, "sysfs: init failed"));
+ }
+
+ 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)
+{
+ if (!lc || !*lc->device)
+ return -EINVAL;
+
+ if (lc->fd < 0) {
+ lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+ 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"));
+ }
+ return lc->fd;
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+{
+ if (!lc)
+ return -EINVAL;
+
+ lc->fd = fd;
+ lc->mode = mode;
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPITER_FL_* flags
+ *
+ * Iterator can be used to scan list of the free or used loop devices.
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags)
+{
+ struct loopdev_iter *iter;
+ struct stat st;
+
+ if (!lc)
+ return -EINVAL;
+
+
+ iter = &lc->iter;
+ DBG(ITER, ul_debugobj(iter, "initialize"));
+
+ /* always zeroize
+ */
+ memset(iter, 0, sizeof(*iter));
+ iter->ncur = -1;
+ iter->flags = flags;
+ iter->default_check = 1;
+
+ if (!lc->extra_check) {
+ /*
+ * Check for /dev/loop/<N> subdirectory
+ */
+ if (!(lc->flags & LOOPDEV_FL_DEVSUBDIR) &&
+ stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode))
+ lc->flags |= LOOPDEV_FL_DEVSUBDIR;
+
+ lc->extra_check = 1;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_deinit_iterator(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter;
+
+ if (!lc)
+ return -EINVAL;
+
+ iter = &lc->iter;
+ DBG(ITER, ul_debugobj(iter, "de-initialize"));
+
+ free(iter->minors);
+ if (iter->proc)
+ fclose(iter->proc);
+ if (iter->sysblock)
+ closedir(iter->sysblock);
+
+ memset(iter, 0, sizeof(*iter));
+ return 0;
+}
+
+/*
+ * Same as loopcxt_set_device, but also checks if the device is
+ * associated with any file.
+ *
+ * Returns: <0 on error, 0 on success, 1 device does not match with
+ * LOOPITER_FL_{USED,FREE} flags.
+ */
+static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
+{
+ int rc = loopcxt_set_device(lc, device);
+ int used;
+
+ if (rc)
+ return rc;
+
+ if (!(lc->iter.flags & LOOPITER_FL_USED) &&
+ !(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)
+ return 0;
+
+ if ((lc->iter.flags & LOOPITER_FL_FREE) && !used)
+ return 0;
+
+ DBG(ITER, ul_debugobj(&lc->iter, "failed to use %s device", lc->device));
+
+ ignore_result( loopcxt_set_device(lc, NULL) );
+ return 1;
+}
+
+static int cmpnum(const void *p1, const void *p2)
+{
+ return (((* (const int *) p1) > (* (const int *) p2)) -
+ ((* (const int *) p1) < (* (const int *) p2)));
+}
+
+/*
+ * The classic scandir() is more expensive and less portable.
+ * We needn't full loop device names -- loop numbers (loop<N>)
+ * are enough.
+ */
+static int loop_scandir(const char *dirname, int **ary, int hasprefix)
+{
+ DIR *dir;
+ struct dirent *d;
+ unsigned int n, count = 0, arylen = 0;
+
+ if (!dirname || !ary)
+ return 0;
+
+ DBG(ITER, ul_debug("scan dir: %s", dirname));
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+ free(*ary);
+ *ary = NULL;
+
+ while((d = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN &&
+ d->d_type != DT_LNK)
+ continue;
+#endif
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (hasprefix) {
+ /* /dev/loop<N> */
+ if (sscanf(d->d_name, "loop%u", &n) != 1)
+ continue;
+ } else {
+ /* /dev/loop/<N> */
+ char *end = NULL;
+
+ errno = 0;
+ n = strtol(d->d_name, &end, 10);
+ if (d->d_name == end || (end && *end) || errno)
+ continue;
+ }
+ if (n < LOOPDEV_DEFAULT_NNODES)
+ continue; /* ignore loop<0..7> */
+
+ if (count + 1 > arylen) {
+ int *tmp;
+
+ arylen += 1;
+
+ tmp = realloc(*ary, arylen * sizeof(int));
+ if (!tmp) {
+ free(*ary);
+ *ary = NULL;
+ closedir(dir);
+ return -1;
+ }
+ *ary = tmp;
+ }
+ if (*ary)
+ (*ary)[count++] = n;
+ }
+ if (count && *ary)
+ qsort(*ary, count, sizeof(int), cmpnum);
+
+ closedir(dir);
+ return count;
+}
+
+/*
+ * Set the next *used* loop device according to /proc/partitions.
+ *
+ * Loop devices smaller than 512 bytes are invisible for this function.
+ */
+static int loopcxt_next_from_proc(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter = &lc->iter;
+ char buf[BUFSIZ];
+
+ DBG(ITER, ul_debugobj(iter, "scan /proc/partitions"));
+
+ if (!iter->proc)
+ iter->proc = fopen(_PATH_PROC_PARTITIONS, "r" UL_CLOEXECSTR);
+ if (!iter->proc)
+ return 1;
+
+ while (fgets(buf, sizeof(buf), iter->proc)) {
+ unsigned int m;
+ char name[128 + 1];
+
+
+ if (sscanf(buf, " %u %*s %*s %128[^\n ]",
+ &m, name) != 2 || m != LOOPDEV_MAJOR)
+ continue;
+
+ DBG(ITER, ul_debugobj(iter, "checking %s", name));
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Set the next *used* loop device according to
+ * /sys/block/loopN/loop/backing_file (kernel >= 2.6.37 is required).
+ *
+ * This is preferred method.
+ */
+static int loopcxt_next_from_sysfs(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter = &lc->iter;
+ struct dirent *d;
+ int fd;
+
+ DBG(ITER, ul_debugobj(iter, "scanning /sys/block"));
+
+ if (!iter->sysblock)
+ iter->sysblock = opendir(_PATH_SYS_BLOCK);
+
+ if (!iter->sysblock)
+ return 1;
+
+ fd = dirfd(iter->sysblock);
+
+ while ((d = readdir(iter->sysblock))) {
+ char name[NAME_MAX + 18 + 1];
+ struct stat st;
+
+ DBG(ITER, ul_debugobj(iter, "check %s", d->d_name));
+
+ if (strcmp(d->d_name, ".") == 0
+ || strcmp(d->d_name, "..") == 0
+ || strncmp(d->d_name, "loop", 4) != 0)
+ continue;
+
+ snprintf(name, sizeof(name), "%s/loop/backing_file", d->d_name);
+ if (fstatat(fd, name, &st, 0) != 0)
+ continue;
+
+ if (loopiter_set_device(lc, d->d_name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * @lc: context, has to initialized by loopcxt_init_iterator()
+ *
+ * Returns: 0 on success, -1 on error, 1 at the end of scanning. The details
+ * about the current loop device are available by
+ * loopcxt_get_{fd,backing_file,device,offset, ...} functions.
+ */
+int loopcxt_next(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter;
+
+ if (!lc)
+ return -EINVAL;
+
+
+ iter = &lc->iter;
+ if (iter->done)
+ return 1;
+
+ DBG(ITER, ul_debugobj(iter, "next"));
+
+ /* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
+ */
+ if (iter->flags & LOOPITER_FL_USED) {
+ int rc;
+
+ if (loopcxt_sysfs_available(lc))
+ rc = loopcxt_next_from_sysfs(lc);
+ else
+ rc = loopcxt_next_from_proc(lc);
+ if (rc == 0)
+ return 0;
+ goto done;
+ }
+
+ /* B) Classic way, try first eight loop devices (default number
+ * of loop devices). This is enough for 99% of all cases.
+ */
+ if (iter->default_check) {
+ DBG(ITER, ul_debugobj(iter, "next: default check"));
+ for (++iter->ncur; iter->ncur < LOOPDEV_DEFAULT_NNODES;
+ iter->ncur++) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", iter->ncur);
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+ iter->default_check = 0;
+ }
+
+ /* C) the worst possibility, scan whole /dev or /dev/loop/<N>
+ */
+ if (!iter->minors) {
+ DBG(ITER, ul_debugobj(iter, "next: scanning /dev"));
+ iter->nminors = (lc->flags & LOOPDEV_FL_DEVSUBDIR) ?
+ loop_scandir(_PATH_DEV_LOOP, &iter->minors, 0) :
+ loop_scandir(_PATH_DEV, &iter->minors, 1);
+ iter->ncur = -1;
+ }
+ for (++iter->ncur; iter->ncur < iter->nminors; iter->ncur++) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", iter->minors[iter->ncur]);
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+done:
+ loopcxt_deinit_iterator(lc);
+ return 1;
+}
+
+/*
+ * @device: path to device
+ */
+int is_loopdev(const char *device)
+{
+ struct stat st;
+
+ if (device && stat(device, &st) == 0 &&
+ S_ISBLK(st.st_mode) &&
+ major(st.st_rdev) == LOOPDEV_MAJOR)
+ return 1;
+
+ errno = ENODEV;
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns result from LOOP_GET_STAT64 ioctl or NULL on error.
+ */
+struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc)
+{
+ int fd;
+
+ if (!lc || lc->info_failed) {
+ errno = EINVAL;
+ return NULL;
+ }
+ errno = 0;
+ if (lc->has_info)
+ return &lc->info;
+
+ fd = loopcxt_get_fd(lc);
+ if (fd < 0)
+ return NULL;
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &lc->info) == 0) {
+ lc->has_info = 1;
+ lc->info_failed = 0;
+ DBG(CXT, ul_debugobj(lc, "reading loop_info64 OK"));
+ return &lc->info;
+ }
+
+ lc->info_failed = 1;
+ DBG(CXT, ul_debugobj(lc, "reading loop_info64 FAILED"));
+
+ return NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with path to the file associated
+ * with the current loop device.
+ */
+char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ char *res = NULL;
+
+ if (sysfs)
+ /*
+ * This is always preferred, the loop_info64
+ * has too small buffer for the filename.
+ */
+ ul_path_read_string(sysfs, &res, "loop/backing_file");
+
+ if (!res && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo) {
+ lo->lo_file_name[LO_NAME_SIZE - 2] = '*';
+ lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+ res = strdup((char *) lo->lo_file_name);
+ }
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_file [%s]", res));
+ return res;
+}
+
+/*
+ * @lc: context
+ * @offset: returns offset number for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = -EINVAL;
+
+ if (sysfs)
+ rc = ul_path_read_u64(sysfs, offset, "loop/offset");
+
+ if (rc && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo) {
+ if (offset)
+ *offset = lo->lo_offset;
+ rc = 0;
+ } else
+ rc = -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_offset [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @blocksize: returns logical blocksize for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = -EINVAL;
+
+ if (sysfs)
+ rc = ul_path_read_u64(sysfs, blocksize, "queue/logical_block_size");
+
+ /* Fallback based on BLKSSZGET ioctl */
+ if (rc) {
+ int fd = loopcxt_get_fd(lc);
+ int sz = 0;
+
+ if (fd < 0)
+ return -EINVAL;
+ rc = blkdev_get_sector_size(fd, &sz);
+ if (rc)
+ return rc;
+
+ *blocksize = sz;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_blocksize [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @sizelimit: returns size limit for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = -EINVAL;
+
+ if (sysfs)
+ rc = ul_path_read_u64(sysfs, size, "loop/sizelimit");
+
+ if (rc && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo) {
+ if (size)
+ *size = lo->lo_sizelimit;
+ rc = 0;
+ } else
+ rc = -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_sizelimit [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns encryption type
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ /* not provided by sysfs */
+ if (lo) {
+ if (type)
+ *type = lo->lo_encrypt_type;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_encrypt_type [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @file_fmt_type_str: file format type string.
+ * @file_fmt_type: returns file format type from the given file format string.
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int parse_file_fmt_type(const char *file_fmt_type_str, uint32_t *file_fmt_type)
+{
+ int rc = 0;
+
+ if (!strcmp(file_fmt_type_str, "RAW"))
+ *file_fmt_type = LO_FILE_FMT_RAW;
+ else if (!strcmp(file_fmt_type_str, "QCOW"))
+ *file_fmt_type = LO_FILE_FMT_QCOW;
+ else if (!strcmp(file_fmt_type_str, "VDI"))
+ *file_fmt_type = LO_FILE_FMT_VDI;
+ else if (!strcmp(file_fmt_type_str, "VMDK"))
+ *file_fmt_type = LO_FILE_FMT_VMDK;
+ else
+ rc = -EINVAL;
+
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @file_fmt_type: returns file format type of the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_file_fmt_type(struct loopdev_cxt *lc, uint32_t* file_fmt_type)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = 0;
+
+ if (sysfs) {
+ /* check if file_fmt_type is accessible and supported by the kernel module */
+ char* file_fmt_str = NULL;
+ if (ul_path_read_string(sysfs, &file_fmt_str, "loop/file_fmt_type") == 0)
+ rc = parse_file_fmt_type(file_fmt_str, file_fmt_type);
+ } else
+ rc = -errno;
+
+ if (rc != 0 && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ *file_fmt_type = lo->lo_file_fmt_type;
+ }
+
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with file format type of the current loop device.
+ */
+char *loopcxt_get_file_fmt_type_string(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+ char *res = NULL;
+
+ if (sysfs)
+ ul_path_read_string(sysfs, &res, "loop/file_fmt_type");
+
+ DBG(CXT, ul_debugobj(lc, "loopcxt_get_file_fmt_type_string [%s]", res));
+ return res;
+}
+
+/*
+ * @lc: context
+ * @devno: returns crypt name
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo)
+ return (char *) lo->lo_crypt_name;
+
+ DBG(CXT, ul_debugobj(lc, "get_crypt_name failed"));
+ return NULL;
+}
+
+/*
+ * @lc: context
+ * @devno: returns backing file devno
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ if (lo) {
+ if (devno)
+ *devno = lo->lo_device;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_devno [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @ino: returns backing file inode
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ if (lo) {
+ if (ino)
+ *ino = lo->lo_inode;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_inode [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Check if the kernel supports partitioned loop devices.
+ *
+ * Notes:
+ * - kernels < 3.2 support partitioned loop devices and PT scanning
+ * only if max_part= module parameter is non-zero
+ *
+ * - kernels >= 3.2 always support partitioned loop devices
+ *
+ * - kernels >= 3.2 always support BLKPG_{ADD,DEL}_PARTITION ioctls
+ *
+ * - kernels >= 3.2 enable PT scanner only if max_part= is non-zero or if the
+ * LO_FLAGS_PARTSCAN flag is set for the device. The PT scanner is disabled
+ * by default.
+ *
+ * See kernel commit e03c8dd14915fabc101aa495828d58598dc5af98.
+ */
+int loopmod_supports_partscan(void)
+{
+ int rc, ret = 0;
+ FILE *f;
+
+ if (get_linux_version() >= KERNEL_VERSION(3,2,0))
+ return 1;
+
+ f = fopen("/sys/module/loop/parameters/max_part", "r" UL_CLOEXECSTR);
+ if (!f)
+ return 0;
+ rc = fscanf(f, "%d", &ret);
+ fclose(f);
+ return rc == 1 ? ret : 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the partscan flags is set *or* (for old kernels) partitions
+ * scanning is enabled for all loop devices.
+ */
+int loopcxt_is_partscan(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ /* kernel >= 3.2 */
+ int fl;
+ if (ul_path_read_s32(sysfs, &fl, "loop/partscan") == 0)
+ return fl;
+ }
+
+ /* old kernels (including kernels without loopN/loop/<flags> directory */
+ return loopmod_supports_partscan();
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the autoclear flags is set.
+ */
+int loopcxt_is_autoclear(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (ul_path_read_s32(sysfs, &fl, "loop/autoclear") == 0)
+ return fl;
+ }
+
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_AUTOCLEAR;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the readonly flags is set.
+ */
+int loopcxt_is_readonly(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (ul_path_read_s32(sysfs, &fl, "ro") == 0)
+ return fl;
+ }
+
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_READ_ONLY;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the dio flags is set.
+ */
+int loopcxt_is_dio(struct loopdev_cxt *lc)
+{
+ struct path_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (ul_path_read_s32(sysfs, &fl, "loop/dio") == 0)
+ return fl;
+ }
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_DIRECT_IO;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @st: backing file stat or NULL
+ * @backing_file: filename
+ * @offset: offset (use LOOPDEV_FL_OFFSET if specified)
+ * @sizelimit: size limit (use LOOPDEV_FL_SIZELIMIT if specified)
+ * @flags: LOOPDEV_FL_{OFFSET,SIZELIMIT}
+ *
+ * Returns 1 if the current @lc loopdev is associated with the given backing
+ * file. Note that the preferred way is to use devno and inode number rather
+ * than filename. The @backing_file filename is poor solution usable in case
+ * that you don't have rights to call stat().
+ *
+ * LOOPDEV_FL_SIZELIMIT requires LOOPDEV_FL_OFFSET being set as well.
+ *
+ * Don't forget that old kernels provide very restricted (in size) backing
+ * filename by LOOP_GET_STAT64 ioctl only.
+ */
+int loopcxt_is_used(struct loopdev_cxt *lc,
+ struct stat *st,
+ const char *backing_file,
+ uint64_t offset,
+ uint64_t sizelimit,
+ int flags)
+{
+ ino_t ino = 0;
+ dev_t dev = 0;
+
+ if (!lc)
+ return 0;
+
+ DBG(CXT, ul_debugobj(lc, "checking %s vs. %s",
+ loopcxt_get_device(lc),
+ backing_file));
+
+ if (st && loopcxt_get_backing_inode(lc, &ino) == 0 &&
+ loopcxt_get_backing_devno(lc, &dev) == 0) {
+
+ if (ino == st->st_ino && dev == st->st_dev)
+ goto found;
+
+ /* don't use filename if we have devno and inode */
+ return 0;
+ }
+
+ /* poor man's solution */
+ if (backing_file) {
+ char *name = loopcxt_get_backing_file(lc);
+ int rc = name && strcmp(name, backing_file) == 0;
+
+ free(name);
+ if (rc)
+ goto found;
+ }
+
+ return 0;
+found:
+ if (flags & LOOPDEV_FL_OFFSET) {
+ uint64_t off = 0;
+
+ int rc = loopcxt_get_offset(lc, &off) == 0 && off == offset;
+
+ if (rc && flags & LOOPDEV_FL_SIZELIMIT) {
+ uint64_t sz = 0;
+
+ return loopcxt_get_sizelimit(lc, &sz) == 0 && sz == sizelimit;
+ }
+ return rc;
+ }
+ return 1;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_offset = offset;
+
+ DBG(CXT, ul_debugobj(lc, "set offset=%jd", offset));
+ return 0;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_sizelimit = sizelimit;
+
+ DBG(CXT, ul_debugobj(lc, "set sizelimit=%jd", sizelimit));
+ return 0;
+}
+
+/*
+ * The blocksize will be used by loopcxt_set_device(). For already exiting
+ * devices use loopcxt_ioctl_blocksize().
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, uint64_t blocksize)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->blocksize = blocksize;
+
+ DBG(CXT, ul_debugobj(lc, "set blocksize=%jd", blocksize));
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @file_fmt_type: kernel LO_FILE_FMT_{RAW,QCOW,VDI,VMDK} flags
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_file_fmt_type(struct loopdev_cxt *lc, uint32_t file_fmt_type) {
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_file_fmt_type = file_fmt_type;
+
+ DBG(CXT, ul_debugobj(lc, "set file_fmt_type=%u", (unsigned) file_fmt_type));
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_flags = flags;
+
+ DBG(CXT, ul_debugobj(lc, "set flags=%u", (unsigned) flags));
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @filename: backing file path (the path will be canonicalized)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
+{
+ if (!lc)
+ return -EINVAL;
+
+ lc->filename = canonicalize_path(filename);
+ if (!lc->filename)
+ return -errno;
+
+ xstrncpy((char *)lc->info.lo_file_name, lc->filename, LO_NAME_SIZE);
+
+ DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->info.lo_file_name));
+ return 0;
+}
+
+/*
+ * In kernels prior to v3.9, if the offset or sizelimit options
+ * are used, the block device's size won't be synced automatically.
+ * blockdev --getsize64 and filesystems will use the backing
+ * file size until the block device has been re-opened or the
+ * LOOP_SET_CAPACITY ioctl is called to sync the sizes.
+ *
+ * Since mount -oloop uses the LO_FLAGS_AUTOCLEAR option and passes
+ * the open file descriptor to the mount system call, we need to use
+ * the ioctl. Calling losetup directly doesn't have this problem since
+ * it closes the device when it exits and whatever consumes the device
+ * next will re-open it, causing the resync.
+ */
+static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
+{
+ uint64_t size, expected_size;
+ int dev_fd;
+ struct stat st;
+
+ if (!lc->info.lo_offset && !lc->info.lo_sizelimit)
+ return 0;
+
+ if (fstat(file_fd, &st)) {
+ DBG(CXT, ul_debugobj(lc, "failed to fstat backing file"));
+ return -errno;
+ }
+ if (S_ISBLK(st.st_mode)) {
+ if (blkdev_get_size(file_fd,
+ (unsigned long long *) &expected_size)) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine device size"));
+ return -errno;
+ }
+ } else
+ expected_size = st.st_size;
+
+ if (expected_size == 0 || expected_size <= lc->info.lo_offset) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine expected size"));
+ return 0; /* ignore this error */
+ }
+
+ if (lc->info.lo_offset > 0)
+ expected_size -= lc->info.lo_offset;
+
+ if (lc->info.lo_sizelimit > 0 && lc->info.lo_sizelimit < expected_size)
+ expected_size = lc->info.lo_sizelimit;
+
+ dev_fd = loopcxt_get_fd(lc);
+ if (dev_fd < 0) {
+ DBG(CXT, ul_debugobj(lc, "failed to get loop FD"));
+ return -errno;
+ }
+
+ if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine loopdev size"));
+ return -errno;
+ }
+
+ /* It's block device, so, align to 512-byte sectors */
+ if (expected_size % 512) {
+ DBG(CXT, ul_debugobj(lc, "expected size misaligned to 512-byte sectors"));
+ expected_size = (expected_size >> 9) << 9;
+ }
+
+ if (expected_size != size) {
+ DBG(CXT, ul_debugobj(lc, "warning: loopdev and expected "
+ "size mismatch (%ju/%ju)",
+ size, expected_size));
+
+ if (loopcxt_ioctl_capacity(lc)) {
+ /* ioctl not available */
+ if (errno == ENOTTY || errno == EINVAL)
+ errno = ERANGE;
+ return -errno;
+ }
+
+ if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
+ return -errno;
+
+ if (expected_size != size) {
+ errno = ERANGE;
+ DBG(CXT, ul_debugobj(lc, "failed to set loopdev size, "
+ "size: %ju, expected: %ju",
+ size, expected_size));
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Associate the current device (see loopcxt_{set,get}_device()) with
+ * a file (see loopcxt_set_backing_file()).
+ *
+ * The device is initialized read-write by default. If you want read-only
+ * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_*
+ * flags are ignored and modified according to LO_FLAGS_*.
+ *
+ * If the device is already open by loopcxt_get_fd() then this setup device
+ * function will re-open the device to fix read/write mode.
+ *
+ * The device is also initialized read-only if the backing file is not
+ * possible to open read-write (e.g. read-only FS).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_setup_device(struct loopdev_cxt *lc)
+{
+ int file_fd, dev_fd, mode = O_RDWR, rc = -1, cnt = 0, err, again;
+ int errsv = 0;
+
+ if (!lc || !*lc->device || !lc->filename)
+ return -EINVAL;
+
+ DBG(SETUP, ul_debugobj(lc, "device setup requested"));
+
+ /*
+ * Open backing file and device
+ */
+ if (lc->info.lo_flags & LO_FLAGS_READ_ONLY)
+ mode = O_RDONLY;
+
+ if ((file_fd = open(lc->filename, mode | O_CLOEXEC)) < 0) {
+ if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+ file_fd = open(lc->filename, mode = O_RDONLY);
+
+ if (file_fd < 0) {
+ DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
+ return -errno;
+ }
+ }
+ 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 */
+ lc->info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */
+ } else {
+ lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */
+ lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY;
+ lc->flags &= ~LOOPDEV_FL_RDONLY;
+ }
+
+ do {
+ errno = 0;
+ dev_fd = loopcxt_get_fd(lc);
+ if (dev_fd >= 0 || lc->control_ok == 0)
+ break;
+ if (errno != EACCES && errno != ENOENT)
+ break;
+ /* We have permissions to open /dev/loop-control, but open
+ * /dev/loopN failed with EACCES, it's probably because udevd
+ * does not applied chown yet. Let's wait a moment. */
+ xusleep(25000);
+ } while (cnt++ < 16);
+
+ if (dev_fd < 0) {
+ rc = -errno;
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "device open: OK"));
+
+ /*
+ * Set FD
+ */
+ if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) {
+ rc = -errno;
+ errsv = errno;
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD failed: %m"));
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD: OK"));
+
+ if (lc->blocksize > 0
+ && (rc = loopcxt_ioctl_blocksize(lc, lc->blocksize)) < 0) {
+ errsv = -rc;
+ goto err;
+ }
+
+ do {
+ err = ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info);
+ again = err && errno == EAGAIN;
+ if (again)
+ xusleep(250000);
+ } while (again);
+ if (err) {
+ rc = -errno;
+ errsv = errno;
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m"));
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK"));
+
+ if ((rc = loopcxt_check_size(lc, file_fd)))
+ goto err;
+
+ close(file_fd);
+
+ memset(&lc->info, 0, sizeof(lc->info));
+ lc->has_info = 0;
+ lc->info_failed = 0;
+
+ DBG(SETUP, ul_debugobj(lc, "success [rc=0]"));
+ return 0;
+err:
+ if (file_fd >= 0)
+ close(file_fd);
+ if (dev_fd >= 0 && rc != -EBUSY)
+ ioctl(dev_fd, LOOP_CLR_FD, 0);
+ if (errsv)
+ errno = errsv;
+
+ DBG(SETUP, ul_debugobj(lc, "failed [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ *
+ * Update status of the current device (see loopcxt_{set,get}_device()).
+ *
+ * Note that once initialized, kernel accepts only selected changes:
+ * LO_FLAGS_AUTOCLEAR and LO_FLAGS_PARTSCAN
+ * For more see linux/drivers/block/loop.c:loop_set_status()
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_ioctl_status(struct loopdev_cxt *lc)
+{
+ int dev_fd, rc = -1, err, again;
+
+ errno = 0;
+ dev_fd = loopcxt_get_fd(lc);
+
+ if (dev_fd < 0) {
+ rc = -errno;
+ return rc;
+ }
+ DBG(SETUP, ul_debugobj(lc, "device open: OK"));
+
+ do {
+ err = ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info);
+ again = err && errno == EAGAIN;
+ if (again)
+ xusleep(250000);
+ } while (again);
+ if (err) {
+ rc = -errno;
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m"));
+ return rc;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK"));
+ return 0;
+}
+
+int loopcxt_ioctl_capacity(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Kernels prior to v2.6.30 don't support this ioctl */
+ if (ioctl(fd, LOOP_SET_CAPACITY, 0) < 0) {
+ int rc = -errno;
+ DBG(CXT, ul_debugobj(lc, "LOOP_SET_CAPACITY failed: %m"));
+ return rc;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "capacity set"));
+ return 0;
+}
+
+int loopcxt_ioctl_dio(struct loopdev_cxt *lc, unsigned long use_dio)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Kernels prior to v4.4 don't support this ioctl */
+ if (ioctl(fd, LOOP_SET_DIRECT_IO, use_dio) < 0) {
+ int rc = -errno;
+ DBG(CXT, ul_debugobj(lc, "LOOP_SET_DIRECT_IO failed: %m"));
+ return rc;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "direct io set"));
+ return 0;
+}
+
+/*
+ * Kernel uses "unsigned long" as ioctl arg, but we use u64 for all sizes to
+ * keep loopdev internal API simple.
+ */
+int loopcxt_ioctl_blocksize(struct loopdev_cxt *lc, uint64_t blocksize)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Kernels prior to v4.14 don't support this ioctl */
+ if (ioctl(fd, LOOP_SET_BLOCK_SIZE, (unsigned long) blocksize) < 0) {
+ int rc = -errno;
+ DBG(CXT, ul_debugobj(lc, "LOOP_SET_BLOCK_SIZE failed: %m"));
+ return rc;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "logical block size set"));
+ return 0;
+}
+
+int loopcxt_delete_device(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+ DBG(CXT, ul_debugobj(lc, "LOOP_CLR_FD failed: %m"));
+ return -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "device removed"));
+ return 0;
+}
+
+int loopcxt_add_device(struct loopdev_cxt *lc)
+{
+ int rc = -EINVAL;
+ int ctl, nr = -1;
+ const char *p, *dev = loopcxt_get_device(lc);
+
+ if (!dev)
+ goto done;
+
+ if (!(lc->flags & LOOPDEV_FL_CONTROL)) {
+ rc = -ENOSYS;
+ goto done;
+ }
+
+ p = strrchr(dev, '/');
+ if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1)
+ || nr < 0)
+ goto done;
+
+ ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+ if (ctl >= 0) {
+ DBG(CXT, ul_debugobj(lc, "add_device %d", nr));
+ rc = ioctl(ctl, LOOP_CTL_ADD, nr);
+ close(ctl);
+ }
+ lc->control_ok = rc >= 0 ? 1 : 0;
+done:
+ DBG(CXT, ul_debugobj(lc, "add_device done [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older
+ * kernels we have to check all loop devices to found unused one.
+ *
+ * See kernel commit 770fe30a46a12b6fb6b63fbe1737654d28e8484.
+ */
+int loopcxt_find_unused(struct loopdev_cxt *lc)
+{
+ int rc = -1;
+
+ DBG(CXT, ul_debugobj(lc, "find_unused requested"));
+
+ if (lc->flags & LOOPDEV_FL_CONTROL) {
+ int ctl;
+
+ DBG(CXT, ul_debugobj(lc, "using loop-control"));
+
+ ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+ if (ctl >= 0)
+ rc = ioctl(ctl, LOOP_CTL_GET_FREE);
+ if (rc >= 0) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", rc);
+
+ rc = loopiter_set_device(lc, name);
+ }
+ lc->control_ok = ctl >= 0 && rc == 0 ? 1 : 0;
+ if (ctl >= 0)
+ close(ctl);
+ DBG(CXT, ul_debugobj(lc, "find_unused by loop-control [rc=%d]", rc));
+ }
+
+ if (rc < 0) {
+ DBG(CXT, ul_debugobj(lc, "using loop scan"));
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
+ if (rc)
+ return rc;
+
+ rc = loopcxt_next(lc);
+ loopcxt_deinit_iterator(lc);
+ DBG(CXT, ul_debugobj(lc, "find_unused by scan [rc=%d]", rc));
+ }
+ return rc;
+}
+
+
+
+/*
+ * Return: TRUE/FALSE
+ */
+int loopdev_is_autoclear(const char *device)
+{
+ struct loopdev_cxt lc;
+ int rc;
+
+ if (!device)
+ return 0;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (!rc)
+ rc = loopcxt_is_autoclear(&lc);
+
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+char *loopdev_get_backing_file(const char *device)
+{
+ struct loopdev_cxt lc;
+ char *res = NULL;
+
+ if (!device)
+ return NULL;
+ if (loopcxt_init(&lc, 0))
+ return NULL;
+ if (loopcxt_set_device(&lc, device) == 0)
+ res = loopcxt_get_backing_file(&lc);
+
+ loopcxt_deinit(&lc);
+ return res;
+}
+
+/*
+ * Returns: TRUE/FALSE
+ */
+int loopdev_is_used(const char *device, const char *filename,
+ uint64_t offset, uint64_t sizelimit, int flags)
+{
+ struct loopdev_cxt lc;
+ struct stat st;
+ int rc = 0;
+
+ if (!device || !filename)
+ return 0;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (rc)
+ return rc;
+
+ rc = !stat(filename, &st);
+ rc = loopcxt_is_used(&lc, rc ? &st : NULL, filename, offset, sizelimit, flags);
+
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+int loopdev_delete(const char *device)
+{
+ struct loopdev_cxt lc;
+ int rc;
+
+ if (!device)
+ return -EINVAL;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (!rc)
+ rc = loopcxt_delete_device(&lc);
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+/*
+ * Returns: 0 = success, < 0 error, 1 not found
+ */
+int loopcxt_find_by_backing_file(struct loopdev_cxt *lc, const char *filename,
+ uint64_t offset, uint64_t sizelimit, int flags)
+{
+ int rc, hasst;
+ struct stat st;
+
+ if (!filename)
+ return -EINVAL;
+
+ hasst = !stat(filename, &st);
+
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+ if (rc)
+ return rc;
+
+ while ((rc = loopcxt_next(lc)) == 0) {
+
+ if (loopcxt_is_used(lc, hasst ? &st : NULL,
+ filename, offset, sizelimit, flags))
+ break;
+ }
+
+ loopcxt_deinit_iterator(lc);
+ return rc;
+}
+
+/*
+ * Returns: 0 = not found, < 0 error, 1 found, 2 found full size and offset match
+ */
+int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename,
+ uint64_t offset, uint64_t sizelimit)
+{
+ int rc, hasst;
+ struct stat st;
+
+ if (!filename)
+ return -EINVAL;
+
+ DBG(CXT, ul_debugobj(lc, "find_overlap requested"));
+ hasst = !stat(filename, &st);
+
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+ if (rc)
+ return rc;
+
+ while ((rc = loopcxt_next(lc)) == 0) {
+ uint64_t lc_sizelimit, lc_offset;
+
+ rc = loopcxt_is_used(lc, hasst ? &st : NULL,
+ filename, offset, sizelimit, 0);
+ if (!rc)
+ continue; /* unused */
+ if (rc < 0)
+ break; /* error */
+
+ DBG(CXT, ul_debugobj(lc, "found %s backed by %s",
+ loopcxt_get_device(lc), filename));
+
+ rc = loopcxt_get_offset(lc, &lc_offset);
+ if (rc) {
+ DBG(CXT, ul_debugobj(lc, "failed to get offset for device %s",
+ loopcxt_get_device(lc)));
+ break;
+ }
+ rc = loopcxt_get_sizelimit(lc, &lc_sizelimit);
+ if (rc) {
+ DBG(CXT, ul_debugobj(lc, "failed to get sizelimit for device %s",
+ loopcxt_get_device(lc)));
+ break;
+ }
+
+ /* full match */
+ if (lc_sizelimit == sizelimit && lc_offset == offset) {
+ DBG(CXT, ul_debugobj(lc, "overlapping loop device %s (full match)",
+ loopcxt_get_device(lc)));
+ rc = 2;
+ goto found;
+ }
+
+ /* overlap */
+ if (lc_sizelimit != 0 && offset >= lc_offset + lc_sizelimit)
+ continue;
+ if (sizelimit != 0 && offset + sizelimit <= lc_offset)
+ continue;
+
+ DBG(CXT, ul_debugobj(lc, "overlapping loop device %s",
+ loopcxt_get_device(lc)));
+ rc = 1;
+ goto found;
+ }
+
+ if (rc == 1)
+ rc = 0; /* not found */
+found:
+ loopcxt_deinit_iterator(lc);
+ DBG(CXT, ul_debugobj(lc, "find_overlap done [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Returns allocated string with device name
+ */
+char *loopdev_find_by_backing_file(const char *filename, uint64_t offset, uint64_t sizelimit, int flags)
+{
+ struct loopdev_cxt lc;
+ char *res = NULL;
+
+ if (!filename)
+ return NULL;
+
+ if (loopcxt_init(&lc, 0))
+ return NULL;
+ if (loopcxt_find_by_backing_file(&lc, filename, offset, sizelimit, flags) == 0)
+ res = loopcxt_strdup_device(&lc);
+ loopcxt_deinit(&lc);
+
+ return res;
+}
+
+/*
+ * Returns number of loop devices associated with @file, if only one loop
+ * device is associated with the given @filename and @loopdev is not NULL then
+ * @loopdev returns name of the device.
+ */
+int loopdev_count_by_backing_file(const char *filename, char **loopdev)
+{
+ struct loopdev_cxt lc;
+ int count = 0, rc;
+
+ if (!filename)
+ return -1;
+
+ rc = loopcxt_init(&lc, 0);
+ if (rc)
+ return rc;
+ if (loopcxt_init_iterator(&lc, LOOPITER_FL_USED))
+ return -1;
+
+ while(loopcxt_next(&lc) == 0) {
+ char *backing = loopcxt_get_backing_file(&lc);
+
+ if (!backing || strcmp(backing, filename) != 0) {
+ free(backing);
+ continue;
+ }
+
+ free(backing);
+ if (loopdev && count == 0)
+ *loopdev = loopcxt_strdup_device(&lc);
+ count++;
+ }
+
+ loopcxt_deinit(&lc);
+
+ if (loopdev && count > 1) {
+ free(*loopdev);
+ *loopdev = NULL;
+ }
+ return count;
+}
+
diff --git a/utils/lib/mangle.c b/utils/lib/mangle.c
new file mode 100644
index 0000000..1a3b89a
--- /dev/null
+++ b/utils/lib/mangle.c
@@ -0,0 +1,169 @@
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mangle.h"
+#include "c.h"
+
+#define isoctal(a) (((a) & ~7) == '0')
+
+#define from_hex(c) (isdigit(c) ? c - '0' : tolower(c) - 'a' + 10)
+
+#define is_unwanted_char(x) (strchr(" \t\n\\", (unsigned int) x) != NULL)
+
+
+char *mangle(const char *s)
+{
+ char *ss, *sp;
+
+ if (!s)
+ return NULL;
+
+ ss = sp = malloc(4 * strlen(s) + 1);
+ if (!sp)
+ return NULL;
+ while(1) {
+ if (!*s) {
+ *sp = '\0';
+ break;
+ }
+ if (is_unwanted_char(*s)) {
+ *sp++ = '\\';
+ *sp++ = '0' + ((*s & 0300) >> 6);
+ *sp++ = '0' + ((*s & 070) >> 3);
+ *sp++ = '0' + (*s & 07);
+ } else
+ *sp++ = *s;
+ s++;
+ }
+ return ss;
+}
+
+
+void unmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+ size_t sz = 0;
+
+ if (!s)
+ return;
+
+ while(*s && sz < len - 1) {
+ if (*s == '\\' && sz + 3 < len - 1 && isoctal(s[1]) &&
+ isoctal(s[2]) && isoctal(s[3])) {
+
+ *buf++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7);
+ s += 4;
+ sz += 4;
+ } else {
+ *buf++ = *s++;
+ sz++;
+ }
+ }
+ *buf = '\0';
+}
+
+size_t unhexmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+ size_t sz = 0;
+ const char *buf0 = buf;
+
+ if (!s)
+ return 0;
+
+ while(*s && sz < len - 1) {
+ if (*s == '\\' && sz + 3 < len - 1 && s[1] == 'x' &&
+ isxdigit(s[2]) && isxdigit(s[3])) {
+
+ *buf++ = from_hex(s[2]) << 4 | from_hex(s[3]);
+ s += 4;
+ sz += 4;
+ } else {
+ *buf++ = *s++;
+ sz++;
+ }
+ }
+ *buf = '\0';
+ return buf - buf0 + 1;
+}
+
+static inline const char *skip_nonspaces(const char *s)
+{
+ while (s && *s && !(*s == ' ' || *s == '\t'))
+ s++;
+ return s;
+}
+
+/*
+ * Returns mallocated buffer or NULL in case of error.
+ */
+char *unmangle(const char *s, const char **end)
+{
+ char *buf;
+ const char *e;
+ size_t sz;
+
+ if (!s)
+ return NULL;
+
+ e = skip_nonspaces(s);
+ sz = e - s + 1;
+
+ if (end)
+ *end = e;
+ if (e == s)
+ return NULL; /* empty string */
+
+ buf = malloc(sz);
+ if (!buf)
+ return NULL;
+
+ unmangle_to_buffer(s, buf, sz);
+ return buf;
+}
+
+#ifdef TEST_PROGRAM_MANGLE
+#include <errno.h>
+int main(int argc, char *argv[])
+{
+ char *p = NULL;
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s --mangle|unmangle <string>\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ if (!strcmp(argv[1], "--mangle")) {
+ p = mangle(argv[2]);
+ printf("mangled: '%s'\n", p);
+ free(p);
+ }
+
+ else if (!strcmp(argv[1], "--unmangle")) {
+ char *x = unmangle(argv[2], NULL);
+
+ if (x) {
+ printf("unmangled: '%s'\n", x);
+ free(x);
+ }
+
+ x = strdup(argv[2]);
+ if (x) {
+ unmangle_to_buffer(x, x, strlen(x) + 1);
+
+ printf("self-unmangled: '%s'\n", x);
+ free(x);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_MANGLE */
diff --git a/utils/lib/match.c b/utils/lib/match.c
new file mode 100644
index 0000000..a286a19
--- /dev/null
+++ b/utils/lib/match.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+
+#include "match.h"
+
+/*
+ * match_fstype:
+ * @type: filesystem type
+ * @pattern: filesystem name or comma delimited list of names
+ *
+ * The @pattern list of filesystem can be prefixed with a global
+ * "no" prefix to invert matching of the whole list. The "no" could
+ * also be used for individual items in the @pattern list. So,
+ * "nofoo,bar" has the same meaning as "nofoo,nobar".
+ */
+int match_fstype(const char *type, const char *pattern)
+{
+ int no = 0; /* negated types list */
+ int len;
+ const char *p;
+
+ if (!pattern && !type)
+ return 1;
+ if (!pattern)
+ return 0;
+
+ if (!strncmp(pattern, "no", 2)) {
+ no = 1;
+ pattern += 2;
+ }
+
+ /* Does type occur in types, separated by commas? */
+ len = strlen(type);
+ p = pattern;
+ while(1) {
+ if (!strncmp(p, "no", 2) && !strncasecmp(p+2, type, len) &&
+ (p[len+2] == 0 || p[len+2] == ','))
+ return 0;
+ if (strncasecmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
+ return !no;
+ p = strchr(p,',');
+ if (!p)
+ break;
+ p++;
+ }
+ return no;
+}
diff --git a/utils/lib/mbsalign.c b/utils/lib/mbsalign.c
new file mode 100644
index 0000000..e251202
--- /dev/null
+++ b/utils/lib/mbsalign.c
@@ -0,0 +1,627 @@
+/* Align/Truncate a string in a given screen width
+ Copyright (C) 2009-2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2.1 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 <http://www.gnu.org/licenses/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "mbsalign.h"
+#include "strutils.h"
+#include "widechar.h"
+
+/*
+ * Counts number of cells in multibyte string. All control and
+ * non-printable chars are ignored.
+ *
+ * Returns: number of cells.
+ */
+size_t mbs_nwidth(const char *buf, size_t bufsz)
+{
+ const char *p = buf, *last = buf;
+ size_t width = 0;
+
+#ifdef HAVE_WIDECHAR
+ mbstate_t st;
+ memset(&st, 0, sizeof(st));
+#endif
+ if (p && *p && bufsz)
+ last = p + (bufsz - 1);
+
+ while (p && *p && p <= last) {
+ if (iscntrl((unsigned char) *p)) {
+ p++;
+
+ /* try detect "\e[x;ym" and skip on success */
+ if (*p && *p == '[') {
+ const char *e = p;
+ while (*e && e < last && *e != 'm')
+ e++;
+ if (*e == 'm')
+ p = e + 1;
+ }
+ continue;
+ }
+#ifdef HAVE_WIDECHAR
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+ if (len == 0)
+ break;
+ if (len > 0 && iswprint(wc)) {
+ int x = wcwidth(wc);
+ if (x > 0)
+ width += x;
+ } else if (len == (size_t) -1 || len == (size_t) -2)
+ len = 1;
+ p += len;
+#else
+ if (isprint((unsigned char) *p))
+ width++;
+ p++;
+#endif
+ }
+
+ return width;
+}
+
+size_t mbs_width(const char *s)
+{
+ if (!s || !*s)
+ return 0;
+ return mbs_nwidth(s, strlen(s));
+}
+
+/*
+ * Counts number of cells in multibyte string. For all control and
+ * non-printable chars is the result width enlarged to store \x?? hex
+ * sequence. See mbs_safe_encode().
+ *
+ * Returns: number of cells, @sz returns number of bytes.
+ */
+size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz)
+{
+ const char *p = buf, *last = buf;
+ size_t width = 0, bytes = 0;
+
+#ifdef HAVE_WIDECHAR
+ mbstate_t st;
+ memset(&st, 0, sizeof(st));
+#endif
+ if (p && *p && bufsz)
+ last = p + (bufsz - 1);
+
+ while (p && *p && p <= last) {
+ if ((p < last && *p == '\\' && *(p + 1) == 'x')
+ || iscntrl((unsigned char) *p)) {
+ width += 4, bytes += 4; /* *p encoded to \x?? */
+ p++;
+ }
+#ifdef HAVE_WIDECHAR
+ else {
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+ if (len == 0)
+ break;
+
+ if (len == (size_t) -1 || len == (size_t) -2) {
+ len = 1;
+ if (isprint((unsigned char) *p))
+ width += 1, bytes += 1;
+ else
+ width += 4, bytes += 4;
+
+ } else if (!iswprint(wc)) {
+ width += len * 4; /* hex encode whole sequence */
+ bytes += len * 4;
+ } else {
+ width += wcwidth(wc); /* number of cells */
+ bytes += len; /* number of bytes */
+ }
+ p += len;
+ }
+#else
+ else if (!isprint((unsigned char) *p)) {
+ width += 4, bytes += 4; /* *p encoded to \x?? */
+ p++;
+ } else {
+ width++, bytes++;
+ p++;
+ }
+#endif
+ }
+
+ if (sz)
+ *sz = bytes;
+ return width;
+}
+
+size_t mbs_safe_width(const char *s)
+{
+ if (!s || !*s)
+ return 0;
+ return mbs_safe_nwidth(s, strlen(s), NULL);
+}
+
+/*
+ * Copy @s to @buf and replace control and non-printable chars with
+ * \x?? hex sequence. The @width returns number of cells. The @safechars
+ * are not encoded.
+ *
+ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+ * bytes.
+ */
+char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars)
+{
+ const char *p = s;
+ char *r;
+ size_t sz = s ? strlen(s) : 0;
+
+#ifdef HAVE_WIDECHAR
+ mbstate_t st;
+ memset(&st, 0, sizeof(st));
+#endif
+ if (!sz || !buf)
+ return NULL;
+
+ r = buf;
+ *width = 0;
+
+ while (p && *p) {
+ if (safechars && strchr(safechars, *p)) {
+ *r++ = *p++;
+ continue;
+ }
+
+ if ((*p == '\\' && *(p + 1) == 'x')
+ || iscntrl((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ p++;
+ }
+#ifdef HAVE_WIDECHAR
+ else {
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+ if (len == 0)
+ break; /* end of string */
+
+ if (len == (size_t) -1 || len == (size_t) -2) {
+ len = 1;
+ /*
+ * Not valid multibyte sequence -- maybe it's
+ * printable char according to the current locales.
+ */
+ if (!isprint((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ } else {
+ (*width)++;
+ *r++ = *p;
+ }
+ } else if (!iswprint(wc)) {
+ size_t i;
+ for (i = 0; i < len; i++) {
+ sprintf(r, "\\x%02x", (unsigned char) p[i]);
+ r += 4;
+ *width += 4;
+ }
+ } else {
+ memcpy(r, p, len);
+ r += len;
+ *width += wcwidth(wc);
+ }
+ p += len;
+ }
+#else
+ else if (!isprint((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ p++;
+ r += 4;
+ *width += 4;
+ } else {
+ *r++ = *p++;
+ (*width)++;
+ }
+#endif
+ }
+
+ *r = '\0';
+ return buf;
+}
+
+/*
+ * Copy @s to @buf and replace broken sequences to \x?? hex sequence. The
+ * @width returns number of cells. The @safechars are not encoded.
+ *
+ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+ * bytes.
+ */
+char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf)
+{
+ const char *p = s;
+ char *r;
+ size_t sz = s ? strlen(s) : 0;
+
+#ifdef HAVE_WIDECHAR
+ mbstate_t st;
+ memset(&st, 0, sizeof(st));
+#endif
+ if (!sz || !buf)
+ return NULL;
+
+ r = buf;
+ *width = 0;
+
+ while (p && *p) {
+#ifdef HAVE_WIDECHAR
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+#else
+ size_t len = 1;
+#endif
+
+ if (len == 0)
+ break; /* end of string */
+
+ if (len == (size_t) -1 || len == (size_t) -2) {
+ len = 1;
+ /*
+ * Not valid multibyte sequence -- maybe it's
+ * printable char according to the current locales.
+ */
+ if (!isprint((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ } else {
+ (*width)++;
+ *r++ = *p;
+ }
+ } else if (*p == '\\' && *(p + 1) == 'x') {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ } else {
+ memcpy(r, p, len);
+ r += len;
+ *width += wcwidth(wc);
+ }
+ p += len;
+ }
+
+ *r = '\0';
+ return buf;
+}
+
+size_t mbs_safe_encode_size(size_t bytes)
+{
+ return (bytes * 4) + 1;
+}
+
+/*
+ * Returns allocated string where all control and non-printable chars are
+ * replaced with \x?? hex sequence.
+ */
+char *mbs_safe_encode(const char *s, size_t *width)
+{
+ size_t sz = s ? strlen(s) : 0;
+ char *buf, *ret = NULL;
+
+ if (!sz)
+ return NULL;
+ buf = malloc(mbs_safe_encode_size(sz));
+ if (buf)
+ ret = mbs_safe_encode_to_buffer(s, width, buf, NULL);
+ if (!ret)
+ free(buf);
+ return ret;
+}
+
+/*
+ * Returns allocated string where all broken widechars chars are
+ * replaced with \x?? hex sequence.
+ */
+char *mbs_invalid_encode(const char *s, size_t *width)
+{
+ size_t sz = s ? strlen(s) : 0;
+ char *buf, *ret = NULL;
+
+ if (!sz)
+ return NULL;
+ buf = malloc(mbs_safe_encode_size(sz));
+ if (buf)
+ ret = mbs_invalid_encode_to_buffer(s, width, buf);
+ if (!ret)
+ free(buf);
+ return ret;
+}
+
+#ifdef HAVE_WIDECHAR
+
+static bool
+wc_ensure_printable (wchar_t *wchars)
+{
+ bool replaced = false;
+ wchar_t *wc = wchars;
+ while (*wc)
+ {
+ if (!iswprint ((wint_t) *wc))
+ {
+ *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+ replaced = true;
+ }
+ wc++;
+ }
+ return replaced;
+}
+
+/* Truncate wchar string to width cells.
+ * Returns number of cells used. */
+
+static size_t
+wc_truncate (wchar_t *wc, size_t width)
+{
+ size_t cells = 0;
+ int next_cells = 0;
+
+ while (*wc)
+ {
+ next_cells = wcwidth (*wc);
+ if (next_cells == -1) /* non printable */
+ {
+ *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+ next_cells = 1;
+ }
+ if (cells + next_cells > width)
+ break;
+
+ cells += next_cells;
+ wc++;
+ }
+ *wc = L'\0';
+ return cells;
+}
+
+static int
+rpl_wcswidth (const wchar_t *s, size_t n)
+{
+ int ret = 0;
+
+ while (n-- > 0 && *s != L'\0')
+ {
+ int nwidth = wcwidth (*s++);
+ if (nwidth == -1) /* non printable */
+ return -1;
+ if (ret > (INT_MAX - nwidth)) /* overflow */
+ return -1;
+ ret += nwidth;
+ }
+
+ return ret;
+}
+#endif /* HAVE_WIDECHAR */
+
+/* Truncate multi-byte string to @width and returns number of
+ * bytes of the new string @str, and in @width returns number
+ * of cells.
+ */
+size_t
+mbs_truncate(char *str, size_t *width)
+{
+ ssize_t bytes = strlen(str);
+#ifdef HAVE_WIDECHAR
+ ssize_t sz = mbstowcs(NULL, str, 0);
+ wchar_t *wcs = NULL;
+
+ if (sz == (ssize_t) -1)
+ goto done;
+
+ wcs = calloc(1, (sz + 1) * sizeof(wchar_t));
+ if (!wcs)
+ goto done;
+
+ if (!mbstowcs(wcs, str, sz))
+ goto done;
+ *width = wc_truncate(wcs, *width);
+ bytes = wcstombs(str, wcs, bytes);
+done:
+ free(wcs);
+#else
+ if (bytes >= 0 && *width < (size_t) bytes)
+ bytes = *width;
+#endif
+ if (bytes >= 0)
+ str[bytes] = '\0';
+ return bytes;
+}
+
+/* Write N_SPACES space characters to DEST while ensuring
+ nothing is written beyond DEST_END. A terminating NUL
+ is always added to DEST.
+ A pointer to the terminating NUL is returned. */
+
+static char*
+mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces, int padchar)
+{
+ for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--)
+ *dest++ = padchar;
+ *dest = '\0';
+ return dest;
+}
+
+size_t
+mbsalign (const char *src, char *dest, size_t dest_size,
+ size_t *width, mbs_align_t align, int flags)
+{
+ return mbsalign_with_padding(src, dest, dest_size, width, align, flags, ' ');
+}
+
+/* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
+ characters; write the result into the DEST_SIZE-byte buffer, DEST.
+ ALIGNMENT specifies whether to left- or right-justify or to center.
+ If SRC requires more than *WIDTH columns, truncate it to fit.
+ When centering, the number of trailing spaces may be one less than the
+ number of leading spaces. The FLAGS parameter is unused at present.
+ Return the length in bytes required for the final result, not counting
+ the trailing NUL. A return value of DEST_SIZE or larger means there
+ wasn't enough space. DEST will be NUL terminated in any case.
+ Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
+ or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
+ Update *WIDTH to indicate how many columns were used before padding. */
+
+size_t
+mbsalign_with_padding (const char *src, char *dest, size_t dest_size,
+ size_t *width, mbs_align_t align,
+#ifdef HAVE_WIDECHAR
+ int flags,
+#else
+ int flags __attribute__((__unused__)),
+#endif
+ int padchar)
+{
+ size_t ret = -1;
+ size_t src_size = strlen (src) + 1;
+ char *newstr = NULL;
+ wchar_t *str_wc = NULL;
+ const char *str_to_print = src;
+ size_t n_cols = src_size - 1;
+ size_t n_used_bytes = n_cols; /* Not including NUL */
+ size_t n_spaces = 0, space_left;
+
+#ifdef HAVE_WIDECHAR
+ bool conversion = false;
+ bool wc_enabled = false;
+
+ /* In multi-byte locales convert to wide characters
+ to allow easy truncation. Also determine number
+ of screen columns used. */
+ if (MB_CUR_MAX > 1)
+ {
+ size_t src_chars = mbstowcs (NULL, src, 0);
+ if (src_chars == (size_t) -1)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ src_chars += 1; /* make space for NUL */
+ str_wc = malloc (src_chars * sizeof (wchar_t));
+ if (str_wc == NULL)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ if (mbstowcs (str_wc, src, src_chars) != 0)
+ {
+ str_wc[src_chars - 1] = L'\0';
+ wc_enabled = true;
+ conversion = wc_ensure_printable (str_wc);
+ n_cols = rpl_wcswidth (str_wc, src_chars);
+ }
+ }
+
+ /* If we transformed or need to truncate the source string
+ then create a modified copy of it. */
+ if (wc_enabled && (conversion || (n_cols > *width)))
+ {
+ if (conversion)
+ {
+ /* May have increased the size by converting
+ \t to \uFFFD for example. */
+ src_size = wcstombs(NULL, str_wc, 0) + 1;
+ }
+ newstr = malloc (src_size);
+ if (newstr == NULL)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ str_to_print = newstr;
+ n_cols = wc_truncate (str_wc, *width);
+ n_used_bytes = wcstombs (newstr, str_wc, src_size);
+ }
+
+mbsalign_unibyte:
+#endif
+
+ if (n_cols > *width) /* Unibyte truncation required. */
+ {
+ n_cols = *width;
+ n_used_bytes = n_cols;
+ }
+
+ if (*width > n_cols) /* Padding required. */
+ n_spaces = *width - n_cols;
+
+ /* indicate to caller how many cells needed (not including padding). */
+ *width = n_cols;
+
+ /* indicate to caller how many bytes needed (not including NUL). */
+ ret = n_used_bytes + (n_spaces * 1);
+
+ /* Write as much NUL terminated output to DEST as possible. */
+ if (dest_size != 0)
+ {
+ char *dest_end = dest + dest_size - 1;
+ size_t start_spaces;
+ size_t end_spaces;
+
+ switch (align)
+ {
+ case MBS_ALIGN_CENTER:
+ start_spaces = n_spaces / 2 + n_spaces % 2;
+ end_spaces = n_spaces / 2;
+ break;
+ case MBS_ALIGN_LEFT:
+ start_spaces = 0;
+ end_spaces = n_spaces;
+ break;
+ case MBS_ALIGN_RIGHT:
+ start_spaces = n_spaces;
+ end_spaces = 0;
+ break;
+ default:
+ abort();
+ }
+
+ dest = mbs_align_pad (dest, dest_end, start_spaces, padchar);
+ space_left = dest_end - dest;
+ dest = mempcpy (dest, str_to_print, min (n_used_bytes, space_left));
+ mbs_align_pad (dest, dest_end, end_spaces, padchar);
+ }
+#ifdef HAVE_WIDECHAR
+mbsalign_cleanup:
+#endif
+ free (str_wc);
+ free (newstr);
+
+ return ret;
+}
diff --git a/utils/lib/mbsedit.c b/utils/lib/mbsedit.c
new file mode 100644
index 0000000..8ce5901
--- /dev/null
+++ b/utils/lib/mbsedit.c
@@ -0,0 +1,225 @@
+/*
+ * Very simple multibyte buffer editor. Allows to maintaine the current
+ * position in the string, add and remove chars on the current position.
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "mbsalign.h"
+#include "mbsedit.h"
+
+struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells)
+{
+ struct mbs_editor *edit = calloc(1, sizeof(*edit));
+
+ if (edit) {
+ edit->buf = buf;
+ edit->max_bytes = bufsz;
+ edit->max_cells = ncells;
+ edit->cur_cells = mbs_safe_width(buf);
+ edit->cur_bytes = strlen(buf);
+ }
+ return edit;
+}
+
+char *mbs_free_edit(struct mbs_editor *edit)
+{
+ char *ret = edit ? edit->buf : NULL;
+
+ free(edit);
+ return ret;
+}
+
+static size_t mbs_next(const char *str, size_t *ncells)
+{
+#ifdef HAVE_WIDECHAR
+ wchar_t wc;
+ size_t n = 0;
+
+ if (!str || !*str)
+ return 0;
+
+ n = mbrtowc(&wc, str, MB_CUR_MAX, NULL);
+ *ncells = wcwidth(wc);
+ return n;
+#else
+ if (!str || !*str)
+ return 0;
+ *ncells = 1;
+ return 1;
+#endif
+}
+
+static size_t mbs_prev(const char *start, const char *end, size_t *ncells)
+{
+#ifdef HAVE_WIDECHAR
+ wchar_t wc = 0;
+ const char *p, *prev;
+ size_t n = 0;
+
+ if (!start || !end || start == end || !*start)
+ return 0;
+
+ prev = p = start;
+ while (p < end) {
+ n = mbrtowc(&wc, p, MB_CUR_MAX, NULL);
+ prev = p;
+
+ if (n == (size_t) -1 || n == (size_t) -2)
+ p++;
+ else
+ p += n;
+ }
+
+ if (prev == end)
+ return 0;
+ *ncells = wcwidth(wc);
+ return n;
+#else
+ if (!start || !end || start == end || !*start)
+ return 0;
+ *ncells = 1;
+ return 1;
+#endif
+}
+
+int mbs_edit_goto(struct mbs_editor *edit, int where)
+{
+ switch (where) {
+ case MBS_EDIT_LEFT:
+ if (edit->cursor == 0)
+ return 1;
+ else {
+ size_t n, cells;
+ n = mbs_prev(edit->buf, edit->buf + edit->cursor, &cells);
+ if (n) {
+ edit->cursor -= n;
+ edit->cursor_cells -= cells;
+ }
+ }
+ break;
+ case MBS_EDIT_RIGHT:
+ if (edit->cursor_cells >= edit->cur_cells)
+ return 1;
+ else {
+ size_t n, cells;
+ n = mbs_next(edit->buf + edit->cursor, &cells);
+ if (n) {
+ edit->cursor += n;
+ edit->cursor_cells += cells;
+ }
+ }
+ break;
+ case MBS_EDIT_HOME:
+ edit->cursor = 0;
+ edit->cursor_cells = 0;
+ break;
+ case MBS_EDIT_END:
+ edit->cursor = edit->cur_bytes;
+ edit->cursor_cells = edit->cur_cells;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Remove next MB from @str, returns number of removed bytes */
+static size_t remove_next(char *str, size_t *ncells)
+{
+ /* all in bytes! */
+ size_t bytes, move_bytes, n;
+
+ n = mbs_next(str, ncells);
+ bytes = strlen(str);
+ move_bytes = bytes - n;
+
+ memmove(str, str + n, move_bytes);
+ str[bytes - n] = '\0';
+ return n;
+}
+
+static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
+{
+ /* all in bytes! */
+ size_t n = 1, bytes;
+ char *in;
+
+#ifdef HAVE_WIDECHAR
+ wchar_t wc = (wchar_t) c;
+ char in_buf[MB_CUR_MAX];
+
+ n = wctomb(in_buf, wc);
+ if (n == (size_t) -1)
+ return n;
+ *ncells = wcwidth(wc);
+ in = in_buf;
+#else
+ *ncells = 1;
+ in = (char *) &c;
+#endif
+ bytes = strlen(str);
+
+ memmove(str + n, str, bytes);
+ memcpy(str, in, n);
+ str[bytes + n] = '\0';
+ return n;
+}
+
+static int mbs_edit_remove(struct mbs_editor *edit)
+{
+ size_t n, ncells;
+
+ if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
+ return 1;
+
+ n = remove_next(edit->buf + edit->cursor, &ncells);
+ if (n == (size_t)-1)
+ return 1;
+
+ edit->cur_bytes -= n;
+ edit->cur_cells = mbs_safe_width(edit->buf);
+ return 0;
+}
+
+int mbs_edit_delete(struct mbs_editor *edit)
+{
+ if (edit->cursor >= edit->cur_bytes
+ && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
+ return 1;
+
+ return mbs_edit_remove(edit);
+}
+
+int mbs_edit_backspace(struct mbs_editor *edit)
+{
+ if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
+ return mbs_edit_remove(edit);
+ return 1;
+}
+
+int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
+{
+ size_t n, ncells;
+
+ if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
+ return 1;
+
+ n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
+ if (n == (size_t)-1)
+ return 1;
+
+ edit->cursor += n;
+ edit->cursor_cells += ncells;
+ edit->cur_bytes += n;
+ edit->cur_cells = mbs_safe_width(edit->buf);
+ return 0;
+}
diff --git a/utils/lib/md5.c b/utils/lib/md5.c
new file mode 100644
index 0000000..3765ab9
--- /dev/null
+++ b/utils/lib/md5.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h> /* for memcpy() */
+
+#include "md5.h"
+
+#if !defined(WORDS_BIGENDIAN)
+# define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif /* !ASM_MD5 */
+#endif /* !WORDS_BIGENDIAN */
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void ul_MD5Init(struct UL_MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void ul_MD5Update(struct UL_MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ ul_MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ ul_MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void ul_MD5Final(unsigned char digest[UL_MD5LENGTH], struct UL_MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ ul_MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform.
+ * Use memcpy to avoid aliasing problems. On most systems,
+ * this will be optimized away to the same code.
+ */
+ memcpy(&ctx->in[14 * sizeof(uint32_t)], &ctx->bits[0], 4);
+ memcpy(&ctx->in[15 * sizeof(uint32_t)], &ctx->bits[1], 4);
+
+ ul_MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, UL_MD5LENGTH);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void ul_MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
+
diff --git a/utils/lib/monotonic.c b/utils/lib/monotonic.c
new file mode 100644
index 0000000..f0aeba6
--- /dev/null
+++ b/utils/lib/monotonic.c
@@ -0,0 +1,81 @@
+/*
+ * Please, don't add this file to libcommon because clock_gettime() requires
+ * -lrt on systems with old libc.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
+#include <time.h>
+#include <signal.h>
+#ifdef HAVE_SYSINFO
+#include <sys/sysinfo.h>
+#endif
+#include <sys/time.h>
+
+#include "c.h"
+#include "monotonic.h"
+
+int get_boot_time(struct timeval *boot_time)
+{
+#ifdef CLOCK_BOOTTIME
+ struct timespec hires_uptime;
+ struct timeval lores_uptime;
+#endif
+ struct timeval now;
+#ifdef HAVE_SYSINFO
+ struct sysinfo info;
+#endif
+
+ if (gettimeofday(&now, NULL) != 0)
+ return -errno;
+#ifdef CLOCK_BOOTTIME
+ if (clock_gettime(CLOCK_BOOTTIME, &hires_uptime) == 0) {
+ TIMESPEC_TO_TIMEVAL(&lores_uptime, &hires_uptime);
+ timersub(&now, &lores_uptime, boot_time);
+ return 0;
+ }
+#endif
+#ifdef HAVE_SYSINFO
+ /* fallback */
+ if (sysinfo(&info) != 0)
+ return -errno;
+
+ boot_time->tv_sec = now.tv_sec - info.uptime;
+ boot_time->tv_usec = 0;
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+time_t get_suspended_time(void)
+{
+#if defined(CLOCK_BOOTTIME) && defined(CLOCK_MONOTONIC)
+ struct timespec boot, mono;
+
+ if (clock_gettime(CLOCK_BOOTTIME, &boot) == 0 &&
+ clock_gettime(CLOCK_MONOTONIC, &mono) == 0)
+ return boot.tv_sec - mono.tv_sec;
+#endif
+ return 0;
+}
+
+int gettime_monotonic(struct timeval *tv)
+{
+#ifdef CLOCK_MONOTONIC
+ /* Can slew only by ntp and adjtime */
+ int ret;
+ struct timespec ts;
+
+ /* Linux specific, can't slew */
+ if (!(ret = clock_gettime(UL_CLOCK_MONOTONIC, &ts))) {
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+ }
+ return ret;
+#else
+ return gettimeofday(tv, NULL);
+#endif
+}
+
+
diff --git a/utils/lib/pager.c b/utils/lib/pager.c
new file mode 100644
index 0000000..b3cf6ee
--- /dev/null
+++ b/utils/lib/pager.c
@@ -0,0 +1,317 @@
+/*
+ * Based on linux-perf/git scm
+ *
+ * Some modifications and simplifications for util-linux
+ * by Davidlohr Bueso <dave@xxxxxxx> - March 2012.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+#include "ttyutils.h"
+#include "pager.h"
+
+#define NULL_DEVICE "/dev/null"
+
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+
+struct child_process {
+ const char **argv;
+ pid_t pid;
+ int in;
+ int out;
+ int err;
+
+ int org_err;
+ int org_out;
+ struct sigaction orig_sigint;
+ struct sigaction orig_sighup;
+ struct sigaction orig_sigterm;
+ struct sigaction orig_sigquit;
+ struct sigaction orig_sigpipe;
+
+ unsigned no_stdin:1;
+ void (*preexec_cb)(void);
+};
+static struct child_process pager_process;
+
+static inline void close_pair(int fd[2])
+{
+ close(fd[0]);
+ close(fd[1]);
+}
+
+static int start_command(struct child_process *cmd)
+{
+ int need_in;
+ int fdin[2];
+
+ /*
+ * In case of errors we must keep the promise to close FDs
+ * that have been passed in via ->in and ->out.
+ */
+ need_in = !cmd->no_stdin && cmd->in < 0;
+ if (need_in) {
+ if (pipe(fdin) < 0) {
+ if (cmd->out > 0)
+ close(cmd->out);
+ return -1;
+ }
+ cmd->in = fdin[1];
+ }
+
+ fflush(NULL);
+ cmd->pid = fork();
+ if (!cmd->pid) {
+ if (need_in) {
+ dup2(fdin[0], STDIN_FILENO);
+ close_pair(fdin);
+ } else if (cmd->in > 0) {
+ dup2(cmd->in, STDIN_FILENO);
+ close(cmd->in);
+ }
+
+ cmd->preexec_cb();
+ execvp(cmd->argv[0], (char *const*) cmd->argv);
+ errexec(cmd->argv[0]);
+ }
+
+ if (cmd->pid < 0) {
+ if (need_in)
+ close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
+ return -1;
+ }
+
+ if (need_in)
+ close(fdin[0]);
+ else if (cmd->in)
+ close(cmd->in);
+ return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+ for (;;) {
+ int status, code;
+ pid_t waiting = waitpid(pid, &status, 0);
+
+ if (waiting < 0) {
+ if (errno == EINTR)
+ continue;
+ err(EXIT_FAILURE, _("waitpid failed (%s)"), strerror(errno));
+ }
+ if (waiting != pid)
+ return -1;
+ if (WIFSIGNALED(status))
+ return -1;
+
+ if (!WIFEXITED(status))
+ return -1;
+ code = WEXITSTATUS(status);
+ switch (code) {
+ case 127:
+ return -1;
+ case 0:
+ return 0;
+ default:
+ return -1;
+ }
+ }
+}
+
+static int finish_command(struct child_process *cmd)
+{
+ return wait_or_whine(cmd->pid);
+}
+
+static void pager_preexec(void)
+{
+ /*
+ * Work around bug in "less" by not starting it until we
+ * have real input
+ */
+ fd_set in, ex;
+
+ FD_ZERO(&in);
+ FD_SET(STDIN_FILENO, &in);
+ ex = in;
+
+ select(STDIN_FILENO + 1, &in, NULL, &ex, NULL);
+
+ if (setenv("LESS", "FRSX", 0) != 0)
+ warn(_("failed to set the %s environment variable"), "LESS");
+}
+
+static void wait_for_pager(void)
+{
+ if (pager_process.pid == 0)
+ return;
+
+ fflush(stdout);
+ fflush(stderr);
+ /* signal EOF to pager */
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+ wait_for_pager();
+ raise(signo);
+}
+
+static int has_command(const char *cmd)
+{
+ const char *path;
+ char *p, *s;
+ int rc = 0;
+
+ if (!cmd)
+ goto done;
+ if (*cmd == '/') {
+ rc = access(cmd, X_OK) == 0;
+ goto done;
+ }
+
+ path = getenv("PATH");
+ if (!path)
+ goto done;
+ p = xstrdup(path);
+ if (!p)
+ goto done;
+
+ 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;
+ close(fd);
+ if (rc)
+ break;
+ }
+ free(p);
+done:
+ /*fprintf(stderr, "has PAGER %s rc=%d\n", cmd, rc);*/
+ return rc;
+}
+
+static void __setup_pager(void)
+{
+ const char *pager = getenv("PAGER");
+ struct sigaction sa;
+
+ if (!isatty(STDOUT_FILENO))
+ return;
+
+ if (!pager)
+ pager = "less";
+ else if (!*pager || !strcmp(pager, "cat"))
+ return;
+
+ if (!has_command(pager))
+ return;
+
+ /* spawn the pager */
+ pager_argv[2] = pager;
+ pager_process.argv = pager_argv;
+ pager_process.in = -1;
+ pager_process.preexec_cb = pager_preexec;
+
+ if (start_command(&pager_process))
+ return;
+
+ /* original process continues, but writes to the pipe */
+ dup2(pager_process.in, STDOUT_FILENO);
+ if (isatty(STDERR_FILENO))
+ dup2(pager_process.in, STDERR_FILENO);
+ close(pager_process.in);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = wait_for_pager_signal;
+
+ /* this makes sure that the parent terminates after the pager */
+ sigaction(SIGINT, &sa, &pager_process.orig_sigint);
+ sigaction(SIGHUP, &sa, &pager_process.orig_sighup);
+ sigaction(SIGTERM, &sa, &pager_process.orig_sigterm);
+ sigaction(SIGQUIT, &sa, &pager_process.orig_sigquit);
+ sigaction(SIGPIPE, &sa, &pager_process.orig_sigpipe);
+}
+
+/* Setup pager and redirects output to the $PAGER. The pager is closed at exit.
+ */
+void pager_redirect(void)
+{
+ if (pager_process.pid)
+ return; /* already running */
+
+ __setup_pager();
+
+ atexit(wait_for_pager);
+}
+
+/* Setup pager and redirect output, the pager may be closed by pager_close().
+ */
+void pager_open(void)
+{
+ if (pager_process.pid)
+ return; /* already running */
+
+ pager_process.org_out = dup(STDOUT_FILENO);
+ pager_process.org_err = dup(STDERR_FILENO);
+
+ __setup_pager();
+}
+
+/* Close pager and restore original std{out,err}.
+ */
+void pager_close(void)
+{
+ if (pager_process.pid == 0)
+ return;
+
+ wait_for_pager();
+
+ /* restore original output */
+ dup2(pager_process.org_out, STDOUT_FILENO);
+ dup2(pager_process.org_err, STDERR_FILENO);
+
+ close(pager_process.org_out);
+ close(pager_process.org_err);
+
+ /* restore original segnals setting */
+ sigaction(SIGINT, &pager_process.orig_sigint, NULL);
+ sigaction(SIGHUP, &pager_process.orig_sighup, NULL);
+ sigaction(SIGTERM, &pager_process.orig_sigterm, NULL);
+ sigaction(SIGQUIT, &pager_process.orig_sigquit, NULL);
+ sigaction(SIGPIPE, &pager_process.orig_sigpipe, NULL);
+
+ memset(&pager_process, 0, sizeof(pager_process));
+}
+
+#ifdef TEST_PROGRAM_PAGER
+
+#define MAX 255
+
+int main(int argc __attribute__ ((__unused__)),
+ char *argv[] __attribute__ ((__unused__)))
+{
+ int i;
+
+ pager_redirect();
+ for (i = 0; i < MAX; i++)
+ printf("%d\n", i);
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_PAGER */
diff --git a/utils/lib/path.c b/utils/lib/path.c
new file mode 100644
index 0000000..75fa853
--- /dev/null
+++ b/utils/lib/path.c
@@ -0,0 +1,1248 @@
+/*
+ * Simple functions to access files. Paths can be globally prefixed to read
+ * data from an alternative source (e.g. a /proc dump for regression tests).
+ *
+ * The paths is possible to format by printf-like way for functions with "f"
+ * postfix in the name (e.g. readf, openf, ... ul_path_readf_u64()).
+ *
+ * The ul_path_read_* API is possible to use without path_cxt handler. In this
+ * case is not possible to use global prefix and printf-like formatting.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com> [February 2018]
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "all-io.h"
+#include "path.h"
+#include "debug.h"
+#include "strutils.h"
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+static UL_DEBUG_DEFINE_MASK(ulpath);
+UL_DEBUG_DEFINE_MASKNAMES(ulpath) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define ULPATH_DEBUG_INIT (1 << 1)
+#define ULPATH_DEBUG_CXT (1 << 2)
+
+#define DBG(m, x) __UL_DBG(ulpath, ULPATH_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(ulpath, ULPATH_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpath)
+#include "debugobj.h"
+
+void ul_path_init_debug(void)
+{
+ if (ulpath_debug_mask)
+ return;
+ __UL_INIT_DEBUG_FROM_ENV(ulpath, ULPATH_DEBUG_, 0, ULPATH_DEBUG);
+}
+
+struct path_cxt *ul_new_path(const char *dir, ...)
+{
+ struct path_cxt *pc = calloc(1, sizeof(*pc));
+
+ if (!pc)
+ return NULL;
+
+ DBG(CXT, ul_debugobj(pc, "alloc"));
+
+ pc->refcount = 1;
+ pc->dir_fd = -1;
+
+ if (dir) {
+ int rc;
+ va_list ap;
+
+ va_start(ap, dir);
+ rc = vasprintf(&pc->dir_path, dir, ap);
+ va_end(ap);
+
+ if (rc < 0 || !pc->dir_path)
+ goto fail;
+ }
+ return pc;
+fail:
+ ul_unref_path(pc);
+ return NULL;
+}
+
+void ul_ref_path(struct path_cxt *pc)
+{
+ if (pc)
+ pc->refcount++;
+}
+
+void ul_unref_path(struct path_cxt *pc)
+{
+ if (!pc)
+ return;
+
+ pc->refcount--;
+
+ if (pc->refcount <= 0) {
+ DBG(CXT, ul_debugobj(pc, "dealloc"));
+ if (pc->dialect)
+ pc->free_dialect(pc);
+ ul_path_close_dirfd(pc);
+ free(pc->dir_path);
+ free(pc->prefix);
+ free(pc);
+ }
+}
+
+int ul_path_set_prefix(struct path_cxt *pc, const char *prefix)
+{
+ char *p = NULL;
+
+ assert(pc->dir_fd < 0);
+
+ if (prefix) {
+ p = strdup(prefix);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(pc->prefix);
+ pc->prefix = p;
+ DBG(CXT, ul_debugobj(pc, "new prefix: '%s'", p));
+ return 0;
+}
+
+const char *ul_path_get_prefix(struct path_cxt *pc)
+{
+ return pc ? pc->prefix : NULL;
+}
+
+int ul_path_set_dir(struct path_cxt *pc, const char *dir)
+{
+ char *p = NULL;
+
+ if (dir) {
+ p = strdup(dir);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ if (pc->dir_fd >= 0) {
+ close(pc->dir_fd);
+ pc->dir_fd = -1;
+ }
+
+ free(pc->dir_path);
+ pc->dir_path = p;
+ DBG(CXT, ul_debugobj(pc, "new dir: '%s'", p));
+ return 0;
+}
+
+const char *ul_path_get_dir(struct path_cxt *pc)
+{
+ return pc ? pc->dir_path : NULL;
+}
+
+int ul_path_set_dialect(struct path_cxt *pc, void *data, void free_data(struct path_cxt *))
+{
+ pc->dialect = data;
+ pc->free_dialect = free_data;
+ DBG(CXT, ul_debugobj(pc, "(re)set dialect"));
+ return 0;
+}
+
+void *ul_path_get_dialect(struct path_cxt *pc)
+{
+ return pc ? pc->dialect : NULL;
+}
+
+int ul_path_set_enoent_redirect(struct path_cxt *pc, int (*func)(struct path_cxt *, const char *, int *))
+{
+ pc->redirect_on_enoent = func;
+ return 0;
+}
+
+static const char *get_absdir(struct path_cxt *pc)
+{
+ int rc;
+ const char *dirpath;
+
+ if (!pc->prefix)
+ return pc->dir_path;
+
+ dirpath = pc->dir_path;
+ if (!dirpath)
+ return pc->prefix;
+ if (*dirpath == '/')
+ dirpath++;
+
+ rc = snprintf(pc->path_buffer, sizeof(pc->path_buffer), "%s/%s", pc->prefix, dirpath);
+ if (rc < 0)
+ return NULL;
+ if ((size_t)rc >= sizeof(pc->path_buffer)) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ return pc->path_buffer;
+}
+
+int ul_path_is_accessible(struct path_cxt *pc)
+{
+ const char *path;
+ assert(pc);
+
+ if (pc->dir_fd >= 0)
+ return 1;
+
+ path = get_absdir(pc);
+ if (!path)
+ return 0;
+ return access(path, F_OK) == 0;
+}
+
+int ul_path_get_dirfd(struct path_cxt *pc)
+{
+ assert(pc);
+ assert(pc->dir_path);
+
+ if (pc->dir_fd < 0) {
+ const char *path = get_absdir(pc);
+ if (!path)
+ return -errno;
+
+ DBG(CXT, ul_debugobj(pc, "opening dir: '%s'", path));
+ pc->dir_fd = open(path, O_RDONLY|O_CLOEXEC);
+ }
+
+ return pc->dir_fd;
+}
+
+/* Note that next ul_path_get_dirfd() will reopen the directory */
+void ul_path_close_dirfd(struct path_cxt *pc)
+{
+ assert(pc);
+
+ if (pc->dir_fd >= 0) {
+ DBG(CXT, ul_debugobj(pc, "closing dir"));
+ close(pc->dir_fd);
+ pc->dir_fd = -1;
+ }
+}
+
+int ul_path_isopen_dirfd(struct path_cxt *pc)
+{
+ return pc && pc->dir_fd >= 0;
+}
+
+static const char *ul_path_mkpath(struct path_cxt *pc, const char *path, va_list ap)
+{
+ int rc;
+
+ errno = 0;
+
+ rc = vsnprintf(pc->path_buffer, sizeof(pc->path_buffer), path, ap);
+ if (rc < 0) {
+ if (!errno)
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((size_t)rc >= sizeof(pc->path_buffer)) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ return pc->path_buffer;
+}
+
+char *ul_path_get_abspath(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
+{
+ if (path) {
+ int rc;
+ va_list ap;
+ const char *tail = NULL, *dirpath = pc->dir_path;
+
+ va_start(ap, path);
+ tail = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ if (dirpath && *dirpath == '/')
+ dirpath++;
+ if (tail && *tail == '/')
+ tail++;
+
+ rc = snprintf(buf, bufsz, "%s/%s/%s",
+ pc->prefix ? pc->prefix : "",
+ dirpath ? dirpath : "",
+ tail ? tail : "");
+
+ if ((size_t)rc >= bufsz) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ } else {
+ const char *tmp = get_absdir(pc);
+
+ if (!tmp)
+ return NULL;
+ xstrncpy(buf, tmp, bufsz);
+ }
+
+ return buf;
+}
+
+
+int ul_path_access(struct path_cxt *pc, int mode, const char *path)
+{
+ int rc;
+
+ if (!pc) {
+ rc = access(path, mode);
+ DBG(CXT, ul_debug("access '%s' [no context, rc=%d]", path, rc));
+ } else {
+ int dir = ul_path_get_dirfd(pc);
+ if (dir < 0)
+ return dir;
+ if (*path == '/')
+ path++;
+
+ rc = faccessat(dir, path, mode, 0);
+
+ if (rc && errno == ENOENT
+ && pc->redirect_on_enoent
+ && pc->redirect_on_enoent(pc, path, &dir) == 0)
+ rc = faccessat(dir, path, mode, 0);
+
+ DBG(CXT, ul_debugobj(pc, "access: '%s' [rc=%d]", path, rc));
+ }
+ return rc;
+}
+
+int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...)
+{
+ va_list ap;
+ const char *p;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_access(pc, mode, p);
+}
+
+int ul_path_stat(struct path_cxt *pc, struct stat *sb, const char *path)
+{
+ int rc;
+
+ if (!pc) {
+ rc = stat(path, sb);
+ DBG(CXT, ul_debug("stat '%s' [no context, rc=%d]", path, rc));
+ } else {
+ int dir = ul_path_get_dirfd(pc);
+ if (dir < 0)
+ return dir;
+ if (*path == '/')
+ path++;
+
+ rc = fstatat(dir, path, sb, 0);
+
+ if (rc && errno == ENOENT
+ && pc->redirect_on_enoent
+ && pc->redirect_on_enoent(pc, path, &dir) == 0)
+ rc = fstatat(dir, path, sb, 0);
+
+ DBG(CXT, ul_debugobj(pc, "stat '%s' [rc=%d]", path, rc));
+ }
+ return rc;
+}
+
+int ul_path_open(struct path_cxt *pc, int flags, const char *path)
+{
+ int fd;
+
+ if (!pc) {
+ fd = open(path, flags);
+ DBG(CXT, ul_debug("opening '%s' [no context]", path));
+ } else {
+ int fdx;
+ int dir = ul_path_get_dirfd(pc);
+ if (dir < 0)
+ return dir;
+
+ if (*path == '/')
+ path++;
+
+ fdx = fd = openat(dir, path, flags);
+
+ if (fd < 0 && errno == ENOENT
+ && pc->redirect_on_enoent
+ && pc->redirect_on_enoent(pc, path, &dir) == 0)
+ fd = openat(dir, path, flags);
+
+ DBG(CXT, ul_debugobj(pc, "opening '%s'%s", path, fdx != fd ? " [redirected]" : ""));
+ }
+ return fd;
+}
+
+int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap)
+{
+ const char *p = ul_path_mkpath(pc, path, ap);
+
+ return !p ? -errno : ul_path_open(pc, flags, p);
+}
+
+int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, path);
+ rc = ul_path_vopenf(pc, flags, path, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+/*
+ * Maybe stupid, but good enough ;-)
+ */
+static int mode2flags(const char *mode)
+{
+ int flags = 0;
+ const char *p;
+
+ for (p = mode; p && *p; p++) {
+ if (*p == 'r' && *(p + 1) == '+')
+ flags |= O_RDWR;
+ else if (*p == 'r')
+ flags |= O_RDONLY;
+
+ else if (*p == 'w' && *(p + 1) == '+')
+ flags |= O_RDWR | O_TRUNC;
+ else if (*p == 'w')
+ flags |= O_WRONLY | O_TRUNC;
+
+ else if (*p == 'a' && *(p + 1) == '+')
+ flags |= O_RDWR | O_APPEND;
+ else if (*p == 'a')
+ flags |= O_WRONLY | O_APPEND;
+#ifdef O_CLOEXEC
+ else if (*p == *UL_CLOEXECSTR)
+ flags |= O_CLOEXEC;
+#endif
+ }
+
+ return flags;
+}
+
+FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path)
+{
+ int flags = mode2flags(mode);
+ int fd = ul_path_open(pc, flags, path);
+
+ if (fd < 0)
+ return NULL;
+
+ return fdopen(fd, mode);
+}
+
+
+FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap)
+{
+ const char *p = ul_path_mkpath(pc, path, ap);
+
+ return !p ? NULL : ul_path_fopen(pc, mode, p);
+}
+
+FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...)
+{
+ FILE *f;
+ va_list ap;
+
+ va_start(ap, path);
+ f = ul_path_vfopenf(pc, mode, path, ap);
+ va_end(ap);
+
+ return f;
+}
+
+/*
+ * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
+ * to the directory addressed by @pc.
+ */
+DIR *ul_path_opendir(struct path_cxt *pc, const char *path)
+{
+ DIR *dir;
+ int fd = -1;
+
+ if (path)
+ fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
+ else if (pc->dir_path) {
+ int dirfd;
+
+ DBG(CXT, ul_debugobj(pc, "duplicate dir path"));
+ dirfd = ul_path_get_dirfd(pc);
+ if (dirfd >= 0)
+ fd = dup_fd_cloexec(dirfd, STDERR_FILENO + 1);
+ }
+
+ if (fd < 0)
+ return NULL;
+
+ dir = fdopendir(fd);
+ if (!dir) {
+ close(fd);
+ return NULL;
+ }
+ if (!path)
+ rewinddir(dir);
+ return dir;
+}
+
+
+/*
+ * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
+ * to the directory addressed by @pc.
+ */
+DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap)
+{
+ const char *p = ul_path_mkpath(pc, path, ap);
+
+ return !p ? NULL : ul_path_opendir(pc, p);
+}
+
+/*
+ * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
+ * to the directory addressed by @pc.
+ */
+DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...)
+{
+ va_list ap;
+ DIR *dir;
+
+ va_start(ap, path);
+ dir = ul_path_vopendirf(pc, path, ap);
+ va_end(ap);
+
+ return dir;
+}
+
+/*
+ * If @path is NULL then readlink is called on @pc directory.
+ */
+ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path)
+{
+ int dirfd;
+
+ if (!path) {
+ const char *p = get_absdir(pc);
+ if (!p)
+ return -errno;
+ return readlink(p, buf, bufsiz);
+ }
+
+ dirfd = ul_path_get_dirfd(pc);
+ if (dirfd < 0)
+ return dirfd;
+
+ if (*path == '/')
+ path++;
+
+ return readlinkat(dirfd, path, buf, bufsiz);
+}
+
+/*
+ * If @path is NULL then readlink is called on @pc directory.
+ */
+ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_readlink(pc, buf, bufsiz, p);
+}
+
+int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path)
+{
+ int rc, errsv;
+ int fd;
+
+ fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
+ if (fd < 0)
+ return -errno;
+
+ DBG(CXT, ul_debug(" reading '%s'", path));
+ rc = read_all(fd, buf, len);
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap)
+{
+ const char *p = ul_path_mkpath(pc, path, ap);
+
+ return !p ? -errno : ul_path_read(pc, buf, len, p);
+}
+
+int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, path);
+ rc = ul_path_vreadf(pc, buf, len, path, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+
+/*
+ * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ
+ * (send patch if you need something bigger;-)
+ *
+ * Returns size of the string!
+ */
+int ul_path_read_string(struct path_cxt *pc, char **str, const char *path)
+{
+ char buf[BUFSIZ];
+ int rc;
+
+ if (!str)
+ return -EINVAL;
+
+ *str = NULL;
+ rc = ul_path_read(pc, buf, sizeof(buf) - 1, path);
+ if (rc < 0)
+ return rc;
+
+ /* Remove tailing newline (usual in sysfs) */
+ if (rc > 0 && *(buf + rc - 1) == '\n')
+ --rc;
+
+ buf[rc] = '\0';
+ *str = strdup(buf);
+ if (!*str)
+ rc = -ENOMEM;
+
+ return rc;
+}
+
+int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_string(pc, str, p);
+}
+
+int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path)
+{
+ int rc = ul_path_read(pc, buf, bufsz - 1, path);
+ if (rc < 0)
+ return rc;
+
+ /* Remove tailing newline (usual in sysfs) */
+ if (rc > 0 && *(buf + rc - 1) == '\n')
+ buf[--rc] = '\0';
+ else
+ buf[rc - 1] = '\0';
+
+ return rc;
+}
+
+int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_buffer(pc, buf, bufsz, p);
+}
+
+int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...)
+{
+ FILE *f;
+ va_list fmt_ap;
+ int rc;
+
+ f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path);
+ if (!f)
+ return -EINVAL;
+
+ DBG(CXT, ul_debug(" fscanf [%s] '%s'", fmt, path));
+
+ va_start(fmt_ap, fmt);
+ rc = vfscanf(f, fmt, fmt_ap);
+ va_end(fmt_ap);
+
+ fclose(f);
+ return rc;
+}
+
+int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
+{
+ FILE *f;
+ va_list fmt_ap;
+ int rc;
+
+ f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
+ if (!f)
+ return -EINVAL;
+
+ va_start(fmt_ap, fmt);
+ rc = vfscanf(f, fmt, fmt_ap);
+ va_end(fmt_ap);
+
+ fclose(f);
+ return rc;
+}
+
+
+int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path)
+{
+ int64_t x = 0;
+ int rc;
+
+ rc = ul_path_scanf(pc, path, "%"SCNd64, &x);
+ if (rc != 1)
+ return -1;
+ if (res)
+ *res = x;
+ return 0;
+}
+
+int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_s64(pc, res, p);
+}
+
+int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path)
+{
+ uint64_t x = 0;
+ int rc;
+
+ rc = ul_path_scanf(pc, path, "%"SCNu64, &x);
+ if (rc != 1)
+ return -1;
+ if (res)
+ *res = x;
+ return 0;
+}
+
+int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_u64(pc, res, p);
+}
+
+int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path)
+{
+ int rc, x = 0;
+
+ rc = ul_path_scanf(pc, path, "%d", &x);
+ if (rc != 1)
+ return -1;
+ if (res)
+ *res = x;
+ return 0;
+}
+
+int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_s32(pc, res, p);
+}
+
+int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path)
+{
+ int rc;
+ unsigned int x;
+
+ rc = ul_path_scanf(pc, path, "%u", &x);
+ if (rc != 1)
+ return -1;
+ if (res)
+ *res = x;
+ return 0;
+}
+
+int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_u32(pc, res, p);
+}
+
+int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path)
+{
+ int rc, maj, min;
+
+ rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min);
+ if (rc != 2)
+ return -1;
+ if (res)
+ *res = makedev(maj, min);
+ return 0;
+}
+
+int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_read_majmin(pc, res, p);
+}
+
+int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path)
+{
+ int rc, errsv;
+ int fd;
+
+ fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
+ if (fd < 0)
+ return -errno;
+
+ rc = write_all(fd, str, strlen(str));
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_write_string(pc, str, p);
+}
+
+int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path)
+{
+ char buf[sizeof(stringify_value(LLONG_MAX))];
+ int rc, errsv;
+ int fd, len;
+
+ fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
+ if (fd < 0)
+ return -errno;
+
+ len = snprintf(buf, sizeof(buf), "%" PRId64, num);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ rc = len < 0 ? -errno : -E2BIG;
+ else
+ rc = write_all(fd, buf, len);
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path)
+{
+ char buf[sizeof(stringify_value(ULLONG_MAX))];
+ int rc, errsv;
+ int fd, len;
+
+ fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
+ if (fd < 0)
+ return -errno;
+
+ len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ rc = len < 0 ? -errno : -E2BIG;
+ else
+ rc = write_all(fd, buf, len);
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_write_u64(pc, num, p);
+
+}
+
+int ul_path_count_dirents(struct path_cxt *pc, const char *path)
+{
+ DIR *dir;
+ int r = 0;
+
+ dir = ul_path_opendir(pc, path);
+ if (!dir)
+ return 0;
+
+ while (xreaddir(dir)) r++;
+
+ closedir(dir);
+ return r;
+}
+
+int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = ul_path_mkpath(pc, path, ap);
+ va_end(ap);
+
+ return !p ? -errno : ul_path_count_dirents(pc, p);
+}
+
+/*
+ * 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];
+ int rc;
+
+ *set = NULL;
+
+ f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
+ if (!f)
+ return -errno;
+
+ rc = fgets(buf, len, f) == NULL ? -errno : 0;
+ fclose(f);
+
+ if (rc)
+ return rc;
+
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ *set = cpuset_alloc(maxcpus, &setsize, NULL);
+ if (!*set)
+ return -ENOMEM;
+
+ if (islist) {
+ if (cpulist_parse(buf, *set, setsize, 0)) {
+ cpuset_free(*set);
+ return -EINVAL;
+ }
+ } else {
+ if (cpumask_parse(buf, *set, setsize)) {
+ cpuset_free(*set);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
+{
+ va_list ap;
+ int rc = 0;
+
+ va_start(ap, path);
+ rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
+{
+ va_list ap;
+ int rc = 0;
+
+ va_start(ap, path);
+ rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+#endif /* HAVE_CPU_SET_T */
+
+
+#ifdef TEST_PROGRAM_PATH
+#include <getopt.h>
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
+ fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
+
+ fputs(" Commands:\n", stdout);
+ fputs(" read-u64 <file> read uint64_t from file\n", stdout);
+ fputs(" read-s64 <file> read int64_t from file\n", stdout);
+ fputs(" read-u32 <file> read uint32_t from file\n", stdout);
+ fputs(" read-s32 <file> read int32_t from file\n", stdout);
+ fputs(" read-string <file> read string from file\n", stdout);
+ fputs(" read-majmin <file> read devno from file\n", stdout);
+ fputs(" read-link <file> read symlink\n", stdout);
+ fputs(" write-string <file> <str> write string from file\n", stdout);
+ fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
+
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ const char *prefix = NULL, *dir, *file, *command;
+ struct path_cxt *pc = NULL;
+
+ static const struct option longopts[] = {
+ { "prefix", 1, NULL, 'p' },
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'p':
+ prefix = optarg;
+ break;
+ case 'h':
+ usage();
+ break;
+ default:
+ err(EXIT_FAILURE, "try --help");
+ }
+ }
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<dir> not defined");
+ dir = argv[optind++];
+
+ ul_path_init_debug();
+
+ pc = ul_new_path(dir);
+ if (!pc)
+ err(EXIT_FAILURE, "failed to initialize path context");
+ if (prefix)
+ ul_path_set_prefix(pc, prefix);
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<command> not defined");
+ command = argv[optind++];
+
+ if (strcmp(command, "read-u32") == 0) {
+ uint32_t res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_u32(pc, &res, file) != 0)
+ err(EXIT_FAILURE, "read u64 failed");
+ printf("read: %s: %u\n", file, res);
+
+ if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
+ err(EXIT_FAILURE, "readf u64 failed");
+ printf("readf: %s: %u\n", file, res);
+
+ } else if (strcmp(command, "read-s32") == 0) {
+ int32_t res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_s32(pc, &res, file) != 0)
+ err(EXIT_FAILURE, "read u64 failed");
+ printf("read: %s: %d\n", file, res);
+
+ if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
+ err(EXIT_FAILURE, "readf u64 failed");
+ printf("readf: %s: %d\n", file, res);
+
+ } else if (strcmp(command, "read-u64") == 0) {
+ uint64_t res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_u64(pc, &res, file) != 0)
+ err(EXIT_FAILURE, "read u64 failed");
+ printf("read: %s: %" PRIu64 "\n", file, res);
+
+ if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
+ err(EXIT_FAILURE, "readf u64 failed");
+ printf("readf: %s: %" PRIu64 "\n", file, res);
+
+ } else if (strcmp(command, "read-s64") == 0) {
+ int64_t res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_s64(pc, &res, file) != 0)
+ err(EXIT_FAILURE, "read u64 failed");
+ printf("read: %s: %" PRIu64 "\n", file, res);
+
+ if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
+ err(EXIT_FAILURE, "readf u64 failed");
+ printf("readf: %s: %" PRIu64 "\n", file, res);
+
+ } else if (strcmp(command, "read-majmin") == 0) {
+ dev_t res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_majmin(pc, &res, file) != 0)
+ err(EXIT_FAILURE, "read maj:min failed");
+ printf("read: %s: %d\n", file, (int) res);
+
+ if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
+ err(EXIT_FAILURE, "readf maj:min failed");
+ printf("readf: %s: %d\n", file, (int) res);
+
+ } else if (strcmp(command, "read-string") == 0) {
+ char *res;
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_read_string(pc, &res, file) < 0)
+ err(EXIT_FAILURE, "read string failed");
+ printf("read: %s: %s\n", file, res);
+
+ if (ul_path_readf_string(pc, &res, "%s", file) < 0)
+ err(EXIT_FAILURE, "readf string failed");
+ printf("readf: %s: %s\n", file, res);
+
+ } else if (strcmp(command, "read-link") == 0) {
+ char res[PATH_MAX];
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "<file> not defined");
+ file = argv[optind++];
+
+ if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
+ err(EXIT_FAILURE, "read symlink failed");
+ printf("read: %s: %s\n", file, res);
+
+ if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
+ err(EXIT_FAILURE, "readf symlink failed");
+ printf("readf: %s: %s\n", file, res);
+
+ } else if (strcmp(command, "write-string") == 0) {
+ char *str;
+
+ if (optind + 1 == argc)
+ errx(EXIT_FAILURE, "<file> <string> not defined");
+ file = argv[optind++];
+ str = argv[optind++];
+
+ if (ul_path_write_string(pc, str, file) != 0)
+ err(EXIT_FAILURE, "write string failed");
+ if (ul_path_writef_string(pc, str, "%s", file) != 0)
+ err(EXIT_FAILURE, "writef string failed");
+
+ } else if (strcmp(command, "write-u64") == 0) {
+ uint64_t num;
+
+ if (optind + 1 == argc)
+ errx(EXIT_FAILURE, "<file> <num> not defined");
+ file = argv[optind++];
+ num = strtoumax(argv[optind++], NULL, 0);
+
+ if (ul_path_write_u64(pc, num, file) != 0)
+ err(EXIT_FAILURE, "write u64 failed");
+ if (ul_path_writef_u64(pc, num, "%s", file) != 0)
+ err(EXIT_FAILURE, "writef u64 failed");
+ }
+
+ ul_unref_path(pc);
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_PATH */
+
diff --git a/utils/lib/plymouth-ctrl.c b/utils/lib/plymouth-ctrl.c
new file mode 100644
index 0000000..2d3deda
--- /dev/null
+++ b/utils/lib/plymouth-ctrl.c
@@ -0,0 +1,144 @@
+/*
+ * plymouth-ctrl.c Simply communications with plymouthd
+ * to avoid forked sub processes and/or
+ * missed plymouth send commands tool
+ * due a plymouthd replacement.
+ *
+ * Copyright (c) 2016 SUSE Linux GmbH, All rights reserved.
+ * Copyright (c) 2016 Werner Fink <werner@suse.de>
+ *
+ * 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, 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 (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Author: Werner Fink <werner@suse.de>
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "all-io.h"
+#include "c.h"
+#include "nls.h"
+#include "plymouth-ctrl.h"
+
+static int can_read(int fd, const long timeout)
+{
+ struct pollfd fds = {
+ .fd = fd,
+ .events = POLLIN|POLLPRI,
+ .revents = 0,
+ };
+ int ret;
+
+ do {
+ ret = poll(&fds, 1, timeout);
+ } while ((ret < 0) && (errno == EINTR));
+
+ return (ret == 1) && (fds.revents & (POLLIN|POLLPRI));
+}
+
+static int open_un_socket_and_connect(void)
+{
+ /* The abstract UNIX socket of plymouth */
+ struct sockaddr_un su = {
+ .sun_family = AF_UNIX,
+ .sun_path = PLYMOUTH_SOCKET_PATH,
+ };
+ const int one = 1;
+ int fd, ret;
+
+ fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
+ warnx(_("cannot open UNIX socket"));
+ goto err;
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (ret < 0) {
+ warnx(_("cannot set option for UNIX socket"));
+ close(fd);
+ fd = -1;
+ goto err;
+ }
+
+ /* Note, the abstract PLYMOUTH_SOCKET_PATH has a leading NULL byte */
+ ret = connect(fd, (struct sockaddr *) &su,
+ offsetof(struct sockaddr_un, sun_path) + 1 + strlen(su.sun_path+1));
+ if (ret < 0) {
+ if (errno != ECONNREFUSED)
+ warnx(_("cannot connect on UNIX socket"));
+ close(fd);
+ fd = -1;
+ goto err;
+ }
+err:
+ return fd;
+}
+
+int plymouth_command(int cmd, ...)
+{
+ uint8_t answer[2], command[2];
+ struct sigaction sp, op;
+ int fdsock = -1, ret = 0;
+
+ sigemptyset (&sp.sa_mask);
+ sp.sa_handler = SIG_IGN;
+ sp.sa_flags = SA_RESTART;
+ sigaction(SIGPIPE, &sp, &op);
+
+ /* The plymouthd does read at least two bytes. */
+ command[1] = '\0';
+ switch (cmd) {
+ case MAGIC_PING:
+ fdsock = open_un_socket_and_connect();
+ if (fdsock >= 0) {
+ command[0] = cmd;
+ write_all(fdsock, command, sizeof(command));
+ }
+ break;
+ case MAGIC_QUIT:
+ fdsock = open_un_socket_and_connect();
+ if (fdsock >= 0) {
+ command[0] = cmd;
+ write_all(fdsock, command, sizeof(command));
+ }
+ break;
+ default:
+ warnx(_("the plymouth request %c is not implemented"), cmd);
+ case '?':
+ goto err;
+ }
+
+ answer[0] = '\0';
+ if (fdsock >= 0) {
+ if (can_read(fdsock, 1000))
+ read_all(fdsock, (char *) &answer[0], sizeof(answer));
+ close(fdsock);
+ }
+ sigaction(SIGPIPE, &op, NULL);
+ ret = (answer[0] == ANSWER_ACK) ? 1 : 0;
+err:
+ return ret;
+}
+
diff --git a/utils/lib/procutils.c b/utils/lib/procutils.c
new file mode 100644
index 0000000..8fb5d5c
--- /dev/null
+++ b/utils/lib/procutils.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ *
+ * procutils.c: General purpose procfs parsing utilities
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, 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 Library Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "procutils.h"
+#include "fileutils.h"
+#include "all-io.h"
+#include "c.h"
+
+/*
+ * @pid: process ID for which we want to obtain the threads group
+ *
+ * Returns: newly allocated tasks structure
+ */
+struct proc_tasks *proc_open_tasks(pid_t pid)
+{
+ struct proc_tasks *tasks;
+ char path[PATH_MAX];
+
+ sprintf(path, "/proc/%d/task/", pid);
+
+ tasks = malloc(sizeof(struct proc_tasks));
+ if (tasks) {
+ tasks->dir = opendir(path);
+ if (tasks->dir)
+ return tasks;
+ }
+
+ free(tasks);
+ return NULL;
+}
+
+/*
+ * @tasks: allocated tasks structure
+ *
+ * Returns: nothing
+ */
+void proc_close_tasks(struct proc_tasks *tasks)
+{
+ if (tasks && tasks->dir)
+ closedir(tasks->dir);
+ free(tasks);
+}
+
+/*
+ * @tasks: allocated task structure
+ * @tid: [output] one of the thread IDs belonging to the thread group
+ * If when an error occurs, it is set to 0.
+ *
+ * Returns: 0 on success, 1 on end, -1 on failure or no more threads
+ */
+int proc_next_tid(struct proc_tasks *tasks, pid_t *tid)
+{
+ struct dirent *d;
+ char *end;
+
+ if (!tasks || !tid)
+ return -EINVAL;
+
+ *tid = 0;
+ errno = 0;
+
+ do {
+ d = readdir(tasks->dir);
+ if (!d)
+ return errno ? -1 : 1; /* error or end-of-dir */
+
+ if (!isdigit((unsigned char) *d->d_name))
+ continue;
+ errno = 0;
+ *tid = (pid_t) strtol(d->d_name, &end, 10);
+ if (errno || d->d_name == end || (end && *end))
+ return -1;
+
+ } while (!*tid);
+
+ return 0;
+}
+
+/* returns process command path, use free() for result */
+static char *proc_file_strdup(pid_t pid, const char *name)
+{
+ char buf[BUFSIZ], *res = NULL;
+ ssize_t sz = 0;
+ size_t i;
+ int fd;
+
+ snprintf(buf, sizeof(buf), "/proc/%d/%s", (int) pid, name);
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ goto done;
+
+ sz = read_all(fd, buf, sizeof(buf));
+ if (sz <= 0)
+ goto done;
+
+ for (i = 0; i < (size_t) sz; i++) {
+
+ if (buf[i] == '\0')
+ buf[i] = ' ';
+ }
+ buf[sz - 1] = '\0';
+ res = strdup(buf);
+done:
+ if (fd >= 0)
+ close(fd);
+ return res;
+}
+
+/* returns process command path, use free() for result */
+char *proc_get_command(pid_t pid)
+{
+ return proc_file_strdup(pid, "cmdline");
+}
+
+/* returns process command name, use free() for result */
+char *proc_get_command_name(pid_t pid)
+{
+ return proc_file_strdup(pid, "comm");
+}
+
+struct proc_processes *proc_open_processes(void)
+{
+ struct proc_processes *ps;
+
+ ps = calloc(1, sizeof(struct proc_processes));
+ if (ps) {
+ ps->dir = opendir("/proc");
+ if (ps->dir)
+ return ps;
+ }
+
+ free(ps);
+ return NULL;
+}
+
+void proc_close_processes(struct proc_processes *ps)
+{
+ if (ps && ps->dir)
+ closedir(ps->dir);
+ free(ps);
+}
+
+void proc_processes_filter_by_name(struct proc_processes *ps, const char *name)
+{
+ ps->fltr_name = name;
+ ps->has_fltr_name = name ? 1 : 0;
+}
+
+void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid)
+{
+ ps->fltr_uid = uid;
+ ps->has_fltr_uid = 1;
+}
+
+int proc_next_pid(struct proc_processes *ps, pid_t *pid)
+{
+ struct dirent *d;
+
+ if (!ps || !pid)
+ return -EINVAL;
+
+ *pid = 0;
+ errno = 0;
+
+ do {
+ char buf[BUFSIZ], *p;
+
+ errno = 0;
+ d = readdir(ps->dir);
+ if (!d)
+ return errno ? -1 : 1; /* error or end-of-dir */
+
+
+ if (!isdigit((unsigned char) *d->d_name))
+ continue;
+
+ /* filter out by UID */
+ if (ps->has_fltr_uid) {
+ struct stat st;
+
+ if (fstatat(dirfd(ps->dir), d->d_name, &st, 0))
+ continue;
+ if (ps->fltr_uid != st.st_uid)
+ continue;
+ }
+
+ /* filter out by NAME */
+ if (ps->has_fltr_name) {
+ char procname[256];
+ FILE *f;
+
+ snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
+ f = fopen_at(dirfd(ps->dir), buf, O_CLOEXEC|O_RDONLY, "r");
+ if (!f)
+ continue;
+
+ p = fgets(buf, sizeof(buf), f);
+ fclose(f);
+ if (!p)
+ continue;
+
+ if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
+ continue;
+
+ /* ok, we got the process name. */
+ if (strcmp(procname, ps->fltr_name) != 0)
+ continue;
+ }
+
+ p = NULL;
+ errno = 0;
+ *pid = (pid_t) strtol(d->d_name, &p, 10);
+ if (errno || d->d_name == p || (p && *p))
+ return errno ? -errno : -1;
+
+ return 0;
+ } while (1);
+
+ return 0;
+}
+
+#ifdef TEST_PROGRAM_PROCUTILS
+
+static int test_tasks(int argc, char *argv[])
+{
+ pid_t tid, pid;
+ struct proc_tasks *ts;
+
+ if (argc != 2)
+ return EXIT_FAILURE;
+
+ pid = strtol(argv[1], (char **) NULL, 10);
+ printf("PID=%d, TIDs:", pid);
+
+ ts = proc_open_tasks(pid);
+ if (!ts)
+ err(EXIT_FAILURE, "open list of tasks failed");
+
+ while (proc_next_tid(ts, &tid) == 0)
+ printf(" %d", tid);
+
+ printf("\n");
+ proc_close_tasks(ts);
+ return EXIT_SUCCESS;
+}
+
+static int test_processes(int argc, char *argv[])
+{
+ pid_t pid;
+ struct proc_processes *ps;
+
+ ps = proc_open_processes();
+ if (!ps)
+ err(EXIT_FAILURE, "open list of processes failed");
+
+ if (argc >= 3 && strcmp(argv[1], "--name") == 0)
+ proc_processes_filter_by_name(ps, argv[2]);
+
+ if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
+ proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
+
+ while (proc_next_pid(ps, &pid) == 0)
+ printf(" %d", pid);
+
+ printf("\n");
+ proc_close_processes(ps);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "usage: %1$s --tasks <pid>\n"
+ " %1$s --processes [---name <name>] [--uid <uid>]\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "--tasks") == 0)
+ return test_tasks(argc - 1, argv + 1);
+ if (strcmp(argv[1], "--processes") == 0)
+ return test_processes(argc - 1, argv + 1);
+
+ return EXIT_FAILURE;
+}
+#endif /* TEST_PROGRAM_PROCUTILS */
diff --git a/utils/lib/pty-session.c b/utils/lib/pty-session.c
new file mode 100644
index 0000000..06b2a49
--- /dev/null
+++ b/utils/lib/pty-session.c
@@ -0,0 +1,725 @@
+/*
+ * This is pseudo-terminal container for child process where parent creates a
+ * proxy between the current std{in,out,etrr} and the child's pty. Advantages:
+ *
+ * - child has no access to parent's terminal (e.g. su --pty)
+ * - parent can log all traffic between user and child's terminal (e.g. script(1))
+ * - it's possible to start commands on terminal although parent has no terminal
+ *
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com> in Jul 2019
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <pty.h>
+#include <poll.h>
+#include <sys/signalfd.h>
+#include <paths.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "all-io.h"
+#include "ttyutils.h"
+#include "pty-session.h"
+#include "monotonic.h"
+#include "debug.h"
+
+static UL_DEBUG_DEFINE_MASK(ulpty);
+UL_DEBUG_DEFINE_MASKNAMES(ulpty) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define ULPTY_DEBUG_INIT (1 << 1)
+#define ULPTY_DEBUG_SETUP (1 << 2)
+#define ULPTY_DEBUG_SIG (1 << 3)
+#define ULPTY_DEBUG_IO (1 << 4)
+#define ULPTY_DEBUG_DONE (1 << 5)
+#define ULPTY_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(ulpty, ULPTY_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(ulpty, ULPTY_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpty)
+#include "debugobj.h"
+
+void ul_pty_init_debug(int mask)
+{
+ if (ulpty_debug_mask)
+ return;
+ __UL_INIT_DEBUG_FROM_ENV(ulpty, ULPTY_DEBUG_, mask, ULPTY_DEBUG);
+}
+
+struct ul_pty *ul_new_pty(int is_stdin_tty)
+{
+ struct ul_pty *pty = calloc(1, sizeof(*pty));
+
+ if (!pty)
+ return NULL;
+
+ DBG(SETUP, ul_debugobj(pty, "alloc handler"));
+ pty->isterm = is_stdin_tty;
+ pty->master = -1;
+ pty->slave = -1;
+ pty->sigfd = -1;
+ pty->child = (pid_t) -1;
+
+ return pty;
+}
+
+void ul_free_pty(struct ul_pty *pty)
+{
+ free(pty);
+}
+
+void ul_pty_slave_echo(struct ul_pty *pty, int enable)
+{
+ assert(pty);
+ pty->slave_echo = enable ? 1 : 0;
+}
+
+int ul_pty_get_delivered_signal(struct ul_pty *pty)
+{
+ assert(pty);
+ return pty->delivered_signal;
+}
+
+struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty)
+{
+ assert(pty);
+ return &pty->callbacks;
+}
+
+void ul_pty_set_callback_data(struct ul_pty *pty, void *data)
+{
+ assert(pty);
+ pty->callback_data = data;
+}
+
+void ul_pty_set_child(struct ul_pty *pty, pid_t child)
+{
+ assert(pty);
+ pty->child = child;
+}
+
+int ul_pty_get_childfd(struct ul_pty *pty)
+{
+ assert(pty);
+ return pty->master;
+}
+
+pid_t ul_pty_get_child(struct ul_pty *pty)
+{
+ assert(pty);
+ return pty->child;
+}
+
+/* it's active when signals are redirected to sigfd */
+int ul_pty_is_running(struct ul_pty *pty)
+{
+ assert(pty);
+ return pty->sigfd >= 0;
+}
+
+void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv)
+{
+ assert(pty);
+ if (!tv) {
+ DBG(IO, ul_debugobj(pty, "mainloop time: clear"));
+ timerclear(&pty->next_callback_time);
+ } else {
+ pty->next_callback_time.tv_sec = tv->tv_sec;
+ pty->next_callback_time.tv_usec = tv->tv_usec;
+ DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec));
+ }
+}
+
+static void pty_signals_cleanup(struct ul_pty *pty)
+{
+ if (pty->sigfd != -1)
+ close(pty->sigfd);
+ pty->sigfd = -1;
+
+ /* restore original setting */
+ sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
+}
+
+/* call me before fork() */
+int ul_pty_setup(struct ul_pty *pty)
+{
+ struct termios attrs;
+ sigset_t ourset;
+ int rc = 0;
+
+ assert(pty->sigfd == -1);
+
+ /* save the current signals setting */
+ sigprocmask(0, NULL, &pty->orgsig);
+
+ if (pty->isterm) {
+ DBG(SETUP, ul_debugobj(pty, "create for terminal"));
+
+ /* original setting of the current terminal */
+ if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0) {
+ rc = -errno;
+ goto done;
+ }
+
+ attrs = pty->stdin_attrs;
+ if (pty->slave_echo)
+ attrs.c_lflag |= ECHO;
+ else
+ attrs.c_lflag &= ~ECHO;
+
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
+ /* create master+slave */
+ rc = openpty(&pty->master, &pty->slave, NULL, &attrs, &pty->win);
+ if (rc)
+ goto done;
+
+ /* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */
+ cfmakeraw(&attrs);
+ tcsetattr(STDIN_FILENO, TCSANOW, &attrs);
+ } else {
+ DBG(SETUP, ul_debugobj(pty, "create for non-terminal"));
+
+ rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
+ if (rc)
+ goto done;
+
+ tcgetattr(pty->slave, &attrs);
+
+ if (pty->slave_echo)
+ attrs.c_lflag |= ECHO;
+ else
+ attrs.c_lflag &= ~ECHO;
+
+ tcsetattr(pty->slave, TCSANOW, &attrs);
+ }
+
+ sigfillset(&ourset);
+ if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
+ rc = -errno;
+ goto done;
+ }
+
+ sigemptyset(&ourset);
+ sigaddset(&ourset, SIGCHLD);
+ sigaddset(&ourset, SIGWINCH);
+ sigaddset(&ourset, SIGALRM);
+ sigaddset(&ourset, SIGTERM);
+ sigaddset(&ourset, SIGINT);
+ sigaddset(&ourset, SIGQUIT);
+
+ if (pty->callbacks.flush_logs)
+ sigaddset(&ourset, SIGUSR1);
+
+ if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0)
+ rc = -errno;
+done:
+ if (rc)
+ ul_pty_cleanup(pty);
+
+ DBG(SETUP, ul_debugobj(pty, "pty setup done [master=%d, slave=%d, rc=%d]",
+ pty->master, pty->slave, rc));
+ return rc;
+}
+
+/* cleanup in parent process */
+void ul_pty_cleanup(struct ul_pty *pty)
+{
+ struct termios rtt;
+
+ pty_signals_cleanup(pty);
+
+ if (pty->master == -1 || !pty->isterm)
+ return;
+
+ DBG(DONE, ul_debugobj(pty, "cleanup"));
+ rtt = pty->stdin_attrs;
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &rtt);
+}
+
+/* call me in child process */
+void ul_pty_init_slave(struct ul_pty *pty)
+{
+ DBG(SETUP, ul_debugobj(pty, "initialize slave"));
+
+ setsid();
+
+ ioctl(pty->slave, TIOCSCTTY, 1);
+ close(pty->master);
+
+ dup2(pty->slave, STDIN_FILENO);
+ dup2(pty->slave, STDOUT_FILENO);
+ dup2(pty->slave, STDERR_FILENO);
+
+ close(pty->slave);
+
+ if (pty->sigfd >= 0)
+ close(pty->sigfd);
+
+ pty->slave = -1;
+ pty->master = -1;
+ pty->sigfd = -1;
+
+ sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
+
+ DBG(SETUP, ul_debugobj(pty, "... initialize slave done"));
+}
+
+static int write_output(char *obuf, ssize_t bytes)
+{
+ DBG(IO, ul_debug(" writing output"));
+
+ if (write_all(STDOUT_FILENO, obuf, bytes)) {
+ DBG(IO, ul_debug(" writing output *failed*"));
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz)
+{
+ return write_all(pty->master, buf, bufsz);
+}
+
+/*
+ * The pty is usually faster than shell, so it's a good idea to wait until
+ * the previous message has been already read by shell from slave before we
+ * write to master. This is necessary especially for EOF situation when we can
+ * send EOF to master before shell is fully initialized, to workaround this
+ * problem we wait until slave is empty. For example:
+ *
+ * echo "date" | su --pty
+ *
+ * Unfortunately, the child (usually shell) can ignore stdin at all, so we
+ * don't wait forever to avoid dead locks...
+ *
+ * Note that su --pty is primarily designed for interactive sessions as it
+ * maintains master+slave tty stuff within the session. Use pipe to write to
+ * pty and assume non-interactive (tee-like) behavior is NOT well supported.
+ */
+void ul_pty_write_eof_to_child(struct ul_pty *pty)
+{
+ unsigned int tries = 0;
+ struct pollfd fds[] = {
+ { .fd = pty->slave, .events = POLLIN }
+ };
+ char c = DEF_EOF;
+
+ DBG(IO, ul_debugobj(pty, " waiting for empty slave"));
+ while (poll(fds, 1, 10) == 1 && tries < 8) {
+ DBG(IO, ul_debugobj(pty, " slave is not empty"));
+ xusleep(250000);
+ tries++;
+ }
+ if (tries < 8)
+ DBG(IO, ul_debugobj(pty, " slave is empty now"));
+
+ DBG(IO, ul_debugobj(pty, " sending EOF to master"));
+ write_to_child(pty, &c, sizeof(char));
+}
+
+static int mainloop_callback(struct ul_pty *pty)
+{
+ int rc;
+
+ if (!pty->callbacks.mainloop)
+ return 0;
+
+ DBG(IO, ul_debugobj(pty, "calling mainloop callback"));
+ rc = pty->callbacks.mainloop(pty->callback_data);
+
+ DBG(IO, ul_debugobj(pty, " callback done [rc=%d]", rc));
+ return rc;
+}
+
+static int handle_io(struct ul_pty *pty, int fd, int *eof)
+{
+ char buf[BUFSIZ];
+ ssize_t bytes;
+ int rc = 0;
+
+ DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd));
+ *eof = 0;
+
+ /* read from active FD */
+ bytes = read(fd, buf, sizeof(buf));
+ if (bytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ return -errno;
+ }
+
+ if (bytes == 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ /* from stdin (user) to command */
+ if (fd == STDIN_FILENO) {
+ DBG(IO, ul_debugobj(pty, " stdin --> master %zd bytes", bytes));
+
+ if (write_to_child(pty, buf, bytes))
+ return -errno;
+
+ /* without sync write_output() will write both input &
+ * shell output that looks like double echoing */
+ fdatasync(pty->master);
+
+ /* from command (master) to stdout */
+ } else if (fd == pty->master) {
+ DBG(IO, ul_debugobj(pty, " master --> stdout %zd bytes", bytes));
+ write_output(buf, bytes);
+ }
+
+ if (pty->callbacks.log_stream_activity)
+ rc = pty->callbacks.log_stream_activity(
+ pty->callback_data, fd, buf, bytes);
+
+ return rc;
+}
+
+void ul_pty_wait_for_child(struct ul_pty *pty)
+{
+ int status;
+ pid_t pid;
+ int options = 0;
+
+ if (pty->child == (pid_t) -1)
+ return;
+
+ DBG(SIG, ul_debug("waiting for child [child=%d]", (int) pty->child));
+
+ if (ul_pty_is_running(pty)) {
+ /* wait for specific child */
+ options = WNOHANG;
+ for (;;) {
+ pid = waitpid(pty->child, &status, options);
+ DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid));
+ if (pid != (pid_t) - 1) {
+ if (pty->callbacks.child_die)
+ pty->callbacks.child_die(
+ pty->callback_data,
+ pty->child, status);
+ ul_pty_set_child(pty, (pid_t) -1);
+ } else
+ break;
+ }
+ } else {
+ /* final wait */
+ while ((pid = wait3(&status, options, NULL)) > 0) {
+ DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid));
+ if (pid == pty->child) {
+ if (pty->callbacks.child_die)
+ pty->callbacks.child_die(
+ pty->callback_data,
+ pty->child, status);
+ ul_pty_set_child(pty, (pid_t) -1);
+ }
+ }
+ }
+}
+
+static int handle_signal(struct ul_pty *pty, int fd)
+{
+ struct signalfd_siginfo info;
+ ssize_t bytes;
+ int rc = 0;
+
+ DBG(SIG, ul_debugobj(pty, " handle signal on fd=%d", fd));
+
+ bytes = read(fd, &info, sizeof(info));
+ if (bytes != sizeof(info)) {
+ if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+ return -errno;
+ }
+
+ switch (info.ssi_signo) {
+ case SIGCHLD:
+ DBG(SIG, ul_debugobj(pty, " get signal SIGCHLD"));
+
+ if (info.ssi_code == CLD_EXITED
+ || info.ssi_code == CLD_KILLED
+ || info.ssi_code == CLD_DUMPED) {
+
+ if (pty->callbacks.child_wait)
+ pty->callbacks.child_wait(pty->callback_data,
+ pty->child);
+ else
+ ul_pty_wait_for_child(pty);
+
+ } else if (info.ssi_status == SIGSTOP && pty->child > 0)
+ pty->callbacks.child_sigstop(pty->callback_data,
+ pty->child);
+
+ if (pty->child <= 0) {
+ DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout"));
+ pty->poll_timeout = 10;
+ timerclear(&pty->next_callback_time);
+ }
+ return 0;
+ case SIGWINCH:
+ DBG(SIG, ul_debugobj(pty, " get signal SIGWINCH"));
+ if (pty->isterm) {
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
+ ioctl(pty->slave, TIOCSWINSZ, (char *)&pty->win);
+
+ if (pty->callbacks.log_signal)
+ rc = pty->callbacks.log_signal(pty->callback_data,
+ &info, (void *) &pty->win);
+ }
+ break;
+ case SIGTERM:
+ /* fallthrough */
+ case SIGINT:
+ /* fallthrough */
+ case SIGQUIT:
+ DBG(SIG, ul_debugobj(pty, " get signal SIG{TERM,INT,QUIT}"));
+ pty->delivered_signal = info.ssi_signo;
+ /* Child termination is going to generate SIGCHLD (see above) */
+ if (pty->child > 0)
+ kill(pty->child, SIGTERM);
+
+ if (pty->callbacks.log_signal)
+ rc = pty->callbacks.log_signal(pty->callback_data,
+ &info, (void *) &pty->win);
+ break;
+ case SIGUSR1:
+ DBG(SIG, ul_debugobj(pty, " get signal SIGUSR1"));
+ if (pty->callbacks.flush_logs)
+ rc = pty->callbacks.flush_logs(pty->callback_data);
+ break;
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+/* loop in parent */
+int ul_pty_proxy_master(struct ul_pty *pty)
+{
+ int rc = 0, ret, eof = 0;
+ enum {
+ POLLFD_SIGNAL = 0,
+ POLLFD_MASTER,
+ POLLFD_STDIN
+
+ };
+ struct pollfd pfd[] = {
+ [POLLFD_SIGNAL] = { .fd = -1, .events = POLLIN | POLLERR | POLLHUP },
+ [POLLFD_MASTER] = { .fd = pty->master, .events = POLLIN | POLLERR | POLLHUP },
+ [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
+ };
+
+ /* We use signalfd, and standard signals by handlers are completely blocked */
+ assert(pty->sigfd >= 0);
+
+ pfd[POLLFD_SIGNAL].fd = pty->sigfd;
+ pty->poll_timeout = -1;
+
+ while (!pty->delivered_signal) {
+ size_t i;
+ int errsv, timeout;
+
+ DBG(IO, ul_debugobj(pty, "--poll() loop--"));
+
+ /* note, callback usually updates @next_callback_time */
+ if (timerisset(&pty->next_callback_time)) {
+ struct timeval now;
+
+ DBG(IO, ul_debugobj(pty, " callback requested"));
+ gettime_monotonic(&now);
+ if (timercmp(&now, &pty->next_callback_time, >)) {
+ rc = mainloop_callback(pty);
+ if (rc)
+ break;
+ }
+ }
+
+ /* set timeout */
+ if (timerisset(&pty->next_callback_time)) {
+ struct timeval now, rest;
+
+ gettime_monotonic(&now);
+ timersub(&pty->next_callback_time, &now, &rest);
+ timeout = (rest.tv_sec * 1000) + (rest.tv_usec / 1000);
+ } else
+ timeout = pty->poll_timeout;
+
+ /* wait for input, signal or timeout */
+ DBG(IO, ul_debugobj(pty, "calling poll() [timeout=%dms]", timeout));
+ ret = poll(pfd, ARRAY_SIZE(pfd), timeout);
+
+ errsv = errno;
+ DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret));
+
+ /* error */
+ if (ret < 0) {
+ if (errsv == EAGAIN)
+ continue;
+ rc = -errno;
+ break;
+ }
+
+ /* timeout */
+ if (ret == 0) {
+ if (timerisset(&pty->next_callback_time)) {
+ rc = mainloop_callback(pty);
+ if (rc == 0)
+ continue;
+ } else
+ rc = 0;
+
+ DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc));
+ break;
+ }
+ /* event */
+ for (i = 0; i < ARRAY_SIZE(pfd); i++) {
+ rc = 0;
+
+ if (pfd[i].revents == 0)
+ continue;
+
+ DBG(IO, ul_debugobj(pty, " active pfd[%s].fd=%d %s %s %s %s",
+ i == POLLFD_STDIN ? "stdin" :
+ i == POLLFD_MASTER ? "master" :
+ i == POLLFD_SIGNAL ? "signal" : "???",
+ pfd[i].fd,
+ pfd[i].revents & POLLIN ? "POLLIN" : "",
+ pfd[i].revents & POLLHUP ? "POLLHUP" : "",
+ pfd[i].revents & POLLERR ? "POLLERR" : "",
+ pfd[i].revents & POLLNVAL ? "POLLNVAL" : ""));
+
+ switch (i) {
+ case POLLFD_STDIN:
+ case POLLFD_MASTER:
+ /* data */
+ if (pfd[i].revents & POLLIN)
+ rc = handle_io(pty, pfd[i].fd, &eof);
+ /* EOF maybe detected in two ways; they are as follows:
+ * A) poll() return POLLHUP event after close()
+ * B) read() returns 0 (no data)
+ *
+ * POLLNVAL means that fd is closed.
+ */
+ if ((pfd[i].revents & POLLHUP) || (pfd[i].revents & POLLNVAL) || eof) {
+ DBG(IO, ul_debugobj(pty, " ignore FD"));
+ pfd[i].fd = -1;
+ if (i == POLLFD_STDIN) {
+ ul_pty_write_eof_to_child(pty);
+ DBG(IO, ul_debugobj(pty, " ignore STDIN"));
+ }
+ }
+ continue;
+ case POLLFD_SIGNAL:
+ rc = handle_signal(pty, pfd[i].fd);
+ break;
+ }
+ if (rc)
+ break;
+ }
+ }
+
+ pty_signals_cleanup(pty);
+
+ DBG(IO, ul_debug("poll() done [signal=%d, rc=%d]", pty->delivered_signal, rc));
+ return rc;
+}
+
+#ifdef TEST_PROGRAM_PTY
+/*
+ * $ make test_pty
+ * $ ./test_pty
+ *
+ * ... and see for example tty(1) or "ps afu"
+ */
+static void child_sigstop(void *data __attribute__((__unused__)), pid_t child)
+{
+ kill(getpid(), SIGSTOP);
+ kill(child, SIGCONT);
+}
+
+int main(int argc, char *argv[])
+{
+ struct ul_pty_callbacks *cb;
+ const char *shell, *command = NULL, *shname = NULL;
+ int caught_signal = 0;
+ pid_t child;
+ struct ul_pty *pty;
+
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = _PATH_BSHELL;
+ if (argc == 2)
+ command = argv[1];
+
+ ul_pty_init_debug(0);
+
+ pty = ul_new_pty(isatty(STDIN_FILENO));
+ if (!pty)
+ err(EXIT_FAILURE, "failed to allocate PTY handler");
+
+ cb = ul_pty_get_callbacks(pty);
+ cb->child_sigstop = child_sigstop;
+
+ if (ul_pty_setup(pty))
+ err(EXIT_FAILURE, "failed to create pseudo-terminal");
+
+ fflush(stdout); /* ??? */
+
+ switch ((int) (child = fork())) {
+ case -1: /* error */
+ ul_pty_cleanup(pty);
+ err(EXIT_FAILURE, "cannot create child process");
+ break;
+
+ case 0: /* child */
+ ul_pty_init_slave(pty);
+
+ signal(SIGTERM, SIG_DFL); /* because /etc/csh.login */
+
+ shname = strrchr(shell, '/');
+ shname = shname ? shname + 1 : shell;
+
+ if (command)
+ execl(shell, shname, "-c", command, NULL);
+ else
+ execl(shell, shname, "-i", NULL);
+ err(EXIT_FAILURE, "failed to execute %s", shell);
+ break;
+
+ default:
+ break;
+ }
+
+ /* parent */
+ ul_pty_set_child(pty, child);
+
+ /* this is the main loop */
+ ul_pty_proxy_master(pty);
+
+ /* all done; cleanup and kill */
+ caught_signal = ul_pty_get_delivered_signal(pty);
+
+ if (!caught_signal && ul_pty_get_child(pty) != (pid_t)-1)
+ ul_pty_wait_for_child(pty); /* final wait */
+
+ if (caught_signal && ul_pty_get_child(pty) != (pid_t)-1) {
+ fprintf(stderr, "\nSession terminated, killing shell...");
+ kill(child, SIGTERM);
+ sleep(2);
+ kill(child, SIGKILL);
+ fprintf(stderr, " ...killed.\n");
+ }
+
+ ul_pty_cleanup(pty);
+ ul_free_pty(pty);
+ return EXIT_SUCCESS;
+}
+
+#endif /* TEST_PROGRAM */
+
diff --git a/utils/lib/pwdutils.c b/utils/lib/pwdutils.c
new file mode 100644
index 0000000..d5f4d2e
--- /dev/null
+++ b/utils/lib/pwdutils.c
@@ -0,0 +1,156 @@
+#include <stdlib.h>
+
+#include "c.h"
+#include "pwdutils.h"
+#include "xalloc.h"
+
+/* Returns allocated passwd and allocated pwdbuf to store passwd strings
+ * fields. In case of error returns NULL and set errno, for unknown user set
+ * errno to EINVAL
+ */
+struct passwd *xgetpwnam(const char *username, char **pwdbuf)
+{
+ struct passwd *pwd = NULL, *res = NULL;
+ int rc;
+
+ if (!pwdbuf || !username)
+ return NULL;
+
+ *pwdbuf = xmalloc(UL_GETPW_BUFSIZ);
+ pwd = xcalloc(1, sizeof(struct passwd));
+
+ errno = 0;
+ rc = getpwnam_r(username, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res);
+ if (rc != 0) {
+ errno = rc;
+ goto failed;
+ }
+ if (!res) {
+ errno = EINVAL;
+ goto failed;
+ }
+ return pwd;
+failed:
+ free(pwd);
+ free(*pwdbuf);
+ return NULL;
+}
+
+/* Returns allocated group and allocated grpbuf to store group strings
+ * fields. In case of error returns NULL and set errno, for unknown group set
+ * errno to EINVAL
+ */
+struct group *xgetgrnam(const char *groupname, char **grpbuf)
+{
+ struct group *grp = NULL, *res = NULL;
+ int rc;
+
+ if (!grpbuf || !groupname)
+ return NULL;
+
+ *grpbuf = xmalloc(UL_GETPW_BUFSIZ);
+ grp = xcalloc(1, sizeof(struct group));
+
+ errno = 0;
+ rc = getgrnam_r(groupname, grp, *grpbuf, UL_GETPW_BUFSIZ, &res);
+ if (rc != 0) {
+ errno = rc;
+ goto failed;
+ }
+ if (!res) {
+ errno = EINVAL;
+ goto failed;
+ }
+ return grp;
+failed:
+ free(grp);
+ free(*grpbuf);
+ return NULL;
+}
+
+struct passwd *xgetpwuid(uid_t uid, char **pwdbuf)
+{
+ struct passwd *pwd = NULL, *res = NULL;
+ int rc;
+
+ if (!pwdbuf)
+ return NULL;
+
+ *pwdbuf = xmalloc(UL_GETPW_BUFSIZ);
+ pwd = xcalloc(1, sizeof(struct passwd));
+
+ errno = 0;
+ rc = getpwuid_r(uid, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res);
+ if (rc != 0) {
+ errno = rc;
+ goto failed;
+ }
+ if (!res) {
+ errno = EINVAL;
+ goto failed;
+ }
+ return pwd;
+failed:
+ free(pwd);
+ free(*pwdbuf);
+ return NULL;
+}
+
+char *xgetlogin(void)
+{
+ struct passwd *pw = NULL;
+ uid_t ruid;
+ char *user;
+
+ user = getlogin();
+ if (user)
+ return xstrdup(user);
+
+ /* GNU Hurd implementation has an extension where a process can exist in a
+ * non-conforming environment, and thus be outside the realms of POSIX
+ * process identifiers; on this platform, getuid() fails with a status of
+ * (uid_t)(-1) and sets errno if a program is run from a non-conforming
+ * environment.
+ *
+ * http://austingroupbugs.net/view.php?id=511
+ */
+ errno = 0;
+ ruid = getuid();
+
+ if (errno == 0)
+ pw = getpwuid(ruid);
+ if (pw && pw->pw_name && *pw->pw_name)
+ return xstrdup(pw->pw_name);
+
+ return NULL;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char *argv[])
+{
+ char *buf = NULL;
+ struct passwd *pwd = NULL;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <username>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ pwd = xgetpwnam(argv[1], &buf);
+ if (!pwd)
+ err(EXIT_FAILURE, "failed to get %s pwd entry", argv[1]);
+
+ printf("Username: %s\n", pwd->pw_name);
+ printf("UID: %d\n", pwd->pw_uid);
+ printf("HOME: %s\n", pwd->pw_dir);
+ printf("GECO: %s\n", pwd->pw_gecos);
+
+ free(pwd);
+ free(buf);
+
+ printf("Current: %s\n", (buf = xgetlogin()));
+ free(buf);
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/utils/lib/randutils.c b/utils/lib/randutils.c
new file mode 100644
index 0000000..bd2a8f6
--- /dev/null
+++ b/utils/lib/randutils.c
@@ -0,0 +1,238 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * General purpose random utilities. Based on libuuid code.
+ *
+ * This code is free software; you can redistribute it and/or modify it under
+ * the terms of the Modified BSD License. The complete text of the license is
+ * available in the Documentation/licenses/COPYING.BSD-3-Clause file.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/syscall.h>
+
+#include "c.h"
+#include "randutils.h"
+#include "nls.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#ifdef HAVE_GETRANDOM
+# include <sys/random.h>
+#elif defined (__linux__)
+# if !defined(SYS_getrandom) && defined(__NR_getrandom)
+ /* usable kernel-headers, but old glibc-headers */
+# define SYS_getrandom __NR_getrandom
+# endif
+#endif
+
+#if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
+/* libc without function, but we have syscal */
+#define GRND_NONBLOCK 0x01
+#define GRND_RANDOM 0x02
+static int getrandom(void *buf, size_t buflen, unsigned int flags)
+{
+ return (syscall(SYS_getrandom, buf, buflen, flags));
+}
+# define HAVE_GETRANDOM
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short ul_jrand_seed[3];
+#endif
+
+int rand_get_number(int low_n, int high_n)
+{
+ return rand() % (high_n - low_n + 1) + low_n;
+}
+
+static void crank_random(void)
+{
+ int i;
+ struct timeval tv;
+ unsigned int n_pid, n_uid;
+
+ gettimeofday(&tv, NULL);
+ n_pid = getpid();
+ n_uid = getuid();
+ srand((n_pid << 16) ^ n_uid ^ tv.tv_sec ^ tv.tv_usec);
+
+#ifdef DO_JRAND_MIX
+ ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+ ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+ ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, NULL);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+}
+
+int random_get_fd(void)
+{
+ int i, fd;
+
+ fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (fd >= 0) {
+ i = fcntl(fd, F_GETFD);
+ if (i >= 0)
+ fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+ }
+ crank_random();
+ return fd;
+}
+
+/*
+ * Generate a stream of random nbytes into buf.
+ * Use /dev/urandom if possible, and if not,
+ * use glibc pseudo-random functions.
+ */
+#define UL_RAND_READ_ATTEMPTS 8
+#define UL_RAND_READ_DELAY 125000 /* microseconds */
+
+void random_get_bytes(void *buf, size_t nbytes)
+{
+ unsigned char *cp = (unsigned char *)buf;
+ size_t i, n = nbytes;
+ int lose_counter = 0;
+
+#ifdef HAVE_GETRANDOM
+ while (n > 0) {
+ int x;
+
+ errno = 0;
+ x = getrandom(cp, n, GRND_NONBLOCK);
+ if (x > 0) { /* success */
+ n -= x;
+ cp += x;
+ lose_counter = 0;
+
+ } else if (errno == ENOSYS) { /* kernel without getrandom() */
+ break;
+
+ } else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
+ xusleep(UL_RAND_READ_DELAY); /* no entropy, wait and try again */
+ lose_counter++;
+ } else
+ break;
+ }
+
+ if (errno == ENOSYS)
+#endif
+ /*
+ * We've been built against headers that support getrandom, but the
+ * running kernel does not. Fallback to reading from /dev/{u,}random
+ * as before
+ */
+ {
+ int fd = random_get_fd();
+
+ lose_counter = 0;
+ if (fd >= 0) {
+ while (n > 0) {
+ ssize_t x = read(fd, cp, n);
+ if (x <= 0) {
+ if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
+ break;
+ xusleep(UL_RAND_READ_DELAY);
+ continue;
+ }
+ n -= x;
+ cp += x;
+ lose_counter = 0;
+ }
+
+ close(fd);
+ }
+ }
+ /*
+ * We do this all the time, but this is the only source of
+ * randomness if /dev/random/urandom is out to lunch.
+ */
+ crank_random();
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (rand() >> 7) & 0xFF;
+
+#ifdef DO_JRAND_MIX
+ {
+ unsigned short tmp_seed[3];
+
+ memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
+ ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+ memcpy(ul_jrand_seed, tmp_seed,
+ sizeof(ul_jrand_seed)-sizeof(unsigned short));
+ }
+#endif
+}
+
+
+/*
+ * Tell source of randomness.
+ */
+const char *random_tell_source(void)
+{
+#ifdef HAVE_GETRANDOM
+ return _("getrandom() function");
+#else
+ size_t i;
+ static const char *random_sources[] = {
+ "/dev/urandom",
+ "/dev/random"
+ };
+
+ for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
+ if (!access(random_sources[i], R_OK))
+ return random_sources[i];
+ }
+#endif
+ return _("libc pseudo-random functions");
+}
+
+#ifdef TEST_PROGRAM_RANDUTILS
+#include <inttypes.h>
+
+int main(int argc, char *argv[])
+{
+ size_t i, n;
+ int64_t *vp, v;
+ char *buf;
+ size_t bufsz;
+
+ n = argc == 1 ? 16 : atoi(argv[1]);
+
+ printf("Multiple random calls:\n");
+ for (i = 0; i < n; i++) {
+ random_get_bytes(&v, sizeof(v));
+ printf("#%02zu: %25"PRIu64"\n", i, v);
+ }
+
+
+ printf("One random call:\n");
+ bufsz = n * sizeof(*vp);
+ buf = malloc(bufsz);
+ if (!buf)
+ err(EXIT_FAILURE, "failed to allocate buffer");
+
+ random_get_bytes(buf, bufsz);
+ for (i = 0; i < n; i++) {
+ vp = (int64_t *) (buf + (i * sizeof(*vp)));
+ printf("#%02zu: %25"PRIu64"\n", i, *vp);
+ }
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_RANDUTILS */
diff --git a/utils/lib/setproctitle.c b/utils/lib/setproctitle.c
new file mode 100644
index 0000000..7168e46
--- /dev/null
+++ b/utils/lib/setproctitle.c
@@ -0,0 +1,75 @@
+/*
+ * set process title for ps (from sendmail)
+ *
+ * Clobbers argv of our main procedure so ps(1) will display the title.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "setproctitle.h"
+
+#ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE 2048
+#endif
+
+extern char **environ;
+
+static char **argv0;
+static size_t argv_lth;
+
+void initproctitle (int argc, char **argv)
+{
+ int i;
+ char **envp = environ;
+
+ /*
+ * Move the environment so we can reuse the memory.
+ * (Code borrowed from sendmail.)
+ * WARNING: ugly assumptions on memory layout here;
+ * if this ever causes problems, #undef DO_PS_FIDDLING
+ */
+ for (i = 0; envp[i] != NULL; i++)
+ continue;
+
+ environ = malloc(sizeof(char *) * (i + 1));
+ if (environ == NULL)
+ return;
+
+ for (i = 0; envp[i] != NULL; i++)
+ if ((environ[i] = strdup(envp[i])) == NULL)
+ return;
+ environ[i] = NULL;
+
+ if (i > 0)
+ argv_lth = envp[i-1] + strlen(envp[i-1]) - argv[0];
+ else
+ argv_lth = argv[argc-1] + strlen(argv[argc-1]) - argv[0];
+ if (argv_lth > 1)
+ argv0 = argv;
+}
+
+void setproctitle (const char *prog, const char *txt)
+{
+ size_t i;
+ char buf[SPT_BUFSIZE];
+
+ if (!argv0)
+ return;
+
+ if (strlen(prog) + strlen(txt) + 5 > SPT_BUFSIZE)
+ return;
+
+ sprintf(buf, "%s -- %s", prog, txt);
+
+ i = strlen(buf);
+ if (i > argv_lth - 2) {
+ i = argv_lth - 2;
+ buf[i] = '\0';
+ }
+ memset(argv0[0], '\0', argv_lth); /* clear the memory area */
+ strcpy(argv0[0], buf);
+
+ argv0[1] = NULL;
+}
diff --git a/utils/lib/sha1.c b/utils/lib/sha1.c
new file mode 100644
index 0000000..22d33b3
--- /dev/null
+++ b/utils/lib/sha1.c
@@ -0,0 +1,256 @@
+/*
+ * SHA-1 in C by Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * 1) "abc": A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * 2) "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq": 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * 3) A million repetitions of "a": 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+#define UL_SHA1HANDSOFF
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "sha1.h"
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+#ifdef WORDS_BIGENDIAN
+# define blk0(i) block->l[i]
+#else
+# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#endif
+
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void ul_SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+ uint32_t a, b, c, d, e;
+
+ typedef union {
+ unsigned char c[64];
+ uint32_t l[16];
+ } CHAR64LONG16;
+
+#ifdef UL_SHA1HANDSOFF
+ CHAR64LONG16 block[1]; /* use array to appear as a pointer */
+
+ memcpy(block, buffer, 64);
+#else
+ /* The following had better never be used because it causes the
+ * pointer-to-const buffer to be cast into a pointer to non-const.
+ * And the result is written through. I threw a "const" in, hoping
+ * this will cause a diagnostic.
+ */
+ CHAR64LONG16 *block = (const CHAR64LONG16 *)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef UL_SHA1HANDSOFF
+ memset(block, '\0', sizeof(block));
+#endif
+}
+
+/* SHA1Init - Initialize new context */
+
+void ul_SHA1Init(UL_SHA1_CTX *context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+/* Run your data through this. */
+
+void ul_SHA1Update(UL_SHA1_CTX *context, const unsigned char *data, uint32_t len)
+{
+ uint32_t i;
+
+ uint32_t j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ ul_SHA1Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ ul_SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+/* Add padding and return the message digest. */
+
+void ul_SHA1Final(unsigned char digest[20], UL_SHA1_CTX *context)
+{
+ unsigned i;
+
+ unsigned char finalcount[8];
+
+ unsigned char c;
+
+#if 0 /* untested "improvement" by DHR */
+ /* Convert context->count to a sequence of bytes
+ * in finalcount. Second element first, but
+ * big-endian order within element.
+ * But we do it all backwards.
+ */
+ unsigned char *fcp = &finalcount[8];
+
+ for (i = 0; i < 2; i++) {
+ uint32_t t = context->count[i];
+
+ int j;
+
+ for (j = 0; j < 4; t >>= 8, j++)
+ *--fcp = (unsigned char)t}
+#else
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ }
+#endif
+ c = 0200;
+ ul_SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ ul_SHA1Update(context, &c, 1);
+ }
+ ul_SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+
+void ul_SHA1(char *hash_out, const char *str, unsigned len)
+{
+ UL_SHA1_CTX ctx;
+ unsigned int ii;
+
+ ul_SHA1Init(&ctx);
+ for (ii = 0; ii < len; ii += 1)
+ ul_SHA1Update(&ctx, (const unsigned char *)str + ii, 1);
+ ul_SHA1Final((unsigned char *)hash_out, &ctx);
+ hash_out[20] = '\0';
+}
diff --git a/utils/lib/signames.c b/utils/lib/signames.c
new file mode 100644
index 0000000..316eec5
--- /dev/null
+++ b/utils/lib/signames.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1988, 1993, 1994, 2017
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+/*
+ * 2017-10-14 Niklas Hambüchen <mail@nh2.me>
+ * - Extracted signal names mapping from kill.c
+ *
+ * Copyright (C) 2014 Sami Kerola <kerolasa@iki.fi>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2017 Niklas Hambüchen <mail@nh2.me>
+ */
+
+#include <ctype.h> /* for isdigit() */
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "c.h"
+#include "strutils.h"
+#include "signames.h"
+
+static const struct ul_signal_name {
+ const char *name;
+ int val;
+} ul_signames[] = {
+ /* POSIX signals */
+ { "HUP", SIGHUP }, /* 1 */
+ { "INT", SIGINT }, /* 2 */
+ { "QUIT", SIGQUIT }, /* 3 */
+ { "ILL", SIGILL }, /* 4 */
+#ifdef SIGTRAP
+ { "TRAP", SIGTRAP }, /* 5 */
+#endif
+ { "ABRT", SIGABRT }, /* 6 */
+#ifdef SIGIOT
+ { "IOT", SIGIOT }, /* 6, same as SIGABRT */
+#endif
+#ifdef SIGEMT
+ { "EMT", SIGEMT }, /* 7 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGBUS
+ { "BUS", SIGBUS }, /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */
+#endif
+ { "FPE", SIGFPE }, /* 8 */
+ { "KILL", SIGKILL }, /* 9 */
+ { "USR1", SIGUSR1 }, /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */
+ { "SEGV", SIGSEGV }, /* 11 */
+ { "USR2", SIGUSR2 }, /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */
+ { "PIPE", SIGPIPE }, /* 13 */
+ { "ALRM", SIGALRM }, /* 14 */
+ { "TERM", SIGTERM }, /* 15 */
+#ifdef SIGSTKFLT
+ { "STKFLT", SIGSTKFLT }, /* 16 (arm,i386,m68k,ppc) */
+#endif
+ { "CHLD", SIGCHLD }, /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */
+#ifdef SIGCLD
+ { "CLD", SIGCLD }, /* same as SIGCHLD (mips) */
+#endif
+ { "CONT", SIGCONT }, /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */
+ { "STOP", SIGSTOP }, /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */
+ { "TSTP", SIGTSTP }, /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */
+ { "TTIN", SIGTTIN }, /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */
+ { "TTOU", SIGTTOU }, /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */
+#ifdef SIGURG
+ { "URG", SIGURG }, /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */
+#endif
+#ifdef SIGXCPU
+ { "XCPU", SIGXCPU }, /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */
+#endif
+#ifdef SIGXFSZ
+ { "XFSZ", SIGXFSZ }, /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */
+#endif
+#ifdef SIGVTALRM
+ { "VTALRM", SIGVTALRM }, /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */
+#endif
+#ifdef SIGPROF
+ { "PROF", SIGPROF }, /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */
+#endif
+#ifdef SIGWINCH
+ { "WINCH", SIGWINCH }, /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */
+#endif
+#ifdef SIGIO
+ { "IO", SIGIO }, /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */
+#endif
+#ifdef SIGPOLL
+ { "POLL", SIGPOLL }, /* same as SIGIO */
+#endif
+#ifdef SIGINFO
+ { "INFO", SIGINFO }, /* 29 (alpha) */
+#endif
+#ifdef SIGLOST
+ { "LOST", SIGLOST }, /* 29 (arm,i386,m68k,ppc,sparc*) */
+#endif
+#ifdef SIGPWR
+ { "PWR", SIGPWR }, /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */
+#endif
+#ifdef SIGUNUSED
+ { "UNUSED", SIGUNUSED }, /* 31 (arm,i386,m68k,ppc) */
+#endif
+#ifdef SIGSYS
+ { "SYS", SIGSYS }, /* 31 (mips,alpha,sparc*) */
+#endif
+};
+
+#ifdef SIGRTMIN
+static int rtsig_to_signum(const char *sig)
+{
+ int num, maxi = 0;
+ char *ep = NULL;
+
+ if (strncasecmp(sig, "min+", 4) == 0)
+ sig += 4;
+ else if (strncasecmp(sig, "max-", 4) == 0) {
+ sig += 4;
+ maxi = 1;
+ }
+ if (!isdigit(*sig))
+ return -1;
+ errno = 0;
+ num = strtol(sig, &ep, 10);
+ if (!ep || sig == ep || errno || num < 0)
+ return -1;
+ num = maxi ? SIGRTMAX - num : SIGRTMIN + num;
+ if (num < SIGRTMIN || SIGRTMAX < num)
+ return -1;
+ return num;
+}
+#endif
+
+int signame_to_signum(const char *sig)
+{
+ size_t n;
+
+ if (!strncasecmp(sig, "sig", 3))
+ sig += 3;
+#ifdef SIGRTMIN
+ /* RT signals */
+ if (!strncasecmp(sig, "rt", 2))
+ return rtsig_to_signum(sig + 2);
+#endif
+ /* Normal signals */
+ for (n = 0; n < ARRAY_SIZE(ul_signames); n++) {
+ if (!strcasecmp(ul_signames[n].name, sig))
+ return ul_signames[n].val;
+ }
+ return -1;
+}
+
+const char *signum_to_signame(int signum)
+{
+ size_t n;
+
+ for (n = 0; n < ARRAY_SIZE(ul_signames); n++) {
+ if (ul_signames[n].val == signum) {
+ return ul_signames[n].name;
+ }
+ }
+
+ return NULL;
+}
+
+int get_signame_by_idx(size_t idx, const char **signame, int *signum)
+{
+ if (idx >= ARRAY_SIZE(ul_signames))
+ return -1;
+
+ if (signame)
+ *signame = ul_signames[idx].name;
+ if (signum)
+ *signum = ul_signames[idx].val;
+ return 0;
+
+}
+
diff --git a/utils/lib/strutils.c b/utils/lib/strutils.c
new file mode 100644
index 0000000..304f314
--- /dev/null
+++ b/utils/lib/strutils.c
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "bitops.h"
+#include "pathnames.h"
+
+static int STRTOXX_EXIT_CODE = EXIT_FAILURE;
+
+void strutils_set_exitcode(int ex) {
+ STRTOXX_EXIT_CODE = ex;
+}
+
+static int do_scale_by_power (uintmax_t *x, int base, int power)
+{
+ while (power--) {
+ if (UINTMAX_MAX / base < *x)
+ return -ERANGE;
+ *x *= base;
+ }
+ return 0;
+}
+
+/*
+ * strtosize() - convert string to size (uintmax_t).
+ *
+ * Supported suffixes:
+ *
+ * XiB or X for 2^N
+ * where X = {K,M,G,T,P,E,Z,Y}
+ * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
+ * for example:
+ * 10KiB = 10240
+ * 10K = 10240
+ *
+ * XB for 10^N
+ * where X = {K,M,G,T,P,E,Z,Y}
+ * for example:
+ * 10KB = 10000
+ *
+ * The optional 'power' variable returns number associated with used suffix
+ * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
+ *
+ * The function also supports decimal point, for example:
+ * 0.5MB = 500000
+ * 0.5MiB = 512000
+ *
+ * Note that the function does not accept numbers with '-' (negative sign)
+ * prefix.
+ */
+int parse_size(const char *str, uintmax_t *res, int *power)
+{
+ const char *p;
+ char *end;
+ uintmax_t x, frac = 0;
+ int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
+
+ static const char *suf = "KMGTPEZY";
+ static const char *suf2 = "kmgtpezy";
+ const char *sp;
+
+ *res = 0;
+
+ if (!str || !*str) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Only positive numbers are acceptable
+ *
+ * Note that this check is not perfect, it would be better to
+ * use lconv->negative_sign. But coreutils use the same solution,
+ * so it's probably good enough...
+ */
+ p = str;
+ while (isspace((unsigned char) *p))
+ p++;
+ if (*p == '-') {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ errno = 0, end = NULL;
+ x = strtoumax(str, &end, 0);
+
+ if (end == str ||
+ (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
+ rc = errno ? -errno : -EINVAL;
+ goto err;
+ }
+ if (!end || !*end)
+ goto done; /* without suffix */
+ p = end;
+
+ /*
+ * Check size suffixes
+ */
+check_suffix:
+ if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
+ base = 1024; /* XiB, 2^N */
+ else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
+ base = 1000; /* XB, 10^N */
+ else if (*(p + 1)) {
+ struct lconv const *l = localeconv();
+ const char *dp = l ? l->decimal_point : NULL;
+ size_t dpsz = dp ? strlen(dp) : 0;
+
+ if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
+ const char *fstr = p + dpsz;
+
+ for (p = fstr; *p == '0'; p++)
+ frac_zeros++;
+ fstr = p;
+ if (isdigit(*fstr)) {
+ errno = 0, end = NULL;
+ frac = strtoumax(fstr, &end, 0);
+ if (end == fstr ||
+ (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
+ rc = errno ? -errno : -EINVAL;
+ goto err;
+ }
+ } else
+ end = (char *) p;
+
+ if (frac && (!end || !*end)) {
+ rc = -EINVAL;
+ goto err; /* without suffix, but with frac */
+ }
+ p = end;
+ goto check_suffix;
+ }
+ rc = -EINVAL;
+ goto err; /* unexpected suffix */
+ }
+
+ sp = strchr(suf, *p);
+ if (sp)
+ pwr = (sp - suf) + 1;
+ else {
+ sp = strchr(suf2, *p);
+ if (sp)
+ pwr = (sp - suf2) + 1;
+ else {
+ rc = -EINVAL;
+ goto err;
+ }
+ }
+
+ rc = do_scale_by_power(&x, base, pwr);
+ if (power)
+ *power = pwr;
+ if (frac && pwr) {
+ int i;
+ uintmax_t frac_div = 10, frac_poz = 1, frac_base = 1;
+
+ /* mega, giga, ... */
+ do_scale_by_power(&frac_base, base, pwr);
+
+ /* maximal divisor for last digit (e.g. for 0.05 is
+ * frac_div=100, for 0.054 is frac_div=1000, etc.)
+ *
+ * Reduce frac if too large.
+ */
+ while (frac_div < frac) {
+ if (frac_div <= UINTMAX_MAX/10)
+ frac_div *= 10;
+ else
+ frac /= 10;
+ }
+
+ /* 'frac' is without zeros (5 means 0.5 as well as 0.05) */
+ for (i = 0; i < frac_zeros; i++) {
+ if (frac_div <= UINTMAX_MAX/10)
+ frac_div *= 10;
+ else
+ frac /= 10;
+ }
+
+ /*
+ * Go backwardly from last digit and add to result what the
+ * digit represents in the frac_base. For example 0.25G
+ *
+ * 5 means 1GiB / (100/5)
+ * 2 means 1GiB / (10/2)
+ */
+ do {
+ unsigned int seg = frac % 10; /* last digit of the frac */
+ uintmax_t seg_div = frac_div / frac_poz; /* what represents the segment 1000, 100, .. */
+
+ frac /= 10; /* remove last digit from frac */
+ frac_poz *= 10;
+
+ if (seg && seg_div / seg)
+ x += frac_base / (seg_div / seg);
+ } while (frac);
+ }
+done:
+ *res = x;
+err:
+ if (rc < 0)
+ errno = -rc;
+ return rc;
+}
+
+int strtosize(const char *str, uintmax_t *res)
+{
+ return parse_size(str, res, NULL);
+}
+
+int isdigit_strend(const char *str, const char **end)
+{
+ const char *p;
+
+ for (p = str; p && *p && isdigit((unsigned char) *p); p++);
+
+ if (end)
+ *end = p;
+ return p && p > str && !*p;
+}
+
+int isxdigit_strend(const char *str, const char **end)
+{
+ const char *p;
+
+ for (p = str; p && *p && isxdigit((unsigned char) *p); p++);
+
+ if (end)
+ *end = p;
+
+ return p && p > str && !*p;
+}
+
+/*
+ * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
+ */
+int parse_switch(const char *arg, const char *errmesg, ...)
+{
+ const char *a, *b;
+ va_list ap;
+
+ va_start(ap, errmesg);
+ do {
+ a = va_arg(ap, char *);
+ if (!a)
+ break;
+ b = va_arg(ap, char *);
+ if (!b)
+ break;
+
+ if (strcmp(arg, a) == 0) {
+ va_end(ap);
+ return 1;
+ }
+
+ if (strcmp(arg, b) == 0) {
+ va_end(ap);
+ return 0;
+ }
+ } while (1);
+ va_end(ap);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, arg);
+}
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+ return ((char *)memcpy(dest, src, n)) + n;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i < maxlen; i++) {
+ if (s[i] == '\0')
+ return i;
+ }
+ return maxlen;
+}
+#endif
+
+#ifndef HAVE_STRNCHR
+char *strnchr(const char *s, size_t maxlen, int c)
+{
+ for (; maxlen-- && *s != '\0'; ++s)
+ if (*s == (char)c)
+ return (char *)s;
+ return NULL;
+}
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strnlen(s, n);
+ char *new = malloc((len + 1) * sizeof(char));
+ if (!new)
+ return NULL;
+ new[len] = '\0';
+ return (char *) memcpy(new, s, len);
+}
+#endif
+
+static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base);
+static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base);
+
+int16_t strtos16_or_err(const char *str, const char *errmesg)
+{
+ int32_t num = strtos32_or_err(str, errmesg);
+
+ if (num < INT16_MIN || num > INT16_MAX) {
+ errno = ERANGE;
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ }
+ return num;
+}
+
+static uint16_t _strtou16_or_err(const char *str, const char *errmesg, int base)
+{
+ uint32_t num = _strtou32_or_err(str, errmesg, base);
+
+ if (num > UINT16_MAX) {
+ errno = ERANGE;
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ }
+ return num;
+}
+
+uint16_t strtou16_or_err(const char *str, const char *errmesg)
+{
+ return _strtou16_or_err(str, errmesg, 10);
+}
+
+uint16_t strtox16_or_err(const char *str, const char *errmesg)
+{
+ return _strtou16_or_err(str, errmesg, 16);
+}
+
+int32_t strtos32_or_err(const char *str, const char *errmesg)
+{
+ int64_t num = strtos64_or_err(str, errmesg);
+
+ if (num < INT32_MIN || num > INT32_MAX) {
+ errno = ERANGE;
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ }
+ return num;
+}
+
+static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base)
+{
+ uint64_t num = _strtou64_or_err(str, errmesg, base);
+
+ if (num > UINT32_MAX) {
+ errno = ERANGE;
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ }
+ return num;
+}
+
+uint32_t strtou32_or_err(const char *str, const char *errmesg)
+{
+ return _strtou32_or_err(str, errmesg, 10);
+}
+
+uint32_t strtox32_or_err(const char *str, const char *errmesg)
+{
+ return _strtou32_or_err(str, errmesg, 16);
+}
+
+int64_t strtos64_or_err(const char *str, const char *errmesg)
+{
+ int64_t num;
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ goto err;
+ num = strtoimax(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base)
+{
+ uintmax_t num;
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ goto err;
+ num = strtoumax(str, &end, base);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uint64_t strtou64_or_err(const char *str, const char *errmesg)
+{
+ return _strtou64_or_err(str, errmesg, 10);
+}
+
+uint64_t strtox64_or_err(const char *str, const char *errmesg)
+{
+ return _strtou64_or_err(str, errmesg, 16);
+}
+
+double strtod_or_err(const char *str, const char *errmesg)
+{
+ double num;
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ goto err;
+ num = strtod(str, &end);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+long strtol_or_err(const char *str, const char *errmesg)
+{
+ long num;
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ goto err;
+ num = strtol(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+unsigned long strtoul_or_err(const char *str, const char *errmesg)
+{
+ unsigned long num;
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ goto err;
+ num = strtoul(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uintmax_t strtosize_or_err(const char *str, const char *errmesg)
+{
+ uintmax_t num;
+
+ if (strtosize(str, &num) == 0)
+ return num;
+
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
+{
+ double user_input;
+
+ user_input = strtod_or_err(str, errmesg);
+ tv->tv_sec = (time_t) user_input;
+ tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
+}
+
+/*
+ * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
+ * be 11 bytes.
+ */
+char *xstrmode(mode_t mode, char *str)
+{
+ unsigned short i = 0;
+
+ if (S_ISDIR(mode))
+ str[i++] = 'd';
+ else if (S_ISLNK(mode))
+ str[i++] = 'l';
+ else if (S_ISCHR(mode))
+ str[i++] = 'c';
+ else if (S_ISBLK(mode))
+ str[i++] = 'b';
+ else if (S_ISSOCK(mode))
+ str[i++] = 's';
+ else if (S_ISFIFO(mode))
+ str[i++] = 'p';
+ else if (S_ISREG(mode))
+ str[i++] = '-';
+
+ str[i++] = mode & S_IRUSR ? 'r' : '-';
+ str[i++] = mode & S_IWUSR ? 'w' : '-';
+ str[i++] = (mode & S_ISUID
+ ? (mode & S_IXUSR ? 's' : 'S')
+ : (mode & S_IXUSR ? 'x' : '-'));
+ str[i++] = mode & S_IRGRP ? 'r' : '-';
+ str[i++] = mode & S_IWGRP ? 'w' : '-';
+ str[i++] = (mode & S_ISGID
+ ? (mode & S_IXGRP ? 's' : 'S')
+ : (mode & S_IXGRP ? 'x' : '-'));
+ str[i++] = mode & S_IROTH ? 'r' : '-';
+ str[i++] = mode & S_IWOTH ? 'w' : '-';
+ str[i++] = (mode & S_ISVTX
+ ? (mode & S_IXOTH ? 't' : 'T')
+ : (mode & S_IXOTH ? 'x' : '-'));
+ str[i] = '\0';
+
+ return str;
+}
+
+/*
+ * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
+ */
+static int get_exp(uint64_t n)
+{
+ int shft;
+
+ for (shft = 10; shft <= 60; shft += 10) {
+ if (n < (1ULL << shft))
+ break;
+ }
+ return shft - 10;
+}
+
+char *size_to_human_string(int options, uint64_t bytes)
+{
+ char buf[32];
+ int dec, exp;
+ uint64_t frac;
+ const char *letters = "BKMGTPE";
+ char suffix[sizeof(" KiB")], *psuf = suffix;
+ char c;
+
+ if (options & SIZE_SUFFIX_SPACE)
+ *psuf++ = ' ';
+
+
+ exp = get_exp(bytes);
+ c = *(letters + (exp ? exp / 10 : 0));
+ dec = exp ? bytes / (1ULL << exp) : bytes;
+ frac = exp ? bytes % (1ULL << exp) : 0;
+
+ *psuf++ = c;
+
+ if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
+ *psuf++ = 'i';
+ *psuf++ = 'B';
+ }
+
+ *psuf = '\0';
+
+ /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
+ * exp, suffix[0], dec, frac);
+ */
+
+ /* round */
+ if (frac) {
+ /* get 3 digits after decimal point */
+ if (frac >= UINT64_MAX / 1000)
+ frac = ((frac / 1024) * 1000) / (1ULL << (exp - 10)) ;
+ else
+ frac = (frac * 1000) / (1ULL << (exp)) ;
+
+ if (options & SIZE_DECIMAL_2DIGITS) {
+ /* round 4/5 and keep 2 digits after decimal point */
+ frac = (frac + 5) / 10 ;
+ } else {
+ /* round 4/5 and keep 1 digit after decimal point */
+ frac = ((frac + 50) / 100) * 10 ;
+ }
+
+ /* rounding could have overflowed */
+ if (frac == 100) {
+ dec++;
+ frac = 0;
+ }
+ }
+
+ if (frac) {
+ struct lconv const *l = localeconv();
+ char *dp = l ? l->decimal_point : NULL;
+ int len;
+
+ if (!dp || !*dp)
+ dp = ".";
+
+ len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac);
+ if (len > 0 && (size_t) len < sizeof(buf)) {
+ /* remove potential extraneous zero */
+ if (buf[len - 1] == '0')
+ buf[len--] = '\0';
+ /* append suffix */
+ xstrncpy(buf+len, suffix, sizeof(buf) - len);
+ } else
+ *buf = '\0'; /* snprintf error */
+ } else
+ snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
+
+ return strdup(buf);
+}
+
+/*
+ * Parses comma delimited list to array with IDs, for example:
+ *
+ * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
+ * ary[1] = FOO_BBB;
+ * ary[3] = FOO_CCC;
+ *
+ * The function name2id() provides conversion from string to ID.
+ *
+ * Returns: >= 0 : number of items added to ary[]
+ * -1 : parse error or unknown item
+ * -2 : arysz reached
+ */
+int string_to_idarray(const char *list, int ary[], size_t arysz,
+ int (name2id)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+ size_t n = 0;
+
+ if (!list || !*list || !ary || !arysz || !name2id)
+ return -1;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ int id;
+
+ if (n >= arysz)
+ return -2;
+ if (!begin)
+ begin = p; /* begin of the column name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ id = name2id(begin, end - begin);
+ if (id == -1)
+ return -1;
+ ary[ n++ ] = id;
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return n;
+}
+
+/*
+ * Parses the array like string_to_idarray but if format is "+aaa,bbb"
+ * it adds fields to array instead of replacing them.
+ */
+int string_add_to_idarray(const char *list, int ary[], size_t arysz,
+ size_t *ary_pos, int (name2id)(const char *, size_t))
+{
+ const char *list_add;
+ int r;
+
+ if (!list || !*list || !ary_pos || *ary_pos > arysz)
+ return -1;
+
+ if (list[0] == '+')
+ list_add = &list[1];
+ else {
+ list_add = list;
+ *ary_pos = 0;
+ }
+
+ r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
+ if (r > 0)
+ *ary_pos += r;
+ return r;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2id() function and the 'id' is used
+ * as a position in the 'ary' bit array. It means that the 'id' has to be in
+ * range <0..N> where N < sizeof(ary) * NBBY.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitarray(const char *list,
+ char *ary,
+ int (*name2bit)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+
+ if (!list || !name2bit || !ary)
+ return -EINVAL;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ int bit;
+
+ if (!begin)
+ begin = p; /* begin of the level name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ bit = name2bit(begin, end - begin);
+ if (bit < 0)
+ return bit;
+ setbit(ary, bit);
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2flag() function and the flags is
+ * set to the 'mask'
+*
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitmask(const char *list,
+ unsigned long *mask,
+ long (*name2flag)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+
+ if (!list || !name2flag || !mask)
+ return -EINVAL;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ long flag;
+
+ if (!begin)
+ begin = p; /* begin of the level name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ flag = name2flag(begin, end - begin);
+ if (flag < 0)
+ return flag; /* error */
+ *mask |= flag;
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Parse the lower and higher values in a string containing
+ * "lower:higher" or "lower-higher" format. Note that either
+ * the lower or the higher values may be missing, and the def
+ * value will be assigned to it by default.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int parse_range(const char *str, int *lower, int *upper, int def)
+{
+ char *end = NULL;
+
+ if (!str)
+ return 0;
+
+ *upper = *lower = def;
+ errno = 0;
+
+ if (*str == ':') { /* <:N> */
+ str++;
+ *upper = strtol(str, &end, 10);
+ if (errno || !end || *end || end == str)
+ return -1;
+ } else {
+ *upper = *lower = strtol(str, &end, 10);
+ if (errno || !end || end == str)
+ return -1;
+
+ if (*end == ':' && !*(end + 1)) /* <M:> */
+ *upper = def;
+ else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
+ str = end + 1;
+ end = NULL;
+ errno = 0;
+ *upper = strtol(str, &end, 10);
+
+ if (errno || !end || *end || end == str)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static const char *next_path_segment(const char *str, size_t *sz)
+{
+ const char *start, *p;
+
+ start = str;
+ *sz = 0;
+ while (start && *start == '/' && *(start + 1) == '/')
+ start++;
+
+ if (!start || !*start)
+ return NULL;
+
+ for (*sz = 1, p = start + 1; *p && *p != '/'; p++) {
+ (*sz)++;
+ }
+
+ return start;
+}
+
+int streq_paths(const char *a, const char *b)
+{
+ while (a && b) {
+ size_t a_sz, b_sz;
+ const char *a_seg = next_path_segment(a, &a_sz);
+ const char *b_seg = next_path_segment(b, &b_sz);
+
+ /*
+ fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
+ fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
+ */
+
+ /* end of the path */
+ if (a_sz + b_sz == 0)
+ return 1;
+
+ /* ignore tailing slash */
+ if (a_sz + b_sz == 1 &&
+ ((a_seg && *a_seg == '/') || (b_seg && *b_seg == '/')))
+ return 1;
+
+ if (!a_seg || !b_seg)
+ break;
+ if (a_sz != b_sz || strncmp(a_seg, b_seg, a_sz) != 0)
+ break;
+
+ a = a_seg + a_sz;
+ b = b_seg + b_sz;
+ };
+
+ return 0;
+}
+
+char *strnappend(const char *s, const char *suffix, size_t b)
+{
+ size_t a;
+ char *r;
+
+ if (!s && !suffix)
+ return strdup("");
+ if (!s)
+ return strndup(suffix, b);
+ if (!suffix)
+ return strdup(s);
+
+ assert(s);
+ assert(suffix);
+
+ a = strlen(s);
+ if (b > ((size_t) -1) - a)
+ return NULL;
+
+ r = malloc(a + b + 1);
+ if (!r)
+ return NULL;
+
+ memcpy(r, s, a);
+ memcpy(r + a, suffix, b);
+ r[a+b] = 0;
+
+ return r;
+}
+
+char *strappend(const char *s, const char *suffix)
+{
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+char *strfappend(const char *s, const char *format, ...)
+{
+ va_list ap;
+ char *val, *res;
+ int sz;
+
+ va_start(ap, format);
+ sz = vasprintf(&val, format, ap);
+ va_end(ap);
+
+ if (sz < 0)
+ return NULL;
+
+ res = strnappend(s, val, sz);
+ free(val);
+ return res;
+}
+
+static size_t strcspn_escaped(const char *s, const char *reject)
+{
+ int escaped = 0;
+ int n;
+
+ for (n=0; s[n]; n++) {
+ if (escaped)
+ escaped = 0;
+ else if (s[n] == '\\')
+ escaped = 1;
+ else if (strchr(reject, s[n]))
+ break;
+ }
+
+ /* if s ends in \, return index of previous char */
+ return n - escaped;
+}
+
+/* Split a string into words. */
+const char *split(const char **state, size_t *l, const char *separator, int quoted)
+{
+ const char *current;
+
+ current = *state;
+
+ if (!*current) {
+ assert(**state == '\0');
+ return NULL;
+ }
+
+ current += strspn(current, separator);
+ if (!*current) {
+ *state = current;
+ return NULL;
+ }
+
+ if (quoted && strchr("\'\"", *current)) {
+ char quotechars[2] = {*current, '\0'};
+
+ *l = strcspn_escaped(current + 1, quotechars);
+ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
+ (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
+ /* right quote missing or garbage at the end */
+ *state = current;
+ return NULL;
+ }
+ *state = current++ + *l + 2;
+ } else if (quoted) {
+ *l = strcspn_escaped(current, separator);
+ if (current[*l] && !strchr(separator, current[*l])) {
+ /* unfinished escape */
+ *state = current;
+ return NULL;
+ }
+ *state = current + *l;
+ } else {
+ *l = strcspn(current, separator);
+ *state = current + *l;
+ }
+
+ return current;
+}
+
+/* Rewind file pointer forward to new line. */
+int skip_fline(FILE *fp)
+{
+ int ch;
+
+ do {
+ if ((ch = fgetc(fp)) == EOF)
+ return 1;
+ if (ch == '\n')
+ return 0;
+ } while (1);
+}
+
+#ifdef TEST_PROGRAM_STRUTILS
+struct testS {
+ char *name;
+ char *value;
+};
+
+static int test_strdup_to_member(int argc, char *argv[])
+{
+ struct testS *xx;
+
+ if (argc < 3)
+ return EXIT_FAILURE;
+
+ xx = calloc(1, sizeof(*xx));
+ if (!xx)
+ err(EXIT_FAILURE, "calloc() failed");
+
+ strdup_to_struct_member(xx, name, argv[1]);
+ strdup_to_struct_member(xx, value, argv[2]);
+
+ if (strcmp(xx->name, argv[1]) != 0 &&
+ strcmp(xx->value, argv[2]) != 0)
+ errx(EXIT_FAILURE, "strdup_to_struct_member() failed");
+
+ printf("1: '%s', 2: '%s'\n", xx->name, xx->value);
+
+ free(xx->name);
+ free(xx->value);
+ free(xx);
+ return EXIT_SUCCESS;
+}
+
+static int test_strutils_sizes(int argc, char *argv[])
+{
+ uintmax_t size = 0;
+ char *hum1, *hum2, *hum3;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ if (strtosize(argv[1], &size))
+ errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
+
+ hum1 = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+ hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
+ SIZE_SUFFIX_SPACE, size);
+ hum3 = size_to_human_string(SIZE_SUFFIX_3LETTER |
+ SIZE_SUFFIX_SPACE |
+ SIZE_DECIMAL_2DIGITS, size);
+
+ printf("%25s : %20ju : %8s : %12s : %13s\n", argv[1], size, hum1, hum2, hum3);
+ free(hum1);
+ free(hum2);
+ free(hum3);
+
+ return EXIT_SUCCESS;
+}
+
+static int test_strutils_cmp_paths(int argc, char *argv[])
+{
+ int rc = streq_paths(argv[1], argv[2]);
+
+ if (argc < 3)
+ return EXIT_FAILURE;
+
+ printf("%s: '%s' '%s'\n", rc == 1 ? "YES" : "NOT", argv[1], argv[2]);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc == 3 && strcmp(argv[1], "--size") == 0)
+ return test_strutils_sizes(argc - 1, argv + 1);
+
+ if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0)
+ return test_strutils_cmp_paths(argc - 1, argv + 1);
+
+ if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0)
+ return test_strdup_to_member(argc - 1, argv + 1);
+
+ fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
+ " %1$s --cmp-paths <path> <path>\n"
+ " %1$s --strdup-member <str> <str>\n",
+ argv[0]);
+
+ return EXIT_FAILURE;
+}
+#endif /* TEST_PROGRAM_STRUTILS */
diff --git a/utils/lib/strv.c b/utils/lib/strv.c
new file mode 100644
index 0000000..ddc2a0c
--- /dev/null
+++ b/utils/lib/strv.c
@@ -0,0 +1,403 @@
+/*
+ *
+ * Copyright 2010 Lennart Poettering
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
+ * Modified the original version from systemd project for util-linux.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "strutils.h"
+#include "strv.h"
+
+void strv_clear(char **l) {
+ char **k;
+
+ if (!l)
+ return;
+
+ for (k = l; *k; k++)
+ free(*k);
+
+ *l = NULL;
+}
+
+char **strv_free(char **l) {
+ strv_clear(l);
+ free(l);
+ return NULL;
+}
+
+char **strv_copy(char * const *l) {
+ char **r, **k;
+
+ k = r = malloc(sizeof(char *) * (strv_length(l) + 1));
+ if (!r)
+ return NULL;
+
+ if (l)
+ for (; *l; k++, l++) {
+ *k = strdup(*l);
+ if (!*k) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ *k = NULL;
+ return r;
+}
+
+unsigned strv_length(char * const *l) {
+ unsigned n = 0;
+
+ if (!l)
+ return 0;
+
+ for (; *l; l++)
+ n++;
+
+ return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap) {
+ const char *s;
+ char **a;
+ unsigned n = 0, i = 0;
+ va_list aq;
+
+ /* As a special trick we ignore all listed strings that equal
+ * (const char*) -1. This is supposed to be used with the
+ * STRV_IFNOTNULL() macro to include possibly NULL strings in
+ * the string list. */
+
+ if (x) {
+ n = x == (const char*) -1 ? 0 : 1;
+
+ va_copy(aq, ap);
+ while ((s = va_arg(aq, const char*))) {
+ if (s == (const char*) -1)
+ continue;
+
+ n++;
+ }
+
+ va_end(aq);
+ }
+
+ a = malloc(sizeof(char *) * (n + 1));
+ if (!a)
+ return NULL;
+
+ if (x) {
+ if (x != (const char*) -1) {
+ a[i] = strdup(x);
+ if (!a[i])
+ goto fail;
+ i++;
+ }
+
+ while ((s = va_arg(ap, const char*))) {
+
+ if (s == (const char*) -1)
+ continue;
+
+ a[i] = strdup(s);
+ if (!a[i])
+ goto fail;
+
+ i++;
+ }
+ }
+
+ a[i] = NULL;
+
+ return a;
+
+fail:
+ strv_free(a);
+ return NULL;
+}
+
+char **strv_new(const char *x, ...) {
+ char **r;
+ va_list ap;
+
+ va_start(ap, x);
+ r = strv_new_ap(x, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int strv_extend_strv(char ***a, char **b) {
+ int r;
+ char **s;
+
+ STRV_FOREACH(s, b) {
+ r = strv_extend(a, *s);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
+ int r;
+ char **s;
+
+ STRV_FOREACH(s, b) {
+ char *v;
+
+ v = strappend(*s, suffix);
+ if (!v)
+ return -ENOMEM;
+
+ r = strv_push(a, v);
+ if (r < 0) {
+ free(v);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+
+#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
+ for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ _FOREACH_WORD(word, length, s, separator, false, state)
+
+
+char **strv_split(const char *s, const char *separator) {
+ const char *word, *state;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
+ n++;
+
+ r = malloc(sizeof(char *) * (n + 1));
+ if (!r)
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
+ r[i] = strndup(word, l);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char *strv_join(char **l, const char *separator) {
+ char *r, *e;
+ char **s;
+ size_t n, k;
+
+ if (!separator)
+ separator = " ";
+
+ k = strlen(separator);
+
+ n = 0;
+ STRV_FOREACH(s, l) {
+ if (n != 0)
+ n += k;
+ n += strlen(*s);
+ }
+
+ r = malloc(n + 1);
+ if (!r)
+ return NULL;
+
+ e = r;
+ STRV_FOREACH(s, l) {
+ if (e != r)
+ e = stpcpy(e, separator);
+
+ e = stpcpy(e, *s);
+ }
+
+ *e = 0;
+
+ return r;
+}
+
+int strv_push(char ***l, char *value) {
+ char **c;
+ unsigned n, m;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* Increase and check for overflow */
+ m = n + 2;
+ if (m < n)
+ return -ENOMEM;
+
+ c = realloc(*l, sizeof(char *) * m);
+ if (!c)
+ return -ENOMEM;
+
+ c[n] = value;
+ c[n+1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+int strv_push_prepend(char ***l, char *value) {
+ char **c;
+ unsigned n, m, i;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* increase and check for overflow */
+ m = n + 2;
+ if (m < n)
+ return -ENOMEM;
+
+ c = malloc(sizeof(char *) * m);
+ if (!c)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++)
+ c[i+1] = (*l)[i];
+
+ c[0] = value;
+ c[n+1] = NULL;
+
+ free(*l);
+ *l = c;
+
+ return 0;
+}
+
+int strv_consume(char ***l, char *value) {
+ int r;
+
+ r = strv_push(l, value);
+ if (r < 0)
+ free(value);
+
+ return r;
+}
+
+int strv_consume_prepend(char ***l, char *value) {
+ int r;
+
+ r = strv_push_prepend(l, value);
+ if (r < 0)
+ free(value);
+
+ return r;
+}
+
+int strv_extend(char ***l, const char *value) {
+ char *v;
+
+ if (!value)
+ return 0;
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ return strv_consume(l, v);
+}
+
+char **strv_remove(char **l, const char *s) {
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(s);
+
+ /* Drops every occurrence of s in the string list, edits
+ * in-place. */
+
+ for (f = t = l; *f; f++)
+ if (strcmp(*f, s) == 0)
+ free(*f);
+ else
+ *(t++) = *f;
+
+ *t = NULL;
+ return l;
+}
+
+int strv_extendf(char ***l, const char *format, ...) {
+ va_list ap;
+ char *x;
+ int r;
+
+ va_start(ap, format);
+ r = vasprintf(&x, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return strv_consume(l, x);
+}
+
+int strv_extendv(char ***l, const char *format, va_list ap) {
+ char *x;
+ int r;
+
+ r = vasprintf(&x, format, ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ return strv_consume(l, x);
+}
+
+char **strv_reverse(char **l) {
+ unsigned n, i;
+
+ n = strv_length(l);
+ if (n <= 1)
+ return l;
+
+ for (i = 0; i < n / 2; i++) {
+ char *t;
+
+ t = l[i];
+ l[i] = l[n-1-i];
+ l[n-1-i] = t;
+ }
+
+ return l;
+}
diff --git a/utils/lib/sysfs.c b/utils/lib/sysfs.c
new file mode 100644
index 0000000..5b4de2c
--- /dev/null
+++ b/utils/lib/sysfs.c
@@ -0,0 +1,1127 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "pathnames.h"
+#include "sysfs.h"
+#include "fileutils.h"
+#include "all-io.h"
+#include "debug.h"
+#include "strutils.h"
+
+static void sysfs_blkdev_deinit_path(struct path_cxt *pc);
+static int sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd);
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+static UL_DEBUG_DEFINE_MASK(ulsysfs);
+UL_DEBUG_DEFINE_MASKNAMES(ulsysfs) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define ULSYSFS_DEBUG_INIT (1 << 1)
+#define ULSYSFS_DEBUG_CXT (1 << 2)
+
+#define DBG(m, x) __UL_DBG(ulsysfs, ULSYSFS_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(ulsysfs, ULSYSFS_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
+#include "debugobj.h"
+
+void ul_sysfs_init_debug(void)
+{
+ if (ulsysfs_debug_mask)
+ return;
+ __UL_INIT_DEBUG_FROM_ENV(ulsysfs, ULSYSFS_DEBUG_, 0, ULSYSFS_DEBUG);
+}
+
+struct path_cxt *ul_new_sysfs_path(dev_t devno, struct path_cxt *parent, const char *prefix)
+{
+ struct path_cxt *pc = ul_new_path(NULL);
+
+ if (!pc)
+ return NULL;
+ if (prefix)
+ ul_path_set_prefix(pc, prefix);
+
+ if (sysfs_blkdev_init_path(pc, devno, parent) != 0) {
+ ul_unref_path(pc);
+ return NULL;
+ }
+
+ DBG(CXT, ul_debugobj(pc, "alloc"));
+ return pc;
+}
+
+/*
+ * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
+ *
+ * The function is possible to call in loop and without sysfs_blkdev_deinit_path().
+ * The sysfs_blkdev_deinit_path() is automatically called by ul_unref_path().
+ *
+ */
+int sysfs_blkdev_init_path(struct path_cxt *pc, dev_t devno, struct path_cxt *parent)
+{
+ struct sysfs_blkdev *blk;
+ int rc;
+ char buf[sizeof(_PATH_SYS_DEVBLOCK)
+ + sizeof(stringify_value(UINT32_MAX)) * 2
+ + 3];
+
+ /* define path to devno stuff */
+ snprintf(buf, sizeof(buf), _PATH_SYS_DEVBLOCK "/%d:%d", major(devno), minor(devno));
+ rc = ul_path_set_dir(pc, buf);
+ if (rc)
+ return rc;
+
+ /* make sure path exists */
+ rc = ul_path_get_dirfd(pc);
+ if (rc < 0)
+ return rc;
+
+ /* initialize sysfs blkdev specific stuff */
+ blk = ul_path_get_dialect(pc);
+ if (!blk) {
+ DBG(CXT, ul_debugobj(pc, "alloc new sysfs handler"));
+ blk = calloc(1, sizeof(struct sysfs_blkdev));
+ if (!blk)
+ return -ENOMEM;
+
+ ul_path_set_dialect(pc, blk, sysfs_blkdev_deinit_path);
+ ul_path_set_enoent_redirect(pc, sysfs_blkdev_enoent_redirect);
+ }
+
+ DBG(CXT, ul_debugobj(pc, "init sysfs stuff"));
+
+ blk->devno = devno;
+ sysfs_blkdev_set_parent(pc, parent);
+
+ return 0;
+}
+
+static void sysfs_blkdev_deinit_path(struct path_cxt *pc)
+{
+ struct sysfs_blkdev *blk;
+
+ if (!pc)
+ return;
+
+ DBG(CXT, ul_debugobj(pc, "deinit"));
+
+ blk = ul_path_get_dialect(pc);
+ if (!blk)
+ return;
+
+ ul_unref_path(blk->parent);
+ free(blk);
+
+ ul_path_set_dialect(pc, NULL, NULL);
+}
+
+int sysfs_blkdev_set_parent(struct path_cxt *pc, struct path_cxt *parent)
+{
+ struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
+
+ if (!pc || !blk)
+ return -EINVAL;
+
+ if (blk->parent) {
+ ul_unref_path(blk->parent);
+ blk->parent = NULL;
+ }
+
+ if (parent) {
+ ul_ref_path(parent);
+ blk->parent = parent;
+ } else
+ blk->parent = NULL;
+
+ DBG(CXT, ul_debugobj(pc, "new parent"));
+ return 0;
+}
+
+struct path_cxt *sysfs_blkdev_get_parent(struct path_cxt *pc)
+{
+ struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
+ return blk ? blk->parent : NULL;
+}
+
+/*
+ * Redirects ENOENT errors to the parent, if the path is to the queue/
+ * sysfs directory. For example
+ *
+ * /sys/dev/block/8:1/queue/logical_block_size redirects to
+ * /sys/dev/block/8:0/queue/logical_block_size
+ */
+static int sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd)
+{
+ struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
+
+ if (blk && blk->parent && strncmp(path, "queue/", 6) == 0) {
+ *dirfd = ul_path_get_dirfd(blk->parent);
+ if (*dirfd >= 0) {
+ DBG(CXT, ul_debugobj(pc, "%s redirected to parent", path));
+ return 0;
+ }
+ }
+ return 1; /* no redirect */
+}
+
+char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz)
+{
+ char link[PATH_MAX];
+ char *name;
+ ssize_t sz;
+
+ /* read /sys/dev/block/<maj:min> link */
+ sz = ul_path_readlink(pc, link, sizeof(link) - 1, NULL);
+ if (sz < 0)
+ return NULL;
+ link[sz] = '\0';
+
+ name = strrchr(link, '/');
+ if (!name)
+ return NULL;
+
+ name++;
+ sz = strlen(name);
+ if ((size_t) sz + 1 > bufsiz)
+ return NULL;
+
+ memcpy(buf, name, sz + 1);
+ sysfs_devname_sys_to_dev(buf);
+ return buf;
+}
+
+int sysfs_blkdev_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
+{
+ char path[NAME_MAX + 6 + 1];
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_DIR &&
+ d->d_type != DT_LNK &&
+ d->d_type != DT_UNKNOWN)
+ return 0;
+#endif
+ if (parent_name) {
+ const char *p = parent_name;
+ size_t len;
+
+ /* /dev/sda --> "sda" */
+ if (*parent_name == '/') {
+ p = strrchr(parent_name, '/');
+ if (!p)
+ return 0;
+ p++;
+ }
+
+ len = strlen(p);
+ if (strlen(d->d_name) <= len)
+ return 0;
+
+ /* partitions subdir name is
+ * "<parent>[:digit:]" or "<parent>p[:digit:]"
+ */
+ return strncmp(p, d->d_name, len) == 0 &&
+ ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
+ || isdigit(*(d->d_name + len)));
+ }
+
+ /* Cannot use /partition file, not supported on old sysfs */
+ snprintf(path, sizeof(path), "%s/start", d->d_name);
+
+ return faccessat(dirfd(dir), path, R_OK, 0) == 0;
+}
+
+int sysfs_blkdev_count_partitions(struct path_cxt *pc, const char *devname)
+{
+ DIR *dir;
+ struct dirent *d;
+ int r = 0;
+
+ dir = ul_path_opendir(pc, NULL);
+ if (!dir)
+ return 0;
+
+ while ((d = xreaddir(dir))) {
+ if (sysfs_blkdev_is_partition_dirent(dir, d, devname))
+ r++;
+ }
+
+ closedir(dir);
+ return r;
+}
+
+/*
+ * Converts @partno (partition number) to devno of the partition.
+ * The @pc handles wholedisk device.
+ *
+ * Note that this code does not expect any special format of the
+ * partitions devnames.
+ */
+dev_t sysfs_blkdev_partno_to_devno(struct path_cxt *pc, int partno)
+{
+ DIR *dir;
+ struct dirent *d;
+ dev_t devno = 0;
+
+ dir = ul_path_opendir(pc, NULL);
+ if (!dir)
+ return 0;
+
+ while ((d = xreaddir(dir))) {
+ int n;
+
+ if (!sysfs_blkdev_is_partition_dirent(dir, d, NULL))
+ continue;
+
+ if (ul_path_readf_s32(pc, &n, "%s/partition", d->d_name))
+ continue;
+
+ if (n == partno) {
+ if (ul_path_readf_majmin(pc, &devno, "%s/dev", d->d_name) == 0)
+ break;
+ }
+ }
+
+ closedir(dir);
+ DBG(CXT, ul_debugobj(pc, "partno (%d) -> devno (%d)", (int) partno, (int) devno));
+ return devno;
+}
+
+
+/*
+ * Returns slave name if there is only one slave, otherwise returns NULL.
+ * The result should be deallocated by free().
+ */
+char *sysfs_blkdev_get_slave(struct path_cxt *pc)
+{
+ DIR *dir;
+ struct dirent *d;
+ char *name = NULL;
+
+ dir = ul_path_opendir(pc, "slaves");
+ if (!dir)
+ return NULL;
+
+ while ((d = xreaddir(dir))) {
+ if (name)
+ goto err; /* more slaves */
+ name = strdup(d->d_name);
+ }
+
+ closedir(dir);
+ return name;
+err:
+ free(name);
+ closedir(dir);
+ return NULL;
+}
+
+
+#define SUBSYSTEM_LINKNAME "/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+ size_t len;
+ char *p;
+
+ if (!chain || !*chain)
+ return NULL;
+
+ len = strlen(chain);
+ if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+ return NULL;
+
+ do {
+ ssize_t sz;
+
+ /* append "/subsystem" to the path */
+ memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+ /* try if subsystem symlink exists */
+ sz = readlink(chain, buf, bufsz - 1);
+
+ /* remove last subsystem from chain */
+ chain[len] = '\0';
+ p = strrchr(chain, '/');
+ if (p) {
+ *p = '\0';
+ len = p - chain;
+ }
+
+ if (sz > 0) {
+ /* we found symlink to subsystem, return basename */
+ buf[sz] = '\0';
+ return basename(buf);
+ }
+
+ } while (p);
+
+ return NULL;
+}
+
+/*
+ * Returns complete path to the device, the patch contains all subsystems
+ * used for the device.
+ */
+char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz)
+{
+ /* read /sys/dev/block/<maj>:<min> symlink */
+ ssize_t sz = ul_path_readlink(pc, buf, bufsz, NULL);
+ const char *prefix;
+ size_t psz = 0;
+
+ if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz)
+ return NULL;
+
+ buf[sz++] = '\0';
+ prefix = ul_path_get_prefix(pc);
+ if (prefix)
+ psz = strlen(prefix);
+
+ /* create absolute patch from the link */
+ memmove(buf + psz + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz);
+ if (prefix)
+ memcpy(buf, prefix, psz);
+
+ memcpy(buf + psz, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+ return buf;
+}
+
+/*
+ * The @subsys returns the next subsystem in the chain. Function modifies
+ * @devchain string.
+ *
+ * Returns: 0 in success, <0 on error, 1 on end of chain
+ */
+int sysfs_blkdev_next_subsystem(struct path_cxt *pc __attribute__((unused)),
+ char *devchain, char **subsys)
+{
+ char subbuf[PATH_MAX];
+ char *sub;
+
+ if (!subsys || !devchain)
+ return -EINVAL;
+
+ *subsys = NULL;
+
+ while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+ *subsys = strdup(sub);
+ if (!*subsys)
+ return -ENOMEM;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int is_hotpluggable_subsystem(const char *name)
+{
+ static const char * const hotplug_subsystems[] = {
+ "usb",
+ "ieee1394",
+ "pcmcia",
+ "mmc",
+ "ccw"
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+ if (strcmp(name, hotplug_subsystems[i]) == 0)
+ return 1;
+
+ return 0;
+}
+
+int sysfs_blkdev_is_hotpluggable(struct path_cxt *pc)
+{
+ char buf[PATH_MAX], *chain, *sub;
+ int rc = 0;
+
+
+ /* check /sys/dev/block/<maj>:<min>/removable attribute */
+ if (ul_path_read_s32(pc, &rc, "removable") == 0 && rc == 1)
+ return 1;
+
+ chain = sysfs_blkdev_get_devchain(pc, buf, sizeof(buf));
+
+ while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) {
+ rc = is_hotpluggable_subsystem(sub);
+ if (rc) {
+ free(sub);
+ break;
+ }
+ free(sub);
+ }
+
+ return rc;
+}
+
+static int get_dm_wholedisk(struct path_cxt *pc, char *diskname,
+ size_t len, dev_t *diskdevno)
+{
+ int rc = 0;
+ char *name;
+
+ /* Note, sysfs_blkdev_get_slave() returns the first slave only,
+ * if there is more slaves, then return NULL
+ */
+ name = sysfs_blkdev_get_slave(pc);
+ if (!name)
+ return -1;
+
+ if (diskname && len)
+ xstrncpy(diskname, name, len);
+
+ if (diskdevno) {
+ *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL);
+ if (!*diskdevno)
+ rc = -1;
+ }
+
+ free(name);
+ return rc;
+}
+
+/*
+ * Returns by @diskdevno whole disk device devno and (optionally) by
+ * @diskname the whole disk device name.
+ */
+int sysfs_blkdev_get_wholedisk( struct path_cxt *pc,
+ char *diskname,
+ size_t len,
+ dev_t *diskdevno)
+{
+ int is_part = 0;
+
+ if (!pc)
+ return -1;
+
+ is_part = ul_path_access(pc, F_OK, "partition") == 0;
+ if (!is_part) {
+ /*
+ * Extra case for partitions mapped by device-mapper.
+ *
+ * All regular partitions (added by BLKPG ioctl or kernel PT
+ * parser) have the /sys/.../partition file. The partitions
+ * mapped by DM don't have such file, but they have "part"
+ * prefix in DM UUID.
+ */
+ char *uuid = NULL, *tmp, *prefix;
+
+ ul_path_read_string(pc, &uuid, "dm/uuid");
+ tmp = uuid;
+ prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+ if (prefix && strncasecmp(prefix, "part", 4) == 0)
+ is_part = 1;
+ free(uuid);
+
+ if (is_part &&
+ get_dm_wholedisk(pc, diskname, len, diskdevno) == 0)
+ /*
+ * partitioned device, mapped by DM
+ */
+ goto done;
+
+ is_part = 0;
+ }
+
+ if (!is_part) {
+ /*
+ * unpartitioned device
+ */
+ if (diskname && !sysfs_blkdev_get_name(pc, diskname, len))
+ goto err;
+ if (diskdevno)
+ *diskdevno = sysfs_blkdev_get_devno(pc);
+
+ } else {
+ /*
+ * partitioned device
+ * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
+ * - dirname ../../block/sda/sda1 = ../../block/sda
+ * - basename ../../block/sda = sda
+ */
+ char linkpath[PATH_MAX];
+ char *name;
+ ssize_t linklen;
+
+ linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath) - 1, NULL);
+ if (linklen < 0)
+ goto err;
+ linkpath[linklen] = '\0';
+
+ stripoff_last_component(linkpath); /* dirname */
+ name = stripoff_last_component(linkpath); /* basename */
+ if (!name)
+ goto err;
+
+ sysfs_devname_sys_to_dev(name);
+ if (diskname && len)
+ xstrncpy(diskname, name, len);
+
+ if (diskdevno) {
+ *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL);
+ if (!*diskdevno)
+ goto err;
+ }
+ }
+
+done:
+ return 0;
+err:
+ return -1;
+}
+
+int sysfs_devno_to_wholedisk(dev_t devno, char *diskname,
+ size_t len, dev_t *diskdevno)
+{
+ struct path_cxt *pc;
+ int rc = 0;
+
+ if (!devno)
+ return -EINVAL;
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc)
+ return -ENOMEM;
+
+ rc = sysfs_blkdev_get_wholedisk(pc, diskname, len, diskdevno);
+ ul_unref_path(pc);
+ return rc;
+}
+
+/*
+ * Returns 1 if the device is private device mapper device. The @uuid
+ * (if not NULL) returns DM device UUID, use free() to deallocate.
+ */
+int sysfs_devno_is_dm_private(dev_t devno, char **uuid)
+{
+ struct path_cxt *pc = NULL;
+ char *id = NULL;
+ int rc = 0;
+
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc)
+ goto done;
+ if (ul_path_read_string(pc, &id, "dm/uuid") <= 0 || !id)
+ goto done;
+
+ /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
+ * is the "LVM" prefix and "-<name>" postfix).
+ */
+ if (strncmp(id, "LVM-", 4) == 0) {
+ char *p = strrchr(id + 4, '-');
+
+ if (p && *(p + 1))
+ rc = 1;
+
+ /* Private Stratis devices prefix the UUID with "stratis-1-private"
+ */
+ } else if (strncmp(id, "stratis-1-private", 17) == 0) {
+ rc = 1;
+ }
+done:
+ ul_unref_path(pc);
+ if (uuid)
+ *uuid = id;
+ else
+ free(id);
+ return rc;
+}
+
+/*
+ * Return 0 or 1, or < 0 in case of error
+ */
+int sysfs_devno_is_wholedisk(dev_t devno)
+{
+ dev_t disk;
+
+ if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
+ return -1;
+
+ return devno == disk;
+}
+
+
+int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l)
+{
+ char buf[PATH_MAX], *hctl;
+ struct sysfs_blkdev *blk;
+ ssize_t len;
+
+ blk = ul_path_get_dialect(pc);
+
+ if (!blk || blk->hctl_error)
+ return -EINVAL;
+ if (blk->has_hctl)
+ goto done;
+
+ blk->hctl_error = 1;
+ len = ul_path_readlink(pc, buf, sizeof(buf) - 1, "device");
+ if (len < 0)
+ return len;
+
+ buf[len] = '\0';
+ hctl = strrchr(buf, '/');
+ if (!hctl)
+ return -1;
+ hctl++;
+
+ if (sscanf(hctl, "%u:%u:%u:%u", &blk->scsi_host, &blk->scsi_channel,
+ &blk->scsi_target, &blk->scsi_lun) != 4)
+ return -1;
+
+ blk->has_hctl = 1;
+done:
+ if (h)
+ *h = blk->scsi_host;
+ if (c)
+ *c = blk->scsi_channel;
+ if (t)
+ *t = blk->scsi_target;
+ if (l)
+ *l = blk->scsi_lun;
+
+ blk->hctl_error = 0;
+ return 0;
+}
+
+
+static char *scsi_host_attribute_path(
+ struct path_cxt *pc,
+ const char *type,
+ char *buf,
+ size_t bufsz,
+ const char *attr)
+{
+ int len;
+ int host;
+ const char *prefix;
+
+ if (sysfs_blkdev_scsi_get_hctl(pc, &host, NULL, NULL, NULL))
+ return NULL;
+
+ prefix = ul_path_get_prefix(pc);
+ if (!prefix)
+ prefix = "";
+
+ if (attr)
+ len = snprintf(buf, bufsz, "%s%s/%s_host/host%d/%s",
+ prefix, _PATH_SYS_CLASS, type, host, attr);
+ else
+ len = snprintf(buf, bufsz, "%s%s/%s_host/host%d",
+ prefix, _PATH_SYS_CLASS, type, host);
+
+ return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
+}
+
+char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc,
+ const char *type, const char *attr)
+{
+ char buf[1024];
+ int rc;
+ FILE *f;
+
+ if (!attr || !type ||
+ !scsi_host_attribute_path(pc, type, buf, sizeof(buf), attr))
+ return NULL;
+
+ if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
+ return NULL;
+
+ rc = fscanf(f, "%1023[^\n]", buf);
+ fclose(f);
+
+ return rc == 1 ? strdup(buf) : NULL;
+}
+
+int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type)
+{
+ char buf[PATH_MAX];
+ struct stat st;
+
+ if (!type || !scsi_host_attribute_path(pc, type,
+ buf, sizeof(buf), NULL))
+ return 0;
+
+ return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static char *scsi_attribute_path(struct path_cxt *pc,
+ char *buf, size_t bufsz, const char *attr)
+{
+ int len, h, c, t, l;
+ const char *prefix;
+
+ if (sysfs_blkdev_scsi_get_hctl(pc, &h, &c, &t, &l) != 0)
+ return NULL;
+
+ prefix = ul_path_get_prefix(pc);
+ if (!prefix)
+ prefix = "";
+
+ if (attr)
+ len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d/%s",
+ prefix, _PATH_SYS_SCSI,
+ h,c,t,l, attr);
+ else
+ len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d",
+ prefix, _PATH_SYS_SCSI,
+ h,c,t,l);
+ return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
+}
+
+int sysfs_blkdev_scsi_has_attribute(struct path_cxt *pc, const char *attr)
+{
+ char path[PATH_MAX];
+ struct stat st;
+
+ if (!scsi_attribute_path(pc, path, sizeof(path), attr))
+ return 0;
+
+ return stat(path, &st) == 0;
+}
+
+int sysfs_blkdev_scsi_path_contains(struct path_cxt *pc, const char *pattern)
+{
+ char path[PATH_MAX], linkc[PATH_MAX];
+ struct stat st;
+ ssize_t len;
+
+ if (!scsi_attribute_path(pc, path, sizeof(path), NULL))
+ return 0;
+
+ if (stat(path, &st) != 0)
+ return 0;
+
+ len = readlink(path, linkc, sizeof(linkc) - 1);
+ if (len < 0)
+ return 0;
+
+ linkc[len] = '\0';
+ return strstr(linkc, pattern) != NULL;
+}
+
+static dev_t read_devno(const char *path)
+{
+ FILE *f;
+ int maj = 0, min = 0;
+ dev_t dev = 0;
+
+ f = fopen(path, "r" UL_CLOEXECSTR);
+ if (!f)
+ return 0;
+
+ if (fscanf(f, "%d:%d", &maj, &min) == 2)
+ dev = makedev(maj, min);
+ fclose(f);
+ return dev;
+}
+
+int sysfs_devname_is_hidden(const char *prefix, const char *name)
+{
+ char buf[PATH_MAX];
+ int rc = 0, hidden = 0, len;
+ FILE *f;
+
+ if (strncmp("/dev/", name, 5) == 0)
+ return 0;
+
+ if (!prefix)
+ prefix = "";
+ /*
+ * Create path to /sys/block/<name>/hidden
+ */
+ len = snprintf(buf, sizeof(buf),
+ "%s" _PATH_SYS_BLOCK "/%s/hidden",
+ prefix, name);
+
+ if (len < 0 || (size_t) len + 1 > sizeof(buf))
+ return 0;
+
+ f = fopen(buf, "r" UL_CLOEXECSTR);
+ if (!f)
+ return 0;
+
+ rc = fscanf(f, "%d", &hidden);
+ fclose(f);
+
+ return rc == 1 ? hidden : 0;
+}
+
+
+dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent)
+{
+ char buf[PATH_MAX];
+ char *_name = NULL; /* name as encoded in sysfs */
+ dev_t dev = 0;
+ int len;
+
+ if (!prefix)
+ prefix = "";
+
+ assert(name);
+
+ if (strncmp("/dev/", name, 5) == 0) {
+ /*
+ * Read from /dev
+ */
+ struct stat st;
+
+ if (stat(name, &st) == 0) {
+ dev = st.st_rdev;
+ goto done;
+ }
+ name += 5; /* unaccessible, or not node in /dev */
+ }
+
+ _name = strdup(name);
+ if (!_name)
+ goto done;
+ sysfs_devname_dev_to_sys(_name);
+
+ if (parent && strncmp("dm-", name, 3) != 0) {
+ /*
+ * Create path to /sys/block/<parent>/<name>/dev
+ */
+ char *_parent = strdup(parent);
+
+ if (!_parent) {
+ free(_parent);
+ goto done;
+ }
+ sysfs_devname_dev_to_sys(_parent);
+ len = snprintf(buf, sizeof(buf),
+ "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
+ prefix, _parent, _name);
+ free(_parent);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ goto done;
+
+ /* don't try anything else for dm-* */
+ dev = read_devno(buf);
+ goto done;
+ }
+
+ /*
+ * Read from /sys/block/<sysname>/dev
+ */
+ len = snprintf(buf, sizeof(buf),
+ "%s" _PATH_SYS_BLOCK "/%s/dev",
+ prefix, _name);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ goto done;
+ dev = read_devno(buf);
+
+ if (!dev) {
+ /*
+ * Read from /sys/block/<sysname>/device/dev
+ */
+ len = snprintf(buf, sizeof(buf),
+ "%s" _PATH_SYS_BLOCK "/%s/device/dev",
+ prefix, _name);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ goto done;
+ dev = read_devno(buf);
+ }
+done:
+ free(_name);
+ return dev;
+}
+
+dev_t sysfs_devname_to_devno(const char *name)
+{
+ return __sysfs_devname_to_devno(NULL, name, NULL);
+}
+
+char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz)
+{
+ const char *name = sysfs_blkdev_get_name(pc, buf, bufsiz);
+ char *res = NULL;
+ size_t sz;
+ struct stat st;
+
+ if (!name)
+ goto done;
+
+ sz = strlen(name);
+ if (sz + sizeof("/dev/") > bufsiz)
+ goto done;
+
+ /* create the final "/dev/<name>" string */
+ memmove(buf + 5, name, sz + 1);
+ memcpy(buf, "/dev/", 5);
+
+ if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == sysfs_blkdev_get_devno(pc))
+ res = buf;
+done:
+ return res;
+}
+
+dev_t sysfs_blkdev_get_devno(struct path_cxt *pc)
+{
+ return ((struct sysfs_blkdev *) ul_path_get_dialect(pc))->devno;
+}
+
+/*
+ * Returns devname (e.g. "/dev/sda1") for the given devno.
+ *
+ * Please, use more robust blkid_devno_to_devname() in your applications.
+ */
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
+{
+ struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
+ char *res = NULL;
+
+ if (pc) {
+ res = sysfs_blkdev_get_path(pc, buf, bufsiz);
+ ul_unref_path(pc);
+ }
+ return res;
+}
+
+char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz)
+{
+ struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
+ char *res = NULL;
+
+ if (pc) {
+ res = sysfs_blkdev_get_name(pc, buf, bufsiz);
+ ul_unref_path(pc);
+ }
+ return res;
+}
+
+int sysfs_devno_count_partitions(dev_t devno)
+{
+ struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
+ int n = 0;
+
+ if (pc) {
+ char buf[PATH_MAX + 1];
+ char *name = sysfs_blkdev_get_name(pc, buf, sizeof(buf));
+
+ n = sysfs_blkdev_count_partitions(pc, name);
+ ul_unref_path(pc);
+ }
+ return n;
+}
+
+
+#ifdef TEST_PROGRAM_SYSFS
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ struct path_cxt *pc;
+ char *devname;
+ dev_t devno, disk_devno;
+ char path[PATH_MAX], *sub, *chain;
+ char diskname[32];
+ int i, is_part, rc = EXIT_SUCCESS;
+ uint64_t u64;
+
+ if (argc != 2)
+ errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
+
+ ul_sysfs_init_debug();
+
+ devname = argv[1];
+ devno = sysfs_devname_to_devno(devname);
+
+ if (!devno)
+ err(EXIT_FAILURE, "failed to read devno");
+
+ printf("non-context:\n");
+ printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
+ printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno, path, sizeof(path)));
+ printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
+
+ sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
+ printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno));
+ printf(" WHOLEDISK-DEVNAME: %s\n", diskname);
+
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc)
+ goto done;
+
+ printf("context based:\n");
+ devno = sysfs_blkdev_get_devno(pc);
+ printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
+ printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc, path, sizeof(path)));
+ printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc, path, sizeof(path)));
+
+ sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
+ printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno));
+ printf(" WHOLEDISK-DEVNAME: %s\n", diskname);
+
+ is_part = ul_path_access(pc, F_OK, "partition") == 0;
+ printf(" PARTITION: %s\n", is_part ? "YES" : "NOT");
+
+ if (is_part && disk_devno) {
+ struct path_cxt *disk_pc = ul_new_sysfs_path(disk_devno, NULL, NULL);
+ sysfs_blkdev_set_parent(pc, disk_pc);
+
+ ul_unref_path(disk_pc);
+ }
+
+ printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc) ? "yes" : "no");
+ printf(" SLAVES: %d\n", ul_path_count_dirents(pc, "slaves"));
+
+ if (!is_part) {
+ printf("First 5 partitions:\n");
+ for (i = 1; i <= 5; i++) {
+ dev_t dev = sysfs_blkdev_partno_to_devno(pc, i);
+ if (dev)
+ printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
+ }
+ }
+
+ if (ul_path_read_u64(pc, &u64, "size") != 0)
+ printf(" (!) read SIZE failed\n");
+ else
+ printf(" SIZE: %jd\n", u64);
+
+ if (ul_path_read_s32(pc, &i, "queue/hw_sector_size"))
+ printf(" (!) read SECTOR failed\n");
+ else
+ printf(" SECTOR: %d\n", i);
+
+
+ chain = sysfs_blkdev_get_devchain(pc, path, sizeof(path));
+ printf(" SUBSUSTEMS:\n");
+
+ while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) {
+ printf("\t%s\n", sub);
+ free(sub);
+ }
+
+ rc = EXIT_SUCCESS;
+done:
+ ul_unref_path(pc);
+ return rc;
+}
+#endif /* TEST_PROGRAM_SYSFS */
diff --git a/utils/lib/timer.c b/utils/lib/timer.c
new file mode 100644
index 0000000..c1ea54e
--- /dev/null
+++ b/utils/lib/timer.c
@@ -0,0 +1,95 @@
+/*
+ * Please, don't add this file to libcommon because timers requires
+ * -lrt on systems with old libc (and probably also -lpthread for static
+ * build).
+ */
+#include <time.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "timer.h"
+
+/*
+ * Note the timeout is used for the first signal, then the signal is send
+ * repeatedly in interval ~1% of the original timeout to avoid race in signal
+ * handling -- for example you want to use timer to define timeout for a
+ * syscall:
+ *
+ * setup_timer()
+ * syscall()
+ * cancel_timer()
+ *
+ * if the timeout is too short than it's possible that the signal is delivered
+ * before application enter the syscall function. For this reason timer send
+ * the signal repeatedly.
+ *
+ * The applications need to ensure that they can tolerate multiple signal
+ * deliveries.
+ */
+#ifdef HAVE_TIMER_CREATE
+int setup_timer(struct ul_timer *timer,
+ struct itimerval *timeout,
+ void (*timeout_handler)(int, siginfo_t *, void *))
+{
+ time_t sec = timeout->it_value.tv_sec;
+ long usec = timeout->it_value.tv_usec;
+ struct sigaction sig_a;
+ static struct sigevent sig_e = {
+ .sigev_notify = SIGEV_SIGNAL,
+ .sigev_signo = SIGALRM
+ };
+ struct itimerspec val = {
+ .it_value.tv_sec = sec,
+ .it_value.tv_nsec = usec * 1000,
+ .it_interval.tv_sec = sec / 100,
+ .it_interval.tv_nsec = (sec ? sec % 100 : 1) * 10*1000*1000
+ };
+
+ if (sigemptyset(&sig_a.sa_mask))
+ return 1;
+
+ sig_a.sa_flags = SA_SIGINFO;
+ sig_a.sa_sigaction = timeout_handler;
+
+ if (sigaction(SIGALRM, &sig_a, NULL))
+ return 1;
+ if (timer_create(CLOCK_MONOTONIC, &sig_e, &timer->t_id))
+ return 1;
+ if (timer_settime(timer->t_id, 0, &val, NULL))
+ return 1;
+ return 0;
+}
+void cancel_timer(struct ul_timer *timer)
+{
+ timer_delete(timer->t_id);
+}
+
+#else /* !HAVE_TIMER_CREATE */
+
+int setup_timer(struct ul_timer *timer,
+ struct itimerval *timeout,
+ void (*timeout_handler)(int, siginfo_t *, void *))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ memset(timer, 0, sizeof(*timer));
+
+ sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
+ sa.sa_sigaction = timeout_handler;
+
+ if (sigaction(SIGALRM, &sa, &timer->old_sa))
+ return 1;
+ if (setitimer(ITIMER_REAL, timeout, &timer->old_timer) != 0)
+ return 1;
+ return 0;
+}
+
+void cancel_timer(struct ul_timer *timer)
+{
+ setitimer(ITIMER_REAL, &timer->old_timer, NULL);
+ sigaction(SIGALRM, &timer->old_sa, NULL);
+
+}
+#endif /* !HAVE_TIMER_CREATE */
diff --git a/utils/lib/timeutils.c b/utils/lib/timeutils.c
new file mode 100644
index 0000000..8b443cd
--- /dev/null
+++ b/utils/lib/timeutils.c
@@ -0,0 +1,611 @@
+/***
+ First set of functions in this file are part of systemd, and were
+ copied to util-linux at August 2013.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with util-linux; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "timeutils.h"
+
+#define WHITESPACE " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+static int parse_sec(const char *t, usec_t *usec)
+{
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "seconds", USEC_PER_SEC },
+ { "second", USEC_PER_SEC },
+ { "sec", USEC_PER_SEC },
+ { "s", USEC_PER_SEC },
+ { "minutes", USEC_PER_MINUTE },
+ { "minute", USEC_PER_MINUTE },
+ { "min", USEC_PER_MINUTE },
+ { "months", USEC_PER_MONTH },
+ { "month", USEC_PER_MONTH },
+ { "msec", USEC_PER_MSEC },
+ { "ms", USEC_PER_MSEC },
+ { "m", USEC_PER_MINUTE },
+ { "hours", USEC_PER_HOUR },
+ { "hour", USEC_PER_HOUR },
+ { "hr", USEC_PER_HOUR },
+ { "h", USEC_PER_HOUR },
+ { "days", USEC_PER_DAY },
+ { "day", USEC_PER_DAY },
+ { "d", USEC_PER_DAY },
+ { "weeks", USEC_PER_WEEK },
+ { "week", USEC_PER_WEEK },
+ { "w", USEC_PER_WEEK },
+ { "years", USEC_PER_YEAR },
+ { "year", USEC_PER_YEAR },
+ { "y", USEC_PER_YEAR },
+ { "usec", 1ULL },
+ { "us", 1ULL },
+ { "", USEC_PER_SEC }, /* default is sec */
+ };
+
+ const char *p;
+ usec_t r = 0;
+ int something = FALSE;
+
+ assert(t);
+ assert(usec);
+
+ p = t;
+ for (;;) {
+ long long l, z = 0;
+ char *e;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno > 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ARRAY_SIZE(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ usec_t k = (usec_t) z * table[i].usec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (usec_t) l *table[i].usec + k;
+ p = e + strlen(table[i].suffix);
+
+ something = TRUE;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(table))
+ return -EINVAL;
+
+ }
+
+ *usec = r;
+
+ return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec)
+{
+ static const struct {
+ const char *name;
+ const int nr;
+ } day_nr[] = {
+ { "Sunday", 0 },
+ { "Sun", 0 },
+ { "Monday", 1 },
+ { "Mon", 1 },
+ { "Tuesday", 2 },
+ { "Tue", 2 },
+ { "Wednesday", 3 },
+ { "Wed", 3 },
+ { "Thursday", 4 },
+ { "Thu", 4 },
+ { "Friday", 5 },
+ { "Fri", 5 },
+ { "Saturday", 6 },
+ { "Sat", 6 },
+ };
+
+ const char *k;
+ struct tm tm, copy;
+ time_t x;
+ usec_t plus = 0, minus = 0, ret;
+ int r, weekday = -1;
+ unsigned i;
+
+ /*
+ * Allowed syntaxes:
+ *
+ * 2012-09-22 16:34:22
+ * 2012-09-22T16:34:22
+ * 2012-09-22 16:34 (seconds will be set to 0)
+ * 2012-09-22 (time will be set to 00:00:00)
+ * 16:34:22 (date will be set to today)
+ * 16:34 (date will be set to today, seconds to 0)
+ * now
+ * yesterday (time is set to 00:00:00)
+ * today (time is set to 00:00:00)
+ * tomorrow (time is set to 00:00:00)
+ * +5min
+ * -5days
+ *
+ */
+
+ assert(t);
+ assert(usec);
+
+ x = time(NULL);
+ localtime_r(&x, &tm);
+ tm.tm_isdst = -1;
+
+ if (streq(t, "now"))
+ goto finish;
+
+ else if (streq(t, "today")) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "yesterday")) {
+ tm.tm_mday--;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "tomorrow")) {
+ tm.tm_mday++;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (t[0] == '+') {
+
+ r = parse_sec(t + 1, &plus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ } else if (t[0] == '-') {
+
+ r = parse_sec(t + 1, &minus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+
+ } else if (endswith(t, " ago")) {
+ char *z;
+
+ z = strndup(t, strlen(t) - 4);
+ if (!z)
+ return -ENOMEM;
+
+ r = parse_sec(z, &minus);
+ free(z);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
+ size_t skip;
+
+ if (!startswith_no_case(t, day_nr[i].name))
+ continue;
+
+ skip = strlen(day_nr[i].name);
+ if (t[skip] != ' ')
+ continue;
+
+ weekday = day_nr[i].nr;
+ t += skip + 1;
+ break;
+ }
+
+ copy = tm;
+ k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y%m%d%H%M%S", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ return -EINVAL;
+
+ finish:
+ x = mktime(&tm);
+ if (x == (time_t)-1)
+ return -EINVAL;
+
+ if (weekday >= 0 && tm.tm_wday != weekday)
+ return -EINVAL;
+
+ ret = (usec_t) x *USEC_PER_SEC;
+
+ ret += plus;
+ if (ret > minus)
+ ret -= minus;
+ else
+ ret = 0;
+
+ *usec = ret;
+
+ return 0;
+}
+
+/* Returns the difference in seconds between its argument and GMT. If if TP is
+ * invalid or no DST information is available default to UTC, that is, zero.
+ * tzset is called so, for example, 'TZ="UTC" hwclock' will work as expected.
+ * Derived from glibc/time/strftime_l.c
+ */
+int get_gmtoff(const struct tm *tp)
+{
+ if (tp->tm_isdst < 0)
+ return 0;
+
+#if HAVE_TM_GMTOFF
+ return tp->tm_gmtoff;
+#else
+ struct tm tm;
+ struct tm gtm;
+ struct tm ltm = *tp;
+ time_t lt;
+
+ tzset();
+ lt = mktime(&ltm);
+ /* Check if mktime returning -1 is an error or a valid time_t */
+ if (lt == (time_t) -1) {
+ if (! localtime_r(&lt, &tm)
+ || ((ltm.tm_sec ^ tm.tm_sec)
+ | (ltm.tm_min ^ tm.tm_min)
+ | (ltm.tm_hour ^ tm.tm_hour)
+ | (ltm.tm_mday ^ tm.tm_mday)
+ | (ltm.tm_mon ^ tm.tm_mon)
+ | (ltm.tm_year ^ tm.tm_year)))
+ return 0;
+ }
+
+ if (! gmtime_r(&lt, &gtm))
+ return 0;
+
+ /* Calculate the GMT offset, that is, the difference between the
+ * TP argument (ltm) and GMT (gtm).
+ *
+ * Compute intervening leap days correctly even if year is negative.
+ * Take care to avoid int overflow in leap day calculations, but it's OK
+ * to assume that A and B are close to each other.
+ */
+ int a4 = (ltm.tm_year >> 2) + (1900 >> 2) - ! (ltm.tm_year & 3);
+ int b4 = (gtm.tm_year >> 2) + (1900 >> 2) - ! (gtm.tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+
+ int years = ltm.tm_year - gtm.tm_year;
+ int days = (365 * years + intervening_leap_days
+ + (ltm.tm_yday - gtm.tm_yday));
+
+ return (60 * (60 * (24 * days + (ltm.tm_hour - gtm.tm_hour))
+ + (ltm.tm_min - gtm.tm_min)) + (ltm.tm_sec - gtm.tm_sec));
+#endif
+}
+
+static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
+{
+ char *p = buf;
+ int len;
+
+ if (flags & ISO_DATE) {
+ len = snprintf(p, bufsz, "%4ld-%.2d-%.2d",
+ tm->tm_year + (long) 1900,
+ tm->tm_mon + 1, tm->tm_mday);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+ }
+
+ if ((flags & ISO_DATE) && (flags & ISO_TIME)) {
+ if (bufsz < 1)
+ goto err;
+ *p++ = (flags & ISO_T) ? 'T' : ' ';
+ bufsz--;
+ }
+
+ if (flags & ISO_TIME) {
+ len = snprintf(p, bufsz, "%02d:%02d:%02d", tm->tm_hour,
+ tm->tm_min, tm->tm_sec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+ }
+
+ if (flags & ISO_DOTUSEC) {
+ len = snprintf(p, bufsz, ".%06ld", (long) usec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+
+ } else if (flags & ISO_COMMAUSEC) {
+ len = snprintf(p, bufsz, ",%06ld", (long) usec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+ }
+
+ if (flags & ISO_TIMEZONE) {
+ int tmin = get_gmtoff(tm) / 60;
+ int zhour = tmin / 60;
+ int zmin = abs(tmin % 60);
+ len = snprintf(p, bufsz, "%+03d:%02d", zhour,zmin);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ }
+ return 0;
+ err:
+ warnx(_("format_iso_time: buffer overflow."));
+ return -1;
+}
+
+/* timeval to ISO 8601 */
+int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
+{
+ struct tm tm;
+ struct tm *rc;
+
+ if (flags & ISO_GMTIME)
+ rc = gmtime_r(&tv->tv_sec, &tm);
+ else
+ rc = localtime_r(&tv->tv_sec, &tm);
+
+ if (rc)
+ return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
+
+ warnx(_("time %ld is out of range."), tv->tv_sec);
+ return -1;
+}
+
+/* struct tm to ISO 8601 */
+int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
+{
+ return format_iso_time(tm, 0, flags, buf, bufsz);
+}
+
+/* time_t to ISO 8601 */
+int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz)
+{
+ struct tm tm;
+ struct tm *rc;
+
+ if (flags & ISO_GMTIME)
+ rc = gmtime_r(t, &tm);
+ else
+ rc = localtime_r(t, &tm);
+
+ if (rc)
+ return format_iso_time(&tm, 0, flags, buf, bufsz);
+
+ warnx(_("time %ld is out of range."), (long)t);
+ return -1;
+}
+
+/* relative time functions */
+static inline int time_is_thisyear(struct tm const *const tm,
+ struct tm const *const tmnow)
+{
+ return tm->tm_year == tmnow->tm_year;
+}
+
+static inline int time_is_today(struct tm const *const tm,
+ struct tm const *const tmnow)
+{
+ return (tm->tm_yday == tmnow->tm_yday &&
+ time_is_thisyear(tm, tmnow));
+}
+
+int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, size_t bufsz)
+{
+ struct tm tm, tmnow;
+ int rc = 0;
+
+ if (now->tv_sec == 0)
+ gettimeofday(now, NULL);
+
+ localtime_r(t, &tm);
+ localtime_r(&now->tv_sec, &tmnow);
+
+ if (time_is_today(&tm, &tmnow)) {
+ rc = snprintf(buf, bufsz, "%02d:%02d", tm.tm_hour, tm.tm_min);
+ if (rc < 0 || (size_t) rc > bufsz)
+ return -1;
+ rc = 1;
+
+ } else if (time_is_thisyear(&tm, &tmnow)) {
+ if (flags & UL_SHORTTIME_THISYEAR_HHMM)
+ rc = strftime(buf, bufsz, "%b%d/%H:%M", &tm);
+ else
+ rc = strftime(buf, bufsz, "%b%d", &tm);
+ } else
+ rc = strftime(buf, bufsz, "%Y-%b%d", &tm);
+
+ return rc <= 0 ? -1 : 0;
+}
+
+#ifndef HAVE_TIMEGM
+time_t timegm(struct tm *tm)
+{
+ const char *zone = getenv("TZ");
+ time_t ret;
+
+ setenv("TZ", "", 1);
+ tzset();
+ ret = mktime(tm);
+ if (zone)
+ setenv("TZ", zone, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+#endif /* HAVE_TIMEGM */
+
+#ifdef TEST_PROGRAM_TIMEUTILS
+
+int main(int argc, char *argv[])
+{
+ struct timeval tv = { 0 };
+ char buf[ISO_BUFSIZ];
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s [<time> [<usec>]] | [--timestamp <str>]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strcmp(argv[1], "--timestamp") == 0) {
+ usec_t usec;
+
+ parse_timestamp(argv[2], &usec);
+ tv.tv_sec = (time_t) (usec / 1000000);
+ tv.tv_usec = usec % 1000000;
+ } else {
+ tv.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>");
+ }
+
+ strtimeval_iso(&tv, ISO_DATE, buf, sizeof(buf));
+ printf("Date: '%s'\n", buf);
+
+ strtimeval_iso(&tv, ISO_TIME, buf, sizeof(buf));
+ printf("Time: '%s'\n", buf);
+
+ strtimeval_iso(&tv, 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));
+ printf("Zone: '%s'\n", buf);
+
+ return EXIT_SUCCESS;
+}
+
+#endif /* TEST_PROGRAM_TIMEUTILS */
diff --git a/utils/lib/ttyutils.c b/utils/lib/ttyutils.c
new file mode 100644
index 0000000..7064565
--- /dev/null
+++ b/utils/lib/ttyutils.c
@@ -0,0 +1,152 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "ttyutils.h"
+
+
+static int get_env_int(const char *name)
+{
+ const char *cp = getenv(name);
+
+ if (cp) {
+ char *end = NULL;
+ long x;
+
+ errno = 0;
+ x = strtol(cp, &end, 10);
+
+ if (errno == 0 && end && *end == '\0' && end > cp &&
+ x > 0 && x <= INT_MAX)
+ return x;
+ }
+
+ return -1;
+}
+
+int get_terminal_dimension(int *cols, int *lines)
+{
+ int c = 0, l = 0;
+
+#if defined(TIOCGWINSZ)
+ struct winsize w_win;
+ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w_win) == 0) {
+ c = w_win.ws_col;
+ l = w_win.ws_row;
+ }
+#elif defined(TIOCGSIZE)
+ struct ttysize t_win;
+ if (ioctl (STDOUT_FILENO, TIOCGSIZE, &t_win) == 0) {
+ c = t_win.ts_cols;
+ l = t_win.ts_lines;
+ }
+#endif
+ if (cols) {
+ if (c <= 0)
+ c = get_env_int("COLUMNS");
+ *cols = c;
+ }
+ if (lines) {
+ if (l <= 0)
+ l = get_env_int("LINES");
+ *lines = l;
+ }
+ return 0;
+}
+
+int get_terminal_width(int default_width)
+{
+ int width = 0;
+
+ get_terminal_dimension(&width, NULL);
+
+ return width > 0 ? width : default_width;
+}
+
+int get_terminal_stdfd(void)
+{
+ if (isatty(STDIN_FILENO))
+ return STDIN_FILENO;
+ if (isatty(STDOUT_FILENO))
+ return STDOUT_FILENO;
+ if (isatty(STDERR_FILENO))
+ return STDERR_FILENO;
+
+ return -EINVAL;
+}
+
+int get_terminal_name(const char **path,
+ const char **name,
+ const char **number)
+{
+ const char *tty;
+ const char *p;
+ int fd;
+
+
+ if (name)
+ *name = NULL;
+ if (path)
+ *path = NULL;
+ if (number)
+ *number = NULL;
+
+ fd = get_terminal_stdfd();
+ if (fd < 0)
+ return fd; /* error */
+
+ tty = ttyname(fd);
+ if (!tty)
+ return -1;
+
+ if (path)
+ *path = tty;
+ if (name || number)
+ tty = strncmp(tty, "/dev/", 5) == 0 ? tty + 5 : tty;
+ if (name)
+ *name = tty;
+ if (number) {
+ for (p = tty; p && *p; p++) {
+ if (isdigit(*p)) {
+ *number = p;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int get_terminal_type(const char **type)
+{
+ *type = getenv("TERM");
+ if (*type)
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef TEST_PROGRAM_TTYUTILS
+# include <stdlib.h>
+int main(void)
+{
+ const char *path, *name, *num;
+ 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);
+ }
+ get_terminal_dimension(&c, &l);
+ fprintf(stderr, "tty cols: %d\n", c);
+ fprintf(stderr, "tty lines: %d\n", l);
+
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM_TTYUTILS */
diff --git a/utils/libsmartcols/CMakeLists.txt b/utils/libsmartcols/CMakeLists.txt
new file mode 100644
index 0000000..c8deb72
--- /dev/null
+++ b/utils/libsmartcols/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop-utils-libsmartcols)
+
+add_library(libsmartcols STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/buffer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/calculate.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/cell.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/column.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/fput.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/grouping.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/init.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/iter.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/line.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/print-api.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/print.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/symbols.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/table.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/version.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/walk.c)
+target_include_directories(libsmartcols PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
+target_link_libraries(libsmartcols LINK_PUBLIC libcommon)
diff --git a/utils/libsmartcols/src/buffer.c b/utils/libsmartcols/src/buffer.c
new file mode 100644
index 0000000..d376e8f
--- /dev/null
+++ b/utils/libsmartcols/src/buffer.c
@@ -0,0 +1,152 @@
+
+#include "smartcolsP.h"
+#include "mbsalign.h"
+
+/* This is private struct to work with output data */
+struct libscols_buffer {
+ char *begin; /* begin of the buffer */
+ char *cur; /* current end of the buffer */
+ char *encdata; /* encoded buffer mbs_safe_encode() */
+
+ size_t bufsz; /* size of the buffer */
+ size_t art_idx; /* begin of the tree ascii art or zero */
+};
+
+struct libscols_buffer *new_buffer(size_t sz)
+{
+ struct libscols_buffer *buf = malloc(sz + sizeof(struct libscols_buffer));
+
+ if (!buf)
+ return NULL;
+
+ buf->cur = buf->begin = ((char *) buf) + sizeof(struct libscols_buffer);
+ buf->encdata = NULL;
+ buf->bufsz = sz;
+
+ DBG(BUFF, ul_debugobj(buf, "alloc (size=%zu)", sz));
+ return buf;
+}
+
+void free_buffer(struct libscols_buffer *buf)
+{
+ if (!buf)
+ return;
+ DBG(BUFF, ul_debugobj(buf, "dealloc"));
+ free(buf->encdata);
+ free(buf);
+}
+
+int buffer_reset_data(struct libscols_buffer *buf)
+{
+ if (!buf)
+ return -EINVAL;
+
+ /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
+ buf->begin[0] = '\0';
+ buf->cur = buf->begin;
+ buf->art_idx = 0;
+ return 0;
+}
+
+int buffer_append_data(struct libscols_buffer *buf, const char *str)
+{
+ size_t maxsz, sz;
+
+ if (!buf)
+ return -EINVAL;
+ if (!str || !*str)
+ return 0;
+
+ sz = strlen(str);
+ maxsz = buf->bufsz - (buf->cur - buf->begin);
+
+ if (maxsz <= sz)
+ return -EINVAL;
+ memcpy(buf->cur, str, sz + 1);
+ buf->cur += sz;
+ return 0;
+}
+
+int buffer_append_ntimes(struct libscols_buffer *buf, size_t n, const char *str)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ int rc = buffer_append_data(buf, str);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int buffer_set_data(struct libscols_buffer *buf, const char *str)
+{
+ int rc = buffer_reset_data(buf);
+ return rc ? rc : buffer_append_data(buf, str);
+}
+
+/* save the current buffer position to art_idx */
+void buffer_set_art_index(struct libscols_buffer *buf)
+{
+ if (buf) {
+ buf->art_idx = buf->cur - buf->begin;
+ /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
+ }
+}
+
+char *buffer_get_data(struct libscols_buffer *buf)
+{
+ return buf ? buf->begin : NULL;
+}
+
+size_t buffer_get_size(struct libscols_buffer *buf)
+{
+ return buf ? buf->bufsz : 0;
+}
+
+/* encode data by mbs_safe_encode() to avoid control and non-printable chars */
+char *buffer_get_safe_data(struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ size_t *cells,
+ const char *safechars)
+{
+ char *data = buffer_get_data(buf);
+ char *res = NULL;
+
+ if (!data)
+ goto nothing;
+
+ if (!buf->encdata) {
+ buf->encdata = malloc(mbs_safe_encode_size(buf->bufsz) + 1);
+ if (!buf->encdata)
+ goto nothing;
+ }
+
+ if (scols_table_is_noencoding(tb)) {
+ *cells = mbs_width(data);
+ strcpy(buf->encdata, data);
+ res = buf->encdata;
+ } else {
+ res = mbs_safe_encode_to_buffer(data, cells, buf->encdata, safechars);
+ }
+
+ if (!res || !*cells || *cells == (size_t) -1)
+ goto nothing;
+ return res;
+nothing:
+ *cells = 0;
+ return NULL;
+}
+
+/* returns size in bytes of the ascii art (according to art_idx) in safe encoding */
+size_t buffer_get_safe_art_size(struct libscols_buffer *buf)
+{
+ char *data = buffer_get_data(buf);
+ size_t bytes = 0;
+
+ if (!data || !buf->art_idx)
+ return 0;
+
+ mbs_safe_nwidth(data, buf->art_idx, &bytes);
+ return bytes;
+}
diff --git a/utils/libsmartcols/src/calculate.c b/utils/libsmartcols/src/calculate.c
new file mode 100644
index 0000000..b6137fd
--- /dev/null
+++ b/utils/libsmartcols/src/calculate.c
@@ -0,0 +1,454 @@
+#include "smartcolsP.h"
+#include "mbsalign.h"
+
+static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
+{
+ if (scols_column_is_hidden(cl)) {
+ DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
+ return;
+ }
+
+ DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
+ "hint=%d, avg=%zu, max=%zu, min=%zu, "
+ "extreme=%s %s",
+
+ cl->header.data, cl->seqnum, cl->width,
+ cl->width_hint > 1 ? (int) cl->width_hint :
+ (int) (cl->width_hint * tb->termwidth),
+ cl->width_avg,
+ cl->width_max,
+ cl->width_min,
+ cl->is_extreme ? "yes" : "not",
+ cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
+}
+
+static void dbg_columns(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_column *cl;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0)
+ dbg_column(tb, cl);
+}
+
+static int count_cell_width(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct libscols_buffer *buf)
+{
+ size_t len;
+ char *data;
+ int rc;
+
+ rc = __cell_to_buffer(tb, ln, cl, buf);
+ if (rc)
+ return rc;
+
+ data = buffer_get_data(buf);
+ 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;
+ cl->width_max = max(len, cl->width_max);
+
+ if (cl->is_extreme && cl->width_avg && len > cl->width_avg * 2)
+ return 0;
+
+ if (scols_column_is_noextremes(cl)) {
+ cl->extreme_sum += len;
+ cl->extreme_count++;
+ }
+ cl->width = max(len, cl->width);
+ if (scols_column_is_tree(cl)) {
+ size_t treewidth = buffer_get_safe_art_size(buf);
+ cl->width_treeart = max(cl->width_treeart, treewidth);
+ }
+ return 0;
+}
+
+
+static int walk_count_cell_width(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ void *data)
+{
+ return count_cell_width(tb, ln, cl, (struct libscols_buffer *) data);
+}
+
+/*
+ * This function counts column width.
+ *
+ * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
+ * two times. The first pass counts the width and average width. If the column
+ * contains fields that are too large (a width greater than 2 * average) then
+ * the column is marked as "extreme". In the second pass all extreme fields
+ * are ignored and the column width is counted from non-extreme fields only.
+ */
+static int count_column_width(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_buffer *buf)
+{
+ int rc = 0, no_header = 0;
+
+ assert(tb);
+ assert(cl);
+
+ cl->width = 0;
+ if (!cl->width_min) {
+ const char *data;
+
+ if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
+ cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
+ if (cl->width_min && !is_last_column(cl))
+ cl->width_min--;
+ }
+
+ data = scols_cell_get_data(&cl->header);
+ if (data) {
+ size_t len = scols_table_is_noencoding(tb) ?
+ mbs_width(data) : mbs_safe_width(data);
+ cl->width_min = max(cl->width_min, len);
+ } else
+ no_header = 1;
+
+ if (!cl->width_min)
+ cl->width_min = 1;
+ }
+
+ if (scols_table_is_tree(tb)) {
+ /* Count width for tree */
+ rc = scols_walk_tree(tb, cl, walk_count_cell_width, (void *) buf);
+ if (rc)
+ goto done;
+ } else {
+ /* Count width for list */
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ rc = count_cell_width(tb, ln, cl, buf);
+ if (rc)
+ goto done;
+ }
+ }
+
+ if (scols_column_is_tree(cl) && has_groups(tb)) {
+ /* We don't fill buffer with groups tree ascii art during width
+ * calculation. The print function only enlarge grpset[] and we
+ * calculate final width from grpset_size.
+ */
+ size_t gprwidth = tb->grpset_size + 1;
+ cl->width_treeart += gprwidth;
+ cl->width_max += gprwidth;
+ cl->width += gprwidth;
+ if (cl->extreme_count)
+ cl->extreme_sum += gprwidth;
+ }
+
+ if (cl->extreme_count && cl->width_avg == 0) {
+ cl->width_avg = cl->extreme_sum / cl->extreme_count;
+ if (cl->width_avg && cl->width_max > cl->width_avg * 2)
+ cl->is_extreme = 1;
+ }
+
+ /* enlarge to minimal width */
+ if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
+ cl->width = cl->width_min;
+
+ /* use absolute size for large columns */
+ else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
+ && cl->width_min < (size_t) cl->width_hint)
+
+ cl->width = (size_t) cl->width_hint;
+
+
+ /* Column without header and data, set minimal size to zero (default is 1) */
+ if (cl->width_max == 0 && no_header && cl->width_min == 1 && cl->width <= 1)
+ cl->width = cl->width_min = 0;
+
+done:
+ ON_DBG(COL, dbg_column(tb, cl));
+ return rc;
+}
+
+/*
+ * This is core of the scols_* voodoo...
+ */
+int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+ size_t width = 0, width_min = 0; /* output width */
+ int stage, rc = 0;
+ int extremes = 0, group_ncolumns = 0;
+ size_t colsepsz;
+
+
+ DBG(TAB, ul_debugobj(tb, "-----calculate-(termwidth=%zu)-----", tb->termwidth));
+ tb->is_dummy_print = 1;
+
+ colsepsz = scols_table_is_noencoding(tb) ?
+ mbs_width(colsep(tb)) :
+ mbs_safe_width(colsep(tb));
+
+ if (has_groups(tb))
+ group_ncolumns = 1;
+
+ /* set basic columns width
+ */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ int is_last;
+
+ if (scols_column_is_hidden(cl))
+ continue;
+
+ /* we print groups chart only for the for the first tree column */
+ if (scols_column_is_tree(cl) && group_ncolumns == 1) {
+ cl->is_groups = 1;
+ group_ncolumns++;
+ }
+
+ rc = count_column_width(tb, cl, buf);
+ if (rc)
+ goto done;
+
+ is_last = is_last_column(cl);
+
+ width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
+ width_min += cl->width_min + (is_last ? 0 : colsepsz);
+ if (cl->is_extreme)
+ extremes++;
+ }
+
+ if (!tb->is_term) {
+ DBG(TAB, ul_debugobj(tb, " non-terminal output"));
+ goto done;
+ }
+
+ /* be paranoid */
+ if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
+ DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (width_min > tb->termwidth
+ && scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ width_min--;
+ cl->width_min--;
+ }
+ DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
+ }
+
+ /* reduce columns with extreme fields */
+ if (width > tb->termwidth && extremes) {
+ DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ size_t org_width;
+
+ if (!cl->is_extreme || scols_column_is_hidden(cl))
+ continue;
+
+ org_width = cl->width;
+ rc = count_column_width(tb, cl, buf);
+ if (rc)
+ goto done;
+
+ if (org_width > cl->width)
+ width -= org_width - cl->width;
+ else
+ extremes--; /* hmm... nothing reduced */
+ }
+ }
+
+ if (width < tb->termwidth) {
+ if (extremes) {
+ DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
+
+ /* enlarge the first extreme column */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ size_t add;
+
+ if (!cl->is_extreme || scols_column_is_hidden(cl))
+ continue;
+
+ /* this column is too large, ignore?
+ if (cl->width_max - cl->width >
+ (tb->termwidth - width))
+ continue;
+ */
+
+ add = tb->termwidth - width;
+ if (add && cl->width + add > cl->width_max)
+ add = cl->width_max - cl->width;
+
+ cl->width += add;
+ width += add;
+
+ if (width == tb->termwidth)
+ break;
+ }
+ }
+
+ if (width < tb->termwidth && scols_table_is_maxout(tb)) {
+ DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
+
+ /* try enlarging all columns */
+ while (width < tb->termwidth) {
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ cl->width++;
+ width++;
+ if (width == tb->termwidth)
+ break;
+ }
+ }
+ } else if (width < tb->termwidth) {
+ /* enlarge the last column */
+ struct libscols_column *col = list_entry(
+ tb->tb_columns.prev, struct libscols_column, cl_columns);
+
+ DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
+
+ if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
+ col->width += tb->termwidth - width;
+ width = tb->termwidth;
+ }
+ }
+ }
+
+ /* bad, we have to reduce output width, this is done in three stages:
+ *
+ * 1) trunc relative with trunc flag if the column width is greater than
+ * expected column width (it means "width_hint * terminal_width").
+ *
+ * 2) trunc all with trunc flag
+ *
+ * 3) trunc relative without trunc flag
+ *
+ * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
+ * interpreted as SCOLS_FL_TRUNC.
+ */
+ for (stage = 1; width > tb->termwidth && stage <= 3; ) {
+ size_t org_width = width;
+
+ DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
+ stage, width, tb->termwidth));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+
+ int trunc_flag = 0;
+
+ DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
+ cl->header.data, cl->width, cl->width_treeart));
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (width <= tb->termwidth)
+ break;
+
+ /* never truncate if already minimal width */
+ if (cl->width == cl->width_min)
+ continue;
+
+ /* never truncate the tree */
+ if (scols_column_is_tree(cl) && width <= cl->width_treeart)
+ continue;
+
+ /* nothing to truncate */
+ if (cl->width == 0)
+ continue;
+
+ trunc_flag = scols_column_is_trunc(cl)
+ || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
+
+ switch (stage) {
+ /* #1 stage - trunc relative with TRUNC flag */
+ case 1:
+ if (!trunc_flag) /* ignore: missing flag */
+ break;
+ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
+ break;
+ if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)"));
+ cl->width--;
+ width--;
+ break;
+
+ /* #2 stage - trunc all with TRUNC flag */
+ case 2:
+ if (!trunc_flag) /* ignore: missing flag */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (all with flag)"));
+ cl->width--;
+ width--;
+ break;
+
+ /* #3 stage - trunc relative without flag */
+ case 3:
+ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)"));
+ cl->width--;
+ width--;
+ break;
+ }
+
+ /* hide zero width columns */
+ if (cl->width == 0)
+ cl->flags |= SCOLS_FL_HIDDEN;
+ }
+
+ /* the current stage is without effect, go to the next */
+ if (org_width == width)
+ stage++;
+ }
+
+ /* ignore last column(s) or force last column to be truncated if
+ * nowrap mode enabled */
+ if (tb->no_wrap && width > tb->termwidth) {
+ scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (width <= tb->termwidth)
+ break;
+ if (width - cl->width < tb->termwidth) {
+ size_t r = width - tb->termwidth;
+
+ cl->flags |= SCOLS_FL_TRUNC;
+ cl->width -= r;
+ width -= r;
+ } else {
+ cl->flags |= SCOLS_FL_HIDDEN;
+ width -= cl->width + colsepsz;
+ }
+ }
+ }
+done:
+ tb->is_dummy_print = 0;
+ DBG(TAB, ul_debugobj(tb, "-----final width: %zu (rc=%d)-----", width, rc));
+ ON_DBG(TAB, dbg_columns(tb));
+
+ return rc;
+}
diff --git a/utils/libsmartcols/src/cell.c b/utils/libsmartcols/src/cell.c
new file mode 100644
index 0000000..4cd6e59
--- /dev/null
+++ b/utils/libsmartcols/src/cell.c
@@ -0,0 +1,257 @@
+/*
+ * cell.c - functions for table handling at the cell level
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: cell
+ * @title: Cell
+ * @short_description: container for your data
+ *
+ * An API to access and modify per-cell data and information. Note that cell is
+ * always part of the line. If you destroy (un-reference) a line than it
+ * destroys all line cells too.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+/*
+ * The cell has no ref-counting, free() and new() functions. All is
+ * handled by libscols_line.
+ */
+
+/**
+ * scols_reset_cell:
+ * @ce: pointer to a struct libscols_cell instance
+ *
+ * Frees the cell's internal data and resets its status.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_reset_cell(struct libscols_cell *ce)
+{
+ if (!ce)
+ return -EINVAL;
+
+ /*DBG(CELL, ul_debugobj(ce, "reset"));*/
+ free(ce->data);
+ free(ce->color);
+ memset(ce, 0, sizeof(*ce));
+ return 0;
+}
+
+/**
+ * scols_cell_set_data:
+ * @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().
+ *
+ * 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);
+}
+
+/**
+ * scols_cell_refer_data:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @data: data (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().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_refer_data(struct libscols_cell *ce, char *data)
+{
+ if (!ce)
+ return -EINVAL;
+ free(ce->data);
+ ce->data = data;
+ return 0;
+}
+
+/**
+ * scols_cell_get_data:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: data in @ce or NULL.
+ */
+const char *scols_cell_get_data(const struct libscols_cell *ce)
+{
+ return ce ? ce->data : NULL;
+}
+
+/**
+ * scols_cell_set_userdata:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @data: private user data
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
+{
+ if (!ce)
+ return -EINVAL;
+ ce->userdata = data;
+ return 0;
+}
+
+/**
+ * scols_cell_get_userdata
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: user data
+ */
+void *scols_cell_get_userdata(struct libscols_cell *ce)
+{
+ return ce->userdata;
+}
+
+/**
+ * scols_cmpstr_cells:
+ * @a: pointer to cell
+ * @b: pointer to cell
+ * @data: unused pointer to private data (defined by API)
+ *
+ * Compares cells data by strcmp(). The function is designed for
+ * scols_column_set_cmpfunc() and scols_sort_table().
+ *
+ * Returns: follows strcmp() return values.
+ */
+int scols_cmpstr_cells(struct libscols_cell *a,
+ struct libscols_cell *b,
+ __attribute__((__unused__)) void *data)
+{
+ const char *adata, *bdata;
+
+ if (a == b)
+ return 0;
+
+ adata = scols_cell_get_data(a);
+ bdata = scols_cell_get_data(b);
+
+ if (adata == NULL && bdata == NULL)
+ return 0;
+ if (adata == NULL)
+ return -1;
+ if (bdata == NULL)
+ return 1;
+ return strcmp(adata, bdata);
+}
+
+/**
+ * scols_cell_set_color:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @color: color name or ESC sequence
+ *
+ * Set the color of @ce to @color.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_color(struct libscols_cell *ce, const char *color)
+{
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(ce, color, color);
+}
+
+/**
+ * scols_cell_get_color:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: the current color of @ce.
+ */
+const char *scols_cell_get_color(const struct libscols_cell *ce)
+{
+ return ce->color;
+}
+
+/**
+ * scols_cell_set_flags:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @flags: SCOLS_CELL_FL_* flags
+ *
+ * Note that cells in the table are always aligned by column flags. The cell
+ * flags are used for table title only (now).
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_flags(struct libscols_cell *ce, int flags)
+{
+ if (!ce)
+ return -EINVAL;
+ ce->flags = flags;
+ return 0;
+}
+
+/**
+ * scols_cell_get_flags:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: the current flags
+ */
+int scols_cell_get_flags(const struct libscols_cell *ce)
+{
+ return ce->flags;
+}
+
+/**
+ * scols_cell_get_alignment:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Since: 2.30
+ *
+ * Returns: SCOLS_CELL_FL_{RIGHT,CELNTER,LEFT}
+ */
+int scols_cell_get_alignment(const struct libscols_cell *ce)
+{
+ if (ce->flags & SCOLS_CELL_FL_RIGHT)
+ return SCOLS_CELL_FL_RIGHT;
+ if (ce->flags & SCOLS_CELL_FL_CENTER)
+ return SCOLS_CELL_FL_CENTER;
+
+ return SCOLS_CELL_FL_LEFT; /* default */
+}
+
+/**
+ * scols_cell_copy_content:
+ * @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.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_copy_content(struct libscols_cell *dest,
+ const struct libscols_cell *src)
+{
+ int rc;
+
+ rc = scols_cell_set_data(dest, scols_cell_get_data(src));
+ if (!rc)
+ rc = scols_cell_set_color(dest, scols_cell_get_color(src));
+ if (!rc)
+ dest->userdata = src->userdata;
+
+ DBG(CELL, ul_debugobj(src, "copy"));
+ return rc;
+}
diff --git a/utils/libsmartcols/src/column.c b/utils/libsmartcols/src/column.c
new file mode 100644
index 0000000..c11df69
--- /dev/null
+++ b/utils/libsmartcols/src/column.c
@@ -0,0 +1,564 @@
+/*
+ * column.c - functions for table handling at the column level
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: column
+ * @title: Column
+ * @short_description: defines output columns formats, headers, etc.
+ *
+ * An API to access and modify per-column data and information.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mbsalign.h"
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_column:
+ *
+ * Allocates space for a new column.
+ *
+ * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
+ */
+struct libscols_column *scols_new_column(void)
+{
+ struct libscols_column *cl;
+
+ cl = calloc(1, sizeof(*cl));
+ if (!cl)
+ return NULL;
+ DBG(COL, ul_debugobj(cl, "alloc"));
+ cl->refcount = 1;
+ INIT_LIST_HEAD(&cl->cl_columns);
+ return cl;
+}
+
+/**
+ * scols_ref_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Increases the refcount of @cl.
+ */
+void scols_ref_column(struct libscols_column *cl)
+{
+ if (cl)
+ cl->refcount++;
+}
+
+/**
+ * scols_unref_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Decreases the refcount of @cl. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_column(struct libscols_column *cl)
+{
+ if (cl && --cl->refcount <= 0) {
+ DBG(COL, ul_debugobj(cl, "dealloc"));
+ list_del(&cl->cl_columns);
+ scols_reset_cell(&cl->header);
+ free(cl->color);
+ free(cl->safechars);
+ free(cl->pending_data_buf);
+ free(cl);
+ }
+}
+
+/**
+ * scols_copy_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Creates a new column and copies @cl's data over to it.
+ *
+ * Returns: a pointer to a new struct libscols_column instance.
+ */
+struct libscols_column *scols_copy_column(const struct libscols_column *cl)
+{
+ struct libscols_column *ret;
+
+ if (!cl)
+ return NULL;
+ ret = scols_new_column();
+ if (!ret)
+ return NULL;
+
+ DBG(COL, ul_debugobj(cl, "copy"));
+
+ if (scols_column_set_color(ret, cl->color))
+ goto err;
+ if (scols_cell_copy_content(&ret->header, &cl->header))
+ goto err;
+
+ ret->width = cl->width;
+ ret->width_min = cl->width_min;
+ ret->width_max = cl->width_max;
+ ret->width_avg = cl->width_avg;
+ ret->width_hint = cl->width_hint;
+ ret->flags = cl->flags;
+ ret->is_extreme = cl->is_extreme;
+ ret->is_groups = cl->is_groups;
+
+ return ret;
+err:
+ scols_unref_column(ret);
+ return NULL;
+}
+
+/**
+ * scols_column_set_whint:
+ * @cl: a pointer to a struct libscols_column instance
+ * @whint: a width hint
+ *
+ * Sets the width hint of column @cl to @whint. See scols_table_new_column().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_whint(struct libscols_column *cl, double whint)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->width_hint = whint;
+ return 0;
+}
+
+/**
+ * scols_column_get_whint:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The width hint of column @cl, a negative value in case of an error.
+ */
+double scols_column_get_whint(const struct libscols_column *cl)
+{
+ return cl->width_hint;
+}
+
+/**
+ * scols_column_set_flags:
+ * @cl: a pointer to a struct libscols_column instance
+ * @flags: a flag mask
+ *
+ * Sets the flags of @cl to @flags.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_flags(struct libscols_column *cl, int flags)
+{
+ if (!cl)
+ return -EINVAL;
+
+ if (cl->table) {
+ if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE))
+ cl->table->ntreecols++;
+ else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE))
+ cl->table->ntreecols--;
+ }
+
+ DBG(COL, ul_debugobj(cl, "setting flags from 0%x to 0%x", cl->flags, flags));
+ cl->flags = flags;
+ return 0;
+}
+
+/**
+ * scols_column_set_json_type:
+ * @cl: a pointer to a struct libscols_column instance
+ * @type: SCOLS_JSON_* type
+ *
+ * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.33
+ */
+int scols_column_set_json_type(struct libscols_column *cl, int type)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->json_type = type;
+ return 0;
+
+}
+
+/**
+ * scols_column_get_json_type:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and
+ * 'n' as "false"; and everything else as "true".
+ *
+ * Returns: JSON type used for formatting or a negative value in case of an error.
+ *
+ * Since: 2.33
+ */
+int scols_column_get_json_type(const struct libscols_column *cl)
+{
+ return cl ? cl->json_type : -EINVAL;
+}
+
+
+/**
+ * scols_column_get_table:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: pointer to the table where columns is used
+ */
+struct libscols_table *scols_column_get_table(const struct libscols_column *cl)
+{
+ return cl->table;
+}
+
+/**
+ * scols_column_get_flags:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The flag mask of @cl, a negative value in case of an error.
+ */
+int scols_column_get_flags(const struct libscols_column *cl)
+{
+ return cl->flags;
+}
+
+/**
+ * scols_column_get_header:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: A pointer to a struct libscols_cell instance, representing the
+ * header info of column @cl or NULL in case of an error.
+ */
+struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
+{
+ return &cl->header;
+}
+
+/**
+ * scols_column_set_color:
+ * @cl: a pointer to a struct libscols_column instance
+ * @color: color name or ESC sequence
+ *
+ * The default color for data cells and column header.
+ *
+ * If you want to set header specific color then use scols_column_get_header()
+ * and scols_cell_set_color().
+ *
+ * If you want to set data cell specific color the use scols_line_get_cell() +
+ * scols_cell_set_color().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_color(struct libscols_column *cl, const char *color)
+{
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(cl, color, color);
+}
+
+/**
+ * scols_column_get_color:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The current color setting of the column @cl.
+ */
+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
+ * @data: string
+ * @userdata: callback private data
+ *
+ * This is built-in function for scols_column_set_wrapfunc(). This function
+ * terminates the current chunk by \0 and returns pointer to the begin of
+ * the next chunk. The chunks are based on \n.
+ *
+ * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
+ *
+ * Returns: next chunk
+ *
+ * Since: 2.29
+ */
+char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
+ char *data,
+ void *userdata __attribute__((unused)))
+{
+ char *p = data ? strchr(data, '\n') : NULL;
+
+ if (p) {
+ *p = '\0';
+ return p + 1;
+ }
+ return NULL;
+}
+
+/**
+ * scols_wrapnl_chunksize:
+ * @cl: a pointer to a struct libscols_column instance
+ * @data: string
+ * @userdata: callback private data
+ *
+ * Analyzes @data and returns size of the largest chunk. The chunks are based
+ * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
+ *
+ * Note that the size has to be based on number of terminal cells rather than
+ * bytes to support multu-byte output.
+ *
+ * Returns: size of the largest chunk.
+ *
+ * Since: 2.29
+ */
+size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
+ const char *data,
+ void *userdata __attribute__((unused)))
+{
+ size_t sum = 0;
+
+ while (data && *data) {
+ const char *p;
+ size_t sz;
+
+ p = strchr(data, '\n');
+ if (p) {
+ sz = cl->table && scols_table_is_noencoding(cl->table) ?
+ mbs_nwidth(data, p - data) :
+ mbs_safe_nwidth(data, p - data, NULL);
+ p++;
+ } else {
+ sz = cl->table && scols_table_is_noencoding(cl->table) ?
+ mbs_width(data) :
+ mbs_safe_width(data);
+ }
+ sum = max(sum, sz);
+ data = p;
+ }
+
+ return sum;
+}
+
+/**
+ * scols_column_set_cmpfunc:
+ * @cl: column
+ * @cmp: pointer to compare function
+ * @data: private data for cmp function
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_cmpfunc(struct libscols_column *cl,
+ int (*cmp)(struct libscols_cell *,
+ struct libscols_cell *,
+ void *),
+ void *data)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->cmpfunc = cmp;
+ cl->cmpfunc_data = data;
+ return 0;
+}
+
+/**
+ * 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_nextchunk: function to return next zero terminated data
+ * @userdata: optional stuff for callbacks
+ *
+ * Extends SCOLS_FL_WRAP and can be used to set custom wrap function. The default
+ * is to wrap by column size, but you can create functions to wrap for example
+ * after \n or after words, etc.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_column_set_wrapfunc(struct libscols_column *cl,
+ size_t (*wrap_chunksize)(const struct libscols_column *,
+ const char *,
+ void *),
+ char * (*wrap_nextchunk)(const struct libscols_column *,
+ char *,
+ void *),
+ void *userdata)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->wrap_nextchunk = wrap_nextchunk;
+ cl->wrap_chunksize = wrap_chunksize;
+ cl->wrapfunc_data = userdata;
+ return 0;
+}
+
+/**
+ * scols_column_set_safechars:
+ * @cl: a pointer to a struct libscols_column instance
+ * @safe: safe characters (e.g. "\n\t")
+ *
+ * Use for bytes you don't want to encode on output. This is for example
+ * necessary if you want to use custom wrap function based on \n, in this case
+ * you have to set "\n" as a safe char.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
+{
+ return strdup_to_struct_member(cl, safechars, safe);
+}
+
+/**
+ * scols_column_get_safechars:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: safe chars
+ *
+ * Since: 2.29
+ */
+const char *scols_column_get_safechars(const struct libscols_column *cl)
+{
+ return cl->safechars;
+}
+
+/**
+ * scols_column_get_width:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Important note: the column width is unknown until library starts printing
+ * (width is calculated before printing). The function is usable for example in
+ * nextchunk() callback specified by scols_column_set_wrapfunc().
+ *
+ * See also scols_column_get_whint(), it returns wanted size (!= final size).
+ *
+ * Returns: column width
+ *
+ * Since: 2.29
+ */
+size_t scols_column_get_width(const struct libscols_column *cl)
+{
+ return cl->width;
+}
+
+/**
+ * scols_column_is_hidden:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag hidden.
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.27
+ */
+int scols_column_is_hidden(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
+}
+
+/**
+ * scols_column_is_trunc:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag trunc.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_trunc(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
+}
+/**
+ * scols_column_is_tree:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag tree.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_tree(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_TREE ? 1 : 0;
+}
+/**
+ * scols_column_is_right:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag right.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_right(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
+}
+/**
+ * scols_column_is_strict_width:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag strict_width.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_strict_width(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
+}
+/**
+ * scols_column_is_noextremes:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag no_extremes.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_noextremes(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
+}
+/**
+ * scols_column_is_wrap:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag wrap.
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.28
+ */
+int scols_column_is_wrap(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
+}
+/**
+ * scols_column_is_customwrap:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.29
+ */
+int scols_column_is_customwrap(const struct libscols_column *cl)
+{
+ return (cl->flags & SCOLS_FL_WRAP)
+ && cl->wrap_chunksize
+ && cl->wrap_nextchunk ? 1 : 0;
+}
diff --git a/utils/libsmartcols/src/fput.c b/utils/libsmartcols/src/fput.c
new file mode 100644
index 0000000..b00c3d8
--- /dev/null
+++ b/utils/libsmartcols/src/fput.c
@@ -0,0 +1,97 @@
+#include "carefulputc.h"
+#include "smartcolsP.h"
+
+void fput_indent(struct libscols_table *tb)
+{
+ int i;
+
+ for (i = 0; i <= tb->indent; i++)
+ fputs(" ", tb->out);
+}
+
+void fput_table_open(struct libscols_table *tb)
+{
+ tb->indent = 0;
+
+ if (scols_table_is_json(tb)) {
+ fputc('{', tb->out);
+ fputs(linesep(tb), tb->out);
+
+ fput_indent(tb);
+ fputs_quoted(tb->name, tb->out);
+ fputs(": [", tb->out);
+ fputs(linesep(tb), tb->out);
+
+ tb->indent++;
+ tb->indent_last_sep = 1;
+ }
+}
+
+void fput_table_close(struct libscols_table *tb)
+{
+ tb->indent--;
+
+ if (scols_table_is_json(tb)) {
+ fput_indent(tb);
+ fputc(']', tb->out);
+ tb->indent--;
+ fputs(linesep(tb), tb->out);
+ fputc('}', tb->out);
+ tb->indent_last_sep = 1;
+ }
+}
+
+void fput_children_open(struct libscols_table *tb)
+{
+ if (scols_table_is_json(tb)) {
+ fputc(',', tb->out);
+ fputs(linesep(tb), tb->out);
+ fput_indent(tb);
+ fputs("\"children\": [", tb->out);
+ }
+ /* between parent and child is separator */
+ fputs(linesep(tb), tb->out);
+ tb->indent_last_sep = 1;
+ tb->indent++;
+ tb->termlines_used++;
+}
+
+void fput_children_close(struct libscols_table *tb)
+{
+ tb->indent--;
+
+ if (scols_table_is_json(tb)) {
+ fput_indent(tb);
+ fputc(']', tb->out);
+ fputs(linesep(tb), tb->out);
+ tb->indent_last_sep = 1;
+ }
+}
+
+void fput_line_open(struct libscols_table *tb)
+{
+ if (scols_table_is_json(tb)) {
+ fput_indent(tb);
+ fputc('{', tb->out);
+ tb->indent_last_sep = 0;
+ }
+ tb->indent++;
+}
+
+void fput_line_close(struct libscols_table *tb, int last, int last_in_table)
+{
+ tb->indent--;
+ if (scols_table_is_json(tb)) {
+ if (tb->indent_last_sep)
+ fput_indent(tb);
+ fputs(last ? "}" : "},", tb->out);
+ if (!tb->no_linesep)
+ fputs(linesep(tb), tb->out);
+
+ } else if (tb->no_linesep == 0 && last_in_table == 0) {
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+
+ tb->indent_last_sep = 1;
+}
diff --git a/utils/libsmartcols/src/grouping.c b/utils/libsmartcols/src/grouping.c
new file mode 100644
index 0000000..0b27cb2
--- /dev/null
+++ b/utils/libsmartcols/src/grouping.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
+ */
+#include "smartcolsP.h"
+
+/**
+ * SECTION: grouping
+ * @title: Grouping
+ * @short_description: lines grouing
+ *
+ * Lines groups manipulation API. The grouping API can be used to create M:N
+ * relations between lines and on tree-like output it prints extra chart to
+ * visualize these relations. The group has unlimited number of members and
+ * group childs. See libsmartcols/sample/grouping* for more details.
+ */
+
+/* Private API */
+void scols_ref_group(struct libscols_group *gr)
+{
+ if (gr)
+ gr->refcount++;
+}
+
+void scols_group_remove_children(struct libscols_group *gr)
+{
+ if (!gr)
+ return;
+
+ while (!list_empty(&gr->gr_children)) {
+ struct libscols_line *ln = list_entry(gr->gr_children.next,
+ struct libscols_line, ln_children);
+
+ DBG(GROUP, ul_debugobj(gr, "remove child"));
+ list_del_init(&ln->ln_children);
+ scols_ref_group(ln->parent_group);
+ ln->parent_group = NULL;
+ scols_unref_line(ln);
+ }
+}
+
+void scols_group_remove_members(struct libscols_group *gr)
+{
+ if (!gr)
+ return;
+
+ while (!list_empty(&gr->gr_members)) {
+ struct libscols_line *ln = list_entry(gr->gr_members.next,
+ struct libscols_line, ln_groups);
+
+ DBG(GROUP, ul_debugobj(gr, "remove member [%p]", ln));
+ list_del_init(&ln->ln_groups);
+
+ scols_unref_group(ln->group);
+ ln->group->nmembers++;
+ ln->group = NULL;
+
+ scols_unref_line(ln);
+ }
+}
+
+/* note group has to be already without members to deallocate */
+void scols_unref_group(struct libscols_group *gr)
+{
+ if (gr && --gr->refcount <= 0) {
+ DBG(GROUP, ul_debugobj(gr, "dealloc"));
+ scols_group_remove_children(gr);
+ list_del(&gr->gr_groups);
+ free(gr);
+ return;
+ }
+}
+
+
+static void groups_fix_members_order(struct libscols_line *ln)
+{
+ struct libscols_iter itr;
+ struct libscols_line *child;
+
+ if (ln->group) {
+ INIT_LIST_HEAD(&ln->ln_groups);
+ list_add_tail(&ln->ln_groups, &ln->group->gr_members);
+ DBG(GROUP, ul_debugobj(ln->group, "fixing member line=%p [%zu/%zu]",
+ ln, ln->group->nmembers,
+ list_count_entries(&ln->group->gr_members)));
+ }
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_child(ln, &itr, &child) == 0)
+ groups_fix_members_order(child);
+
+ /*
+ * We modify gr_members list, so is_last_group_member() does not have
+ * to provide reliable answer, we need to verify by list_count_entries().
+ */
+ if (ln->group
+ && is_last_group_member(ln)
+ && ln->group->nmembers == list_count_entries(&ln->group->gr_members)) {
+
+ DBG(GROUP, ul_debugobj(ln->group, "fixing childs"));
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_group_child(ln, &itr, &child) == 0)
+ groups_fix_members_order(child);
+ }
+}
+
+void scols_groups_fix_members_order(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+ struct libscols_group *gr;
+
+ /* remove all from groups lists */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ while (!list_empty(&gr->gr_members)) {
+ struct libscols_line *line = list_entry(gr->gr_members.next,
+ struct libscols_line, ln_groups);
+ list_del_init(&line->ln_groups);
+ }
+ }
+
+ /* add again to the groups list in order we walk in tree */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent || ln->parent_group)
+ continue;
+ groups_fix_members_order(ln);
+ }
+
+ /* If group child is member of another group *
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ struct libscols_iter xitr;
+ struct libscols_line *child;
+
+ scols_reset_iter(&xitr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_group_child(ln, &xitr, &child) == 0)
+ groups_fix_members_order(child);
+ }
+ */
+}
+
+static inline const char *group_state_to_string(int state)
+{
+ static const char *grpstates[] = {
+ [SCOLS_GSTATE_NONE] = "none",
+ [SCOLS_GSTATE_FIRST_MEMBER] = "1st-member",
+ [SCOLS_GSTATE_MIDDLE_MEMBER] = "middle-member",
+ [SCOLS_GSTATE_LAST_MEMBER] = "last-member",
+ [SCOLS_GSTATE_MIDDLE_CHILD] = "middle-child",
+ [SCOLS_GSTATE_LAST_CHILD] = "last-child",
+ [SCOLS_GSTATE_CONT_MEMBERS] = "continue-members",
+ [SCOLS_GSTATE_CONT_CHILDREN] = "continue-children"
+ };
+
+ assert(state >= 0);
+ assert((size_t) state < ARRAY_SIZE(grpstates));
+
+ return grpstates[state];
+}
+/*
+static void grpset_debug(struct libscols_table *tb, struct libscols_line *ln)
+{
+ size_t i;
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (tb->grpset[i]) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (ln)
+ DBG(LINE, ul_debugobj(ln, "grpset[%zu]: %p %s", i,
+ gr, group_state_to_string(gr->state)));
+ else
+ DBG(LINE, ul_debug("grpset[%zu]: %p %s", i,
+ gr, group_state_to_string(gr->state)));
+ } else if (ln) {
+ DBG(LINE, ul_debugobj(ln, "grpset[%zu]: free", i));
+ } else
+ DBG(LINE, ul_debug("grpset[%zu]: free", i));
+ }
+}
+*/
+static int group_state_for_line(struct libscols_group *gr, struct libscols_line *ln)
+{
+ if (gr->state == SCOLS_GSTATE_NONE &&
+ (ln->group != gr || !is_first_group_member(ln)))
+ /*
+ * NONE is possible to translate to FIRST_MEMBER only, and only if
+ * line group matches with the current group.
+ */
+ return SCOLS_GSTATE_NONE;
+
+ if (ln->group != gr && ln->parent_group != gr) {
+ /* Not our line, continue */
+ if (gr->state == SCOLS_GSTATE_FIRST_MEMBER ||
+ gr->state == SCOLS_GSTATE_MIDDLE_MEMBER ||
+ gr->state == SCOLS_GSTATE_CONT_MEMBERS)
+ return SCOLS_GSTATE_CONT_MEMBERS;
+
+ if (gr->state == SCOLS_GSTATE_LAST_MEMBER ||
+ gr->state == SCOLS_GSTATE_MIDDLE_CHILD ||
+ gr->state == SCOLS_GSTATE_CONT_CHILDREN)
+ return SCOLS_GSTATE_CONT_CHILDREN;
+
+ } else if (ln->group == gr && is_first_group_member(ln)) {
+ return SCOLS_GSTATE_FIRST_MEMBER;
+
+ } else if (ln->group == gr && is_last_group_member(ln)) {
+ return SCOLS_GSTATE_LAST_MEMBER;
+
+ } else if (ln->group == gr && is_group_member(ln)) {
+ return SCOLS_GSTATE_MIDDLE_MEMBER;
+
+ } else if (ln->parent_group == gr && is_last_group_child(ln)) {
+ return SCOLS_GSTATE_LAST_CHILD;
+
+ } else if (ln->parent_group == gr && is_group_child(ln)) {
+ return SCOLS_GSTATE_MIDDLE_CHILD;
+ }
+
+ return SCOLS_GSTATE_NONE;
+}
+
+/*
+ * apply new @state to the chunk (addressed by @xx) of grpset used for the group (@gr)
+ */
+static void grpset_apply_group_state(struct libscols_group **xx, int state, struct libscols_group *gr)
+{
+ size_t i;
+
+ DBG(GROUP, ul_debugobj(gr, " applying state to grpset"));
+
+ /* gr->state holds the old state, @state is the new state
+ */
+ for (i = 0; i < SCOLS_GRPSET_CHUNKSIZ; i++)
+ xx[i] = state == SCOLS_GSTATE_NONE ? NULL : gr;
+
+ gr->state = state;
+}
+
+static struct libscols_group **grpset_locate_freespace(struct libscols_table *tb, int chunks, int prepend)
+{
+ size_t i, avail = 0;
+ struct libscols_group **tmp, **first = NULL;
+ const size_t wanted = chunks * SCOLS_GRPSET_CHUNKSIZ;
+
+ if (!tb->grpset_size)
+ prepend = 0;
+ /*
+ DBG(TAB, ul_debugobj(tb, "orig grpset:"));
+ grpset_debug(tb, NULL);
+ */
+ if (prepend) {
+ for (i = tb->grpset_size - 1; ; i--) {
+ if (tb->grpset[i] == NULL) {
+ first = &tb->grpset[i];
+ avail++;
+ } else
+ avail = 0;
+ if (avail == wanted)
+ goto done;
+ if (i == 0)
+ break;
+ }
+ } else {
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (tb->grpset[i] == NULL) {
+ if (avail == 0)
+ first = &tb->grpset[i];
+ avail++;
+ } else
+ avail = 0;
+ if (avail == wanted)
+ goto done;
+ }
+ }
+
+ 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 *));
+ if (!tmp)
+ return NULL;
+
+ tb->grpset = tmp;
+
+ if (prepend) {
+ DBG(TAB, ul_debugobj(tb, " prepending free space"));
+ char *dest = (char *) tb->grpset;
+
+ memmove( dest + (wanted * sizeof(struct libscols_group *)),
+ tb->grpset,
+ tb->grpset_size * sizeof(struct libscols_group *));
+ first = tb->grpset;
+ } else {
+ first = tb->grpset + tb->grpset_size;
+ }
+
+ memset(first, 0, wanted * sizeof(struct libscols_group *));
+ tb->grpset_size += wanted;
+
+done:
+ /*
+ DBG(TAB, ul_debugobj(tb, "new grpset:"));
+ grpset_debug(tb, NULL);
+ */
+ return first;
+}
+
+static struct libscols_group **grpset_locate_group(struct libscols_table *tb, struct libscols_group *gr)
+{
+ size_t i;
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (gr == tb->grpset[i])
+ return &tb->grpset[i];
+ }
+
+ return NULL;
+}
+
+
+static int grpset_update(struct libscols_table *tb, struct libscols_line *ln, struct libscols_group *gr)
+{
+ struct libscols_group **xx;
+ int state;
+
+ DBG(LINE, ul_debugobj(ln, " group [%p] grpset update [grpset size=%zu]", gr, tb->grpset_size));
+
+ /* new state, note that gr->state still holds the original state */
+ state = group_state_for_line(gr, ln);
+ DBG(LINE, ul_debugobj(ln, " state %s --> %s",
+ group_state_to_string(gr->state),
+ group_state_to_string(state)));
+
+ if (state == SCOLS_GSTATE_FIRST_MEMBER && gr->state != SCOLS_GSTATE_NONE) {
+ DBG(LINE, ul_debugobj(ln, "wrong group initialization (%s)", group_state_to_string(gr->state)));
+ abort();
+ }
+ if (state != SCOLS_GSTATE_NONE && gr->state == SCOLS_GSTATE_LAST_CHILD) {
+ DBG(LINE, ul_debugobj(ln, "wrong group termination (%s)", group_state_to_string(gr->state)));
+ abort();
+ }
+ if (gr->state == SCOLS_GSTATE_LAST_MEMBER &&
+ !(state == SCOLS_GSTATE_LAST_CHILD ||
+ state == SCOLS_GSTATE_CONT_CHILDREN ||
+ state == SCOLS_GSTATE_MIDDLE_CHILD ||
+ state == SCOLS_GSTATE_NONE)) {
+ DBG(LINE, ul_debugobj(ln, "wrong group member->child order"));
+ abort();
+ }
+
+ /* should not happen; probably wrong line... */
+ if (gr->state == SCOLS_GSTATE_NONE && state == SCOLS_GSTATE_NONE)
+ return 0;
+
+ /* locate place in grpset where we draw the group */
+ if (!tb->grpset || gr->state == SCOLS_GSTATE_NONE)
+ xx = grpset_locate_freespace(tb, 1, 1);
+ else
+ xx = grpset_locate_group(tb, gr);
+ if (!xx) {
+ DBG(LINE, ul_debugobj(ln, "failed to locate group or reallocate grpset"));
+ return -ENOMEM;
+ }
+
+ grpset_apply_group_state(xx, state, gr);
+ /*ON_DBG(LINE, grpset_debug(tb, ln));*/
+ return 0;
+}
+
+static int grpset_update_active_groups(struct libscols_table *tb, struct libscols_line *ln)
+{
+ int rc = 0;
+ size_t i;
+ struct libscols_group *last = NULL;
+
+ DBG(LINE, ul_debugobj(ln, " update for active groups"));
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (!gr || last == gr)
+ continue;
+ last = gr;
+ rc = grpset_update(tb, ln, gr);
+ if (rc)
+ break;
+ }
+
+ DBG(LINE, ul_debugobj(ln, " <- active groups updated [rc=%d]", rc));
+ return rc;
+}
+
+int scols_groups_update_grpset(struct libscols_table *tb, struct libscols_line *ln)
+{
+ int rc = 0;
+
+ DBG(LINE, ul_debugobj(ln, " grpset update [line: group=%p, parent_group=%p",
+ ln->group, ln->parent_group));
+
+ rc = grpset_update_active_groups(tb, ln);
+ if (!rc && ln->group && ln->group->state == SCOLS_GSTATE_NONE) {
+ DBG(LINE, ul_debugobj(ln, " introduce a new group"));
+ rc = grpset_update(tb, ln, ln->group);
+ }
+ return rc;
+}
+
+void scols_groups_reset_state(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_group *gr;
+
+ DBG(TAB, ul_debugobj(tb, "reset groups states"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ DBG(GROUP, ul_debugobj(gr, " reset to NONE"));
+ gr->state = SCOLS_GSTATE_NONE;
+ }
+
+ if (tb->grpset) {
+ DBG(TAB, ul_debugobj(tb, " zeroize grpset"));
+ memset(tb->grpset, 0, tb->grpset_size * sizeof(struct libscols_group *));
+ }
+ tb->ngrpchlds_pending = 0;
+}
+
+static void add_member(struct libscols_group *gr, struct libscols_line *ln)
+{
+ DBG(GROUP, ul_debugobj(gr, "add member %p", ln));
+
+ ln->group = gr;
+ gr->nmembers++;
+ scols_ref_group(gr);
+
+ INIT_LIST_HEAD(&ln->ln_groups);
+ list_add_tail(&ln->ln_groups, &gr->gr_members);
+ scols_ref_line(ln);
+}
+
+/*
+ * Returns first group which is ready to print group children.
+ *
+ * This function scans grpset[] in backward order and returns first group
+ * with SCOLS_GSTATE_CONT_CHILDREN or SCOLS_GSTATE_LAST_MEMBER state.
+ */
+struct libscols_group *scols_grpset_get_printable_children(struct libscols_table *tb)
+{
+ size_t i;
+
+ for (i = tb->grpset_size; i > 0; i -= SCOLS_GRPSET_CHUNKSIZ) {
+ struct libscols_group *gr = tb->grpset[i-1];
+
+ if (gr == NULL)
+ continue;
+ if (gr->state == SCOLS_GSTATE_CONT_CHILDREN ||
+ gr->state == SCOLS_GSTATE_LAST_MEMBER)
+ return gr;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * scols_table_group_lines:
+ * @tb: a pointer to a struct libscols_table instance
+ * @ln: new group member
+ * @member: group member
+ * @id: group identifier (unused, not implemented yet), use zero.
+ *
+ * This function add line @ln to group of lines represented by @member. If the
+ * group is not yet defined (@member is not member of any group) than a new one
+ * is allocated.
+ *
+ * The @ln maybe a NULL -- in this case only a new group is allocated if not
+ * defined yet.
+ *
+ * Note that the same line cannot be member of more groups (not implemented
+ * yet). The child of any group can be member of another group.
+ *
+ * The @id is not used for now, use 0. The plan is to use it to support
+ * multi-group membership in future.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_table_group_lines( struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_line *member,
+ __attribute__((__unused__)) int id)
+{
+ struct libscols_group *gr = NULL;
+
+ if (!tb || !member) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (no table or member)"));
+ return -EINVAL;
+ }
+ if (ln) {
+ if (ln->group && !member->group) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (new group, line member of another)"));
+ return -EINVAL;
+ }
+ if (ln->group && member->group && ln->group != member->group) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (groups mismatch bwteen member and line"));
+ return -EINVAL;
+ }
+ }
+
+ gr = member->group;
+
+ /* create a new group */
+ if (!gr) {
+ gr = calloc(1, sizeof(*gr));
+ if (!gr)
+ return -ENOMEM;
+ DBG(GROUP, ul_debugobj(gr, "alloc"));
+ gr->refcount = 1;
+ INIT_LIST_HEAD(&gr->gr_members);
+ INIT_LIST_HEAD(&gr->gr_children);
+ INIT_LIST_HEAD(&gr->gr_groups);
+
+ /* add group to the table */
+ list_add_tail(&gr->gr_groups, &tb->tb_groups);
+
+ /* add the first member */
+ add_member(gr, member);
+ }
+
+ /* add to group */
+ if (ln && !ln->group)
+ add_member(gr, ln);
+
+ return 0;
+}
+
+/**
+ * scols_line_link_group:
+ * @ln: line instance
+ * @member: group member
+ * @id: group identifier (unused, not implemented yet))
+ *
+ * Define @ln as child of group represented by group @member. The line @ln
+ * cannot be child of any other line. It's possible to create group->child or
+ * parent->child relationship, but no both for the same line (child).
+ *
+ * The @id is not used for now, use 0. The plan is to use it to support
+ * multi-group membership in future.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member,
+ __attribute__((__unused__)) int id)
+{
+ if (!ln || !member || !member->group || ln->parent)
+ return -EINVAL;
+
+ if (!list_empty(&ln->ln_children))
+ return -EINVAL;
+
+ DBG(GROUP, ul_debugobj(member->group, "add child"));
+
+ list_add_tail(&ln->ln_children, &member->group->gr_children);
+ scols_ref_line(ln);
+
+ ln->parent_group = member->group;
+ scols_ref_group(member->group);
+
+ return 0;
+}
diff --git a/utils/libsmartcols/src/init.c b/utils/libsmartcols/src/init.c
new file mode 100644
index 0000000..dfd7510
--- /dev/null
+++ b/utils/libsmartcols/src/init.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debugging
+ *
+ * The library debug stuff.
+ */
+
+#include <stdarg.h>
+
+#include "smartcolsP.h"
+
+UL_DEBUG_DEFINE_MASK(libsmartcols);
+UL_DEBUG_DEFINE_MASKNAMES(libsmartcols) =
+{
+ { "all", SCOLS_DEBUG_ALL, "info about all subsystems" },
+ { "buff", SCOLS_DEBUG_BUFF, "output buffer utils" },
+ { "cell", SCOLS_DEBUG_CELL, "table cell utils" },
+ { "col", SCOLS_DEBUG_COL, "cols utils" },
+ { "help", SCOLS_DEBUG_HELP, "this help" },
+ { "group", SCOLS_DEBUG_GROUP, "lines grouping utils" },
+ { "line", SCOLS_DEBUG_LINE, "table line utils" },
+ { "tab", SCOLS_DEBUG_TAB, "table utils" },
+ { NULL, 0, NULL }
+};
+
+/**
+ * scols_init_debug:
+ * @mask: debug mask (0xffff to enable full debugging)
+ *
+ * If the @mask is not specified, then this function reads
+ * the LIBSMARTCOLS_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. Calling
+ * this function twice has no effect.
+ */
+void scols_init_debug(int mask)
+{
+ if (libsmartcols_debug_mask)
+ return;
+
+ __UL_INIT_DEBUG_FROM_ENV(libsmartcols, SCOLS_DEBUG_, mask, LIBSMARTCOLS_DEBUG);
+
+ if (libsmartcols_debug_mask != SCOLS_DEBUG_INIT
+ && libsmartcols_debug_mask != (SCOLS_DEBUG_HELP|SCOLS_DEBUG_INIT)) {
+ const char *ver = NULL;
+
+ scols_get_library_version(&ver);
+
+ DBG(INIT, ul_debug("library debug mask: 0x%04x", libsmartcols_debug_mask));
+ DBG(INIT, ul_debug("library version: %s", ver));
+ }
+ ON_DBG(HELP, ul_debug_print_masks("LIBSMARTCOLS_DEBUG",
+ UL_DEBUG_MASKNAMES(libsmartcols)));
+}
diff --git a/utils/libsmartcols/src/iter.c b/utils/libsmartcols/src/iter.c
new file mode 100644
index 0000000..91cc080
--- /dev/null
+++ b/utils/libsmartcols/src/iter.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps the direction and the last position
+ * for access to the internal library tables/lists.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_iter:
+ * @direction: SCOLS_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct libscols_iter *scols_new_iter(int direction)
+{
+ struct libscols_iter *itr = calloc(1, sizeof(*itr));
+ if (!itr)
+ return NULL;
+ itr->direction = direction;
+ return itr;
+}
+
+/**
+ * scols_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates the iterator.
+ */
+void scols_free_iter(struct libscols_iter *itr)
+{
+ free(itr);
+}
+
+/**
+ * scols_reset_iter:
+ * @itr: iterator pointer
+ * @direction: SCOLS_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged
+ *
+ * Resets the iterator.
+ */
+void scols_reset_iter(struct libscols_iter *itr, int direction)
+{
+ if (direction == -1)
+ direction = itr->direction;
+
+ memset(itr, 0, sizeof(*itr));
+ itr->direction = direction;
+}
+
+/**
+ * scols_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: SCOLS_INTER_{FOR,BACK}WARD
+ */
+int scols_iter_get_direction(const struct libscols_iter *itr)
+{
+ return itr->direction;
+}
diff --git a/utils/libsmartcols/src/libsmartcols.h b/utils/libsmartcols/src/libsmartcols.h
new file mode 100644
index 0000000..2b2c08f
--- /dev/null
+++ b/utils/libsmartcols/src/libsmartcols.h
@@ -0,0 +1,336 @@
+/*
+ * Prints table or tree.
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _LIBSMARTCOLS_H
+#define _LIBSMARTCOLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+/**
+ * LIBSMARTCOLS_VERSION:
+ *
+ * Library version string
+ */
+#define LIBSMARTCOLS_VERSION "2.36.97"
+
+/**
+ * libscols_iter:
+ *
+ * Generic iterator
+ */
+struct libscols_iter;
+
+/**
+ * libscols_symbols:
+ *
+ * Symbol groups for printing tree hierarchies
+ */
+struct libscols_symbols;
+
+/**
+ * libscols_cell:
+ *
+ * A cell - the smallest library object
+ */
+struct libscols_cell;
+
+/**
+ * libscols_line:
+ *
+ * A line - an array of cells
+ */
+struct libscols_line;
+
+/**
+ * libscols_table:
+ *
+ * A table - The most abstract object, encapsulating lines, columns, symbols and cells
+ */
+struct libscols_table;
+
+/**
+ * libscols_column:
+ *
+ * A column - defines the number of columns and column names
+ */
+struct libscols_column;
+
+/* iter.c */
+enum {
+
+ SCOLS_ITER_FORWARD = 0,
+ SCOLS_ITER_BACKWARD
+};
+
+/*
+ * Column flags
+ */
+enum {
+ SCOLS_FL_TRUNC = (1 << 0), /* truncate fields data if necessary */
+ SCOLS_FL_TREE = (1 << 1), /* use tree "ascii art" */
+ SCOLS_FL_RIGHT = (1 << 2), /* align to the right */
+ SCOLS_FL_STRICTWIDTH = (1 << 3), /* don't reduce width if column is empty */
+ SCOLS_FL_NOEXTREMES = (1 << 4), /* ignore extreme fields when count column width*/
+ SCOLS_FL_HIDDEN = (1 << 5), /* maintain data, but don't print */
+ SCOLS_FL_WRAP = (1 << 6) /* wrap long lines to multi-line cells */
+};
+
+/*
+ * Column JSON types
+ */
+enum {
+ SCOLS_JSON_STRING = 0, /* default */
+ SCOLS_JSON_NUMBER = 1,
+ SCOLS_JSON_BOOLEAN = 2
+};
+
+/*
+ * Cell flags, see scols_cell_set_flags() before use
+ */
+enum {
+ /* alignment evaluated in order: right,center,left */
+ SCOLS_CELL_FL_LEFT = 0,
+ SCOLS_CELL_FL_CENTER = (1 << 0),
+ SCOLS_CELL_FL_RIGHT = (1 << 1)
+};
+
+extern struct libscols_iter *scols_new_iter(int direction);
+extern void scols_free_iter(struct libscols_iter *itr);
+extern void scols_reset_iter(struct libscols_iter *itr, int direction);
+extern int scols_iter_get_direction(const struct libscols_iter *itr);
+
+/* init.c */
+extern void scols_init_debug(int mask);
+
+/* version.c */
+extern int scols_parse_version_string(const char *ver_string);
+extern int scols_get_library_version(const char **ver_string);
+
+/* symbols.c */
+extern struct libscols_symbols *scols_new_symbols(void);
+extern void scols_ref_symbols(struct libscols_symbols *sy);
+extern void scols_unref_symbols(struct libscols_symbols *sy);
+extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy);
+extern int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_right(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str);
+
+extern int scols_symbols_set_group_vertical(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_horizontal(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_first_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_last_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_middle_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_last_child(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_middle_child(struct libscols_symbols *sy, const char *str);
+
+/* cell.c */
+extern int scols_reset_cell(struct libscols_cell *ce);
+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 const char *scols_cell_get_data(const 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);
+
+extern int scols_cell_set_flags(struct libscols_cell *ce, int flags);
+extern int scols_cell_get_flags(const struct libscols_cell *ce);
+extern int scols_cell_get_alignment(const struct libscols_cell *ce);
+
+extern void *scols_cell_get_userdata(struct libscols_cell *ce);
+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);
+extern int scols_column_is_right(const struct libscols_column *cl);
+extern int scols_column_is_strict_width(const struct libscols_column *cl);
+extern int scols_column_is_hidden(const struct libscols_column *cl);
+extern int scols_column_is_noextremes(const struct libscols_column *cl);
+extern int scols_column_is_wrap(const struct libscols_column *cl);
+extern int scols_column_is_customwrap(const struct libscols_column *cl);
+
+extern size_t scols_column_get_width(const struct libscols_column *cl);
+
+extern int scols_column_set_safechars(struct libscols_column *cl, const char *safe);
+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_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);
+extern void scols_ref_column(struct libscols_column *cl);
+extern void scols_unref_column(struct libscols_column *cl);
+extern struct libscols_column *scols_copy_column(const struct libscols_column *cl);
+extern int scols_column_set_whint(struct libscols_column *cl, double whint);
+extern double scols_column_get_whint(const struct libscols_column *cl);
+extern struct libscols_cell *scols_column_get_header(struct libscols_column *cl);
+extern int scols_column_set_color(struct libscols_column *cl, const char *color);
+extern const char *scols_column_get_color(const struct libscols_column *cl);
+extern struct libscols_table *scols_column_get_table(const struct libscols_column *cl);
+
+extern int scols_column_set_cmpfunc(struct libscols_column *cl,
+ int (*cmp)(struct libscols_cell *a,
+ struct libscols_cell *b, void *),
+ void *data);
+
+extern int scols_column_set_wrapfunc(struct libscols_column *cl,
+ size_t (*wrap_chunksize)(const struct libscols_column *,
+ const char *, void *),
+ char * (*wrap_nextchunk)(const struct libscols_column *,
+ char *, void *),
+ void *userdata);
+
+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);
+
+/* line.c */
+extern struct libscols_line *scols_new_line(void);
+extern void scols_ref_line(struct libscols_line *ln);
+extern void scols_unref_line(struct libscols_line *ln);
+extern int scols_line_alloc_cells(struct libscols_line *ln, size_t n);
+extern void scols_line_free_cells(struct libscols_line *ln);
+extern int scols_line_set_userdata(struct libscols_line *ln, void *data);
+extern void *scols_line_get_userdata(struct libscols_line *ln);
+extern int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child);
+extern int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child);
+extern int scols_line_has_children(struct libscols_line *ln);
+extern int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent);
+extern int scols_line_next_child(struct libscols_line *ln,
+ struct libscols_iter *itr, struct libscols_line **chld);
+extern struct libscols_line *scols_line_get_parent(const struct libscols_line *ln);
+extern int scols_line_set_color(struct libscols_line *ln, const char *color);
+extern const char *scols_line_get_color(const struct libscols_line *ln);
+extern size_t scols_line_get_ncells(const struct libscols_line *ln);
+extern struct libscols_cell *scols_line_get_cell(struct libscols_line *ln, size_t n);
+extern struct libscols_cell *scols_line_get_column_cell(
+ struct libscols_line *ln,
+ 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_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data);
+extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data);
+extern struct libscols_line *scols_copy_line(const struct libscols_line *ln);
+
+/* table */
+extern int scols_table_colors_wanted(const struct libscols_table *tb);
+extern int scols_table_set_name(struct libscols_table *tb, const char *name);
+extern const char *scols_table_get_name(const struct libscols_table *tb);
+extern struct libscols_cell *scols_table_get_title(struct libscols_table *tb);
+extern int scols_table_is_raw(const struct libscols_table *tb);
+extern int scols_table_is_ascii(const struct libscols_table *tb);
+extern int scols_table_is_json(const struct libscols_table *tb);
+extern int scols_table_is_noheadings(const struct libscols_table *tb);
+extern int scols_table_is_header_repeat(const struct libscols_table *tb);
+extern int scols_table_is_empty(const struct libscols_table *tb);
+extern int scols_table_is_export(const struct libscols_table *tb);
+extern int scols_table_is_maxout(const struct libscols_table *tb);
+extern int scols_table_is_minout(const struct libscols_table *tb);
+extern int scols_table_is_nowrap(const struct libscols_table *tb);
+extern int scols_table_is_nolinesep(const struct libscols_table *tb);
+extern int scols_table_is_tree(const struct libscols_table *tb);
+extern int scols_table_is_noencoding(const struct libscols_table *tb);
+
+extern int scols_table_enable_colors(struct libscols_table *tb, int enable);
+extern int scols_table_enable_raw(struct libscols_table *tb, int enable);
+extern int scols_table_enable_ascii(struct libscols_table *tb, int enable);
+extern int scols_table_enable_json(struct libscols_table *tb, int enable);
+extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable);
+extern int scols_table_enable_header_repeat(struct libscols_table *tb, int enable);
+extern int scols_table_enable_export(struct libscols_table *tb, int enable);
+extern int scols_table_enable_maxout(struct libscols_table *tb, int enable);
+extern int scols_table_enable_minout(struct libscols_table *tb, int enable);
+extern int scols_table_enable_nowrap(struct libscols_table *tb, int enable);
+extern int scols_table_enable_nolinesep(struct libscols_table *tb, int enable);
+extern int scols_table_enable_noencoding(struct libscols_table *tb, int enable);
+
+extern int scols_table_set_column_separator(struct libscols_table *tb, const char *sep);
+extern int scols_table_set_line_separator(struct libscols_table *tb, const char *sep);
+
+extern struct libscols_table *scols_new_table(void);
+extern void scols_ref_table(struct libscols_table *tb);
+extern void scols_unref_table(struct libscols_table *tb);
+extern int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl);
+extern int scols_table_remove_column(struct libscols_table *tb, struct libscols_column *cl);
+extern int scols_table_remove_columns(struct libscols_table *tb);
+extern int scols_table_move_column(struct libscols_table *tb, struct libscols_column *pre, struct libscols_column *cl);
+extern struct libscols_column *scols_table_new_column(struct libscols_table *tb, const char *name, double whint, int flags);
+extern int scols_table_next_column(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column **cl);
+extern int scols_table_set_columns_iter(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column *cl);
+extern const char *scols_table_get_column_separator(const struct libscols_table *tb);
+extern const char *scols_table_get_line_separator(const struct libscols_table *tb);
+extern size_t scols_table_get_ncols(const struct libscols_table *tb);
+extern size_t scols_table_get_nlines(const struct libscols_table *tb);
+extern struct libscols_column *scols_table_get_column(struct libscols_table *tb, size_t n);
+extern int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln);
+extern int scols_table_remove_line(struct libscols_table *tb, struct libscols_line *ln);
+extern void scols_table_remove_lines(struct libscols_table *tb);
+extern int scols_table_next_line(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_line **ln);
+extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, struct libscols_line *parent);
+extern struct libscols_line *scols_table_get_line(struct libscols_table *tb, size_t n);
+extern struct libscols_table *scols_copy_table(struct libscols_table *tb);
+extern int scols_table_set_symbols(struct libscols_table *tb, struct libscols_symbols *sy);
+extern int scols_table_set_default_symbols(struct libscols_table *tb);
+extern struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb);
+
+extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream);
+extern FILE *scols_table_get_stream(const struct libscols_table *tb);
+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);
+/*
+ *
+ */
+enum {
+ SCOLS_TERMFORCE_AUTO = 0,
+ SCOLS_TERMFORCE_NEVER,
+ SCOLS_TERMFORCE_ALWAYS
+};
+extern int scols_table_set_termforce(struct libscols_table *tb, int force);
+extern int scols_table_get_termforce(const struct libscols_table *tb);
+extern int scols_table_set_termwidth(struct libscols_table *tb, size_t width);
+extern size_t scols_table_get_termwidth(const struct libscols_table *tb);
+extern int scols_table_set_termheight(struct libscols_table *tb, size_t height);
+extern size_t scols_table_get_termheight(const struct libscols_table *tb);
+
+
+/* table_print.c */
+extern int scols_print_table(struct libscols_table *tb);
+extern int scols_print_table_to_string(struct libscols_table *tb, char **data);
+
+extern int scols_table_print_range( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end);
+extern int scols_table_print_range_to_string( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end,
+ char **data);
+
+/* grouping.c */
+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);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSMARTCOLS_H */
diff --git a/utils/libsmartcols/src/line.c b/utils/libsmartcols/src/line.c
new file mode 100644
index 0000000..351bed7
--- /dev/null
+++ b/utils/libsmartcols/src/line.c
@@ -0,0 +1,540 @@
+/*
+ * line.c - functions for table handling at the line level
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: line
+ * @title: Line
+ * @short_description: cells container, also keeps tree (parent->child) information
+ *
+ * An API to access and modify per-line data and information.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_line:
+ *
+ * Note that the line is allocated without cells, the cells will be allocated
+ * later when you add the line to the table. If you want to use the line
+ * without table then you have to explicitly allocate the cells by
+ * scols_line_alloc_cells().
+ *
+ * Returns: a pointer to a new struct libscols_line instance.
+ */
+struct libscols_line *scols_new_line(void)
+{
+ struct libscols_line *ln;
+
+ ln = calloc(1, sizeof(*ln));
+ if (!ln)
+ return NULL;
+
+ DBG(LINE, ul_debugobj(ln, "alloc"));
+ ln->refcount = 1;
+ INIT_LIST_HEAD(&ln->ln_lines);
+ INIT_LIST_HEAD(&ln->ln_children);
+ INIT_LIST_HEAD(&ln->ln_branch);
+ INIT_LIST_HEAD(&ln->ln_groups);
+ return ln;
+}
+
+/**
+ * scols_ref_line:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Increases the refcount of @ln.
+ */
+void scols_ref_line(struct libscols_line *ln)
+{
+ if (ln)
+ ln->refcount++;
+}
+
+/**
+ * scols_unref_line:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Decreases the refcount of @ln. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_line(struct libscols_line *ln)
+{
+ if (ln && --ln->refcount <= 0) {
+ DBG(CELL, ul_debugobj(ln, "dealloc"));
+ list_del(&ln->ln_lines);
+ list_del(&ln->ln_children);
+ list_del(&ln->ln_groups);
+ scols_unref_group(ln->group);
+ scols_line_free_cells(ln);
+ free(ln->color);
+ free(ln);
+ return;
+ }
+}
+
+/**
+ * scols_line_free_cells:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Frees the allocated cells referenced to by @ln.
+ */
+void scols_line_free_cells(struct libscols_line *ln)
+{
+ size_t i;
+
+ if (!ln || !ln->cells)
+ return;
+
+ DBG(LINE, ul_debugobj(ln, "free cells"));
+
+ for (i = 0; i < ln->ncells; i++)
+ scols_reset_cell(&ln->cells[i]);
+
+ free(ln->cells);
+ ln->ncells = 0;
+ ln->cells = NULL;
+}
+
+/**
+ * scols_line_alloc_cells:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: the number of elements
+ *
+ * Allocates space for @n cells. This function is optional,
+ * and libsmartcols automatically allocates necessary cells
+ * according to number of columns in the table when you add
+ * the line to the table. See scols_table_add_line().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
+{
+ struct libscols_cell *ce;
+
+ if (!ln)
+ return -EINVAL;
+ if (ln->ncells == n)
+ return 0;
+
+ if (!n) {
+ scols_line_free_cells(ln);
+ return 0;
+ }
+
+ DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n));
+
+ ce = realloc(ln->cells, n * sizeof(struct libscols_cell));
+ if (!ce)
+ return -errno;
+
+ if (n > ln->ncells)
+ memset(ce + ln->ncells, 0,
+ (n - ln->ncells) * sizeof(struct libscols_cell));
+
+ ln->cells = ce;
+ ln->ncells = n;
+ return 0;
+}
+
+int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn)
+{
+ struct libscols_cell ce;
+
+ if (!ln || newn >= ln->ncells || oldn >= ln->ncells)
+ return -EINVAL;
+ if (oldn == newn)
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn));
+
+ /* remember data from old position */
+ memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell));
+
+ /* remove old position (move data behind oldn to oldn) */
+ if (oldn + 1 < ln->ncells)
+ memmove(ln->cells + oldn, ln->cells + oldn + 1,
+ (ln->ncells - oldn - 1) * sizeof(struct libscols_cell));
+
+ /* create a space for new position */
+ if (newn + 1 < ln->ncells)
+ memmove(ln->cells + newn + 1, ln->cells + newn,
+ (ln->ncells - newn - 1) * sizeof(struct libscols_cell));
+
+ /* copy original data to new position */
+ memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell));
+ return 0;
+}
+
+/**
+ * scols_line_set_userdata:
+ * @ln: a pointer to a struct libscols_line instance
+ * @data: user data
+ *
+ * Binds @data to @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_set_userdata(struct libscols_line *ln, void *data)
+{
+ if (!ln)
+ return -EINVAL;
+ ln->userdata = data;
+ return 0;
+}
+
+/**
+ * scols_line_get_userdata:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: user data
+ */
+void *scols_line_get_userdata(struct libscols_line *ln)
+{
+ return ln->userdata;
+}
+
+/**
+ * scols_line_remove_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @child: a pointer to a struct libscols_line instance
+ *
+ * Removes @child as a child of @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
+{
+ if (!ln || !child)
+ return -EINVAL;
+
+ DBG(LINE, ul_debugobj(ln, "remove child"));
+
+ list_del_init(&child->ln_children);
+ child->parent = NULL;
+ scols_unref_line(child);
+
+ scols_unref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_line_add_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @child: a pointer to a struct libscols_line instance
+ *
+ * Sets @child as a child of @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
+{
+ if (!ln || !child)
+ return -EINVAL;
+
+ DBG(LINE, ul_debugobj(ln, "add child"));
+ scols_ref_line(child);
+ scols_ref_line(ln);
+
+ /* unref old<->parent */
+ if (child->parent)
+ scols_line_remove_child(child->parent, child);
+
+ /* new reference from parent to child */
+ list_add_tail(&child->ln_children, &ln->ln_branch);
+
+ /* new reference from child to parent */
+ child->parent = ln;
+ return 0;
+}
+
+/**
+ * scols_line_get_parent:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
+ */
+struct libscols_line *scols_line_get_parent(const struct libscols_line *ln)
+{
+ return ln ? ln->parent : NULL;
+}
+
+/**
+ * scols_line_has_children:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: 1 if @ln has any children, otherwise 0.
+ */
+int scols_line_has_children(struct libscols_line *ln)
+{
+ return ln ? !list_empty(&ln->ln_branch) : 0;
+}
+
+/**
+ * scols_line_next_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @chld: a pointer to a pointer to a struct libscols_line instance
+ *
+ * Finds the next child and returns a pointer to it via @chld.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_next_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld)
+{
+ int rc = 1;
+
+ if (!ln || !itr || !chld)
+ return -EINVAL;
+ *chld = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &ln->ln_branch);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* private API */
+int scols_line_next_group_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld)
+{
+ int rc = 1;
+
+ if (!ln || !itr || !chld || !ln->group)
+ return -EINVAL;
+ *chld = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &ln->group->gr_children);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_line_is_ancestor:
+ * @ln: line
+ * @parent: potential parent
+ *
+ * The function is designed to detect circular dependencies between @ln and
+ * @parent. It checks if @ln is not any (grand) parent in the @parent's tree.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0 or 1
+ */
+int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent)
+{
+ while (parent) {
+ if (parent == ln)
+ return 1;
+ parent = scols_line_get_parent(parent);
+ };
+ return 0;
+}
+
+/**
+ * scols_line_set_color:
+ * @ln: a pointer to a struct libscols_line instance
+ * @color: color name or ESC sequence
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_set_color(struct libscols_line *ln, const char *color)
+{
+ if (color && isalnum(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(ln, color, color);
+}
+
+/**
+ * scols_line_get_color:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: @ln's color string, NULL in case of an error.
+ */
+const char *scols_line_get_color(const struct libscols_line *ln)
+{
+ return ln->color;
+}
+
+/**
+ * scols_line_get_ncells:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: number of cells
+ */
+size_t scols_line_get_ncells(const struct libscols_line *ln)
+{
+ return ln->ncells;
+}
+
+/**
+ * scols_line_get_cell:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: cell number to retrieve
+ *
+ * Returns: the @n-th cell in @ln, NULL in case of an error.
+ */
+struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
+ size_t n)
+{
+ if (!ln || n >= ln->ncells)
+ return NULL;
+ return &ln->cells[n];
+}
+
+/**
+ * scols_line_get_column_cell:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: pointer to cell
+ *
+ * Like scols_line_get_cell() by cell is referenced by column.
+ *
+ * Returns: the @n-th cell in @ln, NULL in case of an error.
+ */
+struct libscols_cell *scols_line_get_column_cell(
+ struct libscols_line *ln,
+ struct libscols_column *cl)
+{
+ if (!ln || !cl)
+ return NULL;
+
+ return scols_line_get_cell(ln, cl->seqnum);
+}
+
+/**
+ * scols_line_set_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: number of the cell, whose data is to be set
+ * @data: actual data to set
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
+{
+ struct libscols_cell *ce = scols_line_get_cell(ln, n);
+
+ if (!ce)
+ return -EINVAL;
+ return scols_cell_set_data(ce, data);
+}
+
+/**
+ * scols_line_set_column_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: column, whose data is to be set
+ * @data: actual data to set
+ *
+ * The same as scols_line_set_data() but cell is referenced by column object.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_line_set_column_data(struct libscols_line *ln,
+ struct libscols_column *cl,
+ const char *data)
+{
+ return scols_line_set_data(ln, cl->seqnum, data);
+}
+
+/**
+ * scols_line_refer_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: number of the cell which will refer to @data
+ * @data: actual data to refer to
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
+{
+ struct libscols_cell *ce = scols_line_get_cell(ln, n);
+
+ if (!ce)
+ return -EINVAL;
+ return scols_cell_refer_data(ce, data);
+}
+
+/**
+ * scols_line_refer_column_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: column, whose data is to be set
+ * @data: actual data to refer to
+ *
+ * The same as scols_line_refer_data() but cell is referenced by column object.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_line_refer_column_data(struct libscols_line *ln,
+ struct libscols_column *cl,
+ char *data)
+{
+ return scols_line_refer_data(ln, cl->seqnum, data);
+}
+
+/**
+ * scols_copy_line:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: A newly allocated copy of @ln, NULL in case of an error.
+ */
+struct libscols_line *scols_copy_line(const struct libscols_line *ln)
+{
+ struct libscols_line *ret;
+ size_t i;
+
+ if (!ln)
+ return NULL;
+
+ ret = scols_new_line();
+ if (!ret)
+ return NULL;
+ if (scols_line_set_color(ret, ln->color))
+ goto err;
+ if (scols_line_alloc_cells(ret, ln->ncells))
+ goto err;
+
+ ret->userdata = ln->userdata;
+ ret->ncells = ln->ncells;
+ ret->seqnum = ln->seqnum;
+
+ DBG(LINE, ul_debugobj(ln, "copy"));
+
+ for (i = 0; i < ret->ncells; ++i) {
+ if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
+ goto err;
+ }
+
+ return ret;
+err:
+ scols_unref_line(ret);
+ return NULL;
+}
diff --git a/utils/libsmartcols/src/print-api.c b/utils/libsmartcols/src/print-api.c
new file mode 100644
index 0000000..9a9f2df
--- /dev/null
+++ b/utils/libsmartcols/src/print-api.c
@@ -0,0 +1,211 @@
+#include "smartcolsP.h"
+
+/**
+ * scola_table_print_range:
+ * @tb: table
+ * @start: first printed line or NULL to print from the begin of the table
+ * @end: last printed line or NULL to print all from start.
+ *
+ * If the start is the first line in the table than prints table header too.
+ * The header is printed only once. This does not work for trees.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_print_range( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end)
+{
+ struct libscols_buffer *buf = NULL;
+ struct libscols_iter itr;
+ int rc;
+
+ if (scols_table_is_tree(tb))
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing range from API"));
+
+ rc = __scols_initialize_printing(tb, &buf);
+ if (rc)
+ return rc;
+
+ if (start) {
+ itr.direction = SCOLS_ITER_FORWARD;
+ itr.head = &tb->tb_lines;
+ itr.p = &start->ln_lines;
+ } else
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ if (!start || itr.p == tb->tb_lines.next) {
+ rc = __scols_print_header(tb, buf);
+ if (rc)
+ goto done;
+ }
+
+ rc = __scols_print_range(tb, buf, &itr, end);
+done:
+ __scols_cleanup_printing(tb, buf);
+ return rc;
+}
+
+/**
+ * scols_table_print_range_to_string:
+ * @tb: table
+ * @start: first printed line or NULL to print from the beginning of the table
+ * @end: last printed line or NULL to print all from start.
+ * @data: pointer to the beginning of a memory area to print to
+ *
+ * The same as scols_table_print_range(), but prints to @data instead of
+ * stream.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+#ifdef HAVE_OPEN_MEMSTREAM
+int scols_table_print_range_to_string( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end,
+ char **data)
+{
+ FILE *stream, *old_stream;
+ size_t sz;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing range to string"));
+
+ /* create a stream for output */
+ stream = open_memstream(data, &sz);
+ if (!stream)
+ return -ENOMEM;
+
+ old_stream = scols_table_get_stream(tb);
+ scols_table_set_stream(tb, stream);
+ rc = scols_table_print_range(tb, start, end);
+ fclose(stream);
+ scols_table_set_stream(tb, old_stream);
+
+ return rc;
+}
+#else
+int scols_table_print_range_to_string(
+ struct libscols_table *tb __attribute__((__unused__)),
+ struct libscols_line *start __attribute__((__unused__)),
+ struct libscols_line *end __attribute__((__unused__)),
+ char **data __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+#endif
+
+static int do_print_table(struct libscols_table *tb, int *is_empty)
+{
+ int rc = 0;
+ struct libscols_buffer *buf = NULL;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing"));
+ if (is_empty)
+ *is_empty = 0;
+
+ if (list_empty(&tb->tb_columns)) {
+ DBG(TAB, ul_debugobj(tb, "error -- no columns"));
+ return -EINVAL;
+ }
+ if (list_empty(&tb->tb_lines)) {
+ DBG(TAB, ul_debugobj(tb, "ignore -- no lines"));
+ if (scols_table_is_json(tb)) {
+ fput_table_open(tb);
+ fput_table_close(tb);
+ } else if (is_empty)
+ *is_empty = 1;
+ return 0;
+ }
+
+ tb->header_printed = 0;
+ rc = __scols_initialize_printing(tb, &buf);
+ if (rc)
+ return rc;
+
+ fput_table_open(tb);
+
+ if (tb->format == SCOLS_FMT_HUMAN)
+ __scols_print_title(tb);
+
+ rc = __scols_print_header(tb, buf);
+ if (rc)
+ goto done;
+
+ if (scols_table_is_tree(tb))
+ rc = __scols_print_tree(tb, buf);
+ else
+ rc = __scols_print_table(tb, buf);
+
+ fput_table_close(tb);
+done:
+ __scols_cleanup_printing(tb, buf);
+ return rc;
+}
+
+/**
+ * scols_print_table:
+ * @tb: table
+ *
+ * Prints the table to the output stream and terminate by \n.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_print_table(struct libscols_table *tb)
+{
+ int empty = 0;
+ int rc = do_print_table(tb, &empty);
+
+ if (rc == 0 && !empty)
+ fputc('\n', tb->out);
+ return rc;
+}
+
+/**
+ * scols_print_table_to_string:
+ * @tb: table
+ * @data: pointer to the beginning of a memory area to print to
+ *
+ * Prints the table to @data.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+#ifdef HAVE_OPEN_MEMSTREAM
+int scols_print_table_to_string(struct libscols_table *tb, char **data)
+{
+ FILE *stream, *old_stream;
+ size_t sz;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing to string"));
+
+ /* create a stream for output */
+ stream = open_memstream(data, &sz);
+ if (!stream)
+ return -ENOMEM;
+
+ old_stream = scols_table_get_stream(tb);
+ scols_table_set_stream(tb, stream);
+ rc = do_print_table(tb, NULL);
+ fclose(stream);
+ scols_table_set_stream(tb, old_stream);
+
+ return rc;
+}
+#else
+int scols_print_table_to_string(
+ struct libscols_table *tb __attribute__((__unused__)),
+ char **data __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+#endif
diff --git a/utils/libsmartcols/src/print.c b/utils/libsmartcols/src/print.c
new file mode 100644
index 0000000..1172533
--- /dev/null
+++ b/utils/libsmartcols/src/print.c
@@ -0,0 +1,1089 @@
+/*
+ * table.c - functions handling the data at the table level
+ *
+ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: table_print
+ * @title: Table print
+ * @short_description: output functions
+ *
+ * Table output API.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+
+#include "mbsalign.h"
+#include "carefulputc.h"
+#include "smartcolsP.h"
+
+/* Fallback for symbols
+ *
+ * Note that by default library define all the symbols, but in case user does
+ * not define all symbols or if we extended the symbols struct then we need
+ * fallback to be more robust and backwardly compatible.
+ */
+#define titlepadding_symbol(tb) ((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
+#define branch_symbol(tb) ((tb)->symbols->tree_branch ? (tb)->symbols->tree_branch : "|-")
+#define vertical_symbol(tb) ((tb)->symbols->tree_vert ? (tb)->symbols->tree_vert : "| ")
+#define right_symbol(tb) ((tb)->symbols->tree_right ? (tb)->symbols->tree_right : "`-")
+
+#define grp_vertical_symbol(tb) ((tb)->symbols->group_vert ? (tb)->symbols->group_vert : "|")
+#define grp_horizontal_symbol(tb) ((tb)->symbols->group_horz ? (tb)->symbols->group_horz : "-")
+#define grp_m_first_symbol(tb) ((tb)->symbols->group_first_member ? (tb)->symbols->group_first_member : ",->")
+#define grp_m_last_symbol(tb) ((tb)->symbols->group_last_member ? (tb)->symbols->group_last_member : "\\->")
+#define grp_m_middle_symbol(tb) ((tb)->symbols->group_middle_member ? (tb)->symbols->group_middle_member : "|->")
+#define grp_c_middle_symbol(tb) ((tb)->symbols->group_middle_child ? (tb)->symbols->group_middle_child : "|-")
+#define grp_c_last_symbol(tb) ((tb)->symbols->group_last_child ? (tb)->symbols->group_last_child : "`-")
+
+#define cellpadding_symbol(tb) ((tb)->padding_debug ? "." : \
+ ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " "))
+
+#define want_repeat_header(tb) (!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used)
+
+static int is_next_columns_empty(
+ struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln)
+{
+ struct libscols_iter itr;
+
+ if (!tb || !cl)
+ return 0;
+ if (is_last_column(cl))
+ return 1;
+ if (!ln)
+ return 0;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ scols_table_set_columns_iter(tb, &itr, cl);
+
+ /* skip current column */
+ scols_table_next_column(tb, &itr, &cl);
+
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ struct libscols_cell *ce;
+ const char *data = NULL;
+
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (scols_column_is_tree(cl))
+ return 0;
+
+ ce = scols_line_get_cell(ln, cl->seqnum);
+ if (ce)
+ data = scols_cell_get_data(ce);
+ if (data && *data)
+ return 0;
+ }
+ return 1;
+}
+
+/* returns pointer to the end of used data */
+static int tree_ascii_art_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_buffer *buf)
+{
+ const char *art;
+ int rc;
+
+ assert(ln);
+ assert(buf);
+
+ if (!ln->parent)
+ return 0;
+
+ rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
+ if (rc)
+ return rc;
+
+ if (is_last_child(ln))
+ art = " ";
+ else
+ art = vertical_symbol(tb);
+
+ return buffer_append_data(buf, art);
+}
+
+static int grpset_is_empty( struct libscols_table *tb,
+ size_t idx,
+ size_t *rest)
+{
+ size_t i;
+
+ for (i = idx; i < tb->grpset_size; i++) {
+ if (tb->grpset[i] == NULL) {
+ if (rest)
+ (*rest)++;
+ } else
+ return 0;
+ }
+ return 1;
+}
+
+static int groups_ascii_art_to_buffer( struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_buffer *buf)
+{
+ int filled = 0;
+ size_t i, rest = 0;
+ const char *filler = cellpadding_symbol(tb);
+
+ if (!has_groups(tb))
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "printing groups chart"));
+
+ if (tb->is_dummy_print)
+ return 0; /* allocate grpset[] only */
+
+ for (i = 0; i < tb->grpset_size; i += SCOLS_GRPSET_CHUNKSIZ) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (!gr) {
+ buffer_append_ntimes(buf, SCOLS_GRPSET_CHUNKSIZ, cellpadding_symbol(tb));
+ continue;
+ }
+
+ switch (gr->state) {
+ case SCOLS_GSTATE_FIRST_MEMBER:
+ buffer_append_data(buf, grp_m_first_symbol(tb));
+ break;
+ case SCOLS_GSTATE_MIDDLE_MEMBER:
+ buffer_append_data(buf, grp_m_middle_symbol(tb));
+ break;
+ case SCOLS_GSTATE_LAST_MEMBER:
+ buffer_append_data(buf, grp_m_last_symbol(tb));
+ break;
+ case SCOLS_GSTATE_CONT_MEMBERS:
+ buffer_append_data(buf, grp_vertical_symbol(tb));
+ buffer_append_ntimes(buf, 2, filler);
+ break;
+ case SCOLS_GSTATE_MIDDLE_CHILD:
+ buffer_append_data(buf, filler);
+ buffer_append_data(buf, grp_c_middle_symbol(tb));
+ if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
+ buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
+ filled = 1;
+ }
+ filler = grp_horizontal_symbol(tb);
+ break;
+ case SCOLS_GSTATE_LAST_CHILD:
+ buffer_append_data(buf, cellpadding_symbol(tb));
+ buffer_append_data(buf, grp_c_last_symbol(tb));
+ if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
+ buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
+ filled = 1;
+ }
+ filler = grp_horizontal_symbol(tb);
+ break;
+ case SCOLS_GSTATE_CONT_CHILDREN:
+ buffer_append_data(buf, filler);
+ buffer_append_data(buf, grp_vertical_symbol(tb));
+ buffer_append_data(buf, filler);
+ break;
+ }
+
+ if (filled)
+ break;
+ }
+
+ if (!filled)
+ buffer_append_data(buf, filler);
+ 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;
+}
+
+/* print padding or ASCII-art instead of data of @cl */
+static void print_empty_cell(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ size_t bufsz)
+{
+ size_t len_pad = 0; /* in screen cells as opposed to bytes */
+
+ DBG(COL, ul_debugobj(cl, " printing empty cell"));
+
+ /* generate tree ASCII-art rather than padding */
+ if (ln && scols_column_is_tree(cl)) {
+ if (!ln->parent) {
+ /* only print symbols->vert if followed by child */
+ if (!list_empty(&ln->ln_branch)) {
+ fputs(vertical_symbol(tb), tb->out);
+ len_pad = scols_table_is_noencoding(tb) ?
+ mbs_width(vertical_symbol(tb)) :
+ mbs_safe_width(vertical_symbol(tb));
+ }
+ } else {
+ /* use the same draw function as though we were intending to draw an L-shape */
+ struct libscols_buffer *art = new_buffer(bufsz);
+ char *data;
+
+ if (art) {
+ /* whatever the rc, len_pad will be sensible */
+ tree_ascii_art_to_buffer(tb, ln, art);
+ if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
+ buffer_append_data(art, vertical_symbol(tb));
+ data = buffer_get_safe_data(tb, art, &len_pad, NULL);
+ if (data && len_pad)
+ fputs(data, tb->out);
+ free_buffer(art);
+ }
+ }
+ }
+
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln))
+ return;
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last_column(cl))
+ return;
+
+ /* fill rest of cell with space */
+ for(; len_pad < cl->width; ++len_pad)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ if (!is_last_column(cl))
+ fputs(colsep(tb), tb->out);
+}
+
+
+static const char *get_cell_color(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ struct libscols_cell *ce) /* optional */
+{
+ const char *color = NULL;
+
+ if (tb && tb->colors_wanted) {
+ if (ce)
+ color = ce->color;
+ if (ln && !color)
+ color = ln->color;
+ if (!color)
+ color = cl->color;
+ }
+ return color;
+}
+
+/* Fill the start of a line with padding (or with tree ascii-art).
+ *
+ * This is necessary after a long non-truncated column, as this requires the
+ * next column to be printed on the next line. For example (see 'DDD'):
+ *
+ * aaa bbb ccc ddd eee
+ * AAA BBB CCCCCCC
+ * DDD EEE
+ * ^^^^^^^^^^^^
+ * new line padding
+ */
+static void print_newline_padding(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ size_t bufsz)
+{
+ size_t i;
+
+ assert(tb);
+ assert(cl);
+
+ DBG(LINE, ul_debugobj(ln, "printing newline padding"));
+
+ fputs(linesep(tb), tb->out); /* line break */
+ tb->termlines_used++;
+
+ /* fill cells after line break */
+ for (i = 0; i <= (size_t) cl->seqnum; i++)
+ print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
+}
+
+/*
+ * 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)
+{
+ 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)
+{
+ const char *color = get_cell_color(tb, cl, ln, ce);
+ size_t width = cl->width, bytes;
+ size_t len = width, i;
+ char *data;
+ char *nextchunk = NULL;
+
+ if (!cl->pending_data)
+ return 0;
+ if (!width)
+ return -EINVAL;
+
+ DBG(COL, ul_debugobj(cl, "printing pending data"));
+
+ data = strdup(cl->pending_data);
+ if (!data)
+ goto err;
+
+ if (scols_column_is_customwrap(cl)
+ && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
+ bytes = nextchunk - data;
+
+ len = scols_table_is_noencoding(tb) ?
+ mbs_nwidth(data, bytes) :
+ mbs_safe_nwidth(data, bytes, NULL);
+ } else
+ bytes = mbs_truncate(data, &len);
+
+ if (bytes == (size_t) -1)
+ goto err;
+
+ if (bytes)
+ step_pending_data(cl, bytes);
+
+ if (color)
+ fputs(color, tb->out);
+ fputs(data, tb->out);
+ if (color)
+ fputs(UL_COLOR_RESET, tb->out);
+ free(data);
+
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln))
+ return 0;
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last_column(cl))
+ return 0;
+
+ /* fill rest of cell with space */
+ for(i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ if (!is_last_column(cl))
+ fputs(colsep(tb), tb->out);
+
+ return 0;
+err:
+ free(data);
+ return -errno;
+}
+
+static int print_data(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ struct libscols_cell *ce, /* optional */
+ struct libscols_buffer *buf)
+{
+ size_t len = 0, i, width, bytes;
+ const char *color = NULL;
+ char *data, *nextchunk;
+ int is_last;
+
+ assert(tb);
+ assert(cl);
+
+ data = buffer_get_data(buf);
+ if (!data)
+ data = "";
+
+ is_last = is_last_column(cl);
+
+ switch (tb->format) {
+ case SCOLS_FMT_RAW:
+ fputs_nonblank(data, tb->out);
+ if (!is_last)
+ fputs(colsep(tb), tb->out);
+ return 0;
+
+ case SCOLS_FMT_EXPORT:
+ fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header));
+ fputs_quoted(data, tb->out);
+ if (!is_last)
+ fputs(colsep(tb), tb->out);
+ return 0;
+
+ case SCOLS_FMT_JSON:
+ fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out);
+ fputs(":", tb->out);
+ switch (cl->json_type) {
+ case SCOLS_JSON_STRING:
+ if (!*data)
+ fputs("null", tb->out);
+ else
+ fputs_quoted_json(data, tb->out);
+ break;
+ case SCOLS_JSON_NUMBER:
+ if (!*data)
+ fputs("null", tb->out);
+ else
+ fputs(data, tb->out);
+ break;
+ case SCOLS_JSON_BOOLEAN:
+ fputs(!*data ? "false" :
+ *data == '0' ? "false" :
+ *data == 'N' || *data == 'n' ? "false" : "true",
+ tb->out);
+ break;
+ }
+ if (!is_last)
+ fputs(", ", tb->out);
+ return 0;
+
+ case SCOLS_FMT_HUMAN:
+ break; /* continue below */
+ }
+
+ color = get_cell_color(tb, cl, ln, ce);
+
+ /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
+ */
+ data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl));
+ if (!data)
+ data = "";
+ bytes = strlen(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)
+ && !scols_column_is_right(cl))
+ width = len;
+
+ /* truncate data */
+ if (len > width && scols_column_is_trunc(cl)) {
+ len = width;
+ bytes = mbs_truncate(data, &len); /* updates 'len' */
+ }
+
+ /* 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 = len = 0;
+ data = NULL;
+ }
+
+ if (data && *data) {
+ if (scols_column_is_right(cl)) {
+ if (color)
+ fputs(color, tb->out);
+ for (i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+ fputs(data, tb->out);
+ if (color)
+ fputs(UL_COLOR_RESET, tb->out);
+ len = width;
+
+ } else if (color) {
+ char *p = data;
+ size_t art = buffer_get_safe_art_size(buf);
+
+ /* we don't want to colorize tree ascii art */
+ if (scols_column_is_tree(cl) && art && art < bytes) {
+ fwrite(p, 1, art, tb->out);
+ p += art;
+ }
+
+ fputs(color, tb->out);
+ fputs(p, tb->out);
+ fputs(UL_COLOR_RESET, tb->out);
+ } else
+ fputs(data, tb->out);
+ }
+
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln))
+ return 0;
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last)
+ return 0;
+
+ /* fill rest of cell with space */
+ for(i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ if (len > width && !scols_column_is_trunc(cl)) {
+ DBG(COL, ul_debugobj(cl, "*** data len=%zu > column width=%zu", len, width));
+ print_newline_padding(tb, cl, ln, buffer_get_size(buf)); /* next column starts on next line */
+
+ } else if (!is_last)
+ fputs(colsep(tb), tb->out); /* columns separator */
+
+ return 0;
+}
+
+int __cell_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct libscols_buffer *buf)
+{
+ const char *data;
+ struct libscols_cell *ce;
+ int rc = 0;
+
+ assert(tb);
+ assert(ln);
+ assert(cl);
+ assert(buf);
+ assert(cl->seqnum <= tb->ncols);
+
+ 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 ? buffer_set_data(buf, data) : 0;
+
+ /*
+ * Group stuff
+ */
+ if (!scols_table_is_json(tb) && cl->is_groups)
+ rc = groups_ascii_art_to_buffer(tb, ln, buf);
+
+ /*
+ * Tree stuff
+ */
+ if (!rc && ln->parent && !scols_table_is_json(tb)) {
+ rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
+
+ if (!rc && is_last_child(ln))
+ rc = buffer_append_data(buf, right_symbol(tb));
+ else if (!rc)
+ rc = buffer_append_data(buf, branch_symbol(tb));
+ }
+
+ if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb))
+ buffer_set_art_index(buf);
+
+ if (!rc && data)
+ rc = buffer_append_data(buf, data);
+ return rc;
+}
+
+/*
+ * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
+ * control and non-printable characters can be encoded in the \x?? encoding.
+ */
+static int print_line(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_buffer *buf)
+{
+ int rc = 0, pending = 0;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ assert(ln);
+
+ DBG(LINE, ul_debugobj(ln, "printing line"));
+
+ /* 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)
+ pending = 1;
+ }
+
+ /* extra lines of the multi-line cells */
+ while (rc == 0 && pending) {
+ DBG(LINE, ul_debugobj(ln, "printing pending data"));
+ pending = 0;
+ fputs(linesep(tb), tb->out);
+ 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)
+ pending = 1;
+ } else
+ print_empty_cell(tb, cl, ln, buffer_get_size(buf));
+ }
+ }
+
+ return 0;
+}
+
+int __scols_print_title(struct libscols_table *tb)
+{
+ int rc, color = 0;
+ mbs_align_t align;
+ size_t width, len = 0, bufsz, titlesz;
+ char *title = NULL, *buf = NULL;
+
+ assert(tb);
+
+ if (!tb->title.data)
+ return 0;
+
+ DBG(TAB, ul_debugobj(tb, "printing title"));
+
+ /* encode data */
+ if (tb->no_encode) {
+ len = bufsz = strlen(tb->title.data) + 1;
+ buf = strdup(tb->title.data);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ } else {
+ bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
+ if (bufsz == 1) {
+ DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
+ return 0;
+ }
+ buf = malloc(bufsz);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) ||
+ !len || len == (size_t) -1) {
+ rc = -EINVAL;
+ goto done;
+ }
+ }
+
+ /* truncate and align */
+ width = tb->is_term ? tb->termwidth : 80;
+ titlesz = width + bufsz;
+
+ title = malloc(titlesz);
+ if (!title) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ switch (scols_cell_get_alignment(&tb->title)) {
+ case SCOLS_CELL_FL_RIGHT:
+ align = MBS_ALIGN_RIGHT;
+ break;
+ case SCOLS_CELL_FL_CENTER:
+ align = MBS_ALIGN_CENTER;
+ break;
+ case SCOLS_CELL_FL_LEFT:
+ default:
+ align = MBS_ALIGN_LEFT;
+ /*
+ * Don't print extra blank chars after the title if on left
+ * (that's same as we use for the last column in the table).
+ */
+ if (len < width
+ && !scols_table_is_maxout(tb)
+ && isblank(*titlepadding_symbol(tb)))
+ width = len;
+ break;
+
+ }
+
+ /* copy from buf to title and align to width with title_padding */
+ rc = mbsalign_with_padding(buf, title, titlesz,
+ &width, align,
+ 0, (int) *titlepadding_symbol(tb));
+
+ if (rc == -1) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (tb->colors_wanted && tb->title.color)
+ color = 1;
+ if (color)
+ fputs(tb->title.color, tb->out);
+
+ fputs(title, tb->out);
+
+ if (color)
+ fputs(UL_COLOR_RESET, tb->out);
+
+ fputc('\n', tb->out);
+ rc = 0;
+done:
+ free(buf);
+ free(title);
+ DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
+ return rc;
+}
+
+int __scols_print_header(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ int rc = 0;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ assert(tb);
+
+ if ((tb->header_printed == 1 && tb->header_repeat == 0) ||
+ scols_table_is_noheadings(tb) ||
+ scols_table_is_export(tb) ||
+ scols_table_is_json(tb) ||
+ list_empty(&tb->tb_lines))
+ return 0;
+
+ DBG(TAB, ul_debugobj(tb, "printing header"));
+
+ /* set the width according to the size of the data */
+ 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;
+
+ buffer_reset_data(buf);
+
+ if (cl->is_groups
+ && scols_table_is_tree(tb) && scols_column_is_tree(cl)) {
+ size_t i;
+ for (i = 0; i < tb->grpset_size + 1; i++) {
+ rc = buffer_append_data(buf, " ");
+ if (rc)
+ break;
+ }
+ }
+ if (!rc)
+ rc = buffer_append_data(buf, scols_cell_get_data(&cl->header));
+ if (!rc)
+ rc = print_data(tb, cl, NULL, &cl->header, buf);
+ }
+
+ if (rc == 0) {
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+
+ tb->header_printed = 1;
+ tb->header_next = tb->termlines_used + tb->termheight;
+ if (tb->header_repeat)
+ DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu, rc=%d]",
+ tb->header_next, tb->termlines_used, rc));
+ return rc;
+}
+
+
+int __scols_print_range(struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ struct libscols_iter *itr,
+ struct libscols_line *end)
+{
+ int rc = 0;
+ struct libscols_line *ln;
+
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, "printing range"));
+
+ while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
+
+ int last = scols_iter_is_last(itr);
+
+ fput_line_open(tb);
+ rc = print_line(tb, ln, buf);
+ fput_line_close(tb, last, last);
+
+ if (end && ln == end)
+ break;
+
+ if (!last && want_repeat_header(tb))
+ __scols_print_header(tb, buf);
+ }
+
+ return rc;
+
+}
+
+int __scols_print_table(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ return __scols_print_range(tb, buf, &itr, NULL);
+}
+
+/* scols_walk_tree() callback to print tree line */
+static int print_tree_line(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl __attribute__((__unused__)),
+ void *data)
+{
+ struct libscols_buffer *buf = (struct libscols_buffer *) data;
+ int rc;
+
+ DBG(LINE, ul_debugobj(ln, " printing tree line"));
+
+ fput_line_open(tb);
+ rc = print_line(tb, ln, buf);
+ if (rc)
+ return rc;
+
+ if (has_children(ln))
+ fput_children_open(tb);
+
+ else {
+ int last_in_tree = scols_walk_is_last(tb, ln);
+ int last;
+
+ /* terminate all open last children for JSON */
+ if (scols_table_is_json(tb)) {
+ do {
+ last = (is_child(ln) && is_last_child(ln)) ||
+ (is_tree_root(ln) && is_last_tree_root(tb, ln));
+
+ fput_line_close(tb, last, last_in_tree);
+ if (last && is_child(ln))
+ fput_children_close(tb);
+
+ last_in_tree = 0;
+ ln = ln->parent;
+ } while(ln && last);
+
+ } else {
+ /* standard output */
+ last = (is_child(ln) && is_last_child(ln)) ||
+ (is_group_child(ln) && is_last_group_child(ln));
+
+ fput_line_close(tb, last, last_in_tree);
+ }
+ }
+
+ return 0;
+}
+
+int __scols_print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, "----printing-tree-----"));
+
+ return scols_walk_tree(tb, NULL, print_tree_line, (void *) buf);
+}
+
+static size_t strlen_line(struct libscols_line *ln)
+{
+ size_t i, sz = 0;
+
+ assert(ln);
+
+ for (i = 0; i < ln->ncells; i++) {
+ struct libscols_cell *ce = scols_line_get_cell(ln, i);
+ const char *data = ce ? scols_cell_get_data(ce) : NULL;
+
+ sz += data ? strlen(data) : 0;
+ }
+
+ return sz;
+}
+
+void __scols_cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ if (!tb)
+ return;
+
+ free_buffer(buf);
+
+ if (tb->priv_symbols) {
+ scols_table_set_symbols(tb, NULL);
+ tb->priv_symbols = 0;
+ }
+}
+
+int __scols_initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
+{
+ size_t bufsz, extra_bufsz = 0;
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+ int rc;
+
+ DBG(TAB, ul_debugobj(tb, "initialize printing"));
+ *buf = NULL;
+
+ if (!tb->symbols) {
+ rc = scols_table_set_default_symbols(tb);
+ if (rc)
+ goto err;
+ tb->priv_symbols = 1;
+ } else
+ tb->priv_symbols = 0;
+
+ if (tb->format == SCOLS_FMT_HUMAN)
+ tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER ? 0 :
+ tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 :
+ isatty(STDOUT_FILENO);
+
+ if (tb->is_term) {
+ size_t width = (size_t) scols_table_get_termwidth(tb);
+
+ if (tb->termreduce > 0 && tb->termreduce < width) {
+ width -= tb->termreduce;
+ scols_table_set_termwidth(tb, width);
+ }
+ bufsz = width;
+ } else
+ bufsz = BUFSIZ;
+
+ if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
+ tb->header_repeat = 0;
+
+ /*
+ * Estimate extra space necessary for tree, JSON or another output
+ * decoration.
+ */
+ if (scols_table_is_tree(tb))
+ extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
+
+ switch (tb->format) {
+ case SCOLS_FMT_RAW:
+ extra_bufsz += tb->ncols; /* separator between columns */
+ break;
+ case SCOLS_FMT_JSON:
+ if (tb->format == SCOLS_FMT_JSON)
+ extra_bufsz += tb->nlines * 3; /* indentation */
+ /* fallthrough */
+ case SCOLS_FMT_EXPORT:
+ {
+ struct libscols_column *cl;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
+ extra_bufsz += 2; /* separators */
+ }
+ break;
+ }
+ case SCOLS_FMT_HUMAN:
+ break;
+ }
+
+ /*
+ * Enlarge buffer if necessary, the buffer should be large enough to
+ * store line data and tree ascii art (or another decoration).
+ */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ size_t sz;
+
+ sz = strlen_line(ln) + extra_bufsz;
+ if (sz > bufsz)
+ bufsz = sz;
+ }
+
+ *buf = new_buffer(bufsz + 1); /* data + space for \0 */
+ if (!*buf) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /*
+ * Make sure groups members are in the same orders as the tree
+ */
+ if (has_groups(tb) && scols_table_is_tree(tb))
+ scols_groups_fix_members_order(tb);
+
+ if (tb->format == SCOLS_FMT_HUMAN) {
+ rc = __scols_calculate(tb, *buf);
+ if (rc != 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ __scols_cleanup_printing(tb, *buf);
+ return rc;
+}
+
diff --git a/utils/libsmartcols/src/smartcolsP.h b/utils/libsmartcols/src/smartcolsP.h
new file mode 100644
index 0000000..e36bb51
--- /dev/null
+++ b/utils/libsmartcols/src/smartcolsP.h
@@ -0,0 +1,468 @@
+/*
+ * smartcolsP.h - private library header file
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBSMARTCOLS_PRIVATE_H
+#define _LIBSMARTCOLS_PRIVATE_H
+
+#include "c.h"
+#include "list.h"
+#include "strutils.h"
+#include "color-names.h"
+#include "debug.h"
+
+#include "libsmartcols.h"
+
+/*
+ * Debug
+ */
+#define SCOLS_DEBUG_HELP (1 << 0)
+#define SCOLS_DEBUG_INIT (1 << 1)
+#define SCOLS_DEBUG_CELL (1 << 2)
+#define SCOLS_DEBUG_LINE (1 << 3)
+#define SCOLS_DEBUG_TAB (1 << 4)
+#define SCOLS_DEBUG_COL (1 << 5)
+#define SCOLS_DEBUG_BUFF (1 << 6)
+#define SCOLS_DEBUG_GROUP (1 << 7)
+#define SCOLS_DEBUG_ALL 0xFFFF
+
+UL_DEBUG_DECLARE_MASK(libsmartcols);
+#define DBG(m, x) __UL_DBG(libsmartcols, SCOLS_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x)
+#define DBG_FLUSH __UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols)
+#include "debugobj.h"
+
+/*
+ * Generic iterator
+ */
+struct libscols_iter {
+ struct list_head *p; /* current position */
+ struct list_head *head; /* start position */
+ int direction; /* SCOLS_ITER_{FOR,BACK}WARD */
+};
+
+/*
+ * Tree symbols
+ */
+struct libscols_symbols {
+ int refcount;
+
+ char *tree_branch;
+ char *tree_vert;
+ char *tree_right;
+
+ char *group_vert;
+ char *group_horz;
+ char *group_first_member;
+ char *group_last_member;
+ char *group_middle_member;
+ char *group_last_child;
+ char *group_middle_child;
+
+ char *title_padding;
+ char *cell_padding;
+};
+
+/*
+ * Table cells
+ */
+struct libscols_cell {
+ char *data;
+ char *color;
+ void *userdata;
+ int flags;
+};
+
+extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn);
+
+/*
+ * Table column
+ */
+struct libscols_column {
+ int refcount; /* reference counter */
+ size_t seqnum; /* column index */
+
+ size_t width; /* real column width */
+ size_t width_min; /* minimal width (usually header width) */
+ size_t width_max; /* maximal width */
+ size_t width_avg; /* average width, used to detect extreme fields */
+ size_t width_treeart; /* size of the tree ascii art */
+ double width_hint; /* hint (N < 1 is in percent of termwidth) */
+
+ size_t extreme_sum;
+ int extreme_count;
+
+ int json_type; /* SCOLS_JSON_* */
+
+ 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 *);
+ void *wrapfunc_data;
+
+
+ struct libscols_cell header;
+ struct list_head cl_columns;
+
+ struct libscols_table *table;
+
+ unsigned int is_extreme : 1, /* extreme width in the column */
+ is_groups : 1; /* print group chart */
+
+};
+
+#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
+#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
+
+enum {
+ SCOLS_GSTATE_NONE = 0, /* not activate yet */
+ SCOLS_GSTATE_FIRST_MEMBER,
+ SCOLS_GSTATE_MIDDLE_MEMBER,
+ SCOLS_GSTATE_LAST_MEMBER,
+ SCOLS_GSTATE_MIDDLE_CHILD,
+ SCOLS_GSTATE_LAST_CHILD,
+ SCOLS_GSTATE_CONT_MEMBERS,
+ SCOLS_GSTATE_CONT_CHILDREN
+};
+
+/*
+ * Every group needs at least 3 columns
+ */
+#define SCOLS_GRPSET_CHUNKSIZ 3
+
+struct libscols_group {
+ int refcount;
+
+ size_t nmembers;
+
+ struct list_head gr_members; /* head of line->ln_group */
+ struct list_head gr_children; /* head of line->ln_children */
+ struct list_head gr_groups; /* member of table->tb_groups */
+
+ int state; /* SCOLS_GSTATE_* */
+};
+
+/*
+ * Table line
+ */
+struct libscols_line {
+ int refcount;
+ size_t seqnum;
+
+ void *userdata;
+ char *color; /* default line color */
+
+ struct libscols_cell *cells; /* array with data */
+ size_t ncells; /* number of cells */
+
+ struct list_head ln_lines; /* member of table->tb_lines */
+ struct list_head ln_branch; /* head of line->ln_children */
+ struct list_head ln_children; /* member of line->ln_children or group->gr_children */
+ struct list_head ln_groups; /* member of group->gr_groups */
+
+ struct libscols_line *parent;
+ struct libscols_group *parent_group; /* for group childs */
+ struct libscols_group *group; /* for group members */
+};
+
+enum {
+ SCOLS_FMT_HUMAN = 0, /* default, human readable */
+ SCOLS_FMT_RAW, /* space separated */
+ SCOLS_FMT_EXPORT, /* COLNAME="data" ... */
+ SCOLS_FMT_JSON /* http://en.wikipedia.org/wiki/JSON */
+};
+
+/*
+ * The table
+ */
+struct libscols_table {
+ int refcount;
+ char *name; /* optional table name (for JSON) */
+ size_t ncols; /* number of columns */
+ size_t ntreecols; /* number of columns with SCOLS_FL_TREE */
+ size_t nlines; /* number of lines */
+ size_t termwidth; /* terminal width (number of columns) */
+ size_t termheight; /* terminal height (number of lines) */
+ size_t termreduce; /* extra blank space */
+ int termforce; /* SCOLS_TERMFORCE_* */
+ FILE *out; /* output stream */
+
+ char *colsep; /* column separator */
+ char *linesep; /* line separator */
+
+ struct list_head tb_columns;
+ struct list_head tb_lines;
+
+ struct list_head tb_groups; /* all defined groups */
+ struct libscols_group **grpset;
+ size_t grpset_size;
+
+ size_t ngrpchlds_pending; /* groups with not yet printed children */
+ struct libscols_line *walk_last_tree_root; /* last root, used by scols_walk_() */
+
+ struct libscols_symbols *symbols;
+ struct libscols_cell title; /* optional table title (for humans) */
+
+ int indent; /* indentation counter */
+ int indent_last_sep;/* last printed has been line separator */
+ int format; /* SCOLS_FMT_* */
+
+ size_t termlines_used; /* printed line counter */
+ size_t header_next; /* where repeat header */
+
+ /* flags */
+ unsigned int ascii :1, /* don't use unicode */
+ colors_wanted :1, /* enable colors */
+ is_term :1, /* isatty() */
+ padding_debug :1, /* output visible padding chars */
+ is_dummy_print :1, /* printing used for width calculation only */
+ maxout :1, /* maximize output */
+ minout :1, /* minimize output (mutually exclusive to maxout) */
+ header_repeat :1, /* print header after libscols_table->termheight */
+ header_printed :1, /* header already printed */
+ priv_symbols :1, /* default private symbols */
+ walk_last_done :1, /* last tree root walked */
+ no_headings :1, /* don't print header */
+ no_encode :1, /* don't care about control and non-printable chars */
+ no_linesep :1, /* don't print line separator */
+ no_wrap :1; /* never wrap lines */
+};
+
+#define IS_ITER_FORWARD(_i) ((_i)->direction == SCOLS_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i) ((_i)->direction == SCOLS_ITER_BACKWARD)
+
+#define SCOLS_ITER_INIT(itr, list) \
+ do { \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (list)->next : (list)->prev; \
+ (itr)->head = (list); \
+ } while(0)
+
+#define SCOLS_ITER_ITERATE(itr, res, restype, member) \
+ do { \
+ res = list_entry((itr)->p, restype, member); \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (itr)->p->next : (itr)->p->prev; \
+ } while(0)
+
+
+static inline int scols_iter_is_last(const struct libscols_iter *itr)
+{
+ if (!itr || !itr->head || !itr->p)
+ return 0;
+
+ return itr->p == itr->head;
+}
+
+/*
+ * line.c
+ */
+int scols_line_next_group_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld);
+
+
+/*
+ * table.c
+ */
+int scols_table_next_group(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_group **gr);
+
+/*
+ * buffer.c
+ */
+struct libscols_buffer;
+extern struct libscols_buffer *new_buffer(size_t sz);
+extern void free_buffer(struct libscols_buffer *buf);
+extern int buffer_reset_data(struct libscols_buffer *buf);
+extern int buffer_append_data(struct libscols_buffer *buf, const char *str);
+extern int buffer_append_ntimes(struct libscols_buffer *buf, size_t n, const char *str);
+extern int buffer_set_data(struct libscols_buffer *buf, const char *str);
+extern void buffer_set_art_index(struct libscols_buffer *buf);
+extern char *buffer_get_data(struct libscols_buffer *buf);
+extern size_t buffer_get_size(struct libscols_buffer *buf);
+extern char *buffer_get_safe_data(struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ size_t *cells,
+ const char *safechars);
+extern size_t buffer_get_safe_art_size(struct libscols_buffer *buf);
+
+/*
+ * grouping.c
+ */
+void scols_ref_group(struct libscols_group *gr);
+void scols_group_remove_children(struct libscols_group *gr);
+void scols_group_remove_members(struct libscols_group *gr);
+void scols_unref_group(struct libscols_group *gr);
+void scols_groups_fix_members_order(struct libscols_table *tb);
+int scols_groups_update_grpset(struct libscols_table *tb, struct libscols_line *ln);
+void scols_groups_reset_state(struct libscols_table *tb);
+struct libscols_group *scols_grpset_get_printable_children(struct libscols_table *tb);
+
+/*
+ * walk.c
+ */
+extern int scols_walk_tree(struct libscols_table *tb,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data);
+extern int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln);
+
+/*
+ * calculate.c
+ */
+extern int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf);
+
+/*
+ * print.c
+ */
+extern int __cell_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct libscols_buffer *buf);
+
+void __scols_cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf);
+int __scols_initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf);
+int __scols_print_tree(struct libscols_table *tb, struct libscols_buffer *buf);
+int __scols_print_table(struct libscols_table *tb, struct libscols_buffer *buf);
+int __scols_print_header(struct libscols_table *tb, struct libscols_buffer *buf);
+int __scols_print_title(struct libscols_table *tb);
+int __scols_print_range(struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ struct libscols_iter *itr,
+ struct libscols_line *end);
+
+/*
+ * fput.c
+ */
+extern void fput_indent(struct libscols_table *tb);
+extern void fput_table_open(struct libscols_table *tb);
+extern void fput_table_close(struct libscols_table *tb);
+extern void fput_children_open(struct libscols_table *tb);
+extern void fput_children_close(struct libscols_table *tb);
+extern void fput_line_open(struct libscols_table *tb);
+extern void fput_line_close(struct libscols_table *tb, int last, int last_in_table);
+
+static inline int is_tree_root(struct libscols_line *ln)
+{
+ return ln && !ln->parent && !ln->parent_group;
+}
+
+static inline int is_last_tree_root(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (!ln || !tb || tb->walk_last_tree_root != ln)
+ return 0;
+
+ return 1;
+}
+
+static inline int is_child(struct libscols_line *ln)
+{
+ return ln && ln->parent;
+}
+
+static inline int is_last_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch);
+}
+
+static inline int is_first_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent)
+ return 0;
+
+ return list_entry_is_first(&ln->ln_children, &ln->parent->ln_branch);
+}
+
+
+static inline int is_last_column(struct libscols_column *cl)
+{
+ struct libscols_column *next;
+
+ if (list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns))
+ return 1;
+
+ next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns);
+ if (next && scols_column_is_hidden(next) && is_last_column(next))
+ return 1;
+ return 0;
+}
+
+static inline int is_last_group_member(struct libscols_line *ln)
+{
+ if (!ln || !ln->group)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_groups, &ln->group->gr_members);
+}
+
+static inline int is_first_group_member(struct libscols_line *ln)
+{
+ if (!ln || !ln->group)
+ return 0;
+
+ return list_entry_is_first(&ln->ln_groups, &ln->group->gr_members);
+}
+
+static inline int is_group_member(struct libscols_line *ln)
+{
+ return ln && ln->group;
+}
+
+static inline int is_last_group_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent_group)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_children, &ln->parent_group->gr_children);
+}
+
+static inline int is_group_child(struct libscols_line *ln)
+{
+ return ln && ln->parent_group;
+}
+
+static inline int has_groups(struct libscols_table *tb)
+{
+ return tb && !list_empty(&tb->tb_groups);
+}
+
+static inline int has_children(struct libscols_line *ln)
+{
+ return ln && !list_empty(&ln->ln_branch);
+}
+
+static inline int has_group_children(struct libscols_line *ln)
+{
+ return ln && ln->group && !list_empty(&ln->group->gr_children);
+}
+
+#endif /* _LIBSMARTCOLS_PRIVATE_H */
diff --git a/utils/libsmartcols/src/symbols.c b/utils/libsmartcols/src/symbols.c
new file mode 100644
index 0000000..2fadfc7
--- /dev/null
+++ b/utils/libsmartcols/src/symbols.c
@@ -0,0 +1,293 @@
+/*
+ * symbols.c - routines for symbol handling
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: symbols
+ * @title: Symbols
+ * @short_description: can be used to overwrite default output chars (for ascii art)
+ *
+ * An API to access and modify data and information per symbol/symbol group.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_symbols:
+ *
+ * Returns: a pointer to a newly allocated struct libscols_symbols instance.
+ */
+struct libscols_symbols *scols_new_symbols(void)
+{
+ struct libscols_symbols *sy = calloc(1, sizeof(struct libscols_symbols));
+
+ if (!sy)
+ return NULL;
+ sy->refcount = 1;
+ return sy;
+}
+
+/**
+ * scols_ref_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * Increases the refcount of @sy.
+ */
+void scols_ref_symbols(struct libscols_symbols *sy)
+{
+ if (sy)
+ sy->refcount++;
+}
+
+/**
+ * scols_unref_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * Decreases the refcount of @sy.
+ */
+void scols_unref_symbols(struct libscols_symbols *sy)
+{
+ if (sy && --sy->refcount <= 0) {
+ free(sy->tree_branch);
+ free(sy->tree_vert);
+ free(sy->tree_right);
+ free(sy->group_last_member);
+ free(sy->group_middle_member);
+ free(sy->group_first_member);
+ free(sy->group_vert);
+ free(sy->group_horz);
+ free(sy->group_last_child);
+ free(sy->group_middle_child);
+ free(sy->title_padding);
+ free(sy->cell_padding);
+ free(sy);
+ }
+}
+
+/**
+ * scols_symbols_set_branch:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the branch part of a tree output
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_branch, str);
+}
+
+/**
+ * scols_symbols_set_vertical:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the vertical part of a tree output
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_vert, str);
+}
+
+/**
+ * scols_symbols_set_right:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the right part of a tree output
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_symbols_set_right(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_right, str);
+}
+
+/**
+ * scols_symbols_set_title_padding:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the symbols which fill title output
+ *
+ * The current implementation uses only the first byte from the padding string.
+ * A multibyte chars are not supported yet.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, title_padding, str);
+}
+
+/**
+ * scols_symbols_set_cell_padding:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the symbols which fill cells
+ *
+ * The padding char has to take up just one cell on the terminal.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, cell_padding, str);
+}
+
+
+/**
+ * scols_symbols_set_group_vertical:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the vertival line
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_vertical(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_vert, str);
+}
+
+/**
+ * scols_symbols_set_group_horizontal:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the horizontal line
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_horizontal(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_horz, str);
+}
+
+/**
+ * scols_symbols_set_group_first_member:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent first member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_first_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_first_member, str);
+}
+
+/**
+ * scols_symbols_set_group_last_member:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_last_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_last_member, str);
+}
+
+/**
+ * scols_symbols_set_group_middle:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent middle member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_middle_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_middle_member, str);
+}
+
+/**
+ * scols_symbols_set_group_last_child:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last child
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_last_child(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_last_child, str);
+}
+
+/**
+ * scols_symbols_set_group_middle_child:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last child
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_middle_child(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_middle_child, str);
+}
+
+/**
+ * scols_copy_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * Returns: a newly allocated copy of the @sy symbol group or NULL in case of an error.
+ */
+struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy)
+{
+ struct libscols_symbols *ret;
+ int rc;
+
+ assert(sy);
+ if (!sy)
+ return NULL;
+
+ ret = scols_new_symbols();
+ if (!ret)
+ return NULL;
+
+ rc = scols_symbols_set_branch(ret, sy->tree_branch);
+ if (!rc)
+ rc = scols_symbols_set_vertical(ret, sy->tree_vert);
+ if (!rc)
+ rc = scols_symbols_set_right(ret, sy->tree_right);
+ if (!rc)
+ rc = scols_symbols_set_group_vertical(ret, sy->group_vert);
+ if (!rc)
+ rc = scols_symbols_set_group_horizontal(ret, sy->group_horz);
+ if (!rc)
+ rc = scols_symbols_set_group_first_member(ret, sy->group_first_member);
+ if (!rc)
+ rc = scols_symbols_set_group_last_member(ret, sy->group_last_member);
+ if (!rc)
+ rc = scols_symbols_set_group_middle_member(ret, sy->group_middle_member);
+ if (!rc)
+ rc = scols_symbols_set_group_middle_child(ret, sy->group_middle_child);
+ if (!rc)
+ rc = scols_symbols_set_group_last_child(ret, sy->group_last_child);
+ if (!rc)
+ rc = scols_symbols_set_title_padding(ret, sy->title_padding);
+ if (!rc)
+ rc = scols_symbols_set_cell_padding(ret, sy->cell_padding);
+ if (!rc)
+ return ret;
+
+ scols_unref_symbols(ret);
+ return NULL;
+}
diff --git a/utils/libsmartcols/src/table.c b/utils/libsmartcols/src/table.c
new file mode 100644
index 0000000..a3ba21d
--- /dev/null
+++ b/utils/libsmartcols/src/table.c
@@ -0,0 +1,1691 @@
+/*
+ * table.c - functions handling the data at the table level
+ *
+ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: table
+ * @title: Table
+ * @short_description: container for rows and columns
+ *
+ * Table data manipulation API.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+
+#include "nls.h"
+#include "ttyutils.h"
+#include "smartcolsP.h"
+
+#ifdef HAVE_WIDECHAR
+#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char | */
+#define UTF_VR "\342\224\234" /* U+251C, Vertical and right |- */
+#define UTF_H "\342\224\200" /* U+2500, Horizontal - */
+#define UTF_UR "\342\224\224" /* U+2514, Up and right '- */
+
+#define UTF_V3 "\342\224\206" /* U+2506 Triple Dash Vertical | */
+#define UTF_H3 "\342\224\210" /* U+2504 Triple Dash Horizontal - */
+#define UTF_DR "\342\224\214" /* U+250C Down and Right ,- */
+#define UTF_DH "\342\224\254" /* U+252C Down and Horizontal |' */
+
+#define UTF_TR "\342\226\266" /* U+25B6 Black Right-Pointing Triangle > */
+#endif /* !HAVE_WIDECHAR */
+
+#define is_last_column(_tb, _cl) \
+ list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
+
+
+static void check_padding_debug(struct libscols_table *tb)
+{
+ const char *str;
+
+ assert(libsmartcols_debug_mask); /* debug has to be enabled! */
+
+ str = getenv("LIBSMARTCOLS_DEBUG_PADDING");
+ if (!str || (strcmp(str, "on") != 0 && strcmp(str, "1") != 0))
+ return;
+
+ DBG(INIT, ul_debugobj(tb, "padding debug: ENABLE"));
+ tb->padding_debug = 1;
+}
+
+/**
+ * scols_new_table:
+ *
+ * Returns: A newly allocated table.
+ */
+struct libscols_table *scols_new_table(void)
+{
+ struct libscols_table *tb;
+ int c, l;
+
+ tb = calloc(1, sizeof(struct libscols_table));
+ if (!tb)
+ return NULL;
+
+ tb->refcount = 1;
+ tb->out = stdout;
+
+ get_terminal_dimension(&c, &l);
+ tb->termwidth = c > 0 ? c : 80;
+ tb->termheight = l > 0 ? l : 24;
+
+ INIT_LIST_HEAD(&tb->tb_lines);
+ INIT_LIST_HEAD(&tb->tb_columns);
+ INIT_LIST_HEAD(&tb->tb_groups);
+
+ DBG(TAB, ul_debugobj(tb, "alloc"));
+ ON_DBG(INIT, check_padding_debug(tb));
+
+ return tb;
+}
+
+/**
+ * scols_ref_table:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Increases the refcount of @tb.
+ */
+void scols_ref_table(struct libscols_table *tb)
+{
+ if (tb)
+ tb->refcount++;
+}
+
+static void scols_table_remove_groups(struct libscols_table *tb)
+{
+ while (!list_empty(&tb->tb_groups)) {
+ struct libscols_group *gr = list_entry(tb->tb_groups.next,
+ struct libscols_group, gr_groups);
+ scols_group_remove_children(gr);
+ scols_group_remove_members(gr);
+ scols_unref_group(gr);
+ }
+}
+
+/**
+ * scols_unref_table:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Decreases the refcount of @tb. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_table(struct libscols_table *tb)
+{
+ if (tb && (--tb->refcount <= 0)) {
+ DBG(TAB, ul_debugobj(tb, "dealloc <-"));
+ scols_table_remove_groups(tb);
+ scols_table_remove_lines(tb);
+ scols_table_remove_columns(tb);
+ scols_unref_symbols(tb->symbols);
+ scols_reset_cell(&tb->title);
+ free(tb->grpset);
+ free(tb->linesep);
+ free(tb->colsep);
+ free(tb->name);
+ free(tb);
+ DBG(TAB, ul_debug("<- done"));
+ }
+}
+
+/* Private API */
+int scols_table_next_group(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_group **gr)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !gr)
+ return -EINVAL;
+ *gr = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_groups);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *gr, struct libscols_group, gr_groups);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_set_name:
+ * @tb: a pointer to a struct libscols_table instance
+ * @name: a name
+ *
+ * The table name is used for example for JSON top level object name.
+ *
+ * Returns: 0, a negative number in case of an error.
+ *
+ * Since: 2.27
+ */
+int scols_table_set_name(struct libscols_table *tb, const char *name)
+{
+ return strdup_to_struct_member(tb, name, name);
+}
+
+/**
+ * scols_table_get_name:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: The current name setting of the table @tb
+ *
+ * Since: 2.29
+ */
+const char *scols_table_get_name(const struct libscols_table *tb)
+{
+ return tb->name;
+}
+
+/**
+ * scols_table_get_title:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * The returned pointer is possible to modify by cell functions. Note that
+ * title output alignment on non-tty is hardcoded to 80 output chars. For the
+ * regular terminal it's based on terminal width.
+ *
+ * Returns: Title of the table, or NULL in case of blank title.
+ *
+ * Since: 2.28
+ */
+struct libscols_cell *scols_table_get_title(struct libscols_table *tb)
+{
+ return &tb->title;
+}
+
+/**
+ * scols_table_add_column:
+ * @tb: a pointer to a struct libscols_table instance
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Adds @cl to @tb's column list. The column cannot be shared between more
+ * tables.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+ int rc = 0;
+
+ if (!tb || !cl || cl->table)
+ return -EINVAL;
+
+ if (!list_empty(&cl->cl_columns))
+ return -EINVAL;
+
+ if (cl->flags & SCOLS_FL_TREE)
+ tb->ntreecols++;
+
+ DBG(TAB, ul_debugobj(tb, "add column"));
+ list_add_tail(&cl->cl_columns, &tb->tb_columns);
+ cl->seqnum = tb->ncols++;
+ cl->table = tb;
+ scols_ref_column(cl);
+
+ if (list_empty(&tb->tb_lines))
+ return 0;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ /* Realloc line cell arrays
+ */
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ rc = scols_line_alloc_cells(ln, tb->ncols);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_remove_column:
+ * @tb: a pointer to a struct libscols_table instance
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Removes @cl from @tb.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_remove_column(struct libscols_table *tb,
+ struct libscols_column *cl)
+{
+ if (!tb || !cl || !list_empty(&tb->tb_lines))
+ return -EINVAL;
+
+ if (cl->flags & SCOLS_FL_TREE)
+ tb->ntreecols--;
+
+ DBG(TAB, ul_debugobj(tb, "remove column"));
+ list_del_init(&cl->cl_columns);
+ tb->ncols--;
+ cl->table = NULL;
+ scols_unref_column(cl);
+ return 0;
+}
+
+/**
+ * scols_table_remove_columns:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Removes all of @tb's columns.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_remove_columns(struct libscols_table *tb)
+{
+ if (!tb || !list_empty(&tb->tb_lines))
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "remove all columns"));
+ while (!list_empty(&tb->tb_columns)) {
+ struct libscols_column *cl = list_entry(tb->tb_columns.next,
+ struct libscols_column, cl_columns);
+ scols_table_remove_column(tb, cl);
+ }
+ return 0;
+}
+
+/**
+ * scols_table_move_column:
+ * @tb: table
+ * @pre: column before the column
+ * @cl: column to move
+ *
+ * Move the @cl behind @pre. If the @pre is NULL then the @col is the first
+ * column in the table.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_move_column(struct libscols_table *tb,
+ struct libscols_column *pre,
+ struct libscols_column *cl)
+{
+ struct list_head *head;
+ struct libscols_iter itr;
+ struct libscols_column *p;
+ struct libscols_line *ln;
+ size_t n = 0, oldseq;
+
+ if (!tb || !cl)
+ return -EINVAL;
+
+ if (pre && pre->seqnum + 1 == cl->seqnum)
+ return 0;
+ if (pre == NULL && cl->seqnum == 0)
+ return 0;
+
+ DBG(TAB, ul_debugobj(tb, "move column %zu behind %zu",
+ cl->seqnum, pre? pre->seqnum : 0));
+
+ list_del_init(&cl->cl_columns); /* remove from old position */
+
+ head = pre ? &pre->cl_columns : &tb->tb_columns;
+ list_add(&cl->cl_columns, head); /* add to the new place */
+
+ oldseq = cl->seqnum;
+
+ /* fix seq. numbers */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &p) == 0)
+ p->seqnum = n++;
+
+ /* move data in lines */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ scols_line_move_cells(ln, cl->seqnum, oldseq);
+ return 0;
+}
+
+/**
+ * scols_table_new_column:
+ * @tb: table
+ * @name: column header
+ * @whint: column width hint (absolute width: N > 1; relative width: 0 < N < 1)
+ * @flags: flags integer
+ *
+ * This is shortcut for
+ *
+ * cl = scols_new_column();
+ * scols_column_set_....(cl, ...);
+ * scols_table_add_column(tb, cl);
+ *
+ * The column width is possible to define by:
+ *
+ * @whint: 0 < N < 1 : relative width, percent of terminal width
+ *
+ * @whint: N >= 1 : absolute width, empty column will be truncated to
+ * the column header width if no specified STRICTWIDTH flag
+ *
+ * Note that if table has disabled "maxout" flag (disabled by default) than
+ * relative width is used as a hint only. It's possible that column will be
+ * narrow if the specified size is too large for column data.
+ *
+ *
+ * If the width of all columns is greater than terminal width then library
+ * tries to reduce width of the individual columns. It's done in three stages:
+ *
+ * #1 reduce columns with SCOLS_FL_TRUNC flag and with relative width if the
+ * width is greater than width defined by @whint (@whint * terminal_width)
+ *
+ * #2 reduce all columns with SCOLS_FL_TRUNC flag
+ *
+ * #3 reduce all columns with relative width
+ *
+ * The next stage is always used if the previous stage is unsuccessful. Note
+ * that SCOLS_FL_WRAP is interpreted as SCOLS_FL_TRUNC when calculate column
+ * width (if custom wrap function is not specified), but the final text is not
+ * truncated, but wrapped to multi-line cell.
+ *
+ *
+ * The column is necessary to address by sequential number. The first defined
+ * column has the colnum = 0. For example:
+ *
+ * scols_table_new_column(tab, "FOO", 0.5, 0); // colnum = 0
+ * scols_table_new_column(tab, "BAR", 0.5, 0); // colnum = 1
+ * .
+ * .
+ * scols_line_get_cell(line, 0); // FOO column
+ * scols_line_get_cell(line, 1); // BAR column
+ *
+ * Returns: newly allocated column
+ */
+struct libscols_column *scols_table_new_column(struct libscols_table *tb,
+ const char *name,
+ double whint,
+ int flags)
+{
+ struct libscols_column *cl;
+ struct libscols_cell *hr;
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "new column name=%s, whint=%g, flags=%d",
+ name, whint, flags));
+ cl = scols_new_column();
+ if (!cl)
+ return NULL;
+
+ /* set column name */
+ hr = scols_column_get_header(cl);
+ if (!hr)
+ goto err;
+ if (scols_cell_set_data(hr, name))
+ goto err;
+
+ scols_column_set_whint(cl, whint);
+ scols_column_set_flags(cl, flags);
+
+ if (scols_table_add_column(tb, cl)) /* this increments column ref-counter */
+ goto err;
+
+ scols_unref_column(cl);
+ return cl;
+err:
+ scols_unref_column(cl);
+ return NULL;
+}
+
+/**
+ * scols_table_next_column:
+ * @tb: a pointer to a struct libscols_table instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @cl: a pointer to a pointer to a struct libscols_column instance
+ *
+ * Returns the next column of @tb via @cl.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_next_column(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_column **cl)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !cl)
+ return -EINVAL;
+ *cl = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_columns);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *cl, struct libscols_column, cl_columns);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_set_columns_iter:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @cl: tab entry
+ *
+ * Sets @iter to the position of @cl in the file @tb.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ *
+ * Since: 2.35
+ */
+int scols_table_set_columns_iter(
+ struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_column *cl)
+{
+ if (!tb || !itr || !cl)
+ return -EINVAL;
+
+ if (cl->table != tb)
+ return -EINVAL;
+
+ SCOLS_ITER_INIT(itr, &tb->tb_columns);
+ itr->p = &cl->cl_columns;
+
+ return 0;
+}
+
+/**
+ * scols_table_get_ncols:
+ * @tb: table
+ *
+ * Returns: the ncols table member.
+ */
+size_t scols_table_get_ncols(const struct libscols_table *tb)
+{
+ return tb->ncols;
+}
+
+/**
+ * scols_table_get_nlines:
+ * @tb: table
+ *
+ * Returns: the nlines table member.
+ */
+size_t scols_table_get_nlines(const struct libscols_table *tb)
+{
+ return tb->nlines;
+}
+
+/**
+ * scols_table_set_stream:
+ * @tb: table
+ * @stream: output stream
+ *
+ * Sets the output stream for table @tb.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_set_stream(struct libscols_table *tb, FILE *stream)
+{
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "setting alternative stream"));
+ tb->out = stream;
+ return 0;
+}
+
+/**
+ * scols_table_get_stream:
+ * @tb: table
+ *
+ * Gets the output stream for table @tb.
+ *
+ * Returns: stream pointer, NULL in case of an error or an unset stream.
+ */
+FILE *scols_table_get_stream(const struct libscols_table *tb)
+{
+ return tb->out;
+}
+
+/**
+ * scols_table_reduce_termwidth:
+ * @tb: table
+ * @reduce: width
+ *
+ * If necessary then libsmartcols use all terminal width, the @reduce setting
+ * provides extra space (for example for borders in ncurses applications).
+ *
+ * The @reduce must be smaller than terminal width, otherwise it's silently
+ * ignored. The reduction is not applied when STDOUT_FILENO is not terminal.
+ *
+ * Note that after output initialization (scols_table_print_* calls) the width
+ * will be reduced, this behavior affects subsequenced scols_table_get_termwidth()
+ * calls.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "reduce terminal width: %zu", reduce));
+ tb->termreduce = reduce;
+ return 0;
+}
+
+/**
+ * scols_table_get_column:
+ * @tb: table
+ * @n: number of column (0..N)
+ *
+ * Returns: pointer to column or NULL
+ */
+struct libscols_column *scols_table_get_column(struct libscols_table *tb,
+ size_t n)
+{
+ struct libscols_iter itr;
+ struct libscols_column *cl;
+
+ if (!tb)
+ return NULL;
+ if (n >= tb->ncols)
+ return NULL;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (cl->seqnum == n)
+ return cl;
+ }
+ return NULL;
+}
+
+/**
+ * scols_table_add_line:
+ * @tb: table
+ * @ln: line
+ *
+ * Note that this function calls scols_line_alloc_cells() if number
+ * of the cells in the line is too small for @tb.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (!tb || !ln)
+ return -EINVAL;
+
+ if (!list_empty(&ln->ln_lines))
+ return -EINVAL;
+
+ if (tb->ncols > ln->ncells) {
+ int rc = scols_line_alloc_cells(ln, tb->ncols);
+ if (rc)
+ return rc;
+ }
+
+ DBG(TAB, ul_debugobj(tb, "add line"));
+ list_add_tail(&ln->ln_lines, &tb->tb_lines);
+ ln->seqnum = tb->nlines++;
+ scols_ref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_table_remove_line:
+ * @tb: table
+ * @ln: line
+ *
+ * Note that this function does not destroy the parent<->child relationship between lines.
+ * You have to call scols_line_remove_child()
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_remove_line(struct libscols_table *tb,
+ struct libscols_line *ln)
+{
+ if (!tb || !ln)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "remove line"));
+ list_del_init(&ln->ln_lines);
+ tb->nlines--;
+ scols_unref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_table_remove_lines:
+ * @tb: table
+ *
+ * This empties the table and also destroys all the parent<->child relationships.
+ */
+void scols_table_remove_lines(struct libscols_table *tb)
+{
+ if (!tb)
+ return;
+
+ DBG(TAB, ul_debugobj(tb, "remove all lines"));
+ while (!list_empty(&tb->tb_lines)) {
+ struct libscols_line *ln = list_entry(tb->tb_lines.next,
+ struct libscols_line, ln_lines);
+ if (ln->parent)
+ scols_line_remove_child(ln->parent, ln);
+ scols_table_remove_line(tb, ln);
+ }
+}
+
+/**
+ * scols_table_next_line:
+ * @tb: a pointer to a struct libscols_table instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @ln: a pointer to a pointer to a struct libscols_line instance
+ *
+ * Finds the next line and returns a pointer to it via @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_next_line(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_line **ln)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !ln)
+ return -EINVAL;
+ *ln = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_lines);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *ln, struct libscols_line, ln_lines);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_new_line:
+ * @tb: table
+ * @parent: parental line or NULL
+ *
+ * This is shortcut for
+ *
+ * ln = scols_new_line();
+ * scols_table_add_line(tb, ln);
+ * scols_line_add_child(parent, ln);
+ *
+ *
+ * Returns: newly allocate line
+ */
+struct libscols_line *scols_table_new_line(struct libscols_table *tb,
+ struct libscols_line *parent)
+{
+ struct libscols_line *ln;
+
+ if (!tb)
+ return NULL;
+
+ ln = scols_new_line();
+ if (!ln)
+ return NULL;
+
+ if (scols_table_add_line(tb, ln))
+ goto err;
+ if (parent)
+ scols_line_add_child(parent, ln);
+
+ scols_unref_line(ln); /* ref-counter incremented by scols_table_add_line() */
+ return ln;
+err:
+ scols_unref_line(ln);
+ return NULL;
+}
+
+/**
+ * scols_table_get_line:
+ * @tb: table
+ * @n: column number (0..N)
+ *
+ * Returns: a line or NULL
+ */
+struct libscols_line *scols_table_get_line(struct libscols_table *tb,
+ size_t n)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+
+ if (!tb)
+ return NULL;
+ if (n >= tb->nlines)
+ return NULL;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->seqnum == n)
+ return ln;
+ }
+ return NULL;
+}
+
+/**
+ * scols_copy_table:
+ * @tb: table
+ *
+ * Creates a new independent table copy, except struct libscols_symbols that
+ * are shared between the tables.
+ *
+ * Returns: a newly allocated copy of @tb
+ */
+struct libscols_table *scols_copy_table(struct libscols_table *tb)
+{
+ struct libscols_table *ret;
+ struct libscols_line *ln;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ if (!tb)
+ return NULL;
+ ret = scols_new_table();
+ if (!ret)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "copy"));
+
+ if (tb->symbols)
+ scols_table_set_symbols(ret, tb->symbols);
+
+ /* columns */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ cl = scols_copy_column(cl);
+ if (!cl)
+ goto err;
+ if (scols_table_add_column(ret, cl))
+ goto err;
+ scols_unref_column(cl);
+ }
+
+ /* lines */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ struct libscols_line *newln = scols_copy_line(ln);
+ if (!newln)
+ goto err;
+ if (scols_table_add_line(ret, newln))
+ goto err;
+ if (ln->parent) {
+ struct libscols_line *p =
+ scols_table_get_line(ret, ln->parent->seqnum);
+ if (p)
+ scols_line_add_child(p, newln);
+ }
+ scols_unref_line(newln);
+ }
+
+ /* separators */
+ if (scols_table_set_column_separator(ret, tb->colsep) ||
+ scols_table_set_line_separator(ret, tb->linesep))
+ goto err;
+
+ return ret;
+err:
+ scols_unref_table(ret);
+ return NULL;
+}
+
+/**
+ * scols_table_set_default_symbols:
+ * @tb: table
+ *
+ * The library check the current environment to select ASCII or UTF8 symbols.
+ * This default behavior could be controlled by scols_table_enable_ascii().
+ *
+ * Use scols_table_set_symbols() to unset symbols or use your own setting.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_default_symbols(struct libscols_table *tb)
+{
+ struct libscols_symbols *sy;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "setting default symbols"));
+
+ sy = scols_new_symbols();
+ if (!sy)
+ return -ENOMEM;
+
+#if defined(HAVE_WIDECHAR)
+ if (!scols_table_is_ascii(tb) &&
+ !strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ /* tree chart */
+ scols_symbols_set_branch(sy, UTF_VR UTF_H);
+ scols_symbols_set_vertical(sy, UTF_V " ");
+ scols_symbols_set_right(sy, UTF_UR UTF_H);
+ /* groups chart */
+ scols_symbols_set_group_horizontal(sy, UTF_H3);
+ scols_symbols_set_group_vertical(sy, UTF_V3);
+
+ scols_symbols_set_group_first_member(sy, UTF_DR UTF_H3 UTF_TR);
+ scols_symbols_set_group_last_member(sy, UTF_UR UTF_DH UTF_TR);
+ scols_symbols_set_group_middle_member(sy, UTF_VR UTF_H3 UTF_TR);
+ scols_symbols_set_group_last_child(sy, UTF_UR UTF_H3);
+ scols_symbols_set_group_middle_child(sy, UTF_VR UTF_H3);
+ } else
+#endif
+ {
+ /* tree chart */
+ scols_symbols_set_branch(sy, "|-");
+ scols_symbols_set_vertical(sy, "| ");
+ scols_symbols_set_right(sy, "`-");
+ /* groups chart */
+ scols_symbols_set_group_horizontal(sy, "-");
+ scols_symbols_set_group_vertical(sy, "|");
+
+ scols_symbols_set_group_first_member(sy, ",->");
+ scols_symbols_set_group_last_member(sy, "'->");
+ scols_symbols_set_group_middle_member(sy, "|->");
+ scols_symbols_set_group_last_child(sy, "`-");
+ scols_symbols_set_group_middle_child(sy, "|-");
+ }
+ scols_symbols_set_title_padding(sy, " ");
+ scols_symbols_set_cell_padding(sy, " ");
+
+ rc = scols_table_set_symbols(tb, sy);
+ scols_unref_symbols(sy);
+ return rc;
+}
+
+
+/**
+ * scols_table_set_symbols:
+ * @tb: table
+ * @sy: symbols or NULL
+ *
+ * Add a reference to @sy from the table. The symbols are used by library to
+ * draw tree output. If no symbols are used for the table then library creates
+ * default temporary symbols to draw output by scols_table_set_default_symbols().
+ *
+ * If @sy is NULL then remove reference from the currently used symbols.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_symbols(struct libscols_table *tb,
+ struct libscols_symbols *sy)
+{
+ if (!tb)
+ return -EINVAL;
+
+ /* remove old */
+ if (tb->symbols) {
+ DBG(TAB, ul_debugobj(tb, "remove symbols reference"));
+ scols_unref_symbols(tb->symbols);
+ tb->symbols = NULL;
+ }
+
+ /* set new */
+ if (sy) { /* ref user defined */
+ DBG(TAB, ul_debugobj(tb, "set symbols"));
+ tb->symbols = sy;
+ scols_ref_symbols(sy);
+ }
+ return 0;
+}
+
+/**
+ * scols_table_get_symbols:
+ * @tb: table
+ *
+ * Returns: pointer to symbols table.
+ *
+ * Since: 2.29
+ */
+struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb)
+{
+ return tb->symbols;
+}
+
+/**
+ * scols_table_enable_nolinesep:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable line separator printing. This is useful if you want to
+ * re-printing the same line more than once (e.g. progress bar). Don't use it
+ * if you're not sure.
+ *
+ * Note that for the last line in the table the separator is disabled at all.
+ * The library differentiate between table terminator and line terminator
+ * (although for standard output \n byte is used in both cases).
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_nolinesep(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "nolinesep: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_linesep = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_is_nolinesep:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if line separator printing is disabled.
+ *
+ * Since: 2.29
+ */
+int scols_table_is_nolinesep(const struct libscols_table *tb)
+{
+ return tb->no_linesep;
+}
+
+/**
+ * scols_table_enable_colors:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable colors.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_colors(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "colors: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->colors_wanted = enable;
+ return 0;
+}
+
+/**
+ * scols_table_enable_raw:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable raw output format. The parsable output formats
+ * (export, raw, JSON, ...) are mutually exclusive.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_raw(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "raw: %s", enable ? "ENABLE" : "DISABLE"));
+ if (enable)
+ tb->format = SCOLS_FMT_RAW;
+ else if (tb->format == SCOLS_FMT_RAW)
+ tb->format = 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_json:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable JSON output format. The parsable output formats
+ * (export, raw, JSON, ...) are mutually exclusive.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.27
+ */
+int scols_table_enable_json(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "json: %s", enable ? "ENABLE" : "DISABLE"));
+ if (enable)
+ tb->format = SCOLS_FMT_JSON;
+ else if (tb->format == SCOLS_FMT_JSON)
+ tb->format = 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_export:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable export output format (COLUMNAME="value" ...).
+ * The parsable output formats (export and raw) are mutually exclusive.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_export(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "export: %s", enable ? "ENABLE" : "DISABLE"));
+ if (enable)
+ tb->format = SCOLS_FMT_EXPORT;
+ else if (tb->format == SCOLS_FMT_EXPORT)
+ tb->format = 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_ascii:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * The ASCII-only output is relevant for tree-like outputs. The library
+ * checks if the current environment is UTF8 compatible by default. This
+ * function overrides this check and force the library to use ASCII chars
+ * for the tree.
+ *
+ * If a custom libcols_symbols are specified (see scols_table_set_symbols()
+ * then ASCII flag setting is ignored.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_ascii(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "ascii: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->ascii = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_noheadings:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable header line.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_headings = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_header_repeat:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable header line repeat. The header line is printed only once by
+ * default. Note that the flag will be silently ignored and disabled if the
+ * output is not on terminal or output format is JSON, raw, etc.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_enable_header_repeat(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "header-repeat: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->header_repeat = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_maxout:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * The extra space after last column is ignored by default. The output
+ * maximization add padding for all columns.
+ *
+ * This setting is mutually exclusive to scols_table_enable_minout().
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_maxout(struct libscols_table *tb, int enable)
+{
+ if (!tb || tb->minout)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->maxout = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_minout:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Force library to terminate line after last column with data. The extra
+ * padding is not added to the empty cells at the end of the line. The default is fill
+ * tailing empty cells except the last line cell.
+ *
+ * This setting is mutually exclusive to scols_table_enable_maxout().
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.35
+ */
+int scols_table_enable_minout(struct libscols_table *tb, int enable)
+{
+ if (!tb || tb->maxout)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "minout: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->minout = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_nowrap:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Never continue on next line, remove last column(s) when too large, truncate last column.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_table_enable_nowrap(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "nowrap: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_wrap = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_is_nowrap:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if nowrap is enabled.
+ *
+ * Since: 2.29
+ */
+int scols_table_is_nowrap(const struct libscols_table *tb)
+{
+ return tb->no_wrap;
+}
+
+/**
+ * scols_table_enable_noencoding:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * The library encode non-printable and control chars by \xHEX by default.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_enable_noencoding(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "encoding: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_encode = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_is_noencoding:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if encoding is disabled.
+ *
+ * Since: 2.31
+ */
+int scols_table_is_noencoding(const struct libscols_table *tb)
+{
+ return tb->no_encode;
+}
+
+/**
+ * scols_table_colors_wanted:
+ * @tb: table
+ *
+ * Returns: 1 if colors are enabled.
+ */
+int scols_table_colors_wanted(const struct libscols_table *tb)
+{
+ return tb->colors_wanted;
+}
+
+/**
+ * scols_table_is_empty:
+ * @tb: table
+ *
+ * Returns: 1 if the table is empty.
+ */
+int scols_table_is_empty(const struct libscols_table *tb)
+{
+ return !tb->nlines;
+}
+
+/**
+ * scols_table_is_ascii:
+ * @tb: table
+ *
+ * Returns: 1 if ASCII tree is enabled.
+ */
+int scols_table_is_ascii(const struct libscols_table *tb)
+{
+ return tb->ascii;
+}
+
+/**
+ * scols_table_is_noheadings:
+ * @tb: table
+ *
+ * Returns: 1 if header output is disabled.
+ */
+int scols_table_is_noheadings(const struct libscols_table *tb)
+{
+ return tb->no_headings;
+}
+
+/**
+ * scols_table_is_header_repeat
+ * @tb: table
+ *
+ * Returns: 1 if header repeat is enabled.
+ *
+ * Since: 2.31
+ */
+int scols_table_is_header_repeat(const struct libscols_table *tb)
+{
+ return tb->header_repeat;
+}
+
+/**
+ * scols_table_is_export:
+ * @tb: table
+ *
+ * Returns: 1 if export output format is enabled.
+ */
+int scols_table_is_export(const struct libscols_table *tb)
+{
+ return tb->format == SCOLS_FMT_EXPORT;
+}
+
+/**
+ * scols_table_is_raw:
+ * @tb: table
+ *
+ * Returns: 1 if raw output format is enabled.
+ */
+int scols_table_is_raw(const struct libscols_table *tb)
+{
+ return tb->format == SCOLS_FMT_RAW;
+}
+
+/**
+ * scols_table_is_json:
+ * @tb: table
+ *
+ * Returns: 1 if JSON output format is enabled.
+ *
+ * Since: 2.27
+ */
+int scols_table_is_json(const struct libscols_table *tb)
+{
+ return tb->format == SCOLS_FMT_JSON;
+}
+
+/**
+ * scols_table_is_maxout
+ * @tb: table
+ *
+ * Returns: 1 if output maximization is enabled or 0
+ */
+int scols_table_is_maxout(const struct libscols_table *tb)
+{
+ return tb->maxout;
+}
+
+/**
+ * scols_table_is_minout
+ * @tb: table
+ *
+ * Returns: 1 if output minimization is enabled or 0
+ *
+ * Since: 2.35
+ */
+int scols_table_is_minout(const struct libscols_table *tb)
+{
+ return tb->minout;
+}
+
+/**
+ * scols_table_is_tree:
+ * @tb: table
+ *
+ * Returns: returns 1 tree-like output is expected.
+ */
+int scols_table_is_tree(const struct libscols_table *tb)
+{
+ return tb->ntreecols > 0;
+}
+
+/**
+ * scols_table_set_column_separator:
+ * @tb: table
+ * @sep: separator
+ *
+ * Sets the column separator of @tb to @sep.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
+{
+ return strdup_to_struct_member(tb, colsep, sep);
+}
+
+/**
+ * scols_table_set_line_separator:
+ * @tb: table
+ * @sep: separator
+ *
+ * Sets the line separator of @tb to @sep.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
+{
+ return strdup_to_struct_member(tb, linesep, sep);
+}
+
+/**
+ * scols_table_get_column_separator:
+ * @tb: table
+ *
+ * Returns: @tb column separator, NULL in case of an error
+ */
+const char *scols_table_get_column_separator(const struct libscols_table *tb)
+{
+ return tb->colsep;
+}
+
+/**
+ * scols_table_get_line_separator:
+ * @tb: table
+ *
+ * Returns: @tb line separator, NULL in case of an error
+ */
+const char *scols_table_get_line_separator(const struct libscols_table *tb)
+{
+ return tb->linesep;
+}
+/* for lines in the struct libscols_line->ln_lines list */
+static int cells_cmp_wrapper_lines(struct list_head *a, struct list_head *b, void *data)
+{
+ struct libscols_column *cl = (struct libscols_column *) data;
+ struct libscols_line *ra, *rb;
+ struct libscols_cell *ca, *cb;
+
+ assert(a);
+ assert(b);
+ assert(cl);
+
+ ra = list_entry(a, struct libscols_line, ln_lines);
+ rb = list_entry(b, struct libscols_line, ln_lines);
+ ca = scols_line_get_cell(ra, cl->seqnum);
+ cb = scols_line_get_cell(rb, cl->seqnum);
+
+ return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
+}
+
+/* for lines in the struct libscols_line->ln_children list */
+static int cells_cmp_wrapper_children(struct list_head *a, struct list_head *b, void *data)
+{
+ struct libscols_column *cl = (struct libscols_column *) data;
+ struct libscols_line *ra, *rb;
+ struct libscols_cell *ca, *cb;
+
+ assert(a);
+ assert(b);
+ assert(cl);
+
+ ra = list_entry(a, struct libscols_line, ln_children);
+ rb = list_entry(b, struct libscols_line, ln_children);
+ ca = scols_line_get_cell(ra, cl->seqnum);
+ cb = scols_line_get_cell(rb, cl->seqnum);
+
+ return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
+}
+
+
+static int sort_line_children(struct libscols_line *ln, struct libscols_column *cl)
+{
+ struct list_head *p;
+
+ if (!list_empty(&ln->ln_branch)) {
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ sort_line_children(chld, cl);
+ }
+
+ list_sort(&ln->ln_branch, cells_cmp_wrapper_children, cl);
+ }
+
+ if (is_first_group_member(ln)) {
+ list_for_each(p, &ln->group->gr_children) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ sort_line_children(chld, cl);
+ }
+
+ list_sort(&ln->group->gr_children, cells_cmp_wrapper_children, cl);
+ }
+
+ return 0;
+}
+
+/**
+ * scols_sort_table:
+ * @tb: table
+ * @cl: order by this column
+ *
+ * Orders the table by the column. See also scols_column_set_cmpfunc(). If the
+ * tree output is enabled then children in the tree are recursively sorted too.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl)
+{
+ if (!tb || !cl || !cl->cmpfunc)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "sorting table"));
+ list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl);
+
+ if (scols_table_is_tree(tb)) {
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ sort_line_children(ln, cl);
+ }
+
+ return 0;
+}
+
+static struct libscols_line *move_line_and_children(struct libscols_line *ln, struct libscols_line *pre)
+{
+ if (pre) {
+ list_del_init(&ln->ln_lines); /* remove from old position */
+ list_add(&ln->ln_lines, &pre->ln_lines); /* add to the new place (behind @pre) */
+ }
+ pre = ln;
+
+ if (!list_empty(&ln->ln_branch)) {
+ struct list_head *p;
+
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ pre = move_line_and_children(chld, pre);
+ }
+ }
+
+ return pre;
+}
+
+/**
+ * scols_sort_table_by_tree:
+ * @tb: table
+ *
+ * Reorders lines in the table by parent->child relation. Note that order of
+ * the lines in the table is independent on the tree hierarchy.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_sort_table_by_tree(struct libscols_table *tb)
+{
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "sorting table by tree"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent)
+ continue;
+
+ move_line_and_children(ln, NULL);
+ }
+
+ return 0;
+}
+
+
+/**
+ * scols_table_set_termforce:
+ * @tb: table
+ * @force: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO}
+ *
+ * Forces library to use stdout as terminal, non-terminal or use automatic
+ * detection (default).
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_termforce(struct libscols_table *tb, int force)
+{
+ if (!tb)
+ return -EINVAL;
+ tb->termforce = force;
+ return 0;
+}
+
+/**
+ * scols_table_get_termforce:
+ * @tb: table
+ *
+ * Returns: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO} or a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_get_termforce(const struct libscols_table *tb)
+{
+ return tb->termforce;
+}
+
+/**
+ * scols_table_set_termwidth
+ * @tb: table
+ * @width: terminal width
+ *
+ * The library automatically detects terminal width or defaults to 80 chars if
+ * detections is unsuccessful. This function override this behaviour.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_termwidth(struct libscols_table *tb, size_t width)
+{
+ DBG(TAB, ul_debugobj(tb, "set terminatl width: %zu", width));
+ tb->termwidth = width;
+ return 0;
+}
+
+/**
+ * scols_table_get_termwidth
+ * @tb: table
+ *
+ * Returns: terminal width.
+ */
+size_t scols_table_get_termwidth(const struct libscols_table *tb)
+{
+ return tb->termwidth;
+}
+
+/**
+ * scols_table_set_termheight
+ * @tb: table
+ * @height: terminal height (number of lines)
+ *
+ * The library automatically detects terminal height or defaults to 24 lines if
+ * detections is unsuccessful. This function override this behaviour.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_set_termheight(struct libscols_table *tb, size_t height)
+{
+ DBG(TAB, ul_debugobj(tb, "set terminatl height: %zu", height));
+ tb->termheight = height;
+ return 0;
+}
+
+/**
+ * scols_table_get_termheight
+ * @tb: table
+ *
+ * Returns: terminal height (number of lines).
+ *
+ * Since: 2.31
+ */
+size_t scols_table_get_termheight(const struct libscols_table *tb)
+{
+ return tb->termheight;
+}
diff --git a/utils/libsmartcols/src/version.c b/utils/libsmartcols/src/version.c
new file mode 100644
index 0000000..e592ccc
--- /dev/null
+++ b/utils/libsmartcols/src/version.c
@@ -0,0 +1,62 @@
+/*
+ * version.c - Return the version of the library
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * See COPYING.libmount for the License of this software.
+ */
+
+/**
+ * SECTION: version-utils
+ * @title: Version functions
+ * @short_description: functions to get the library version.
+ *
+ * Note that library version is not the same thing as SONAME version. The
+ * libsmarcols uses symbols versioning and SONAME is not modified for releases.
+ *
+ * The library version and symbols version follow util-linux package versioning.
+ */
+
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+static const char *lib_version = LIBSMARTCOLS_VERSION;
+
+/**
+ * scols_parse_version_string:
+ * @ver_string: version string (e.g "2.18.0")
+ *
+ * Returns: release version code.
+ */
+int scols_parse_version_string(const char *ver_string)
+{
+ const char *cp;
+ int version = 0;
+
+ assert(ver_string);
+
+ for (cp = ver_string; *cp; cp++) {
+ if (*cp == '.')
+ continue;
+ if (!isdigit(*cp))
+ break;
+ version = (version * 10) + (*cp - '0');
+ }
+ return version;
+}
+
+/**
+ * scols_get_library_version:
+ * @ver_string: return pointer to the static library version string if not NULL
+ *
+ * Returns: release version number.
+ */
+int scols_get_library_version(const char **ver_string)
+{
+ if (ver_string)
+ *ver_string = lib_version;
+
+ return scols_parse_version_string(lib_version);
+}
+
diff --git a/utils/libsmartcols/src/walk.c b/utils/libsmartcols/src/walk.c
new file mode 100644
index 0000000..a75fde6
--- /dev/null
+++ b/utils/libsmartcols/src/walk.c
@@ -0,0 +1,152 @@
+#include "smartcolsP.h"
+
+static int walk_line(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data)
+{
+ int rc = 0;
+
+ DBG(LINE, ul_debugobj(ln, " wall line"));
+
+ /* we list group children in __scols_print_tree() after tree root node */
+ if (is_group_member(ln) && is_last_group_member(ln) && has_group_children(ln))
+ tb->ngrpchlds_pending++;
+
+ if (has_groups(tb))
+ rc = scols_groups_update_grpset(tb, ln);
+ if (rc == 0)
+ rc = callback(tb, ln, cl, data);
+
+ /* children */
+ if (rc == 0 && has_children(ln)) {
+ struct list_head *p;
+
+ DBG(LINE, ul_debugobj(ln, " children walk"));
+
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld = list_entry(p,
+ struct libscols_line, ln_children);
+
+ rc = walk_line(tb, chld, cl, callback, data);
+ if (rc)
+ break;
+ }
+ }
+
+ DBG(LINE, ul_debugobj(ln, "<- walk line done [rc=%d]", rc));
+ return rc;
+}
+
+/* last line in the tree? */
+int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (tb->walk_last_done == 0)
+ return 0;
+ if (tb->ngrpchlds_pending > 0)
+ return 0;
+ if (has_children(ln))
+ return 0;
+ if (is_tree_root(ln) && !is_last_tree_root(tb, ln))
+ return 0;
+ if (is_group_member(ln) && (!is_last_group_member(ln) || has_group_children(ln)))
+ return 0;
+ if (is_child(ln)) {
+ struct libscols_line *parent = ln->parent;
+
+ if (!is_last_child(ln))
+ return 0;
+ while (parent) {
+ if (is_child(parent) && !is_last_child(parent))
+ return 0;
+ if (!parent->parent)
+ break;
+ parent = parent->parent;
+ }
+ if (is_tree_root(parent) && !is_last_tree_root(tb, parent))
+ return 0;
+ }
+ if (is_group_child(ln) && !is_last_group_child(ln))
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "last in table"));
+ return 1;
+}
+
+int scols_walk_tree(struct libscols_table *tb,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data)
+{
+ int rc = 0;
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, ">> walk start"));
+
+ /* init */
+ tb->ngrpchlds_pending = 0;
+ tb->walk_last_tree_root = NULL;
+ tb->walk_last_done = 0;
+
+ if (has_groups(tb))
+ scols_groups_reset_state(tb);
+
+ /* set pointer to last tree root */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (!tb->walk_last_tree_root)
+ tb->walk_last_tree_root = ln;
+ if (is_child(ln) || is_group_child(ln))
+ continue;
+ tb->walk_last_tree_root = ln;
+ }
+
+ /* walk */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent || ln->parent_group)
+ continue;
+
+ if (tb->walk_last_tree_root == ln)
+ tb->walk_last_done = 1;
+ rc = walk_line(tb, ln, cl, callback, data);
+
+ /* walk group's children */
+ while (rc == 0 && tb->ngrpchlds_pending) {
+ struct libscols_group *gr = scols_grpset_get_printable_children(tb);
+ struct list_head *p;
+
+ DBG(LINE, ul_debugobj(ln, " walk group children [pending=%zu]", tb->ngrpchlds_pending));
+ if (!gr) {
+ DBG(LINE, ul_debugobj(ln, " *** ngrpchlds_pending counter invalid"));
+ tb->ngrpchlds_pending = 0;
+ break;
+ }
+
+ tb->ngrpchlds_pending--;
+
+ list_for_each(p, &gr->gr_children) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+
+ rc = walk_line(tb, chld, cl, callback, data);
+ if (rc)
+ break;
+ }
+ }
+ }
+
+ tb->ngrpchlds_pending = 0;
+ tb->walk_last_done = 0;
+ DBG(TAB, ul_debugobj(tb, "<< walk end [rc=%d]", rc));
+ return rc;
+}
diff --git a/utils/sys-utils/CMakeLists.txt b/utils/sys-utils/CMakeLists.txt
new file mode 100644
index 0000000..1e007dc
--- /dev/null
+++ b/utils/sys-utils/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+project(xloop-utils-sys-utils)
+
+# add xlosetup executable
+add_executable(xlosetup ${CMAKE_CURRENT_SOURCE_DIR}/losetup.c)
+target_link_libraries(xlosetup LINK_PUBLIC libcommon libsmartcols)
+target_include_directories(xlosetup PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${CMAKE_CURRENT_SOURCE_DIR}/../libsmartcols)
diff --git a/utils/sys-utils/losetup.8 b/utils/sys-utils/losetup.8
new file mode 100644
index 0000000..92aff3e
--- /dev/null
+++ b/utils/sys-utils/losetup.8
@@ -0,0 +1,215 @@
+.TH LOSETUP 8 "November 2015" "util-linux" "System Administration"
+.SH NAME
+losetup \- set up and control loop devices
+.SH SYNOPSIS
+.ad l
+Get info:
+.sp
+.in +5
+.B losetup
+[\fIloopdev\fP]
+.sp
+.B losetup \-l
+.RB [ \-a ]
+.sp
+.B losetup \-j
+.I file
+.RB [ \-o
+.IR offset ]
+.sp
+.in -5
+Detach a loop device:
+.sp
+.in +5
+.B "losetup \-d"
+.IR loopdev ...
+.sp
+.in -5
+Detach all associated loop devices:
+.sp
+.in +5
+.B "losetup \-D"
+.sp
+.in -5
+Set up a loop device:
+.sp
+.in +5
+.B losetup
+.RB [ \-o
+.IR offset ]
+.RB [ \-\-sizelimit
+.IR size ]
+.RB [ \-\-sector\-size
+.IR size ]
+.in +8
+.RB [ \-Pr ]
+.RB [ \-\-show ] " \-f" | \fIloopdev\fP
+.I file
+.sp
+.in -13
+Resize a loop device:
+.sp
+.in +5
+.B "losetup \-c"
+.I loopdev
+.in -5
+.ad b
+.SH DESCRIPTION
+.B losetup
+is used to associate loop devices with regular files or block devices,
+to detach loop devices, and to query the status of a loop device. If only the
+\fIloopdev\fP argument is given, the status of the corresponding loop
+device is shown. If no option is given, all loop devices are shown.
+.sp
+Note that the old output format (i.e., \fBlosetup \-a\fR) with comma-delimited
+strings is deprecated in favour of the \fB\-\-list\fR output format.
+.sp
+It's possible to create more independent loop devices for the same backing
+file.
+.B This setup may be dangerous, can cause data loss, corruption and overwrites.
+Use \fB\-\-nooverlap\fR with \fB\-\-find\fR during setup to avoid this problem.
+
+.SH OPTIONS
+The \fIsize\fR and \fIoffset\fR
+arguments may be followed by the multiplicative suffixes KiB (=1024),
+MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is
+optional, e.g., "K" has the same meaning as "KiB") or the suffixes
+KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB.
+
+.TP
+.BR \-a , " \-\-all"
+Show the status of all loop devices. Note that not all information is accessible
+for non-root users. See also \fB\-\-list\fR. The old output format (as printed
+without \fB\-\-list)\fR is deprecated.
+.TP
+.BR \-d , " \-\-detach " \fIloopdev\fR...
+Detach the file or device associated with the specified loop device(s). Note
+that since Linux v3.7 kernel uses "lazy device destruction". The detach
+operation does not return EBUSY error anymore if device is actively used by
+system, but it is marked by autoclear flag and destroyed later.
+.TP
+.BR \-D , " \-\-detach\-all"
+Detach all associated loop devices.
+.TP
+.BR \-f , " \-\-find " "\fR[\fIfile\fR]"
+Find the first unused loop device. If a \fIfile\fR argument is present, use
+the found device as loop device. Otherwise, just print its name.
+.IP "\fB\-\-show\fP"
+Display the name of the assigned loop device if the \fB\-f\fP option and a
+\fIfile\fP argument are present.
+.TP
+.BR \-L , " \-\-nooverlap"
+Check for conflicts between loop devices to avoid situation when the same
+backing file is shared between more loop devices. If the file is already used
+by another device then re-use the device rather than a new one. The option
+makes sense only with \fB\-\-find\fP.
+.TP
+.BR \-j , " \-\-associated " \fIfile\fR " \fR[\fB\-o \fIoffset\fR]"
+Show the status of all loop devices associated with the given \fIfile\fR.
+.TP
+.BR \-o , " \-\-offset " \fIoffset
+The data start is moved \fIoffset\fP bytes into the specified file or device. The \fIoffset\fP
+may be followed by the multiplicative suffixes; see above.
+.IP "\fB\-\-sizelimit \fIsize\fP"
+The data end is set to no more than \fIsize\fP bytes after the data start. The \fIsize\fP
+may be followed by the multiplicative suffixes; see above.
+.TP
+.BR \-b , " \-\-sector-size " \fIsize
+Set the logical sector size of the loop device in bytes (since Linux 4.14). The
+option may be used when create a new loop device as well as stand-alone command
+to modify sector size of the already existing loop device.
+.TP
+.BR \-c , " \-\-set\-capacity " \fIloopdev
+Force the loop driver to reread the size of the file associated with the
+specified loop device.
+.TP
+.BR \-P , " \-\-partscan"
+Force the kernel to scan the partition table on a newly created loop device. Note that the
+partition table parsing depends on sector sizes. The default is sector size is 512 bytes,
+otherwise you need to use the option \fB\-\-sector\-size\fR together with \fB\-\-partscan\fR.
+.TP
+.BR \-r , " \-\-read\-only"
+Set up a read-only loop device.
+.TP
+.BR \-\-direct\-io [ =on | off ]
+Enable or disable direct I/O for the backing file. The optional argument
+can be either \fBon\fR or \fBoff\fR. If the argument is omitted, it defaults
+to \fBoff\fR.
+.TP
+.BR \-t , " \-\-type \fIformat\fR"
+Set the file format type of the loop device. If no file format type is specified,
+the RAW file format is used by default. Valid file formats are: \fBRAW\fR,
+\fBQCOW\fR, \fBVDI\fR, \fBVMDK\fR.
+.TP
+.BR \-v , " \-\-verbose"
+Verbose mode.
+.TP
+.BR \-l , " \-\-list"
+If a loop device or the \fB\-a\fR option is specified, print the default columns
+for either the specified loop device or all loop devices; the default is to
+print info about all devices. See also \fB\-\-output\fP, \fB\-\-noheadings\fP,
+\fB\-\-raw\fP, and \fB\-\-json\fP.
+.TP
+.BR \-O , " \-\-output " \fIcolumn\fR[,\fIcolumn\fR]...
+Specify the columns that are to be printed for the \fB\-\-list\fP output.
+Use \fB\-\-help\fR to get a list of all supported columns.
+.TP
+.B \-\-output\-all
+Output all available columns.
+.TP
+.BR \-n , " \-\-noheadings"
+Don't print headings for \fB\-\-list\fP output format.
+.IP "\fB\-\-raw\fP"
+Use the raw \fB\-\-list\fP output format.
+.TP
+.BR \-J , " \-\-json"
+Use JSON format for \fB\-\-list\fP output.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+
+.SH ENCRYPTION
+.B Cryptoloop is no longer supported in favor of dm-crypt.
+.B For more details see cryptsetup(8).
+
+.SH EXIT STATUS
+.B losetup
+returns 0 on success, nonzero on failure. When
+.B losetup
+displays the status of a loop device, it returns 1 if the device
+is not configured and 2 if an error occurred which prevented
+determining the status of the device.
+
+.SH ENVIRONMENT
+.IP LOOPDEV_DEBUG=all
+enables debug output.
+
+.SH FILES
+.TP
+.I /dev/loop[0..N]
+loop block devices
+.TP
+.I /dev/loop-control
+loop control device
+.SH EXAMPLE
+The following commands can be used as an example of using the loop device.
+.nf
+.IP
+# dd if=/dev/zero of=~/file.img bs=1024k count=10
+# losetup \-\-find \-\-show ~/file.img
+/dev/loop0
+# mkfs \-t ext2 /dev/loop0
+# mount /dev/loop0 /mnt
+ ...
+# umount /dev/loop0
+# losetup \-\-detach /dev/loop0
+.fi
+.SH AUTHORS
+Karel Zak <kzak@redhat.com>, based on the original version from
+Theodore Ts'o <tytso@athena.mit.edu>
+.SH AVAILABILITY
+The losetup command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/utils/sys-utils/losetup.c b/utils/sys-utils/losetup.c
new file mode 100644
index 0000000..5e5ce9e
--- /dev/null
+++ b/utils/sys-utils/losetup.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ * Originally from Ted's losetup.c
+ *
+ * losetup.c - setup and control loop devices
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <libsmartcols.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "loopdev.h"
+#include "closestream.h"
+#include "optutils.h"
+#include "xalloc.h"
+#include "canonicalize.h"
+#include "pathnames.h"
+
+enum {
+ A_CREATE = 1, /* setup a new device */
+ A_DELETE, /* delete given device(s) */
+ A_DELETE_ALL, /* delete all devices */
+ A_SHOW, /* list devices */
+ A_SHOW_ONE, /* print info about one device */
+ A_FIND_FREE, /* find first unused */
+ A_SET_CAPACITY, /* set device capacity */
+ A_SET_DIRECT_IO, /* set accessing backing file by direct io */
+ A_SET_BLOCKSIZE, /* set logical block size of the loop device */
+};
+
+enum {
+ COL_NAME = 0,
+ COL_AUTOCLR,
+ COL_BACK_FILE,
+ COL_FILE_FMT_TYPE,
+ COL_BACK_INO,
+ COL_BACK_MAJMIN,
+ COL_MAJMIN,
+ COL_OFFSET,
+ COL_PARTSCAN,
+ COL_RO,
+ COL_SIZELIMIT,
+ COL_DIO,
+ COL_LOGSEC,
+};
+
+/* basic output flags */
+static int no_headings;
+static int raw;
+static int json;
+
+struct colinfo {
+ const char *name;
+ double whint;
+ int flags;
+ const char *help;
+
+ int json_type; /* default is string */
+};
+
+static struct colinfo infos[] = {
+ [COL_AUTOCLR] = { "AUTOCLEAR", 1, SCOLS_FL_RIGHT, N_("autoclear flag set"), SCOLS_JSON_BOOLEAN},
+ [COL_BACK_FILE] = { "BACK-FILE", 0.3, 0, N_("device backing file")},
+ [COL_FILE_FMT_TYPE] = { "FILE-FORMAT", 1, 0, N_("backing file format")},
+ [COL_BACK_INO] = { "BACK-INO", 4, SCOLS_FL_RIGHT, N_("backing file inode number"), SCOLS_JSON_NUMBER},
+ [COL_BACK_MAJMIN] = { "BACK-MAJ:MIN", 6, 0, N_("backing file major:minor device number")},
+ [COL_NAME] = { "NAME", 0.25, 0, N_("loop device name")},
+ [COL_OFFSET] = { "OFFSET", 5, SCOLS_FL_RIGHT, N_("offset from the beginning"), SCOLS_JSON_NUMBER},
+ [COL_PARTSCAN] = { "PARTSCAN", 1, SCOLS_FL_RIGHT, N_("partscan flag set"), SCOLS_JSON_BOOLEAN},
+ [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device"), SCOLS_JSON_BOOLEAN},
+ [COL_SIZELIMIT] = { "SIZELIMIT", 5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes"), SCOLS_JSON_NUMBER},
+ [COL_MAJMIN] = { "MAJ:MIN", 3, 0, N_("loop device major:minor number")},
+ [COL_DIO] = { "DIO", 1, SCOLS_FL_RIGHT, N_("access backing file with direct-io"), SCOLS_JSON_BOOLEAN},
+ [COL_LOGSEC] = { "LOG-SEC", 4, SCOLS_FL_RIGHT, N_("logical sector size in bytes"), SCOLS_JSON_NUMBER},
+};
+
+static int columns[ARRAY_SIZE(infos) * 2] = {-1};
+static size_t ncolumns;
+
+static int get_column_id(int num)
+{
+ assert(num >= 0);
+ assert((size_t) num < ncolumns);
+ assert(columns[num] < (int) ARRAY_SIZE(infos));
+ return columns[num];
+}
+
+static struct colinfo *get_column_info(int num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static int column_name_to_id(const char *name, size_t namesz)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(infos); i++) {
+ const char *cn = infos[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+static int printf_loopdev(struct loopdev_cxt *lc)
+{
+ uint64_t x;
+ dev_t dev = 0;
+ ino_t ino = 0;
+ char *fname;
+ uint32_t type;
+ char *file_fmt_str;
+
+ fname = loopcxt_get_backing_file(lc);
+ if (!fname)
+ return -EINVAL;
+
+ file_fmt_str = loopcxt_get_file_fmt_type_string(lc);
+ if (!file_fmt_str)
+ return -EINVAL;
+
+ if (loopcxt_get_backing_devno(lc, &dev) == 0)
+ loopcxt_get_backing_inode(lc, &ino);
+
+ if (!dev && !ino) {
+ /*
+ * Probably non-root user (no permissions to
+ * call LOOP_GET_STATUS ioctls).
+ */
+ printf("%s: []: (%s)",
+ loopcxt_get_device(lc), fname);
+
+ if (loopcxt_get_offset(lc, &x) == 0 && x)
+ printf(_(", offset %ju"), x);
+
+ if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
+ printf(_(", sizelimit %ju"), x);
+
+ printf(_(", file-format %s"), file_fmt_str);
+
+ goto done;
+ }
+
+ printf("%s: [%04d]:%" PRIu64 " (%s)",
+ loopcxt_get_device(lc), (int) dev, ino, fname);
+
+ if (loopcxt_get_offset(lc, &x) == 0 && x)
+ printf(_(", offset %ju"), x);
+
+ if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
+ printf(_(", sizelimit %ju"), x);
+
+ if (loopcxt_get_encrypt_type(lc, &type) == 0) {
+ const char *e = loopcxt_get_crypt_name(lc);
+
+ if ((!e || !*e) && type == 1)
+ e = "XOR";
+ if (e && *e)
+ printf(_(", encryption %s (type %u)"), e, type);
+ }
+
+ printf(_(", file-format %s"), file_fmt_str);
+
+done:
+ free(fname);
+ printf("\n");
+ return 0;
+}
+
+static int show_all_loops(struct loopdev_cxt *lc, const char *file,
+ uint64_t offset, int flags)
+{
+ struct stat sbuf, *st = &sbuf;
+ char *cn_file = NULL;
+
+ if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
+ return -1;
+
+ if (!file || stat(file, st))
+ st = NULL;
+
+ while (loopcxt_next(lc) == 0) {
+ if (file) {
+ int used;
+ const char *bf = cn_file ? cn_file : file;
+
+ used = loopcxt_is_used(lc, st, bf, offset, 0, flags);
+ if (!used && !cn_file) {
+ bf = cn_file = canonicalize_path(file);
+ used = loopcxt_is_used(lc, st, bf, offset, 0, flags);
+ }
+ if (!used)
+ continue;
+ }
+ printf_loopdev(lc);
+ }
+ loopcxt_deinit_iterator(lc);
+ free(cn_file);
+ return 0;
+}
+
+static int delete_loop(struct loopdev_cxt *lc)
+{
+ if (loopcxt_delete_device(lc))
+ warn(_("%s: detach failed"), loopcxt_get_device(lc));
+ else
+ return 0;
+
+ return -1;
+}
+
+static int delete_all_loops(struct loopdev_cxt *lc)
+{
+ int res = 0;
+
+ if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
+ return -1;
+
+ while (loopcxt_next(lc) == 0)
+ res += delete_loop(lc);
+
+ loopcxt_deinit_iterator(lc);
+ return res;
+}
+
+static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln)
+{
+ size_t i;
+
+ for (i = 0; i < ncolumns; i++) {
+ const char *p = NULL; /* external data */
+ char *np = NULL; /* allocated here */
+ uint64_t x = 0;
+ int rc = 0;
+
+ switch(get_column_id(i)) {
+ case COL_NAME:
+ p = loopcxt_get_device(lc);
+ break;
+ case COL_BACK_FILE:
+ p = loopcxt_get_backing_file(lc);
+ break;
+ case COL_FILE_FMT_TYPE:
+ p = loopcxt_get_file_fmt_type_string(lc);
+ break;
+ case COL_OFFSET:
+ if (loopcxt_get_offset(lc, &x) == 0)
+ xasprintf(&np, "%jd", x);
+ break;
+ case COL_SIZELIMIT:
+ if (loopcxt_get_sizelimit(lc, &x) == 0)
+ xasprintf(&np, "%jd", x);
+ break;
+ case COL_BACK_MAJMIN:
+ {
+ dev_t dev = 0;
+ if (loopcxt_get_backing_devno(lc, &dev) == 0 && dev)
+ xasprintf(&np, "%8u:%-3u", major(dev), minor(dev));
+ break;
+ }
+ case COL_MAJMIN:
+ {
+ struct stat st;
+
+ if (loopcxt_get_device(lc)
+ && stat(loopcxt_get_device(lc), &st) == 0
+ && S_ISBLK(st.st_mode)
+ && major(st.st_rdev) == LOOPDEV_MAJOR)
+ xasprintf(&np, "%3u:%-3u", major(st.st_rdev),
+ minor(st.st_rdev));
+ break;
+ }
+ case COL_BACK_INO:
+ {
+ ino_t ino = 0;
+ if (loopcxt_get_backing_inode(lc, &ino) == 0 && ino)
+ xasprintf(&np, "%ju", ino);
+ break;
+ }
+ case COL_AUTOCLR:
+ p = loopcxt_is_autoclear(lc) ? "1" : "0";
+ break;
+ case COL_RO:
+ p = loopcxt_is_readonly(lc) ? "1" : "0";
+ break;
+ case COL_DIO:
+ p = loopcxt_is_dio(lc) ? "1" : "0";
+ break;
+ case COL_PARTSCAN:
+ p = loopcxt_is_partscan(lc) ? "1" : "0";
+ break;
+ case COL_LOGSEC:
+ if (loopcxt_get_blocksize(lc, &x) == 0)
+ xasprintf(&np, "%jd", x);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ if (p)
+ rc = scols_line_set_data(ln, i, p); /* calls strdup() */
+ else if (np)
+ rc = scols_line_refer_data(ln, i, np); /* only refers */
+
+ if (rc)
+ err(EXIT_FAILURE, _("failed to add output data"));
+ }
+
+ return 0;
+}
+
+static int show_table(struct loopdev_cxt *lc,
+ const char *file,
+ uint64_t offset,
+ int flags)
+{
+ struct stat sbuf, *st = &sbuf;
+ struct libscols_table *tb;
+ struct libscols_line *ln;
+ int rc = 0;
+ size_t i;
+
+ scols_init_debug(0);
+
+ if (!(tb = scols_new_table()))
+ err(EXIT_FAILURE, _("failed to allocate output table"));
+ scols_table_enable_raw(tb, raw);
+ scols_table_enable_json(tb, json);
+ scols_table_enable_noheadings(tb, no_headings);
+
+ if (json)
+ scols_table_set_name(tb, "loopdevices");
+
+ for (i = 0; i < ncolumns; i++) {
+ struct colinfo *ci = get_column_info(i);
+ struct libscols_column *cl;
+
+ cl = scols_table_new_column(tb, ci->name, ci->whint, ci->flags);
+ if (!cl)
+ err(EXIT_FAILURE, _("failed to allocate output column"));
+ if (json)
+ scols_column_set_json_type(cl, ci->json_type);
+ }
+
+ /* only one loopdev requested (already assigned to loopdev_cxt) */
+ if (loopcxt_get_device(lc)) {
+ ln = scols_table_new_line(tb, NULL);
+ if (!ln)
+ err(EXIT_FAILURE, _("failed to allocate output line"));
+ rc = set_scols_data(lc, ln);
+
+ /* list all loopdevs */
+ } else {
+ char *cn_file = NULL;
+
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+ if (rc)
+ goto done;
+ if (!file || stat(file, st))
+ st = NULL;
+
+ while (loopcxt_next(lc) == 0) {
+ if (file) {
+ int used;
+ const char *bf = cn_file ? cn_file : file;
+
+ used = loopcxt_is_used(lc, st, bf, offset, 0, flags);
+ if (!used && !cn_file) {
+ bf = cn_file = canonicalize_path(file);
+ used = loopcxt_is_used(lc, st, bf, offset, 0, flags);
+ }
+ if (!used)
+ continue;
+ }
+
+ ln = scols_table_new_line(tb, NULL);
+ if (!ln)
+ err(EXIT_FAILURE, _("failed to allocate output line"));
+ rc = set_scols_data(lc, ln);
+ if (rc)
+ break;
+ }
+
+ loopcxt_deinit_iterator(lc);
+ free(cn_file);
+ }
+done:
+ if (rc == 0)
+ rc = scols_print_table(tb);
+ scols_unref_table(tb);
+ return rc;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] [<loopdev>]\n"
+ " %1$s [options] -f | <loopdev> <file>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Set up and control loop devices.\n"), out);
+
+ /* commands */
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all list all used devices\n"), out);
+ fputs(_(" -d, --detach <loopdev>... detach one or more devices\n"), out);
+ fputs(_(" -D, --detach-all detach all used devices\n"), out);
+ fputs(_(" -f, --find find first unused device\n"), out);
+ fputs(_(" -c, --set-capacity <loopdev> resize the device\n"), out);
+ fputs(_(" -j, --associated <file> list all devices associated with <file>\n"), out);
+ fputs(_(" -L, --nooverlap avoid possible conflict between devices\n"), out);
+
+ /* commands options */
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" -o, --offset <num> start at offset <num> into file\n"), out);
+ fputs(_(" --sizelimit <num> device is limited to <num> bytes of the file\n"), out);
+ fputs(_(" -b, --sector-size <num> set the logical sector size to <num>\n"), out);
+ fputs(_(" -P, --partscan create a partitioned loop device\n"), out);
+ fputs(_(" -r, --read-only set up a read-only loop device\n"), out);
+ fputs(_(" --direct-io[=<on|off>] open backing file with O_DIRECT\n"), out);
+ fputs(_(" --show print device name after setup (with -f)\n"), out);
+ fputs(_(" -t, --type set file format type of the loop device\n"), out);
+ fputs(_(" -v, --verbose verbose mode\n"), out);
+
+ /* output options */
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" -J, --json use JSON --list output format\n"), out);
+ fputs(_(" -l, --list list info about all or specified (default)\n"), out);
+ fputs(_(" -n, --noheadings don't print headings for --list output\n"), out);
+ fputs(_(" -O, --output <cols> specify columns to output for --list\n"), out);
+ fputs(_(" --output-all output all columns\n"), out);
+ fputs(_(" --raw use raw --list output format\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(31));
+
+ fputs(USAGE_COLUMNS, out);
+ for (i = 0; i < ARRAY_SIZE(infos); i++)
+ fprintf(out, " %12s %s\n", infos[i].name, _(infos[i].help));
+
+ printf(USAGE_MAN_TAIL("losetup(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+static void warn_size(const char *filename, uint64_t size, uint64_t offset, int flags)
+{
+ struct stat st;
+
+ if (!size) {
+ if (stat(filename, &st) || S_ISBLK(st.st_mode))
+ return;
+ size = st.st_size;
+
+ if (flags & LOOPDEV_FL_OFFSET)
+ size -= offset;
+ }
+
+ if (size < 512)
+ warnx(_("%s: Warning: file is smaller than 512 bytes; the loop device "
+ "may be useless or invisible for system tools."),
+ filename);
+ else if (size % 512)
+ warnx(_("%s: Warning: file does not fit into a 512-byte sector; "
+ "the end of the file will be ignored."),
+ filename);
+}
+
+static int create_loop(struct loopdev_cxt *lc,
+ int nooverlap, int lo_flags, int flags,
+ const char *file, uint64_t offset, uint64_t sizelimit,
+ uint64_t blocksize, uint32_t file_fmt_type)
+{
+ int hasdev = loopcxt_has_device(lc);
+ int rc = 0;
+
+ /* losetup --find --noverlap file.img */
+ if (!hasdev && nooverlap) {
+ rc = loopcxt_find_overlap(lc, file, offset, sizelimit);
+ switch (rc) {
+ case 0: /* not found */
+ break;
+
+ case 1: /* overlap */
+ loopcxt_deinit(lc);
+ errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file);
+
+ case 2: /* overlap -- full size and offset match (reuse) */
+ {
+ uint32_t lc_encrypt_type;
+
+ /* Once a loop is initialized RO, there is no
+ * way to change its parameters. */
+ if (loopcxt_is_readonly(lc)
+ && !(lo_flags & LO_FLAGS_READ_ONLY)) {
+ loopcxt_deinit(lc);
+ errx(EXIT_FAILURE, _("%s: overlapping read-only loop device exists"), file);
+ }
+
+ /* This is no more supported, but check to be safe. */
+ if (loopcxt_get_encrypt_type(lc, &lc_encrypt_type) == 0
+ && lc_encrypt_type != LO_CRYPT_NONE) {
+ loopcxt_deinit(lc);
+ errx(EXIT_FAILURE, _("%s: overlapping encrypted loop device exists"), file);
+ }
+
+ lc->info.lo_flags &= ~LO_FLAGS_AUTOCLEAR;
+ if (loopcxt_ioctl_status(lc)) {
+ loopcxt_deinit(lc);
+ errx(EXIT_FAILURE, _("%s: failed to re-use loop device"), file);
+ }
+ return 0; /* success, re-use */
+ }
+ default: /* error */
+ loopcxt_deinit(lc);
+ errx(EXIT_FAILURE, _("failed to inspect loop devices"));
+ return -errno;
+ }
+ }
+
+ if (hasdev && !is_loopdev(loopcxt_get_device(lc)))
+ loopcxt_add_device(lc);
+
+ /* losetup --noverlap /dev/loopN file.img */
+ if (hasdev && nooverlap) {
+ struct loopdev_cxt lc2;
+
+ if (loopcxt_init(&lc2, 0)) {
+ loopcxt_deinit(lc);
+ err(EXIT_FAILURE, _("failed to initialize loopcxt"));
+ }
+ rc = loopcxt_find_overlap(&lc2, file, offset, sizelimit);
+ loopcxt_deinit(&lc2);
+
+ if (rc) {
+ loopcxt_deinit(lc);
+ if (rc > 0)
+ errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file);
+ err(EXIT_FAILURE, _("%s: failed to check for conflicting loop devices"), file);
+ }
+ }
+
+ /* Create a new device */
+ do {
+ const char *errpre;
+
+ /* Note that loopcxt_{find_unused,set_device}() resets
+ * loopcxt struct.
+ */
+ if (!hasdev && (rc = loopcxt_find_unused(lc))) {
+ warnx(_("cannot find an unused loop device"));
+ break;
+ }
+ if (flags & LOOPDEV_FL_OFFSET)
+ loopcxt_set_offset(lc, offset);
+ if (flags & LOOPDEV_FL_SIZELIMIT)
+ loopcxt_set_sizelimit(lc, sizelimit);
+ if (lo_flags)
+ loopcxt_set_flags(lc, lo_flags);
+ if (blocksize > 0)
+ loopcxt_set_blocksize(lc, blocksize);
+
+ if ((rc = loopcxt_set_backing_file(lc, file))) {
+ warn(_("%s: failed to use backing file"), file);
+ break;
+ }
+
+ if ((rc = loopcxt_set_file_fmt_type(lc, file_fmt_type))) {
+ warn(_("failed to use backing file format type"));
+ break;
+ }
+
+ errno = 0;
+ rc = loopcxt_setup_device(lc);
+ if (rc == 0)
+ break; /* success */
+ if (errno == EBUSY && !hasdev)
+ continue;
+
+ /* errors */
+ errpre = hasdev && loopcxt_get_fd(lc) < 0 ?
+ loopcxt_get_device(lc) : file;
+ warn(_("%s: failed to set up loop device"), errpre);
+ break;
+ } while (hasdev == 0);
+
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ struct loopdev_cxt lc;
+ int act = 0, flags = 0, no_overlap = 0, c;
+ char *file = NULL;
+ uint64_t offset = 0, sizelimit = 0, blocksize = 0;
+ int res = 0, showdev = 0, lo_flags = 0;
+ char *outarg = NULL;
+ int list = 0;
+ unsigned long use_dio = 0, set_dio = 0, set_blocksize = 0;
+ int use_file_fmt_type = 0;
+ uint32_t file_fmt_type = 0;
+
+ enum {
+ OPT_SIZELIMIT = CHAR_MAX + 1,
+ OPT_SHOW,
+ OPT_RAW,
+ OPT_DIO,
+ OPT_OUTPUT_ALL
+ };
+ static const struct option longopts[] = {
+ { "all", no_argument, NULL, 'a' },
+ { "set-capacity", required_argument, NULL, 'c' },
+ { "detach", required_argument, NULL, 'd' },
+ { "detach-all", no_argument, NULL, 'D' },
+ { "find", no_argument, NULL, 'f' },
+ { "nooverlap", no_argument, NULL, 'L' },
+ { "help", no_argument, NULL, 'h' },
+ { "associated", required_argument, NULL, 'j' },
+ { "json", no_argument, NULL, 'J' },
+ { "list", no_argument, NULL, 'l' },
+ { "sector-size", required_argument, NULL, 'b' },
+ { "noheadings", no_argument, NULL, 'n' },
+ { "offset", required_argument, NULL, 'o' },
+ { "output", required_argument, NULL, 'O' },
+ { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
+ { "sizelimit", required_argument, NULL, OPT_SIZELIMIT },
+ { "partscan", no_argument, NULL, 'P' },
+ { "read-only", no_argument, NULL, 'r' },
+ { "direct-io", optional_argument, NULL, OPT_DIO },
+ { "raw", no_argument, NULL, OPT_RAW },
+ { "show", no_argument, NULL, OPT_SHOW },
+ { "type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'D','a','c','d','f','j' },
+ { 'D','c','d','f','l' },
+ { 'D','c','d','f','O' },
+ { 'J',OPT_RAW },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ if (loopcxt_init(&lc, 0))
+ err(EXIT_FAILURE, _("failed to initialize loopcxt"));
+
+ while ((c = getopt_long(argc, argv, "ab:c:d:Dfhj:JlLno:O:Prt:vV",
+ longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'a':
+ act = A_SHOW;
+ break;
+ case 'b':
+ set_blocksize = 1;
+ blocksize = strtosize_or_err(optarg, _("failed to parse logical block size"));
+ break;
+ case 'c':
+ act = A_SET_CAPACITY;
+ if (!is_loopdev(optarg) ||
+ loopcxt_set_device(&lc, optarg))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ optarg);
+ break;
+ case 'r':
+ lo_flags |= LO_FLAGS_READ_ONLY;
+ break;
+ case 'd':
+ act = A_DELETE;
+ if (!is_loopdev(optarg) ||
+ loopcxt_set_device(&lc, optarg))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ optarg);
+ break;
+ case 'D':
+ act = A_DELETE_ALL;
+ break;
+ case 'f':
+ act = A_FIND_FREE;
+ break;
+ case 'J':
+ json = 1;
+ break;
+ case 'j':
+ act = A_SHOW;
+ file = optarg;
+ break;
+ case 'l':
+ list = 1;
+ break;
+ case 'L':
+ no_overlap = 1;
+ break;
+ case 'n':
+ no_headings = 1;
+ break;
+ case OPT_RAW:
+ raw = 1;
+ break;
+ case 'o':
+ offset = strtosize_or_err(optarg, _("failed to parse offset"));
+ flags |= LOOPDEV_FL_OFFSET;
+ break;
+ case 'O':
+ outarg = optarg;
+ list = 1;
+ break;
+ case OPT_OUTPUT_ALL:
+ for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
+ columns[ncolumns] = ncolumns;
+ break;
+ case 'P':
+ lo_flags |= LO_FLAGS_PARTSCAN;
+ break;
+ case OPT_SHOW:
+ showdev = 1;
+ break;
+ case OPT_DIO:
+ use_dio = set_dio = 1;
+ if (optarg)
+ use_dio = parse_switch(optarg, _("argument error"), "on", "off", NULL);
+ break;
+ case 't':
+ if (optarg) {
+ if (parse_file_fmt_type(optarg, &file_fmt_type) == 0)
+ use_file_fmt_type = 1;
+ else
+ errx(EXIT_FAILURE, _("failed to parse file format type"));
+ }
+ break;
+ case 'v':
+ break;
+ case OPT_SIZELIMIT: /* --sizelimit */
+ sizelimit = strtosize_or_err(optarg, _("failed to parse size"));
+ flags |= LOOPDEV_FL_SIZELIMIT;
+ break;
+
+ case 'h':
+ usage();
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ ul_path_init_debug();
+ ul_sysfs_init_debug();
+
+ /* default is --list --all */
+ if (argc == 1) {
+ act = A_SHOW;
+ list = 1;
+ }
+
+ if (!act && argc == 2 && (raw || json)) {
+ act = A_SHOW;
+ list = 1;
+ }
+
+ /* default --list output columns */
+ if (list && !ncolumns) {
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_SIZELIMIT;
+ columns[ncolumns++] = COL_OFFSET;
+ columns[ncolumns++] = COL_AUTOCLR;
+ columns[ncolumns++] = COL_RO;
+ columns[ncolumns++] = COL_BACK_FILE;
+ columns[ncolumns++] = COL_FILE_FMT_TYPE;
+ columns[ncolumns++] = COL_DIO;
+ columns[ncolumns++] = COL_LOGSEC;
+ }
+
+ if (act == A_FIND_FREE && optind < argc) {
+ /*
+ * losetup -f <backing_file>
+ */
+ act = A_CREATE;
+ file = argv[optind++];
+
+ if (optind < argc)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+ }
+
+ if (list && !act && optind == argc)
+ /*
+ * losetup --list defaults to --all
+ */
+ act = A_SHOW;
+
+ if (!act && optind + 1 == argc) {
+ /*
+ * losetup [--list] <device>
+ * OR
+ * losetup {--direct-io[=off]|--logical-blocksize=size}... <device>
+ */
+ if (!(set_dio || set_blocksize))
+ act = A_SHOW_ONE;
+ if (set_dio)
+ act = A_SET_DIRECT_IO;
+ if (set_blocksize)
+ act = A_SET_BLOCKSIZE;
+ if (!is_loopdev(argv[optind]) ||
+ loopcxt_set_device(&lc, argv[optind]))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ argv[optind]);
+ optind++;
+ }
+ if (!act) {
+ /*
+ * losetup <loopdev> <backing_file>
+ */
+ act = A_CREATE;
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, _("no loop device specified"));
+ /* don't use is_loopdev() here, the device does not have exist yet */
+ if (loopcxt_set_device(&lc, argv[optind]))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ argv[optind]);
+ optind++;
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, _("no file specified"));
+ file = argv[optind++];
+ }
+
+ if (act != A_CREATE &&
+ (sizelimit || lo_flags || showdev || use_file_fmt_type))
+ errx(EXIT_FAILURE,
+ _("the options %s are allowed during loop device setup only"),
+ "--{sizelimit,partscan,read-only,show,type}");
+
+ if ((flags & LOOPDEV_FL_OFFSET) &&
+ act != A_CREATE && (act != A_SHOW || !file))
+ errx(EXIT_FAILURE, _("the option --offset is not allowed in this context"));
+
+ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
+ switch (act) {
+ case A_CREATE:
+ res = create_loop(&lc, no_overlap, lo_flags, flags, file,
+ offset, sizelimit, blocksize, file_fmt_type);
+ if (res == 0) {
+ if (showdev)
+ printf("%s\n", loopcxt_get_device(&lc));
+ warn_size(file, sizelimit, offset, flags);
+ if (set_dio)
+ goto lo_set_dio;
+ }
+ break;
+ case A_DELETE:
+ res = delete_loop(&lc);
+ while (optind < argc) {
+ if (!is_loopdev(argv[optind]) ||
+ loopcxt_set_device(&lc, argv[optind]))
+ warn(_("%s: failed to use device"),
+ argv[optind]);
+ optind++;
+ res += delete_loop(&lc);
+ }
+ break;
+ case A_DELETE_ALL:
+ res = delete_all_loops(&lc);
+ break;
+ case A_FIND_FREE:
+ res = loopcxt_find_unused(&lc);
+ if (res) {
+ int errsv = errno;
+
+ if (access(_PATH_DEV_LOOPCTL, F_OK) == 0 &&
+ access(_PATH_DEV_LOOPCTL, W_OK) != 0)
+ ;
+ else
+ errno = errsv;
+
+ warn(_("cannot find an unused loop device"));
+ } else
+ printf("%s\n", loopcxt_get_device(&lc));
+ break;
+ case A_SHOW:
+ if (list)
+ res = show_table(&lc, file, offset, flags);
+ else
+ res = show_all_loops(&lc, file, offset, flags);
+ break;
+ case A_SHOW_ONE:
+ if (list)
+ res = show_table(&lc, NULL, 0, 0);
+ else
+ res = printf_loopdev(&lc);
+ if (res)
+ warn("%s", loopcxt_get_device(&lc));
+ break;
+ case A_SET_CAPACITY:
+ res = loopcxt_ioctl_capacity(&lc);
+ if (res)
+ warn(_("%s: set capacity failed"),
+ loopcxt_get_device(&lc));
+ break;
+ case A_SET_DIRECT_IO:
+lo_set_dio:
+ res = loopcxt_ioctl_dio(&lc, use_dio);
+ if (res)
+ warn(_("%s: set direct io failed"),
+ loopcxt_get_device(&lc));
+ break;
+ case A_SET_BLOCKSIZE:
+ res = loopcxt_ioctl_blocksize(&lc, blocksize);
+ if (res)
+ warn(_("%s: set logical block size failed"),
+ loopcxt_get_device(&lc));
+ break;
+ default:
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ break;
+ }
+
+ loopcxt_deinit(&lc);
+ return res ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+