diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:35:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:35:28 +0000 |
commit | ea314d2f45c40a006c0104157013ab4b857f665f (patch) | |
tree | 3ef2971cb3675c318b8d9effd987854ad3f6d3e8 /lib | |
parent | Initial commit. (diff) | |
download | dpkg-ea314d2f45c40a006c0104157013ab4b857f665f.tar.xz dpkg-ea314d2f45c40a006c0104157013ab4b857f665f.zip |
Adding upstream version 1.22.4.upstream/1.22.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
206 files changed, 35860 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..c09fc7a --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,6 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = \ + compat \ + dpkg \ + # EOL diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..e0644ac --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,712 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/dpkg-arch.m4 \ + $(top_srcdir)/m4/dpkg-build.m4 \ + $(top_srcdir)/m4/dpkg-compiler.m4 \ + $(top_srcdir)/m4/dpkg-coverage.m4 \ + $(top_srcdir)/m4/dpkg-funcs.m4 \ + $(top_srcdir)/m4/dpkg-headers.m4 $(top_srcdir)/m4/dpkg-libs.m4 \ + $(top_srcdir)/m4/dpkg-linker.m4 $(top_srcdir)/m4/dpkg-progs.m4 \ + $(top_srcdir)/m4/dpkg-types.m4 \ + $(top_srcdir)/m4/dpkg-unicode.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_DEVEL_DOCS = @BUILD_DEVEL_DOCS@ +BZ2_LIBS = @BZ2_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURSES_LIBS = @CURSES_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEB_DEFAULT_COMPRESSOR = @DEB_DEFAULT_COMPRESSOR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DPKG_DEFAULT_PAGER = @DPKG_DEFAULT_PAGER@ +DPKG_DEFAULT_SHELL = @DPKG_DEFAULT_SHELL@ +DPKG_PAGER = @DPKG_PAGER@ +DPKG_SHELL = @DPKG_SHELL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GCOV = @GCOV@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KVM_LIBS = @KVM_LIBS@ +LCOV = @LCOV@ +LCOV_GENHTML = @LCOV_GENHTML@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZMA_LIBS = @LZMA_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MD_LIBS = @MD_LIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_BUG_WEB = @PACKAGE_BUG_WEB@ +PACKAGE_COPYRIGHT_HOLDER = @PACKAGE_COPYRIGHT_HOLDER@ +PACKAGE_CPAN_NAME = @PACKAGE_CPAN_NAME@ +PACKAGE_DIST_IS_RELEASE = @PACKAGE_DIST_IS_RELEASE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_RELEASE_DATE = @PACKAGE_RELEASE_DATE@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VCS_TYPE = @PACKAGE_VCS_TYPE@ +PACKAGE_VCS_URL = @PACKAGE_VCS_URL@ +PACKAGE_VCS_WEB = @PACKAGE_VCS_WEB@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATCH = @PATCH@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PERL_COVER = @PERL_COVER@ +PERL_COVERAGE = @PERL_COVERAGE@ +PERL_LIBDIR = @PERL_LIBDIR@ +PERL_MIN_VERSION = @PERL_MIN_VERSION@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PO4A = @PO4A@ +POD2MAN = @POD2MAN@ +POSUB = @POSUB@ +PS_LIBS = @PS_LIBS@ +RANLIB = @RANLIB@ +RT_LIBS = @RT_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKET_LIBS = @SOCKET_LIBS@ +STRIP = @STRIP@ +TAR = @TAR@ +USE_NLS = @USE_NLS@ +USE_PO4A = @USE_PO4A@ +USE_UNICODE = @USE_UNICODE@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZSTD_LIBS = @ZSTD_LIBS@ +Z_LIBS = @Z_LIBS@ +Z_NG_LIBS = @Z_NG_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +admindir = @admindir@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +backupsdir = @backupsdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devlibdir = @devlibdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +logdir = @logdir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfdir = @pkgconfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +zshcompletionsdir = @zshcompletionsdir@ +SUBDIRS = \ + compat \ + dpkg \ + # EOL + +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/compat/Makefile.am b/lib/compat/Makefile.am new file mode 100644 index 0000000..58a2524 --- /dev/null +++ b/lib/compat/Makefile.am @@ -0,0 +1,96 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -idirafter $(top_srcdir)/lib/compat \ + -I$(top_builddir) \ + # EOL + + +noinst_LTLIBRARIES = \ + libcompat-test.la \ + libcompat.la \ + # EOL + +libcompat_test_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DTEST_LIBCOMPAT=1 \ + # EOL +libcompat_test_la_SOURCES = \ + compat.h \ + strchrnul.c \ + strnlen.c \ + strndup.c \ + strsignal.c \ + snprintf.c vsnprintf.c \ + asprintf.c vasprintf.c \ + alphasort.c \ + scandir.c \ + unsetenv.c \ + # EOL + +if HAVE_SYS_ERRLIST +libcompat_test_la_SOURCES += strerror.c +endif + +libcompat_la_SOURCES = \ + empty.c \ + compat.h \ + compat-zlib.h \ + gettext.h \ + # EOL + +if !HAVE_GETOPT +libcompat_la_SOURCES += getopt.c getopt.h getopt_int.h +else +if !HAVE_GETOPT_LONG +libcompat_la_SOURCES += getopt.c getopt.h getopt_int.h +endif +endif + +if !HAVE_GETOPT_LONG +libcompat_la_SOURCES += getopt1.c +endif + +if !HAVE_OBSTACK_FREE +libcompat_la_SOURCES += obstack.c obstack.h +endif + +if !HAVE_STRNLEN +libcompat_la_SOURCES += strnlen.c +endif + +if !HAVE_STRCHRNUL +libcompat_la_SOURCES += strchrnul.c +endif + +if !HAVE_STRNDUP +libcompat_la_SOURCES += strndup.c +endif + +if !HAVE_STRERROR +libcompat_la_SOURCES += strerror.c +endif + +if !HAVE_STRSIGNAL +libcompat_la_SOURCES += strsignal.c +endif + +if !HAVE_C99_SNPRINTF +libcompat_la_SOURCES += snprintf.c vsnprintf.c +endif + +if !HAVE_ASPRINTF +libcompat_la_SOURCES += asprintf.c vasprintf.c +endif + +if !HAVE_ALPHASORT +libcompat_la_SOURCES += alphasort.c +endif + +if !HAVE_SCANDIR +libcompat_la_SOURCES += scandir.c +endif + +if !HAVE_UNSETENV +libcompat_la_SOURCES += unsetenv.c +endif diff --git a/lib/compat/Makefile.in b/lib/compat/Makefile.in new file mode 100644 index 0000000..05e090b --- /dev/null +++ b/lib/compat/Makefile.in @@ -0,0 +1,948 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_SYS_ERRLIST_TRUE@am__append_1 = strerror.c +@HAVE_GETOPT_FALSE@am__append_2 = getopt.c getopt.h getopt_int.h +@HAVE_GETOPT_LONG_FALSE@@HAVE_GETOPT_TRUE@am__append_3 = getopt.c getopt.h getopt_int.h +@HAVE_GETOPT_LONG_FALSE@am__append_4 = getopt1.c +@HAVE_OBSTACK_FREE_FALSE@am__append_5 = obstack.c obstack.h +@HAVE_STRNLEN_FALSE@am__append_6 = strnlen.c +@HAVE_STRCHRNUL_FALSE@am__append_7 = strchrnul.c +@HAVE_STRNDUP_FALSE@am__append_8 = strndup.c +@HAVE_STRERROR_FALSE@am__append_9 = strerror.c +@HAVE_STRSIGNAL_FALSE@am__append_10 = strsignal.c +@HAVE_C99_SNPRINTF_FALSE@am__append_11 = snprintf.c vsnprintf.c +@HAVE_ASPRINTF_FALSE@am__append_12 = asprintf.c vasprintf.c +@HAVE_ALPHASORT_FALSE@am__append_13 = alphasort.c +@HAVE_SCANDIR_FALSE@am__append_14 = scandir.c +@HAVE_UNSETENV_FALSE@am__append_15 = unsetenv.c +subdir = lib/compat +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/dpkg-arch.m4 \ + $(top_srcdir)/m4/dpkg-build.m4 \ + $(top_srcdir)/m4/dpkg-compiler.m4 \ + $(top_srcdir)/m4/dpkg-coverage.m4 \ + $(top_srcdir)/m4/dpkg-funcs.m4 \ + $(top_srcdir)/m4/dpkg-headers.m4 $(top_srcdir)/m4/dpkg-libs.m4 \ + $(top_srcdir)/m4/dpkg-linker.m4 $(top_srcdir)/m4/dpkg-progs.m4 \ + $(top_srcdir)/m4/dpkg-types.m4 \ + $(top_srcdir)/m4/dpkg-unicode.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcompat_test_la_LIBADD = +am__libcompat_test_la_SOURCES_DIST = compat.h strchrnul.c strnlen.c \ + strndup.c strsignal.c snprintf.c vsnprintf.c asprintf.c \ + vasprintf.c alphasort.c scandir.c unsetenv.c strerror.c +@HAVE_SYS_ERRLIST_TRUE@am__objects_1 = libcompat_test_la-strerror.lo +am_libcompat_test_la_OBJECTS = libcompat_test_la-strchrnul.lo \ + libcompat_test_la-strnlen.lo libcompat_test_la-strndup.lo \ + libcompat_test_la-strsignal.lo libcompat_test_la-snprintf.lo \ + libcompat_test_la-vsnprintf.lo libcompat_test_la-asprintf.lo \ + libcompat_test_la-vasprintf.lo libcompat_test_la-alphasort.lo \ + libcompat_test_la-scandir.lo libcompat_test_la-unsetenv.lo \ + $(am__objects_1) +libcompat_test_la_OBJECTS = $(am_libcompat_test_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libcompat_la_LIBADD = +am__libcompat_la_SOURCES_DIST = empty.c compat.h compat-zlib.h \ + gettext.h getopt.c getopt.h getopt_int.h getopt1.c obstack.c \ + obstack.h strnlen.c strchrnul.c strndup.c strerror.c \ + strsignal.c snprintf.c vsnprintf.c asprintf.c vasprintf.c \ + alphasort.c scandir.c unsetenv.c +@HAVE_GETOPT_FALSE@am__objects_2 = getopt.lo +@HAVE_GETOPT_LONG_FALSE@@HAVE_GETOPT_TRUE@am__objects_3 = getopt.lo +@HAVE_GETOPT_LONG_FALSE@am__objects_4 = getopt1.lo +@HAVE_OBSTACK_FREE_FALSE@am__objects_5 = obstack.lo +@HAVE_STRNLEN_FALSE@am__objects_6 = strnlen.lo +@HAVE_STRCHRNUL_FALSE@am__objects_7 = strchrnul.lo +@HAVE_STRNDUP_FALSE@am__objects_8 = strndup.lo +@HAVE_STRERROR_FALSE@am__objects_9 = strerror.lo +@HAVE_STRSIGNAL_FALSE@am__objects_10 = strsignal.lo +@HAVE_C99_SNPRINTF_FALSE@am__objects_11 = snprintf.lo vsnprintf.lo +@HAVE_ASPRINTF_FALSE@am__objects_12 = asprintf.lo vasprintf.lo +@HAVE_ALPHASORT_FALSE@am__objects_13 = alphasort.lo +@HAVE_SCANDIR_FALSE@am__objects_14 = scandir.lo +@HAVE_UNSETENV_FALSE@am__objects_15 = unsetenv.lo +am_libcompat_la_OBJECTS = empty.lo $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) $(am__objects_6) \ + $(am__objects_7) $(am__objects_8) $(am__objects_9) \ + $(am__objects_10) $(am__objects_11) $(am__objects_12) \ + $(am__objects_13) $(am__objects_14) $(am__objects_15) +libcompat_la_OBJECTS = $(am_libcompat_la_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/alphasort.Plo \ + ./$(DEPDIR)/asprintf.Plo ./$(DEPDIR)/empty.Plo \ + ./$(DEPDIR)/getopt.Plo ./$(DEPDIR)/getopt1.Plo \ + ./$(DEPDIR)/libcompat_test_la-alphasort.Plo \ + ./$(DEPDIR)/libcompat_test_la-asprintf.Plo \ + ./$(DEPDIR)/libcompat_test_la-scandir.Plo \ + ./$(DEPDIR)/libcompat_test_la-snprintf.Plo \ + ./$(DEPDIR)/libcompat_test_la-strchrnul.Plo \ + ./$(DEPDIR)/libcompat_test_la-strerror.Plo \ + ./$(DEPDIR)/libcompat_test_la-strndup.Plo \ + ./$(DEPDIR)/libcompat_test_la-strnlen.Plo \ + ./$(DEPDIR)/libcompat_test_la-strsignal.Plo \ + ./$(DEPDIR)/libcompat_test_la-unsetenv.Plo \ + ./$(DEPDIR)/libcompat_test_la-vasprintf.Plo \ + ./$(DEPDIR)/libcompat_test_la-vsnprintf.Plo \ + ./$(DEPDIR)/obstack.Plo ./$(DEPDIR)/scandir.Plo \ + ./$(DEPDIR)/snprintf.Plo ./$(DEPDIR)/strchrnul.Plo \ + ./$(DEPDIR)/strerror.Plo ./$(DEPDIR)/strndup.Plo \ + ./$(DEPDIR)/strnlen.Plo ./$(DEPDIR)/strsignal.Plo \ + ./$(DEPDIR)/unsetenv.Plo ./$(DEPDIR)/vasprintf.Plo \ + ./$(DEPDIR)/vsnprintf.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcompat_test_la_SOURCES) $(libcompat_la_SOURCES) +DIST_SOURCES = $(am__libcompat_test_la_SOURCES_DIST) \ + $(am__libcompat_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_DEVEL_DOCS = @BUILD_DEVEL_DOCS@ +BZ2_LIBS = @BZ2_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURSES_LIBS = @CURSES_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEB_DEFAULT_COMPRESSOR = @DEB_DEFAULT_COMPRESSOR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DPKG_DEFAULT_PAGER = @DPKG_DEFAULT_PAGER@ +DPKG_DEFAULT_SHELL = @DPKG_DEFAULT_SHELL@ +DPKG_PAGER = @DPKG_PAGER@ +DPKG_SHELL = @DPKG_SHELL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GCOV = @GCOV@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KVM_LIBS = @KVM_LIBS@ +LCOV = @LCOV@ +LCOV_GENHTML = @LCOV_GENHTML@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZMA_LIBS = @LZMA_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MD_LIBS = @MD_LIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_BUG_WEB = @PACKAGE_BUG_WEB@ +PACKAGE_COPYRIGHT_HOLDER = @PACKAGE_COPYRIGHT_HOLDER@ +PACKAGE_CPAN_NAME = @PACKAGE_CPAN_NAME@ +PACKAGE_DIST_IS_RELEASE = @PACKAGE_DIST_IS_RELEASE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_RELEASE_DATE = @PACKAGE_RELEASE_DATE@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VCS_TYPE = @PACKAGE_VCS_TYPE@ +PACKAGE_VCS_URL = @PACKAGE_VCS_URL@ +PACKAGE_VCS_WEB = @PACKAGE_VCS_WEB@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATCH = @PATCH@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PERL_COVER = @PERL_COVER@ +PERL_COVERAGE = @PERL_COVERAGE@ +PERL_LIBDIR = @PERL_LIBDIR@ +PERL_MIN_VERSION = @PERL_MIN_VERSION@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PO4A = @PO4A@ +POD2MAN = @POD2MAN@ +POSUB = @POSUB@ +PS_LIBS = @PS_LIBS@ +RANLIB = @RANLIB@ +RT_LIBS = @RT_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKET_LIBS = @SOCKET_LIBS@ +STRIP = @STRIP@ +TAR = @TAR@ +USE_NLS = @USE_NLS@ +USE_PO4A = @USE_PO4A@ +USE_UNICODE = @USE_UNICODE@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZSTD_LIBS = @ZSTD_LIBS@ +Z_LIBS = @Z_LIBS@ +Z_NG_LIBS = @Z_NG_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +admindir = @admindir@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +backupsdir = @backupsdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devlibdir = @devlibdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +logdir = @logdir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfdir = @pkgconfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +zshcompletionsdir = @zshcompletionsdir@ +AM_CPPFLAGS = \ + -idirafter $(top_srcdir)/lib/compat \ + -I$(top_builddir) \ + # EOL + +noinst_LTLIBRARIES = \ + libcompat-test.la \ + libcompat.la \ + # EOL + +libcompat_test_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DTEST_LIBCOMPAT=1 \ + # EOL + +libcompat_test_la_SOURCES = compat.h strchrnul.c strnlen.c strndup.c \ + strsignal.c snprintf.c vsnprintf.c asprintf.c vasprintf.c \ + alphasort.c scandir.c unsetenv.c $(am__append_1) +libcompat_la_SOURCES = empty.c compat.h compat-zlib.h gettext.h \ + $(am__append_2) $(am__append_3) $(am__append_4) \ + $(am__append_5) $(am__append_6) $(am__append_7) \ + $(am__append_8) $(am__append_9) $(am__append_10) \ + $(am__append_11) $(am__append_12) $(am__append_13) \ + $(am__append_14) $(am__append_15) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/compat/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/compat/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libcompat-test.la: $(libcompat_test_la_OBJECTS) $(libcompat_test_la_DEPENDENCIES) $(EXTRA_libcompat_test_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcompat_test_la_OBJECTS) $(libcompat_test_la_LIBADD) $(LIBS) + +libcompat.la: $(libcompat_la_OBJECTS) $(libcompat_la_DEPENDENCIES) $(EXTRA_libcompat_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcompat_la_OBJECTS) $(libcompat_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alphasort.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/empty.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-alphasort.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-asprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-scandir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-snprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-strchrnul.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-strerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-strndup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-strnlen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-strsignal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-unsetenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-vasprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcompat_test_la-vsnprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obstack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scandir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strchrnul.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strndup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnlen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strsignal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unsetenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vasprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vsnprintf.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libcompat_test_la-strchrnul.lo: strchrnul.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-strchrnul.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-strchrnul.Tpo -c -o libcompat_test_la-strchrnul.lo `test -f 'strchrnul.c' || echo '$(srcdir)/'`strchrnul.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-strchrnul.Tpo $(DEPDIR)/libcompat_test_la-strchrnul.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strchrnul.c' object='libcompat_test_la-strchrnul.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-strchrnul.lo `test -f 'strchrnul.c' || echo '$(srcdir)/'`strchrnul.c + +libcompat_test_la-strnlen.lo: strnlen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-strnlen.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-strnlen.Tpo -c -o libcompat_test_la-strnlen.lo `test -f 'strnlen.c' || echo '$(srcdir)/'`strnlen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-strnlen.Tpo $(DEPDIR)/libcompat_test_la-strnlen.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strnlen.c' object='libcompat_test_la-strnlen.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-strnlen.lo `test -f 'strnlen.c' || echo '$(srcdir)/'`strnlen.c + +libcompat_test_la-strndup.lo: strndup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-strndup.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-strndup.Tpo -c -o libcompat_test_la-strndup.lo `test -f 'strndup.c' || echo '$(srcdir)/'`strndup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-strndup.Tpo $(DEPDIR)/libcompat_test_la-strndup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strndup.c' object='libcompat_test_la-strndup.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-strndup.lo `test -f 'strndup.c' || echo '$(srcdir)/'`strndup.c + +libcompat_test_la-strsignal.lo: strsignal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-strsignal.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-strsignal.Tpo -c -o libcompat_test_la-strsignal.lo `test -f 'strsignal.c' || echo '$(srcdir)/'`strsignal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-strsignal.Tpo $(DEPDIR)/libcompat_test_la-strsignal.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strsignal.c' object='libcompat_test_la-strsignal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-strsignal.lo `test -f 'strsignal.c' || echo '$(srcdir)/'`strsignal.c + +libcompat_test_la-snprintf.lo: snprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-snprintf.Tpo -c -o libcompat_test_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-snprintf.Tpo $(DEPDIR)/libcompat_test_la-snprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf.c' object='libcompat_test_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c + +libcompat_test_la-vsnprintf.lo: vsnprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-vsnprintf.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-vsnprintf.Tpo -c -o libcompat_test_la-vsnprintf.lo `test -f 'vsnprintf.c' || echo '$(srcdir)/'`vsnprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-vsnprintf.Tpo $(DEPDIR)/libcompat_test_la-vsnprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vsnprintf.c' object='libcompat_test_la-vsnprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-vsnprintf.lo `test -f 'vsnprintf.c' || echo '$(srcdir)/'`vsnprintf.c + +libcompat_test_la-asprintf.lo: asprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-asprintf.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-asprintf.Tpo -c -o libcompat_test_la-asprintf.lo `test -f 'asprintf.c' || echo '$(srcdir)/'`asprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-asprintf.Tpo $(DEPDIR)/libcompat_test_la-asprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asprintf.c' object='libcompat_test_la-asprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-asprintf.lo `test -f 'asprintf.c' || echo '$(srcdir)/'`asprintf.c + +libcompat_test_la-vasprintf.lo: vasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-vasprintf.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-vasprintf.Tpo -c -o libcompat_test_la-vasprintf.lo `test -f 'vasprintf.c' || echo '$(srcdir)/'`vasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-vasprintf.Tpo $(DEPDIR)/libcompat_test_la-vasprintf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vasprintf.c' object='libcompat_test_la-vasprintf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-vasprintf.lo `test -f 'vasprintf.c' || echo '$(srcdir)/'`vasprintf.c + +libcompat_test_la-alphasort.lo: alphasort.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-alphasort.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-alphasort.Tpo -c -o libcompat_test_la-alphasort.lo `test -f 'alphasort.c' || echo '$(srcdir)/'`alphasort.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-alphasort.Tpo $(DEPDIR)/libcompat_test_la-alphasort.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='alphasort.c' object='libcompat_test_la-alphasort.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-alphasort.lo `test -f 'alphasort.c' || echo '$(srcdir)/'`alphasort.c + +libcompat_test_la-scandir.lo: scandir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-scandir.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-scandir.Tpo -c -o libcompat_test_la-scandir.lo `test -f 'scandir.c' || echo '$(srcdir)/'`scandir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-scandir.Tpo $(DEPDIR)/libcompat_test_la-scandir.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scandir.c' object='libcompat_test_la-scandir.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-scandir.lo `test -f 'scandir.c' || echo '$(srcdir)/'`scandir.c + +libcompat_test_la-unsetenv.lo: unsetenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-unsetenv.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-unsetenv.Tpo -c -o libcompat_test_la-unsetenv.lo `test -f 'unsetenv.c' || echo '$(srcdir)/'`unsetenv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-unsetenv.Tpo $(DEPDIR)/libcompat_test_la-unsetenv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unsetenv.c' object='libcompat_test_la-unsetenv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-unsetenv.lo `test -f 'unsetenv.c' || echo '$(srcdir)/'`unsetenv.c + +libcompat_test_la-strerror.lo: strerror.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcompat_test_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcompat_test_la-strerror.Tpo -c -o libcompat_test_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcompat_test_la-strerror.Tpo $(DEPDIR)/libcompat_test_la-strerror.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcompat_test_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcompat_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcompat_test_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/alphasort.Plo + -rm -f ./$(DEPDIR)/asprintf.Plo + -rm -f ./$(DEPDIR)/empty.Plo + -rm -f ./$(DEPDIR)/getopt.Plo + -rm -f ./$(DEPDIR)/getopt1.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-alphasort.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-asprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-scandir.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-snprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strchrnul.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strndup.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strnlen.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strsignal.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-unsetenv.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-vasprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-vsnprintf.Plo + -rm -f ./$(DEPDIR)/obstack.Plo + -rm -f ./$(DEPDIR)/scandir.Plo + -rm -f ./$(DEPDIR)/snprintf.Plo + -rm -f ./$(DEPDIR)/strchrnul.Plo + -rm -f ./$(DEPDIR)/strerror.Plo + -rm -f ./$(DEPDIR)/strndup.Plo + -rm -f ./$(DEPDIR)/strnlen.Plo + -rm -f ./$(DEPDIR)/strsignal.Plo + -rm -f ./$(DEPDIR)/unsetenv.Plo + -rm -f ./$(DEPDIR)/vasprintf.Plo + -rm -f ./$(DEPDIR)/vsnprintf.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/alphasort.Plo + -rm -f ./$(DEPDIR)/asprintf.Plo + -rm -f ./$(DEPDIR)/empty.Plo + -rm -f ./$(DEPDIR)/getopt.Plo + -rm -f ./$(DEPDIR)/getopt1.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-alphasort.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-asprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-scandir.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-snprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strchrnul.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strerror.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strndup.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strnlen.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-strsignal.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-unsetenv.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-vasprintf.Plo + -rm -f ./$(DEPDIR)/libcompat_test_la-vsnprintf.Plo + -rm -f ./$(DEPDIR)/obstack.Plo + -rm -f ./$(DEPDIR)/scandir.Plo + -rm -f ./$(DEPDIR)/snprintf.Plo + -rm -f ./$(DEPDIR)/strchrnul.Plo + -rm -f ./$(DEPDIR)/strerror.Plo + -rm -f ./$(DEPDIR)/strndup.Plo + -rm -f ./$(DEPDIR)/strnlen.Plo + -rm -f ./$(DEPDIR)/strsignal.Plo + -rm -f ./$(DEPDIR)/unsetenv.Plo + -rm -f ./$(DEPDIR)/vasprintf.Plo + -rm -f ./$(DEPDIR)/vsnprintf.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/compat/alphasort.c b/lib/compat/alphasort.c new file mode 100644 index 0000000..b393b87 --- /dev/null +++ b/lib/compat/alphasort.c @@ -0,0 +1,32 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <dirent.h> + +#include "compat.h" + +int +alphasort(const void *a, const void *b) +{ + return strcmp((*(const struct dirent **)a)->d_name, + (*(const struct dirent **)b)->d_name); +} diff --git a/lib/compat/asprintf.c b/lib/compat/asprintf.c new file mode 100644 index 0000000..9605faf --- /dev/null +++ b/lib/compat/asprintf.c @@ -0,0 +1,38 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdarg.h> +#include <stdio.h> + +#include "compat.h" + +int +asprintf(char **strp, char const *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vasprintf(strp, fmt, args); + va_end(args); + + return n; +} diff --git a/lib/compat/compat-zlib.h b/lib/compat/compat-zlib.h new file mode 100644 index 0000000..aa15e4a --- /dev/null +++ b/lib/compat/compat-zlib.h @@ -0,0 +1,41 @@ +/* + * libcompat - system compatibility library + * compat-zlib.h - zlib compatibility declarations + * + * Copyright © 2021 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef COMPAT_ZLIB_H +#define COMPAT_ZLIB_H + +#if USE_LIBZ_IMPL == USE_LIBZ_IMPL_ZLIB_NG +#include <zlib-ng.h> +#elif USE_LIBZ_IMPL == USE_LIBZ_IMPL_ZLIB +#include <zlib.h> +#endif + +#if USE_LIBZ_IMPL == USE_LIBZ_IMPL_ZLIB_NG +/* Compatibility symbols for zlib-ng. */ +#define gzdopen zng_gzdopen +#define gzopen zng_gzopen +#define gzread zng_gzread +#define gzwrite zng_gzwrite +#define gzerror zng_gzerror +#define gzclose zng_gzclose +#define zError zng_zError +#endif + +#endif /* COMPAT_ZLIB_H */ diff --git a/lib/compat/compat.h b/lib/compat/compat.h new file mode 100644 index 0000000..861299c --- /dev/null +++ b/lib/compat/compat.h @@ -0,0 +1,218 @@ +/* + * libcompat - system compatibility library + * compat.h - system compatibility declarations + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008, 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef COMPAT_H +#define COMPAT_H + +#ifndef TEST_LIBCOMPAT +#define TEST_LIBCOMPAT 0 +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRNLEN) || !defined(HAVE_STRNDUP) || \ + !defined(HAVE_C99_SNPRINTF) +#include <stddef.h> +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_ASPRINTF) || !defined(HAVE_C99_SNPRINTF) +#include <stdarg.h> +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_VA_COPY) +#include <string.h> +#endif + +/* Language definitions. */ + +/* Supported since gcc 5.1.0 and clang 2.9.0. For attributes that appeared + * before these versions, in addition we need to do version checks. */ +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_include +#define __has_include(x) 0 +#endif + +#ifdef __GNUC__ +#define LIBCOMPAT_GCC_VERSION (__GNUC__ << 8 | __GNUC_MINOR__) +#else +#define LIBCOMPAT_GCC_VERSION 0 +#endif + +#if LIBCOMPAT_GCC_VERSION >= 0x0300 || __has_attribute(__format__) +#define LIBCOMPAT_ATTR_FMT(t, f, a) __attribute__((__format__(t, f, a))) +#define LIBCOMPAT_ATTR_PRINTF(n) LIBCOMPAT_ATTR_FMT(__printf__, n, n + 1) +#define LIBCOMPAT_ATTR_VPRINTF(n) LIBCOMPAT_ATTR_FMT(__printf__, n, 0) +#else +#define LIBCOMPAT_ATTR_FMT(t, f, a) +#define LIBCOMPAT_ATTR_PRINTF(n) +#define LIBCOMPAT_ATTR_VPRINTF(n) +#endif + +#if LIBCOMPAT_GCC_VERSION >= 0x0300 || __has_attribute(__noreturn__) +#define LIBCOMPAT_ATTR_NORET __attribute__((__noreturn__)) +#else +#define LIBCOMPAT_ATTR_NORET +#endif + +#if LIBCOMPAT_GCC_VERSION >= 0x0400 || __has_attribute(__sentinel__) +#define LIBCOMPAT_ATTR_SENTINEL __attribute__((__sentinel__)) +#else +#define LIBCOMPAT_ATTR_SENTINEL +#endif + +#if __has_attribute(__enum_extensibility__) +#define LIBCOMPAT_ATTR_ENUM_FLAGS \ + __attribute__((__enum_extensibility__(closed),__flag_enum__)) +#else +#define LIBCOMPAT_ATTR_ENUM_FLAGS +#endif + +/* For C++, define a __func__ fallback in case it's not natively supported. */ +#if defined(__cplusplus) && __cplusplus < 201103L +# if LIBCOMPAT_GCC_VERSION >= 0x0200 +# define __func__ __PRETTY_FUNCTION__ +# else +# define __func__ __FUNCTION__ +# endif +#endif + +#if defined(__cplusplus) && __cplusplus < 201103L +#define nullptr 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_OFFSETOF +#define offsetof(st, m) ((size_t)&((st *)NULL)->m) +#endif + +#ifndef HAVE_MAKEDEV +#define makedev(maj, min) ((((maj) & 0xff) << 8) | ((min) & 0xff)) +#endif + +#ifndef HAVE_O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + +#ifndef HAVE_P_TMPDIR +#define P_tmpdir "/tmp" +#endif + +/* + * Define WCOREDUMP if we don't have it already, coredumps won't be + * detected, though. + */ +#ifndef HAVE_WCOREDUMP +#define WCOREDUMP(x) 0 +#endif + +#ifndef HAVE_VA_COPY +#define va_copy(dest, src) memcpy(&(dest), &(src), sizeof(va_list)) +#endif + +#if TEST_LIBCOMPAT +#undef snprintf +#define snprintf test_snprintf +#undef vsnprintf +#define vsnprintf test_vsnprintf +#undef asprintf +#define asprintf test_asprintf +#undef vasprintf +#define vasprintf test_vasprintf +#undef strchrnul +#define strchrnul test_strchrnul +#undef strndup +#define strndup test_strndup +#undef strnlen +#define strnlen test_strnlen +#undef strerror +#define strerror test_strerror +#undef strsignal +#define strsignal test_strsignal +#undef scandir +#define scandir test_scandir +#undef alphasort +#define alphasort test_alphasort +#undef unsetenv +#define unsetenv test_unsetenv +#endif + +#if !HAVE_DECL_SYS_SIGLIST +extern const char *const sys_siglist[]; +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_C99_SNPRINTF) +int snprintf(char *str, size_t n, char const *fmt, ...) + LIBCOMPAT_ATTR_PRINTF(3); +int vsnprintf(char *buf, size_t maxsize, const char *fmt, va_list args) + LIBCOMPAT_ATTR_VPRINTF(3); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_ASPRINTF) +int asprintf(char **str, char const *fmt, ...) + LIBCOMPAT_ATTR_PRINTF(2); +int vasprintf(char **str, const char *fmt, va_list args) + LIBCOMPAT_ATTR_VPRINTF(2); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRCHRNUL) +char *strchrnul(const char *s, int c); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRNLEN) +size_t strnlen(const char *s, size_t n); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRNDUP) +char *strndup(const char *s, size_t n); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRERROR) +const char *strerror(int); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_STRSIGNAL) +const char *strsignal(int); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_SCANDIR) +struct dirent; +int scandir(const char *dir, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*cmp)(const void *, const void *)); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_ALPHASORT) +int alphasort(const void *a, const void *b); +#endif + +#if TEST_LIBCOMPAT || !defined(HAVE_UNSETENV) +int unsetenv(const char *x); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* COMPAT_H */ diff --git a/lib/compat/empty.c b/lib/compat/empty.c new file mode 100644 index 0000000..544a484 --- /dev/null +++ b/lib/compat/empty.c @@ -0,0 +1,3 @@ +/* Some implementations of ar cannot create an empty archive. */ +extern int libdpkg_empty_dummy_symbol; +int libdpkg_empty_dummy_symbol; diff --git a/lib/compat/getopt.c b/lib/compat/getopt.c new file mode 100644 index 0000000..73f05b4 --- /dev/null +++ b/lib/compat/getopt.c @@ -0,0 +1,854 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "getopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __VMS +# include <unixlib.h> +#endif + +/* XXX: Disable intl support, because we do not carry the translations anyway + * and this pulls indirectly libintl, which we do not want to impose. */ +#ifndef _ +#define _(msgid) (msgid) +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +/* Unlike standard Unix `getopt', functions like `getopt_long' + let the user intersperse the options with the other arguments. + + As `getopt_long' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Using `getopt' or setting the environment variable POSIXLY_CORRECT + disables permutation. + Then the application's behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt_int.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Keep a global copy of all internal members of getopt_data. */ + +static struct _getopt_data getopt_data; + + +#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV +extern char *getenv (); +#endif + +# define SWAP_FLAGS(ch1, ch2) + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv, struct _getopt_data *d) +{ + int bottom = d->__first_nonopt; + int middle = d->__last_nonopt; + int top = d->optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + d->__first_nonopt += (d->optind - d->__last_nonopt); + d->__last_nonopt = d->optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (int argc, char **argv, const char *optstring, + int posixly_correct, struct _getopt_data *d) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + d->__first_nonopt = d->__last_nonopt = d->optind; + + d->__nextchar = NULL; + + d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + d->__ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + d->__ordering = REQUIRE_ORDER; + ++optstring; + } + else if (d->__posixly_correct) + d->__ordering = REQUIRE_ORDER; + else + d->__ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. + + If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT + environment variable were set. */ + +int +_getopt_internal_r (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct, struct _getopt_data *d) +{ + int print_errors = d->opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + d->optarg = NULL; + + if (d->optind == 0 || !d->__initialized) + { + if (d->optind == 0) + d->optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring, + posixly_correct, d); + d->__initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') + + if (d->__nextchar == NULL || *d->__nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (d->__last_nonopt > d->optind) + d->__last_nonopt = d->optind; + if (d->__first_nonopt > d->optind) + d->__first_nonopt = d->optind; + + if (d->__ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__last_nonopt != d->optind) + d->__first_nonopt = d->optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (d->optind < argc && NONOPTION_P) + d->optind++; + d->__last_nonopt = d->optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (d->optind != argc && !strcmp (argv[d->optind], "--")) + { + d->optind++; + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__first_nonopt == d->__last_nonopt) + d->__first_nonopt = d->optind; + d->__last_nonopt = argc; + + d->optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (d->optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (d->__first_nonopt != d->__last_nonopt) + d->optind = d->__first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (d->__ordering == REQUIRE_ORDER) + return -1; + d->optarg = argv[d->optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + d->__nextchar = (argv[d->optind] + 1 + + (longopts != NULL && argv[d->optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[d->optind][1] == '-' + || (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[d->optind]); + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + d->optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[d->optind - 1][1] == '-') + { + /* --option */ + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + } + else + { + /* +option or -option */ + fprintf (stderr, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); + } + + } + + d->__nextchar += strlen (d->__nextchar); + + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); + } + d->__nextchar += strlen (d->__nextchar); + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + { + if (argv[d->optind][1] == '-') + { + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); + } + else + { + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); + } + + } + d->__nextchar = (char *) ""; + d->optind++; + d->optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *d->__nextchar++; + const char *temp = strchr (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*d->__nextchar == '\0') + ++d->optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (d->__posixly_correct) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); + } + else + { + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); + } + } + d->optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `d->optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[d->optind]); + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + } + + d->__nextchar += strlen (d->__nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); + } + d->__nextchar += strlen (d->__nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + d->__nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + d->optind++; + } + else + d->optarg = NULL; + d->__nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + d->__nextchar = NULL; + } + } + return c; + } +} + +int +_getopt_internal (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct) +{ + int result; + + getopt_data.optind = optind; + getopt_data.opterr = opterr; + + result = _getopt_internal_r (argc, argv, optstring, longopts, longind, + long_only, posixly_correct, &getopt_data); + + optind = getopt_data.optind; + optarg = getopt_data.optarg; + optopt = getopt_data.optopt; + + return result; +} + +/* glibc gets a LSB-compliant getopt. + Standalone applications get a POSIX-compliant getopt. */ +#if _LIBC +enum { POSIXLY_CORRECT = 0 }; +#else +enum { POSIXLY_CORRECT = 1 }; +#endif + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, + POSIXLY_CORRECT); +} + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int c; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/compat/getopt.h b/lib/compat/getopt.h new file mode 100644 index 0000000..d9c1494 --- /dev/null +++ b/lib/compat/getopt.h @@ -0,0 +1,129 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + the C library, however. The master source lives in /gd/gnu/lib. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ + +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/lib/compat/getopt1.c b/lib/compat/getopt1.c new file mode 100644 index 0000000..0ee87b0 --- /dev/null +++ b/lib/compat/getopt1.c @@ -0,0 +1,170 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "getopt.h" +#include "getopt_int.h" + +#include <stdio.h> + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 0, 0); +} + +int +_getopt_long_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 0, 0, d); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *const *argv, + const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 1, 0); +} + +int +_getopt_long_only_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 1, 0, d); +} + + +#ifdef TEST + +#include <stdio.h> + +int +main (int argc, char **argv) +{ + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static const struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + int c; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/compat/getopt_int.h b/lib/compat/getopt_int.h new file mode 100644 index 0000000..401579f --- /dev/null +++ b/lib/compat/getopt_int.h @@ -0,0 +1,131 @@ +/* Internal declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 _GETOPT_INT_H +#define _GETOPT_INT_H 1 + +extern int _getopt_internal (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); + + +/* Reentrant versions which can handle parsing multiple argument + vectors at the same time. */ + +/* Data type for reentrant functions. */ +struct _getopt_data +{ + /* These have exactly the same meaning as the corresponding global + variables, except that they are used for the reentrant + versions of getopt. */ + int optind; + int opterr; + int optopt; + char *optarg; + + /* Internal members. */ + + /* True if the internal members have been initialized. */ + int __initialized; + + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + char *__nextchar; + + /* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters, or by calling getopt. + + PERMUTE is the default. We permute the contents of ARGV as we + scan, so that eventually all the non-options are at the end. + This allows options to be given in any order, even with programs + that were not written to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. Using `-' as the first character of the + list of option characters selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + + enum + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + } __ordering; + + /* If the POSIXLY_CORRECT environment variable is set + or getopt was called. */ + int __posixly_correct; + + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first + of them; `last_nonopt' is the index after the last of them. */ + + int __first_nonopt; + int __last_nonopt; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + int __nonoption_flags_max_len; + int __nonoption_flags_len; +# endif +}; + +/* The initializer is necessary to set OPTIND and OPTERR to their + default values and to clear the initialization flag. */ +#define _GETOPT_DATA_INITIALIZER { 1, 1 } + +extern int _getopt_internal_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct, + struct _getopt_data *__data); + +extern int _getopt_long_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + struct _getopt_data *__data); + +extern int _getopt_long_only_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, + int *__longind, + struct _getopt_data *__data); + +#endif /* getopt_int.h */ diff --git a/lib/compat/gettext.h b/lib/compat/gettext.h new file mode 100644 index 0000000..69cb448 --- /dev/null +++ b/lib/compat/gettext.h @@ -0,0 +1,307 @@ +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002, 2004-2006 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, 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 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 _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + <libintl.h>, which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of <libintl.h> a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include <cstdlib> +# if (__GLIBC__ >= 2 && !defined __UCLIBC__) || _GLIBCXX_HAVE_LIBINTL_H +# include <libintl.h> +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? (const char *)(Msgid1) \ + : (const char *)(Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +#if ENABLE_NLS + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include <string.h> + +/* We do not want VLAs, which have a terrible failure mode on stack + * exhaustion. */ +#if 0 +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) +#endif + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include <stdlib.h> +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#else /* ENABLE_NLS */ + +#define pgettext(Msgctxt, Msgid) ((void) (Msgctxt), gettext (Msgid)) +#define dpgettext(Domainname, Msgctxt, Msgid) \ + ((void) (Domainname), pgettext (Msgctxt, Msgid)) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + ((void) (Category), dpgettext (Domainname, Msgctxt, Msgid)) + +#define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + ((void) (Msgctxt), ngettext (Msgid, MsgidPlural, N)) +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + ((void) (Domainname), npgettext (Msgctxt, Msgid, MsgidPlural, N) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + ((void) (Category), dnpgettext (Domainname, Msgctxt, Msgid, MsgidPlural, N) + +#define pgettext_expr(Msgctxt, Msgid) pgettext (Msgctxt, Msgid) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dpgettext (Domainname, Msgctxt, Msgid) +#define dcpgettext_expr(Domainname, Msgctxt, Msgid, Category) \ + dcpgettext (Domainname, Msgctxt, Msgid, Category) + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext (Msgctxt, Msgid, MsgidPlural, N) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dnpgettext (Domainname, Msgctxt, Msgid, MsgidPlural, N) +#define dcnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + dcnpgettext (Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) + +#endif /* ENABLE_NLS */ + +#endif /* _LIBGETTEXT_H */ diff --git a/lib/compat/obstack.c b/lib/compat/obstack.c new file mode 100644 index 0000000..ca9aa11 --- /dev/null +++ b/lib/compat/obstack.c @@ -0,0 +1,416 @@ +/* obstack.c - subroutines used implicitly by object stack macros + + Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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, 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. */ + +#ifdef _LIBC +# include <obstack.h> +# include <shlib-compat.h> +#else +# include <config.h> +# include "obstack.h" +#endif + +/* NOTE BEFORE MODIFYING THIS FILE: This version number must be + incremented whenever callers compiled using an old obstack.h can no + longer properly call the functions in this obstack.c. */ +#define OBSTACK_INTERFACE_VERSION 1 + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself, and the installed library + supports the same library interface we do. This code is part of the GNU + C Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object + files, it is simpler to just do this in the source for each such file. */ + +#include <stdio.h> /* Random thing to get __GNU_LIBRARY__. */ +#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1 +# include <gnu-versions.h> +# if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#include <stddef.h> + +#ifndef ELIDE_CODE + +# include <stdint.h> + +/* Determine default alignment. */ +union fooround +{ + uintmax_t i; + long double d; + void *p; +}; +struct fooalign +{ + char c; + union fooround u; +}; +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +enum + { + DEFAULT_ALIGNMENT = offsetof (struct fooalign, u), + DEFAULT_ROUNDING = sizeof (union fooround) + }; + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +# ifndef COPYING_UNIT +# define COPYING_UNIT int +# endif + + +/* The functions allocating more room by calling `obstack_chunk_alloc' + jump to the handler pointed to by `obstack_alloc_failed_handler'. + This can be set to a user defined function which should either + abort gracefully or use longjmp - but shouldn't return. This + variable by default points to the internal function + `print_and_abort'. */ +static void print_and_abort (void); +void (*obstack_alloc_failed_handler) (void) = print_and_abort; + +/* Exit value used when `print_and_abort' is used. */ +# include <stdlib.h> +# ifdef _LIBC +int obstack_exit_failure = EXIT_FAILURE; +# else +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif +extern int volatile exit_failure; +int volatile exit_failure = EXIT_FAILURE; +# define obstack_exit_failure exit_failure +# endif + +# ifdef _LIBC +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +/* A looong time ago (before 1994, anyway; we're not sure) this global variable + was used by non-GNU-C macros to avoid multiple evaluation. The GNU C + library still exports it because somebody might use it. */ +struct obstack *_obstack_compat; +compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0); +# endif +# endif + +/* Define a macro that either calls functions with the traditional malloc/free + calling interface, or calls functions with the mmalloc/mfree interface + (that adds an extra first argument), based on the state of use_extra_arg. + For free, do not use ?:, since some compilers, like the MIPS compilers, + do not allow (expr) ? void : void. */ + +# define CALL_CHUNKFUN(h, size) \ + (((h) -> use_extra_arg) \ + ? (*(h)->chunkfun.arg2) ((h)->extra_arg, (size)) \ + : (*(h)->chunkfun.arg1) ((size))) + +# define CALL_FREEFUN(h, old_chunk) \ + do { \ + if ((h) -> use_extra_arg) \ + (*(h)->freefun.arg2) ((h)->extra_arg, (old_chunk)); \ + else \ + (*(h)->freefun.arg1) ((old_chunk)); \ + } while (0) + + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. + + Return nonzero if successful, calls obstack_alloc_failed_handler if + allocation fails. */ + +int +_obstack_begin (struct obstack *h, + size_t size, size_t alignment, + void *(*chunkfun) (size_t), + void (*freefun) (void *)) +{ + struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun.arg1 = chunkfun; + h->freefun.arg1 = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->use_extra_arg = 0; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +int +_obstack_begin_1 (struct obstack *h, size_t size, size_t alignment, + void *(*chunkfun) (void *, size_t), + void (*freefun) (void *, void *), + void *arg) +{ + struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun.arg2 = chunkfun; + h->freefun.arg2 = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->extra_arg = arg; + h->use_extra_arg = 1; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (struct obstack *h, size_t length) +{ + struct _obstack_chunk *old_chunk = h->chunk; + struct _obstack_chunk *new_chunk; + size_t new_size; + size_t obj_size = h->next_free - h->object_base; + char *object_base; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = CALL_CHUNKFUN (h, new_size); + if (!new_chunk) + (*obstack_alloc_failed_handler) (); + h->chunk = new_chunk; + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Compute an aligned object_base in the new chunk */ + object_base = + __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask); + + /* Move the existing object to the new chunk. */ + memcpy(object_base, h->object_base, obj_size); + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. + But not if that chunk might contain an empty object. */ + if (! h->maybe_empty_object + && (h->object_base + == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents, + h->alignment_mask))) + { + new_chunk->prev = old_chunk->prev; + CALL_FREEFUN (h, old_chunk); + } + + h->object_base = object_base; + h->next_free = h->object_base + obj_size; + /* The new chunk certainly contains no empty object yet. */ + h->maybe_empty_object = 0; +} +# ifdef _LIBC +libc_hidden_def (_obstack_newchunk) +# endif + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +/* Suppress -Wmissing-prototypes warning. We don't want to declare this in + obstack.h because it is just for debugging. */ +int _obstack_allocated_p (struct obstack *h, void *obj); + +int +_obstack_allocated_p (struct obstack *h, void *obj) +{ + struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= rather than > since the object cannot be exactly at + the beginning of the chunk but might be an empty object exactly + at the end of an adjacent chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +# undef obstack_free + +void +__obstack_free (struct obstack *h, void *obj) +{ + struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *) (obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +# ifdef _LIBC +/* Older versions of libc used a function _obstack_free intended to be + called by non-GCC compilers. */ +strong_alias (obstack_free, _obstack_free) +# endif + +size_t +_obstack_memory_used (struct obstack *h) +{ + struct _obstack_chunk* lp; + size_t nbytes = 0; + + for (lp = h->chunk; lp != 0; lp = lp->prev) + { + nbytes += lp->limit - (char *) lp; + } + return nbytes; +} + +/* Define the error handler. */ +# ifdef _LIBC +# include <libintl.h> +# else +# include "gettext.h" +# endif +# ifndef _ +# define _(msgid) gettext (msgid) +# endif + +# ifdef _LIBC +# include <libio/iolibio.h> +# endif + +# ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +# define __attribute__(Spec) /* empty */ +# endif +# endif + +static void +__attribute__ ((noreturn)) +print_and_abort (void) +{ + /* Don't change any of these strings. Yes, it would be possible to add + the newline to the string and use fputs or so. But this must not + happen because the "memory exhausted" message appears in other places + like this and the translation should be reused instead of creating + a very similar string which requires a separate translation. */ +# ifdef _LIBC + (void) __fxprintf (NULL, "%s\n", _("memory exhausted")); +# else + fprintf (stderr, "%s\n", _("memory exhausted")); +# endif + exit (obstack_exit_failure); +} + +#endif /* !ELIDE_CODE */ diff --git a/lib/compat/obstack.h b/lib/compat/obstack.h new file mode 100644 index 0000000..6236cf3 --- /dev/null +++ b/lib/compat/obstack.h @@ -0,0 +1,509 @@ +/* obstack.h - object stack macros + Copyright (C) 1988-1994,1996-1999,2003,2004,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +--Gosper's immortal quote from HAKMEM item 154, out of context--you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef _OBSTACK_H +#define _OBSTACK_H 1 + +# include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* If B is the base of an object addressed by P, return the result of + aligning P to the next multiple of A + 1. B and P must be of type + char *. A + 1 must be a power of 2. */ + +#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A))) + +/* Similar to _BPTR_ALIGN (B, P, A), except optimize the common case + where pointers can be converted to integers, aligned as integers, + and converted back again. If ptrdiff_t is narrower than a + pointer (e.g., the AS/400), play it safe and compute the alignment + relative to B. Otherwise, use the faster strategy of computing the + alignment relative to 0. */ + +#define __PTR_ALIGN(B, P, A) \ + __BPTR_ALIGN (sizeof (ptrdiff_t) < sizeof (void *) ? (B) : (char *) 0, \ + P, A) + +#include <string.h> + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + size_t chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + union + { + ptrdiff_t tempint; + void *tempptr; + } temp; /* Temporary for some macros. */ + size_t alignment_mask; /* Mask of alignment for each object. */ + /* These prototypes vary based on `use_extra_arg', and we use + casts to the prototypeless function type in all assignments, + but having prototypes here quiets -Wstrict-prototypes. */ + union { + void *(*arg1) (size_t); + void *(*arg2) (void *, size_t); + } chunkfun; + union { + void (*arg1) (void *); + void (*arg2) (void *, void *); + } freefun; + void *extra_arg; /* first arg for chunk alloc/dealloc funcs */ + unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object:1;/* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ + unsigned alloc_failed:1; /* No longer used, as we now call the failed + handler on error, but retained for binary + compatibility. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +extern void _obstack_newchunk (struct obstack *, size_t); +extern int _obstack_begin (struct obstack *, size_t, size_t, + void *(*) (size_t), void (*) (void *)); +extern int _obstack_begin_1 (struct obstack *, size_t, size_t, + void *(*) (void *, size_t), + void (*) (void *, void *), void *); +extern size_t _obstack_memory_used (struct obstack *); + +/* The default name of the function for freeing a chunk is 'obstack_free', + but gnulib users can override this by defining '__obstack_free'. */ +#ifndef __obstack_free +# define __obstack_free obstack_free +#endif +extern void __obstack_free (struct obstack *obstack, void *block); + + +/* Error handler called when `obstack_chunk_alloc' failed to allocate + more memory. This can be set to a user defined function which + should either abort gracefully or use longjmp - but shouldn't + return. The default action is to print a message and abort. */ +extern void (*obstack_alloc_failed_handler) (void); + +/* Exit value used when `print_and_abort' is used. */ +extern int obstack_exit_failure; + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((void *) (h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +/* To prevent prototype warnings provide complete argument list. */ +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) (size_t)) obstack_chunk_alloc, \ + (void (*) (void *)) obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) (size_t)) obstack_chunk_alloc, \ + (void (*) (void *)) obstack_chunk_free) + +#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) (size_t)) (chunkfun), \ + (void (*) (void *)) (freefun)) + +#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) (void *, size_t)) (chunkfun), \ + (void (*) (void *, void *)) (freefun), (arg)) + +#define obstack_chunkfun(h, newchunkfun) \ + ((h) -> chunkfun.arg2 = (struct _obstack_chunk *(*)(void *, size_t)) (newchunkfun)) + +#define obstack_freefun(h, newfreefun) \ + ((h) -> freefun.arg2 = (void (*)(void *, struct _obstack_chunk *)) (newfreefun)) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar)) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#define obstack_memory_used(h) _obstack_memory_used (h) + +#if defined __GNUC__ && defined __STDC__ && __STDC__ +/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and + does not implement __extension__. But that compiler doesn't define + __GNUC_MINOR__. */ +# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__) +# define __extension__ +# endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +# define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (size_t) (__o->next_free - __o->object_base); }) + +# define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (size_t) (__o->chunk_limit - __o->next_free); }) + +# define obstack_make_room(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + size_t __len = (length); \ + if ((size_t) (__o->chunk_limit - __o->next_free) < __len) \ + _obstack_newchunk (__o, __len); \ + (void) 0; }) + +# define obstack_empty_p(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (__o->chunk->prev == 0 \ + && __o->next_free == __PTR_ALIGN ((char *) __o->chunk, \ + __o->chunk->contents, \ + __o->alignment_mask)); }) + +# define obstack_grow(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + size_t __len = (length); \ + if (__o->next_free + __len > __o->chunk_limit) \ + _obstack_newchunk (__o, __len); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +# define obstack_grow0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + size_t __len = (length); \ + if (__o->next_free + __len + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, __len + 1); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +# define obstack_1grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, 1); \ + obstack_1grow_fast (__o, datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers + or ints, and that the data added so far to the current object + shares that much alignment. */ + +# define obstack_ptr_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (void *) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (void *)); \ + obstack_ptr_grow_fast (__o, datum); }) \ + +# define obstack_int_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (int) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (int)); \ + obstack_int_grow_fast (__o, datum); }) + +# define obstack_ptr_grow_fast(OBSTACK,aptr) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(const void **) __o1->next_free = (aptr); \ + __o1->next_free += sizeof (const void *); \ + (void) 0; }) + +# define obstack_int_grow_fast(OBSTACK,aint) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(int *) __o1->next_free = (aint); \ + __o1->next_free += sizeof (int); \ + (void) 0; }) + +# define obstack_blank(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + size_t __len = (length); \ + if ((size_t) (__o->chunk_limit - __o->next_free) < __len) \ + _obstack_newchunk (__o, __len); \ + obstack_blank_fast (__o, __len); \ + (void) 0; }) + +# define obstack_alloc(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +# define obstack_copy(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +# define obstack_copy0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +# define obstack_finish(OBSTACK) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + void *__value = (void *) __o1->object_base; \ + if (__o1->next_free == __value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __PTR_ALIGN (__o1->object_base, __o1->next_free, \ + __o1->alignment_mask); \ + if ((size_t) (__o1->next_free - (char *)__o1->chunk) \ + > (size_t) (__o1->chunk_limit - (char *)__o1->chunk)) \ + __o1->next_free = __o1->chunk_limit; \ + __o1->object_base = __o1->next_free; \ + __value; }) + +# define obstack_free(OBSTACK, OBJ) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = (char *)__obj; \ + else (__obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +# define obstack_object_size(h) \ + (size_t) ((h)->next_free - (h)->object_base) + +# define obstack_room(h) \ + (size_t) ((h)->chunk_limit - (h)->next_free) + +# define obstack_empty_p(h) \ + ((h)->chunk->prev == 0 \ + && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk, \ + (h)->chunk->contents, \ + (h)->alignment_mask)) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ + +# define obstack_make_room(h,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0)) + +# define obstack_grow(h,where,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint) + +# define obstack_grow0(h,where,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint, \ + *((h)->next_free)++ = 0) + +# define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + obstack_1grow_fast (h, datum)) + +# define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + obstack_ptr_grow_fast (h, datum)) + +# define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + obstack_int_grow_fast (h, datum)) + +# define obstack_ptr_grow_fast(h,aptr) \ + (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr)) + +# define obstack_int_grow_fast(h,aint) \ + (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint)) + +# define obstack_blank(h,length) \ +( (h)->temp.tempint = (length), \ + (((size_t) ((h)->chunk_limit - (h)->next_free) < (h)->temp.tempint) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + obstack_blank_fast (h, (h)->temp.tempint)) + +# define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +# define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_finish(h) \ +( ((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp.tempptr = (h)->object_base, \ + (h)->next_free \ + = __PTR_ALIGN ((h)->object_base, (h)->next_free, \ + (h)->alignment_mask), \ + (((size_t) ((h)->next_free - (char *) (h)->chunk) \ + > (size_t) ((h)->chunk_limit - (char *) (h)->chunk)) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + (h)->temp.tempptr) + +# define obstack_free(h,obj) \ +( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \ + ((((h)->temp.tempint > 0 \ + && (h)->temp.tempint < (size_t) ((h)->chunk_limit - (char *) (h)->chunk))) \ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp.tempint + (char *) (h)->chunk) \ + : (((__obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0))) + +#endif /* not __GNUC__ or not __STDC__ */ + +#ifdef __cplusplus +} /* C++ */ +#endif + +#endif /* obstack.h */ diff --git a/lib/compat/scandir.c b/lib/compat/scandir.c new file mode 100644 index 0000000..8771de0 --- /dev/null +++ b/lib/compat/scandir.c @@ -0,0 +1,99 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008, 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <sys/types.h> + +#include <string.h> +#include <dirent.h> +#include <stdlib.h> + +#include "compat.h" + +static int +cleanup(DIR *dir, struct dirent **dirlist, int used) +{ + if (dir) + closedir(dir); + + if (dirlist) { + int i; + + for (i = 0; i < used; i++) + free(dirlist[i]); + free(dirlist); + } + + return -1; +} + +int +scandir(const char *dir, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*cmp)(const void *, const void *)) +{ + DIR *d; + struct dirent *e, *m, **list; + int used, avail; + + d = opendir(dir); + if (!d) + return -1; + + list = NULL; + used = avail = 0; + + while ((e = readdir(d)) != NULL) { + if (filter != NULL && !filter(e)) + continue; + + if (used >= avail - 1) { + struct dirent **newlist; + + if (avail) + avail *= 2; + else + avail = 20; + newlist = realloc(list, avail * sizeof(*newlist)); + if (!newlist) + return cleanup(d, list, used); + list = newlist; + } + + m = malloc(sizeof(*m) + strlen(e->d_name)); + if (!m) + return cleanup(d, list, used); + *m = *e; + strcpy(m->d_name, e->d_name); + + list[used] = m; + used++; + } + + closedir(d); + + if (list != NULL && cmp != NULL) + qsort(list, used, sizeof(list[0]), cmp); + + *namelist = list; + + return used; +} diff --git a/lib/compat/snprintf.c b/lib/compat/snprintf.c new file mode 100644 index 0000000..cf15442 --- /dev/null +++ b/lib/compat/snprintf.c @@ -0,0 +1,39 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> + +#include "compat.h" + +int +snprintf(char *str, size_t n, char const *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsnprintf(str, n, fmt, args); + va_end(args); + + return i; +} diff --git a/lib/compat/strchrnul.c b/lib/compat/strchrnul.c new file mode 100644 index 0000000..b072e1a --- /dev/null +++ b/lib/compat/strchrnul.c @@ -0,0 +1,37 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include "compat.h" + +char * +strchrnul(const char *s, int c) +{ + char *match; + + match = strchr(s, c); + if (match) + return match; + + return (char *)s + strlen(s); +} diff --git a/lib/compat/strerror.c b/lib/compat/strerror.c new file mode 100644 index 0000000..e35ffc1 --- /dev/null +++ b/lib/compat/strerror.c @@ -0,0 +1,48 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <gettext.h> + +#include "compat.h" + +#define _(str) gettext(str) + +#if !HAVE_DECL_SYS_ERRLIST +extern const char *const sys_errlist[]; +#endif +#if !HAVE_DECL_SYS_NERR +extern const int sys_nerr; +#endif + +const char * +strerror(int e) +{ + static char buf[100]; + + if (e >= 0 && e < sys_nerr) + return sys_errlist[e]; + + sprintf(buf, _("Unknown error %d"), e); + + return buf; +} diff --git a/lib/compat/strndup.c b/lib/compat/strndup.c new file mode 100644 index 0000000..788c013 --- /dev/null +++ b/lib/compat/strndup.c @@ -0,0 +1,42 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include "compat.h" + +char * +strndup(const char *s, size_t n) +{ + size_t len; + char *str; + + len = strnlen(s, n); + str = malloc(len + 1); + if (str == NULL) + return NULL; + + memcpy(str, s, len); + str[len] = '\0'; + + return str; +} diff --git a/lib/compat/strnlen.c b/lib/compat/strnlen.c new file mode 100644 index 0000000..d02bb4b --- /dev/null +++ b/lib/compat/strnlen.c @@ -0,0 +1,33 @@ +/* Find the length of STRING, but scan at most MAXLEN characters. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + Written by Simon Josefsson. + + 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. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "compat.h" + +/* Find the length of STRING, but scan at most MAXLEN characters. + If no '\0' terminator is found in that many characters, return MAXLEN. */ + +size_t +strnlen (const char *string, size_t maxlen) +{ + const char *end = memchr (string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} diff --git a/lib/compat/strsignal.c b/lib/compat/strsignal.c new file mode 100644 index 0000000..66ed0c3 --- /dev/null +++ b/lib/compat/strsignal.c @@ -0,0 +1,76 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <gettext.h> + +#include "compat.h" + +#define _(str) gettext(str) + +#if !HAVE_DECL_SYS_SIGLIST +const char *const sys_siglist[] = { + NULL, /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGEMT", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ +}; +# define COMPAT_NSIGLIST (int)(sizeof(sys_siglist) / sizeof(sys_siglist[0])) +#else +# ifndef NSIG +# define NSIG 32 +# endif +# define COMPAT_NSIGLIST NSIG +#endif + +const char * +strsignal(int s) +{ + static char buf[100]; + + if (s > 0 && s < COMPAT_NSIGLIST) + return sys_siglist[s]; + + sprintf(buf, _("Unknown signal %d"), s); + + return buf; +} diff --git a/lib/compat/unsetenv.c b/lib/compat/unsetenv.c new file mode 100644 index 0000000..4094ae3 --- /dev/null +++ b/lib/compat/unsetenv.c @@ -0,0 +1,39 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include "compat.h" + +int +unsetenv(const char *p) +{ + char *q; + + q = malloc(strlen(p) + 3); + if (!q) + return -1; + + strcpy(q, p); + strcat(q, "="); + return putenv(q); +} diff --git a/lib/compat/vasprintf.c b/lib/compat/vasprintf.c new file mode 100644 index 0000000..9d53a32 --- /dev/null +++ b/lib/compat/vasprintf.c @@ -0,0 +1,59 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "compat.h" + +int +vasprintf(char **strp, char const *fmt, va_list args) +{ + va_list args_copy; + int needed, n; + char *str; + + va_copy(args_copy, args); + needed = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + if (needed < 0) { + *strp = NULL; + return -1; + } + + str = malloc(needed + 1); + if (str == NULL) { + *strp = NULL; + return -1; + } + + n = vsnprintf(str, needed + 1, fmt, args); + if (n < 0) { + free(str); + str = NULL; + } + + *strp = str; + + return n; +} diff --git a/lib/compat/vsnprintf.c b/lib/compat/vsnprintf.c new file mode 100644 index 0000000..5f709d9 --- /dev/null +++ b/lib/compat/vsnprintf.c @@ -0,0 +1,81 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <sys/types.h> + +#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> + +#include "compat.h" + +int +vsnprintf(char *buf, size_t maxsize, const char *fmt, va_list args) +{ + static FILE *file = NULL; + static pid_t file_pid; + + size_t want, nr; + int total; + + if (maxsize != 0 && buf == NULL) + return -1; + + /* Avoid race conditions from children after a fork(2). */ + if (file_pid > 0 && file_pid != getpid()) { + fclose(file); + file = NULL; + } + + if (!file) { + file = tmpfile(); + if (!file) + return -1; + file_pid = getpid(); + } else { + if (fseek(file, 0, 0)) + return -1; + if (ftruncate(fileno(file), 0)) + return -1; + } + + total = vfprintf(file, fmt, args); + if (total < 0) + return -1; + if (maxsize == 0) + return total; + if (total >= (int)maxsize) + want = maxsize - 1; + else + want = total; + if (fflush(file)) + return -1; + if (fseek(file, 0, SEEK_SET)) + return -1; + + nr = fread(buf, 1, want, file); + if (nr != want) + return -1; + buf[want] = '\0'; + + return total; +} diff --git a/lib/dpkg/Makefile.am b/lib/dpkg/Makefile.am new file mode 100644 index 0000000..9482e32 --- /dev/null +++ b/lib/dpkg/Makefile.am @@ -0,0 +1,296 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DLOCALEDIR=\"$(localedir)\" \ + -DCONFIGDIR=\"$(pkgconfdir)\" \ + -DADMINDIR=\"$(admindir)\" \ + -DDEFAULT_TEXT_DOMAIN=\"@PACKAGE@\" \ + -idirafter $(top_srcdir)/lib/compat \ + -I$(top_builddir) \ + -I$(top_srcdir)/lib \ + # EOL + +# Needed for the test suite +LDADD = \ + libdpkg.la \ + $(LIBINTL) \ + ../compat/libcompat.la \ + # EOL + +DISTCLEANFILES = + +EXTRA_DIST = \ + $(test_scripts) \ + $(test_data) \ + libdpkg.map \ + libdpkg.pc.in \ + # EOL + +pkgconfigdir = $(devlibdir)/pkgconfig +pkgconfig_DATA = libdpkg.pc + +devlib_LTLIBRARIES = libdpkg.la + +EXTRA_libdpkg_la_DEPENDENCIES = \ + libdpkg.map \ + # EOL + +libdpkg_la_LDFLAGS = \ + -no-undefined \ + # EOL +if HAVE_LINKER_VERSION_SCRIPT +libdpkg_la_LDFLAGS += \ + -Wl,--version-script=$(srcdir)/libdpkg.map \ + # EOL +else +libdpkg_la_LDFLAGS += \ + -export-symbols libdpkg.sym \ + # EOL +EXTRA_libdpkg_la_DEPENDENCIES += \ + libdpkg.sym \ + # EOL +endif +libdpkg_la_LDFLAGS += $(MD_LIBS) +libdpkg_la_LIBADD = \ + ../compat/libcompat.la \ + # EOL +if BUILD_SHARED +libdpkg_la_LIBADD += \ + $(LIBINTL) \ + $(Z_LIBS) \ + $(LZMA_LIBS) \ + $(ZSTD_LIBS) \ + $(BZ2_LIBS) \ + # EOL +endif +libdpkg_la_SOURCES = \ + dlist.h \ + ar.c \ + arch.c \ + atomic-file.c \ + buffer.c \ + c-ctype.c \ + cleanup.c \ + color.c \ + command.c \ + compress.c \ + dbdir.c \ + dbmodify.c \ + db-ctrl-access.c \ + db-ctrl-format.c \ + db-ctrl-upgrade.c \ + db-fsys-digest.c \ + db-fsys-divert.c \ + db-fsys-files.c \ + db-fsys-override.c \ + deb-version.c \ + debug.c \ + depcon.c \ + dir.c \ + dump.c \ + ehandle.c \ + error.c \ + fdio.c \ + file.c \ + fields.c \ + fsys-dir.c\ + fsys-iter.c \ + fsys-hash.c \ + glob.c \ + i18n.c i18n.h \ + log.c \ + meminfo.c \ + mustlib.c \ + namevalue.c \ + nfmalloc.c \ + options.c \ + options-dirs.c \ + options-parsers.c \ + pager.c \ + parse.c \ + parsehelp.c \ + path.c \ + path-remove.c \ + perf.h \ + pkg.c \ + pkg-array.c \ + pkg-files.c \ + pkg-format.c \ + pkg-hash.c \ + pkg-list.c \ + pkg-namevalue.c \ + pkg-queue.c \ + pkg-show.c \ + pkg-spec.c \ + progname.c \ + program.c \ + progress.c \ + report.c \ + string.c \ + strhash.c \ + strwide.c \ + subproc.c \ + tarfn.c \ + test.h \ + treewalk.c \ + trigname.c \ + trignote.c \ + triglib.c \ + trigdeferred.c \ + utils.c \ + varbuf.c \ + version.c \ + # EOL + +pkginclude_HEADERS = \ + ar.h \ + arch.h \ + atomic-file.h \ + buffer.h \ + c-ctype.h \ + color.h \ + command.h \ + compress.h \ + db-ctrl.h \ + db-fsys.h \ + deb-version.h \ + debug.h \ + dir.h \ + dpkg.h \ + dpkg-db.h \ + ehandle.h \ + error.h \ + fdio.h \ + file.h \ + fsys.h \ + glob.h \ + macros.h \ + meminfo.h \ + namevalue.h \ + options.h \ + pager.h \ + parsedump.h \ + path.h \ + pkg.h \ + pkg-array.h \ + pkg-files.h \ + pkg-format.h \ + pkg-list.h \ + pkg-queue.h \ + pkg-show.h \ + pkg-spec.h \ + progname.h \ + program.h \ + progress.h \ + report.h \ + string.h \ + subproc.h \ + tarfn.h \ + treewalk.h \ + trigdeferred.h \ + triglib.h \ + varbuf.h \ + version.h \ + # EOL + +DISTCLEANFILES += \ + libdpkg.sym \ + # EOL + +# Generate a simple libtool symbol export list to be used as a fallback if +# there is no version script support. +libdpkg.sym: libdpkg.map + $(AM_V_GEN) $(SED) -n \ + -e 's/#.*$$//g' \ + -e 's/^[[:space:]]\{1,\}\([A-Za-z0-9_]\{1,\}\);/\1/p' \ + $(srcdir)/libdpkg.map \ + >$@ + +t_t_headers_cpp_SOURCES = \ + t/t-headers-cpp.cc \ + # EOL + +# The tests are sorted in order of increasing complexity. +test_programs = \ + t/t-test \ + t/t-test-skip \ + t/t-macros \ + t/t-headers-cpp \ + t/t-c-ctype \ + t/t-namevalue \ + t/t-ehandle \ + t/t-error \ + t/t-string \ + t/t-file \ + t/t-buffer \ + t/t-meminfo \ + t/t-path \ + t/t-progname \ + t/t-subproc \ + t/t-command \ + t/t-pager \ + t/t-varbuf \ + t/t-ar \ + t/t-tar \ + t/t-deb-version \ + t/t-arch \ + t/t-version \ + t/t-pkginfo \ + t/t-pkg-list \ + t/t-pkg-queue \ + t/t-pkg-hash \ + t/t-pkg-show \ + t/t-pkg-format \ + t/t-fsys-dir \ + t/t-fsys-hash \ + t/t-trigger \ + t/t-mod-db \ + # EOL + +test_scripts = \ + t/t-tarextract.t \ + t/t-treewalk.t \ + t/t-trigdeferred.t \ + # EOL + +test_data = \ + t/data/command/path-a/cmd \ + t/data/command/path-a/cmd-a \ + t/data/command/path-b/cmd \ + t/data/command/path-b/cmd-b \ + t/data/command/path-noexec/cmd \ + t/data/command/path-noexec/cmd-noexec \ + t/data/meminfo-no-data \ + t/data/meminfo-no-info \ + t/data/meminfo-no-unit \ + t/data/meminfo-ok \ + # EOL + +BENCHMARK_LDADD_FLAGS = \ + $(RT_LIBS) \ + $(LDADD) \ + # EOL + +t_b_fsys_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) +t_b_pkg_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) + +check_PROGRAMS = \ + $(test_programs) \ + t/b-fsys-hash \ + t/b-pkg-hash \ + t/c-tarextract \ + t/c-treewalk \ + t/c-trigdeferred \ + # EOL + +TEST_ENV_VARS = \ + DPKG_PROGTAR=$(TAR) \ + # EOL + +test_tmpdir = t.tmp + +include $(top_srcdir)/build-aux/tap.am + +check-local: tap-check + +clean-local: tap-clean diff --git a/lib/dpkg/Makefile.in b/lib/dpkg/Makefile.in new file mode 100644 index 0000000..a03e3d3 --- /dev/null +++ b/lib/dpkg/Makefile.in @@ -0,0 +1,1991 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Variables to be defined: +# +# TEST_VERBOSE - set to 0 (default) or 1 to control test suite verbosity +# TEST_PARALLEL - set to 1 (default) or N to control the parallel jobs +# TEST_ENV_VARS - environment variables to be set for the test suite +# TEST_COVERAGE - set to the perl module in charge of getting test coverage +# test_tmpdir - test suite temporary directory +# test_scripts - list of test case scripts +# test_programs - list of test case programs +# test_data - list of test data files + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_LINKER_VERSION_SCRIPT_TRUE@am__append_1 = \ +@HAVE_LINKER_VERSION_SCRIPT_TRUE@ -Wl,--version-script=$(srcdir)/libdpkg.map \ +@HAVE_LINKER_VERSION_SCRIPT_TRUE@ # EOL + +@HAVE_LINKER_VERSION_SCRIPT_FALSE@am__append_2 = \ +@HAVE_LINKER_VERSION_SCRIPT_FALSE@ -export-symbols libdpkg.sym \ +@HAVE_LINKER_VERSION_SCRIPT_FALSE@ # EOL + +@HAVE_LINKER_VERSION_SCRIPT_FALSE@am__append_3 = \ +@HAVE_LINKER_VERSION_SCRIPT_FALSE@ libdpkg.sym \ +@HAVE_LINKER_VERSION_SCRIPT_FALSE@ # EOL + +@BUILD_SHARED_TRUE@am__append_4 = \ +@BUILD_SHARED_TRUE@ $(LIBINTL) \ +@BUILD_SHARED_TRUE@ $(Z_LIBS) \ +@BUILD_SHARED_TRUE@ $(LZMA_LIBS) \ +@BUILD_SHARED_TRUE@ $(ZSTD_LIBS) \ +@BUILD_SHARED_TRUE@ $(BZ2_LIBS) \ +@BUILD_SHARED_TRUE@ # EOL + +check_PROGRAMS = $(am__EXEEXT_1) t/b-fsys-hash$(EXEEXT) \ + t/b-pkg-hash$(EXEEXT) t/c-tarextract$(EXEEXT) \ + t/c-treewalk$(EXEEXT) t/c-trigdeferred$(EXEEXT) +subdir = lib/dpkg +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/dpkg-arch.m4 \ + $(top_srcdir)/m4/dpkg-build.m4 \ + $(top_srcdir)/m4/dpkg-compiler.m4 \ + $(top_srcdir)/m4/dpkg-coverage.m4 \ + $(top_srcdir)/m4/dpkg-funcs.m4 \ + $(top_srcdir)/m4/dpkg-headers.m4 $(top_srcdir)/m4/dpkg-libs.m4 \ + $(top_srcdir)/m4/dpkg-linker.m4 $(top_srcdir)/m4/dpkg-progs.m4 \ + $(top_srcdir)/m4/dpkg-types.m4 \ + $(top_srcdir)/m4/dpkg-unicode.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libdpkg.pc +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = t/t-test$(EXEEXT) t/t-test-skip$(EXEEXT) \ + t/t-macros$(EXEEXT) t/t-headers-cpp$(EXEEXT) \ + t/t-c-ctype$(EXEEXT) t/t-namevalue$(EXEEXT) \ + t/t-ehandle$(EXEEXT) t/t-error$(EXEEXT) t/t-string$(EXEEXT) \ + t/t-file$(EXEEXT) t/t-buffer$(EXEEXT) t/t-meminfo$(EXEEXT) \ + t/t-path$(EXEEXT) t/t-progname$(EXEEXT) t/t-subproc$(EXEEXT) \ + t/t-command$(EXEEXT) t/t-pager$(EXEEXT) t/t-varbuf$(EXEEXT) \ + t/t-ar$(EXEEXT) t/t-tar$(EXEEXT) t/t-deb-version$(EXEEXT) \ + t/t-arch$(EXEEXT) t/t-version$(EXEEXT) t/t-pkginfo$(EXEEXT) \ + t/t-pkg-list$(EXEEXT) t/t-pkg-queue$(EXEEXT) \ + t/t-pkg-hash$(EXEEXT) t/t-pkg-show$(EXEEXT) \ + t/t-pkg-format$(EXEEXT) t/t-fsys-dir$(EXEEXT) \ + t/t-fsys-hash$(EXEEXT) t/t-trigger$(EXEEXT) \ + t/t-mod-db$(EXEEXT) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(devlibdir)" "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(pkgincludedir)" +LTLIBRARIES = $(devlib_LTLIBRARIES) +am__DEPENDENCIES_1 = +@BUILD_SHARED_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ +@BUILD_SHARED_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@BUILD_SHARED_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +libdpkg_la_DEPENDENCIES = ../compat/libcompat.la $(am__DEPENDENCIES_2) +am_libdpkg_la_OBJECTS = ar.lo arch.lo atomic-file.lo buffer.lo \ + c-ctype.lo cleanup.lo color.lo command.lo compress.lo dbdir.lo \ + dbmodify.lo db-ctrl-access.lo db-ctrl-format.lo \ + db-ctrl-upgrade.lo db-fsys-digest.lo db-fsys-divert.lo \ + db-fsys-files.lo db-fsys-override.lo deb-version.lo debug.lo \ + depcon.lo dir.lo dump.lo ehandle.lo error.lo fdio.lo file.lo \ + fields.lo fsys-dir.lo fsys-iter.lo fsys-hash.lo glob.lo \ + i18n.lo log.lo meminfo.lo mustlib.lo namevalue.lo nfmalloc.lo \ + options.lo options-dirs.lo options-parsers.lo pager.lo \ + parse.lo parsehelp.lo path.lo path-remove.lo pkg.lo \ + pkg-array.lo pkg-files.lo pkg-format.lo pkg-hash.lo \ + pkg-list.lo pkg-namevalue.lo pkg-queue.lo pkg-show.lo \ + pkg-spec.lo progname.lo program.lo progress.lo report.lo \ + string.lo strhash.lo strwide.lo subproc.lo tarfn.lo \ + treewalk.lo trigname.lo trignote.lo triglib.lo trigdeferred.lo \ + utils.lo varbuf.lo version.lo +libdpkg_la_OBJECTS = $(am_libdpkg_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdpkg_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libdpkg_la_LDFLAGS) $(LDFLAGS) -o $@ +t_b_fsys_hash_SOURCES = t/b-fsys-hash.c +am__dirstamp = $(am__leading_dot)dirstamp +t_b_fsys_hash_OBJECTS = t/b-fsys-hash.$(OBJEXT) +am__DEPENDENCIES_3 = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_3) +t_b_fsys_hash_DEPENDENCIES = $(am__DEPENDENCIES_4) +t_b_pkg_hash_SOURCES = t/b-pkg-hash.c +t_b_pkg_hash_OBJECTS = t/b-pkg-hash.$(OBJEXT) +t_b_pkg_hash_DEPENDENCIES = $(am__DEPENDENCIES_4) +t_c_tarextract_SOURCES = t/c-tarextract.c +t_c_tarextract_OBJECTS = t/c-tarextract.$(OBJEXT) +t_c_tarextract_LDADD = $(LDADD) +t_c_tarextract_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_c_treewalk_SOURCES = t/c-treewalk.c +t_c_treewalk_OBJECTS = t/c-treewalk.$(OBJEXT) +t_c_treewalk_LDADD = $(LDADD) +t_c_treewalk_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_c_trigdeferred_SOURCES = t/c-trigdeferred.c +t_c_trigdeferred_OBJECTS = t/c-trigdeferred.$(OBJEXT) +t_c_trigdeferred_LDADD = $(LDADD) +t_c_trigdeferred_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_ar_SOURCES = t/t-ar.c +t_t_ar_OBJECTS = t/t-ar.$(OBJEXT) +t_t_ar_LDADD = $(LDADD) +t_t_ar_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_arch_SOURCES = t/t-arch.c +t_t_arch_OBJECTS = t/t-arch.$(OBJEXT) +t_t_arch_LDADD = $(LDADD) +t_t_arch_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_buffer_SOURCES = t/t-buffer.c +t_t_buffer_OBJECTS = t/t-buffer.$(OBJEXT) +t_t_buffer_LDADD = $(LDADD) +t_t_buffer_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_c_ctype_SOURCES = t/t-c-ctype.c +t_t_c_ctype_OBJECTS = t/t-c-ctype.$(OBJEXT) +t_t_c_ctype_LDADD = $(LDADD) +t_t_c_ctype_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_command_SOURCES = t/t-command.c +t_t_command_OBJECTS = t/t-command.$(OBJEXT) +t_t_command_LDADD = $(LDADD) +t_t_command_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_deb_version_SOURCES = t/t-deb-version.c +t_t_deb_version_OBJECTS = t/t-deb-version.$(OBJEXT) +t_t_deb_version_LDADD = $(LDADD) +t_t_deb_version_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_ehandle_SOURCES = t/t-ehandle.c +t_t_ehandle_OBJECTS = t/t-ehandle.$(OBJEXT) +t_t_ehandle_LDADD = $(LDADD) +t_t_ehandle_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_error_SOURCES = t/t-error.c +t_t_error_OBJECTS = t/t-error.$(OBJEXT) +t_t_error_LDADD = $(LDADD) +t_t_error_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_file_SOURCES = t/t-file.c +t_t_file_OBJECTS = t/t-file.$(OBJEXT) +t_t_file_LDADD = $(LDADD) +t_t_file_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_fsys_dir_SOURCES = t/t-fsys-dir.c +t_t_fsys_dir_OBJECTS = t/t-fsys-dir.$(OBJEXT) +t_t_fsys_dir_LDADD = $(LDADD) +t_t_fsys_dir_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_fsys_hash_SOURCES = t/t-fsys-hash.c +t_t_fsys_hash_OBJECTS = t/t-fsys-hash.$(OBJEXT) +t_t_fsys_hash_LDADD = $(LDADD) +t_t_fsys_hash_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +am_t_t_headers_cpp_OBJECTS = t/t-headers-cpp.$(OBJEXT) +t_t_headers_cpp_OBJECTS = $(am_t_t_headers_cpp_OBJECTS) +t_t_headers_cpp_LDADD = $(LDADD) +t_t_headers_cpp_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_macros_SOURCES = t/t-macros.c +t_t_macros_OBJECTS = t/t-macros.$(OBJEXT) +t_t_macros_LDADD = $(LDADD) +t_t_macros_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_meminfo_SOURCES = t/t-meminfo.c +t_t_meminfo_OBJECTS = t/t-meminfo.$(OBJEXT) +t_t_meminfo_LDADD = $(LDADD) +t_t_meminfo_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_mod_db_SOURCES = t/t-mod-db.c +t_t_mod_db_OBJECTS = t/t-mod-db.$(OBJEXT) +t_t_mod_db_LDADD = $(LDADD) +t_t_mod_db_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_namevalue_SOURCES = t/t-namevalue.c +t_t_namevalue_OBJECTS = t/t-namevalue.$(OBJEXT) +t_t_namevalue_LDADD = $(LDADD) +t_t_namevalue_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pager_SOURCES = t/t-pager.c +t_t_pager_OBJECTS = t/t-pager.$(OBJEXT) +t_t_pager_LDADD = $(LDADD) +t_t_pager_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_path_SOURCES = t/t-path.c +t_t_path_OBJECTS = t/t-path.$(OBJEXT) +t_t_path_LDADD = $(LDADD) +t_t_path_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkg_format_SOURCES = t/t-pkg-format.c +t_t_pkg_format_OBJECTS = t/t-pkg-format.$(OBJEXT) +t_t_pkg_format_LDADD = $(LDADD) +t_t_pkg_format_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkg_hash_SOURCES = t/t-pkg-hash.c +t_t_pkg_hash_OBJECTS = t/t-pkg-hash.$(OBJEXT) +t_t_pkg_hash_LDADD = $(LDADD) +t_t_pkg_hash_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkg_list_SOURCES = t/t-pkg-list.c +t_t_pkg_list_OBJECTS = t/t-pkg-list.$(OBJEXT) +t_t_pkg_list_LDADD = $(LDADD) +t_t_pkg_list_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkg_queue_SOURCES = t/t-pkg-queue.c +t_t_pkg_queue_OBJECTS = t/t-pkg-queue.$(OBJEXT) +t_t_pkg_queue_LDADD = $(LDADD) +t_t_pkg_queue_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkg_show_SOURCES = t/t-pkg-show.c +t_t_pkg_show_OBJECTS = t/t-pkg-show.$(OBJEXT) +t_t_pkg_show_LDADD = $(LDADD) +t_t_pkg_show_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_pkginfo_SOURCES = t/t-pkginfo.c +t_t_pkginfo_OBJECTS = t/t-pkginfo.$(OBJEXT) +t_t_pkginfo_LDADD = $(LDADD) +t_t_pkginfo_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_progname_SOURCES = t/t-progname.c +t_t_progname_OBJECTS = t/t-progname.$(OBJEXT) +t_t_progname_LDADD = $(LDADD) +t_t_progname_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_string_SOURCES = t/t-string.c +t_t_string_OBJECTS = t/t-string.$(OBJEXT) +t_t_string_LDADD = $(LDADD) +t_t_string_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_subproc_SOURCES = t/t-subproc.c +t_t_subproc_OBJECTS = t/t-subproc.$(OBJEXT) +t_t_subproc_LDADD = $(LDADD) +t_t_subproc_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_tar_SOURCES = t/t-tar.c +t_t_tar_OBJECTS = t/t-tar.$(OBJEXT) +t_t_tar_LDADD = $(LDADD) +t_t_tar_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_test_SOURCES = t/t-test.c +t_t_test_OBJECTS = t/t-test.$(OBJEXT) +t_t_test_LDADD = $(LDADD) +t_t_test_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_test_skip_SOURCES = t/t-test-skip.c +t_t_test_skip_OBJECTS = t/t-test-skip.$(OBJEXT) +t_t_test_skip_LDADD = $(LDADD) +t_t_test_skip_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_trigger_SOURCES = t/t-trigger.c +t_t_trigger_OBJECTS = t/t-trigger.$(OBJEXT) +t_t_trigger_LDADD = $(LDADD) +t_t_trigger_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_varbuf_SOURCES = t/t-varbuf.c +t_t_varbuf_OBJECTS = t/t-varbuf.$(OBJEXT) +t_t_varbuf_LDADD = $(LDADD) +t_t_varbuf_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +t_t_version_SOURCES = t/t-version.c +t_t_version_OBJECTS = t/t-version.$(OBJEXT) +t_t_version_LDADD = $(LDADD) +t_t_version_DEPENDENCIES = libdpkg.la $(am__DEPENDENCIES_1) \ + ../compat/libcompat.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/ar.Plo ./$(DEPDIR)/arch.Plo \ + ./$(DEPDIR)/atomic-file.Plo ./$(DEPDIR)/buffer.Plo \ + ./$(DEPDIR)/c-ctype.Plo ./$(DEPDIR)/cleanup.Plo \ + ./$(DEPDIR)/color.Plo ./$(DEPDIR)/command.Plo \ + ./$(DEPDIR)/compress.Plo ./$(DEPDIR)/db-ctrl-access.Plo \ + ./$(DEPDIR)/db-ctrl-format.Plo ./$(DEPDIR)/db-ctrl-upgrade.Plo \ + ./$(DEPDIR)/db-fsys-digest.Plo ./$(DEPDIR)/db-fsys-divert.Plo \ + ./$(DEPDIR)/db-fsys-files.Plo ./$(DEPDIR)/db-fsys-override.Plo \ + ./$(DEPDIR)/dbdir.Plo ./$(DEPDIR)/dbmodify.Plo \ + ./$(DEPDIR)/deb-version.Plo ./$(DEPDIR)/debug.Plo \ + ./$(DEPDIR)/depcon.Plo ./$(DEPDIR)/dir.Plo \ + ./$(DEPDIR)/dump.Plo ./$(DEPDIR)/ehandle.Plo \ + ./$(DEPDIR)/error.Plo ./$(DEPDIR)/fdio.Plo \ + ./$(DEPDIR)/fields.Plo ./$(DEPDIR)/file.Plo \ + ./$(DEPDIR)/fsys-dir.Plo ./$(DEPDIR)/fsys-hash.Plo \ + ./$(DEPDIR)/fsys-iter.Plo ./$(DEPDIR)/glob.Plo \ + ./$(DEPDIR)/i18n.Plo ./$(DEPDIR)/log.Plo \ + ./$(DEPDIR)/meminfo.Plo ./$(DEPDIR)/mustlib.Plo \ + ./$(DEPDIR)/namevalue.Plo ./$(DEPDIR)/nfmalloc.Plo \ + ./$(DEPDIR)/options-dirs.Plo ./$(DEPDIR)/options-parsers.Plo \ + ./$(DEPDIR)/options.Plo ./$(DEPDIR)/pager.Plo \ + ./$(DEPDIR)/parse.Plo ./$(DEPDIR)/parsehelp.Plo \ + ./$(DEPDIR)/path-remove.Plo ./$(DEPDIR)/path.Plo \ + ./$(DEPDIR)/pkg-array.Plo ./$(DEPDIR)/pkg-files.Plo \ + ./$(DEPDIR)/pkg-format.Plo ./$(DEPDIR)/pkg-hash.Plo \ + ./$(DEPDIR)/pkg-list.Plo ./$(DEPDIR)/pkg-namevalue.Plo \ + ./$(DEPDIR)/pkg-queue.Plo ./$(DEPDIR)/pkg-show.Plo \ + ./$(DEPDIR)/pkg-spec.Plo ./$(DEPDIR)/pkg.Plo \ + ./$(DEPDIR)/progname.Plo ./$(DEPDIR)/program.Plo \ + ./$(DEPDIR)/progress.Plo ./$(DEPDIR)/report.Plo \ + ./$(DEPDIR)/strhash.Plo ./$(DEPDIR)/string.Plo \ + ./$(DEPDIR)/strwide.Plo ./$(DEPDIR)/subproc.Plo \ + ./$(DEPDIR)/tarfn.Plo ./$(DEPDIR)/treewalk.Plo \ + ./$(DEPDIR)/trigdeferred.Plo ./$(DEPDIR)/triglib.Plo \ + ./$(DEPDIR)/trigname.Plo ./$(DEPDIR)/trignote.Plo \ + ./$(DEPDIR)/utils.Plo ./$(DEPDIR)/varbuf.Plo \ + ./$(DEPDIR)/version.Plo t/$(DEPDIR)/b-fsys-hash.Po \ + t/$(DEPDIR)/b-pkg-hash.Po t/$(DEPDIR)/c-tarextract.Po \ + t/$(DEPDIR)/c-treewalk.Po t/$(DEPDIR)/c-trigdeferred.Po \ + t/$(DEPDIR)/t-ar.Po t/$(DEPDIR)/t-arch.Po \ + t/$(DEPDIR)/t-buffer.Po t/$(DEPDIR)/t-c-ctype.Po \ + t/$(DEPDIR)/t-command.Po t/$(DEPDIR)/t-deb-version.Po \ + t/$(DEPDIR)/t-ehandle.Po t/$(DEPDIR)/t-error.Po \ + t/$(DEPDIR)/t-file.Po t/$(DEPDIR)/t-fsys-dir.Po \ + t/$(DEPDIR)/t-fsys-hash.Po t/$(DEPDIR)/t-headers-cpp.Po \ + t/$(DEPDIR)/t-macros.Po t/$(DEPDIR)/t-meminfo.Po \ + t/$(DEPDIR)/t-mod-db.Po t/$(DEPDIR)/t-namevalue.Po \ + t/$(DEPDIR)/t-pager.Po t/$(DEPDIR)/t-path.Po \ + t/$(DEPDIR)/t-pkg-format.Po t/$(DEPDIR)/t-pkg-hash.Po \ + t/$(DEPDIR)/t-pkg-list.Po t/$(DEPDIR)/t-pkg-queue.Po \ + t/$(DEPDIR)/t-pkg-show.Po t/$(DEPDIR)/t-pkginfo.Po \ + t/$(DEPDIR)/t-progname.Po t/$(DEPDIR)/t-string.Po \ + t/$(DEPDIR)/t-subproc.Po t/$(DEPDIR)/t-tar.Po \ + t/$(DEPDIR)/t-test-skip.Po t/$(DEPDIR)/t-test.Po \ + t/$(DEPDIR)/t-trigger.Po t/$(DEPDIR)/t-varbuf.Po \ + t/$(DEPDIR)/t-version.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libdpkg_la_SOURCES) t/b-fsys-hash.c t/b-pkg-hash.c \ + t/c-tarextract.c t/c-treewalk.c t/c-trigdeferred.c t/t-ar.c \ + t/t-arch.c t/t-buffer.c t/t-c-ctype.c t/t-command.c \ + t/t-deb-version.c t/t-ehandle.c t/t-error.c t/t-file.c \ + t/t-fsys-dir.c t/t-fsys-hash.c $(t_t_headers_cpp_SOURCES) \ + t/t-macros.c t/t-meminfo.c t/t-mod-db.c t/t-namevalue.c \ + t/t-pager.c t/t-path.c t/t-pkg-format.c t/t-pkg-hash.c \ + t/t-pkg-list.c t/t-pkg-queue.c t/t-pkg-show.c t/t-pkginfo.c \ + t/t-progname.c t/t-string.c t/t-subproc.c t/t-tar.c t/t-test.c \ + t/t-test-skip.c t/t-trigger.c t/t-varbuf.c t/t-version.c +DIST_SOURCES = $(libdpkg_la_SOURCES) t/b-fsys-hash.c t/b-pkg-hash.c \ + t/c-tarextract.c t/c-treewalk.c t/c-trigdeferred.c t/t-ar.c \ + t/t-arch.c t/t-buffer.c t/t-c-ctype.c t/t-command.c \ + t/t-deb-version.c t/t-ehandle.c t/t-error.c t/t-file.c \ + t/t-fsys-dir.c t/t-fsys-hash.c $(t_t_headers_cpp_SOURCES) \ + t/t-macros.c t/t-meminfo.c t/t-mod-db.c t/t-namevalue.c \ + t/t-pager.c t/t-path.c t/t-pkg-format.c t/t-pkg-hash.c \ + t/t-pkg-list.c t/t-pkg-queue.c t/t-pkg-show.c t/t-pkginfo.c \ + t/t-progname.c t/t-string.c t/t-subproc.c t/t-tar.c t/t-test.c \ + t/t-test-skip.c t/t-trigger.c t/t-varbuf.c t/t-version.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libdpkg.pc.in \ + $(top_srcdir)/build-aux/depcomp $(top_srcdir)/build-aux/tap.am +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_DEVEL_DOCS = @BUILD_DEVEL_DOCS@ +BZ2_LIBS = @BZ2_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURSES_LIBS = @CURSES_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEB_DEFAULT_COMPRESSOR = @DEB_DEFAULT_COMPRESSOR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DPKG_DEFAULT_PAGER = @DPKG_DEFAULT_PAGER@ +DPKG_DEFAULT_SHELL = @DPKG_DEFAULT_SHELL@ +DPKG_PAGER = @DPKG_PAGER@ +DPKG_SHELL = @DPKG_SHELL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GCOV = @GCOV@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_DOT = @HAVE_DOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KVM_LIBS = @KVM_LIBS@ +LCOV = @LCOV@ +LCOV_GENHTML = @LCOV_GENHTML@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZMA_LIBS = @LZMA_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MD_LIBS = @MD_LIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_BUG_WEB = @PACKAGE_BUG_WEB@ +PACKAGE_COPYRIGHT_HOLDER = @PACKAGE_COPYRIGHT_HOLDER@ +PACKAGE_CPAN_NAME = @PACKAGE_CPAN_NAME@ +PACKAGE_DIST_IS_RELEASE = @PACKAGE_DIST_IS_RELEASE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_RELEASE_DATE = @PACKAGE_RELEASE_DATE@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VCS_TYPE = @PACKAGE_VCS_TYPE@ +PACKAGE_VCS_URL = @PACKAGE_VCS_URL@ +PACKAGE_VCS_WEB = @PACKAGE_VCS_WEB@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATCH = @PATCH@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PERL_COVER = @PERL_COVER@ +PERL_COVERAGE = @PERL_COVERAGE@ +PERL_LIBDIR = @PERL_LIBDIR@ +PERL_MIN_VERSION = @PERL_MIN_VERSION@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PO4A = @PO4A@ +POD2MAN = @POD2MAN@ +POSUB = @POSUB@ +PS_LIBS = @PS_LIBS@ +RANLIB = @RANLIB@ +RT_LIBS = @RT_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKET_LIBS = @SOCKET_LIBS@ +STRIP = @STRIP@ +TAR = @TAR@ +USE_NLS = @USE_NLS@ +USE_PO4A = @USE_PO4A@ +USE_UNICODE = @USE_UNICODE@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZSTD_LIBS = @ZSTD_LIBS@ +Z_LIBS = @Z_LIBS@ +Z_NG_LIBS = @Z_NG_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +admindir = @admindir@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +backupsdir = @backupsdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devlibdir = @devlibdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +logdir = @logdir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfdir = @pkgconfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +zshcompletionsdir = @zshcompletionsdir@ +AM_CPPFLAGS = \ + -DLOCALEDIR=\"$(localedir)\" \ + -DCONFIGDIR=\"$(pkgconfdir)\" \ + -DADMINDIR=\"$(admindir)\" \ + -DDEFAULT_TEXT_DOMAIN=\"@PACKAGE@\" \ + -idirafter $(top_srcdir)/lib/compat \ + -I$(top_builddir) \ + -I$(top_srcdir)/lib \ + # EOL + + +# Needed for the test suite +LDADD = \ + libdpkg.la \ + $(LIBINTL) \ + ../compat/libcompat.la \ + # EOL + +DISTCLEANFILES = libdpkg.sym # EOL +EXTRA_DIST = \ + $(test_scripts) \ + $(test_data) \ + libdpkg.map \ + libdpkg.pc.in \ + # EOL + +pkgconfigdir = $(devlibdir)/pkgconfig +pkgconfig_DATA = libdpkg.pc +devlib_LTLIBRARIES = libdpkg.la +EXTRA_libdpkg_la_DEPENDENCIES = libdpkg.map $(am__append_3) +libdpkg_la_LDFLAGS = -no-undefined $(am__append_1) $(am__append_2) \ + $(MD_LIBS) +libdpkg_la_LIBADD = ../compat/libcompat.la $(am__append_4) +libdpkg_la_SOURCES = \ + dlist.h \ + ar.c \ + arch.c \ + atomic-file.c \ + buffer.c \ + c-ctype.c \ + cleanup.c \ + color.c \ + command.c \ + compress.c \ + dbdir.c \ + dbmodify.c \ + db-ctrl-access.c \ + db-ctrl-format.c \ + db-ctrl-upgrade.c \ + db-fsys-digest.c \ + db-fsys-divert.c \ + db-fsys-files.c \ + db-fsys-override.c \ + deb-version.c \ + debug.c \ + depcon.c \ + dir.c \ + dump.c \ + ehandle.c \ + error.c \ + fdio.c \ + file.c \ + fields.c \ + fsys-dir.c\ + fsys-iter.c \ + fsys-hash.c \ + glob.c \ + i18n.c i18n.h \ + log.c \ + meminfo.c \ + mustlib.c \ + namevalue.c \ + nfmalloc.c \ + options.c \ + options-dirs.c \ + options-parsers.c \ + pager.c \ + parse.c \ + parsehelp.c \ + path.c \ + path-remove.c \ + perf.h \ + pkg.c \ + pkg-array.c \ + pkg-files.c \ + pkg-format.c \ + pkg-hash.c \ + pkg-list.c \ + pkg-namevalue.c \ + pkg-queue.c \ + pkg-show.c \ + pkg-spec.c \ + progname.c \ + program.c \ + progress.c \ + report.c \ + string.c \ + strhash.c \ + strwide.c \ + subproc.c \ + tarfn.c \ + test.h \ + treewalk.c \ + trigname.c \ + trignote.c \ + triglib.c \ + trigdeferred.c \ + utils.c \ + varbuf.c \ + version.c \ + # EOL + +pkginclude_HEADERS = \ + ar.h \ + arch.h \ + atomic-file.h \ + buffer.h \ + c-ctype.h \ + color.h \ + command.h \ + compress.h \ + db-ctrl.h \ + db-fsys.h \ + deb-version.h \ + debug.h \ + dir.h \ + dpkg.h \ + dpkg-db.h \ + ehandle.h \ + error.h \ + fdio.h \ + file.h \ + fsys.h \ + glob.h \ + macros.h \ + meminfo.h \ + namevalue.h \ + options.h \ + pager.h \ + parsedump.h \ + path.h \ + pkg.h \ + pkg-array.h \ + pkg-files.h \ + pkg-format.h \ + pkg-list.h \ + pkg-queue.h \ + pkg-show.h \ + pkg-spec.h \ + progname.h \ + program.h \ + progress.h \ + report.h \ + string.h \ + subproc.h \ + tarfn.h \ + treewalk.h \ + trigdeferred.h \ + triglib.h \ + varbuf.h \ + version.h \ + # EOL + +t_t_headers_cpp_SOURCES = \ + t/t-headers-cpp.cc \ + # EOL + + +# The tests are sorted in order of increasing complexity. +test_programs = \ + t/t-test \ + t/t-test-skip \ + t/t-macros \ + t/t-headers-cpp \ + t/t-c-ctype \ + t/t-namevalue \ + t/t-ehandle \ + t/t-error \ + t/t-string \ + t/t-file \ + t/t-buffer \ + t/t-meminfo \ + t/t-path \ + t/t-progname \ + t/t-subproc \ + t/t-command \ + t/t-pager \ + t/t-varbuf \ + t/t-ar \ + t/t-tar \ + t/t-deb-version \ + t/t-arch \ + t/t-version \ + t/t-pkginfo \ + t/t-pkg-list \ + t/t-pkg-queue \ + t/t-pkg-hash \ + t/t-pkg-show \ + t/t-pkg-format \ + t/t-fsys-dir \ + t/t-fsys-hash \ + t/t-trigger \ + t/t-mod-db \ + # EOL + +test_scripts = \ + t/t-tarextract.t \ + t/t-treewalk.t \ + t/t-trigdeferred.t \ + # EOL + +test_data = \ + t/data/command/path-a/cmd \ + t/data/command/path-a/cmd-a \ + t/data/command/path-b/cmd \ + t/data/command/path-b/cmd-b \ + t/data/command/path-noexec/cmd \ + t/data/command/path-noexec/cmd-noexec \ + t/data/meminfo-no-data \ + t/data/meminfo-no-info \ + t/data/meminfo-no-unit \ + t/data/meminfo-ok \ + # EOL + +BENCHMARK_LDADD_FLAGS = \ + $(RT_LIBS) \ + $(LDADD) \ + # EOL + +t_b_fsys_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) +t_b_pkg_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) +TEST_ENV_VARS = \ + DPKG_PROGTAR=$(TAR) \ + # EOL + +test_tmpdir = t.tmp +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build-aux/tap.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/dpkg/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/dpkg/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/build-aux/tap.am $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libdpkg.pc: $(top_builddir)/config.status $(srcdir)/libdpkg.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-devlibLTLIBRARIES: $(devlib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(devlib_LTLIBRARIES)'; test -n "$(devlibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(devlibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(devlibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(devlibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(devlibdir)"; \ + } + +uninstall-devlibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(devlib_LTLIBRARIES)'; test -n "$(devlibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(devlibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(devlibdir)/$$f"; \ + done + +clean-devlibLTLIBRARIES: + -test -z "$(devlib_LTLIBRARIES)" || rm -f $(devlib_LTLIBRARIES) + @list='$(devlib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdpkg.la: $(libdpkg_la_OBJECTS) $(libdpkg_la_DEPENDENCIES) $(EXTRA_libdpkg_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdpkg_la_LINK) -rpath $(devlibdir) $(libdpkg_la_OBJECTS) $(libdpkg_la_LIBADD) $(LIBS) +t/$(am__dirstamp): + @$(MKDIR_P) t + @: > t/$(am__dirstamp) +t/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) t/$(DEPDIR) + @: > t/$(DEPDIR)/$(am__dirstamp) +t/b-fsys-hash.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/b-fsys-hash$(EXEEXT): $(t_b_fsys_hash_OBJECTS) $(t_b_fsys_hash_DEPENDENCIES) $(EXTRA_t_b_fsys_hash_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/b-fsys-hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_b_fsys_hash_OBJECTS) $(t_b_fsys_hash_LDADD) $(LIBS) +t/b-pkg-hash.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/b-pkg-hash$(EXEEXT): $(t_b_pkg_hash_OBJECTS) $(t_b_pkg_hash_DEPENDENCIES) $(EXTRA_t_b_pkg_hash_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/b-pkg-hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_b_pkg_hash_OBJECTS) $(t_b_pkg_hash_LDADD) $(LIBS) +t/c-tarextract.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/c-tarextract$(EXEEXT): $(t_c_tarextract_OBJECTS) $(t_c_tarextract_DEPENDENCIES) $(EXTRA_t_c_tarextract_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/c-tarextract$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_c_tarextract_OBJECTS) $(t_c_tarextract_LDADD) $(LIBS) +t/c-treewalk.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/c-treewalk$(EXEEXT): $(t_c_treewalk_OBJECTS) $(t_c_treewalk_DEPENDENCIES) $(EXTRA_t_c_treewalk_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/c-treewalk$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_c_treewalk_OBJECTS) $(t_c_treewalk_LDADD) $(LIBS) +t/c-trigdeferred.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/c-trigdeferred$(EXEEXT): $(t_c_trigdeferred_OBJECTS) $(t_c_trigdeferred_DEPENDENCIES) $(EXTRA_t_c_trigdeferred_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/c-trigdeferred$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_c_trigdeferred_OBJECTS) $(t_c_trigdeferred_LDADD) $(LIBS) +t/t-ar.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-ar$(EXEEXT): $(t_t_ar_OBJECTS) $(t_t_ar_DEPENDENCIES) $(EXTRA_t_t_ar_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-ar$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_ar_OBJECTS) $(t_t_ar_LDADD) $(LIBS) +t/t-arch.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-arch$(EXEEXT): $(t_t_arch_OBJECTS) $(t_t_arch_DEPENDENCIES) $(EXTRA_t_t_arch_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-arch$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_arch_OBJECTS) $(t_t_arch_LDADD) $(LIBS) +t/t-buffer.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-buffer$(EXEEXT): $(t_t_buffer_OBJECTS) $(t_t_buffer_DEPENDENCIES) $(EXTRA_t_t_buffer_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-buffer$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_buffer_OBJECTS) $(t_t_buffer_LDADD) $(LIBS) +t/t-c-ctype.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-c-ctype$(EXEEXT): $(t_t_c_ctype_OBJECTS) $(t_t_c_ctype_DEPENDENCIES) $(EXTRA_t_t_c_ctype_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-c-ctype$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_c_ctype_OBJECTS) $(t_t_c_ctype_LDADD) $(LIBS) +t/t-command.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-command$(EXEEXT): $(t_t_command_OBJECTS) $(t_t_command_DEPENDENCIES) $(EXTRA_t_t_command_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-command$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_command_OBJECTS) $(t_t_command_LDADD) $(LIBS) +t/t-deb-version.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/t-deb-version$(EXEEXT): $(t_t_deb_version_OBJECTS) $(t_t_deb_version_DEPENDENCIES) $(EXTRA_t_t_deb_version_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-deb-version$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_deb_version_OBJECTS) $(t_t_deb_version_LDADD) $(LIBS) +t/t-ehandle.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-ehandle$(EXEEXT): $(t_t_ehandle_OBJECTS) $(t_t_ehandle_DEPENDENCIES) $(EXTRA_t_t_ehandle_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-ehandle$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_ehandle_OBJECTS) $(t_t_ehandle_LDADD) $(LIBS) +t/t-error.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-error$(EXEEXT): $(t_t_error_OBJECTS) $(t_t_error_DEPENDENCIES) $(EXTRA_t_t_error_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-error$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_error_OBJECTS) $(t_t_error_LDADD) $(LIBS) +t/t-file.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-file$(EXEEXT): $(t_t_file_OBJECTS) $(t_t_file_DEPENDENCIES) $(EXTRA_t_t_file_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-file$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_file_OBJECTS) $(t_t_file_LDADD) $(LIBS) +t/t-fsys-dir.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-fsys-dir$(EXEEXT): $(t_t_fsys_dir_OBJECTS) $(t_t_fsys_dir_DEPENDENCIES) $(EXTRA_t_t_fsys_dir_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-fsys-dir$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_fsys_dir_OBJECTS) $(t_t_fsys_dir_LDADD) $(LIBS) +t/t-fsys-hash.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-fsys-hash$(EXEEXT): $(t_t_fsys_hash_OBJECTS) $(t_t_fsys_hash_DEPENDENCIES) $(EXTRA_t_t_fsys_hash_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-fsys-hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_fsys_hash_OBJECTS) $(t_t_fsys_hash_LDADD) $(LIBS) +t/t-headers-cpp.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/t-headers-cpp$(EXEEXT): $(t_t_headers_cpp_OBJECTS) $(t_t_headers_cpp_DEPENDENCIES) $(EXTRA_t_t_headers_cpp_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-headers-cpp$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(t_t_headers_cpp_OBJECTS) $(t_t_headers_cpp_LDADD) $(LIBS) +t/t-macros.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-macros$(EXEEXT): $(t_t_macros_OBJECTS) $(t_t_macros_DEPENDENCIES) $(EXTRA_t_t_macros_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-macros$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_macros_OBJECTS) $(t_t_macros_LDADD) $(LIBS) +t/t-meminfo.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-meminfo$(EXEEXT): $(t_t_meminfo_OBJECTS) $(t_t_meminfo_DEPENDENCIES) $(EXTRA_t_t_meminfo_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-meminfo$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_meminfo_OBJECTS) $(t_t_meminfo_LDADD) $(LIBS) +t/t-mod-db.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-mod-db$(EXEEXT): $(t_t_mod_db_OBJECTS) $(t_t_mod_db_DEPENDENCIES) $(EXTRA_t_t_mod_db_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-mod-db$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_mod_db_OBJECTS) $(t_t_mod_db_LDADD) $(LIBS) +t/t-namevalue.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-namevalue$(EXEEXT): $(t_t_namevalue_OBJECTS) $(t_t_namevalue_DEPENDENCIES) $(EXTRA_t_t_namevalue_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-namevalue$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_namevalue_OBJECTS) $(t_t_namevalue_LDADD) $(LIBS) +t/t-pager.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pager$(EXEEXT): $(t_t_pager_OBJECTS) $(t_t_pager_DEPENDENCIES) $(EXTRA_t_t_pager_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pager$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pager_OBJECTS) $(t_t_pager_LDADD) $(LIBS) +t/t-path.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-path$(EXEEXT): $(t_t_path_OBJECTS) $(t_t_path_DEPENDENCIES) $(EXTRA_t_t_path_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-path$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_path_OBJECTS) $(t_t_path_LDADD) $(LIBS) +t/t-pkg-format.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkg-format$(EXEEXT): $(t_t_pkg_format_OBJECTS) $(t_t_pkg_format_DEPENDENCIES) $(EXTRA_t_t_pkg_format_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkg-format$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkg_format_OBJECTS) $(t_t_pkg_format_LDADD) $(LIBS) +t/t-pkg-hash.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkg-hash$(EXEEXT): $(t_t_pkg_hash_OBJECTS) $(t_t_pkg_hash_DEPENDENCIES) $(EXTRA_t_t_pkg_hash_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkg-hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkg_hash_OBJECTS) $(t_t_pkg_hash_LDADD) $(LIBS) +t/t-pkg-list.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkg-list$(EXEEXT): $(t_t_pkg_list_OBJECTS) $(t_t_pkg_list_DEPENDENCIES) $(EXTRA_t_t_pkg_list_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkg-list$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkg_list_OBJECTS) $(t_t_pkg_list_LDADD) $(LIBS) +t/t-pkg-queue.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkg-queue$(EXEEXT): $(t_t_pkg_queue_OBJECTS) $(t_t_pkg_queue_DEPENDENCIES) $(EXTRA_t_t_pkg_queue_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkg-queue$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkg_queue_OBJECTS) $(t_t_pkg_queue_LDADD) $(LIBS) +t/t-pkg-show.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkg-show$(EXEEXT): $(t_t_pkg_show_OBJECTS) $(t_t_pkg_show_DEPENDENCIES) $(EXTRA_t_t_pkg_show_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkg-show$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkg_show_OBJECTS) $(t_t_pkg_show_LDADD) $(LIBS) +t/t-pkginfo.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-pkginfo$(EXEEXT): $(t_t_pkginfo_OBJECTS) $(t_t_pkginfo_DEPENDENCIES) $(EXTRA_t_t_pkginfo_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-pkginfo$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_pkginfo_OBJECTS) $(t_t_pkginfo_LDADD) $(LIBS) +t/t-progname.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-progname$(EXEEXT): $(t_t_progname_OBJECTS) $(t_t_progname_DEPENDENCIES) $(EXTRA_t_t_progname_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-progname$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_progname_OBJECTS) $(t_t_progname_LDADD) $(LIBS) +t/t-string.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-string$(EXEEXT): $(t_t_string_OBJECTS) $(t_t_string_DEPENDENCIES) $(EXTRA_t_t_string_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-string$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_string_OBJECTS) $(t_t_string_LDADD) $(LIBS) +t/t-subproc.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-subproc$(EXEEXT): $(t_t_subproc_OBJECTS) $(t_t_subproc_DEPENDENCIES) $(EXTRA_t_t_subproc_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-subproc$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_subproc_OBJECTS) $(t_t_subproc_LDADD) $(LIBS) +t/t-tar.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-tar$(EXEEXT): $(t_t_tar_OBJECTS) $(t_t_tar_DEPENDENCIES) $(EXTRA_t_t_tar_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-tar$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_tar_OBJECTS) $(t_t_tar_LDADD) $(LIBS) +t/t-test.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-test$(EXEEXT): $(t_t_test_OBJECTS) $(t_t_test_DEPENDENCIES) $(EXTRA_t_t_test_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_test_OBJECTS) $(t_t_test_LDADD) $(LIBS) +t/t-test-skip.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-test-skip$(EXEEXT): $(t_t_test_skip_OBJECTS) $(t_t_test_skip_DEPENDENCIES) $(EXTRA_t_t_test_skip_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-test-skip$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_test_skip_OBJECTS) $(t_t_test_skip_LDADD) $(LIBS) +t/t-trigger.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-trigger$(EXEEXT): $(t_t_trigger_OBJECTS) $(t_t_trigger_DEPENDENCIES) $(EXTRA_t_t_trigger_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-trigger$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_trigger_OBJECTS) $(t_t_trigger_LDADD) $(LIBS) +t/t-varbuf.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-varbuf$(EXEEXT): $(t_t_varbuf_OBJECTS) $(t_t_varbuf_DEPENDENCIES) $(EXTRA_t_t_varbuf_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-varbuf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_varbuf_OBJECTS) $(t_t_varbuf_LDADD) $(LIBS) +t/t-version.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/t-version$(EXEEXT): $(t_t_version_OBJECTS) $(t_t_version_DEPENDENCIES) $(EXTRA_t_t_version_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/t-version$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_t_version_OBJECTS) $(t_t_version_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f t/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ar.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomic-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/c-ctype.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-ctrl-access.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-ctrl-format.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-ctrl-upgrade.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-fsys-digest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-fsys-divert.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-fsys-files.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db-fsys-override.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbdir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbmodify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deb-version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/depcon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ehandle.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fields.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsys-dir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsys-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsys-iter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/glob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meminfo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mustlib.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/namevalue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfmalloc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options-dirs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options-parsers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsehelp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path-remove.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-files.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-format.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-list.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-namevalue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-queue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-show.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg-spec.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/program.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/report.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strhash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strwide.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subproc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tarfn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/treewalk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trigdeferred.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/triglib.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trigname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trignote.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/b-fsys-hash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/b-pkg-hash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/c-tarextract.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/c-treewalk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/c-trigdeferred.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-ar.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-arch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-c-ctype.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-command.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-deb-version.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-ehandle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-error.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-fsys-dir.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-fsys-hash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-headers-cpp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-macros.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-meminfo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-mod-db.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-namevalue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pager.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-path.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkg-format.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkg-hash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkg-list.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkg-queue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkg-show.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-pkginfo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-progname.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-string.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-subproc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-tar.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-test-skip.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-trigger.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-varbuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/t-version.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf t/.libs t/_libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(devlibdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f t/$(DEPDIR)/$(am__dirstamp) + -rm -f t/$(am__dirstamp) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-devlibLTLIBRARIES clean-generic \ + clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ar.Plo + -rm -f ./$(DEPDIR)/arch.Plo + -rm -f ./$(DEPDIR)/atomic-file.Plo + -rm -f ./$(DEPDIR)/buffer.Plo + -rm -f ./$(DEPDIR)/c-ctype.Plo + -rm -f ./$(DEPDIR)/cleanup.Plo + -rm -f ./$(DEPDIR)/color.Plo + -rm -f ./$(DEPDIR)/command.Plo + -rm -f ./$(DEPDIR)/compress.Plo + -rm -f ./$(DEPDIR)/db-ctrl-access.Plo + -rm -f ./$(DEPDIR)/db-ctrl-format.Plo + -rm -f ./$(DEPDIR)/db-ctrl-upgrade.Plo + -rm -f ./$(DEPDIR)/db-fsys-digest.Plo + -rm -f ./$(DEPDIR)/db-fsys-divert.Plo + -rm -f ./$(DEPDIR)/db-fsys-files.Plo + -rm -f ./$(DEPDIR)/db-fsys-override.Plo + -rm -f ./$(DEPDIR)/dbdir.Plo + -rm -f ./$(DEPDIR)/dbmodify.Plo + -rm -f ./$(DEPDIR)/deb-version.Plo + -rm -f ./$(DEPDIR)/debug.Plo + -rm -f ./$(DEPDIR)/depcon.Plo + -rm -f ./$(DEPDIR)/dir.Plo + -rm -f ./$(DEPDIR)/dump.Plo + -rm -f ./$(DEPDIR)/ehandle.Plo + -rm -f ./$(DEPDIR)/error.Plo + -rm -f ./$(DEPDIR)/fdio.Plo + -rm -f ./$(DEPDIR)/fields.Plo + -rm -f ./$(DEPDIR)/file.Plo + -rm -f ./$(DEPDIR)/fsys-dir.Plo + -rm -f ./$(DEPDIR)/fsys-hash.Plo + -rm -f ./$(DEPDIR)/fsys-iter.Plo + -rm -f ./$(DEPDIR)/glob.Plo + -rm -f ./$(DEPDIR)/i18n.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/meminfo.Plo + -rm -f ./$(DEPDIR)/mustlib.Plo + -rm -f ./$(DEPDIR)/namevalue.Plo + -rm -f ./$(DEPDIR)/nfmalloc.Plo + -rm -f ./$(DEPDIR)/options-dirs.Plo + -rm -f ./$(DEPDIR)/options-parsers.Plo + -rm -f ./$(DEPDIR)/options.Plo + -rm -f ./$(DEPDIR)/pager.Plo + -rm -f ./$(DEPDIR)/parse.Plo + -rm -f ./$(DEPDIR)/parsehelp.Plo + -rm -f ./$(DEPDIR)/path-remove.Plo + -rm -f ./$(DEPDIR)/path.Plo + -rm -f ./$(DEPDIR)/pkg-array.Plo + -rm -f ./$(DEPDIR)/pkg-files.Plo + -rm -f ./$(DEPDIR)/pkg-format.Plo + -rm -f ./$(DEPDIR)/pkg-hash.Plo + -rm -f ./$(DEPDIR)/pkg-list.Plo + -rm -f ./$(DEPDIR)/pkg-namevalue.Plo + -rm -f ./$(DEPDIR)/pkg-queue.Plo + -rm -f ./$(DEPDIR)/pkg-show.Plo + -rm -f ./$(DEPDIR)/pkg-spec.Plo + -rm -f ./$(DEPDIR)/pkg.Plo + -rm -f ./$(DEPDIR)/progname.Plo + -rm -f ./$(DEPDIR)/program.Plo + -rm -f ./$(DEPDIR)/progress.Plo + -rm -f ./$(DEPDIR)/report.Plo + -rm -f ./$(DEPDIR)/strhash.Plo + -rm -f ./$(DEPDIR)/string.Plo + -rm -f ./$(DEPDIR)/strwide.Plo + -rm -f ./$(DEPDIR)/subproc.Plo + -rm -f ./$(DEPDIR)/tarfn.Plo + -rm -f ./$(DEPDIR)/treewalk.Plo + -rm -f ./$(DEPDIR)/trigdeferred.Plo + -rm -f ./$(DEPDIR)/triglib.Plo + -rm -f ./$(DEPDIR)/trigname.Plo + -rm -f ./$(DEPDIR)/trignote.Plo + -rm -f ./$(DEPDIR)/utils.Plo + -rm -f ./$(DEPDIR)/varbuf.Plo + -rm -f ./$(DEPDIR)/version.Plo + -rm -f t/$(DEPDIR)/b-fsys-hash.Po + -rm -f t/$(DEPDIR)/b-pkg-hash.Po + -rm -f t/$(DEPDIR)/c-tarextract.Po + -rm -f t/$(DEPDIR)/c-treewalk.Po + -rm -f t/$(DEPDIR)/c-trigdeferred.Po + -rm -f t/$(DEPDIR)/t-ar.Po + -rm -f t/$(DEPDIR)/t-arch.Po + -rm -f t/$(DEPDIR)/t-buffer.Po + -rm -f t/$(DEPDIR)/t-c-ctype.Po + -rm -f t/$(DEPDIR)/t-command.Po + -rm -f t/$(DEPDIR)/t-deb-version.Po + -rm -f t/$(DEPDIR)/t-ehandle.Po + -rm -f t/$(DEPDIR)/t-error.Po + -rm -f t/$(DEPDIR)/t-file.Po + -rm -f t/$(DEPDIR)/t-fsys-dir.Po + -rm -f t/$(DEPDIR)/t-fsys-hash.Po + -rm -f t/$(DEPDIR)/t-headers-cpp.Po + -rm -f t/$(DEPDIR)/t-macros.Po + -rm -f t/$(DEPDIR)/t-meminfo.Po + -rm -f t/$(DEPDIR)/t-mod-db.Po + -rm -f t/$(DEPDIR)/t-namevalue.Po + -rm -f t/$(DEPDIR)/t-pager.Po + -rm -f t/$(DEPDIR)/t-path.Po + -rm -f t/$(DEPDIR)/t-pkg-format.Po + -rm -f t/$(DEPDIR)/t-pkg-hash.Po + -rm -f t/$(DEPDIR)/t-pkg-list.Po + -rm -f t/$(DEPDIR)/t-pkg-queue.Po + -rm -f t/$(DEPDIR)/t-pkg-show.Po + -rm -f t/$(DEPDIR)/t-pkginfo.Po + -rm -f t/$(DEPDIR)/t-progname.Po + -rm -f t/$(DEPDIR)/t-string.Po + -rm -f t/$(DEPDIR)/t-subproc.Po + -rm -f t/$(DEPDIR)/t-tar.Po + -rm -f t/$(DEPDIR)/t-test-skip.Po + -rm -f t/$(DEPDIR)/t-test.Po + -rm -f t/$(DEPDIR)/t-trigger.Po + -rm -f t/$(DEPDIR)/t-varbuf.Po + -rm -f t/$(DEPDIR)/t-version.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-devlibLTLIBRARIES install-pkgconfigDATA \ + install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/ar.Plo + -rm -f ./$(DEPDIR)/arch.Plo + -rm -f ./$(DEPDIR)/atomic-file.Plo + -rm -f ./$(DEPDIR)/buffer.Plo + -rm -f ./$(DEPDIR)/c-ctype.Plo + -rm -f ./$(DEPDIR)/cleanup.Plo + -rm -f ./$(DEPDIR)/color.Plo + -rm -f ./$(DEPDIR)/command.Plo + -rm -f ./$(DEPDIR)/compress.Plo + -rm -f ./$(DEPDIR)/db-ctrl-access.Plo + -rm -f ./$(DEPDIR)/db-ctrl-format.Plo + -rm -f ./$(DEPDIR)/db-ctrl-upgrade.Plo + -rm -f ./$(DEPDIR)/db-fsys-digest.Plo + -rm -f ./$(DEPDIR)/db-fsys-divert.Plo + -rm -f ./$(DEPDIR)/db-fsys-files.Plo + -rm -f ./$(DEPDIR)/db-fsys-override.Plo + -rm -f ./$(DEPDIR)/dbdir.Plo + -rm -f ./$(DEPDIR)/dbmodify.Plo + -rm -f ./$(DEPDIR)/deb-version.Plo + -rm -f ./$(DEPDIR)/debug.Plo + -rm -f ./$(DEPDIR)/depcon.Plo + -rm -f ./$(DEPDIR)/dir.Plo + -rm -f ./$(DEPDIR)/dump.Plo + -rm -f ./$(DEPDIR)/ehandle.Plo + -rm -f ./$(DEPDIR)/error.Plo + -rm -f ./$(DEPDIR)/fdio.Plo + -rm -f ./$(DEPDIR)/fields.Plo + -rm -f ./$(DEPDIR)/file.Plo + -rm -f ./$(DEPDIR)/fsys-dir.Plo + -rm -f ./$(DEPDIR)/fsys-hash.Plo + -rm -f ./$(DEPDIR)/fsys-iter.Plo + -rm -f ./$(DEPDIR)/glob.Plo + -rm -f ./$(DEPDIR)/i18n.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/meminfo.Plo + -rm -f ./$(DEPDIR)/mustlib.Plo + -rm -f ./$(DEPDIR)/namevalue.Plo + -rm -f ./$(DEPDIR)/nfmalloc.Plo + -rm -f ./$(DEPDIR)/options-dirs.Plo + -rm -f ./$(DEPDIR)/options-parsers.Plo + -rm -f ./$(DEPDIR)/options.Plo + -rm -f ./$(DEPDIR)/pager.Plo + -rm -f ./$(DEPDIR)/parse.Plo + -rm -f ./$(DEPDIR)/parsehelp.Plo + -rm -f ./$(DEPDIR)/path-remove.Plo + -rm -f ./$(DEPDIR)/path.Plo + -rm -f ./$(DEPDIR)/pkg-array.Plo + -rm -f ./$(DEPDIR)/pkg-files.Plo + -rm -f ./$(DEPDIR)/pkg-format.Plo + -rm -f ./$(DEPDIR)/pkg-hash.Plo + -rm -f ./$(DEPDIR)/pkg-list.Plo + -rm -f ./$(DEPDIR)/pkg-namevalue.Plo + -rm -f ./$(DEPDIR)/pkg-queue.Plo + -rm -f ./$(DEPDIR)/pkg-show.Plo + -rm -f ./$(DEPDIR)/pkg-spec.Plo + -rm -f ./$(DEPDIR)/pkg.Plo + -rm -f ./$(DEPDIR)/progname.Plo + -rm -f ./$(DEPDIR)/program.Plo + -rm -f ./$(DEPDIR)/progress.Plo + -rm -f ./$(DEPDIR)/report.Plo + -rm -f ./$(DEPDIR)/strhash.Plo + -rm -f ./$(DEPDIR)/string.Plo + -rm -f ./$(DEPDIR)/strwide.Plo + -rm -f ./$(DEPDIR)/subproc.Plo + -rm -f ./$(DEPDIR)/tarfn.Plo + -rm -f ./$(DEPDIR)/treewalk.Plo + -rm -f ./$(DEPDIR)/trigdeferred.Plo + -rm -f ./$(DEPDIR)/triglib.Plo + -rm -f ./$(DEPDIR)/trigname.Plo + -rm -f ./$(DEPDIR)/trignote.Plo + -rm -f ./$(DEPDIR)/utils.Plo + -rm -f ./$(DEPDIR)/varbuf.Plo + -rm -f ./$(DEPDIR)/version.Plo + -rm -f t/$(DEPDIR)/b-fsys-hash.Po + -rm -f t/$(DEPDIR)/b-pkg-hash.Po + -rm -f t/$(DEPDIR)/c-tarextract.Po + -rm -f t/$(DEPDIR)/c-treewalk.Po + -rm -f t/$(DEPDIR)/c-trigdeferred.Po + -rm -f t/$(DEPDIR)/t-ar.Po + -rm -f t/$(DEPDIR)/t-arch.Po + -rm -f t/$(DEPDIR)/t-buffer.Po + -rm -f t/$(DEPDIR)/t-c-ctype.Po + -rm -f t/$(DEPDIR)/t-command.Po + -rm -f t/$(DEPDIR)/t-deb-version.Po + -rm -f t/$(DEPDIR)/t-ehandle.Po + -rm -f t/$(DEPDIR)/t-error.Po + -rm -f t/$(DEPDIR)/t-file.Po + -rm -f t/$(DEPDIR)/t-fsys-dir.Po + -rm -f t/$(DEPDIR)/t-fsys-hash.Po + -rm -f t/$(DEPDIR)/t-headers-cpp.Po + -rm -f t/$(DEPDIR)/t-macros.Po + -rm -f t/$(DEPDIR)/t-meminfo.Po + -rm -f t/$(DEPDIR)/t-mod-db.Po + -rm -f t/$(DEPDIR)/t-namevalue.Po + -rm -f t/$(DEPDIR)/t-pager.Po + -rm -f t/$(DEPDIR)/t-path.Po + -rm -f t/$(DEPDIR)/t-pkg-format.Po + -rm -f t/$(DEPDIR)/t-pkg-hash.Po + -rm -f t/$(DEPDIR)/t-pkg-list.Po + -rm -f t/$(DEPDIR)/t-pkg-queue.Po + -rm -f t/$(DEPDIR)/t-pkg-show.Po + -rm -f t/$(DEPDIR)/t-pkginfo.Po + -rm -f t/$(DEPDIR)/t-progname.Po + -rm -f t/$(DEPDIR)/t-string.Po + -rm -f t/$(DEPDIR)/t-subproc.Po + -rm -f t/$(DEPDIR)/t-tar.Po + -rm -f t/$(DEPDIR)/t-test-skip.Po + -rm -f t/$(DEPDIR)/t-test.Po + -rm -f t/$(DEPDIR)/t-trigger.Po + -rm -f t/$(DEPDIR)/t-varbuf.Po + -rm -f t/$(DEPDIR)/t-version.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-devlibLTLIBRARIES uninstall-pkgconfigDATA \ + uninstall-pkgincludeHEADERS + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \ + check-local clean clean-checkPROGRAMS clean-devlibLTLIBRARIES \ + clean-generic clean-libtool clean-local cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-devlibLTLIBRARIES install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkgconfigDATA \ + install-pkgincludeHEADERS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-devlibLTLIBRARIES uninstall-pkgconfigDATA \ + uninstall-pkgincludeHEADERS + +.PRECIOUS: Makefile + + +# Generate a simple libtool symbol export list to be used as a fallback if +# there is no version script support. +libdpkg.sym: libdpkg.map + $(AM_V_GEN) $(SED) -n \ + -e 's/#.*$$//g' \ + -e 's/^[[:space:]]\{1,\}\([A-Za-z0-9_]\{1,\}\);/\1/p' \ + $(srcdir)/libdpkg.map \ + >$@ + +TEST_VERBOSE ?= 0 +TEST_PARALLEL ?= 1 + +tap-clean: + [ -z "$(test_tmpdir)" ] || rm -fr $(test_tmpdir) + +tap-check: $(test_data) $(test_programs) $(test_scripts) + [ -z "$(test_tmpdir)" ] || $(MKDIR_P) $(test_tmpdir) + $(TEST_ENV_VARS) \ + abs_top_srcdir=$(abs_top_srcdir) \ + abs_top_builddir=$(abs_top_builddir) \ + srcdir=$(srcdir) builddir=$(builddir) \ + CC=$(CC) \ + SHELL=$(SHELL) \ + PERL=$(PERL) \ + PERL_DL_NONLAZY=1 \ + PERL5OPT=$(TEST_COVERAGE) \ + $(PERL) $(top_srcdir)/build-aux/test-runner \ + $(addprefix $(builddir)/,$(test_programs)) \ + $(addprefix $(srcdir)/,$(test_scripts)) + +authorcheck: + AUTHOR_TESTING=1 $(MAKE) check + +check-local: tap-check + +clean-local: tap-clean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/dpkg/ar.c b/lib/dpkg/ar.c new file mode 100644 index 0000000..6eea5ad --- /dev/null +++ b/lib/dpkg/ar.c @@ -0,0 +1,234 @@ +/* + * libdpkg - Debian packaging suite library routines + * ar.c - primitives for ar handling + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <time.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> +#include <dpkg/ar.h> + +struct dpkg_ar * +dpkg_ar_fdopen(const char *filename, int fd) +{ + struct dpkg_ar *ar; + struct stat st; + + if (fstat(fd, &st) != 0) + ohshite(_("failed to fstat archive")); + + ar = m_malloc(sizeof(*ar)); + ar->name = filename; + ar->mode = st.st_mode; + ar->size = st.st_size; + ar->time = st.st_mtime; + ar->fd = fd; + + return ar; +} + +struct dpkg_ar * +dpkg_ar_open(const char *filename) +{ + int fd; + + if (strcmp(filename, "-") == 0) + fd = STDIN_FILENO; + else + fd = open(filename, O_RDONLY); + if (fd < 0) + ohshite(_("failed to read archive '%.255s'"), filename); + + return dpkg_ar_fdopen(filename, fd); +} + +struct dpkg_ar * +dpkg_ar_create(const char *filename, mode_t mode) +{ + int fd; + + fd = creat(filename, mode); + if (fd < 0) + ohshite(_("unable to create '%.255s'"), filename); + + return dpkg_ar_fdopen(filename, fd); +} + +void +dpkg_ar_set_mtime(struct dpkg_ar *ar, intmax_t mtime) +{ + ar->time = mtime; +} + +void +dpkg_ar_close(struct dpkg_ar *ar) +{ + if (close(ar->fd)) + ohshite(_("unable to close file '%s'"), ar->name); + free(ar); +} + +static void +dpkg_ar_member_init(struct dpkg_ar *ar, struct dpkg_ar_member *member, + const char *name, off_t size) +{ + member->name = name; + member->size = size; + member->time = ar->time; + member->mode = 0100644; + member->uid = 0; + member->gid = 0; +} + +void +dpkg_ar_normalize_name(struct dpkg_ar_hdr *arh) +{ + char *name = arh->ar_name; + int i; + + /* Remove trailing spaces from the member name. */ + for (i = sizeof(arh->ar_name) - 1; i >= 0 && name[i] == ' '; i--) + name[i] = '\0'; + + /* Remove optional slash terminator (on GNU-style archives). */ + if (i >= 0 && name[i] == '/') + name[i] = '\0'; +} + +off_t +dpkg_ar_member_get_size(struct dpkg_ar *ar, struct dpkg_ar_hdr *arh) +{ + const char *str = arh->ar_size; + int len = sizeof(arh->ar_size); + off_t size = 0; + + while (len && *str == ' ') + str++, len--; + + while (len--) { + if (*str == ' ') + break; + if (*str < '0' || *str > '9') + ohshit(_("invalid character '%c' in archive '%.250s' " + "member '%.16s' size"), + *str, ar->name, arh->ar_name); + + size *= 10; + size += *str++ - '0'; + } + + return size; +} + +bool +dpkg_ar_member_is_illegal(struct dpkg_ar_hdr *arh) +{ + return memcmp(arh->ar_fmag, DPKG_AR_FMAG, sizeof(arh->ar_fmag)) != 0; +} + +void +dpkg_ar_put_magic(struct dpkg_ar *ar) +{ + if (fd_write(ar->fd, DPKG_AR_MAGIC, strlen(DPKG_AR_MAGIC)) < 0) + ohshite(_("unable to write file '%s'"), ar->name); +} + +void +dpkg_ar_member_put_header(struct dpkg_ar *ar, struct dpkg_ar_member *member) +{ + char header[sizeof(struct dpkg_ar_hdr) + 1]; + int n; + + if (strlen(member->name) > 15) + ohshit(_("ar member name '%s' length too long"), member->name); + if (member->size > 9999999999L) + ohshit(_("ar member size %jd too large"), (intmax_t)member->size); + if (member->time > 999999999999L) + ohshit(_("ar member time %jd too large"), (intmax_t)member->time); + + n = snprintf(header, sizeof(struct dpkg_ar_hdr) + 1, + "%-16s%-12jd%-6lu%-6lu%-8lo%-10jd`\n", + member->name, (intmax_t)member->time, + (unsigned long)member->uid, (unsigned long)member->gid, + (unsigned long)member->mode, (intmax_t)member->size); + if (n != sizeof(struct dpkg_ar_hdr)) + ohshit(_("generated corrupt ar header for '%s'"), ar->name); + + if (fd_write(ar->fd, header, n) < 0) + ohshite(_("unable to write file '%s'"), ar->name); +} + +void +dpkg_ar_member_put_mem(struct dpkg_ar *ar, + const char *name, const void *data, size_t size) +{ + struct dpkg_ar_member member; + + dpkg_ar_member_init(ar, &member, name, size); + dpkg_ar_member_put_header(ar, &member); + + /* Copy data contents. */ + if (fd_write(ar->fd, data, size) < 0) + ohshite(_("unable to write file '%s'"), ar->name); + + if (size & 1) + if (fd_write(ar->fd, "\n", 1) < 0) + ohshite(_("unable to write file '%s'"), ar->name); +} + +void +dpkg_ar_member_put_file(struct dpkg_ar *ar, + const char *name, int fd, off_t size) +{ + struct dpkg_error err; + struct dpkg_ar_member member; + + if (size <= 0) { + struct stat st; + + if (fstat(fd, &st)) + ohshite(_("failed to fstat ar member file (%s)"), name); + size = st.st_size; + } + + dpkg_ar_member_init(ar, &member, name, size); + dpkg_ar_member_put_header(ar, &member); + + /* Copy data contents. */ + if (fd_fd_copy(fd, ar->fd, size, &err) < 0) + ohshit(_("cannot append ar member file (%s) to '%s': %s"), + name, ar->name, err.str); + + if (size & 1) + if (fd_write(ar->fd, "\n", 1) < 0) + ohshite(_("unable to write file '%s'"), ar->name); +} diff --git a/lib/dpkg/ar.h b/lib/dpkg/ar.h new file mode 100644 index 0000000..8a7aff9 --- /dev/null +++ b/lib/dpkg/ar.h @@ -0,0 +1,102 @@ +/* + * libdpkg - Debian packaging suite library routines + * ar.h - primitives for ar handling + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_AR_H +#define LIBDPKG_AR_H + +#include <sys/types.h> + +#include <stdbool.h> +#include <stdint.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup ar Ar archive handling + * @ingroup dpkg-public + * @{ + */ + +#define DPKG_AR_MAGIC "!<arch>\n" +#define DPKG_AR_FMAG "`\n" + +/** + * An on-disk archive header. + */ +struct dpkg_ar_hdr { + char ar_name[16]; /* Member file name, sometimes / terminated. */ + char ar_date[12]; /* File date, decimal seconds since Epoch. */ + char ar_uid[6], ar_gid[6]; /* User and group IDs, in ASCII decimal. */ + char ar_mode[8]; /* File mode, in ASCII octal. */ + char ar_size[10]; /* File size, in ASCII decimal. */ + char ar_fmag[2]; +}; + +/** + * An archive (Unix ar) file. + */ +struct dpkg_ar { + const char *name; + mode_t mode; + intmax_t time; + off_t size; + int fd; +}; + +/** + * In-memory archive member information. + */ +struct dpkg_ar_member { + struct dpkg_ar_member *next; + const char *name; + off_t offset; + off_t size; + intmax_t time; + mode_t mode; + uid_t uid; + gid_t gid; +}; + +struct dpkg_ar * +dpkg_ar_fdopen(const char *filename, int fd); +struct dpkg_ar *dpkg_ar_open(const char *filename); +struct dpkg_ar *dpkg_ar_create(const char *filename, mode_t mode); +void dpkg_ar_set_mtime(struct dpkg_ar *ar, intmax_t mtime); +void dpkg_ar_close(struct dpkg_ar *ar); + +void dpkg_ar_normalize_name(struct dpkg_ar_hdr *arh); +bool dpkg_ar_member_is_illegal(struct dpkg_ar_hdr *arh); + +void dpkg_ar_put_magic(struct dpkg_ar *ar); +void dpkg_ar_member_put_header(struct dpkg_ar *ar, + struct dpkg_ar_member *member); +void dpkg_ar_member_put_file(struct dpkg_ar *ar, const char *name, + int fd, off_t size); +void dpkg_ar_member_put_mem(struct dpkg_ar *ar, const char *name, + const void *data, size_t size); +off_t dpkg_ar_member_get_size(struct dpkg_ar *ar, struct dpkg_ar_hdr *arh); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_AR_H */ diff --git a/lib/dpkg/arch.c b/lib/dpkg/arch.c new file mode 100644 index 0000000..9409f5a --- /dev/null +++ b/lib/dpkg/arch.c @@ -0,0 +1,342 @@ +/* + * libdpkg - Debian packaging suite library routines + * arch.c - architecture database functions + * + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <limits.h> +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/ehandle.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/dir.h> +#include <dpkg/varbuf.h> +#include <dpkg/arch.h> + +#define DPKG_DB_ARCH_FILE "arch" + +/** + * Verify if the architecture name is valid. + * + * Returns NULL if the architecture name is valid. Otherwise it returns a + * string describing why it's not valid. Currently it ensures the name + * starts with an alphanumeric and is then composed of a combinations of + * hyphens and alphanumerics. + * + * The function will abort if you pass it a NULL pointer. + * + * @param name The architecture name to verify. + */ +const char * +dpkg_arch_name_is_illegal(const char *name) +{ + static char buf[150]; + const char *p = name; + + if (p == NULL) + internerr("arch name argument is NULL"); + if (!*p) + return _("may not be empty string"); + if (!c_isalnum(*p)) + return _("must start with an alphanumeric"); + while (*++p != '\0') + if (!c_isalnum(*p) && *p != '-') + break; + if (*p == '\0') + return NULL; + + snprintf(buf, sizeof(buf), _("character '%c' not allowed (only " + "letters, digits and characters '%s')"), + *p, "-"); + return buf; +} + +/* This is a special architecture used to guarantee we always have a valid + * structure to handle. */ +static struct dpkg_arch arch_item_none = { + .name = "", + .type = DPKG_ARCH_NONE, + .next = NULL, +}; +static struct dpkg_arch arch_item_empty = { + .name = "", + .type = DPKG_ARCH_EMPTY, + .next = NULL, +}; + +static struct dpkg_arch arch_item_any = { + .name = "any", + .type = DPKG_ARCH_WILDCARD, + .next = NULL, +}; +static struct dpkg_arch arch_item_all = { + .name = "all", + .type = DPKG_ARCH_ALL, + .next = &arch_item_any, +}; +static struct dpkg_arch arch_item_native = { + .name = ARCHITECTURE, + .type = DPKG_ARCH_NATIVE, + .next = &arch_item_all, +}; +static struct dpkg_arch *arch_head = &arch_item_native; +static struct dpkg_arch *arch_builtin_tail = &arch_item_any; +static bool arch_list_dirty; + +static struct dpkg_arch * +dpkg_arch_new(const char *name, enum dpkg_arch_type type) +{ + struct dpkg_arch *new; + + new = nfmalloc(sizeof(*new)); + new->next = NULL; + new->name = nfstrsave(name); + new->type = type; + + return new; +} + +/** + * Retrieve the struct dpkg_arch for the given architecture. + * + * Create a new structure for the architecture if it is not yet known from + * the system, in that case it will have type == DPKG_ARCH_UNKNOWN, if the + * architecture is illegal it will have type == DPKG_ARCH_ILLEGAL, if name + * is an empty string it will have type == DPKG_ARCH_EMPTY, and if it is + * NULL then it will have type == DPKG_ARCH_NONE. + * + * @param name The architecture name. + */ +struct dpkg_arch * +dpkg_arch_find(const char *name) +{ + struct dpkg_arch *arch, *last_arch = NULL; + enum dpkg_arch_type type; + + if (name == NULL) + return &arch_item_none; + if (name[0] == '\0') + return &arch_item_empty; + + for (arch = arch_head; arch; arch = arch->next) { + if (strcmp(arch->name, name) == 0) + return arch; + last_arch = arch; + } + + if (dpkg_arch_name_is_illegal(name)) + type = DPKG_ARCH_ILLEGAL; + else + type = DPKG_ARCH_UNKNOWN; + + arch = dpkg_arch_new(name, type); + last_arch->next = arch; + + return arch; +} + +/** + * Return the struct dpkg_arch corresponding to the architecture type. + * + * The function only returns instances for types which are unique. For + * forward-compatibility any unknown type will return NULL. + */ +struct dpkg_arch * +dpkg_arch_get(enum dpkg_arch_type type) +{ + switch (type) { + case DPKG_ARCH_NONE: + return &arch_item_none; + case DPKG_ARCH_EMPTY: + return &arch_item_empty; + case DPKG_ARCH_WILDCARD: + return &arch_item_any; + case DPKG_ARCH_ALL: + return &arch_item_all; + case DPKG_ARCH_NATIVE: + return &arch_item_native; + case DPKG_ARCH_ILLEGAL: + case DPKG_ARCH_FOREIGN: + case DPKG_ARCH_UNKNOWN: + internerr("architecture type %d is not unique", type); + default: + /* Ignore unknown types for forward-compatibility. */ + return NULL; + } +} + +/** + * Return the complete list of architectures. + * + * In fact it returns the first item of the linked list and you can + * traverse the list by following arch->next until it's NULL. + */ +struct dpkg_arch * +dpkg_arch_get_list(void) +{ + return arch_head; +} + +/** + * Reset the list of architectures. + * + * Must be called before nffreeall() to ensure we don't point to + * unallocated memory. + */ +void +dpkg_arch_reset_list(void) +{ + arch_builtin_tail->next = NULL; + arch_list_dirty = false; +} + +void +varbuf_add_archqual(struct varbuf *vb, const struct dpkg_arch *arch) +{ + if (arch->type == DPKG_ARCH_NONE) + return; + if (arch->type == DPKG_ARCH_EMPTY) + return; + + varbuf_add_char(vb, ':'); + varbuf_add_str(vb, arch->name); +} + +/** + * Return a descriptive architecture name. + */ +const char * +dpkg_arch_describe(const struct dpkg_arch *arch) +{ + if (arch->type == DPKG_ARCH_NONE) + return C_("architecture", "<none>"); + if (arch->type == DPKG_ARCH_EMPTY) + return C_("architecture", "<empty>"); + + return arch->name; +} + +/** + * Add a new foreign dpkg_arch architecture. + */ +struct dpkg_arch * +dpkg_arch_add(const char *name) +{ + struct dpkg_arch *arch; + + arch = dpkg_arch_find(name); + if (arch->type == DPKG_ARCH_UNKNOWN) { + arch->type = DPKG_ARCH_FOREIGN; + arch_list_dirty = true; + } + + return arch; +} + +/** + * Unmark a foreign dpkg_arch architecture. + */ +void +dpkg_arch_unmark(const struct dpkg_arch *arch_remove) +{ + struct dpkg_arch *arch; + + for (arch = arch_builtin_tail->next; arch; arch = arch->next) { + if (arch->type != DPKG_ARCH_FOREIGN) + continue; + + if (arch == arch_remove) { + arch->type = DPKG_ARCH_UNKNOWN; + arch_list_dirty = true; + return; + } + } +} + +/** + * Load the architecture database. + */ +void +dpkg_arch_load_list(void) +{ + FILE *fp; + char *archfile; + char archname[_POSIX2_LINE_MAX]; + + archfile = dpkg_db_get_path(DPKG_DB_ARCH_FILE); + fp = fopen(archfile, "r"); + if (fp == NULL) { + arch_list_dirty = true; + free(archfile); + return; + } + + while (fgets_checked(archname, sizeof(archname), fp, archfile) >= 0) + dpkg_arch_add(archname); + + free(archfile); + fclose(fp); +} + +/** + * Save the architecture database. + */ +void +dpkg_arch_save_list(void) +{ + struct atomic_file *file; + struct dpkg_arch *arch; + char *archfile; + + if (!arch_list_dirty) + return; + + archfile = dpkg_db_get_path(DPKG_DB_ARCH_FILE); + file = atomic_file_new(archfile, ATOMIC_FILE_MKPATH); + atomic_file_open(file); + + for (arch = arch_head; arch; arch = arch->next) { + if (arch->type != DPKG_ARCH_FOREIGN && + arch->type != DPKG_ARCH_NATIVE) + continue; + + if (fprintf(file->fp, "%s\n", arch->name) < 0) + ohshite(_("error writing to architecture list")); + } + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + dir_sync_path(dpkg_db_get_dir()); + + arch_list_dirty = false; + + free(archfile); +} diff --git a/lib/dpkg/arch.h b/lib/dpkg/arch.h new file mode 100644 index 0000000..02ef06c --- /dev/null +++ b/lib/dpkg/arch.h @@ -0,0 +1,73 @@ +/* + * libdpkg - Debian packaging suite library routines + * arch.h - architecture database functions + * + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_ARCH_H +#define LIBDPKG_ARCH_H + +#include <dpkg/macros.h> +#include <dpkg/varbuf.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup arch Architecture database + * @ingroup dpkg-public + * @{ + */ + +enum dpkg_arch_type { + DPKG_ARCH_NONE, + DPKG_ARCH_EMPTY, + DPKG_ARCH_ILLEGAL, + DPKG_ARCH_WILDCARD, + DPKG_ARCH_ALL, + DPKG_ARCH_NATIVE, + DPKG_ARCH_FOREIGN, + DPKG_ARCH_UNKNOWN, +}; + +struct dpkg_arch { + struct dpkg_arch *next; + const char *name; + enum dpkg_arch_type type; +}; + +const char *dpkg_arch_name_is_illegal(const char *name) DPKG_ATTR_NONNULL(1); +struct dpkg_arch *dpkg_arch_find(const char *name); +struct dpkg_arch *dpkg_arch_get(enum dpkg_arch_type type); +struct dpkg_arch *dpkg_arch_get_list(void); +void dpkg_arch_reset_list(void); + +const char *dpkg_arch_describe(const struct dpkg_arch *arch); + +struct dpkg_arch *dpkg_arch_add(const char *name); +void dpkg_arch_unmark(const struct dpkg_arch *arch); +void dpkg_arch_load_list(void); +void dpkg_arch_save_list(void); + +void varbuf_add_archqual(struct varbuf *vb, const struct dpkg_arch *arch); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_ARCH_H */ diff --git a/lib/dpkg/atomic-file.c b/lib/dpkg/atomic-file.c new file mode 100644 index 0000000..be7a33e --- /dev/null +++ b/lib/dpkg/atomic-file.c @@ -0,0 +1,132 @@ +/* + * libdpkg - Debian packaging suite library routines + * atomic-file.c - atomic file helper functions + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dir.h> +#include <dpkg/atomic-file.h> + +#define ATOMIC_FILE_NEW_EXT "-new" +#define ATOMIC_FILE_OLD_EXT "-old" + +struct atomic_file * +atomic_file_new(const char *filename, enum atomic_file_flags flags) +{ + struct atomic_file *file; + + file = m_malloc(sizeof(*file)); + file->flags = flags; + file->fp = NULL; + file->name = m_strdup(filename); + file->name_new = str_fmt("%s%s", filename, ATOMIC_FILE_NEW_EXT); + + return file; +} + +void +atomic_file_open(struct atomic_file *file) +{ + file->fp = fopen(file->name_new, "w"); + if (file->fp == NULL && file->flags & ATOMIC_FILE_MKPATH) { + if (dir_make_path_parent(file->name, 0755) < 0) + ohshite(_("cannot create base directory for %s"), + file->name); + + file->fp = fopen(file->name_new, "w"); + } + if (file->fp == NULL) + ohshite(_("unable to create new file '%.250s'"), + file->name_new); + fchmod(fileno(file->fp), 0644); + + push_cleanup(cu_closestream, ~ehflag_normaltidy, 1, file->fp); +} + +void +atomic_file_sync(struct atomic_file *file) +{ + if (ferror(file->fp)) + ohshite(_("unable to write new file '%.250s'"), file->name_new); + if (fflush(file->fp)) + ohshite(_("unable to flush new file '%.250s'"), file->name_new); + if (fsync(fileno(file->fp))) + ohshite(_("unable to sync new file '%.250s'"), file->name_new); +} + +void +atomic_file_close(struct atomic_file *file) +{ + pop_cleanup(ehflag_normaltidy); /* fopen */ + + if (fclose(file->fp)) + ohshite(_("unable to close new file '%.250s'"), file->name_new); +} + +static void +atomic_file_backup(struct atomic_file *file) +{ + char *name_old; + + name_old = str_fmt("%s%s", file->name, ATOMIC_FILE_OLD_EXT); + + if (unlink(name_old) && errno != ENOENT) + ohshite(_("error removing old backup file '%s'"), name_old); + if (link(file->name, name_old) && errno != ENOENT) + ohshite(_("error creating new backup file '%s'"), name_old); + + free(name_old); +} + +void +atomic_file_remove(struct atomic_file *file) +{ + if (unlink(file->name_new)) + ohshite(_("cannot remove '%.250s'"), file->name_new); + if (unlink(file->name) && errno != ENOENT) + ohshite(_("cannot remove '%.250s'"), file->name); +} + +void +atomic_file_commit(struct atomic_file *file) +{ + if (file->flags & ATOMIC_FILE_BACKUP) + atomic_file_backup(file); + + if (rename(file->name_new, file->name)) + ohshite(_("error installing new file '%s'"), file->name); +} + +void +atomic_file_free(struct atomic_file *file) +{ + free(file->name_new); + free(file->name); + free(file); +} diff --git a/lib/dpkg/atomic-file.h b/lib/dpkg/atomic-file.h new file mode 100644 index 0000000..17467ae --- /dev/null +++ b/lib/dpkg/atomic-file.h @@ -0,0 +1,62 @@ +/* + * libdpkg - Debian packaging suite library routines + * atomic-file.h - atomic file helper functions + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_ATOMIC_FILE_H +#define LIBDPKG_ATOMIC_FILE_H + +#include <stdio.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup atomic-file Atomic file operations + * @ingroup dpkg-internal + * @{ + */ + +enum DPKG_ATTR_ENUM_FLAGS atomic_file_flags { + ATOMIC_FILE_NORMAL = 0, + ATOMIC_FILE_BACKUP = DPKG_BIT(0), + ATOMIC_FILE_MKPATH = DPKG_BIT(1), +}; + +struct atomic_file { + enum atomic_file_flags flags; + char *name; + char *name_new; + FILE *fp; +}; + +struct atomic_file * +atomic_file_new(const char *filename, enum atomic_file_flags flags); +void atomic_file_open(struct atomic_file *file); +void atomic_file_sync(struct atomic_file *file); +void atomic_file_close(struct atomic_file *file); +void atomic_file_commit(struct atomic_file *file); +void atomic_file_remove(struct atomic_file *file); +void atomic_file_free(struct atomic_file *file); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_ATOMIC_FILE_H */ diff --git a/lib/dpkg/buffer.c b/lib/dpkg/buffer.c new file mode 100644 index 0000000..ed05f4b --- /dev/null +++ b/lib/dpkg/buffer.c @@ -0,0 +1,293 @@ +/* + * libdpkg - Debian packaging suite library routines + * buffer.c - buffer I/O handling routines + * + * Copyright © 1999, 2000 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2000-2003 Adam Heath <doogie@debian.org> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <errno.h> +#include <md5.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/varbuf.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> + +struct buffer_md5_ctx { + MD5_CTX ctx; + char *hash; +}; + +static void +buffer_md5_init(struct buffer_data *data) +{ + struct buffer_md5_ctx *ctx; + + ctx = m_malloc(sizeof(*ctx)); + ctx->hash = data->arg.ptr; + data->arg.ptr = ctx; + MD5Init(&ctx->ctx); +} + +static off_t +buffer_digest_init(struct buffer_data *data) +{ + switch (data->type) { + case BUFFER_DIGEST_NULL: + break; + case BUFFER_DIGEST_MD5: + buffer_md5_init(data); + break; + } + return 0; +} + +static off_t +buffer_digest_update(struct buffer_data *digest, const void *buf, off_t length) +{ + off_t ret = length; + + switch (digest->type) { + case BUFFER_DIGEST_NULL: + break; + case BUFFER_DIGEST_MD5: + MD5Update(&(((struct buffer_md5_ctx *)digest->arg.ptr)->ctx), + buf, length); + break; + default: + internerr("unknown data type %i", digest->type); + } + + return ret; +} + +static void +buffer_md5_done(struct buffer_data *data) +{ + struct buffer_md5_ctx *ctx; + unsigned char digest[16], *p = digest; + char *hash; + int i; + + ctx = (struct buffer_md5_ctx *)data->arg.ptr; + hash = ctx->hash; + MD5Final(digest, &ctx->ctx); + for (i = 0; i < 16; ++i) { + sprintf(hash, "%02x", *p++); + hash += 2; + } + *hash = '\0'; + free(ctx); +} + +static off_t +buffer_digest_done(struct buffer_data *data) +{ + switch (data->type) { + case BUFFER_DIGEST_NULL: + break; + case BUFFER_DIGEST_MD5: + buffer_md5_done(data); + break; + } + return 0; +} + +static off_t +buffer_write(struct buffer_data *data, const void *buf, off_t length, + struct dpkg_error *err) +{ + off_t ret = length; + + switch (data->type) { + case BUFFER_WRITE_VBUF: + varbuf_add_buf((struct varbuf *)data->arg.ptr, buf, length); + break; + case BUFFER_WRITE_FD: + ret = fd_write(data->arg.i, buf, length); + if (ret < 0) + dpkg_put_errno(err, _("failed to write")); + break; + case BUFFER_WRITE_NULL: + break; + default: + internerr("unknown data type %i", data->type); + } + + return ret; +} + +static off_t +buffer_read(struct buffer_data *data, void *buf, off_t length, + struct dpkg_error *err) +{ + off_t ret; + + switch (data->type) { + case BUFFER_READ_FD: + ret = fd_read(data->arg.i, buf, length); + if (ret < 0) + dpkg_put_errno(err, _("failed to read")); + break; + default: + internerr("unknown data type %i", data->type); + } + + return ret; +} + +off_t +buffer_digest(const void *input, void *output, int type, off_t limit) +{ + struct buffer_data data = { .arg.ptr = output, .type = type }; + off_t ret; + + buffer_digest_init(&data); + ret = buffer_digest_update(&data, input, limit); + buffer_digest_done(&data); + + return ret; +} + +static off_t +buffer_copy(struct buffer_data *read_data, + struct buffer_data *digest, + struct buffer_data *write_data, + off_t limit, struct dpkg_error *err) +{ + char *buf; + int bufsize = DPKG_BUFFER_SIZE; + off_t bytesread = 0, byteswritten = 0; + off_t totalread = 0, totalwritten = 0; + + if ((limit != -1) && (limit < bufsize)) + bufsize = limit; + if (bufsize == 0) + buf = NULL; + else + buf = m_malloc(bufsize); + + buffer_digest_init(digest); + + while (bufsize > 0) { + bytesread = buffer_read(read_data, buf, bufsize, err); + if (bytesread < 0) + break; + if (bytesread == 0) + break; + + totalread += bytesread; + + if (limit != -1) { + limit -= bytesread; + if (limit < bufsize) + bufsize = limit; + } + + buffer_digest_update(digest, buf, bytesread); + + byteswritten = buffer_write(write_data, buf, bytesread, err); + if (byteswritten < 0) + break; + if (byteswritten == 0) + break; + + totalwritten += byteswritten; + } + + buffer_digest_done(digest); + + free(buf); + + if (bytesread < 0 || byteswritten < 0) + return -1; + if (totalread != totalwritten) + return -1; + if (limit > 0) + return dpkg_put_error(err, _("unexpected end of file or stream")); + + return totalread; +} + +off_t +buffer_copy_IntInt(int Iin, int Tin, + void *Pdigest, int Tdigest, + int Iout, int Tout, + off_t limit, struct dpkg_error *err) +{ + struct buffer_data read_data = { .type = Tin, .arg.i = Iin }; + struct buffer_data digest = { .type = Tdigest, .arg.ptr = Pdigest }; + struct buffer_data write_data = { .type = Tout, .arg.i = Iout }; + + return buffer_copy(&read_data, &digest, &write_data, limit, err); +} + +off_t +buffer_copy_IntPtr(int Iin, int Tin, + void *Pdigest, int Tdigest, + void *Pout, int Tout, + off_t limit, struct dpkg_error *err) +{ + struct buffer_data read_data = { .type = Tin, .arg.i = Iin }; + struct buffer_data digest = { .type = Tdigest, .arg.ptr = Pdigest }; + struct buffer_data write_data = { .type = Tout, .arg.ptr = Pout }; + + return buffer_copy(&read_data, &digest, &write_data, limit, err); +} + +static off_t +buffer_skip(struct buffer_data *input, off_t limit, struct dpkg_error *err) +{ + struct buffer_data output; + struct buffer_data digest; + + switch (input->type) { + case BUFFER_READ_FD: + if (lseek(input->arg.i, limit, SEEK_CUR) != -1) + return limit; + if (errno != ESPIPE) + return dpkg_put_errno(err, _("failed to seek")); + break; + default: + internerr("unknown data type %i", input->type); + } + + output.type = BUFFER_WRITE_NULL; + output.arg.ptr = NULL; + digest.type = BUFFER_DIGEST_NULL; + digest.arg.ptr = NULL; + + return buffer_copy(input, &digest, &output, limit, err); +} + +off_t +buffer_skip_Int(int I, int T, off_t limit, struct dpkg_error *err) +{ + struct buffer_data input = { .type = T, .arg.i = I }; + + return buffer_skip(&input, limit, err); +} diff --git a/lib/dpkg/buffer.h b/lib/dpkg/buffer.h new file mode 100644 index 0000000..e297660 --- /dev/null +++ b/lib/dpkg/buffer.h @@ -0,0 +1,104 @@ +/* + * libdpkg - Debian packaging suite library routines + * buffer.h - buffer I/O handling routines + * + * Copyright © 1999, 2000 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2000-2003 Adam Heath <doogie@debian.org> + * Copyright © 2005 Scott James Remnant + * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_BUFFER_H +#define LIBDPKG_BUFFER_H + +#include <sys/types.h> + +#include <dpkg/macros.h> +#include <dpkg/error.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup buffer Buffer I/O + * @ingroup dpkg-internal + * @{ + */ + +#define DPKG_BUFFER_SIZE (32 * 1024) + +#define BUFFER_WRITE_VBUF 1 +#define BUFFER_WRITE_FD 2 +#define BUFFER_WRITE_NULL 3 + +#define BUFFER_DIGEST_NULL 4 +#define BUFFER_DIGEST_MD5 5 + +#define BUFFER_READ_FD 0 + +struct buffer_data { + union { + void *ptr; + int i; + } arg; + int type; +}; + +# define buffer_md5(buf, hash, limit) \ + buffer_digest(buf, hash, BUFFER_DIGEST_MD5, limit) + +# define fd_md5(fd, hash, limit, err) \ + buffer_copy_IntPtr(fd, BUFFER_READ_FD, \ + hash, BUFFER_DIGEST_MD5, \ + NULL, BUFFER_WRITE_NULL, \ + limit, err) +# define fd_fd_copy(fd1, fd2, limit, err) \ + buffer_copy_IntInt(fd1, BUFFER_READ_FD, \ + NULL, BUFFER_DIGEST_NULL, \ + fd2, BUFFER_WRITE_FD, \ + limit, err) +# define fd_fd_copy_and_md5(fd1, fd2, hash, limit, err) \ + buffer_copy_IntInt(fd1, BUFFER_READ_FD, \ + hash, BUFFER_DIGEST_MD5, \ + fd2, BUFFER_WRITE_FD, \ + limit, err) +# define fd_vbuf_copy(fd, buf, limit, err) \ + buffer_copy_IntPtr(fd, BUFFER_READ_FD, \ + NULL, BUFFER_DIGEST_NULL, \ + buf, BUFFER_WRITE_VBUF, \ + limit, err) +# define fd_skip(fd, limit, err) \ + buffer_skip_Int(fd, BUFFER_READ_FD, limit, err) + + +off_t buffer_copy_IntPtr(int i, int typeIn, + void *f, int typeDigest, + void *p, int typeOut, + off_t limit, struct dpkg_error *err) + DPKG_ATTR_REQRET; +off_t buffer_copy_IntInt(int i1, int typeIn, + void *f, int typeDigest, + int i2, int typeOut, + off_t limit, struct dpkg_error *err) + DPKG_ATTR_REQRET; +off_t buffer_skip_Int(int I, int T, off_t limit, struct dpkg_error *err) + DPKG_ATTR_REQRET; +off_t buffer_digest(const void *buf, void *hash, int typeDigest, off_t length); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_BUFFER_H */ diff --git a/lib/dpkg/c-ctype.c b/lib/dpkg/c-ctype.c new file mode 100644 index 0000000..b7afd39 --- /dev/null +++ b/lib/dpkg/c-ctype.c @@ -0,0 +1,186 @@ +/* + * libdpkg - Debian packaging suite library routines + * c-ctype.c - ASCII C locale-only functions + * + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/c-ctype.h> + +#undef S +#define S C_CTYPE_SPACE +#undef W +#define W C_CTYPE_WHITE +#undef B +#define B C_CTYPE_BLANK +#undef U +#define U C_CTYPE_UPPER +#undef L +#define L C_CTYPE_LOWER +#undef D +#define D C_CTYPE_DIGIT + +static unsigned short int c_ctype[256] = { +/** 0 **/ + /* \0 */ 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* \a */ 0, + /* \b */ 0, + /* \t */ S | W | B, + /* \n */ S | W, + /* \v */ S, + /* \f */ S, + /* \r */ S, + 0, + 0, +/** 16 **/ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +/** 32 **/ + /* */ S | W | B, + /* ! */ 0, + /* " */ 0, + /* # */ 0, + /* $ */ 0, + /* % */ 0, + /* & */ 0, + /* ' */ 0, + /* ( */ 0, + /* ) */ 0, + /* * */ 0, + /* + */ 0, + /* , */ 0, + /* - */ 0, + /* . */ 0, + /* / */ 0, +/** 48 **/ + /* 0 */ D, + /* 1 */ D, + /* 2 */ D, + /* 3 */ D, + /* 4 */ D, + /* 5 */ D, + /* 6 */ D, + /* 7 */ D, + /* 8 */ D, + /* 9 */ D, + /* : */ 0, + /* ; */ 0, + /* < */ 0, + /* = */ 0, + /* > */ 0, + /* ? */ 0, +/* 64 */ + /* @ */ 0, + /* A */ U, + /* B */ U, + /* C */ U, + /* D */ U, + /* E */ U, + /* F */ U, + /* G */ U, + /* H */ U, + /* I */ U, + /* J */ U, + /* K */ U, + /* L */ U, + /* M */ U, + /* N */ U, + /* O */ U, +/* 80 */ + /* P */ U, + /* Q */ U, + /* R */ U, + /* S */ U, + /* T */ U, + /* U */ U, + /* V */ U, + /* W */ U, + /* X */ U, + /* Y */ U, + /* Z */ U, + /* [ */ 0, + /* \ */ 0, + /* ] */ 0, + /* ^ */ 0, + /* _ */ 0, +/* 96 */ + /* ` */ 0, + /* a */ L, + /* b */ L, + /* c */ L, + /* d */ L, + /* e */ L, + /* f */ L, + /* g */ L, + /* h */ L, + /* i */ L, + /* j */ L, + /* k */ L, + /* l */ L, + /* m */ L, + /* n */ L, + /* o */ L, +/* 112 */ + /* p */ L, + /* q */ L, + /* r */ L, + /* s */ L, + /* t */ L, + /* u */ L, + /* v */ L, + /* w */ L, + /* x */ L, + /* y */ L, + /* z */ L, + /* { */ 0, + /* | */ 0, + /* } */ 0, + /* ~ */ 0, + 0, +/* 128 */ +}; + +/** + * Check if the character is bits ctype. + */ +bool +c_isbits(int c, enum c_ctype_bit bits) +{ + return ((c_ctype[(unsigned char)c] & bits) != 0); +} diff --git a/lib/dpkg/c-ctype.h b/lib/dpkg/c-ctype.h new file mode 100644 index 0000000..a08d852 --- /dev/null +++ b/lib/dpkg/c-ctype.h @@ -0,0 +1,131 @@ +/* + * libdpkg - Debian packaging suite library routines + * c-ctype.h - ASCII C locale-only functions + * + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_C_CTYPE_H +#define LIBDPKG_C_CTYPE_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +#define C_CTYPE_BIT(bit) (1 << (bit)) + +enum DPKG_ATTR_ENUM_FLAGS c_ctype_bit { + C_CTYPE_BLANK = C_CTYPE_BIT(0), + C_CTYPE_WHITE = C_CTYPE_BIT(1), + C_CTYPE_SPACE = C_CTYPE_BIT(2), + C_CTYPE_UPPER = C_CTYPE_BIT(3), + C_CTYPE_LOWER = C_CTYPE_BIT(4), + C_CTYPE_DIGIT = C_CTYPE_BIT(5), + + C_CTYPE_ALPHA = C_CTYPE_UPPER | C_CTYPE_LOWER, + C_CTYPE_ALNUM = C_CTYPE_ALPHA | C_CTYPE_DIGIT, +}; + +bool +c_isbits(int c, enum c_ctype_bit bits); + +/** + * Check if the character is [ \t]. + */ +static inline bool +c_isblank(int c) +{ + return c_isbits(c, C_CTYPE_BLANK); +} + +/** + * Check if the character is [ \t\n]. + */ +static inline bool +c_iswhite(int c) +{ + return c_isbits(c, C_CTYPE_WHITE); +} + +/** + * Check if the character is [ \v\t\f\r\n]. + */ +static inline bool +c_isspace(int c) +{ + return c_isbits(c, C_CTYPE_SPACE); +} + +/** + * Check if the character is [0-9]. + */ +static inline bool +c_isdigit(int c) +{ + return c_isbits(c, C_CTYPE_DIGIT); +} + +/** + * Check if the character is [A-Z]. + */ +static inline bool +c_isupper(int c) +{ + return c_isbits(c, C_CTYPE_UPPER); +} + +/** + * Check if the character is [a-z]. + */ +static inline bool +c_islower(int c) +{ + return c_isbits(c, C_CTYPE_LOWER); +} + +/** + * Check if the character is [a-zA-Z]. + */ +static inline bool +c_isalpha(int c) +{ + return c_isbits(c, C_CTYPE_ALPHA); +} + +/** + * Check if the character is [a-zA-Z0-9]. + */ +static inline bool +c_isalnum(int c) +{ + return c_isbits(c, C_CTYPE_ALNUM); +} + +/** + * Maps the character to its lower-case form. + */ +static inline int +c_tolower(int c) +{ + return (c_isupper(c) ? + (DPKG_STATIC_CAST(unsigned char, c) & ~0x20) | 0x20 : c); +} + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/cleanup.c b/lib/dpkg/cleanup.c new file mode 100644 index 0000000..ff9d6fa --- /dev/null +++ b/lib/dpkg/cleanup.c @@ -0,0 +1,69 @@ +/* + * libdpkg - Debian packaging suite library routines + * cleanup.c - cleanup functions, used when we need to unwind + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dirent.h> +#include <unistd.h> +#include <stdio.h> + +#include <dpkg/dpkg.h> + +void +cu_closepipe(int argc, void **argv) +{ + int *p1 = (int *)argv[0]; + + close(p1[0]); + close(p1[1]); +} + +void +cu_closestream(int argc, void **argv) +{ + FILE *f = (FILE *)(argv[0]); + + fclose(f); +} + +void +cu_closedir(int argc, void **argv) +{ + DIR *d = (DIR *)(argv[0]); + + closedir(d); +} + +void +cu_closefd(int argc, void **argv) +{ + int ip = *(int *)argv[0]; + + close(ip); +} + +void +cu_filename(int argc, void **argv) +{ + const char *filename = argv[0]; + + (void)unlink(filename); +} diff --git a/lib/dpkg/color.c b/lib/dpkg/color.c new file mode 100644 index 0000000..9a33f12 --- /dev/null +++ b/lib/dpkg/color.c @@ -0,0 +1,74 @@ +/* + * libdpkg - Debian packaging suite library routines + * color.c - color support + * + * Copyright © 2015-2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <dpkg/macros.h> +#include <dpkg/color.h> + +static enum color_mode color_mode = COLOR_MODE_UNKNOWN; +static bool use_color = false; + +bool +color_set_mode(const char *mode) +{ + if (strcmp(mode, "auto") == 0) { + color_mode = COLOR_MODE_AUTO; + use_color = isatty(STDOUT_FILENO); + } else if (strcmp(mode, "always") == 0) { + color_mode = COLOR_MODE_ALWAYS; + use_color = true; + } else { + color_mode = COLOR_MODE_NEVER; + use_color = false; + } + + return use_color; +} + +static bool +color_enabled(void) +{ + const char *mode; + + if (color_mode != COLOR_MODE_UNKNOWN) + return use_color; + + mode = getenv("DPKG_COLORS"); + if (mode == NULL) + mode = "auto"; + + return color_set_mode(mode); +} + +const char * +color_get(const char *color) +{ + if (!color_enabled()) + return ""; + + return color; +} diff --git a/lib/dpkg/color.h b/lib/dpkg/color.h new file mode 100644 index 0000000..29f5359 --- /dev/null +++ b/lib/dpkg/color.h @@ -0,0 +1,87 @@ +/* + * libdpkg - Debian packaging suite library routines + * color.h - color support + * + * Copyright © 2015-2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_COLOR_H +#define LIBDPKG_COLOR_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup color Color support + * @ingroup dpkg-internal + * @{ + */ + +/* Standard ANSI colors and attributes. */ +#define COLOR_NORMAL "" +#define COLOR_RESET "\e[0m" +#define COLOR_BOLD "\e[1m" +#define COLOR_BLACK "\e[30m" +#define COLOR_RED "\e[31m" +#define COLOR_GREEN "\e[32m" +#define COLOR_YELLOW "\e[33m" +#define COLOR_BLUE "\e[34m" +#define COLOR_MAGENTA "\e[35m" +#define COLOR_CYAN "\e[36m" +#define COLOR_WHITE "\e[37m" +#define COLOR_BOLD_BLACK "\e[1;30m" +#define COLOR_BOLD_RED "\e[1;31m" +#define COLOR_BOLD_GREEN "\e[1;32m" +#define COLOR_BOLD_YELLOW "\e[1;33m" +#define COLOR_BOLD_BLUE "\e[1;34m" +#define COLOR_BOLD_MAGENTA "\e[1;35m" +#define COLOR_BOLD_CYAN "\e[1;36m" +#define COLOR_BOLD_WHITE "\e[1;37m" + +/* Current defaults. These might become configurable in the future. */ +#define COLOR_PROG COLOR_BOLD +#define COLOR_INFO COLOR_GREEN +#define COLOR_NOTICE COLOR_YELLOW +#define COLOR_WARN COLOR_BOLD_YELLOW +#define COLOR_ERROR COLOR_BOLD_RED + +enum color_mode { + COLOR_MODE_UNKNOWN = -1, + COLOR_MODE_NEVER, + COLOR_MODE_ALWAYS, + COLOR_MODE_AUTO, +}; + +bool +color_set_mode(const char *mode); + +const char * +color_get(const char *color); + +static inline const char * +color_reset(void) +{ + return color_get(COLOR_RESET); +} + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_COLOR_H */ diff --git a/lib/dpkg/command.c b/lib/dpkg/command.c new file mode 100644 index 0000000..18c917b --- /dev/null +++ b/lib/dpkg/command.c @@ -0,0 +1,260 @@ +/* + * libdpkg - Debian packaging suite library routines + * command.c - command execution support + * + * Copyright © 2010-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/string.h> +#include <dpkg/varbuf.h> +#include <dpkg/file.h> +#include <dpkg/path.h> +#include <dpkg/command.h> + +/** + * Initialize a command structure. + * + * If name is NULL, then the last component of the filename path will be + * used to initialize the name member. + * + * @param cmd The command structure to initialize. + * @param filename The filename of the command to execute. + * @param name The description of the command to execute. + */ +void +command_init(struct command *cmd, const char *filename, const char *name) +{ + cmd->filename = filename; + if (name == NULL) + cmd->name = path_basename(filename); + else + cmd->name = name; + cmd->argc = 0; + cmd->argv_size = 10; + cmd->argv = m_malloc(cmd->argv_size * sizeof(cmd->argv[0])); + cmd->argv[0] = NULL; +} + +/** + * Destroy a command structure. + * + * Free the members managed by the command functions (i.e. the argv pointer + * array), and zero all members of a command structure. + * + * @param cmd The command structure to free. + */ +void +command_destroy(struct command *cmd) +{ + cmd->filename = NULL; + cmd->name = NULL; + cmd->argc = 0; + cmd->argv_size = 0; + free(cmd->argv); + cmd->argv = NULL; +} + +static void +command_grow_argv(struct command *cmd, int need) +{ + /* We need a ghost byte for the NUL character. */ + need++; + + /* Check if we already have enough room. */ + if ((cmd->argv_size - cmd->argc) >= need) + return; + + cmd->argv_size = (cmd->argv_size + need) * 2; + cmd->argv = m_realloc(cmd->argv, cmd->argv_size * sizeof(cmd->argv[0])); +} + +/** + * Append an argument to the command's argv. + * + * @param cmd The command structure to act on. + * @param arg The argument to append to argv. + */ +void +command_add_arg(struct command *cmd, const char *arg) +{ + command_grow_argv(cmd, 1); + + cmd->argv[cmd->argc++] = arg; + cmd->argv[cmd->argc] = NULL; +} + +/** + * Append an argument array to the command's argv. + * + * @param cmd The command structure to act on. + * @param argv The NULL terminated argument array to append to argv. + */ +void +command_add_argl(struct command *cmd, const char **argv) +{ + int i, add_argc = 0; + + while (argv[add_argc] != NULL) + add_argc++; + + command_grow_argv(cmd, add_argc); + + for (i = 0; i < add_argc; i++) + cmd->argv[cmd->argc++] = argv[i]; + + cmd->argv[cmd->argc] = NULL; +} + +/** + * Append a va_list of argument to the command's argv. + * + * @param cmd The command structure to act on. + * @param args The NULL terminated va_list of argument array to append to argv. + */ +void +command_add_argv(struct command *cmd, va_list args) +{ + va_list args_copy; + int i, add_argc = 0; + + va_copy(args_copy, args); + while (va_arg(args_copy, const char *) != NULL) + add_argc++; + va_end(args_copy); + + command_grow_argv(cmd, add_argc); + + for (i = 0; i < add_argc; i++) + cmd->argv[cmd->argc++] = va_arg(args, const char *); + + cmd->argv[cmd->argc] = NULL; +} + +/** + * Append a variable list of argument to the command's argv. + * + * @param cmd The command structure to act on. + * @param ... The NULL terminated variable list of argument to append to argv. + */ +void +command_add_args(struct command *cmd, ...) +{ + va_list args; + + va_start(args, cmd); + command_add_argv(cmd, args); + va_end(args); +} + +/** + * Execute the command specified. + * + * The command is executed searching the PATH if the filename does not + * contain any slashes, or using the full path if it's either a relative or + * absolute pathname. This functions does not return. + * + * @param cmd The command structure to act on. + */ +void +command_exec(struct command *cmd) +{ + execvp(cmd->filename, (char * const *)cmd->argv); + ohshite(_("unable to execute %s (%s)"), cmd->name, cmd->filename); +} + +/** + * Execute a shell with a possible command. + * + * @param cmd The command string to execute, if it's NULL an interactive + * shell will be executed instead. + * @param name The description of the command to execute. + */ +void +command_shell(const char *cmd, const char *name) +{ + const char *shell; + const char *mode; + + if (cmd == NULL) { + mode = "-i"; + shell = getenv("SHELL"); + } else { + mode = "-c"; + shell = NULL; + } + + if (str_is_unset(shell)) + shell = DPKG_DEFAULT_SHELL; + +#if HAVE_DPKG_SHELL_WITH_DASH_DASH + execlp(shell, shell, mode, "--", cmd, NULL); +#else + execlp(shell, shell, mode, cmd, NULL); +#endif + ohshite(_("unable to execute %s (%s)"), name, cmd); +} + +/** + * Check whether a command can be found in PATH. + * + * @param cmd The command name to check. This is a relative pathname. + * + * @return A boolean denoting whether the command has been found. + */ +bool +command_in_path(const char *cmd) +{ + struct varbuf filename = VARBUF_INIT; + const char *path_list; + const char *path, *path_end; + + if (cmd[0] == '/') + return file_is_exec(cmd); + + path_list = getenv("PATH"); + if (!path_list) + ohshit(_("PATH is not set")); + + for (path = path_list; path; path = *path_end ? path_end + 1 : NULL) { + size_t path_len; + + path_end = strchrnul(path, ':'); + path_len = (size_t)(path_end - path); + + varbuf_set_buf(&filename, path, path_len); + if (path_len) + varbuf_add_char(&filename, '/'); + varbuf_add_str(&filename, cmd); + varbuf_end_str(&filename); + + if (file_is_exec(filename.buf)) { + varbuf_destroy(&filename); + return true; + } + } + + varbuf_destroy(&filename); + return false; +} diff --git a/lib/dpkg/command.h b/lib/dpkg/command.h new file mode 100644 index 0000000..768aba4 --- /dev/null +++ b/lib/dpkg/command.h @@ -0,0 +1,67 @@ +/* + * libdpkg - Debian packaging suite library routines + * command.h - command execution support + * + * Copyright © 2010, 2012, 2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_COMMAND_H +#define LIBDPKG_COMMAND_H + +#include <dpkg/macros.h> + +#include <stdarg.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup command Command execution + * @ingroup dpkg-internal + * @{ + */ + +/** + * Describe a command to execute. + */ +struct command { + /** Descriptive name of the command, used when printing. */ + const char *name; + /** Filename to execute; either a path or the progname. */ + const char *filename; + int argc; + int argv_size; + const char **argv; +}; + +void command_init(struct command *cmd, const char *filename, const char *name); +void command_destroy(struct command *cmd); + +void command_add_arg(struct command *cmd, const char *arg); +void command_add_argl(struct command *cmd, const char **argv); +void command_add_argv(struct command *cmd, va_list args); +void command_add_args(struct command *cmd, ...) DPKG_ATTR_SENTINEL; + +void command_exec(struct command *cmd) DPKG_ATTR_NORET; + +void command_shell(const char *cmd, const char *name) DPKG_ATTR_NORET; + +bool command_in_path(const char *cmd); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_COMMAND_H */ diff --git a/lib/dpkg/compress.c b/lib/dpkg/compress.c new file mode 100644 index 0000000..adf26ea --- /dev/null +++ b/lib/dpkg/compress.c @@ -0,0 +1,1447 @@ +/* + * libdpkg - Debian packaging suite library routines + * compress.c - compression support functions + * + * Copyright © 2000 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2004 Scott James Remnant <scott@netsplit.com> + * Copyright © 2006-2023 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdlib.h> + +#if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE +#include <compat-zlib.h> +#endif +#ifdef WITH_LIBLZMA +#include <lzma.h> +#endif +#ifdef WITH_LIBZSTD +#include <zstd.h> +#define DPKG_ZSTD_MAX_LEVEL ZSTD_maxCLevel() +#else +#define DPKG_ZSTD_MAX_LEVEL 22 +#define ZSTD_CLEVEL_DEFAULT 3 +#endif +#ifdef WITH_LIBBZ2 +#include <bzlib.h> +#endif + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/error.h> +#include <dpkg/varbuf.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> +#include <dpkg/meminfo.h> +#include <dpkg/command.h> +#include <dpkg/compress.h> +#if USE_LIBZ_IMPL == USE_LIBZ_IMPL_NONE || \ + !defined(WITH_LIBLZMA) || \ + !defined(WITH_LIBZSTD) || \ + !defined(WITH_LIBBZ2) +#include <dpkg/subproc.h> + +static void +fd_fd_filter(struct command *cmd, int fd_in, int fd_out, const char *delenv[]) +{ + pid_t pid; + + pid = subproc_fork(); + if (pid == 0) { + int i; + + if (fd_in != 0) { + m_dup2(fd_in, 0); + close(fd_in); + } + if (fd_out != 1) { + m_dup2(fd_out, 1); + close(fd_out); + } + + for (i = 0; delenv[i]; i++) + unsetenv(delenv[i]); + + command_exec(cmd); + } + subproc_reap(pid, cmd->name, 0); +} + +static void +command_compress_init(struct command *cmd, const char *name, const char *desc, + int level) +{ + static char combuf[6]; + + command_init(cmd, name, desc); + command_add_arg(cmd, name); + + snprintf(combuf, sizeof(combuf), "-c%d", level); + command_add_arg(cmd, combuf); +} + +static void +command_decompress_init(struct command *cmd, const char *name, const char *desc) +{ + command_init(cmd, name, desc); + command_add_arg(cmd, name); + command_add_arg(cmd, "-dc"); +} +#endif + +#if defined(WITH_LIBLZMA) || defined(WITH_LIBZSTD) +enum dpkg_stream_filter { + DPKG_STREAM_COMPRESS = 1, + DPKG_STREAM_DECOMPRESS = 2, +}; + +enum dpkg_stream_action { + DPKG_STREAM_INIT = 0, + DPKG_STREAM_RUN = 1, + DPKG_STREAM_FINISH = 2, +}; + +enum dpkg_stream_status { + DPKG_STREAM_OK, + DPKG_STREAM_END, + DPKG_STREAM_ERROR, +}; +#endif + +struct compressor { + const char *name; + const char *extension; + int default_level; + void (*fixup_params)(struct compress_params *params); + void (*compress)(struct compress_params *params, + int fd_in, int fd_out, const char *desc); + void (*decompress)(struct compress_params *params, + int fd_in, int fd_out, const char *desc); +}; + +/* + * No compressor (pass-through). + */ + +static void +fixup_none_params(struct compress_params *params) +{ +} + +static void +decompress_none(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct dpkg_error err; + + if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0) + ohshit(_("%s: pass-through copy error: %s"), desc, err.str); +} + +static void +compress_none(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct dpkg_error err; + + if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0) + ohshit(_("%s: pass-through copy error: %s"), desc, err.str); +} + +static const struct compressor compressor_none = { + .name = "none", + .extension = "", + .default_level = 0, + .fixup_params = fixup_none_params, + .compress = compress_none, + .decompress = decompress_none, +}; + +/* + * Gzip compressor. + */ + +#define GZIP "gzip" + +static void +fixup_gzip_params(struct compress_params *params) +{ + /* Normalize compression level. */ + if (params->level == 0) + params->type = COMPRESSOR_TYPE_NONE; +} + +#if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE +static void +decompress_gzip(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + char *buffer; + size_t bufsize = DPKG_BUFFER_SIZE; + int z_errnum; + gzFile gzfile = gzdopen(fd_in, "r"); + + if (gzfile == NULL) + ohshit(_("%s: error binding input to gzip stream"), desc); + + buffer = m_malloc(bufsize); + + for (;;) { + int actualread, actualwrite; + + actualread = gzread(gzfile, buffer, bufsize); + if (actualread < 0) { + const char *errmsg = gzerror(gzfile, &z_errnum); + + if (z_errnum == Z_ERRNO) + errmsg = strerror(errno); + ohshit(_("%s: internal gzip read error: '%s'"), desc, + errmsg); + } + if (actualread == 0) /* EOF. */ + break; + + actualwrite = fd_write(fd_out, buffer, actualread); + if (actualwrite != actualread) + ohshite(_("%s: internal gzip write error"), desc); + } + + free(buffer); + + z_errnum = gzclose(gzfile); + if (z_errnum) { + const char *errmsg; + + if (z_errnum == Z_ERRNO) + errmsg = strerror(errno); + else + errmsg = zError(z_errnum); + ohshit(_("%s: internal gzip read error: %s"), desc, errmsg); + } + + if (close(fd_out)) + ohshite(_("%s: internal gzip write error"), desc); +} + +static void +compress_gzip(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + char *buffer; + char combuf[6]; + size_t bufsize = DPKG_BUFFER_SIZE; + int strategy; + int z_errnum; + gzFile gzfile; + + if (params->strategy == COMPRESSOR_STRATEGY_FILTERED) + strategy = 'f'; + else if (params->strategy == COMPRESSOR_STRATEGY_HUFFMAN) + strategy = 'h'; + else if (params->strategy == COMPRESSOR_STRATEGY_RLE) + strategy = 'R'; + else if (params->strategy == COMPRESSOR_STRATEGY_FIXED) + strategy = 'F'; + else + strategy = ' '; + + snprintf(combuf, sizeof(combuf), "w%d%c", params->level, strategy); + gzfile = gzdopen(fd_out, combuf); + if (gzfile == NULL) + ohshit(_("%s: error binding output to gzip stream"), desc); + + buffer = m_malloc(bufsize); + + for (;;) { + int actualread, actualwrite; + + actualread = fd_read(fd_in, buffer, bufsize); + if (actualread < 0) + ohshite(_("%s: internal gzip read error"), desc); + if (actualread == 0) /* EOF. */ + break; + + actualwrite = gzwrite(gzfile, buffer, actualread); + if (actualwrite != actualread) { + const char *errmsg = gzerror(gzfile, &z_errnum); + + if (z_errnum == Z_ERRNO) + errmsg = strerror(errno); + ohshit(_("%s: internal gzip write error: '%s'"), desc, + errmsg); + } + } + + free(buffer); + + z_errnum = gzclose(gzfile); + if (z_errnum) { + const char *errmsg; + + if (z_errnum == Z_ERRNO) + errmsg = strerror(errno); + else + errmsg = zError(z_errnum); + ohshit(_("%s: internal gzip write error: %s"), desc, errmsg); + } +} +#else +static const char *env_gzip[] = { "GZIP", NULL }; + +static void +decompress_gzip(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_decompress_init(&cmd, GZIP, desc); + + fd_fd_filter(&cmd, fd_in, fd_out, env_gzip); + + command_destroy(&cmd); +} + +static void +compress_gzip(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_compress_init(&cmd, GZIP, desc, params->level); + command_add_arg(&cmd, "-n"); + + fd_fd_filter(&cmd, fd_in, fd_out, env_gzip); + + command_destroy(&cmd); +} +#endif + +static const struct compressor compressor_gzip = { + .name = "gzip", + .extension = ".gz", + .default_level = 9, + .fixup_params = fixup_gzip_params, + .compress = compress_gzip, + .decompress = decompress_gzip, +}; + +/* + * Bzip2 compressor. + */ + +#define BZIP2 "bzip2" + +static void +fixup_bzip2_params(struct compress_params *params) +{ + /* Normalize compression level. */ + if (params->level == 0) + params->level = 1; +} + +#ifdef WITH_LIBBZ2 +static void +decompress_bzip2(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + char *buffer; + size_t bufsize = DPKG_BUFFER_SIZE; + BZFILE *bzfile = BZ2_bzdopen(fd_in, "r"); + + if (bzfile == NULL) + ohshit(_("%s: error binding input to bzip2 stream"), desc); + + buffer = m_malloc(bufsize); + + for (;;) { + int actualread, actualwrite; + + actualread = BZ2_bzread(bzfile, buffer, bufsize); + if (actualread < 0) { + int bz_errnum = 0; + const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum); + + if (bz_errnum == BZ_IO_ERROR) + errmsg = strerror(errno); + ohshit(_("%s: internal bzip2 read error: '%s'"), desc, + errmsg); + } + if (actualread == 0) /* EOF. */ + break; + + actualwrite = fd_write(fd_out, buffer, actualread); + if (actualwrite != actualread) + ohshite(_("%s: internal bzip2 write error"), desc); + } + + free(buffer); + + BZ2_bzclose(bzfile); + + if (close(fd_out)) + ohshite(_("%s: internal bzip2 write error"), desc); +} + +static void +compress_bzip2(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + char *buffer; + char combuf[6]; + size_t bufsize = DPKG_BUFFER_SIZE; + int bz_errnum; + BZFILE *bzfile; + + snprintf(combuf, sizeof(combuf), "w%d", params->level); + bzfile = BZ2_bzdopen(fd_out, combuf); + if (bzfile == NULL) + ohshit(_("%s: error binding output to bzip2 stream"), desc); + + buffer = m_malloc(bufsize); + + for (;;) { + int actualread, actualwrite; + + actualread = fd_read(fd_in, buffer, bufsize); + if (actualread < 0) + ohshite(_("%s: internal bzip2 read error"), desc); + if (actualread == 0) /* EOF. */ + break; + + actualwrite = BZ2_bzwrite(bzfile, buffer, actualread); + if (actualwrite != actualread) { + const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum); + + if (bz_errnum == BZ_IO_ERROR) + errmsg = strerror(errno); + ohshit(_("%s: internal bzip2 write error: '%s'"), desc, + errmsg); + } + } + + free(buffer); + + BZ2_bzWriteClose(&bz_errnum, bzfile, 0, NULL, NULL); + if (bz_errnum != BZ_OK) { + const char *errmsg = _("unexpected bzip2 error"); + + if (bz_errnum == BZ_IO_ERROR) + errmsg = strerror(errno); + ohshit(_("%s: internal bzip2 write error: '%s'"), desc, + errmsg); + } + + /* Because BZ2_bzWriteClose has done a fflush on the file handle, + * doing a close on the file descriptor associated with it should + * be safe™. */ + if (close(fd_out)) + ohshite(_("%s: internal bzip2 write error"), desc); +} +#else +static const char *env_bzip2[] = { "BZIP", "BZIP2", NULL }; + +static void +decompress_bzip2(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_decompress_init(&cmd, BZIP2, desc); + + fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2); + + command_destroy(&cmd); +} + +static void +compress_bzip2(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_compress_init(&cmd, BZIP2, desc, params->level); + + fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2); + + command_destroy(&cmd); +} +#endif + +static const struct compressor compressor_bzip2 = { + .name = "bzip2", + .extension = ".bz2", + .default_level = 9, + .fixup_params = fixup_bzip2_params, + .compress = compress_bzip2, + .decompress = decompress_bzip2, +}; + +/* + * Xz compressor. + */ + +#define XZ "xz" + +#ifdef WITH_LIBLZMA +struct io_lzma { + const char *desc; + + struct compress_params *params; + + enum dpkg_stream_filter filter; + enum dpkg_stream_action action; + enum dpkg_stream_status status; + + void (*init)(struct io_lzma *io, lzma_stream *s); + void (*code)(struct io_lzma *io, lzma_stream *s); + void (*done)(struct io_lzma *io, lzma_stream *s); +}; + +/* XXX: liblzma does not expose error messages. */ +static const char * +dpkg_lzma_strerror(struct io_lzma *io, lzma_ret code) +{ + const char *const impossible = _("internal error (bug)"); + + switch (code) { + case LZMA_MEM_ERROR: + return strerror(ENOMEM); + case LZMA_MEMLIMIT_ERROR: + if (io->action == DPKG_STREAM_RUN) + return _("memory usage limit reached"); + return impossible; + case LZMA_OPTIONS_ERROR: + if (io->filter == DPKG_STREAM_COMPRESS && + io->action == DPKG_STREAM_INIT) + return _("unsupported compression preset"); + if (io->filter == DPKG_STREAM_DECOMPRESS && + io->action == DPKG_STREAM_RUN) + return _("unsupported options in file header"); + return impossible; + case LZMA_DATA_ERROR: + if (io->action == DPKG_STREAM_RUN) + return _("compressed data is corrupt"); + return impossible; + case LZMA_BUF_ERROR: + if (io->action == DPKG_STREAM_RUN) + return _("unexpected end of input"); + return impossible; + case LZMA_FORMAT_ERROR: + if (io->filter == DPKG_STREAM_DECOMPRESS && + io->action == DPKG_STREAM_RUN) + return _("file format not recognized"); + return impossible; + case LZMA_UNSUPPORTED_CHECK: + if (io->filter == DPKG_STREAM_COMPRESS && + io->action == DPKG_STREAM_INIT) + return _("unsupported type of integrity check"); + return impossible; + default: + return impossible; + } +} + +static void +filter_lzma(struct io_lzma *io, int fd_in, int fd_out) +{ + uint8_t *buf_in; + uint8_t *buf_out; + size_t buf_size = DPKG_BUFFER_SIZE; + lzma_stream s = LZMA_STREAM_INIT; + + buf_in = m_malloc(buf_size); + buf_out = m_malloc(buf_size); + + s.next_out = buf_out; + s.avail_out = buf_size; + + io->status = DPKG_STREAM_OK; + io->action = DPKG_STREAM_INIT; + io->init(io, &s); + io->action = DPKG_STREAM_RUN; + + do { + ssize_t len; + + if (s.avail_in == 0 && io->action != DPKG_STREAM_FINISH) { + len = fd_read(fd_in, buf_in, buf_size); + if (len < 0) + ohshite(_("%s: lzma read error"), io->desc); + if (len == 0) + io->action = DPKG_STREAM_FINISH; + s.next_in = buf_in; + s.avail_in = len; + } + + io->code(io, &s); + + if (s.avail_out == 0 || io->status == DPKG_STREAM_END) { + len = fd_write(fd_out, buf_out, s.next_out - buf_out); + if (len < 0) + ohshite(_("%s: lzma write error"), io->desc); + s.next_out = buf_out; + s.avail_out = buf_size; + } + } while (io->status != DPKG_STREAM_END); + + io->done(io, &s); + + free(buf_in); + free(buf_out); + + if (close(fd_out)) + ohshite(_("%s: lzma close error"), io->desc); +} + +static void DPKG_ATTR_NORET +filter_lzma_error(struct io_lzma *io, lzma_ret ret) +{ + ohshit(_("%s: lzma error: %s"), io->desc, + dpkg_lzma_strerror(io, ret)); +} + +#ifdef HAVE_LZMA_MT_ENCODER +static uint64_t +filter_xz_get_memlimit(void) +{ + uint64_t mt_memlimit; + + /* Ask the kernel what is currently available for us. If this fails + * initialize the memory limit to half the physical RAM, or to 128 MiB + * if we cannot infer the number. */ + if (meminfo_get_available(&mt_memlimit) < 0) { + mt_memlimit = lzma_physmem() / 2; + if (mt_memlimit == 0) + mt_memlimit = 128 * 1024 * 1024; + } + /* Clamp the multi-threaded memory limit to half the addressable + * memory on this architecture. */ + if (mt_memlimit > INTPTR_MAX) + mt_memlimit = INTPTR_MAX; + + return mt_memlimit; +} + +static uint32_t +filter_xz_get_cputhreads(struct compress_params *params) +{ + long threads_max; + + threads_max = lzma_cputhreads(); + if (threads_max == 0) + threads_max = 1; + + if (params->threads_max >= 0) + return clamp(params->threads_max, 1, threads_max); + + return threads_max; +} +#endif + +static void +filter_unxz_init(struct io_lzma *io, lzma_stream *s) +{ +#ifdef HAVE_LZMA_MT_DECODER + lzma_mt mt_options = { + .flags = 0, + .block_size = 0, + .timeout = 0, + .filters = NULL, + }; +#else + uint64_t memlimit = UINT64_MAX; +#endif + lzma_ret ret; + + io->filter = DPKG_STREAM_DECOMPRESS; + +#ifdef HAVE_LZMA_MT_DECODER + mt_options.memlimit_stop = UINT64_MAX; + mt_options.memlimit_threading = filter_xz_get_memlimit(); + mt_options.threads = filter_xz_get_cputhreads(io->params); + + ret = lzma_stream_decoder_mt(s, &mt_options); +#else + ret = lzma_stream_decoder(s, memlimit, 0); +#endif + if (ret != LZMA_OK) + filter_lzma_error(io, ret); +} + +static void +filter_xz_init(struct io_lzma *io, lzma_stream *s) +{ + uint32_t preset; + lzma_check check = LZMA_CHECK_CRC64; +#ifdef HAVE_LZMA_MT_ENCODER + uint64_t mt_memlimit; + lzma_mt mt_options = { + .flags = 0, + .block_size = 0, + .timeout = 0, + .filters = NULL, + .check = check, + }; +#endif + lzma_ret ret; + + io->filter = DPKG_STREAM_COMPRESS; + + preset = io->params->level; + if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME) + preset |= LZMA_PRESET_EXTREME; + +#ifdef HAVE_LZMA_MT_ENCODER + mt_options.preset = preset; + mt_memlimit = filter_xz_get_memlimit(); + mt_options.threads = filter_xz_get_cputhreads(io->params); + + /* Guess whether we have enough RAM to use the multi-threaded encoder, + * and decrease them up to single-threaded to reduce memory usage. */ + for (; mt_options.threads > 1; mt_options.threads--) { + uint64_t mt_memusage; + + mt_memusage = lzma_stream_encoder_mt_memusage(&mt_options); + if (mt_memusage < mt_memlimit) + break; + } + + ret = lzma_stream_encoder_mt(s, &mt_options); +#else + ret = lzma_easy_encoder(s, preset, check); +#endif + + if (ret != LZMA_OK) + filter_lzma_error(io, ret); +} + +static void +filter_lzma_code(struct io_lzma *io, lzma_stream *s) +{ + lzma_ret ret; + lzma_action action; + + if (io->action == DPKG_STREAM_RUN) + action = LZMA_RUN; + else if (io->action == DPKG_STREAM_FINISH) + action = LZMA_FINISH; + else + internerr("unknown stream filter action %d\n", io->action); + + ret = lzma_code(s, action); + + if (ret == LZMA_STREAM_END) + io->status = DPKG_STREAM_END; + else if (ret != LZMA_OK) + filter_lzma_error(io, ret); +} + +static void +filter_lzma_done(struct io_lzma *io, lzma_stream *s) +{ + lzma_end(s); +} + +static void +decompress_xz(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_lzma io; + + io.init = filter_unxz_init; + io.code = filter_lzma_code; + io.done = filter_lzma_done; + io.desc = desc; + io.params = params; + + filter_lzma(&io, fd_in, fd_out); +} + +static void +compress_xz(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_lzma io; + + io.init = filter_xz_init; + io.code = filter_lzma_code; + io.done = filter_lzma_done; + io.desc = desc; + io.params = params; + + filter_lzma(&io, fd_in, fd_out); +} +#else +static const char *env_xz[] = { "XZ_DEFAULTS", "XZ_OPT", NULL }; + +static void +decompress_xz(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + char *threads_opt = NULL; + + command_decompress_init(&cmd, XZ, desc); + + if (params->threads_max > 0) { + threads_opt = str_fmt("-T%d", params->threads_max); + command_add_arg(&cmd, threads_opt); + } + + fd_fd_filter(&cmd, fd_in, fd_out, env_xz); + + command_destroy(&cmd); + free(threads_opt); +} + +static void +compress_xz(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + char *threads_opt = NULL; + + command_compress_init(&cmd, XZ, desc, params->level); + + if (params->strategy == COMPRESSOR_STRATEGY_EXTREME) + command_add_arg(&cmd, "-e"); + + if (params->threads_max > 0) { + /* Do not generate warnings when adjusting memory usage, nor + * exit with non-zero due to those not emitted warnings. */ + command_add_arg(&cmd, "--quiet"); + command_add_arg(&cmd, "--no-warn"); + + /* Do not let xz fallback to single-threaded mode, to avoid + * non-reproducible output. */ + command_add_arg(&cmd, "--no-adjust"); + + /* The xz -T1 option selects a single-threaded mode which + * generates different output than in multi-threaded mode. + * To avoid the non-reproducible output we pass -T+1 + * (supported with xz >= 5.4.0) to request multi-threaded + * mode with a single thread. */ + if (params->threads_max == 1) + threads_opt = m_strdup("-T+1"); + else + threads_opt = str_fmt("-T%d", params->threads_max); + command_add_arg(&cmd, threads_opt); + } + + fd_fd_filter(&cmd, fd_in, fd_out, env_xz); + + command_destroy(&cmd); + free(threads_opt); +} +#endif + +static const struct compressor compressor_xz = { + .name = "xz", + .extension = ".xz", + .default_level = 6, + .fixup_params = fixup_none_params, + .compress = compress_xz, + .decompress = decompress_xz, +}; + +/* + * Lzma compressor. + */ + +#ifdef WITH_LIBLZMA +static void +filter_unlzma_init(struct io_lzma *io, lzma_stream *s) +{ + uint64_t memlimit = UINT64_MAX; + lzma_ret ret; + + io->filter = DPKG_STREAM_DECOMPRESS; + + ret = lzma_alone_decoder(s, memlimit); + if (ret != LZMA_OK) + filter_lzma_error(io, ret); +} + +static void +filter_lzma_init(struct io_lzma *io, lzma_stream *s) +{ + uint32_t preset; + lzma_options_lzma options; + lzma_ret ret; + + io->filter = DPKG_STREAM_COMPRESS; + + preset = io->params->level; + if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME) + preset |= LZMA_PRESET_EXTREME; + if (lzma_lzma_preset(&options, preset)) + filter_lzma_error(io, LZMA_OPTIONS_ERROR); + + ret = lzma_alone_encoder(s, &options); + if (ret != LZMA_OK) + filter_lzma_error(io, ret); +} + +static void +decompress_lzma(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_lzma io; + + io.init = filter_unlzma_init; + io.code = filter_lzma_code; + io.done = filter_lzma_done; + io.desc = desc; + io.params = params; + + filter_lzma(&io, fd_in, fd_out); +} + +static void +compress_lzma(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_lzma io; + + io.init = filter_lzma_init; + io.code = filter_lzma_code; + io.done = filter_lzma_done; + io.desc = desc; + io.params = params; + + filter_lzma(&io, fd_in, fd_out); +} +#else +static void +decompress_lzma(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_decompress_init(&cmd, XZ, desc); + command_add_arg(&cmd, "--format=lzma"); + + fd_fd_filter(&cmd, fd_in, fd_out, env_xz); + + command_destroy(&cmd); +} + +static void +compress_lzma(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + + command_compress_init(&cmd, XZ, desc, params->level); + command_add_arg(&cmd, "--format=lzma"); + + fd_fd_filter(&cmd, fd_in, fd_out, env_xz); + + command_destroy(&cmd); +} +#endif + +static const struct compressor compressor_lzma = { + .name = "lzma", + .extension = ".lzma", + .default_level = 6, + .fixup_params = fixup_none_params, + .compress = compress_lzma, + .decompress = decompress_lzma, +}; + +/* + * ZStandard compressor. + */ + +#define ZSTD "zstd" + +#ifdef WITH_LIBZSTD +struct io_zstd_stream { + enum dpkg_stream_filter filter; + enum dpkg_stream_action action; + enum dpkg_stream_status status; + + union { + ZSTD_CCtx *c; + ZSTD_DCtx *d; + } ctx; + + const uint8_t *next_in; + size_t avail_in; + uint8_t *next_out; + size_t avail_out; +}; + +struct io_zstd { + const char *desc; + + struct compress_params *params; + + void (*init)(struct io_zstd *io, struct io_zstd_stream *s); + void (*code)(struct io_zstd *io, struct io_zstd_stream *s); + void (*done)(struct io_zstd *io, struct io_zstd_stream *s); +}; + +static void DPKG_ATTR_NORET +filter_zstd_error(struct io_zstd *io, size_t ret) +{ + ohshit(_("%s: zstd error: %s"), io->desc, ZSTD_getErrorName(ret)); +} + +static uint32_t +filter_zstd_get_cputhreads(struct compress_params *params) +{ + ZSTD_bounds workers; + long threads_max = 1; + + /* The shared library has not been built with multi-threading. */ + workers = ZSTD_cParam_getBounds(ZSTD_c_nbWorkers); + if (workers.upperBound == 0) + return 1; + +#ifdef _SC_NPROCESSORS_ONLN + threads_max = sysconf(_SC_NPROCESSORS_ONLN); + if (threads_max < 0) + return 1; +#endif + + if (params->threads_max >= 0) + return clamp(params->threads_max, 1, threads_max); + + return threads_max; +} + +static size_t +filter_zstd_get_buf_in_size(struct io_zstd_stream *s) +{ + if (s->filter == DPKG_STREAM_DECOMPRESS) + return ZSTD_DStreamInSize(); + else + return ZSTD_CStreamInSize(); +} + +static size_t +filter_zstd_get_buf_out_size(struct io_zstd_stream *s) +{ + if (s->filter == DPKG_STREAM_DECOMPRESS) + return ZSTD_DStreamOutSize(); + else + return ZSTD_CStreamOutSize(); +} + +static void +filter_unzstd_init(struct io_zstd *io, struct io_zstd_stream *s) +{ + s->filter = DPKG_STREAM_DECOMPRESS; + s->action = DPKG_STREAM_RUN; + s->status = DPKG_STREAM_OK; + + s->ctx.d = ZSTD_createDCtx(); + if (s->ctx.d == NULL) + ohshit(_("%s: cannot create zstd decompression context"), + io->desc); +} + +static void +filter_unzstd_code(struct io_zstd *io, struct io_zstd_stream *s) +{ + ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 }; + ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 }; + size_t ret; + + ret = ZSTD_decompressStream(s->ctx.d, &buf_out, &buf_in); + if (ZSTD_isError(ret)) + filter_zstd_error(io, ret); + + s->next_in += buf_in.pos; + s->avail_in -= buf_in.pos; + s->next_out += buf_out.pos; + s->avail_out -= buf_out.pos; + + if (ret == 0) + s->status = DPKG_STREAM_END; +} + +static void +filter_unzstd_done(struct io_zstd *io, struct io_zstd_stream *s) +{ + ZSTD_freeDCtx(s->ctx.d); +} + +static void +filter_zstd_init(struct io_zstd *io, struct io_zstd_stream *s) +{ + int clevel = io->params->level; + uint32_t nthreads; + size_t ret; + + s->filter = DPKG_STREAM_COMPRESS; + s->action = DPKG_STREAM_RUN; + s->status = DPKG_STREAM_OK; + + s->ctx.c = ZSTD_createCCtx(); + if (s->ctx.c == NULL) + ohshit(_("%s: cannot create zstd compression context"), + io->desc); + + ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_compressionLevel, clevel); + if (ZSTD_isError(ret)) + filter_zstd_error(io, ret); + ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_checksumFlag, 1); + if (ZSTD_isError(ret)) + filter_zstd_error(io, ret); + + nthreads = filter_zstd_get_cputhreads(io->params); + if (nthreads > 1) + ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_nbWorkers, nthreads); +} + +static void +filter_zstd_code(struct io_zstd *io, struct io_zstd_stream *s) +{ + ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 }; + ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 }; + ZSTD_EndDirective action; + size_t ret; + + if (s->action == DPKG_STREAM_FINISH) + action = ZSTD_e_end; + else + action = ZSTD_e_continue; + + ret = ZSTD_compressStream2(s->ctx.c, &buf_out, &buf_in, action); + if (ZSTD_isError(ret)) + filter_zstd_error(io, ret); + + s->next_in += buf_in.pos; + s->avail_in -= buf_in.pos; + s->next_out += buf_out.pos; + s->avail_out -= buf_out.pos; + + if (s->action == DPKG_STREAM_FINISH && ret == 0) + s->status = DPKG_STREAM_END; +} + +static void +filter_zstd_done(struct io_zstd *io, struct io_zstd_stream *s) +{ + ZSTD_freeCCtx(s->ctx.c); +} + +static void +filter_zstd(struct io_zstd *io, int fd_in, int fd_out) +{ + ssize_t buf_in_size; + ssize_t buf_out_size; + uint8_t *buf_in; + uint8_t *buf_out; + struct io_zstd_stream s = { + .action = DPKG_STREAM_INIT, + }; + + io->init(io, &s); + + buf_in_size = filter_zstd_get_buf_in_size(&s); + buf_in = m_malloc(buf_in_size); + buf_out_size = filter_zstd_get_buf_out_size(&s); + buf_out = m_malloc(buf_out_size); + + s.next_out = buf_out; + s.avail_out = buf_out_size; + + do { + ssize_t len; + + if (s.avail_in == 0 && s.action != DPKG_STREAM_FINISH) { + len = fd_read(fd_in, buf_in, buf_in_size); + if (len < 0) + ohshite(_("%s: zstd read error"), io->desc); + if (len < buf_in_size) + s.action = DPKG_STREAM_FINISH; + s.next_in = buf_in; + s.avail_in = len; + } + + io->code(io, &s); + + if (s.avail_out == 0 || s.status == DPKG_STREAM_END) { + len = fd_write(fd_out, buf_out, s.next_out - buf_out); + if (len < 0) + ohshite(_("%s: zstd write error"), io->desc); + s.next_out = buf_out; + s.avail_out = buf_out_size; + } + } while (s.status != DPKG_STREAM_END); + + io->done(io, &s); + + free(buf_in); + free(buf_out); + + if (close(fd_out)) + ohshite(_("%s: zstd close error"), io->desc); +} + +static void +decompress_zstd(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_zstd io; + + io.init = filter_unzstd_init; + io.code = filter_unzstd_code; + io.done = filter_unzstd_done; + io.desc = desc; + io.params = params; + + filter_zstd(&io, fd_in, fd_out); +} + +static void +compress_zstd(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct io_zstd io; + + io.init = filter_zstd_init; + io.code = filter_zstd_code; + io.done = filter_zstd_done; + io.desc = desc; + io.params = params; + + filter_zstd(&io, fd_in, fd_out); +} +#else +static const char *env_zstd[] = { + "ZSTD_CLEVEL", + "ZSTD_NBTHREADS", + NULL, +}; + +static void +decompress_zstd(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + char *threads_opt = NULL; + + command_decompress_init(&cmd, ZSTD, desc); + command_add_arg(&cmd, "-q"); + + if (params->threads_max > 0) { + threads_opt = str_fmt("-T%d", params->threads_max); + command_add_arg(&cmd, threads_opt); + } + + fd_fd_filter(&cmd, fd_in, fd_out, env_zstd); + + command_destroy(&cmd); + free(threads_opt); +} + +static void +compress_zstd(struct compress_params *params, int fd_in, int fd_out, + const char *desc) +{ + struct command cmd; + char *threads_opt = NULL; + + command_compress_init(&cmd, ZSTD, desc, params->level); + command_add_arg(&cmd, "-q"); + + if (params->level > 19) + command_add_arg(&cmd, "--ultra"); + + if (params->threads_max > 0) { + threads_opt = str_fmt("-T%d", params->threads_max); + command_add_arg(&cmd, threads_opt); + } + + fd_fd_filter(&cmd, fd_in, fd_out, env_zstd); + + command_destroy(&cmd); + free(threads_opt); +} +#endif + +static const struct compressor compressor_zstd = { + .name = "zstd", + .extension = ".zst", + .default_level = ZSTD_CLEVEL_DEFAULT, + .fixup_params = fixup_none_params, + .compress = compress_zstd, + .decompress = decompress_zstd, +}; + +/* + * Generic compressor filter. + */ + +static const struct compressor *compressor_array[] = { + [COMPRESSOR_TYPE_NONE] = &compressor_none, + [COMPRESSOR_TYPE_GZIP] = &compressor_gzip, + [COMPRESSOR_TYPE_XZ] = &compressor_xz, + [COMPRESSOR_TYPE_ZSTD] = &compressor_zstd, + [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2, + [COMPRESSOR_TYPE_LZMA] = &compressor_lzma, +}; + +static const struct compressor * +compressor(enum compressor_type type) +{ + const enum compressor_type max_type = array_count(compressor_array); + + if (type < 0 || type >= max_type) + internerr("compressor_type %d is out of range", type); + + return compressor_array[type]; +} + +const char * +compressor_get_name(enum compressor_type type) +{ + return compressor(type)->name; +} + +const char * +compressor_get_extension(enum compressor_type type) +{ + return compressor(type)->extension; +} + +enum compressor_type +compressor_find_by_name(const char *name) +{ + size_t i; + + for (i = 0; i < array_count(compressor_array); i++) + if (strcmp(compressor_array[i]->name, name) == 0) + return i; + + return COMPRESSOR_TYPE_UNKNOWN; +} + +enum compressor_type +compressor_find_by_extension(const char *extension) +{ + size_t i; + + for (i = 0; i < array_count(compressor_array); i++) + if (strcmp(compressor_array[i]->extension, extension) == 0) + return i; + + return COMPRESSOR_TYPE_UNKNOWN; +} + +enum compressor_strategy +compressor_get_strategy(const char *name) +{ + if (strcmp(name, "none") == 0) + return COMPRESSOR_STRATEGY_NONE; + if (strcmp(name, "filtered") == 0) + return COMPRESSOR_STRATEGY_FILTERED; + if (strcmp(name, "huffman") == 0) + return COMPRESSOR_STRATEGY_HUFFMAN; + if (strcmp(name, "rle") == 0) + return COMPRESSOR_STRATEGY_RLE; + if (strcmp(name, "fixed") == 0) + return COMPRESSOR_STRATEGY_FIXED; + if (strcmp(name, "extreme") == 0) + return COMPRESSOR_STRATEGY_EXTREME; + + return COMPRESSOR_STRATEGY_UNKNOWN; +} + +static void +compressor_fixup_params(struct compress_params *params) +{ + compressor(params->type)->fixup_params(params); + + if (params->level < 0) + params->level = compressor(params->type)->default_level; +} + +bool +compressor_check_params(struct compress_params *params, struct dpkg_error *err) +{ + compressor_fixup_params(params); + + if ((params->type == COMPRESSOR_TYPE_ZSTD && + params->level > DPKG_ZSTD_MAX_LEVEL) || + (params->type != COMPRESSOR_TYPE_ZSTD && + params->level > 9)) { + dpkg_put_error(err, _("invalid compression level %d"), + params->level); + return false; + } + + if (params->strategy == COMPRESSOR_STRATEGY_NONE) + return true; + + if (params->type == COMPRESSOR_TYPE_GZIP && + (params->strategy == COMPRESSOR_STRATEGY_FILTERED || + params->strategy == COMPRESSOR_STRATEGY_HUFFMAN || + params->strategy == COMPRESSOR_STRATEGY_RLE || + params->strategy == COMPRESSOR_STRATEGY_FIXED)) + return true; + + if (params->type == COMPRESSOR_TYPE_XZ && + params->strategy == COMPRESSOR_STRATEGY_EXTREME) + return true; + + dpkg_put_error(err, _("unknown compression strategy")); + return false; +} + +void +decompress_filter(struct compress_params *params, int fd_in, int fd_out, + const char *desc_fmt, ...) +{ + va_list args; + struct varbuf desc = VARBUF_INIT; + + va_start(args, desc_fmt); + varbuf_vprintf(&desc, desc_fmt, args); + va_end(args); + + compressor(params->type)->decompress(params, fd_in, fd_out, desc.buf); + + varbuf_destroy(&desc); +} + +void +compress_filter(struct compress_params *params, int fd_in, int fd_out, + const char *desc_fmt, ...) +{ + va_list args; + struct varbuf desc = VARBUF_INIT; + + va_start(args, desc_fmt); + varbuf_vprintf(&desc, desc_fmt, args); + va_end(args); + + compressor(params->type)->compress(params, fd_in, fd_out, desc.buf); + + varbuf_destroy(&desc); +} diff --git a/lib/dpkg/compress.h b/lib/dpkg/compress.h new file mode 100644 index 0000000..b05bccf --- /dev/null +++ b/lib/dpkg/compress.h @@ -0,0 +1,89 @@ +/* + * libdpkg - Debian packaging suite library routines + * compress.h - compression support functions + * + * Copyright © 2004 Scott James Remnant <scott@netsplit.com> + * Copyright © 2006-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_COMPRESS_H +#define LIBDPKG_COMPRESS_H + +#include <dpkg/macros.h> +#include <dpkg/error.h> + +#include <stdbool.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup compress Compression + * @ingroup dpkg-internal + * @{ + */ + +enum compressor_type { + COMPRESSOR_TYPE_UNKNOWN = -1, + COMPRESSOR_TYPE_NONE, + COMPRESSOR_TYPE_GZIP, + COMPRESSOR_TYPE_XZ, + COMPRESSOR_TYPE_ZSTD, + COMPRESSOR_TYPE_BZIP2, + COMPRESSOR_TYPE_LZMA, + + COMPRESSOR_TYPE_MAX, +}; + +enum compressor_strategy { + COMPRESSOR_STRATEGY_UNKNOWN = -1, + COMPRESSOR_STRATEGY_NONE, + COMPRESSOR_STRATEGY_FILTERED, + COMPRESSOR_STRATEGY_HUFFMAN, + COMPRESSOR_STRATEGY_RLE, + COMPRESSOR_STRATEGY_FIXED, + COMPRESSOR_STRATEGY_EXTREME, +}; + +struct compress_params { + enum compressor_type type; + enum compressor_strategy strategy; + int level; + int threads_max; +}; + +enum compressor_type compressor_find_by_name(const char *name); +enum compressor_type compressor_find_by_extension(const char *name); + +const char *compressor_get_name(enum compressor_type type); +const char *compressor_get_extension(enum compressor_type type); + +enum compressor_strategy compressor_get_strategy(const char *name); + +bool compressor_check_params(struct compress_params *params, + struct dpkg_error *err); + +void decompress_filter(struct compress_params *params, int fd_in, int fd_out, + const char *desc, ...) + DPKG_ATTR_PRINTF(4); +void compress_filter(struct compress_params *params, int fd_in, int fd_out, + const char *desc, ...) + DPKG_ATTR_PRINTF(4); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_COMPRESS_H */ diff --git a/lib/dpkg/db-ctrl-access.c b/lib/dpkg/db-ctrl-access.c new file mode 100644 index 0000000..c252336 --- /dev/null +++ b/lib/dpkg/db-ctrl-access.c @@ -0,0 +1,119 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-ctrl-access.c - package control information database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <dirent.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/debug.h> + +bool +pkg_infodb_has_file(struct pkginfo *pkg, struct pkgbin *pkgbin, + const char *name) +{ + const char *filename; + struct stat stab; + + filename = pkg_infodb_get_file(pkg, pkgbin, name); + if (lstat(filename, &stab) == 0) + return true; + else if (errno == ENOENT) + return false; + else + ohshite(_("unable to check existence of '%.250s'"), filename); +} + +void +pkg_infodb_foreach(struct pkginfo *pkg, struct pkgbin *pkgbin, + pkg_infodb_file_func *func) +{ + DIR *db_dir; + struct dirent *db_de; + struct varbuf_state db_path_state; + struct varbuf db_path = VARBUF_INIT; + const char *pkgname; + enum pkg_infodb_format db_format; + + /* Make sure to always read and verify the format version. */ + db_format = pkg_infodb_get_format(); + + if (pkgbin->multiarch == PKG_MULTIARCH_SAME && + db_format == PKG_INFODB_FORMAT_MULTIARCH) + pkgname = pkgbin_name(pkg, pkgbin, pnaw_always); + else + pkgname = pkgbin_name(pkg, pkgbin, pnaw_never); + + varbuf_add_dir(&db_path, pkg_infodb_get_dir()); + varbuf_end_str(&db_path); + varbuf_snapshot(&db_path, &db_path_state); + + db_dir = opendir(db_path.buf); + if (!db_dir) + ohshite(_("cannot read info directory")); + + push_cleanup(cu_closedir, ~0, 1, (void *)db_dir); + while ((db_de = readdir(db_dir)) != NULL) { + const char *filename, *filetype, *dot; + + debug(dbg_veryverbose, "infodb foreach info file '%s'", + db_de->d_name); + + /* Ignore dotfiles, including ‘.’ and ‘..’. */ + if (db_de->d_name[0] == '.') + continue; + + /* Ignore anything odd. */ + dot = strrchr(db_de->d_name, '.'); + if (dot == NULL) + continue; + + /* Ignore files from other packages. */ + if (strlen(pkgname) != (size_t)(dot - db_de->d_name) || + strncmp(db_de->d_name, pkgname, dot - db_de->d_name)) + continue; + + debug(dbg_stupidlyverbose, "infodb foreach file this pkg"); + + /* Skip past the full stop. */ + filetype = dot + 1; + + varbuf_rollback(&db_path_state); + varbuf_add_str(&db_path, db_de->d_name); + varbuf_end_str(&db_path); + filename = db_path.buf; + + func(filename, filetype); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + varbuf_destroy(&db_path); +} diff --git a/lib/dpkg/db-ctrl-format.c b/lib/dpkg/db-ctrl-format.c new file mode 100644 index 0000000..34b5ff5 --- /dev/null +++ b/lib/dpkg/db-ctrl-format.c @@ -0,0 +1,157 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-ctrl-format.c - package control information database format + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/varbuf.h> +#include <dpkg/db-ctrl.h> + +static enum pkg_infodb_format db_format = PKG_INFODB_FORMAT_UNKNOWN; +static bool db_upgrading; +static char *db_infodir; + +static enum pkg_infodb_format +pkg_infodb_parse_format(const char *file) +{ + FILE *fp; + unsigned int format; + + fp = fopen(file, "r"); + if (fp == NULL) { + /* A missing format file means legacy format (0). */ + if (errno == ENOENT) + return PKG_INFODB_FORMAT_LEGACY; + ohshite(_("error trying to open %.250s"), file); + } + + if (fscanf(fp, "%u", &format) != 1) + ohshit(_("corrupt info database format file '%s'"), file); + + fclose(fp); + + return format; +} + +static enum pkg_infodb_format +pkg_infodb_read_format(void) +{ + struct atomic_file *file; + struct stat st; + char *filename; + + filename = dpkg_db_get_path(INFODIR "/format"); + file = atomic_file_new(filename, 0); + + db_format = pkg_infodb_parse_format(file->name); + + /* Check if a previous upgrade got interrupted. Because we are only + * supposed to upgrade the db layout one format at a time, if the + * new file exists that means the new format is just one ahead, + * we don't try to read it because it contains unreliable data. */ + if (stat(file->name_new, &st) == 0) { + db_format++; + db_upgrading = true; + } + + atomic_file_free(file); + free(filename); + + if (db_format < 0 || db_format >= PKG_INFODB_FORMAT_LAST) + ohshit(_("info database format (%d) is bogus or too new; " + "try getting a newer dpkg"), db_format); + + return db_format; +} + +enum pkg_infodb_format +pkg_infodb_get_format(void) +{ + if (db_format > PKG_INFODB_FORMAT_UNKNOWN) + return db_format; + else + return pkg_infodb_read_format(); +} + +void +pkg_infodb_set_format(enum pkg_infodb_format version) +{ + db_format = version; +} + +bool +pkg_infodb_is_upgrading(void) +{ + if (db_format < 0) + pkg_infodb_read_format(); + + return db_upgrading; +} + +const char * +pkg_infodb_get_dir(void) +{ + if (db_infodir == NULL) + db_infodir = dpkg_db_get_path(INFODIR); + + return db_infodir; +} + +const char * +pkg_infodb_get_file(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + const char *filetype) +{ + static struct varbuf vb; + enum pkg_infodb_format format; + + /* Make sure to always read and verify the format version. */ + format = pkg_infodb_get_format(); + + varbuf_reset(&vb); + varbuf_add_dir(&vb, pkg_infodb_get_dir()); + varbuf_add_str(&vb, pkg->set->name); + if (pkgbin->multiarch == PKG_MULTIARCH_SAME && + format == PKG_INFODB_FORMAT_MULTIARCH) + varbuf_add_archqual(&vb, pkgbin->arch); + varbuf_add_char(&vb, '.'); + varbuf_add_str(&vb, filetype); + varbuf_end_str(&vb); + + return vb.buf; +} + +const char * +pkg_infodb_reset_dir(void) +{ + free(db_infodir); + db_infodir = NULL; + + return pkg_infodb_get_dir(); +} diff --git a/lib/dpkg/db-ctrl-upgrade.c b/lib/dpkg/db-ctrl-upgrade.c new file mode 100644 index 0000000..86f584b --- /dev/null +++ b/lib/dpkg/db-ctrl-upgrade.c @@ -0,0 +1,250 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-ctrl-upgrade.c - package control information database format upgrade + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/path.h> +#include <dpkg/dir.h> + +struct rename_node { + struct rename_node *next; + char *old; + char *new; +}; + +/* Global variables. */ +static struct rename_node *rename_head = NULL; + +static struct rename_node * +rename_node_new(const char *old, const char *new, struct rename_node *next) +{ + struct rename_node *node; + + node = m_malloc(sizeof(*node)); + node->next = next; + node->old = m_strdup(old); + node->new = m_strdup(new); + + return node; +} + +static void +rename_node_free(struct rename_node *node) +{ + free(node->old); + free(node->new); + free(node); +} + +static void +pkg_infodb_link_multiarch_files(void) +{ + DIR *db_dir; + struct dirent *db_de; + struct varbuf pkgname = VARBUF_INIT; + struct varbuf oldname = VARBUF_INIT; + struct varbuf newname = VARBUF_INIT; + struct varbuf_state oldname_state; + struct varbuf_state newname_state; + + varbuf_add_dir(&oldname, pkg_infodb_get_dir()); + varbuf_end_str(&oldname); + varbuf_snapshot(&oldname, &oldname_state); + + varbuf_set_varbuf(&newname, &oldname); + varbuf_snapshot(&newname, &newname_state); + + db_dir = opendir(pkg_infodb_get_dir()); + if (!db_dir) + ohshite(_("cannot read info directory")); + + push_cleanup(cu_closedir, ~0, 1, (void *)db_dir); + while ((db_de = readdir(db_dir)) != NULL) { + const char *filetype, *dot; + struct pkginfo *pkg; + struct pkgset *set; + + /* Ignore dotfiles, including ‘.’ and ‘..’. */ + if (db_de->d_name[0] == '.') + continue; + + /* Ignore anything odd. */ + dot = strrchr(db_de->d_name, '.'); + if (dot == NULL) + continue; + + varbuf_set_buf(&pkgname, db_de->d_name, dot - db_de->d_name); + + /* Skip files already converted. */ + if (strchr(pkgname.buf, ':')) + continue; + + set = pkg_hash_find_set(pkgname.buf); + for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) + if (pkg->status != PKG_STAT_NOTINSTALLED) + break; + if (!pkg) { + warning(_("info file %s/%s not associated to any package"), + pkg_infodb_get_dir(), db_de->d_name); + continue; + } + + /* Does it need to be upgraded? */ + if (pkg->installed.multiarch != PKG_MULTIARCH_SAME) + continue; + + /* Skip past the full stop. */ + filetype = dot + 1; + + varbuf_rollback(&oldname_state); + varbuf_add_str(&oldname, db_de->d_name); + varbuf_end_str(&oldname); + + varbuf_rollback(&newname_state); + varbuf_add_pkgbin_name(&newname, pkg, &pkg->installed, pnaw_always); + varbuf_add_char(&newname, '.'); + varbuf_add_str(&newname, filetype); + varbuf_end_str(&newname); + + if (link(oldname.buf, newname.buf) && errno != EEXIST) + ohshite(_("error creating hard link '%.255s'"), + newname.buf); + rename_head = rename_node_new(oldname.buf, newname.buf, rename_head); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + varbuf_destroy(&pkgname); + varbuf_destroy(&newname); + varbuf_destroy(&oldname); +} + +static void +cu_abort_db_upgrade(int argc, void **argv) +{ + struct atomic_file *file = argv[0]; + struct rename_node *next; + + /* Restore the old files if needed and drop the newly created files. */ + while (rename_head) { + next = rename_head->next; + if (link(rename_head->new, rename_head->old) && errno != EEXIST) + ohshite(_("error creating hard link '%.255s'"), + rename_head->old); + if (unlink(rename_head->new)) + ohshite(_("cannot remove '%.250s'"), rename_head->new); + rename_node_free(rename_head); + rename_head = next; + } + if (unlink(file->name_new) && errno != ENOENT) + ohshite(_("cannot remove '%.250s'"), file->name_new); + + atomic_file_free(file); +} + +static void +pkg_infodb_write_format(struct atomic_file *file, int version) +{ + if (fprintf(file->fp, "%d\n", version) < 0) + ohshite(_("error while writing '%s'"), file->name_new); + + atomic_file_sync(file); + atomic_file_close(file); + dir_sync_path_parent(file->name); + + pkg_infodb_set_format(version); +} + +static void +pkg_infodb_unlink_monoarch_files(void) +{ + struct rename_node *next; + + while (rename_head) { + next = rename_head->next; + if (unlink(rename_head->old)) + ohshite(_("cannot remove '%.250s'"), rename_head->old); + rename_node_free(rename_head); + rename_head = next; + } +} + +static void +pkg_infodb_upgrade_to_multiarch(void) +{ + struct atomic_file *db_file; + char *db_format_file; + + db_format_file = dpkg_db_get_path(INFODIR "/format"); + db_file = atomic_file_new(db_format_file, ATOMIC_FILE_MKPATH); + atomic_file_open(db_file); + + push_cleanup(cu_abort_db_upgrade, ehflag_bombout, 1, db_file); + + pkg_infodb_link_multiarch_files(); + pkg_infodb_write_format(db_file, 1); + + pkg_infodb_unlink_monoarch_files(); + atomic_file_commit(db_file); + dir_sync_path(pkg_infodb_get_dir()); + + pop_cleanup(ehflag_normaltidy); + + atomic_file_free(db_file); + free(db_format_file); +} + +/** + * Upgrade the infodb if there's the need and possibility. + * + * Currently this implies, that the modstatdb was opened for writing and: + * - previous upgrade has not been completed; or + * - current format is not the latest one. + */ +void +pkg_infodb_upgrade(void) +{ + enum pkg_infodb_format db_format; + + /* Make sure to always read and verify the format version. */ + db_format = pkg_infodb_get_format(); + + if (modstatdb_get_status() < msdbrw_write) + return; + + if (db_format < PKG_INFODB_FORMAT_MULTIARCH || + pkg_infodb_is_upgrading()) + pkg_infodb_upgrade_to_multiarch(); +} diff --git a/lib/dpkg/db-ctrl.h b/lib/dpkg/db-ctrl.h new file mode 100644 index 0000000..d21bb7c --- /dev/null +++ b/lib/dpkg/db-ctrl.h @@ -0,0 +1,56 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-ctrl.h - package control information database + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DB_CTRL_H +#define LIBDPKG_DB_CTRL_H + +#include <stdbool.h> + +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +enum pkg_infodb_format { + PKG_INFODB_FORMAT_UNKNOWN = -1, + PKG_INFODB_FORMAT_LEGACY = 0, + PKG_INFODB_FORMAT_MULTIARCH = 1, + PKG_INFODB_FORMAT_LAST, +}; + +enum pkg_infodb_format pkg_infodb_get_format(void); +void pkg_infodb_set_format(enum pkg_infodb_format format); +bool pkg_infodb_is_upgrading(void); +void pkg_infodb_upgrade(void); + +const char *pkg_infodb_get_dir(void); +const char *pkg_infodb_get_file(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + const char *filetype); +const char *pkg_infodb_reset_dir(void); +bool pkg_infodb_has_file(struct pkginfo *pkg, struct pkgbin *pkgbin, + const char *name); + +typedef void pkg_infodb_file_func(const char *filename, const char *filetype); + +void pkg_infodb_foreach(struct pkginfo *pkg, struct pkgbin *pkgbin, + pkg_infodb_file_func *func); + +DPKG_END_DECLS + +#endif /* LIBDPKG_DB_CTRL_H */ diff --git a/lib/dpkg/db-fsys-digest.c b/lib/dpkg/db-fsys-digest.c new file mode 100644 index 0000000..5b4252b --- /dev/null +++ b/lib/dpkg/db-fsys-digest.c @@ -0,0 +1,151 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-digest.c - management of filesystem digests database + * + * Copyright © 2012-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/debug.h> +#include <dpkg/fdio.h> +#include <dpkg/dir.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> + +/* + * If mask is nonzero, will not write any file whose fsys_namenode + * has any flag bits set in mask. + */ +void +write_filehash_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask) +{ + struct atomic_file *file; + const char *hashfile; + + debug(dbg_general, "generating infodb hashfile"); + + if (pkg_infodb_has_file(pkg, &pkg->available, HASHFILE)) + return; + + hashfile = pkg_infodb_get_file(pkg, pkgbin, HASHFILE); + + file = atomic_file_new(hashfile, 0); + atomic_file_open(file); + + for (; list; list = list->next) { + const struct fsys_namenode *namenode = list->namenode; + + if (mask && (namenode->flags & mask)) + continue; + if (namenode->newhash == NULL) + continue; + + fprintf(file->fp, "%s %s\n", + namenode->newhash, namenode->name + 1); + } + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + dir_sync_path(pkg_infodb_get_dir()); +} + +static void +parse_filehash_buffer(struct varbuf *buf, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + char *thisline, *nextline; + const char *pkgname = pkg_name(pkg, pnaw_nonambig); + const char *buf_end = buf->buf + buf->used; + + for (thisline = buf->buf; thisline < buf_end; thisline = nextline) { + struct fsys_namenode *namenode; + char *endline, *hash_end, *filename; + + endline = memchr(thisline, '\n', buf_end - thisline); + if (endline == NULL) + ohshit(_("control file '%s' for package '%s' is " + "missing final newline"), HASHFILE, pkgname); + + /* The md5sum hash has a constant length. */ + hash_end = thisline + MD5HASHLEN; + + filename = hash_end + 2; + if (filename + 1 > endline) + ohshit(_("control file '%s' for package '%s' is " + "missing value"), HASHFILE, pkgname); + + if (hash_end[0] != ' ' || hash_end[1] != ' ') + ohshit(_("control file '%s' for package '%s' is " + "missing value separator"), HASHFILE, pkgname); + hash_end[0] = '\0'; + + /* Where to start next time around. */ + nextline = endline + 1; + + /* Strip trailing ‘/’. */ + if (endline > thisline && endline[-1] == '/') + endline--; + *endline = '\0'; + + if (endline == thisline) + ohshit(_("control file '%s' for package '%s' " + "contains empty filename"), HASHFILE, pkgname); + + debug(dbg_eachfiledetail, "load digest '%s' for filename '%s'", + thisline, filename); + + /* Add the file to the list. */ + namenode = fsys_hash_find_node(filename, FHFF_NONE); + namenode->newhash = nfstrsave(thisline); + } +} + +void +parse_filehash(struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + const char *hashfile; + struct varbuf buf = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + + hashfile = pkg_infodb_get_file(pkg, pkgbin, HASHFILE); + + if (file_slurp(hashfile, &buf, &err) < 0 && err.syserrno != ENOENT) + dpkg_error_print(&err, + _("loading control file '%s' for package '%s'"), + HASHFILE, pkg_name(pkg, pnaw_nonambig)); + + if (buf.used > 0) + parse_filehash_buffer(&buf, pkg, pkgbin); + + varbuf_destroy(&buf); +} diff --git a/lib/dpkg/db-fsys-divert.c b/lib/dpkg/db-fsys-divert.c new file mode 100644 index 0000000..e0054bb --- /dev/null +++ b/lib/dpkg/db-fsys-divert.c @@ -0,0 +1,132 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-divert.c - management of filesystem diverted files database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/debug.h> +#include <dpkg/db-fsys.h> + +static struct fsys_diversion *diversions = NULL; +static char *diversionsname; + +void +ensure_diversions(void) +{ + static struct stat sb_prev; + struct stat sb_next; + char linebuf[MAXDIVERTFILENAME]; + static FILE *file_prev; + FILE *file; + struct fsys_diversion *ov, *oicontest, *oialtname; + + if (diversionsname == NULL) + diversionsname = dpkg_db_get_path(DIVERSIONSFILE); + + onerr_abort++; + + file = fopen(diversionsname, "r"); + if (!file) { + if (errno != ENOENT) + ohshite(_("failed to open diversions file")); + } else { + setcloexec(fileno(file), diversionsname); + + if (fstat(fileno(file), &sb_next)) + ohshite(_("failed to fstat diversions file")); + + /* + * We need to keep the database file open so that the + * filesystem cannot reuse the inode number (f.ex. during + * multiple dpkg-divert invocations in a maintainer script), + * otherwise the following check might turn true, and we + * would skip reloading a modified database. + */ + if (file_prev && + sb_prev.st_dev == sb_next.st_dev && + sb_prev.st_ino == sb_next.st_ino) { + fclose(file); + onerr_abort--; + debug(dbg_general, "%s: same, skipping", __func__); + return; + } + sb_prev = sb_next; + } + if (file_prev) + fclose(file_prev); + file_prev = file; + + for (ov = diversions; ov; ov = ov->next) { + ov->useinstead->divert->camefrom->divert = NULL; + ov->useinstead->divert = NULL; + } + diversions = NULL; + if (!file) { + onerr_abort--; + debug(dbg_general, "%s: none, resetting", __func__); + return; + } + debug(dbg_general, "%s: new, (re)loading", __func__); + + while (fgets_checked(linebuf, sizeof(linebuf), file, diversionsname) >= 0) { + oicontest = nfmalloc(sizeof(*oicontest)); + oialtname = nfmalloc(sizeof(*oialtname)); + + oialtname->camefrom = fsys_hash_find_node(linebuf, FHFF_NONE); + oialtname->useinstead = NULL; + + fgets_must(linebuf, sizeof(linebuf), file, diversionsname); + oicontest->useinstead = fsys_hash_find_node(linebuf, FHFF_NONE); + oicontest->camefrom = NULL; + + fgets_must(linebuf, sizeof(linebuf), file, diversionsname); + oicontest->pkgset = strcmp(linebuf, ":") ? + pkg_hash_find_set(linebuf) : NULL; + oialtname->pkgset = oicontest->pkgset; + + if (oialtname->camefrom->divert || + oicontest->useinstead->divert) + ohshit(_("conflicting diversions involving '%.250s' or '%.250s'"), + oialtname->camefrom->name, oicontest->useinstead->name); + + oialtname->camefrom->divert = oicontest; + oicontest->useinstead->divert = oialtname; + + oicontest->next = diversions; + diversions = oicontest; + } + + onerr_abort--; +} diff --git a/lib/dpkg/db-fsys-files.c b/lib/dpkg/db-fsys-files.c new file mode 100644 index 0000000..099cad3 --- /dev/null +++ b/lib/dpkg/db-fsys-files.c @@ -0,0 +1,329 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-files.c - management of filesystem files database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#ifdef HAVE_LINUX_FIEMAP_H +#include <linux/fiemap.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#include <sys/vfs.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/dir.h> +#include <dpkg/fdio.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-files.h> +#include <dpkg/progress.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> + +/*** Generic data structures and routines. ***/ + +static bool allpackagesdone = false; + +void note_must_reread_files_inpackage(struct pkginfo *pkg) { + allpackagesdone = false; + pkg->files_list_valid = false; +} + +enum pkg_filesdb_load_status { + PKG_FILESDB_LOAD_NONE = 0, + PKG_FILESDB_LOAD_INPROGRESS = 1, + PKG_FILESDB_LOAD_DONE = 2, +}; + +static enum pkg_filesdb_load_status saidread = PKG_FILESDB_LOAD_NONE; + +/** + * Load the list of files in this package into memory, or update the + * list if it is there but stale. + */ +void +ensure_packagefiles_available(struct pkginfo *pkg) +{ + const char *filelistfile; + struct varbuf buf = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + + if (pkg->files_list_valid) + return; + + /* Throw away any stale data, if there was any. */ + pkg_files_blank(pkg); + + /* Packages which aren't installed don't have a files list. */ + if (pkg->status == PKG_STAT_NOTINSTALLED) { + pkg->files_list_valid = true; + return; + } + + filelistfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + onerr_abort++; + + if (file_slurp(filelistfile, &buf, &err) < 0) { + if (err.syserrno != ENOENT) + dpkg_error_print(&err, _("loading files list file for package '%s'"), + pkg_name(pkg, pnaw_nonambig)); + + onerr_abort--; + if (pkg->status != PKG_STAT_CONFIGFILES && + dpkg_version_is_informative(&pkg->configversion)) { + warning(_("files list file for package '%.250s' missing; assuming " + "package has no files currently installed"), + pkg_name(pkg, pnaw_nonambig)); + } + pkg->files = NULL; + pkg->files_list_valid = true; + return; + } + + if (buf.used) { + struct fsys_namenode_list **lendp; + char *loaded_list_end, *thisline; + + loaded_list_end = buf.buf + buf.used; + + lendp = &pkg->files; + thisline = buf.buf; + while (thisline < loaded_list_end) { + struct fsys_namenode *namenode; + char *nextline, *ptr; + + ptr = memchr(thisline, '\n', loaded_list_end - thisline); + if (ptr == NULL) + ohshit(_("files list file for package '%.250s' is missing final newline"), + pkg_name(pkg, pnaw_nonambig)); + /* Where to start next time around. */ + nextline = ptr + 1; + /* Strip trailing ‘/’. */ + if (ptr > thisline && ptr[-1] == '/') ptr--; + /* Add the file to the list. */ + if (ptr == thisline) + ohshit(_("files list file for package '%.250s' contains empty filename"), + pkg_name(pkg, pnaw_nonambig)); + *ptr = '\0'; + + namenode = fsys_hash_find_node(thisline, FHFF_NONE); + lendp = pkg_files_add_file(pkg, namenode, lendp); + thisline = nextline; + } + } + + varbuf_destroy(&buf); + + onerr_abort--; + + pkg->files_list_valid = true; +} + +#if defined(HAVE_LINUX_FIEMAP_H) +static int +pkg_sorter_by_files_list_phys_offs(const void *a, const void *b) +{ + const struct pkginfo *pa = *(const struct pkginfo **)a; + const struct pkginfo *pb = *(const struct pkginfo **)b; + + /* We can't simply subtract, because the difference may be greater than + * INT_MAX. */ + if (pa->files_list_phys_offs < pb->files_list_phys_offs) + return -1; + else if (pa->files_list_phys_offs > pb->files_list_phys_offs) + return 1; + else + return 0; +} + +#define DPKG_FIEMAP_EXTENTS 1 +#define DPKG_FIEMAP_BUF_SIZE \ + (sizeof(struct fiemap) + \ + sizeof(struct fiemap_extent) * DPKG_FIEMAP_EXTENTS) / sizeof(__u64) + +static void +pkg_files_optimize_load(struct pkg_array *array) +{ + __u64 fiemap_buf[DPKG_FIEMAP_BUF_SIZE]; + struct fiemap *fm = (struct fiemap *)fiemap_buf; + struct statfs fs; + int i; + + /* Get the filesystem block size. */ + if (statfs(pkg_infodb_get_dir(), &fs) < 0) + return; + + /* Sort packages by the physical location of their list files, so that + * scanning them later will minimize disk drive head movements. */ + for (i = 0; i < array->n_pkgs; i++) { + struct pkginfo *pkg = array->pkgs[i]; + const char *listfile; + int fd; + + if (pkg->status == PKG_STAT_NOTINSTALLED || + pkg->files_list_phys_offs != 0) + continue; + + pkg->files_list_phys_offs = -1; + + listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + fd = open(listfile, O_RDONLY); + if (fd < 0) + continue; + + memset(fiemap_buf, 0, sizeof(fiemap_buf)); + fm->fm_start = 0; + fm->fm_length = fs.f_bsize; + fm->fm_flags = 0; + fm->fm_extent_count = DPKG_FIEMAP_EXTENTS; + + if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fm) == 0) + pkg->files_list_phys_offs = fm->fm_extents[0].fe_physical; + + close(fd); + } + + pkg_array_sort(array, pkg_sorter_by_files_list_phys_offs); +} +#elif defined(HAVE_POSIX_FADVISE) +static void +pkg_files_optimize_load(struct pkg_array *array) +{ + int i; + + /* Ask the kernel to start preloading the list files, so as to get a + * boost when later we actually load them. */ + for (i = 0; i < array->n_pkgs; i++) { + struct pkginfo *pkg = array->pkgs[i]; + const char *listfile; + int fd; + + listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + fd = open(listfile, O_RDONLY | O_NONBLOCK); + if (fd != -1) { + posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); + close(fd); + } + } +} +#else +static void +pkg_files_optimize_load(struct pkg_array *array) +{ +} +#endif + +void ensure_allinstfiles_available(void) { + struct pkg_array array; + struct progress progress; + int i; + + if (allpackagesdone) return; + if (saidread < PKG_FILESDB_LOAD_DONE) { + int max = pkg_hash_count_pkg(); + + saidread = PKG_FILESDB_LOAD_INPROGRESS; + progress_init(&progress, _("(Reading database ... "), max); + } + + pkg_array_init_from_hash(&array); + + pkg_files_optimize_load(&array); + + for (i = 0; i < array.n_pkgs; i++) { + struct pkginfo *pkg = array.pkgs[i]; + + ensure_packagefiles_available(pkg); + + if (saidread == PKG_FILESDB_LOAD_INPROGRESS) + progress_step(&progress); + } + + pkg_array_destroy(&array); + + allpackagesdone = true; + + if (saidread == PKG_FILESDB_LOAD_INPROGRESS) { + progress_done(&progress); + printf(P_("%d file or directory currently installed.)\n", + "%d files and directories currently installed.)\n", + fsys_hash_entries()), + fsys_hash_entries()); + saidread = PKG_FILESDB_LOAD_DONE; + } +} + +void ensure_allinstfiles_available_quiet(void) { + saidread = PKG_FILESDB_LOAD_DONE; + ensure_allinstfiles_available(); +} + +/* + * If mask is nonzero, will not write any file whose fsys_namenode + * has any flag bits set in mask. + */ +void +write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask) +{ + struct atomic_file *file; + struct fsys_namenode_list *node; + const char *listfile; + + listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE); + + file = atomic_file_new(listfile, 0); + atomic_file_open(file); + + for (node = list; node; node = node->next) { + if (!(mask && (node->namenode->flags & mask))) { + fputs(node->namenode->name, file->fp); + putc('\n', file->fp); + } + } + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + dir_sync_path(pkg_infodb_get_dir()); + + note_must_reread_files_inpackage(pkg); +} diff --git a/lib/dpkg/db-fsys-override.c b/lib/dpkg/db-fsys-override.c new file mode 100644 index 0000000..b74f6cb --- /dev/null +++ b/lib/dpkg/db-fsys-override.c @@ -0,0 +1,268 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-override.c - management of filesystem stat overrides database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fdio.h> +#include <dpkg/debug.h> +#include <dpkg/db-fsys.h> + +static char *statoverridename; + +uid_t +statdb_parse_uid(const char *str) +{ + char *endptr; + uid_t uid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride uid %s"), str); + uid = (uid_t)value; + } else { + const struct passwd *pw = getpwnam(str); + + if (pw == NULL) + uid = (uid_t)-1; + else + uid = pw->pw_uid; + } + + return uid; +} + +gid_t +statdb_parse_gid(const char *str) +{ + char *endptr; + gid_t gid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride gid %s"), str); + gid = (gid_t)value; + } else { + const struct group *gr = getgrnam(str); + + if (gr == NULL) + gid = (gid_t)-1; + else + gid = gr->gr_gid; + } + + return gid; +} + +mode_t +statdb_parse_mode(const char *str) +{ + char *endptr; + long int mode; + + mode = strtol(str, &endptr, 8); + if (str == endptr || *endptr || mode < 0 || mode > 07777) + ohshit(_("invalid statoverride mode %s"), str); + + return (mode_t)mode; +} + +void +ensure_statoverrides(enum statdb_parse_flags flags) +{ + static struct stat sb_prev; + struct stat sb_next; + static FILE *file_prev; + FILE *file; + char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; + struct file_stat *fso; + struct fsys_namenode *fnn; + struct fsys_hash_iter *iter; + + if (statoverridename == NULL) + statoverridename = dpkg_db_get_path(STATOVERRIDEFILE); + + onerr_abort++; + + file = fopen(statoverridename, "r"); + if (!file) { + if (errno != ENOENT) + ohshite(_("failed to open statoverride file")); + } else { + setcloexec(fileno(file), statoverridename); + + if (fstat(fileno(file), &sb_next)) + ohshite(_("failed to fstat statoverride file")); + + /* + * We need to keep the database file open so that the + * filesystem cannot reuse the inode number (f.ex. during + * multiple dpkg-statoverride invocations in a maintainer + * script), otherwise the following check might turn true, + * and we would skip reloading a modified database. + */ + if (file_prev && + sb_prev.st_dev == sb_next.st_dev && + sb_prev.st_ino == sb_next.st_ino) { + fclose(file); + onerr_abort--; + debug(dbg_general, "%s: same, skipping", __func__); + return; + } + sb_prev = sb_next; + } + if (file_prev) + fclose(file_prev); + file_prev = file; + + /* Reset statoverride information. */ + iter = fsys_hash_iter_new(); + while ((fnn = fsys_hash_iter_next(iter))) + fnn->statoverride = NULL; + fsys_hash_iter_free(iter); + + if (!file) { + onerr_abort--; + debug(dbg_general, "%s: none, resetting", __func__); + return; + } + debug(dbg_general, "%s: new, (re)loading", __func__); + + /* If the statoverride list is empty we don't need to bother + * reading it. */ + if (!sb_next.st_size) { + onerr_abort--; + return; + } + + loaded_list = m_malloc(sb_next.st_size); + loaded_list_end = loaded_list + sb_next.st_size; + + if (fd_read(fileno(file), loaded_list, sb_next.st_size) < 0) + ohshite(_("reading statoverride file '%.250s'"), statoverridename); + + thisline = loaded_list; + while (thisline < loaded_list_end) { + fso = nfmalloc(sizeof(*fso)); + + ptr = memchr(thisline, '\n', loaded_list_end - thisline); + if (ptr == NULL) + ohshit(_("statoverride file is missing final newline")); + /* Where to start next time around. */ + nextline = ptr + 1; + if (ptr == thisline) + ohshit(_("statoverride file contains empty line")); + *ptr = '\0'; + + /* Extract the uid. */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->uid = statdb_parse_uid(thisline); + if (fso->uid == (uid_t)-1) + fso->uname = nfstrsave(thisline); + else + fso->uname = NULL; + + if (fso->uid == (uid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system user '%s' in statoverride file; the system user got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the gid */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->gid = statdb_parse_gid(thisline); + if (fso->gid == (gid_t)-1) + fso->gname = nfstrsave(thisline); + else + fso->gname = NULL; + + if (fso->gid == (gid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system group '%s' in statoverride file; the system group got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the mode */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->mode = statdb_parse_mode(thisline); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + fnn = fsys_hash_find_node(thisline, FHFF_NONE); + if (fnn->statoverride) + ohshit(_("multiple statoverrides present for file '%.250s'"), + thisline); + fnn->statoverride = fso; + + /* Moving on... */ + thisline = nextline; + } + + free(loaded_list); + + onerr_abort--; +} diff --git a/lib/dpkg/db-fsys.h b/lib/dpkg/db-fsys.h new file mode 100644 index 0000000..a95b29d --- /dev/null +++ b/lib/dpkg/db-fsys.h @@ -0,0 +1,80 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys.h - management of database of files installed on system + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DB_FSYS_H +#define LIBDPKG_DB_FSYS_H + +#include <dpkg/file.h> +#include <dpkg/fsys.h> + +DPKG_BEGIN_DECLS + +/* + * Data structure here is as follows: + * + * For each package we have a ‘struct fsys_namenode_list *’, the head of a list of + * files in that package. They are in ‘forwards’ order. Each entry has a + * pointer to the ‘struct fsys_namenode’. + * + * The struct fsys_namenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each fsys_namenode has a (possibly empty) list of ‘struct filepackage’, + * giving a list of the packages listing that filename. + * + * When we read files contained info about a particular package we set the + * ‘files’ member of the clientdata struct to the appropriate thing. When + * not yet set the files pointer is made to point to ‘fileslist_uninited’ + * (this is available only internally, within filesdb.c - the published + * interface is ensure_*_available). + */ + +struct pkginfo; +struct pkgbin; + +void ensure_diversions(void); + +enum statdb_parse_flags { + STATDB_PARSE_NORMAL = 0, + STATDB_PARSE_LAX = 1, +}; + +uid_t statdb_parse_uid(const char *str); +gid_t statdb_parse_gid(const char *str); +mode_t statdb_parse_mode(const char *str); +void ensure_statoverrides(enum statdb_parse_flags flags); + +#define LISTFILE "list" +#define HASHFILE "md5sums" + +void ensure_packagefiles_available(struct pkginfo *pkg); +void ensure_allinstfiles_available(void); +void ensure_allinstfiles_available_quiet(void); +void note_must_reread_files_inpackage(struct pkginfo *pkg); +void parse_filehash(struct pkginfo *pkg, struct pkgbin *pkgbin); +void write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask); +void write_filehash_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask); + +DPKG_END_DECLS + +#endif /* LIBDPKG_DB_FSYS_H */ diff --git a/lib/dpkg/dbdir.c b/lib/dpkg/dbdir.c new file mode 100644 index 0000000..e86affa --- /dev/null +++ b/lib/dpkg/dbdir.c @@ -0,0 +1,117 @@ +/* + * libdpkg - Debian packaging suite library routines + * dbdir.c - on-disk database directory functions + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> + +static char *db_dir; + +/** + * Allocate the default on-disk database directory. + * + * The directory defaults to the value from environment variable + * DPKG_ADMINDIR, and if not set the built-in default ADMINDIR. + * + * @return The database directory. + */ +static char * +dpkg_db_new_dir(void) +{ + const char *env; + char *dir; + + /* Make sure the filesystem root directory is initialized. */ + dpkg_fsys_get_dir(); + + env = getenv("DPKG_ADMINDIR"); + if (env) + dir = m_strdup(env); + else + dir = dpkg_fsys_get_path(ADMINDIR); + + return dir; +} + +/** + * Set current on-disk database directory. + * + * This function can be used to set the directory to a new value, or to + * reset it to a default value if dir is NULL. + * + * @param dir The new database directory, or NULL to set to default. + * + * @return The new database directory. + */ +const char * +dpkg_db_set_dir(const char *dir) +{ + char *dir_new; + + if (dir == NULL) + dir_new = dpkg_db_new_dir(); + else + dir_new = m_strdup(dir); + + free(db_dir); + db_dir = dir_new; + + return db_dir; +} + +/** + * Get current on-disk database directory. + * + * This function will take care of initializing the directory if it has not + * been initialized before. + * + * @return The current database directory. + */ +const char * +dpkg_db_get_dir(void) +{ + if (db_dir == NULL) + db_dir = dpkg_db_new_dir(); + + return db_dir; +} + +/** + * Get a pathname to the current on-disk database directory. + * + * This function returns an allocated string, which should be freed with + * free(2). + * + * @param pathpart The pathpart to append to the new pathname. + * + * @return The newly allocated pathname. + */ +char * +dpkg_db_get_path(const char *pathpart) +{ + return str_fmt("%s/%s", dpkg_db_get_dir(), pathpart); +} diff --git a/lib/dpkg/dbmodify.c b/lib/dpkg/dbmodify.c new file mode 100644 index 0000000..f38bece --- /dev/null +++ b/lib/dpkg/dbmodify.c @@ -0,0 +1,535 @@ +/* + * libdpkg - Debian packaging suite library routines + * dbmodify.c - routines for managing dpkg database updates + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2001 Wichert Akkerman <wichert@debian.org> + * Copyright © 2006-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/file.h> +#include <dpkg/dir.h> +#include <dpkg/triglib.h> + +static bool db_initialized; + +static enum modstatdb_rw cstatus=-1, cflags=0; +static char *lockfile; +static char *frontendlockfile; +static char *statusfile, *availablefile; +static char *importanttmpfile=NULL; +static FILE *importanttmp; +static int nextupdate; +static char *updatesdir; +static int updateslength; +static struct varbuf updatefn; +static struct varbuf_state updatefn_state; +static struct varbuf uvb; + +static int ulist_select(const struct dirent *de) { + const char *p; + int l; + for (p= de->d_name, l=0; *p; p++, l++) + if (!c_isdigit(*p)) + return 0; + if (l > IMPORTANTMAXLEN) + ohshit(_("updates directory contains file '%.250s' whose name is too long " + "(length=%d, max=%d)"), de->d_name, l, IMPORTANTMAXLEN); + if (updateslength == -1) updateslength= l; + else if (l != updateslength) + ohshit(_("updates directory contains files with different length names " + "(both %d and %d)"), l, updateslength); + return 1; +} + +static void cleanupdates(void) { + struct dirent **cdlist; + int cdn; + + parsedb(statusfile, pdb_parse_status, NULL); + + updateslength= -1; + cdn = scandir(updatesdir, &cdlist, &ulist_select, alphasort); + if (cdn == -1) { + if (errno == ENOENT) { + if (cstatus >= msdbrw_write && + dir_make_path(updatesdir, 0755) < 0) + ohshite(_("cannot create the dpkg updates directory %s"), + updatesdir); + return; + } + ohshite(_("cannot scan updates directory '%.255s'"), updatesdir); + } + + if (cdn) { + int i; + + for (i=0; i<cdn; i++) { + varbuf_rollback(&updatefn_state); + varbuf_add_str(&updatefn, cdlist[i]->d_name); + varbuf_end_str(&updatefn); + parsedb(updatefn.buf, pdb_parse_update, NULL); + } + + if (cstatus >= msdbrw_write) { + writedb(statusfile, wdb_must_sync); + + for (i=0; i<cdn; i++) { + varbuf_rollback(&updatefn_state); + varbuf_add_str(&updatefn, cdlist[i]->d_name); + varbuf_end_str(&updatefn); + if (unlink(updatefn.buf)) + ohshite(_("failed to remove incorporated update file %.255s"), + updatefn.buf); + } + + dir_sync_path(updatesdir); + } + + for (i = 0; i < cdn; i++) + free(cdlist[i]); + } + free(cdlist); + + nextupdate= 0; +} + +static void createimptmp(void) { + int i; + + onerr_abort++; + + importanttmp= fopen(importanttmpfile,"w"); + if (!importanttmp) + ohshite(_("unable to create '%.255s'"), importanttmpfile); + setcloexec(fileno(importanttmp),importanttmpfile); + for (i=0; i<512; i++) fputs("#padding\n",importanttmp); + if (ferror(importanttmp)) + ohshite(_("unable to fill %.250s with padding"),importanttmpfile); + if (fflush(importanttmp)) + ohshite(_("unable to flush %.250s after padding"), importanttmpfile); + if (fseek(importanttmp,0,SEEK_SET)) + ohshite(_("unable to seek to start of %.250s after padding"), + importanttmpfile); + + onerr_abort--; +} + +static const struct fni { + const char *suffix; + char **store; +} fnis[] = { + { + .suffix = LOCKFILE, + .store = &lockfile, + }, { + .suffix = FRONTENDLOCKFILE, + .store = &frontendlockfile, + }, { + .suffix = STATUSFILE, + .store = &statusfile, + }, { + .suffix = AVAILFILE, + .store = &availablefile, + }, { + .suffix = UPDATESDIR, + .store = &updatesdir, + }, { + .suffix = UPDATESDIR "/" IMPORTANTTMP, + .store = &importanttmpfile, + }, { + .suffix = NULL, + .store = NULL, + } +}; + +void +modstatdb_init(void) +{ + const struct fni *fnip; + + if (db_initialized) + return; + + for (fnip = fnis; fnip->suffix; fnip++) { + free(*fnip->store); + *fnip->store = dpkg_db_get_path(fnip->suffix); + } + + varbuf_init(&updatefn, strlen(updatesdir) + 1 + IMPORTANTMAXLEN); + varbuf_add_dir(&updatefn, updatesdir); + varbuf_end_str(&updatefn); + varbuf_snapshot(&updatefn, &updatefn_state); + + db_initialized = true; +} + +void +modstatdb_done(void) +{ + const struct fni *fnip; + + if (!db_initialized) + return; + + for (fnip = fnis; fnip->suffix; fnip++) { + free(*fnip->store); + *fnip->store = NULL; + } + varbuf_destroy(&updatefn); + + db_initialized = false; +} + +static int dblockfd = -1; +static int frontendlockfd = -1; + +bool +modstatdb_is_locked(void) +{ + int lockfd; + bool locked; + + if (dblockfd == -1) { + lockfd = open(lockfile, O_RDONLY); + if (lockfd == -1) { + if (errno == ENOENT) + return false; + ohshite(_("unable to check lock file for dpkg database directory %s"), + dpkg_db_get_dir()); + } + } else { + lockfd = dblockfd; + } + + locked = file_is_locked(lockfd, lockfile); + + /* We only close the file if there was no lock open, otherwise we would + * release the existing lock on close. */ + if (dblockfd == -1) + close(lockfd); + + return locked; +} + +bool +modstatdb_can_lock(void) +{ + if (dblockfd >= 0) + return true; + + if (getenv("DPKG_FRONTEND_LOCKED") == NULL) { + frontendlockfd = open(frontendlockfile, O_RDWR | O_CREAT | O_TRUNC, 0660); + if (frontendlockfd == -1) { + if (errno == EACCES || errno == EPERM) + return false; + else + ohshite(_("unable to open/create dpkg frontend lock for directory %s"), + dpkg_db_get_dir()); + } + } else { + frontendlockfd = -1; + } + + dblockfd = open(lockfile, O_RDWR | O_CREAT | O_TRUNC, 0660); + if (dblockfd == -1) { + if (errno == EACCES || errno == EPERM) + return false; + else + ohshite(_("unable to open/create dpkg database lock file for directory %s"), + dpkg_db_get_dir()); + } + + return true; +} + +void +modstatdb_lock(void) +{ + if (!modstatdb_can_lock()) + ohshit(_("you do not have permission to lock the dpkg database directory %s"), + dpkg_db_get_dir()); + + if (frontendlockfd != -1) + file_lock(&frontendlockfd, FILE_LOCK_NOWAIT, frontendlockfile, + _("dpkg frontend lock")); + file_lock(&dblockfd, FILE_LOCK_NOWAIT, lockfile, + _("dpkg database lock")); +} + +void +modstatdb_unlock(void) +{ + /* Unlock. */ + pop_cleanup(ehflag_normaltidy); + if (frontendlockfd != -1) + pop_cleanup(ehflag_normaltidy); + + dblockfd = -1; + frontendlockfd = -1; +} + +enum modstatdb_rw +modstatdb_open(enum modstatdb_rw readwritereq) +{ + bool db_can_access = false; + + modstatdb_init(); + + cflags = readwritereq & msdbrw_available_mask; + readwritereq &= ~msdbrw_available_mask; + + switch (readwritereq) { + case msdbrw_needsuperuser: + case msdbrw_needsuperuserlockonly: + if (getuid() || geteuid()) + ohshit(_("requested operation requires superuser privilege")); + /* Fall through. */ + case msdbrw_write: case msdbrw_writeifposs: + db_can_access = access(dpkg_db_get_dir(), W_OK) == 0; + if (!db_can_access && errno == ENOENT) { + if (dir_make_path(dpkg_db_get_dir(), 0755) == 0) + db_can_access = true; + else if (readwritereq >= msdbrw_write) + ohshite(_("cannot create the dpkg database directory %s"), + dpkg_db_get_dir()); + else if (errno == EROFS) + /* If we cannot create the directory on read-only modes on read-only + * filesystems, make it look like an access error to be skipped. */ + errno = EACCES; + } + + if (!db_can_access) { + if (errno != EACCES) + ohshite(_("unable to access the dpkg database directory %s"), + dpkg_db_get_dir()); + else if (readwritereq >= msdbrw_write) + ohshit(_("required read/write access to the dpkg database directory %s"), + dpkg_db_get_dir()); + cstatus= msdbrw_readonly; + } else { + modstatdb_lock(); + cstatus= (readwritereq == msdbrw_needsuperuserlockonly ? + msdbrw_needsuperuserlockonly : + msdbrw_write); + } + break; + case msdbrw_readonly: + cstatus= msdbrw_readonly; break; + default: + internerr("unknown modstatdb_rw '%d'", readwritereq); + } + + dpkg_arch_load_list(); + + if (cstatus != msdbrw_needsuperuserlockonly) { + cleanupdates(); + if (cflags >= msdbrw_available_readonly) + parsedb(availablefile, pdb_parse_available, NULL); + } + + if (cstatus >= msdbrw_write) { + createimptmp(); + varbuf_init(&uvb, 10240); + } + + trig_fixup_awaiters(cstatus); + trig_incorporate(cstatus); + + return cstatus; +} + +enum modstatdb_rw +modstatdb_get_status(void) +{ + return cstatus; +} + +void modstatdb_checkpoint(void) { + int i; + + if (cstatus < msdbrw_write) + internerr("modstatdb status '%d' is not writable", cstatus); + + writedb(statusfile, wdb_must_sync); + + for (i=0; i<nextupdate; i++) { + varbuf_rollback(&updatefn_state); + varbuf_printf(&updatefn, IMPORTANTFMT, i); + + /* Have we made a real mess? */ + if (varbuf_rollback_len(&updatefn_state) > IMPORTANTMAXLEN) + internerr("modstatdb update entry name '%s' longer than %d", + varbuf_rollback_start(&updatefn_state), IMPORTANTMAXLEN); + + if (unlink(updatefn.buf)) + ohshite(_("failed to remove my own update file %.255s"), updatefn.buf); + } + + dir_sync_path(updatesdir); + + nextupdate= 0; +} + +void modstatdb_shutdown(void) { + if (cflags >= msdbrw_available_write) + writedb(availablefile, wdb_dump_available); + + switch (cstatus) { + case msdbrw_write: + modstatdb_checkpoint(); + /* Tidy up a bit, but don't worry too much about failure. */ + fclose(importanttmp); + (void)unlink(importanttmpfile); + varbuf_destroy(&uvb); + /* Fall through. */ + case msdbrw_needsuperuserlockonly: + modstatdb_unlock(); + default: + break; + } + + pkg_hash_reset(); + + modstatdb_done(); +} + +static void +modstatdb_note_core(struct pkginfo *pkg) +{ + if (cstatus < msdbrw_write) + internerr("modstatdb status '%d' is not writable", cstatus); + + varbuf_reset(&uvb); + varbuf_stanza(&uvb, pkg, &pkg->installed); + + if (fwrite(uvb.buf, 1, uvb.used, importanttmp) != uvb.used) + ohshite(_("unable to write updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + if (fflush(importanttmp)) + ohshite(_("unable to flush updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + if (ftruncate(fileno(importanttmp), uvb.used)) + ohshite(_("unable to truncate for updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + if (fsync(fileno(importanttmp))) + ohshite(_("unable to fsync updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + if (fclose(importanttmp)) + ohshite(_("unable to close updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + varbuf_rollback(&updatefn_state); + varbuf_printf(&updatefn, IMPORTANTFMT, nextupdate); + if (rename(importanttmpfile, updatefn.buf)) + ohshite(_("unable to install updated status of '%.250s'"), + pkg_name(pkg, pnaw_nonambig)); + + dir_sync_path(updatesdir); + + /* Have we made a real mess? */ + if (varbuf_rollback_len(&updatefn_state) > IMPORTANTMAXLEN) + internerr("modstatdb update entry name '%s' longer than %d", + varbuf_rollback_start(&updatefn_state), IMPORTANTMAXLEN); + + nextupdate++; + + if (nextupdate > MAXUPDATES) { + modstatdb_checkpoint(); + nextupdate = 0; + } + + createimptmp(); +} + +/* + * Note: If anyone wants to set some triggers-pending, they must also + * set status appropriately, or we will undo it. That is, it is legal + * to call this when pkg->status and pkg->trigpend_head disagree and + * in that case pkg->status takes precedence and pkg->trigpend_head + * will be adjusted. + */ +void modstatdb_note(struct pkginfo *pkg) { + struct trigaw *ta; + + onerr_abort++; + + /* Clear pending triggers here so that only code that sets the status + * to interesting (for triggers) values has to care about triggers. */ + if (pkg->status != PKG_STAT_TRIGGERSPENDING && + pkg->status != PKG_STAT_TRIGGERSAWAITED) + pkg->trigpend_head = NULL; + + if (pkg->status <= PKG_STAT_CONFIGFILES) { + for (ta = pkg->trigaw.head; ta; ta = ta->sameaw.next) + ta->aw = NULL; + pkg->trigaw.head = pkg->trigaw.tail = NULL; + } + + if (pkg->status_dirty) { + log_message("status %s %s %s", pkg_status_name(pkg), + pkg_name(pkg, pnaw_always), + versiondescribe_c(&pkg->installed.version, vdew_nonambig)); + statusfd_send("status: %s: %s", pkg_name(pkg, pnaw_nonambig), + pkg_status_name(pkg)); + + pkg->status_dirty = false; + } + + if (cstatus >= msdbrw_write) + modstatdb_note_core(pkg); + + if (!pkg->trigpend_head && pkg->othertrigaw_head) { + /* Automatically remove us from other packages' Triggers-Awaited. + * We do this last because we want to maximize our chances of + * successfully recording the status of the package we were + * pointed at by our caller, although there is some risk of + * leaving us in a slightly odd situation which is cleared up + * by the trigger handling logic in deppossi_ok_found. */ + trig_clear_awaiters(pkg); + } + + onerr_abort--; +} + +void +modstatdb_note_ifwrite(struct pkginfo *pkg) +{ + if (cstatus >= msdbrw_write) + modstatdb_note(pkg); +} + diff --git a/lib/dpkg/deb-version.c b/lib/dpkg/deb-version.c new file mode 100644 index 0000000..cee5ddd --- /dev/null +++ b/lib/dpkg/deb-version.c @@ -0,0 +1,87 @@ +/* + * libdpkg - Debian packaging suite library routines + * deb-version.c - deb format version handling routines + * + * Copyright © 2012-2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <limits.h> +#include <string.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/deb-version.h> + +/** + * Parse a .deb format version. + * + * It takes a string and parses a .deb archive format version in the form + * of "X.Y", without any leading whitespace, and ending in either a newline + * or a NUL. If there is any syntax error a descriptive error string is + * returned. + * + * @param version The version to return. + * @param str The string to parse. + * + * @return An error string, or NULL if there was no error. + */ +const char * +deb_version_parse(struct deb_version *version, const char *str) +{ + const char *str_minor, *end; + unsigned int major = 0; + unsigned int minor = 0; + unsigned int divlimit = INT_MAX / 10; + int modlimit = INT_MAX % 10; + + for (end = str; *end && c_isdigit(*end); end++) { + int ord = *end - '0'; + + if (major > divlimit || (major == divlimit && ord > modlimit)) + return _("format version with too big major component"); + + major = major * 10 + ord; + } + + if (end == str) + return _("format version with empty major component"); + if (*end != '.') + return _("format version has no dot"); + + for (end = str_minor = end + 1; *end && c_isdigit(*end); end++) { + int ord = *end - '0'; + + if (minor > divlimit || (minor == divlimit && ord > modlimit)) + return _("format version with too big minor component"); + + minor = minor * 10 + ord; + } + + if (end == str_minor) + return _("format version with empty minor component"); + if (*end != '\n' && *end != '\0') + return _("format version followed by junk"); + + version->major = major; + version->minor = minor; + + return NULL; +} diff --git a/lib/dpkg/deb-version.h b/lib/dpkg/deb-version.h new file mode 100644 index 0000000..620d23d --- /dev/null +++ b/lib/dpkg/deb-version.h @@ -0,0 +1,60 @@ +/* + * libdpkg - Debian packaging suite library routines + * deb-version.h - deb format version handling routines + * + * Copyright © 2012-2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DEB_VERSION_H +#define LIBDPKG_DEB_VERSION_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup deb-version .deb format version handling + * @ingroup dpkg-public + * @{ + */ + +/** + * Data structure representing a .deb format version. + */ +struct deb_version { + int major; + int minor; +}; + +/** + * Constant initializer for a deb_version. + */ +#define DEB_VERSION(X, Y) \ + { .major = (X), .minor = (Y) } + +/** + * Compound literal for a deb_version. + */ +#define DEB_VERSION_OBJECT(X, Y) \ + (struct deb_version)DEB_VERSION(X, Y) + +const char *deb_version_parse(struct deb_version *version, const char *str); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_DEB_VERSION_H */ diff --git a/lib/dpkg/debug.c b/lib/dpkg/debug.c new file mode 100644 index 0000000..115d455 --- /dev/null +++ b/lib/dpkg/debug.c @@ -0,0 +1,133 @@ +/* + * libdpkg - Debian packaging suite library routines + * debug.c - debugging support + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/report.h> +#include <dpkg/debug.h> + +static int debug_mask = 0; +static FILE *debug_output = NULL; + +/** + * Set the debugging output file. + * + * Marks the file descriptor as close-on-exec. + */ +void +debug_set_output(FILE *output, const char *filename) +{ + setcloexec(fileno(output), filename); + dpkg_set_report_buffer(output); + debug_output = output; +} + +/** + * Set the debugging mask. + * + * The mask determines what debugging flags are going to take effect at + * run-time. The output will be set to stderr if it has not been set before. + */ +void +debug_set_mask(int mask) +{ + debug_mask = mask; + if (!debug_output) + debug_output = stderr; +} + +/** + * Parse the debugging mask. + * + * The mask is parsed from the specified string and sets the global debugging + * mask. If there is any error while parsing a negative number is returned. + */ +int +debug_parse_mask(const char *str) +{ + char *endp; + long mask; + + errno = 0; + mask = strtol(str, &endp, 8); + if (str == endp || *endp || mask < 0 || errno == ERANGE) + return -1; + + debug_set_mask(mask); + + return mask; +} + +/** + * Check if a debugging flag is currently set on the debugging mask. + */ +bool +debug_has_flag(int flag) +{ + return debug_mask & flag; +} + +/** + * Output a debugging message. + * + * The message will be printed to the previously specified output if the + * specified flag is present in the current debugging mask. + */ +void +debug(int flag, const char *fmt, ...) +{ + va_list args; + + if (!debug_has_flag(flag)) + return; + + fprintf(debug_output, "D0%05o: ", flag); + va_start(args, fmt); + vfprintf(debug_output, fmt, args); + va_end(args); + putc('\n', debug_output); +} + +/** + * Initialize the debugging support. + */ +void +dpkg_debug_init(void) +{ + const char envvar[] = "DPKG_DEBUG"; + const char *env; + + env = getenv(envvar); + if (env == NULL) + return; + + if (debug_parse_mask(env) < 0) + warning(_("cannot parse debug mask from environment variable %s"), + envvar); +} diff --git a/lib/dpkg/debug.h b/lib/dpkg/debug.h new file mode 100644 index 0000000..be4efac --- /dev/null +++ b/lib/dpkg/debug.h @@ -0,0 +1,68 @@ +/* + * libdpkg - Debian packaging suite library routines + * debug.h - debugging support + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DEBUG_H +#define LIBDPKG_DEBUG_H + +#include <dpkg/macros.h> + +#include <stdbool.h> +#include <stdio.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup debug Debugging + * @ingroup dpkg-internal + * @{ + */ + +/* + * XXX: We do not use DPKG_BIT() here, because the octal values are part + * of the current output interface. + */ +enum DPKG_ATTR_ENUM_FLAGS debugflags { + dbg_general = 01, + dbg_scripts = 02, + dbg_eachfile = 010, + dbg_eachfiledetail = 0100, + dbg_conff = 020, + dbg_conffdetail = 0200, + dbg_depcon = 040, + dbg_depcondetail = 0400, + dbg_veryverbose = 01000, + dbg_stupidlyverbose = 02000, + dbg_triggers = 010000, + dbg_triggersdetail = 020000, + dbg_triggersstupid = 040000, +}; + +void dpkg_debug_init(void); +void debug_set_output(FILE *output, const char *filename); +void debug_set_mask(int mask); +int debug_parse_mask(const char *str); +bool debug_has_flag(int flag); +void debug(int flag, const char *fmt, ...) DPKG_ATTR_PRINTF(2); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_DEBUG_H */ diff --git a/lib/dpkg/depcon.c b/lib/dpkg/depcon.c new file mode 100644 index 0000000..488cd6b --- /dev/null +++ b/lib/dpkg/depcon.c @@ -0,0 +1,125 @@ +/* + * libdpkg - Debian packaging suite library routines + * depcon.c - dependency and conflict checking + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * Copyright © 2009 Canonical Ltd. + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> + +bool +versionsatisfied(struct pkgbin *it, struct deppossi *against) +{ + return dpkg_version_relate(&it->version, + against->verrel, + &against->version); +} + +/** + * Check if the architecture qualifier in the dependency is satisfied. + * + * The rules are supposed to be: + * - unqualified Depends/Pre-Depends/Recommends/Suggests are only + * satisfied by a package of a different architecture if the target + * package is Multi-Arch: foreign. + * - Depends/Pre-Depends/Recommends/Suggests on pkg:any are satisfied by + * a package of a different architecture if the target package is + * Multi-Arch: allowed. + * - all other Depends/Pre-Depends/Recommends/Suggests are only + * satisfied by packages of the same architecture. + * - Architecture: all packages are treated the same as packages of the + * native architecture. + * - Conflicts/Replaces/Breaks are assumed to apply to packages of any arch. + */ +bool +deparchsatisfied(struct pkgbin *it, const struct dpkg_arch *it_arch, + struct deppossi *against) +{ + const struct dpkg_arch *dep_arch, *pkg_arch; + + if (against->arch_is_implicit && + it->multiarch == PKG_MULTIARCH_FOREIGN) + return true; + + dep_arch = against->arch; + if (dep_arch->type == DPKG_ARCH_WILDCARD && + (it->multiarch == PKG_MULTIARCH_ALLOWED || + against->up->type == dep_conflicts || + against->up->type == dep_replaces || + against->up->type == dep_breaks)) + return true; + + pkg_arch = it_arch; + if (dep_arch->type == DPKG_ARCH_NONE || dep_arch->type == DPKG_ARCH_ALL) + dep_arch = dpkg_arch_get(DPKG_ARCH_NATIVE); + if (pkg_arch->type == DPKG_ARCH_NONE || pkg_arch->type == DPKG_ARCH_ALL) + pkg_arch = dpkg_arch_get(DPKG_ARCH_NATIVE); + + return (dep_arch == pkg_arch); +} + +bool +archsatisfied(struct pkgbin *it, struct deppossi *against) +{ + return deparchsatisfied(it, it->arch, against); +} + +/** + * Check if the dependency is satisfied by a virtual package. + * + * For versioned depends, we only check providers with #DPKG_RELATION_EQ. It + * does not make sense to check ones without a version since we have nothing + * to verify against. Also, it is way too complex to allow anything but an + * equal in a provided version. A few examples below to deter you from trying: + * + * - pkg1 depends on virt (>= 0.6), pkg2 provides virt (<= 1.0). + * Should pass (easy enough). + * + * - pkg1 depends on virt (>= 0.7) and (<= 1.1), pkg2 provides virt (>= 1.2). + * Should fail (little harder). + * + * - pkg1 depends on virt (>= 0.4), pkg2 provides virt (<= 1.0) and (>= 0.5), + * IOW, inclusive of only those versions. This would require backchecking + * the other provided versions in the possi, which would make things sickly + * complex and overly time consuming. Should fail (very hard to implement). + * + * This could be handled by switching to a SAT solver, but that would imply + * lots of work for very little gain. Packages can easily get around most of + * these by providing multiple #DPKG_RELATION_EQ versions. + */ +bool +pkg_virtual_deppossi_satisfied(struct deppossi *dependee, + struct deppossi *provider) +{ + if (provider->verrel != DPKG_RELATION_NONE && + provider->verrel != DPKG_RELATION_EQ) + return false; + + if (provider->verrel == DPKG_RELATION_NONE && + dependee->verrel != DPKG_RELATION_NONE) + return false; + + return dpkg_version_relate(&provider->version, + dependee->verrel, + &dependee->version); +} diff --git a/lib/dpkg/dir.c b/lib/dpkg/dir.c new file mode 100644 index 0000000..f03aabc --- /dev/null +++ b/lib/dpkg/dir.c @@ -0,0 +1,213 @@ +/* + * libdpkg - Debian packaging suite library routines + * dir.c - directory handling functions + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/dir.h> + +static int +dir_make_path_noalloc(char *dirname, mode_t mode) +{ + char *slash; + + /* Find the first slash, and ignore it, as it will be either the + * slash for the root directory, for the current directory in a + * relative pathname or its parent. */ + slash = strchr(dirname, '/'); + + while (slash != NULL) { + slash = strchr(slash + 1, '/'); + if (slash) + *slash = '\0'; + + if (mkdir(dirname, mode) < 0 && errno != EEXIST) + return -1; + if (slash) + *slash = '/'; + } + + return 0; +} + +/** + * Create the directory and all its parents if necessary. + * + * @param path The pathname to create directories for. + * @param mode The pathname mode. + */ +int +dir_make_path(const char *path, mode_t mode) +{ + char *dirname; + int rc; + + dirname = m_strdup(path); + rc = dir_make_path_noalloc(dirname, mode); + free(dirname); + + return rc; +} + +int +dir_make_path_parent(const char *path, mode_t mode) +{ + char *dirname, *slash; + int rc; + + dirname = m_strdup(path); + slash = strrchr(dirname, '/'); + if (slash != NULL) { + *slash = '\0'; + rc = dir_make_path_noalloc(dirname, mode); + } else { + rc = -1; + } + free(dirname); + + return rc; +} + +/** + * Sync a directory to disk from a DIR structure. + * + * @param dir The directory to sync. + * @param path The name of the directory to sync (for error messages). + */ +static void +dir_sync(DIR *dir, const char *path) +{ +#ifdef HAVE_FSYNC_DIR + int fd; + + fd = dirfd(dir); + if (fd < 0) + ohshite(_("unable to get file descriptor for directory '%s'"), + path); + + if (fsync(fd)) + ohshite(_("unable to sync directory '%s'"), path); +#endif +} + +/** + * Sync a directory to disk from a pathname. + * + * @param path The name of the directory to sync. + */ +void +dir_sync_path(const char *path) +{ +#ifdef HAVE_FSYNC_DIR + DIR *dir; + + dir = opendir(path); + if (!dir) + ohshite(_("unable to open directory '%s'"), path); + + dir_sync(dir, path); + + closedir(dir); +#endif +} + +/** + * Sync to disk the parent directory of a pathname. + * + * @param path The child pathname of the directory to sync. + */ +void +dir_sync_path_parent(const char *path) +{ +#ifdef HAVE_FSYNC_DIR + char *dirname, *slash; + + dirname = m_strdup(path); + + slash = strrchr(dirname, '/'); + if (slash != NULL) { + *slash = '\0'; + dir_sync_path(dirname); + } + + free(dirname); +#endif +} + +/* TODO: Ideally we'd use openat() here, to avoid the path mangling, but + * it's not widely available yet, so this will do for now. */ +static void +dir_file_sync(const char *dir, const char *filename) +{ + char *path; + int fd; + + path = str_fmt("%s/%s", dir, filename); + + fd = open(path, O_WRONLY); + if (fd < 0) + ohshite(_("unable to open file '%s'"), path); + if (fsync(fd)) + ohshite(_("unable to sync file '%s'"), path); + if (close(fd)) + ohshite(_("unable to close file '%s'"), path); + + free(path); +} + +/** + * Sync to disk the contents and the directory specified. + * + * @param path The pathname to sync. + */ +void +dir_sync_contents(const char *path) +{ + DIR *dir; + struct dirent *dent; + + dir = opendir(path); + if (!dir) + ohshite(_("unable to open directory '%s'"), path); + + while ((dent = readdir(dir)) != NULL) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + dir_file_sync(path, dent->d_name); + } + + dir_sync(dir, path); + + closedir(dir); +} diff --git a/lib/dpkg/dir.h b/lib/dpkg/dir.h new file mode 100644 index 0000000..65e2f46 --- /dev/null +++ b/lib/dpkg/dir.h @@ -0,0 +1,51 @@ +/* + * libdpkg - Debian packaging suite library routines + * dir.h - directory handling routines + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DIR_H +#define LIBDPKG_DIR_H + +#include <dpkg/macros.h> + +#include <sys/types.h> + +#include <dirent.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup dir Directory handling + * @ingroup dpkg-internal + * @{ + */ + +int +dir_make_path(const char *path, mode_t mode); +int +dir_make_path_parent(const char *path, mode_t mode); + +void dir_sync_path(const char *path); +void dir_sync_path_parent(const char *path); +void dir_sync_contents(const char *path); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_DIR_H */ diff --git a/lib/dpkg/dlist.h b/lib/dpkg/dlist.h new file mode 100644 index 0000000..1d3a91e --- /dev/null +++ b/lib/dpkg/dlist.h @@ -0,0 +1,45 @@ +/* + * dlist.h - macros for handling doubly linked lists + * + * Copyright © 1997-1999 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef DPKG_DLIST_H +#define DPKG_DLIST_H + +#define LIST_UNLINK_PART(list, node, part) \ + do { \ + if ((node)->part.prev) \ + (node)->part.prev->part.next = (node)->part.next; \ + else \ + (list).head = (node)->part.next; \ + if ((node)->part.next) \ + (node)->part.next->part.prev = (node)->part.prev; \ + else \ + (list).tail = (node)->part.prev; \ + } while (0) + +#define LIST_LINK_TAIL_PART(list, node, part) \ + do { \ + (node)->part.next = NULL; \ + (node)->part.prev = (list).tail; \ + if ((list).tail) \ + (list).tail->part.next = (node); \ + else (list).head = (node); \ + (list).tail = (node); \ + } while (0) + +#endif diff --git a/lib/dpkg/dpkg-db.h b/lib/dpkg/dpkg-db.h new file mode 100644 index 0000000..61f2205 --- /dev/null +++ b/lib/dpkg/dpkg-db.h @@ -0,0 +1,505 @@ +/* + * libdpkg - Debian packaging suite library routines + * dpkg-db.h - declarations for in-core package database management + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman + * Copyright © 2006-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DPKG_DB_H +#define LIBDPKG_DPKG_DB_H + +#include <sys/types.h> + +#include <stdbool.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#include <dpkg/varbuf.h> +#include <dpkg/version.h> +#include <dpkg/arch.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup dpkg-db In-core package database management + * @ingroup dpkg-public + * @{ + */ + +enum deptype { + dep_suggests, + dep_recommends, + dep_depends, + dep_predepends, + dep_breaks, + dep_conflicts, + dep_provides, + dep_replaces, + dep_enhances +}; + +struct dependency { + struct pkginfo *up; + struct dependency *next; + struct deppossi *list; + enum deptype type; +}; + +struct deppossi { + struct dependency *up; + struct pkgset *ed; + struct deppossi *next, *rev_next, *rev_prev; + const struct dpkg_arch *arch; + struct dpkg_version version; + enum dpkg_relation verrel; + bool arch_is_implicit; + bool cyclebreak; +}; + +struct arbitraryfield { + struct arbitraryfield *next; + const char *name; + const char *value; +}; + +struct conffile { + struct conffile *next; + const char *name; + const char *hash; + bool obsolete; + bool remove_on_upgrade; +}; + +struct archivedetails { + struct archivedetails *next; + const char *name; + const char *msdosname; + const char *size; + const char *md5sum; +}; + +enum pkgmultiarch { + PKG_MULTIARCH_NO, + PKG_MULTIARCH_SAME, + PKG_MULTIARCH_ALLOWED, + PKG_MULTIARCH_FOREIGN, +}; + +/** + * Node describing a binary package file. + * + * This structure holds information contained on each binary package. + */ +struct pkgbin { + struct dependency *depends; + /** The ‘essential’ flag, true = yes, false = no (absent). */ + bool essential; + /** The ‘protected’ flag, true = yes, false = no (absent). */ + bool is_protected; + enum pkgmultiarch multiarch; + const struct dpkg_arch *arch; + /** The following is the "pkgname:archqual" cached string, if this was a + * C++ class this member would be mutable. */ + const char *pkgname_archqual; + const char *description; + const char *maintainer; + const char *source; + const char *installedsize; + const char *origin; + const char *bugs; + struct dpkg_version version; + struct conffile *conffiles; + struct arbitraryfield *arbs; +}; + +/** + * Node indicates that parent's Triggers-Pending mentions name. + * + * Note: These nodes do double duty: after they're removed from a package's + * trigpend list, references may be preserved by the trigger cycle checker + * (see trigproc.c). + */ +struct trigpend { + struct trigpend *next; + const char *name; +}; + +/** + * Node indicates that aw's Triggers-Awaited mentions pend. + */ +struct trigaw { + struct pkginfo *aw, *pend; + struct trigaw *samepend_next; + struct { + struct trigaw *next, *prev; + } sameaw; +}; + +/* Note: dselect and dpkg have different versions of this. */ +struct perpackagestate; + +enum pkgwant { + PKG_WANT_UNKNOWN, + PKG_WANT_INSTALL, + PKG_WANT_HOLD, + PKG_WANT_DEINSTALL, + PKG_WANT_PURGE, + /** Not allowed except as special sentinel value in some places. */ + PKG_WANT_SENTINEL, +}; + +enum pkgeflag { + PKG_EFLAG_OK = 0, + PKG_EFLAG_REINSTREQ = 1, +}; + +enum pkgstatus { + PKG_STAT_NOTINSTALLED, + PKG_STAT_CONFIGFILES, + PKG_STAT_HALFINSTALLED, + PKG_STAT_UNPACKED, + PKG_STAT_HALFCONFIGURED, + PKG_STAT_TRIGGERSAWAITED, + PKG_STAT_TRIGGERSPENDING, + PKG_STAT_INSTALLED, +}; + +enum pkgpriority { + PKG_PRIO_REQUIRED, + PKG_PRIO_IMPORTANT, + PKG_PRIO_STANDARD, + PKG_PRIO_OPTIONAL, + PKG_PRIO_EXTRA, + PKG_PRIO_OTHER, + PKG_PRIO_UNKNOWN, + PKG_PRIO_UNSET = -1, +}; + +/** + * Node describing an architecture package instance. + * + * This structure holds state information. + */ +struct pkginfo { + struct pkgset *set; + struct pkginfo *arch_next; + + enum pkgwant want; + /** The error flag bitmask. */ + enum pkgeflag eflag; + enum pkgstatus status; + enum pkgpriority priority; + const char *otherpriority; + const char *section; + struct dpkg_version configversion; + struct pkgbin installed; + struct pkgbin available; + struct perpackagestate *clientdata; + + struct archivedetails *archives; + + struct { + /* ->aw == this */ + struct trigaw *head, *tail; + } trigaw; + + /* ->pend == this, non-NULL for us when Triggers-Pending. */ + struct trigaw *othertrigaw_head; + struct trigpend *trigpend_head; + + /** + * files_list_valid files Meaning + * ---------------- ----- ------- + * false NULL Not read yet, must do so if want them. + * false !NULL Read, but rewritten and now out of date. If want + * info must throw away old and reread file. + * true !NULL Read, all is OK. + * true NULL Read OK, but, there were no files. + */ + struct fsys_namenode_list *files; + off_t files_list_phys_offs; + bool files_list_valid; + + /* The status has changed, it needs to be logged. */ + bool status_dirty; +}; + +/** + * Node describing a package set sharing the same package name. + */ +struct pkgset { + struct pkgset *next; + const char *name; + struct pkginfo pkg; + struct { + struct deppossi *available; + struct deppossi *installed; + } depended; + int installed_instances; +}; + +/*** from dbdir.c ***/ + +const char *dpkg_db_set_dir(const char *dir); +const char *dpkg_db_get_dir(void); +char *dpkg_db_get_path(const char *pathpart); + +#include <dpkg/atomic-file.h> + +/*** from dbmodify.c ***/ + +enum DPKG_ATTR_ENUM_FLAGS modstatdb_rw { + msdbrw_unset = -1, + + /* Those marked with «return» are possible returns from modstatdb_open(). */ + msdbrw_readonly, /* «return» */ + msdbrw_needsuperuserlockonly, /* «return» */ + msdbrw_writeifposs, + msdbrw_write, /* «return» */ + msdbrw_needsuperuser, + + /* Now some optional flags (starting at bit 8): */ + msdbrw_available_readonly = DPKG_BIT(8), + msdbrw_available_write = DPKG_BIT(9), + msdbrw_available_mask = 0x0300, +}; + +void modstatdb_init(void); +void modstatdb_done(void); +bool modstatdb_is_locked(void); +bool modstatdb_can_lock(void); +void modstatdb_lock(void); +void modstatdb_unlock(void); +enum modstatdb_rw modstatdb_open(enum modstatdb_rw reqrwflags); +enum modstatdb_rw modstatdb_get_status(void); +void modstatdb_note(struct pkginfo *pkg); +void modstatdb_note_ifwrite(struct pkginfo *pkg); +void modstatdb_checkpoint(void); +void modstatdb_shutdown(void); + +/*** from database.c ***/ + +void pkgset_blank(struct pkgset *set); +int pkgset_installed_instances(struct pkgset *set); + +void pkg_blank(struct pkginfo *pp); +void pkgbin_blank(struct pkgbin *pkgbin); +bool pkg_is_informative(struct pkginfo *pkg, struct pkgbin *info); + +struct pkgset * +pkg_hash_find_set(const char *name); +struct pkginfo * +pkg_hash_get_singleton(struct pkgset *set); +struct pkginfo * +pkg_hash_find_singleton(const char *name); +struct pkginfo * +pkg_hash_get_pkg(struct pkgset *set, const struct dpkg_arch *arch); +struct pkginfo * +pkg_hash_find_pkg(const char *name, const struct dpkg_arch *arch); +int +pkg_hash_count_set(void); +int +pkg_hash_count_pkg(void); +void +pkg_hash_reset(void); + +struct pkg_hash_iter * +pkg_hash_iter_new(void); +struct pkgset * +pkg_hash_iter_next_set(struct pkg_hash_iter *iter); +struct pkginfo * +pkg_hash_iter_next_pkg(struct pkg_hash_iter *iter); +void +pkg_hash_iter_free(struct pkg_hash_iter *iter); + +void +pkg_hash_report(FILE *); + +/*** from parse.c ***/ + +enum DPKG_ATTR_ENUM_FLAGS parsedbflags { + /** Parse a single control stanza. */ + pdb_single_stanza = DPKG_BIT(0), + /** Store in ‘available’ in-core structures, not ‘status’. */ + pdb_recordavailable = DPKG_BIT(1), + /** Throw up an error if ‘Status’ encountered. */ + pdb_rejectstatus = DPKG_BIT(2), + /** Ignore priority/section info if we already have any. */ + pdb_weakclassification = DPKG_BIT(3), + /** Ignore archives info if we already have them. */ + pdb_ignore_archives = DPKG_BIT(4), + /** Ignore packages with older versions already read. */ + pdb_ignoreolder = DPKG_BIT(5), + /** Perform laxer version parsing. */ + pdb_lax_version_parser = DPKG_BIT(6), + /** Perform laxer control stanza parsing. */ + pdb_lax_stanza_parser = DPKG_BIT(7), + /** Perform laxer parsing, used to transition to stricter parsing. */ + pdb_lax_parser = pdb_lax_stanza_parser | pdb_lax_version_parser, + /** Close file descriptor on context destruction. */ + pdb_close_fd = DPKG_BIT(8), + /** Interpret filename ‘-’ as stdin. */ + pdb_dash_is_stdin = DPKG_BIT(9), + /** Allow empty/missing files. */ + pdb_allow_empty = DPKG_BIT(10), + + /* Standard operations. */ + + pdb_parse_status = pdb_lax_parser | pdb_weakclassification | + pdb_allow_empty, + pdb_parse_update = pdb_parse_status | pdb_single_stanza, + pdb_parse_available = pdb_recordavailable | pdb_rejectstatus | + pdb_lax_parser | pdb_allow_empty, + pdb_parse_binary = pdb_recordavailable | pdb_rejectstatus | + pdb_single_stanza, +}; + +const char *pkg_name_is_illegal(const char *p); + +const struct fieldinfo * +find_field_info(const struct fieldinfo *fields, const char *fieldname); +const struct arbitraryfield * +find_arbfield_info(const struct arbitraryfield *arbs, const char *fieldname); + +int parsedb(const char *filename, enum parsedbflags, struct pkginfo **donep); +void copy_dependency_links(struct pkginfo *pkg, + struct dependency **updateme, + struct dependency *newdepends, + bool available); + +/*** from parsehelp.c ***/ + +#include <dpkg/namevalue.h> + +extern const struct namevalue booleaninfos[]; +extern const struct namevalue multiarchinfos[]; +extern const struct namevalue priorityinfos[]; +extern const struct namevalue statusinfos[]; +extern const struct namevalue eflaginfos[]; +extern const struct namevalue wantinfos[]; + +#include <dpkg/error.h> + +enum versiondisplayepochwhen { + vdew_never, + vdew_nonambig, + vdew_always, +}; +void varbufversion(struct varbuf *, const struct dpkg_version *, + enum versiondisplayepochwhen); +int parseversion(struct dpkg_version *version, const char *, + struct dpkg_error *err); +const char *versiondescribe(const struct dpkg_version *, + enum versiondisplayepochwhen); +const char *versiondescribe_c(const struct dpkg_version *version, + enum versiondisplayepochwhen vdew); + +enum pkg_name_arch_when { + /** Never display arch. */ + pnaw_never, + /** Display arch only when it's non-ambiguous. */ + pnaw_nonambig, + /** Display arch only when it's a Multi-Arch same package. */ + pnaw_same, + /** Display arch only when it's a foreign one. */ + pnaw_foreign, + /** Always display arch. */ + pnaw_always, +}; + +void varbuf_add_pkgbin_name(struct varbuf *vb, const struct pkginfo *pkg, + const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw); + +const char * +pkgbin_name_archqual(const struct pkginfo *pkg, const struct pkgbin *pkgbin); + +const char * +pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw); +const char * +pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw); + +const char * +pkgbin_name_const(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw); +const char * +pkg_name_const(const struct pkginfo *pkg, enum pkg_name_arch_when pnaw); + +void +pkg_source_version(struct dpkg_version *version, + const struct pkginfo *pkg, const struct pkgbin *pkgbin); + +void +varbuf_add_source_version(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin); + +const char *pkg_want_name(const struct pkginfo *pkg); +const char *pkg_status_name(const struct pkginfo *pkg); +const char *pkg_eflag_name(const struct pkginfo *pkg); + +const char *pkg_priority_name(const struct pkginfo *pkg); + +/*** from dump.c ***/ + +void +write_stanza(FILE *, const char *, + const struct pkginfo *, const struct pkgbin *); + +enum DPKG_ATTR_ENUM_FLAGS writedb_flags { + /** No flags. */ + wdb_none = 0, + /** Dump ‘available’ in-core structures, not ‘status’. */ + wdb_dump_available = DPKG_BIT(0), + /** Must sync the written file. */ + wdb_must_sync = DPKG_BIT(1), +}; + +void +writedb_stanzas(FILE *fp, const char *filename, enum writedb_flags flags); +void writedb(const char *filename, enum writedb_flags flags); + +/* Note: The varbufs must have been initialized and will not be + * NUL-terminated. */ +void +varbuf_stanza(struct varbuf *, const struct pkginfo *, + const struct pkgbin *); +void varbufdependency(struct varbuf *vb, struct dependency *dep); + +/*** from depcon.c ***/ + +bool versionsatisfied(struct pkgbin *it, struct deppossi *against); +bool deparchsatisfied(struct pkgbin *it, const struct dpkg_arch *arch, + struct deppossi *against); +bool archsatisfied(struct pkgbin *it, struct deppossi *against); + +bool +pkg_virtual_deppossi_satisfied(struct deppossi *dependee, + struct deppossi *provider); + +/*** from nfmalloc.c ***/ +void *nfmalloc(size_t); +char *nfstrsave(const char*); +char *nfstrnsave(const char*, size_t); +void nffreeall(void); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_DPKG_DB_H */ diff --git a/lib/dpkg/dpkg.h b/lib/dpkg/dpkg.h new file mode 100644 index 0000000..ee6fd3b --- /dev/null +++ b/lib/dpkg/dpkg.h @@ -0,0 +1,158 @@ +/* + * libdpkg - Debian packaging suite library routines + * dpkg.h - general header for Debian package handling + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wichert@debian.org> + * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DPKG_H +#define LIBDPKG_DPKG_H + +#include <sys/types.h> + +#include <stddef.h> +#include <stdio.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @mainpage libdpkg C API + * + * This is the documentation for the libdpkg C API. It is divided in an + * @ref dpkg-internal "internal API" and a @ref dpkg-public "public API". + * Applications closely tied to dpkg can make use of the internal API, the + * rest should only assume the availability of the public API. + * + * Applications need to define the LIBDPKG_VOLATILE_API macro to acknowledge + * that the API is to be considered volatile, please read doc/README.api for + * more information. + * + * @defgroup dpkg-internal Internal libdpkg C API + * + * @defgroup dpkg-public Public libdpkg C API + */ + +#define MAXCONFFILENAME 1000 +#define MAXDIVERTFILENAME 1024 +#define MAXCONTROLFILENAME 100 +#define DEBEXT ".deb" +#define REMOVECONFFEXTS "~", ".bak", "%", \ + DPKGTEMPEXT, DPKGNEWEXT, DPKGOLDEXT, DPKGDISTEXT + +#define NEWCONFFILEFLAG "newconffile" +#define NONEXISTENTFLAG "nonexistent" +#define EMPTYHASHFLAG "-" + +#define DPKGTEMPEXT ".dpkg-tmp" +#define DPKGNEWEXT ".dpkg-new" +#define DPKGOLDEXT ".dpkg-old" +#define DPKGDISTEXT ".dpkg-dist" + +#define CONTROLFILE "control" +#define CONFFILESFILE "conffiles" +#define PREINSTFILE "preinst" +#define POSTINSTFILE "postinst" +#define PRERMFILE "prerm" +#define POSTRMFILE "postrm" +/* Debconf config maintainer script. */ +#define MAINTSCRIPT_FILE_CONFIG "config" +#define TRIGGERSCIFILE "triggers" + +#define STATUSFILE "status" +#define AVAILFILE "available" +#define LOCKFILE "lock" +#define FRONTENDLOCKFILE "lock-frontend" +#define DIVERSIONSFILE "diversions" +#define STATOVERRIDEFILE "statoverride" +#define UPDATESDIR "updates" +#define INFODIR "info" +#define TRIGGERSDIR "triggers" +#define TRIGGERSFILEFILE "File" +#define TRIGGERSDEFERREDFILE "Unincorp" +#define TRIGGERSLOCKFILE "Lock" +#define CONTROLDIRTMP "tmp.ci" +#define IMPORTANTTMP "tmp.i" +#define REASSEMBLETMP "reassemble" DEBEXT +#define IMPORTANTMAXLEN 10 +#define IMPORTANTFMT "%04d" +#define MAXUPDATES 250 + +#define MD5HASHLEN 32 +#define MAXTRIGDIRECTIVE 256 + +#define BACKEND "dpkg-deb" +#define SPLITTER "dpkg-split" +#define DPKGQUERY "dpkg-query" +#define DPKGDIVERT "dpkg-divert" +#define DPKGSTAT "dpkg-statoverride" +#define DPKGTRIGGER "dpkg-trigger" +#define DPKG "dpkg" +#define DEBSIGVERIFY "debsig-verify" + +#define RM "rm" +#define CAT "cat" +#define DIFF "diff" + +#include <dpkg/progname.h> +#include <dpkg/ehandle.h> +#include <dpkg/report.h> +#include <dpkg/string.h> +#include <dpkg/program.h> + +/*** log.c ***/ + +extern const char *log_file; +void log_message(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + +void statusfd_add(int fd); +void statusfd_send(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + +/*** cleanup.c ***/ + +void cu_closestream(int argc, void **argv); +void cu_closepipe(int argc, void **argv); +void cu_closedir(int argc, void **argv); +void cu_closefd(int argc, void **argv); +void cu_filename(int argc, void **argv); + +/*** from mustlib.c ***/ + +void setcloexec(int fd, const char *fn); +void *m_malloc(size_t); +void *m_calloc(size_t nmemb, size_t size); +void *m_realloc(void *, size_t); +char *m_strdup(const char *str); +char *m_strndup(const char *str, size_t n); +int m_asprintf(char **strp, const char *fmt, ...) DPKG_ATTR_PRINTF(2); +int m_vasprintf(char **strp, const char *fmt, va_list args) + DPKG_ATTR_VPRINTF(2); +int m_dup(int oldfd); +void m_dup2(int oldfd, int newfd); +void m_pipe(int fds[2]); +void m_output(FILE *f, const char *name); + +/*** from utils.c ***/ + +int fgets_checked(char *buf, size_t bufsz, FILE *f, const char *fn); +int fgets_must(char *buf, size_t bufsz, FILE *f, const char *fn); + +DPKG_END_DECLS + +#endif /* LIBDPKG_DPKG_H */ diff --git a/lib/dpkg/dump.c b/lib/dpkg/dump.c new file mode 100644 index 0000000..b27e5db --- /dev/null +++ b/lib/dpkg/dump.c @@ -0,0 +1,561 @@ +/* + * libdpkg - Debian packaging suite library routines + * dump.c - code to write in-core database to a file + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2001 Wichert Akkerman + * Copyright © 2006,2008-2014 Guillem Jover <guillem@debian.org> + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-show.h> +#include <dpkg/string.h> +#include <dpkg/dir.h> +#include <dpkg/parsedump.h> + +static inline void +varbuf_add_fieldname(struct varbuf *vb, const struct fieldinfo *fip) +{ + varbuf_add_str(vb, fip->name); + varbuf_add_str(vb, ": "); +} + +void +w_name(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkg->set->name == NULL) + internerr("pkgset has no name"); + + if (flags&fw_printheader) + varbuf_add_str(vb, "Package: "); + varbuf_add_str(vb, pkg->set->name); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_version(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (!dpkg_version_is_informative(&pkgbin->version)) + return; + if (flags&fw_printheader) + varbuf_add_str(vb, "Version: "); + varbufversion(vb, &pkgbin->version, vdew_nonambig); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_configversion(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + if (!dpkg_version_is_informative(&pkg->configversion)) + return; + if (pkg->status == PKG_STAT_INSTALLED || + pkg->status == PKG_STAT_NOTINSTALLED || + pkg->status == PKG_STAT_TRIGGERSPENDING) + return; + if (flags&fw_printheader) + varbuf_add_str(vb, "Config-Version: "); + varbufversion(vb, &pkg->configversion, vdew_nonambig); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_null(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ +} + +void +w_section(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *value = pkg->section; + + if (str_is_unset(value)) + return; + if (flags&fw_printheader) + varbuf_add_str(vb, "Section: "); + varbuf_add_str(vb, value); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_charfield(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *value = STRUCTFIELD(pkgbin, fip->integer, const char *); + + if (str_is_unset(value)) + return; + if (flags & fw_printheader) + varbuf_add_fieldname(vb, fip); + varbuf_add_str(vb, value); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_archives(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct archivedetails *archive; + + if (pkgbin != &pkg->available) + return; + archive = pkg->archives; + if (!archive || !STRUCTFIELD(archive, fip->integer, const char *)) + return; + + if (flags&fw_printheader) { + varbuf_add_str(vb, fip->name); + varbuf_add_char(vb, ':'); + } + + while (archive) { + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, STRUCTFIELD(archive, fip->integer, const char *)); + archive = archive->next; + } + + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_booleandefno(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + bool value = STRUCTFIELD(pkgbin, fip->integer, bool); + + if ((flags & fw_printheader) && !value) + return; + + if (flags & fw_printheader) + varbuf_add_fieldname(vb, fip); + + varbuf_add_str(vb, value ? "yes" : "no"); + + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_multiarch(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + int value = STRUCTFIELD(pkgbin, fip->integer, int); + + if ((flags & fw_printheader) && !value) + return; + + if (flags & fw_printheader) + varbuf_add_fieldname(vb, fip); + + varbuf_add_str(vb, multiarchinfos[value].name); + + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_architecture(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (!pkgbin->arch) + return; + if (pkgbin->arch->type == DPKG_ARCH_NONE) + return; + if (pkgbin->arch->type == DPKG_ARCH_EMPTY) + return; + + if (flags & fw_printheader) + varbuf_add_fieldname(vb, fip); + varbuf_add_str(vb, pkgbin->arch->name); + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_priority(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkg->priority == PKG_PRIO_UNKNOWN) + return; + + if (pkg->priority > PKG_PRIO_UNKNOWN) + internerr("package %s has out-of-range priority %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->priority); + + if (flags&fw_printheader) + varbuf_add_str(vb, "Priority: "); + varbuf_add_str(vb, pkg_priority_name(pkg)); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_status(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + + if (pkg->want > PKG_WANT_PURGE) + internerr("package %s has unknown want state %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->want); + if (pkg->eflag > PKG_EFLAG_REINSTREQ) + internerr("package %s has unknown error state %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->eflag); + + switch (pkg->status) { + case PKG_STAT_NOTINSTALLED: + case PKG_STAT_CONFIGFILES: + if (pkg->trigpend_head || pkg->trigaw.head) + internerr("package %s in state %s, has awaited or pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + break; + case PKG_STAT_HALFINSTALLED: + case PKG_STAT_UNPACKED: + case PKG_STAT_HALFCONFIGURED: + if (pkg->trigpend_head) + internerr("package %s in state %s, has pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + break; + case PKG_STAT_TRIGGERSAWAITED: + if (pkg->trigaw.head == NULL) + internerr("package %s in state %s, has no awaited triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + break; + case PKG_STAT_TRIGGERSPENDING: + if (pkg->trigpend_head == NULL || pkg->trigaw.head) + internerr("package %s in stata %s, has awaited or no pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + break; + case PKG_STAT_INSTALLED: + if (pkg->trigpend_head || pkg->trigaw.head) + internerr("package %s in state %s, has awaited or pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + break; + default: + internerr("unknown package status '%d'", pkg->status); + } + + if (flags&fw_printheader) + varbuf_add_str(vb, "Status: "); + varbuf_add_str(vb, pkg_want_name(pkg)); + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, pkg_eflag_name(pkg)); + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, pkg_status_name(pkg)); + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void varbufdependency(struct varbuf *vb, struct dependency *dep) { + struct deppossi *dop; + const char *possdel; + + possdel= ""; + for (dop= dep->list; dop; dop= dop->next) { + if (dop->up != dep) + internerr("dependency and deppossi not linked properly"); + + varbuf_add_str(vb, possdel); + possdel = " | "; + varbuf_add_str(vb, dop->ed->name); + if (!dop->arch_is_implicit) + varbuf_add_archqual(vb, dop->arch); + if (dop->verrel != DPKG_RELATION_NONE) { + varbuf_add_str(vb, " ("); + switch (dop->verrel) { + case DPKG_RELATION_EQ: + varbuf_add_char(vb, '='); + break; + case DPKG_RELATION_GE: + varbuf_add_str(vb, ">="); + break; + case DPKG_RELATION_LE: + varbuf_add_str(vb, "<="); + break; + case DPKG_RELATION_GT: + varbuf_add_str(vb, ">>"); + break; + case DPKG_RELATION_LT: + varbuf_add_str(vb, "<<"); + break; + default: + internerr("unknown dpkg_relation %d", dop->verrel); + } + varbuf_add_char(vb, ' '); + varbufversion(vb,&dop->version,vdew_nonambig); + varbuf_add_char(vb, ')'); + } + } +} + +void +w_dependency(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct dependency *dyp; + bool dep_found = false; + + for (dyp = pkgbin->depends; dyp; dyp = dyp->next) { + if (dyp->type != fip->integer) continue; + + if (dyp->up != pkg) + internerr("dependency and package %s not linked properly", + pkgbin_name_const(pkg, pkgbin, pnaw_always)); + + if (dep_found) { + varbuf_add_str(vb, ", "); + } else { + if (flags & fw_printheader) + varbuf_add_fieldname(vb, fip); + dep_found = true; + } + varbufdependency(vb,dyp); + } + if ((flags & fw_printheader) && dep_found) + varbuf_add_char(vb, '\n'); +} + +void +w_conffiles(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct conffile *i; + + if (!pkgbin->conffiles || pkgbin == &pkg->available) + return; + if (flags&fw_printheader) + varbuf_add_str(vb, "Conffiles:\n"); + for (i = pkgbin->conffiles; i; i = i->next) { + if (i != pkgbin->conffiles) + varbuf_add_char(vb, '\n'); + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, i->name); + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, i->hash); + if (i->obsolete) + varbuf_add_str(vb, " obsolete"); + if (i->remove_on_upgrade) + varbuf_add_str(vb, " remove-on-upgrade"); + } + if (flags&fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_trigpend(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct trigpend *tp; + + if (pkgbin == &pkg->available || !pkg->trigpend_head) + return; + + if (pkg->status < PKG_STAT_TRIGGERSAWAITED || + pkg->status > PKG_STAT_TRIGGERSPENDING) + internerr("package %s in non-trigger state %s, has pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + + if (flags & fw_printheader) + varbuf_add_str(vb, "Triggers-Pending:"); + for (tp = pkg->trigpend_head; tp; tp = tp->next) { + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, tp->name); + } + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +w_trigaw(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct trigaw *ta; + + if (pkgbin == &pkg->available || !pkg->trigaw.head) + return; + + if (pkg->status <= PKG_STAT_CONFIGFILES || + pkg->status > PKG_STAT_TRIGGERSAWAITED) + internerr("package %s in state %s, has awaited triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); + + if (flags & fw_printheader) + varbuf_add_str(vb, "Triggers-Awaited:"); + for (ta = pkg->trigaw.head; ta; ta = ta->sameaw.next) { + varbuf_add_char(vb, ' '); + varbuf_add_pkgbin_name(vb, ta->pend, &ta->pend->installed, pnaw_nonambig); + } + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +varbuf_add_arbfield(struct varbuf *vb, const struct arbitraryfield *arbfield, + enum fwriteflags flags) +{ + if (flags & fw_printheader) { + varbuf_add_str(vb, arbfield->name); + varbuf_add_str(vb, ": "); + } + varbuf_add_str(vb, arbfield->value); + if (flags & fw_printheader) + varbuf_add_char(vb, '\n'); +} + +void +varbuf_stanza(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + const struct fieldinfo *fip; + const struct arbitraryfield *afp; + + for (fip= fieldinfos; fip->name; fip++) { + fip->wcall(vb, pkg, pkgbin, fw_printheader, fip); + } + for (afp = pkgbin->arbs; afp; afp = afp->next) { + varbuf_add_arbfield(vb, afp, fw_printheader); + } +} + +void +write_stanza(FILE *file, const char *filename, + const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + struct varbuf vb = VARBUF_INIT; + + varbuf_stanza(&vb, pkg, pkgbin); + varbuf_end_str(&vb); + + if (fputs(vb.buf, file) < 0) + ohshite(_("failed to write details of '%.50s' to '%.250s'"), + pkgbin_name_const(pkg, pkgbin, pnaw_nonambig), filename); + + varbuf_destroy(&vb); +} + +void +writedb_stanzas(FILE *fp, const char *filename, enum writedb_flags flags) +{ + static char writebuf[8192]; + + struct pkg_array array; + const char *which; + struct varbuf vb = VARBUF_INIT; + int i; + + which = (flags & wdb_dump_available) ? "available" : "status"; + + if (setvbuf(fp, writebuf, _IOFBF, sizeof(writebuf))) + ohshite(_("unable to set buffering on %s database file"), which); + + pkg_array_init_from_hash(&array); + pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); + + for (i = 0; i < array.n_pkgs; i++) { + struct pkginfo *pkg; + struct pkgbin *pkgbin; + + pkg = array.pkgs[i]; + pkgbin = (flags & wdb_dump_available) ? &pkg->available : &pkg->installed; + + /* Don't dump stanzas which have no useful content. */ + if (!pkg_is_informative(pkg, pkgbin)) + continue; + + varbuf_stanza(&vb, pkg, pkgbin); + varbuf_add_char(&vb, '\n'); + varbuf_end_str(&vb); + if (fputs(vb.buf, fp) < 0) + ohshite(_("failed to write %s database stanza about '%s' to '%s'"), + which, pkgbin_name(pkg, pkgbin, pnaw_nonambig), filename); + varbuf_reset(&vb); + } + + pkg_array_destroy(&array); + varbuf_destroy(&vb); +} + +void +writedb(const char *filename, enum writedb_flags flags) +{ + struct atomic_file *file; + enum atomic_file_flags atomic_flags = ATOMIC_FILE_BACKUP; + + if (flags & wdb_dump_available) + atomic_flags = 0; + + file = atomic_file_new(filename, atomic_flags); + atomic_file_open(file); + + writedb_stanzas(file->fp, filename, flags); + + if (flags & wdb_must_sync) + atomic_file_sync(file); + + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + if (flags & wdb_must_sync) + dir_sync_path_parent(filename); +} diff --git a/lib/dpkg/ehandle.c b/lib/dpkg/ehandle.c new file mode 100644 index 0000000..be95c36 --- /dev/null +++ b/lib/dpkg/ehandle.c @@ -0,0 +1,511 @@ +/* + * libdpkg - Debian packaging suite library routines + * ehandle.c - error handling + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#include <dpkg/i18n.h> +#include <dpkg/progname.h> +#include <dpkg/color.h> +#include <dpkg/ehandle.h> + +/* Incremented when we do some kind of generally necessary operation, + * so that loops &c know to quit if we take an error exit. Decremented + * again afterwards. */ +volatile int onerr_abort = 0; + +#define NCALLS 2 + +struct cleanup_entry { + struct cleanup_entry *next; + struct { + int mask; + void (*call)(int argc, void **argv); + } calls[NCALLS]; + int cpmask, cpvalue; + int argc; + void *argv[1]; +}; + +struct error_context { + struct error_context *next; + + enum { + HANDLER_TYPE_FUNC, + HANDLER_TYPE_JUMP, + } handler_type; + + union { + error_handler_func *func; + jmp_buf *jump; + } handler; + + struct { + error_printer_func *func; + const void *data; + } printer; + + struct cleanup_entry *cleanups; + + char *errmsg; +}; + +static struct error_context *volatile econtext = NULL; + +/** + * Emergency variables. + * + * These are used when the system is out of resources, and we want to be able + * to proceed anyway at least to the point of a controlled shutdown. + */ +static struct { + struct cleanup_entry ce; + void *args[20]; + + /** + * Emergency error message buffer. + * + * The size is estimated using the following heuristic: + * - 6x255 For inserted strings (%.255s &c in fmt; and %s with limited + * length arg). + * - 1x255 For constant text. + * - 1x255 For strerror(). + * - And the total doubled just in case. + */ + char errmsg[4096]; +} emergency; + +/** + * Default fatal error handler. + * + * This handler performs all error unwinding for the current context, and + * terminates the program with an error exit code. + */ +void +catch_fatal_error(void) +{ + pop_error_context(ehflag_bombout); + exit(2); +} + +void +print_fatal_error(const char *emsg, const void *data) +{ + fprintf(stderr, "%s%s:%s %s%s:%s %s\n", + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), + color_get(COLOR_ERROR), _("error"), color_reset(), emsg); +} + +static void +print_abort_error(const char *etype, const char *emsg) +{ + fprintf(stderr, _("%s%s%s: %s%s:%s\n %s\n"), + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), + color_get(COLOR_ERROR), etype, color_reset(), emsg); +} + +static struct error_context * +error_context_new(void) +{ + struct error_context *necp; + + necp = malloc(sizeof(*necp)); + if (!necp) + ohshite(_("out of memory for new error context")); + necp->next= econtext; + necp->cleanups= NULL; + necp->errmsg = NULL; + econtext= necp; + + return necp; +} + +static void +set_error_printer(struct error_context *ec, error_printer_func *func, + const void *data) +{ + ec->printer.func = func; + ec->printer.data = data; +} + +static void +set_func_handler(struct error_context *ec, error_handler_func *func) +{ + ec->handler_type = HANDLER_TYPE_FUNC; + ec->handler.func = func; +} + +static void +set_jump_handler(struct error_context *ec, jmp_buf *jump) +{ + ec->handler_type = HANDLER_TYPE_JUMP; + ec->handler.jump = jump; +} + +static void +error_context_errmsg_free(struct error_context *ec) +{ + if (ec->errmsg != emergency.errmsg) + free(ec->errmsg); + ec->errmsg = NULL; +} + +static void +error_context_errmsg_set(struct error_context *ec, char *errmsg) +{ + error_context_errmsg_free(ec); + ec->errmsg = errmsg; +} + +static int DPKG_ATTR_VPRINTF(1) +error_context_errmsg_format(const char *fmt, va_list args) +{ + va_list args_copy; + char *errmsg = NULL; + int rc; + + va_copy(args_copy, args); + rc = vasprintf(&errmsg, fmt, args_copy); + va_end(args_copy); + + /* If the message was constructed successfully, at least we have some + * error message, which is better than nothing. */ + if (rc >= 0) + error_context_errmsg_set(econtext, errmsg); + + if (rc < 0) { + /* If there was any error, just use the emergency error message buffer, + * even if it ends up being truncated, at least we will have a big part + * of the problem. */ + rc = vsnprintf(emergency.errmsg, sizeof(emergency.errmsg), fmt, args); + + /* Return failure only if we get truncated. */ + if (rc >= (int)sizeof(emergency.errmsg)) + rc = -1; + + error_context_errmsg_set(econtext, emergency.errmsg); + } + + return rc; +} + +void +push_error_context_func(error_handler_func *handler, + error_printer_func *printer, + const void *printer_data) +{ + struct error_context *ec; + + ec = error_context_new(); + set_error_printer(ec, printer, printer_data); + set_func_handler(ec, handler); + onerr_abort = 0; +} + +void +push_error_context_jump(jmp_buf *jumper, + error_printer_func *printer, + const void *printer_data) +{ + struct error_context *ec; + + ec = error_context_new(); + set_error_printer(ec, printer, printer_data); + set_jump_handler(ec, jumper); + onerr_abort = 0; +} + +void +push_error_context(void) +{ + push_error_context_func(catch_fatal_error, print_fatal_error, NULL); +} + +static void +print_cleanup_error(const char *emsg, const void *data) +{ + print_abort_error(_("error while cleaning up"), emsg); +} + +static void +run_cleanups(struct error_context *econ, int flagsetin) +{ + static volatile int preventrecurse= 0; + struct cleanup_entry *volatile cep; + struct cleanup_entry *ncep; + struct error_context recurserr, *oldecontext; + jmp_buf recurse_jump; + volatile int i, flagset; + + if (econ->printer.func) + econ->printer.func(econ->errmsg, econ->printer.data); + + if (++preventrecurse > 3) { + onerr_abort++; + print_cleanup_error(_("too many nested errors during error recovery"), NULL); + flagset= 0; + } else { + flagset= flagsetin; + } + cep= econ->cleanups; + oldecontext= econtext; + while (cep) { + for (i=0; i<NCALLS; i++) { + if (cep->calls[i].call && cep->calls[i].mask & flagset) { + if (setjmp(recurse_jump)) { + run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror); + error_context_errmsg_free(&recurserr); + } else { + memset(&recurserr, 0, sizeof(recurserr)); + set_error_printer(&recurserr, print_cleanup_error, NULL); + set_jump_handler(&recurserr, &recurse_jump); + econtext= &recurserr; + cep->calls[i].call(cep->argc,cep->argv); + } + econtext= oldecontext; + } + } + flagset &= cep->cpmask; + flagset |= cep->cpvalue; + ncep= cep->next; + if (cep != &emergency.ce) free(cep); + cep= ncep; + } + preventrecurse--; +} + +/** + * Push an error cleanup checkpoint. + * + * This will arrange that when pop_error_context() is called, all previous + * cleanups will be executed with + * flagset = (original_flagset & mask) | value + * where original_flagset is the argument to pop_error_context() (as + * modified by any checkpoint which was pushed later). + */ +void push_checkpoint(int mask, int value) { + struct cleanup_entry *cep; + int i; + + cep = malloc(sizeof(*cep) + sizeof(void *)); + if (cep == NULL) { + onerr_abort++; + ohshite(_("out of memory for new cleanup entry")); + } + + for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; } + cep->cpmask= mask; cep->cpvalue= value; + cep->argc= 0; cep->argv[0]= NULL; + cep->next= econtext->cleanups; + econtext->cleanups= cep; +} + +static void +cleanup_entry_new(void (*call1)(int argc, void **argv), int mask1, + void (*call2)(int argc, void **argv), int mask2, + unsigned int nargs, va_list vargs) +{ + struct cleanup_entry *cep; + void **argv; + int e = 0; + va_list args; + + onerr_abort++; + + cep = malloc(sizeof(*cep) + sizeof(void *) * (nargs + 1)); + if (!cep) { + if (nargs > array_count(emergency.args)) + ohshite(_("out of memory for new cleanup entry with many arguments")); + e= errno; cep= &emergency.ce; + } + cep->calls[0].call= call1; cep->calls[0].mask= mask1; + cep->calls[1].call= call2; cep->calls[1].mask= mask2; + cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs; + + va_copy(args, vargs); + argv = cep->argv; + while (nargs-- > 0) + *argv++ = va_arg(args, void *); + *argv++ = NULL; + va_end(args); + cep->next= econtext->cleanups; + econtext->cleanups= cep; + if (cep == &emergency.ce) { + errno = e; + ohshite(_("out of memory for new cleanup entry")); + } + + onerr_abort--; +} + +void +push_cleanup(void (*call)(int argc, void **argv), int mask, + unsigned int nargs, ...) +{ + va_list args; + + va_start(args, nargs); + cleanup_entry_new(call, mask, NULL, 0, nargs, args); + va_end(args); +} + +void +push_cleanup_fallback(void (*call1)(int argc, void **argv), int mask1, + void (*call2)(int argc, void **argv), int mask2, + unsigned int nargs, ...) +{ + va_list args; + + va_start(args, nargs); + cleanup_entry_new(call1, mask1, call2, mask2, nargs, args); + va_end(args); +} + +void pop_cleanup(int flagset) { + struct cleanup_entry *cep; + int i; + + cep= econtext->cleanups; + econtext->cleanups= cep->next; + for (i=0; i<NCALLS; i++) { + if (cep->calls[i].call && cep->calls[i].mask & flagset) + cep->calls[i].call(cep->argc,cep->argv); + } + if (cep != &emergency.ce) free(cep); +} + +/** + * Unwind the current error context by running its registered cleanups. + */ +void +pop_error_context(int flagset) +{ + struct error_context *tecp; + + tecp = econtext; + econtext = tecp->next; + + /* If we are cleaning up normally, do not print anything. */ + if (flagset & ehflag_normaltidy) + set_error_printer(tecp, NULL, NULL); + run_cleanups(tecp, flagset); + + error_context_errmsg_free(tecp); + free(tecp); +} + +static void DPKG_ATTR_NORET +run_error_handler(void) +{ + if (onerr_abort) { + /* We arrived here due to a fatal error from which we cannot recover, + * and trying to do so would most probably get us here again. That's + * why we will not try to do any error unwinding either. We'll just + * abort. Hopefully the user can fix the situation (out of disk, out + * of memory, etc). */ + print_abort_error(_("unrecoverable fatal error, aborting"), econtext->errmsg); + error_context_errmsg_free(econtext); + exit(2); + } + + if (econtext == NULL) { + print_abort_error(_("outside error context, aborting"), + _("an error occurred with no error handling in place")); + exit(2); + } else if (econtext->handler_type == HANDLER_TYPE_FUNC) { + econtext->handler.func(); + internerr("error handler returned unexpectedly!"); + } else if (econtext->handler_type == HANDLER_TYPE_JUMP) { + longjmp(*econtext->handler.jump, 1); + } else { + internerr("unknown error handler type %d!", econtext->handler_type); + } +} + +void ohshit(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + error_context_errmsg_format(fmt, args); + va_end(args); + + run_error_handler(); +} + +void +ohshitv(const char *fmt, va_list args) +{ + error_context_errmsg_format(fmt, args); + + run_error_handler(); +} + +void ohshite(const char *fmt, ...) { + int e, rc; + va_list args; + + e=errno; + + /* First we construct the formatted message. */ + va_start(args, fmt); + rc = error_context_errmsg_format(fmt, args); + va_end(args); + + /* Then if there was no error we append the string for errno. Otherwise + * we just use the emergency error message buffer, and ignore the errno + * value, as we will probably have no space left anyway. */ + if (rc > 0) { + char *errmsg = NULL; + + rc = asprintf(&errmsg, "%s: %s", econtext->errmsg, strerror(e)); + if (rc > 0) + error_context_errmsg_set(econtext, errmsg); + } + + run_error_handler(); +} + +void +do_internerr(const char *file, int line, const char *func, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + error_context_errmsg_format(fmt, args); + va_end(args); + + fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG), + dpkg_get_progname(), file, line, func, color_reset(), + color_get(COLOR_ERROR), _("internal error"), color_reset(), + econtext->errmsg); + + error_context_errmsg_free(econtext); + + abort(); +} diff --git a/lib/dpkg/ehandle.h b/lib/dpkg/ehandle.h new file mode 100644 index 0000000..3605904 --- /dev/null +++ b/lib/dpkg/ehandle.h @@ -0,0 +1,84 @@ +/* + * libdpkg - Debian packaging suite library routines + * ehandle.h - error handling + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wichert@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_EHANDLE_H +#define LIBDPKG_EHANDLE_H + +#include <setjmp.h> +#include <stddef.h> +#include <stdarg.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup ehandle Error context handling + * @ingroup dpkg-public + * @{ + */ + +extern volatile int onerr_abort; + +enum { + ehflag_normaltidy = DPKG_BIT(0), + ehflag_bombout = DPKG_BIT(1), + ehflag_recursiveerror = DPKG_BIT(2), +}; + +typedef void error_handler_func(void); +typedef void error_printer_func(const char *emsg, const void *data); + +void print_fatal_error(const char *emsg, const void *data); +void catch_fatal_error(void); + +void push_error_context_jump(jmp_buf *jumper, + error_printer_func *printer, + const void *printer_data); +void push_error_context_func(error_handler_func *handler, + error_printer_func *printer, + const void *printer_data); +void push_error_context(void); +void pop_error_context(int flagset); + +void push_cleanup_fallback(void (*f1)(int argc, void **argv), int flagmask1, + void (*f2)(int argc, void **argv), int flagmask2, + unsigned int nargs, ...); +void push_cleanup(void (*call)(int argc, void **argv), int flagmask, + unsigned int nargs, ...); +void push_checkpoint(int mask, int value); +void pop_cleanup(int flagset); + +void ohshitv(const char *fmt, va_list args) + DPKG_ATTR_NORET DPKG_ATTR_VPRINTF(1); +void ohshit(const char *fmt, ...) DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1); +void ohshite(const char *fmt, ...) DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1); + +void do_internerr(const char *file, int line, const char *func, + const char *fmt, ...) + DPKG_ATTR_NORET DPKG_ATTR_PRINTF(4); +#define internerr(...) do_internerr(__FILE__, __LINE__, __func__, __VA_ARGS__) + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_EHANDLE_H */ diff --git a/lib/dpkg/error.c b/lib/dpkg/error.c new file mode 100644 index 0000000..42bdb43 --- /dev/null +++ b/lib/dpkg/error.c @@ -0,0 +1,127 @@ +/* + * libdpkg - Debian packaging suite library routines + * error.c - error message reporting + * + * Copyright © 2011-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/varbuf.h> +#include <dpkg/error.h> + +static void DPKG_ATTR_VPRINTF(4) +dpkg_error_set(struct dpkg_error *err, enum dpkg_msg_type type, int syserrno, + const char *fmt, va_list args) +{ + struct varbuf str = VARBUF_INIT; + + if (err == NULL) + return; + + err->type = type; + err->syserrno = syserrno; + + varbuf_vprintf(&str, fmt, args); + if (syserrno) + varbuf_printf(&str, " (%s)", strerror(syserrno)); + + err->str = str.buf; +} + +bool +dpkg_has_error(struct dpkg_error *err) +{ + return err != NULL && err->type != DPKG_MSG_NONE; +} + +int +dpkg_put_warn(struct dpkg_error *err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dpkg_error_set(err, DPKG_MSG_WARN, 0, fmt, args); + va_end(args); + + return -1; +} + +int +dpkg_put_error(struct dpkg_error *err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dpkg_error_set(err, DPKG_MSG_ERROR, 0, fmt, args); + va_end(args); + + return -1; +} + +int +dpkg_put_errno(struct dpkg_error *err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dpkg_error_set(err, DPKG_MSG_ERROR, errno, fmt, args); + va_end(args); + + return -1; +} + +void +dpkg_error_print(struct dpkg_error *err, const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + m_vasprintf(&str, fmt, args); + va_end(args); + + if (err->type == DPKG_MSG_WARN) + warning("%s: %s", str, err->str); + else + ohshit("%s: %s", str, err->str); + + free(str); +} + +void +dpkg_error_move(struct dpkg_error *dst, struct dpkg_error *src) +{ + dst->type = src->type; + src->type = DPKG_MSG_NONE; + dst->str = src->str; + src->str = NULL; +} + +void +dpkg_error_destroy(struct dpkg_error *err) +{ + err->type = DPKG_MSG_NONE; + err->syserrno = 0; + free(err->str); + err->str = NULL; +} diff --git a/lib/dpkg/error.h b/lib/dpkg/error.h new file mode 100644 index 0000000..9fd7ead --- /dev/null +++ b/lib/dpkg/error.h @@ -0,0 +1,72 @@ +/* + * libdpkg - Debian packaging suite library routines + * error.h - error message reporting + * + * Copyright © 2011-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_ERROR_H +#define LIBDPKG_ERROR_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup dpkg_error Error message reporting + * @ingroup dpkg-public + * @{ + */ + +enum dpkg_msg_type { + DPKG_MSG_NONE, + DPKG_MSG_WARN, + DPKG_MSG_ERROR, +}; + +struct dpkg_error { + enum dpkg_msg_type type; + + int syserrno; + char *str; +}; + +#define DPKG_ERROR_INIT { DPKG_MSG_NONE, 0, NULL } + +#define DPKG_ERROR_OBJECT (struct dpkg_error)DPKG_ERROR_INIT + +bool dpkg_has_error(struct dpkg_error *err); + +int dpkg_put_warn(struct dpkg_error *err, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); +int dpkg_put_error(struct dpkg_error *err, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); +int dpkg_put_errno(struct dpkg_error *err, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); + +void dpkg_error_print(struct dpkg_error *err, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); + +void dpkg_error_move(struct dpkg_error *dst, struct dpkg_error *src); +void dpkg_error_destroy(struct dpkg_error *err); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_ERROR_H */ diff --git a/lib/dpkg/fdio.c b/lib/dpkg/fdio.c new file mode 100644 index 0000000..b50322b --- /dev/null +++ b/lib/dpkg/fdio.c @@ -0,0 +1,163 @@ +/* + * libdpkg - Debian packaging suite library routines + * fdio.c - safe file descriptor based input/output + * + * Copyright © 2009-2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dpkg/fdio.h> + +ssize_t +fd_read(int fd, void *buf, size_t len) +{ + ssize_t total = 0; + char *ptr = buf; + + while (len > 0) { + ssize_t n; + + n = read(fd, ptr + total, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + return total ? -total : n; + } + if (n == 0) + break; + + total += n; + len -= n; + } + + return total; +} + +ssize_t +fd_write(int fd, const void *buf, size_t len) +{ + ssize_t total = 0; + const char *ptr = buf; + + while (len > 0) { + ssize_t n; + + n = write(fd, ptr + total, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + return total ? -total : n; + } + if (n == 0) + break; + + total += n; + len -= n; + } + + return total; +} + +#ifdef USE_DISK_PREALLOCATE +#ifdef HAVE_F_PREALLOCATE +static void +fd_preallocate_setup(fstore_t *fs, int flags, off_t offset, off_t len) +{ + fs->fst_flags = flags; + fs->fst_posmode = F_PEOFPOSMODE; + fs->fst_offset = offset; + fs->fst_length = len; + fs->fst_bytesalloc = 0; +} +#endif + +/** + * Request the kernel to allocate the specified size for a file descriptor. + * + * We only want to send a hint that we will be using the requested size. But + * we do not want to unnecessarily write the file contents. That is why we + * are not using posix_fallocate(3) directly if possible, and not at all + * on glibc based systems (except on GNU/kFreeBSD). + */ +int +fd_allocate_size(int fd, off_t offset, off_t len) +{ + int rc; + + /* Do not preallocate on very small files as that degrades performance + * on some filesystems. */ + if (len < (4 * 4096) - 1) + return 0; + +#if defined(HAVE_F_PREALLOCATE) + /* On macOS. */ + fstore_t fs; + + fd_preallocate_setup(&fs, F_ALLOCATECONTIG, offset, len); + rc = fcntl(fd, F_PREALLOCATE, &fs); + if (rc < 0 && errno == ENOSPC) { + /* If we cannot get a contiguous allocation, then try + * non-contiguous. */ + fd_preallocate_setup(&fs, F_ALLOCATEALL, offset, len); + rc = fcntl(fd, F_PREALLOCATE, &fs); + } +#elif defined(HAVE_F_ALLOCSP64) + /* On Solaris. */ + struct flock64 fl; + + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = len; + + rc = fcntl(fd, F_ALLOCSP64, &fl); +#elif defined(HAVE_FALLOCATE) + /* On Linux. */ + do { + rc = fallocate(fd, 0, offset, len); + } while (rc < 0 && errno == EINTR); +#elif defined(HAVE_POSIX_FALLOCATE) && \ + ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || \ + !defined(__GLIBC__)) + /* + * On BSDs, newer GNU/kFreeBSD and other non-glibc based systems + * we can use posix_fallocate(2) which should be a simple syscall + * wrapper. But not on other glibc systems, as there the function + * will try to allocate the size by writing a '\0' to each block + * if the syscall is not implemented or not supported by the + * kernel or the filesystem, which we do not want. + */ + rc = posix_fallocate(fd, offset, len); +#else + errno = ENOSYS; + rc = -1; +#endif + + return rc; +} +#else +int +fd_allocate_size(int fd, off_t offset, off_t len) +{ + errno = ENOSYS; + return -1; +} +#endif diff --git a/lib/dpkg/fdio.h b/lib/dpkg/fdio.h new file mode 100644 index 0000000..d714dbd --- /dev/null +++ b/lib/dpkg/fdio.h @@ -0,0 +1,46 @@ +/* + * libdpkg - Debian packaging suite library routines + * fdio.h - safe file descriptor based input/output + * + * Copyright © 2009-2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_FDIO_H +#define LIBDPKG_FDIO_H + +#include <sys/types.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup fdio File descriptor I/O + * @ingroup dpkg-internal + * @{ + */ + +ssize_t fd_read(int fd, void *buf, size_t len); +ssize_t fd_write(int fd, const void *buf, size_t len); + +int +fd_allocate_size(int fd, off_t offset, off_t len); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_FDIO_H */ diff --git a/lib/dpkg/fields.c b/lib/dpkg/fields.c new file mode 100644 index 0000000..e62b7b5 --- /dev/null +++ b/lib/dpkg/fields.c @@ -0,0 +1,739 @@ +/* + * libdpkg - Debian packaging suite library routines + * fields.c - parsing of all the different fields, when reading in + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2001 Wichert Akkerman + * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> + * Copyright © 2009 Canonical Ltd. + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/parsedump.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/triglib.h> + +/** + * Flags to parse a name associated to a value. + */ +enum parse_nv_mode { + /** Expect no more words (default). */ + PARSE_NV_LAST = 0, + /** Expect another word after the parsed name. */ + PARSE_NV_NEXT = 1, + /** Do not fail if there is no name with an associated value found. */ + PARSE_NV_FALLBACK = 2, +}; + +/** + * Parses a name and returns its associated value. + * + * Gets a pointer to the string to parse in @a strp, and modifies the pointer + * to the string to point to the end of the parsed text. If no value is found + * for the name and @a flags is set to #PARSE_NV_FALLBACK then @a strp is set + * to NULL and returns -1, otherwise a parse error is emitted. + */ +static int +parse_nv(struct parsedb_state *ps, enum parse_nv_mode parse_mode, + const char **strp, const struct namevalue *nv_head) +{ + const char *str_start = *strp, *str_end; + const struct namevalue *nv; + int value; + + dpkg_error_destroy(&ps->err); + + if (str_start[0] == '\0') + return dpkg_put_error(&ps->err, _("is missing a value")); + + nv = namevalue_find_by_name(nv_head, str_start); + if (nv == NULL) { + /* We got no match, skip further string validation. */ + if (parse_mode != PARSE_NV_FALLBACK) + return dpkg_put_error(&ps->err, _("has invalid value '%.50s'"), str_start); + + str_end = NULL; + value = -1; + } else { + str_end = str_start + nv->length; + while (c_isspace(str_end[0])) + str_end++; + value = nv->value; + + if (parse_mode != PARSE_NV_NEXT && str_is_set(str_end)) + return dpkg_put_error(&ps->err, _("has trailing junk")); + } + + *strp = str_end; + + return value; +} + +void +f_name(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + const char *e; + + e = pkg_name_is_illegal(value); + if (e != NULL) + parse_error(ps, _("invalid package name in '%s' field: %s"), fip->name, e); + /* We use the new name, as pkg_hash_find_set() may have done a tolower for us. */ + pkg->set->name = pkg_hash_find_set(value)->name; +} + +void +f_archives(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + struct archivedetails *fdp, **fdpp; + char *cpos, *space; + int allowextend; + + if (!*value) + parse_error(ps, _("empty archive details '%s' field"), fip->name); + if (!(ps->flags & pdb_recordavailable)) + parse_error(ps, + _("archive details '%s' field not allowed in status file"), + fip->name); + allowextend = !pkg->archives; + fdpp = &pkg->archives; + cpos= nfstrsave(value); + while (*cpos) { + space = cpos; + while (*space && !c_isspace(*space)) + space++; + if (*space) + *space++ = '\0'; + fdp= *fdpp; + if (!fdp) { + if (!allowextend) + parse_error(ps, + _("too many values in archive details '%s' field " + "(compared to others)"), fip->name); + fdp = nfmalloc(sizeof(*fdp)); + fdp->next= NULL; + fdp->name= fdp->msdosname= fdp->size= fdp->md5sum= NULL; + *fdpp= fdp; + } + STRUCTFIELD(fdp, fip->integer, const char *) = cpos; + fdpp= &fdp->next; + while (*space && c_isspace(*space)) + space++; + cpos= space; + } + if (*fdpp) + parse_error(ps, + _("too few values in archive details '%s' field " + "(compared to others)"), fip->name); +} + +void +f_charfield(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + if (*value) + STRUCTFIELD(pkgbin, fip->integer, char *) = nfstrsave(value); +} + +void +f_boolean(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + bool boolean; + + if (!*value) + return; + + boolean = parse_nv(ps, PARSE_NV_LAST, &value, booleaninfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("boolean (yes/no) '%s' field: %s"), + fip->name, ps->err.str); + + STRUCTFIELD(pkgbin, fip->integer, bool) = boolean; +} + +void +f_multiarch(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + int multiarch; + + if (!*value) + return; + + multiarch = parse_nv(ps, PARSE_NV_LAST, &value, multiarchinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("quadstate (foreign/allowed/same/no) '%s' field: %s"), + fip->name, ps->err.str); + STRUCTFIELD(pkgbin, fip->integer, int) = multiarch; +} + +void +f_architecture(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + pkgbin->arch = dpkg_arch_find(value); + if (pkgbin->arch->type == DPKG_ARCH_ILLEGAL) + parse_warn(ps, _("'%s' is not a valid architecture name in '%s' field: %s"), + value, fip->name, dpkg_arch_name_is_illegal(value)); +} + +void +f_section(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + if (!*value) return; + pkg->section = nfstrsave(value); +} + +void +f_priority(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + const char *str = value; + int priority; + + if (!*value) return; + + priority = parse_nv(ps, PARSE_NV_FALLBACK, &str, priorityinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("word in '%s' field: %s"), fip->name, ps->err.str); + + if (str == NULL) { + pkg->priority = PKG_PRIO_OTHER; + pkg->otherpriority = nfstrsave(value); + } else { + pkg->priority = priority; + } +} + +void +f_obs_class(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + parse_warn(ps, _("obsolete '%s' field used"), fip->name); + f_priority(pkg, pkgbin, ps, value, fip); +} + +void +f_status(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + if (ps->flags & pdb_rejectstatus) + parse_error(ps, + _("value for '%s' field not allowed in this context"), + fip->name); + if (ps->flags & pdb_recordavailable) + return; + + pkg->want = parse_nv(ps, PARSE_NV_NEXT, &value, wantinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("first (want) word in '%s' field: %s"), + fip->name, ps->err.str); + pkg->eflag = parse_nv(ps, PARSE_NV_NEXT, &value, eflaginfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("second (error) word in '%s' field: %s"), + fip->name, ps->err.str); + pkg->status = parse_nv(ps, PARSE_NV_LAST, &value, statusinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("third (status) word in '%s' field: %s"), + fip->name, ps->err.str); +} + +void +f_version(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + if (parse_db_version(ps, &pkgbin->version, value) < 0) + parse_problem(ps, _("'%s' field value '%.250s'"), fip->name, value); +} + +void +f_obs_revision(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + char *newversion; + + parse_warn(ps, _("obsolete '%s' field used"), fip->name); + if (!*value) return; + if (str_is_set(pkgbin->version.revision)) { + newversion = nfmalloc(strlen(pkgbin->version.version) + + strlen(pkgbin->version.revision) + 2); + sprintf(newversion, "%s-%s", pkgbin->version.version, + pkgbin->version.revision); + pkgbin->version.version = newversion; + } + pkgbin->version.revision = nfstrsave(value); +} + +void +f_configversion(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + if (ps->flags & pdb_rejectstatus) + parse_error(ps, + _("value for '%s' field not allowed in this context"), + fip->name); + if (ps->flags & pdb_recordavailable) + return; + + if (parse_db_version(ps, &pkg->configversion, value) < 0) + parse_problem(ps, _("'%s' field value '%.250s'"), fip->name, value); +} + +/* + * The code in f_conffiles ensures that value[-1] == ' ', which is helpful. + */ +static void conffvalue_lastword(const char *value, const char *from, + const char *endent, + const char **word_start_r, int *word_len_r, + const char **new_from_r, + struct parsedb_state *ps) +{ + const char *lastspc; + + if (from <= value+1) goto malformed; + for (lastspc= from-1; *lastspc != ' '; lastspc--); + if (lastspc <= value+1 || lastspc >= endent-1) goto malformed; + + *new_from_r= lastspc; + *word_start_r= lastspc + 1; + *word_len_r= (int)(from - *word_start_r); + return; + +malformed: + parse_error(ps, + _("value for '%s' field has malformed line '%.*s'"), + "Conffiles", (int)min(endent - value, 250), value); +} + +void +f_conffiles(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + static const char obsolete_str[]= "obsolete"; + static const char remove_on_upgrade_str[] = "remove-on-upgrade"; + struct conffile **lastp; + + lastp = &pkgbin->conffiles; + while (*value) { + struct conffile *newlink; + const char *endent, *endfn, *hashstart; + char *newptr; + int c, namelen, hashlen; + bool obsolete, remove_on_upgrade; + + c= *value++; + if (c == '\n') continue; + if (c != ' ') + parse_error(ps, + _("value for '%s' field has line starting with non-space '%c'"), + fip->name, c); + for (endent = value; (c = *endent) != '\0' && c != '\n'; endent++) ; + conffvalue_lastword(value, endent, endent, + &hashstart, &hashlen, &endfn, + ps); + remove_on_upgrade = (hashlen == sizeof(remove_on_upgrade_str) - 1 && + memcmp(hashstart, remove_on_upgrade_str, hashlen) == 0); + if (remove_on_upgrade) + conffvalue_lastword(value, endfn, endent, &hashstart, &hashlen, &endfn, + ps); + + obsolete= (hashlen == sizeof(obsolete_str)-1 && + memcmp(hashstart, obsolete_str, hashlen) == 0); + if (obsolete) + conffvalue_lastword(value, endfn, endent, + &hashstart, &hashlen, &endfn, + ps); + newlink = nfmalloc(sizeof(*newlink)); + value = path_skip_slash_dotslash(value); + namelen= (int)(endfn-value); + if (namelen <= 0) + parse_error(ps, + _("root or empty directory listed as a conffile in '%s' field"), + fip->name); + newptr = nfmalloc(namelen+2); + newptr[0]= '/'; + memcpy(newptr+1,value,namelen); + newptr[namelen+1] = '\0'; + newlink->name= newptr; + newptr= nfmalloc(hashlen+1); + memcpy(newptr, hashstart, hashlen); + newptr[hashlen] = '\0'; + newlink->hash= newptr; + newlink->obsolete= obsolete; + newlink->remove_on_upgrade = remove_on_upgrade; + newlink->next =NULL; + *lastp= newlink; + lastp= &newlink->next; + value= endent; + } +} + +void +f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + const char *p, *emsg; + static struct varbuf depname, version; + + struct dependency **ldypp; + + /* Empty fields are ignored. */ + if (!*value) + return; + p= value; + + ldypp = &pkgbin->depends; + while (*ldypp) + ldypp = &(*ldypp)->next; + + /* Loop creating new struct dependency's. */ + for (;;) { + struct deppossi **ldopp; + struct dependency *dyp; + + dyp = nfmalloc(sizeof(*dyp)); + /* Set this to NULL for now, as we don't know what our real + * struct pkginfo address (in the database) is going to be yet. */ + dyp->up = NULL; + dyp->next= NULL; *ldypp= dyp; ldypp= &dyp->next; + dyp->list= NULL; ldopp= &dyp->list; + dyp->type= fip->integer; + + /* Loop creating new struct deppossi's. */ + for (;;) { + const char *depnamestart; + int depnamelength; + struct deppossi *dop; + + depnamestart= p; + /* Skip over package name characters. */ + while (*p && !c_isspace(*p) && *p != ':' && *p != '(' && *p != ',' && + *p != '|') + p++; + depnamelength= p - depnamestart ; + if (depnamelength == 0) + parse_error(ps, + _("'%s' field, missing package name, or garbage where " + "package name expected"), fip->name); + + varbuf_set_buf(&depname, depnamestart, depnamelength); + + emsg = pkg_name_is_illegal(depname.buf); + if (emsg) + parse_error(ps, + _("'%s' field, invalid package name '%.255s': %s"), + fip->name, depname.buf, emsg); + dop = nfmalloc(sizeof(*dop)); + dop->up= dyp; + dop->ed = pkg_hash_find_set(depname.buf); + dop->next= NULL; *ldopp= dop; ldopp= &dop->next; + + /* Don't link this (which is after all only ‘new_pkg’ from + * the main parsing loop in parsedb) into the depended on + * packages' lists yet. This will be done later when we + * install this (in parse.c). For the moment we do the + * ‘forward’ links in deppossi (‘ed’) only, and the ‘backward’ + * links from the depended on packages to dop are left undone. */ + dop->rev_next = NULL; + dop->rev_prev = NULL; + + dop->cyclebreak = false; + + /* See if we have an architecture qualifier. */ + if (*p == ':') { + static struct varbuf arch; + const char *archstart; + int archlength; + + archstart = ++p; + while (*p && !c_isspace(*p) && *p != '(' && *p != ',' && *p != '|') + p++; + archlength = p - archstart; + if (archlength == 0) + parse_error(ps, _("'%s' field, missing architecture name, or garbage " + "where architecture name expected"), fip->name); + + varbuf_set_buf(&arch, archstart, archlength); + + dop->arch_is_implicit = false; + dop->arch = dpkg_arch_find(arch.buf); + + if (dop->arch->type == DPKG_ARCH_ILLEGAL) + emsg = dpkg_arch_name_is_illegal(arch.buf); + if (emsg) + parse_error(ps, _("'%s' field, reference to '%.255s': " + "invalid architecture name '%.255s': %s"), + fip->name, depname.buf, arch.buf, emsg); + } else if (fip->integer == dep_conflicts || fip->integer == dep_breaks || + fip->integer == dep_replaces) { + /* Conflicts/Breaks/Replaces get an implicit "any" arch qualifier. */ + dop->arch_is_implicit = true; + dop->arch = dpkg_arch_get(DPKG_ARCH_WILDCARD); + } else { + /* Otherwise use the pkgbin architecture, which will be assigned to + * later on by parse.c, once we can guarantee we have parsed it from + * the control stanza. */ + dop->arch_is_implicit = true; + dop->arch = NULL; + } + + /* Skip whitespace after package name. */ + while (c_isspace(*p)) + p++; + + /* See if we have a versioned relation. */ + if (*p == '(') { + char c1; + const char *versionstart; + int versionlength; + + p++; + while (c_isspace(*p)) + p++; + c1= *p; + if (c1 == '<' || c1 == '>') { + char c2; + + c2= *++p; + dop->verrel = (c1 == '<') ? DPKG_RELATION_LT : DPKG_RELATION_GT; + if (c2 == '=') { + dop->verrel |= DPKG_RELATION_EQ; + p++; + } else if (c2 == c1) { + /* Either ‘<<’ or ‘>>’. */ + p++; + } else if (c2 == '<' || c2 == '>') { + parse_error(ps, + _("'%s' field, reference to '%.255s':\n" + " bad version relationship %c%c"), + fip->name, depname.buf, c1, c2); + } else { + parse_warn(ps, + _("'%s' field, reference to '%.255s':\n" + " '%c' is obsolete, use '%c=' or '%c%c' instead"), + fip->name, depname.buf, c1, c1, c1, c1); + dop->verrel |= DPKG_RELATION_EQ; + } + } else if (c1 == '=') { + dop->verrel = DPKG_RELATION_EQ; + p++; + } else { + parse_warn(ps, + _("'%s' field, reference to '%.255s':\n" + " implicit exact match on version number, " + "suggest using '=' instead"), + fip->name, depname.buf); + dop->verrel = DPKG_RELATION_EQ; + } + if ((dop->verrel != DPKG_RELATION_EQ) && (fip->integer == dep_provides)) + parse_warn(ps, + _("only exact versions may be used for '%s' field"), + fip->name); + + if (!c_isspace(*p) && !c_isalnum(*p)) { + parse_warn(ps, + _("'%s' field, reference to '%.255s':\n" + " version value starts with non-alphanumeric, " + "suggest adding a space"), + fip->name, depname.buf); + } + /* Skip spaces between the relation and the version. */ + while (c_isspace(*p)) + p++; + + versionstart= p; + while (*p && *p != ')' && *p != '(') { + if (c_isspace(*p)) + break; + p++; + } + versionlength= p - versionstart; + while (c_isspace(*p)) + p++; + if (*p == '\0') + parse_error(ps, + _("'%s' field, reference to '%.255s': " + "version unterminated"), fip->name, depname.buf); + else if (*p != ')') + parse_error(ps, + _("'%s' field, reference to '%.255s': " + "version contains '%c' instead of '%c'"), + fip->name, depname.buf, *p, ')'); + varbuf_set_buf(&version, versionstart, versionlength); + if (parse_db_version(ps, &dop->version, version.buf) < 0) + parse_problem(ps, + _("'%s' field, reference to '%.255s': version '%s'"), + fip->name, depname.buf, version.buf); + p++; + while (c_isspace(*p)) + p++; + } else { + dop->verrel = DPKG_RELATION_NONE; + dpkg_version_blank(&dop->version); + } + if (!*p || *p == ',') break; + if (*p != '|') + parse_error(ps, + _("'%s' field, syntax error after reference to package '%.255s'"), + fip->name, dop->ed->name); + if (fip->integer == dep_conflicts || + fip->integer == dep_breaks || + fip->integer == dep_provides || + fip->integer == dep_replaces) + parse_error(ps, _("alternatives ('|') not allowed in '%s' field"), + fip->name); + p++; + while (c_isspace(*p)) + p++; + } + if (!*p) break; + p++; + while (c_isspace(*p)) + p++; + } +} + +void +f_obs_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + parse_warn(ps, _("obsolete '%s' field used"), fip->name); + f_dependency(pkg, pkgbin, ps, value, fip); +} + +static const char * +scan_word(const char **valp) +{ + static struct varbuf word; + const char *p, *start, *end; + + p = *valp; + for (;;) { + if (!*p) { + *valp = p; + return NULL; + } + if (c_iswhite(*p)) { + p++; + continue; + } + start = p; + break; + } + for (;;) { + if (*p && !c_iswhite(*p)) { + p++; + continue; + } + end = p; + break; + } + + varbuf_set_buf(&word, start, end - start); + + *valp = p; + + return word.buf; +} + +void +f_trigpend(struct pkginfo *pend, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + const char *word; + + if (ps->flags & pdb_rejectstatus) + parse_error(ps, + _("value for '%s' field not allowed in this context"), + fip->name); + + while ((word = scan_word(&value))) { + const char *emsg; + + emsg = trig_name_is_illegal(word); + if (emsg) + parse_error(ps, + _("illegal pending trigger name '%.255s': %s"), word, emsg); + + if (!trig_note_pend_core(pend, nfstrsave(word))) + parse_error(ps, + _("duplicate pending trigger '%.255s'"), word); + } +} + +void +f_trigaw(struct pkginfo *aw, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) +{ + const char *word; + + if (ps->flags & pdb_rejectstatus) + parse_error(ps, + _("value for '%s' field not allowed in this context"), + fip->name); + + while ((word = scan_word(&value))) { + struct pkginfo *pend; + struct dpkg_error err; + + pend = pkg_spec_parse_pkg(word, &err); + if (pend == NULL) + parse_error(ps, + _("illegal package name in awaited trigger '%.255s': %s"), + word, err.str); + + if (!trig_note_aw(pend, aw)) + parse_error(ps, + _("duplicate awaited trigger package '%.255s'"), word); + + trig_awaited_pend_enqueue(pend); + } +} diff --git a/lib/dpkg/file.c b/lib/dpkg/file.c new file mode 100644 index 0000000..4d02520 --- /dev/null +++ b/lib/dpkg/file.c @@ -0,0 +1,257 @@ +/* + * libdpkg - Debian packaging suite library routines + * file.c - file handling functions + * + * Copyright © 1994, 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/pager.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> +#include <dpkg/file.h> + +/** + * Check whether a filename is executable. + * + * @param filename The filename to check. + */ +bool +file_is_exec(const char *filename) +{ + struct stat st; + + if (stat(filename, &st) < 0) + return false; + + if (!S_ISREG(st.st_mode)) + return false; + + return st.st_mode & 0111; +} + +/** + * Copy file ownership and permissions from one file to another. + * + * @param src The source filename. + * @param dst The destination filename. + */ +void +file_copy_perms(const char *src, const char *dst) +{ + struct stat stab; + + if (stat(src, &stab) == -1) { + if (errno == ENOENT) + return; + ohshite(_("unable to stat source file '%.250s'"), src); + } + + if (chown(dst, stab.st_uid, stab.st_gid) == -1) + ohshite(_("unable to change ownership of target file '%.250s'"), + dst); + + if (chmod(dst, (stab.st_mode & ~S_IFMT)) == -1) + ohshite(_("unable to set mode of target file '%.250s'"), dst); +} + +static int +file_slurp_fd(int fd, const char *filename, struct varbuf *vb, + struct dpkg_error *err) +{ + struct stat st; + + if (fstat(fd, &st) < 0) + return dpkg_put_errno(err, _("cannot stat %s"), filename); + + if (!S_ISREG(st.st_mode)) + return dpkg_put_error(err, _("%s is not a regular file"), + filename); + + if (st.st_size == 0) + return 0; + + varbuf_init(vb, st.st_size); + if (fd_read(fd, vb->buf, st.st_size) < 0) + return dpkg_put_errno(err, _("cannot read %s"), filename); + vb->used = st.st_size; + + return 0; +} + +int +file_slurp(const char *filename, struct varbuf *vb, struct dpkg_error *err) +{ + int fd; + int rc; + + varbuf_init(vb, 0); + + fd = open(filename, O_RDONLY); + if (fd < 0) + return dpkg_put_errno(err, _("cannot open %s"), filename); + + rc = file_slurp_fd(fd, filename, vb, err); + + (void)close(fd); + + return rc; +} + +static void +file_lock_setup(struct flock *fl, short type) +{ + fl->l_type = type; + fl->l_whence = SEEK_SET; + fl->l_start = 0; + fl->l_len = 0; + fl->l_pid = 0; +} + +/** + * Unlock a previously locked file. + */ +void +file_unlock(int lockfd, const char *lockfile, const char *lockdesc) +{ + struct flock fl; + + if (lockfd < 0) + internerr("%s (%s) fd is %d < 0", lockdesc, lockfile, lockfd); + + file_lock_setup(&fl, F_UNLCK); + + if (fcntl(lockfd, F_SETLK, &fl) == -1) + ohshite(_("unable to unlock %s"), lockdesc); +} + +static void +file_unlock_cleanup(int argc, void **argv) +{ + int lockfd = *(int *)argv[0]; + const char *lockfile = argv[1]; + const char *lockdesc = argv[2]; + + file_unlock(lockfd, lockfile, lockdesc); +} + +/** + * Check if a file has a lock acquired. + * + * @param lockfd The file descriptor for the lock. + * @param filename The file name associated to the file descriptor. + */ +bool +file_is_locked(int lockfd, const char *filename) +{ + struct flock fl; + + file_lock_setup(&fl, F_WRLCK); + + if (fcntl(lockfd, F_GETLK, &fl) == -1) + ohshit(_("unable to check file '%s' lock status"), filename); + + if (fl.l_type == F_WRLCK && fl.l_pid != getpid()) + return true; + else + return false; +} + +/** + * Lock a file. + * + * @param lockfd The pointer to the lock file descriptor. It must be allocated + * statically as its addresses is passed to a cleanup handler. + * @param flags The lock flags specifying what type of locking to perform. + * @param filename The name of the file to lock. + * @param desc The description of the file to lock. + */ +void +file_lock(int *lockfd, enum file_lock_flags flags, const char *filename, + const char *desc) +{ + struct flock fl; + int lock_cmd; + + setcloexec(*lockfd, filename); + + file_lock_setup(&fl, F_WRLCK); + + if (flags == FILE_LOCK_WAIT) + lock_cmd = F_SETLKW; + else + lock_cmd = F_SETLK; + + if (fcntl(*lockfd, lock_cmd, &fl) == -1) { + const char *warnmsg; + + if (errno != EACCES && errno != EAGAIN) + ohshite(_("unable to lock %s"), desc); + + warnmsg = _("Note: removing the lock file is always wrong, " + "can damage the locked area\n" + "and the entire system. " + "See <https://wiki.debian.org/Teams/Dpkg/FAQ#db-lock>."); + + file_lock_setup(&fl, F_WRLCK); + if (fcntl(*lockfd, F_GETLK, &fl) == -1) + ohshit(_("%s was locked by another process\n%s"), + desc, warnmsg); + + ohshit(_("%s was locked by another process with pid %d\n%s"), + desc, fl.l_pid, warnmsg); + } + + push_cleanup(file_unlock_cleanup, ~0, 3, lockfd, filename, desc); +} + +void +file_show(const char *filename) +{ + struct pager *pager; + struct dpkg_error err; + int fd, rc; + + if (filename == NULL) + internerr("filename is NULL"); + + fd = open(filename, O_RDONLY); + if (fd < 0) + ohshite(_("cannot open file %s"), filename); + + pager = pager_spawn(_("pager to show file")); + rc = fd_fd_copy(fd, STDOUT_FILENO, -1, &err); + pager_reap(pager); + + close(fd); + + if (rc < 0 && err.syserrno != EPIPE) { + errno = err.syserrno; + ohshite(_("cannot write file %s into the pager"), filename); + } +} diff --git a/lib/dpkg/file.h b/lib/dpkg/file.h new file mode 100644 index 0000000..0136f69 --- /dev/null +++ b/lib/dpkg/file.h @@ -0,0 +1,73 @@ +/* + * libdpkg - Debian packaging suite library routines + * file.h - file handling routines + * + * Copyright © 2008-2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_FILE_H +#define LIBDPKG_FILE_H + +#include <sys/types.h> + +#include <stdbool.h> + +#include <dpkg/macros.h> +#include <dpkg/error.h> +#include <dpkg/varbuf.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup file File handling + * @ingroup dpkg-internal + * @{ + */ + +struct file_stat { + uid_t uid; + gid_t gid; + mode_t mode; + + /* Used by dpkg-statoverride when parsing the database to preserve the + * user and group names in case the system does not know about them. */ + char *uname; + char *gname; +}; + +bool file_is_exec(const char *filename); + +void file_copy_perms(const char *src, const char *dst); + +int +file_slurp(const char *filename, struct varbuf *vb, struct dpkg_error *err); + +enum file_lock_flags { + FILE_LOCK_NOWAIT, + FILE_LOCK_WAIT, +}; + +bool file_is_locked(int lockfd, const char *filename); +void file_lock(int *lockfd, enum file_lock_flags flags, const char *filename, + const char *filedesc); +void file_unlock(int fd, const char *filename, const char *filedesc); +void file_show(const char *filename); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_FILE_H */ diff --git a/lib/dpkg/fsys-dir.c b/lib/dpkg/fsys-dir.c new file mode 100644 index 0000000..76d5f39 --- /dev/null +++ b/lib/dpkg/fsys-dir.c @@ -0,0 +1,119 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-dir.c - filesystem root directory functions + * + * Copyright © 2011, 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/fsys.h> + +static char *fsys_dir; + +/** + * Allocate the default on-disk filesystem root directory. + * + * The directory defaults to the value from environment variable + * DPKG_ROOT, and if not set the built-in default "". + * + * @return The filesystem root directory. + */ +static char * +dpkg_fsys_new_dir(void) +{ + const char *env; + char *dir; + + env = getenv("DPKG_ROOT"); + if (env == NULL) { + dir = m_strdup(""); + } else { + dir = m_strdup(env); + path_trim_slash_slashdot(dir); + } + + return dir; +} + +/** + * Set current on-disk filesystem root directory. + * + * This function can be used to set the directory to a new value, or to + * reset it to a default value if dir is NULL. + * + * @param dir The new filesystem root directory, or NULL to set to default. + * + * @return The new filesystem root directory. + */ +const char * +dpkg_fsys_set_dir(const char *dir) +{ + char *dir_new; + + if (dir == NULL) { + dir_new = dpkg_fsys_new_dir(); + } else { + dir_new = m_strdup(dir); + path_trim_slash_slashdot(dir_new); + } + + free(fsys_dir); + fsys_dir = dir_new; + + return fsys_dir; +} + +/** + * Get current on-disk filesystem root directory. + * + * This function will take care of initializing the directory if it has not + * been initialized before. + * + * @return The current filesystem root directory. + */ +const char * +dpkg_fsys_get_dir(void) +{ + if (fsys_dir == NULL) + fsys_dir = dpkg_fsys_new_dir(); + + return fsys_dir; +} + +/** + * Get a pathname to the current on-disk filesystem root directory. + * + * This function returns an allocated string, which should be freed with + * free(2). + * + * @param pathpart The pathpart to append to the new pathname. + * + * @return The newly allocated pathname. + */ +char * +dpkg_fsys_get_path(const char *pathpart) +{ + pathpart = path_skip_slash_dotslash(pathpart); + + return str_fmt("%s/%s", dpkg_fsys_get_dir(), pathpart); +} diff --git a/lib/dpkg/fsys-hash.c b/lib/dpkg/fsys-hash.c new file mode 100644 index 0000000..42cb956 --- /dev/null +++ b/lib/dpkg/fsys-hash.c @@ -0,0 +1,196 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-hash.c - filesystem nodes hash table + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/path.h> + +#include "fsys.h" + +/* This must always be a prime for optimal performance. + * This is the closest one to 2^18 (262144). */ +#define BINS 262139 + +static struct fsys_namenode *bins[BINS]; +static int nfiles = 0; + +void +fsys_hash_init(void) +{ + struct fsys_namenode *fnn; + int i; + + for (i = 0; i < BINS; i++) { + for (fnn = bins[i]; fnn; fnn = fnn->next) { + fnn->flags = 0; + fnn->oldhash = NULL; + fnn->newhash = NULL; + fnn->file_ondisk_id = NULL; + } + } +} + +void +fsys_hash_reset(void) +{ + memset(bins, 0, sizeof(bins)); + nfiles = 0; +} + +int +fsys_hash_entries(void) +{ + return nfiles; +} + +struct fsys_namenode * +fsys_hash_find_node(const char *name, enum fsys_hash_find_flags flags) +{ + struct fsys_namenode **pointerp, *newnode; + const char *orig_name = name; + + /* We skip initial slashes and ‘./’ pairs, and add our own single + * leading slash. */ + name = path_skip_slash_dotslash(name); + + pointerp = bins + (str_fnv_hash(name) % (BINS)); + while (*pointerp) { + /* XXX: This should not be needed, but it has been a constant + * source of assertions over the years. Hopefully with the + * internerr() we will get better diagnostics. */ + if ((*pointerp)->name[0] != '/') + internerr("filename node '%s' does not start with '/'", + (*pointerp)->name); + + if (strcmp((*pointerp)->name + 1, name) == 0) + return *pointerp; + pointerp = &(*pointerp)->next; + } + + if (flags & FHFF_NO_NEW) + return NULL; + + newnode = nfmalloc(sizeof(*newnode)); + memset(newnode, 0, sizeof(*newnode)); + if ((flags & FHFF_NO_COPY) && name > orig_name && name[-1] == '/') { + newnode->name = name - 1; + } else { + char *newname = nfmalloc(strlen(name) + 2); + + newname[0] = '/'; + strcpy(newname + 1, name); + newnode->name = newname; + } + *pointerp = newnode; + nfiles++; + + return newnode; +} + +void +fsys_hash_report(FILE *file) +{ + struct fsys_namenode *node; + int i, c; + int *freq; + int empty = 0, used = 0, collided = 0; + + freq = m_malloc(sizeof(freq[0]) * nfiles + 1); + for (i = 0; i <= nfiles; i++) + freq[i] = 0; + for (i = 0; i < BINS; i++) { + for (c = 0, node = bins[i]; node; c++, node = node->next); + fprintf(file, "fsys-hash: bin %5d has %7d\n", i, c); + if (c == 0) + empty++; + else if (c == 1) + used++; + else { + used++; + collided++; + } + freq[c]++; + } + for (i = nfiles; i > 0 && freq[i] == 0; i--); + while (i >= 0) { + fprintf(file, "fsys-hash: size %7d occurs %5d times\n", + i, freq[i]); + i--; + } + fprintf(file, "fsys-hash: bins empty %d\n", empty); + fprintf(file, "fsys-hash: bins used %d (collided %d)\n", used, + collided); + + m_output(file, "<hash report>"); + + free(freq); +} + +/* + * Forward iterator. + */ + +struct fsys_hash_iter { + struct fsys_namenode *namenode; + int nbinn; +}; + +struct fsys_hash_iter * +fsys_hash_iter_new(void) +{ + struct fsys_hash_iter *iter; + + iter = m_malloc(sizeof(*iter)); + iter->namenode = NULL; + iter->nbinn = 0; + + return iter; +} + +struct fsys_namenode * +fsys_hash_iter_next(struct fsys_hash_iter *iter) +{ + struct fsys_namenode *fnn = NULL; + + while (!iter->namenode) { + if (iter->nbinn >= BINS) + return NULL; + iter->namenode = bins[iter->nbinn++]; + } + fnn = iter->namenode; + iter->namenode = fnn->next; + + return fnn; +} + +void +fsys_hash_iter_free(struct fsys_hash_iter *iter) +{ + free(iter); +} diff --git a/lib/dpkg/fsys-iter.c b/lib/dpkg/fsys-iter.c new file mode 100644 index 0000000..807aabb --- /dev/null +++ b/lib/dpkg/fsys-iter.c @@ -0,0 +1,126 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-iter.c - filesystem nodes iterators + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> + +#include "fsys.h" + +/* + * Reverse iterator. + */ + +/* + * Initializes an iterator that appears to go through the file list ‘files’ + * in reverse order, returning the namenode from each. What actually happens + * is that we walk the list here, building up a reverse list, and then peel + * it apart one entry at a time. + */ +void +fsys_hash_rev_iter_init(struct fsys_hash_rev_iter *iter, + struct fsys_namenode_list *files) +{ + struct fsys_namenode_list *newent; + + iter->todo = NULL; + while (files) { + newent = m_malloc(sizeof(*newent)); + newent->namenode = files->namenode; + newent->next = iter->todo; + iter->todo = newent; + files = files->next; + } +} + +struct fsys_namenode * +fsys_hash_rev_iter_next(struct fsys_hash_rev_iter *iter) +{ + struct fsys_namenode *next; + struct fsys_namenode_list *todo; + + todo = iter->todo; + if (!todo) + return NULL; + next = todo->namenode; + iter->todo = todo->next; + free(todo); + + return next; +} + +/* + * Clients must call this function to clean up the fsys_hash_rev_iter + * if they wish to break out of the iteration before it is all done. + * Calling this function is not necessary if fsys_hash_rev_iter_next() has + * been called until it returned 0. + */ +void +fsys_hash_rev_iter_abort(struct fsys_hash_rev_iter *iter) +{ + while (fsys_hash_rev_iter_next(iter)) + ; +} + +/* + * Iterator for packages owning a file. + */ + +struct fsys_node_pkgs_iter { + struct pkg_list *pkg_node; +}; + +struct fsys_node_pkgs_iter * +fsys_node_pkgs_iter_new(struct fsys_namenode *fnn) +{ + struct fsys_node_pkgs_iter *iter; + + iter = m_malloc(sizeof(*iter)); + iter->pkg_node = fnn->packages; + + return iter; +} + +struct pkginfo * +fsys_node_pkgs_iter_next(struct fsys_node_pkgs_iter *iter) +{ + struct pkg_list *pkg_node; + + if (iter->pkg_node == NULL) + return NULL; + + pkg_node = iter->pkg_node; + iter->pkg_node = pkg_node->next; + + return pkg_node->pkg; +} + +void +fsys_node_pkgs_iter_free(struct fsys_node_pkgs_iter *iter) +{ + free(iter); +} diff --git a/lib/dpkg/fsys.h b/lib/dpkg/fsys.h new file mode 100644 index 0000000..ede5cdf --- /dev/null +++ b/lib/dpkg/fsys.h @@ -0,0 +1,214 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys.h - filesystem nodes hash table + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_FSYS_H +#define LIBDPKG_FSYS_H + +#include <stdio.h> + +#include <dpkg/file.h> + +DPKG_BEGIN_DECLS + +/* + * Data structure here is as follows: + * + * For each package we have a ‘struct fsys_namenode_list *’, the head of a list of + * files in that package. They are in ‘forwards’ order. Each entry has a + * pointer to the ‘struct fsys_namenode’. + * + * The struct fsys_namenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each fsys_namenode has a (possibly empty) list of ‘struct filepackage’, + * giving a list of the packages listing that filename. + * + * When we read files contained info about a particular package we set the + * ‘files’ member of the clientdata struct to the appropriate thing. When + * not yet set the files pointer is made to point to ‘fileslist_uninited’ + * (this is available only internally, within filesdb.c - the published + * interface is ensure_*_available). + */ + +struct pkginfo; + +/** + * Flags to fsys_hash_find_node(). + */ +enum DPKG_ATTR_ENUM_FLAGS fsys_hash_find_flags { + /** No flags. */ + FHFF_NONE = 0, + /** Do not need to copy filename. */ + FHFF_NO_COPY = DPKG_BIT(0), + /** Do not insert the item if it is not found, and return NULL. */ + FHFF_NO_NEW = DPKG_BIT(1), +}; + +enum DPKG_ATTR_ENUM_FLAGS fsys_namenode_flags { + /** No flags. */ + FNNF_NONE = 0, + /** In the newconffiles list. */ + FNNF_NEW_CONFF = DPKG_BIT(0), + /** In the new filesystem archive. */ + FNNF_NEW_INARCHIVE = DPKG_BIT(1), + /** In the old package's conffiles list. */ + FNNF_OLD_CONFF = DPKG_BIT(2), + /** Obsolete conffile. */ + FNNF_OBS_CONFF = DPKG_BIT(3), + /** Must remove from other packages' lists. */ + FNNF_ELIDE_OTHER_LISTS = DPKG_BIT(4), + /** >= 1 instance is a dir, cannot rename over. */ + FNNF_NO_ATOMIC_OVERWRITE = DPKG_BIT(5), + /** New file has been placed on the disk. */ + FNNF_PLACED_ON_DISK = DPKG_BIT(6), + FNNF_DEFERRED_FSYNC = DPKG_BIT(7), + FNNF_DEFERRED_RENAME = DPKG_BIT(8), + /** Path being filtered. */ + FNNF_FILTERED = DPKG_BIT(9), + /** Conffile removal requested by upgrade. */ + FNNF_RM_CONFF_ON_UPGRADE = DPKG_BIT(10), +}; + +/** + * Stores information to uniquely identify an on-disk file. + */ +struct file_ondisk_id { + dev_t id_dev; + ino_t id_ino; +}; + +struct fsys_namenode { + struct fsys_namenode *next; + const char *name; + struct pkg_list *packages; + struct fsys_diversion *divert; + + /** We allow the administrator to override the owner, group and mode + * of a file. If such an override is present we use that instead of + * the stat information stored in the archive. + * + * This functionality used to be in the suidmanager package. */ + struct file_stat *statoverride; + + struct trigfileint *trig_interested; + + /* + * Fields from here on are used by archives.c &c, and cleared by + * fsys_hash_init(). + */ + + /** Set to zero when a new node is created. */ + enum fsys_namenode_flags flags; + + /** Valid iff this namenode is in the newconffiles list. */ + const char *oldhash; + + /** Valid iff the file was unpacked and hashed on this run. */ + const char *newhash; + + struct file_ondisk_id *file_ondisk_id; +}; + +struct fsys_namenode_list { + struct fsys_namenode_list *next; + struct fsys_namenode *namenode; +}; + +/** + * Queue of fsys_namenode entries. + */ +struct fsys_namenode_queue { + struct fsys_namenode_list *head, **tail; +}; + +/** + * When we deal with an ‘overridden’ file, every package except the + * overriding one is considered to contain the other file instead. Both + * files have entries in the filesdb database, and they refer to each other + * via these diversion structures. + * + * The contested filename's fsys_namenode has a diversion entry with + * useinstead set to point to the redirected filename's fsys_namenode; the + * redirected fsys_namenode has camefrom set to the contested fsys_namenode. + * Both sides' diversion entries will have pkg set to the package (if any) + * which is allowed to use the contended filename. + * + * Packages that contain either version of the file will all refer to the + * contested fsys_namenode in their per-file package lists (both in core and + * on disk). References are redirected to the other fsys_namenode's filename + * where appropriate. + */ +struct fsys_diversion { + struct fsys_namenode *useinstead; + struct fsys_namenode *camefrom; + struct pkgset *pkgset; + + /** The ‘contested’ halves are in this list for easy cleanup. */ + struct fsys_diversion *next; +}; + +struct fsys_node_pkgs_iter; +struct fsys_node_pkgs_iter * +fsys_node_pkgs_iter_new(struct fsys_namenode *fnn); +struct pkginfo * +fsys_node_pkgs_iter_next(struct fsys_node_pkgs_iter *iter); +void +fsys_node_pkgs_iter_free(struct fsys_node_pkgs_iter *iter); + +void +fsys_hash_init(void); +void +fsys_hash_reset(void); +void +fsys_hash_report(FILE *file); +int +fsys_hash_entries(void); + +struct fsys_hash_iter; +struct fsys_hash_iter * +fsys_hash_iter_new(void); +struct fsys_namenode * +fsys_hash_iter_next(struct fsys_hash_iter *iter); +void +fsys_hash_iter_free(struct fsys_hash_iter *iter); + +struct fsys_namenode * +fsys_hash_find_node(const char *filename, enum fsys_hash_find_flags flags); + +struct fsys_hash_rev_iter { + struct fsys_namenode_list *todo; +}; + +void +fsys_hash_rev_iter_init(struct fsys_hash_rev_iter *iter, + struct fsys_namenode_list *files); +struct fsys_namenode * +fsys_hash_rev_iter_next(struct fsys_hash_rev_iter *iter); +void +fsys_hash_rev_iter_abort(struct fsys_hash_rev_iter *iter); + +const char *dpkg_fsys_set_dir(const char *dir); +const char *dpkg_fsys_get_dir(void); +char *dpkg_fsys_get_path(const char *pathpart); + +DPKG_END_DECLS + +#endif /* LIBDPKG_FSYS_H */ diff --git a/lib/dpkg/glob.c b/lib/dpkg/glob.c new file mode 100644 index 0000000..90e401c --- /dev/null +++ b/lib/dpkg/glob.c @@ -0,0 +1,50 @@ +/* + * libdpkg - Debian packaging suite library routines + * glob.c - file globing functions + * + * Copyright © 2009, 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/glob.h> + +void +glob_list_prepend(struct glob_node **list, char *pattern) +{ + struct glob_node *node; + + node = m_malloc(sizeof(*node)); + node->pattern = pattern; + node->next = *list; + *list = node; +} + +void +glob_list_free(struct glob_node *head) +{ + while (head) { + struct glob_node *node = head; + + head = head->next; + free(node->pattern); + free(node); + } +} diff --git a/lib/dpkg/glob.h b/lib/dpkg/glob.h new file mode 100644 index 0000000..ed1830b --- /dev/null +++ b/lib/dpkg/glob.h @@ -0,0 +1,46 @@ +/* + * libdpkg - Debian packaging suite library routines + * glob.h - file globing functions + * + * Copyright © 2009, 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_GLOB_H +#define LIBDPKG_GLOB_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup glob File globbing + * @ingroup dpkg-internal + * @{ + */ + +struct glob_node { + struct glob_node *next; + char *pattern; +}; + +void glob_list_prepend(struct glob_node **list, char *pattern); +void glob_list_free(struct glob_node *head); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_GLOB_H */ diff --git a/lib/dpkg/i18n.c b/lib/dpkg/i18n.c new file mode 100644 index 0000000..30e28bf --- /dev/null +++ b/lib/dpkg/i18n.c @@ -0,0 +1,91 @@ +/* + * libdpkg - Debian packaging suite library routines + * i18n.c - i18n support + * + * Copyright © 2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> + +#ifdef HAVE_USELOCALE +#ifdef HAVE_XLOCALE_H +#include <xlocale.h> +#endif +static locale_t dpkg_C_locale; +#endif + +void +dpkg_locales_init(const char *package) +{ + setlocale(LC_ALL, ""); + bindtextdomain(package, LOCALEDIR); + textdomain(package); + +#ifdef HAVE_USELOCALE + dpkg_C_locale = newlocale(LC_ALL_MASK, "C", (locale_t)0); +#endif + +#if defined(__APPLE__) && defined(__MACH__) + /* + * On macOS, the libintl code needs to call into the CoreFoundation + * framework, which is internally threaded, to initialize some caches. + * This is a problem when that first call is done after a fork(3), + * because per POSIX, only one thread will survive, leaving the + * process in a very inconsistent state, leading to crashes. + * + * XXX: To workaround this, we try to force the caches initialization + * at program startup time, by performing a dummy gettext() call. + */ + gettext(""); +#endif +} + +void +dpkg_locales_done(void) +{ +#ifdef HAVE_USELOCALE + freelocale(dpkg_C_locale); + dpkg_C_locale = (locale_t)0; +#endif +} + +struct dpkg_locale +dpkg_locale_switch_C(void) +{ + struct dpkg_locale loc; + +#ifdef HAVE_USELOCALE + loc.oldloc = uselocale(dpkg_C_locale); +#else + loc.oldloc = setlocale(LC_ALL, NULL); + setlocale(LC_ALL, "C"); +#endif + + return loc; +} + +void +dpkg_locale_switch_back(struct dpkg_locale loc) +{ +#ifdef HAVE_USELOCALE + uselocale(loc.oldloc); +#else + setlocale(LC_ALL, loc.oldloc); +#endif +} diff --git a/lib/dpkg/i18n.h b/lib/dpkg/i18n.h new file mode 100644 index 0000000..f574433 --- /dev/null +++ b/lib/dpkg/i18n.h @@ -0,0 +1,59 @@ +/* + * libdpkg - Debian packaging suite library routines + * i18n.h - i18n support + * + * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_I18N_H +#define LIBDPKG_I18N_H + +#include <dpkg/macros.h> + +#include <gettext.h> + +/* We need to include this because pgettext() uses LC_MESSAGES, but libintl.h + * which gets pulled by gettext.h only includes it if building optimized. */ +#include <locale.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup i18n Internationalization support + * @ingroup dpkg-internal + * @{ + */ + +#define _(str) gettext(str) +#define P_(str, str_plural, n) ngettext(str, str_plural, n) +#define N_(str) gettext_noop(str) +#define C_(ctxt, str) pgettext(ctxt, str) + +void dpkg_locales_init(const char *package); +void dpkg_locales_done(void); + +struct dpkg_locale { + void *oldloc; +}; + +struct dpkg_locale dpkg_locale_switch_C(void); +void dpkg_locale_switch_back(struct dpkg_locale loc); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_I18N_H */ diff --git a/lib/dpkg/libdpkg.map b/lib/dpkg/libdpkg.map new file mode 100644 index 0000000..e0b1152 --- /dev/null +++ b/lib/dpkg/libdpkg.map @@ -0,0 +1,498 @@ +LIBDPKG_0 { +global: + # Error reporting + dpkg_has_error; + dpkg_put_warn; + dpkg_put_error; + dpkg_put_errno; + dpkg_error_print; + dpkg_error_move; + dpkg_error_destroy; + + # Charset and string functions + dpkg_locales_init; + dpkg_locales_done; + + # Program name + dpkg_set_progname; + dpkg_get_progname; + + # Program setup/teardown + dpkg_program_init; + dpkg_program_done; + + # Ar support + dpkg_ar_fdopen; + dpkg_ar_create; + dpkg_ar_open; + dpkg_ar_set_mtime; + dpkg_ar_close; + dpkg_ar_normalize_name; + dpkg_ar_member_is_illegal; + +local: + *; +}; + +LIBDPKG_PRIVATE { + # Color handling + color_set_mode; + color_get; + color_reset; + + # Error handling + push_error_context_jump; + push_error_context_func; + push_error_context; + pop_error_context; + print_fatal_error; + catch_fatal_error; + push_checkpoint; + push_cleanup; + push_cleanup_fallback; + pop_cleanup; + onerr_abort; # XXX variable, do not export + ohshitv; + ohshite; + ohshit; + do_internerr; + dpkg_set_report_piped_mode; + dpkg_set_report_buffer; + dpkg_warning_printer; + dpkg_set_warning_printer; + warning_get_count; + warningv; + warning; + notice; + info; + + debug_set_output; + debug_set_mask; + debug_parse_mask; + debug_has_flag; + debug; + dpkg_debug_init; + + # Generic cleanup + cu_closepipe; + cu_closestream; + cu_closedir; + cu_closefd; + cu_filename; + + # ‘Must do’ functions + m_malloc; + m_calloc; + m_realloc; + m_strdup; + m_vasprintf; + m_asprintf; + m_dup; + m_dup2; + m_pipe; + m_output; + fgets_must; + fgets_checked; + + # Charset and string functions + c_isbits; + + str_match_end; + str_fnv_hash; + str_concat; + str_fmt; + str_escape_fmt; + str_strip_quotes; + str_rtrim_spaces; + str_quote_meta; + str_width; + str_gen_crop; + + # Variable buffer support + varbuf_new; + varbuf_init; + varbuf_reset; + varbuf_grow; + varbuf_trunc; + varbuf_set_varbuf; + varbuf_set_buf; + varbuf_add_varbuf; + varbuf_add_char; + varbuf_dup_char; + varbuf_map_char; + varbuf_add_buf; + varbuf_add_dir; + varbuf_get_str; + varbuf_end_str; + varbuf_printf; + varbuf_vprintf; + varbuf_detach; + varbuf_snapshot; + varbuf_rollback; + varbuf_rollback_len; + varbuf_rollback_start; + varbuf_destroy; + varbuf_free; + + # Path, directory and file functions + secure_unlink_statted; + secure_unlink; + secure_remove; + path_remove_tree; + path_skip_slash_dotslash; + path_trim_slash_slashdot; + path_basename; + path_make_temp_template; + path_quote_filename; + + dir_make_path; + dir_make_path_parent; + dir_sync_path; + dir_sync_path_parent; + dir_sync_contents; + + treenode_get_name; + treenode_get_mode; + treenode_get_virtname; + treenode_get_pathname; + treewalk_open; + treewalk_node; + treewalk_next; + treewalk_close; + treewalk; + + file_is_exec; + file_copy_perms; + file_show; + file_slurp; + + atomic_file_new; + atomic_file_open; + atomic_file_sync; + atomic_file_close; + atomic_file_commit; + atomic_file_remove; + atomic_file_free; + + glob_list_prepend; + glob_list_free; + + # Data structure functions + namevalue_find_by_name; + + # Buffer I/O functions + fd_read; + fd_write; + fd_allocate_size; + buffer_digest; + buffer_skip_*; + buffer_copy_*; + + # Subprocess and command handling + subproc_signals_ignore; + subproc_signals_cleanup; + subproc_signals_restore; + subproc_fork; + subproc_reap; + + command_init; + command_add_arg; + command_add_argl; + command_add_argv; + command_add_args; + command_exec; + command_shell; + command_in_path; + command_destroy; + + pager_get_exec; + pager_spawn; + pager_reap; + pager_enable; + + setcloexec; + + # Memory information + meminfo_get_available_from_file; + meminfo_get_available; + + # Compression support + compressor_find_by_name; + compressor_find_by_extension; + compressor_get_name; + compressor_get_extension; + compressor_get_strategy; + compressor_check_params; + compress_filter; + decompress_filter; + + # Ar support + dpkg_ar_put_magic; + dpkg_ar_member_put_header; + dpkg_ar_member_put_file; + dpkg_ar_member_put_mem; + dpkg_ar_member_get_size; + + # deb version support + deb_version_parse; + + # Configuration and command line handling + dpkg_options_load; + dpkg_options_parse; + dpkg_options_parse_arg_int; + dpkg_options_parse_pkgname; + badusage; + cipaction; # XXX variable, do not export + setaction; + setobsolete; + set_instdir; + set_admindir; + set_root; + + # General logging + log_file; # XXX variable, do not export + log_message; + + # Action logging + statusfd_add; + statusfd_send; + + # Progress report support + progress_init; + progress_step; + progress_done; + + # Tar support + tar_atoul; + tar_atosl; + tar_extractor; + tar_entry_update_from_system; + + # Non-freeing malloc (pool/arena) + nfmalloc; + nfstrnsave; + nfstrsave; + nffreeall; + + # Version struct handling + dpkg_version_blank; + dpkg_version_is_informative; + dpkg_version_compare; + dpkg_version_relate; + versiondescribe; + versiondescribe_c; + parseversion; + + # Architecture database + dpkg_arch_name_is_illegal; + dpkg_arch_describe; + dpkg_arch_find; + dpkg_arch_get; + dpkg_arch_get_list; + dpkg_arch_reset_list; + dpkg_arch_add; + dpkg_arch_unmark; + dpkg_arch_load_list; + dpkg_arch_save_list; + + # Package struct handling + pkgset_blank; + pkgset_link_pkg; + pkgset_installed_instances; + pkg_blank; + pkgbin_blank; + pkg_name_is_illegal; + pkg_set_status; + pkg_set_eflags; + pkg_clear_eflags; + pkg_reset_eflags; + pkg_copy_eflags; + pkg_set_want; + pkg_is_informative; + copy_dependency_links; + pkg_sorter_by_nonambig_name_arch; + varbuf_add_pkgbin_name; + varbuf_add_archqual; + varbuf_add_source_version; + pkgbin_name; + pkg_name; + pkgbin_name_const; + pkg_name_const; + pkg_source_version; + pkgbin_synopsis; + pkg_synopsis; + pkg_abbrev_want; + pkg_abbrev_status; + pkg_abbrev_eflag; + pkg_want_name; + pkg_eflag_name; + pkg_status_name; + pkg_priority_name; + + # Package list handling + pkg_list_new; + pkg_list_free; + pkg_list_prepend; + + # Package array handling + pkg_array_init_from_hash; + pkg_array_init_from_names; + pkg_array_foreach; + pkg_array_sort; + pkg_array_destroy; + + # Package queue handling + pkg_queue_init; + pkg_queue_destroy; + pkg_queue_is_empty; + pkg_queue_push; + pkg_queue_pop; + + # Package in-core database functions + pkg_hash_find_set; + pkg_hash_find_singleton; + pkg_hash_find_pkg; + pkg_hash_get_singleton; + pkg_hash_count_set; + pkg_hash_count_pkg; + pkg_hash_reset; + pkg_hash_iter_new; + pkg_hash_iter_next_set; + pkg_hash_iter_next_pkg; + pkg_hash_iter_free; + pkg_hash_report; + + # Package field handling + booleaninfos; # XXX variable, do not export + fieldinfos; # XXX variable, do not export + find_field_info; + find_arbfield_info; + + # Package field format handling + pkg_format_parse; + pkg_format_print; + pkg_format_show; + pkg_format_needs_db_fsys; + pkg_format_free; + + # Package specifiers + pkg_spec_is_illegal; + pkg_spec_init; + pkg_spec_destroy; + pkg_spec_set; + pkg_spec_parse; + pkg_spec_match_pkg; + pkg_spec_parse_pkg; + pkg_spec_find_pkg; + pkg_spec_iter_init; + pkg_spec_iter_next_pkg; + pkg_spec_iter_destroy; + + # Dependency and Conflict functions + pkg_virtual_deppossi_satisfied; + deparchsatisfied; + archsatisfied; + versionsatisfied; + + # Package on-disk database functions + wantinfos; # XXX variable, do not export + varbuf_add_arbfield; + varbufdependency; + varbuf_stanza; + write_stanza; + parsedb_new; + parsedb_open; + parsedb_load; + parsedb_parse; + parsedb_close; + parsedb; + writedb_stanzas; + writedb; + + dpkg_db_set_dir; + dpkg_db_get_dir; + dpkg_db_get_path; + + # Log based package on-disk database support + modstatdb_init; + modstatdb_is_locked; + modstatdb_can_lock; + modstatdb_lock; + modstatdb_unlock; + modstatdb_open; + modstatdb_get_status; + modstatdb_note; + modstatdb_note_ifwrite; + modstatdb_checkpoint; + modstatdb_shutdown; + modstatdb_done; + + # Package on-disk control database support + pkg_infodb_foreach; + pkg_infodb_get_dir; + pkg_infodb_get_file; + pkg_infodb_has_file; + pkg_infodb_upgrade; + + # Package on-disk diversion database support + ensure_diversions; + + # Filesystem node hash support + fsys_hash_init; + fsys_hash_reset; + fsys_hash_entries; + fsys_hash_find_node; + fsys_hash_report; + + fsys_hash_iter_new; + fsys_hash_iter_next; + fsys_hash_iter_free; + + fsys_hash_rev_iter_init; + fsys_hash_rev_iter_next; + fsys_hash_rev_iter_abort; + + fsys_node_pkgs_iter_new; + fsys_node_pkgs_iter_next; + fsys_node_pkgs_iter_free; + + dpkg_fsys_set_dir; + dpkg_fsys_get_dir; + dpkg_fsys_get_path; + + # Package on-disk filesystem database support + parse_filehash; + write_filelist_except; + write_filehash_except; + ensure_packagefiles_available; + ensure_allinstfiles_available; + ensure_allinstfiles_available_quiet; + + # Package on-disk stat override database support + statdb_parse_uid; + statdb_parse_gid; + statdb_parse_mode; + ensure_statoverrides; + + # Triggers support + trig_name_is_illegal; + trigdef_set_methods; + trigdef_update_start; + trigdef_update_printf; + trigdef_parse; + trigdef_process_done; + trig_override_hooks; + trig_file_activate_byname; + trig_file_activate; + trig_path_activate; + trig_note_pend; + trig_note_aw; + trig_clear_awaiters; + trig_file_interests_ensure; + trig_file_interests_save; + trig_cicb_interest_delete; + trig_cicb_interest_add; + trig_parse_ci; + trig_cicb_statuschange_activate; + trig_incorporate; +} LIBDPKG_0; diff --git a/lib/dpkg/libdpkg.pc.in b/lib/dpkg/libdpkg.pc.in new file mode 100644 index 0000000..1a91594 --- /dev/null +++ b/lib/dpkg/libdpkg.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@devlibdir@ +includedir=@includedir@ + +Name: libdpkg +Description: Debian package management system library +Version: @VERSION@ +Libs: -L${libdir} -ldpkg +Libs.private: @MD_LIBS@ @Z_LIBS@ @LZMA_LIBS@ @ZSTD_LIBS@ @BZ2_LIBS@ +Cflags: -I${includedir} diff --git a/lib/dpkg/log.c b/lib/dpkg/log.c new file mode 100644 index 0000000..10a33a2 --- /dev/null +++ b/lib/dpkg/log.c @@ -0,0 +1,129 @@ +/* + * libdpkg - Debian packaging suite library routines + * log.c - logging related functions + * + * Copyright © 2005 Scott James Remnant <scott@netsplit.com> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fdio.h> + +const char *log_file = NULL; + +void +log_message(const char *fmt, ...) +{ + static struct varbuf log; + static int logfd = -1; + char time_str[20]; + time_t now; + struct tm tm; + va_list args; + + if (!log_file) + return; + + if (logfd < 0) { + logfd = open(log_file, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (logfd < 0) { + notice(_("could not open log '%s': %s"), + log_file, strerror(errno)); + log_file = NULL; + return; + } + setcloexec(logfd, log_file); + } + + time(&now); + if (localtime_r(&now, &tm) == NULL) { + notice(_("cannot get local time to log into '%s': %s"), + log_file, strerror(errno)); + return; + } + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm); + + va_start(args, fmt); + varbuf_set_str(&log, time_str); + varbuf_add_char(&log, ' '); + varbuf_vprintf(&log, fmt, args); + varbuf_add_char(&log, '\n'); + varbuf_end_str(&log); + va_end(args); + + if (fd_write(logfd, log.buf, log.used) < 0) + notice(_("cannot write to log file '%s': %s"), + log_file, strerror(errno)); +} + +struct pipef { + struct pipef *next; + int fd; +}; + +static struct pipef *status_pipes = NULL; + +void +statusfd_add(int fd) +{ + struct pipef *pipe_new; + + setcloexec(fd, _("<package status and progress file descriptor>")); + + pipe_new = nfmalloc(sizeof(*pipe_new)); + pipe_new->fd = fd; + pipe_new->next = status_pipes; + status_pipes = pipe_new; +} + +void +statusfd_send(const char *fmt, ...) +{ + static struct varbuf vb; + struct pipef *pipef; + va_list args; + + if (!status_pipes) + return; + + va_start(args, fmt); + varbuf_reset(&vb); + varbuf_vprintf(&vb, fmt, args); + /* Sanitize string to not include new lines, as front-ends should be + * doing their own word-wrapping. */ + varbuf_map_char(&vb, '\n', ' '); + varbuf_add_char(&vb, '\n'); + va_end(args); + + for (pipef = status_pipes; pipef; pipef = pipef->next) { + if (fd_write(pipef->fd, vb.buf, vb.used) < 0) + ohshite(_("unable to write to status fd %d"), + pipef->fd); + } +} diff --git a/lib/dpkg/macros.h b/lib/dpkg/macros.h new file mode 100644 index 0000000..277e67e --- /dev/null +++ b/lib/dpkg/macros.h @@ -0,0 +1,243 @@ +/* + * libdpkg - Debian packaging suite library routines + * macros.h - C language support macros + * + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_MACROS_H +#define LIBDPKG_MACROS_H + +/** + * @defgroup macros C language support macros + * @ingroup dpkg-public + * @{ + */ + +#ifndef LIBDPKG_VOLATILE_API +#error "The libdpkg API is to be considered volatile, please read 'README.api'." +#endif + +/* Language definitions. */ + +#ifndef __has_include +#define __has_include(x) 0 +#endif + +/* Supported since clang 1.0. */ +#ifndef __has_warning +#define __has_warning(w) (0) +#endif + +#define DPKG_PRAGMA(x) _Pragma(#x) + +#if defined(__clang__) +#define DPKG_PRAGMA_CC(x) DPKG_PRAGMA(clang x) +#elif defined(__GNUC__) +#define DPKG_PRAGMA_CC(x) DPKG_PRAGMA(GCC x) +#else +#define DPKG_PRAGMA_CC(x) +#endif + +#define DPKG_IGNORE_WARNING(w) \ + DPKG_PRAGMA_CC(diagnostic push); \ + DPKG_PRAGMA_CC(diagnostic ignored w) +#define DPKG_ACCEPT_WARNING() \ + DPKG_PRAGMA_CC(diagnostic pop) + +#if __has_warning("-Wassign-enum") +#define DPKG_IGNORE_WARNING_ASSIGN_ENUM() DPKG_IGNORE_WARNING("-Wassign-enum") +#define DPKG_ACCEPT_WARNING_ASSIGN_ENUM() DPKG_ACCEPT_WARNING() +#else +#define DPKG_IGNORE_WARNING_ASSIGN_ENUM() +#define DPKG_ACCEPT_WARNING_ASSIGN_ENUM() +#endif + +/* Supported since gcc 5.1.0 and clang 2.9.0. For attributes that appeared + * before these versions, in addition we need to do version checks. */ +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifdef __GNUC__ +#define DPKG_GCC_VERSION (__GNUC__ << 8 | __GNUC_MINOR__) +#else +#define DPKG_GCC_VERSION 0 +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__unused__) +#define DPKG_ATTR_UNUSED __attribute__((__unused__)) +#else +#define DPKG_ATTR_UNUSED +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__const__) +#define DPKG_ATTR_CONST __attribute__((__const__)) +#else +#define DPKG_ATTR_CONST +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__pure__) +#define DPKG_ATTR_PURE __attribute__((__pure__)) +#else +#define DPKG_ATTR_PURE +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__malloc__) +#define DPKG_ATTR_MALLOC __attribute__((__malloc__)) +#else +#define DPKG_ATTR_MALLOC +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__noreturn__) +#define DPKG_ATTR_NORET __attribute__((__noreturn__)) +#else +#define DPKG_ATTR_NORET +#endif + +#if DPKG_GCC_VERSION >= 0x0300 || __has_attribute(__format__) +#define DPKG_ATTR_FMT(t, f, a) __attribute__((__format__(t, f, a))) +#define DPKG_ATTR_PRINTF(n) DPKG_ATTR_FMT(__printf__, n, n + 1) +#define DPKG_ATTR_VPRINTF(n) DPKG_ATTR_FMT(__printf__, n, 0) +#else +#define DPKG_ATTR_FMT(t, f, a) +#define DPKG_ATTR_PRINTF(n) +#define DPKG_ATTR_VPRINTF(n) +#endif + +#if DPKG_GCC_VERSION > 0x0302 || __has_attribute(__nonnull__) +#define DPKG_ATTR_NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +#define DPKG_ATTR_NONNULL(...) +#endif + +#if DPKG_GCC_VERSION > 0x0302 || __has_attribute(__warn_unused_result__) +#define DPKG_ATTR_REQRET __attribute__((__warn_unused_result__)) +#else +#define DPKG_ATTR_REQRET +#endif + +#if DPKG_GCC_VERSION >= 0x0400 || __has_attribute(__sentinel__) +#define DPKG_ATTR_SENTINEL __attribute__((__sentinel__)) +#else +#define DPKG_ATTR_SENTINEL +#endif + +#if DPKG_GCC_VERSION >= 0x0801 || __has_attribute(__nonstring__) +#define DPKG_ATTR_NONSTRING __attribute__((__nonstring__)) +#else +#define DPKG_ATTR_NONSTRING +#endif + +#if __has_attribute(__enum_extensibility__) +#define DPKG_ATTR_ENUM_FLAGS \ + __attribute__((__enum_extensibility__(closed),__flag_enum__)) +#else +#define DPKG_ATTR_ENUM_FLAGS +#endif + +#if defined(__cplusplus) && __cplusplus >= 201103L +#define DPKG_ATTR_THROW(exception) +#define DPKG_ATTR_NOEXCEPT noexcept +#elif defined(__cplusplus) +#define DPKG_ATTR_THROW(exception) throw(exception) +#define DPKG_ATTR_NOEXCEPT throw() +#endif + +#ifdef __cplusplus +#define DPKG_BEGIN_DECLS extern "C" { +#define DPKG_END_DECLS } +#else +#define DPKG_BEGIN_DECLS +#define DPKG_END_DECLS +#endif + +/** + * @def DPKG_NULL + * + * A null pointer constant that works on C or C++. + * + * To be used only on header files, where having to conditionalize the code + * to use either NULL or nullptr would be too cumbersome. Non-header files + * should use the appropriate constant directly. + */ +#if defined(__cplusplus) || __STDC_VERSION__ > 201710L +#define DPKG_NULL nullptr +#else +#define DPKG_NULL NULL +#endif + +/** + * @def DPKG_STATIC_CAST + * + * Cast an expression to a given type that works on C or C++. + * + * To be used only on header files, where having to conditionalize the code + * to use either NULL or nullptr would be too cumbersome. Non-header files + * should use the appropriate constant directly. + */ +#if defined(__cplusplus) +#define DPKG_STATIC_CAST(type, expr) static_cast<type>(expr) +#else +#define DPKG_STATIC_CAST(type, expr) (type)(expr) +#endif + +/** + * @def DPKG_BIT + * + * Return the integer value of bit n. + */ +#define DPKG_BIT(n) (1UL << (n)) + +/** + * @def array_count + * + * Returns the amount of items in an array. + */ +#ifndef array_count +#define array_count(a) (sizeof(a) / sizeof((a)[0])) +#endif + +/* For C++ use native implementations from STL or similar. */ +#ifndef __cplusplus +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#endif + +/** + * @def clamp + * + * Returns a normalized value within the low and high limits. + * + * @param v The value to clamp. + * @param l The low limit. + * @param h The high limit. + */ +/* For C++ use native implementations from STL or similar. */ +#ifndef __cplusplus +#ifndef clamp +#define clamp(v, l, h) ((v) > (h) ? (h) : ((v) < (l) ? (l) : (v))) +#endif +#endif + +/** @} */ + +#endif /* LIBDPKG_MACROS_H */ diff --git a/lib/dpkg/meminfo.c b/lib/dpkg/meminfo.c new file mode 100644 index 0000000..998f65c --- /dev/null +++ b/lib/dpkg/meminfo.c @@ -0,0 +1,168 @@ +/* + * libdpkg - Debian packaging suite library routines + * meminfo.c - system memory information functions + * + * Copyright © 2021 Sebastian Andrzej Siewior <sebastian@breakpoint.cc> + * Copyright © 2021-2022 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <inttypes.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/fdio.h> +#include <dpkg/meminfo.h> + +/* + * An estimate of how much memory is available. Swap will not be used, the + * page cache may be purged, not everything will be reclaimed that might be + * reclaimed, watermarks are considered. + */ + +struct meminfo_field { + const char *name; + ssize_t len; + int tag; + uint64_t value; +}; +#define MEMINFO_FIELD(name, tag) name, sizeof(name) - 1, tag, 0 + +static struct meminfo_field * +meminfo_find_field(struct meminfo_field *fields, const size_t nfields, + const char *fieldname, const ssize_t fieldlen) +{ + size_t f; + + for (f = 0; f < nfields; f++) { + if (fieldlen != fields[f].len) + continue; + if (strncmp(fieldname, fields[f].name, fields[f].len) != 0) + continue; + + return &fields[f]; + } + + return NULL; +} + +static uint64_t +meminfo_sum_fields(const struct meminfo_field *fields, const size_t nfields) +{ + uint64_t sum = 0; + size_t f; + + for (f = 0; f < nfields; f++) + sum += fields[f].value; + + return sum; +} + +enum meminfo_error_code +meminfo_get_available_from_file(const char *filename, uint64_t *val) +{ + char buf[4096]; + char *str; + ssize_t bytes; + int fd; + struct meminfo_field fields[] = { + { MEMINFO_FIELD("MemFree", DPKG_BIT(0)) }, + { MEMINFO_FIELD("Buffers", DPKG_BIT(1)) }, + { MEMINFO_FIELD("Cached", DPKG_BIT(2)) }, + }; + const int want_tags = DPKG_BIT(array_count(fields)) - 1; + int seen_tags = 0; + + *val = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return MEMINFO_NO_FILE; + + bytes = fd_read(fd, buf, sizeof(buf)); + close(fd); + + if (bytes <= 0) + return MEMINFO_NO_DATA; + + buf[bytes] = '\0'; + + str = buf; + while (1) { + struct meminfo_field *field; + char *end; + + end = strchr(str, ':'); + if (end == 0) + break; + + field = meminfo_find_field(fields, array_count(fields), + str, end - str); + if (field) { + intmax_t num; + + str = end + 1; + errno = 0; + num = strtoimax(str, &end, 10); + if (num <= 0) + return MEMINFO_INT_NEG; + if ((num == INTMAX_MAX) && errno == ERANGE) + return MEMINFO_INT_MAX; + /* It should end with ' kB\n'. */ + if (*end != ' ' || *(end + 1) != 'k' || + *(end + 2) != 'B') + return MEMINFO_NO_UNIT; + + /* This should not overflow, but just in case. */ + if (num < (INTMAX_MAX / 1024)) + num *= 1024; + + field->value = num; + seen_tags |= field->tag; + } + + if (seen_tags == want_tags) + break; + + end = strchr(end + 1, '\n'); + if (end == 0) + break; + str = end + 1; + } + + if (seen_tags != want_tags) + return MEMINFO_NO_INFO; + + *val = meminfo_sum_fields(fields, array_count(fields)); + return MEMINFO_OK; +} + +enum meminfo_error_code +meminfo_get_available(uint64_t *val) +{ +#ifdef __linux__ + return meminfo_get_available_from_file("/proc/meminfo", val); +#else + return MEMINFO_NO_FILE; +#endif +} diff --git a/lib/dpkg/meminfo.h b/lib/dpkg/meminfo.h new file mode 100644 index 0000000..d7dee07 --- /dev/null +++ b/lib/dpkg/meminfo.h @@ -0,0 +1,55 @@ +/* + * libdpkg - Debian packaging suite library routines + * meminfo.h - system memory information functions + * + * Copyright © 2022 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_MEMINFO_H +#define LIBDPKG_MEMINFO_H + +#include <stdint.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup meminfo Memory information handling + * @ingroup dpkg-internal + * @{ + */ + +enum meminfo_error_code { + MEMINFO_OK = 0, + MEMINFO_NO_FILE = -1, + MEMINFO_NO_DATA = -2, + MEMINFO_INT_NEG = -3, + MEMINFO_INT_MAX = -4, + MEMINFO_NO_UNIT = -5, + MEMINFO_NO_INFO = -6, +}; + +enum meminfo_error_code +meminfo_get_available_from_file(const char *filename, uint64_t *val); +enum meminfo_error_code +meminfo_get_available(uint64_t *val); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_MEMINFO_H */ diff --git a/lib/dpkg/mustlib.c b/lib/dpkg/mustlib.c new file mode 100644 index 0000000..52d9752 --- /dev/null +++ b/lib/dpkg/mustlib.c @@ -0,0 +1,148 @@ +/* + * libdpkg - Debian packaging suite library routines + * mustlib.c - ‘must’ library: routines will succeed or longjmp + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2006-2013, 2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> + +static inline void * +must_alloc(void *ptr) +{ + if (ptr) + return ptr; + + onerr_abort++; + ohshite(_("failed to allocate memory")); +} + +void *m_malloc(size_t amount) { + return must_alloc(malloc(amount)); +} + +void * +m_calloc(size_t nmemb, size_t size) +{ + return must_alloc(calloc(nmemb, size)); +} + +void *m_realloc(void *r, size_t amount) { + return must_alloc(realloc(r, amount)); +} + +char * +m_strdup(const char *str) +{ + return must_alloc(strdup(str)); +} + +char * +m_strndup(const char *str, size_t n) +{ + return must_alloc(strndup(str, n)); +} + +int +m_vasprintf(char **strp, const char *fmt, va_list args) +{ + int n; + + n = vasprintf(strp, fmt, args); + if (n >= 0) + return n; + + onerr_abort++; + ohshite(_("failed to allocate memory")); +} + +int +m_asprintf(char **strp, const char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = m_vasprintf(strp, fmt, args); + va_end(args); + + return n; +} + +int +m_dup(int oldfd) +{ + int newfd; + + newfd = dup(oldfd); + if (newfd >= 0) + return newfd; + + onerr_abort++; + ohshite(_("failed to dup for fd %d"), oldfd); +} + +void m_dup2(int oldfd, int newfd) { + const char *const stdstrings[]= { "in", "out", "err" }; + + if (dup2(oldfd,newfd) == newfd) return; + + onerr_abort++; + if (newfd < 3) ohshite(_("failed to dup for std%s"),stdstrings[newfd]); + ohshite(_("failed to dup for fd %d"),newfd); +} + +void +m_pipe(int fds[2]) +{ + if (!pipe(fds)) return; + onerr_abort++; + ohshite(_("failed to create pipe")); +} + +void +m_output(FILE *f, const char *name) +{ + fflush(f); + if (ferror(f) && errno != EPIPE) + ohshite(_("error writing to '%s'"), name); +} + +void +setcloexec(int fd, const char *fn) +{ + int f; + + f = fcntl(fd, F_GETFD); + if (f == -1) + ohshite(_("unable to read filedescriptor flags for %.250s"),fn); + if (fcntl(fd, F_SETFD, (f|FD_CLOEXEC))==-1) + ohshite(_("unable to set close-on-exec flag for %.250s"),fn); +} diff --git a/lib/dpkg/namevalue.c b/lib/dpkg/namevalue.c new file mode 100644 index 0000000..d138f87 --- /dev/null +++ b/lib/dpkg/namevalue.c @@ -0,0 +1,40 @@ +/* + * libdpkg - Debian packaging suite library routines + * namevalue.c - name value structure handling + * + * Copyright © 2010-2011, 2014-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stddef.h> +#include <string.h> + +#include <dpkg/namevalue.h> + +const struct namevalue * +namevalue_find_by_name(const struct namevalue *head, const char *str) +{ + const struct namevalue *nv; + + for (nv = head; nv->name; nv++) { + if (strncasecmp(str, nv->name, nv->length) == 0) + return nv; + } + + return NULL; +} diff --git a/lib/dpkg/namevalue.h b/lib/dpkg/namevalue.h new file mode 100644 index 0000000..337b491 --- /dev/null +++ b/lib/dpkg/namevalue.h @@ -0,0 +1,51 @@ +/* + * libdpkg - Debian packaging suite library routines + * namevalue.h - name value structure handling + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2009-2012, 2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_NAMEVALUE_H +#define LIBDPKG_NAMEVALUE_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup namevalue Name/Value data + * @ingroup dpkg-public + * @{ + */ + +struct namevalue { + const char *name; + int value; + int length; +}; + +#define NAMEVALUE_DEF(n, v) \ + [v] = { .name = n, .value = v, .length = sizeof(n) - 1 } + +const struct namevalue *namevalue_find_by_name(const struct namevalue *head, + const char *str); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_NAMEVALUE_H */ diff --git a/lib/dpkg/nfmalloc.c b/lib/dpkg/nfmalloc.c new file mode 100644 index 0000000..ebe35b8 --- /dev/null +++ b/lib/dpkg/nfmalloc.c @@ -0,0 +1,81 @@ +/* + * libdpkg - Debian packaging suite library routines + * nfmalloc.c - non-freeing malloc, used for in-core database + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> +#include <obstack.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#define obstack_chunk_alloc m_malloc +#define obstack_chunk_free free + +static struct obstack db_obs; +static bool dbobs_init = false; + +/* We use lots of mem, so use a large chunk. */ +#define CHUNK_SIZE 8192 + +#define OBSTACK_INIT do { if (!dbobs_init) { nfobstack_init(); } } while (0) + +static void nfobstack_init(void) { + obstack_init(&db_obs); + dbobs_init = true; + obstack_chunk_size(&db_obs) = CHUNK_SIZE; +} + +void * +nfmalloc(size_t size) +{ + OBSTACK_INIT; + /* cppcheck-suppress[nullPointerArithmetic]: + * False positive, imported module. */ + return obstack_alloc(&db_obs, size); +} + +char *nfstrsave(const char *string) { + OBSTACK_INIT; + /* cppcheck-suppress[nullPointerArithmetic]: + * False positive, imported module. */ + return obstack_copy0 (&db_obs, string, strlen(string)); +} + +char * +nfstrnsave(const char *string, size_t size) +{ + OBSTACK_INIT; + /* cppcheck-suppress[nullPointerArithmetic]: + * False positive, imported module. */ + return obstack_copy0(&db_obs, string, size); +} + +void nffreeall(void) { + if (dbobs_init) { + /* cppcheck-suppress[nullPointerArithmetic,pointerLessThanZero]: + * False positive, imported module. */ + obstack_free(&db_obs, NULL); + dbobs_init = false; + } +} diff --git a/lib/dpkg/options-dirs.c b/lib/dpkg/options-dirs.c new file mode 100644 index 0000000..9b7a122 --- /dev/null +++ b/lib/dpkg/options-dirs.c @@ -0,0 +1,61 @@ +/* + * libdpkg - Debian packaging suite library routines + * options-dirs.c - CLI options parsing directories + * + * Copyright © 2022 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/macros.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> +#include <dpkg/options.h> + +void +set_instdir(const struct cmdinfo *cip, const char *value) +{ + /* Make sure the database is initialized before the root directory, + * otherwise, on first use it would get initialized based on the + * newly set root directory. */ + dpkg_db_get_dir(); + + dpkg_fsys_set_dir(value); +} + +void +set_admindir(const struct cmdinfo *cip, const char *value) +{ + dpkg_db_set_dir(value); +} + +void +set_root(const struct cmdinfo *cip, const char *value) +{ + char *db_dir; + + /* Initialize the root directory. */ + dpkg_fsys_set_dir(value); + + /* Set the database directory based on the new root directory. */ + db_dir = dpkg_fsys_get_path(ADMINDIR); + dpkg_db_set_dir(db_dir); + free(db_dir); +} diff --git a/lib/dpkg/options-parsers.c b/lib/dpkg/options-parsers.c new file mode 100644 index 0000000..de5bf69 --- /dev/null +++ b/lib/dpkg/options-parsers.c @@ -0,0 +1,53 @@ +/* + * libdpkg - Debian packaging suite library routines + * options-parsers.c - command-line options parser helpers + * + * Copyright © 2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/error.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/options.h> + +/** + * Parse an argument as a package name. + * + * The string is parsed as a singleton package name, and a #pkginfo instance + * is returned. + * + * @param cmd The command information structure currently parsed. + * @param name The package name argument + * + * @return A package instance. + */ +struct pkginfo * +dpkg_options_parse_pkgname(const struct cmdinfo *cmd, const char *name) +{ + struct pkginfo *pkg; + struct dpkg_error err; + + pkg = pkg_spec_parse_pkg(name, &err); + if (pkg == NULL) + badusage(_("--%s needs a valid package name but '%.250s' is not: %s"), + cmd->olong, name, err.str); + + return pkg; +} diff --git a/lib/dpkg/options.c b/lib/dpkg/options.c new file mode 100644 index 0000000..4244b7d --- /dev/null +++ b/lib/dpkg/options.c @@ -0,0 +1,326 @@ +/* + * libdpkg - Debian packaging suite library routines + * options.c - option parsing functions + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2002 Wichert Akkerman <wichert@deephackmode.org> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <dirent.h> +#include <stdarg.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/string.h> +#include <dpkg/options.h> + +static const char *printforhelp; + +void +badusage(const char *fmt, ...) +{ + char *buf = NULL; + va_list args; + + va_start(args, fmt); + m_vasprintf(&buf, fmt, args); + va_end(args); + + ohshit("%s\n\n%s", buf, gettext(printforhelp)); +} + +static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(3) +config_error(const char *file_name, int line_num, const char *fmt, ...) +{ + char *buf = NULL; + va_list args; + + va_start(args, fmt); + m_vasprintf(&buf, fmt, args); + va_end(args); + + ohshit(_("configuration error: %s:%d: %s"), file_name, line_num, buf); +} + +static void +dpkg_options_load_file(const char *fn, const struct cmdinfo *cmdinfos) +{ + FILE *file; + int line_num = 0; + char linebuf[MAX_CONFIG_LINE]; + + file= fopen(fn, "r"); + if (!file) { + if (errno==ENOENT) + return; + warning(_("failed to open configuration file '%.255s' for reading: %s"), + fn, strerror(errno)); + return; + } + + while (fgets(linebuf, sizeof(linebuf), file)) { + char *opt; + const struct cmdinfo *cip; + + line_num++; + + str_rtrim_spaces(linebuf, linebuf + strlen(linebuf)); + + if ((linebuf[0] == '#') || (linebuf[0] == '\0')) + continue; + for (opt = linebuf; c_isalnum(*opt) || *opt == '-'; opt++) ; + if (*opt == '\0') + opt=NULL; + else { + *opt++ = '\0'; + if (*opt=='=') opt++; + while (c_isspace(*opt)) + opt++; + + opt = str_strip_quotes(opt); + if (opt == NULL) + config_error(fn, line_num, _("unbalanced quotes in '%s'"), linebuf); + } + + for (cip=cmdinfos; cip->olong || cip->oshort; cip++) { + int l; + + if (!cip->olong) continue; + if (strcmp(cip->olong, linebuf) == 0) + break; + l=strlen(cip->olong); + if ((cip->takesvalue==2) && (linebuf[l]=='-') && + !opt && strncmp(linebuf, cip->olong, l) == 0) { + opt=linebuf+l+1; + break; + } + } + + if (!cip->olong) + config_error(fn, line_num, _("unknown option '%s'"), linebuf); + + if (cip->takesvalue) { + if (!opt) + config_error(fn, line_num, _("'%s' needs a value"), linebuf); + if (cip->call) cip->call(cip,opt); + else + *cip->sassignto = m_strdup(opt); + } else { + if (opt) + config_error(fn, line_num, _("'%s' does not take a value"), linebuf); + if (cip->call) cip->call(cip,NULL); + else + *cip->iassignto = cip->arg_int; + } + } + if (ferror(file)) + ohshite(_("read error in configuration file '%.255s'"), fn); + if (fclose(file)) + ohshite(_("error closing configuration file '%.255s'"), fn); +} + +static int +valid_config_filename(const struct dirent *dent) +{ + const char *c; + + if (dent->d_name[0] == '.') + return 0; + + for (c = dent->d_name; *c; c++) + if (!c_isalnum(*c) && *c != '_' && *c != '-') + return 0; + + if (*c == '\0') + return 1; + else + return 0; +} + +static void +dpkg_options_load_dir(const char *prog, const struct cmdinfo *cmdinfos) +{ + char *dirname; + struct dirent **dlist; + int dlist_n, i; + + dirname = str_fmt("%s/%s.cfg.d", CONFIGDIR, prog); + + dlist_n = scandir(dirname, &dlist, valid_config_filename, alphasort); + if (dlist_n < 0) { + if (errno == ENOENT) { + free(dirname); + return; + } else + ohshite(_("error opening configuration directory '%s'"), dirname); + } + + for (i = 0; i < dlist_n; i++) { + char *filename; + + filename = str_fmt("%s/%s", dirname, dlist[i]->d_name); + dpkg_options_load_file(filename, cmdinfos); + + free(dlist[i]); + free(filename); + } + + free(dirname); + free(dlist); +} + +void +dpkg_options_load(const char *prog, const struct cmdinfo *cmdinfos) +{ + char *home, *file; + + dpkg_options_load_dir(prog, cmdinfos); + + file = str_fmt("%s/%s.cfg", CONFIGDIR, prog); + dpkg_options_load_file(file, cmdinfos); + free(file); + + home = getenv("HOME"); + if (home != NULL) { + file = str_fmt("%s/.%s.cfg", home, prog); + dpkg_options_load_file(file, cmdinfos); + free(file); + } +} + +void +dpkg_options_parse(const char *const **argvp, const struct cmdinfo *cmdinfos, + const char *help_str) +{ + const struct cmdinfo *cip; + const char *p, *value; + int l; + + if (**argvp == NULL) + badusage(_("missing program name in argv[0]")); + + printforhelp = help_str; + + ++(*argvp); + while ((p = **argvp) && p[0] == '-' && p[1] != '\0') { + ++(*argvp); + if (strcmp(p, "--") == 0) + break; + if (*++p == '-') { + ++p; value=NULL; + for (cip= cmdinfos; + cip->olong || cip->oshort; + cip++) { + if (!cip->olong) continue; + if (strcmp(p, cip->olong) == 0) + break; + l= strlen(cip->olong); + if (strncmp(p, cip->olong, l) == 0 && + (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; } + } + if (!cip->olong) badusage(_("unknown option --%s"),p); + if (cip->takesvalue) { + if (!value) { + value= *(*argvp)++; + if (!value) badusage(_("--%s option takes a value"),cip->olong); + } + if (cip->call) cip->call(cip,value); + else *cip->sassignto= value; + } else { + if (value) badusage(_("--%s option does not take a value"),cip->olong); + if (cip->call) cip->call(cip,NULL); + else + *cip->iassignto = cip->arg_int; + } + } else { + while (*p) { + for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++); + if (!cip->oshort) badusage(_("unknown option -%c"),*p); + p++; + if (cip->takesvalue) { + if (!*p) { + value= *(*argvp)++; + if (!value) badusage(_("-%c option takes a value"),cip->oshort); + } else { + value= p; p=""; + if (*value == '=') value++; + } + if (cip->call) cip->call(cip,value); + else *cip->sassignto= value; + } else { + if (*p == '=') badusage(_("-%c option does not take a value"),cip->oshort); + if (cip->call) cip->call(cip,NULL); + else + *cip->iassignto = cip->arg_int; + } + } + } + } +} + +long +dpkg_options_parse_arg_int(const struct cmdinfo *cmd, const char *str) +{ + long value; + char *end; + + errno = 0; + value = strtol(str, &end, 0); + if (str == end || *end || value < 0 || value > INT_MAX || errno != 0) { + if (cmd->olong) + badusage(_("invalid integer for --%s: '%.250s'"), cmd->olong, str); + else + badusage(_("invalid integer for -%c: '%.250s'"), cmd->oshort, str); + } + + return value; +} + +void +setobsolete(const struct cmdinfo *cip, const char *value) +{ + warning(_("obsolete option '--%s'"), cip->olong); +} + +const struct cmdinfo *cipaction = NULL; + +/* XXX: This function is a hack. */ +static inline int +option_short(int c) +{ + return c ? c : '\b'; +} + +void +setaction(const struct cmdinfo *cip, const char *value) +{ + if (cipaction && cip) + badusage(_("conflicting actions -%c (--%s) and -%c (--%s)"), + option_short(cip->oshort), cip->olong, + option_short(cipaction->oshort), cipaction->olong); + cipaction = cip; + if (cip && cip->takesvalue == 2 && cip->sassignto) + *cipaction->sassignto = value; +} diff --git a/lib/dpkg/options.h b/lib/dpkg/options.h new file mode 100644 index 0000000..c08f286 --- /dev/null +++ b/lib/dpkg/options.h @@ -0,0 +1,98 @@ +/* + * libdpkg - Debian packaging suite library routines + * options.h - option parsing functions + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_OPTIONS_H +#define LIBDPKG_OPTIONS_H + +#include <dpkg/macros.h> +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup options Option parsing + * @ingroup dpkg-internal + * @{ + */ + +typedef int action_func(const char *const *argv); + +struct cmdinfo { + const char *olong; + char oshort; + + /* + * 0 = Normal (-o, --option) + * 1 = Standard value (-o=value, --option=value or + * -o value, --option value) + * 2 = Option string continued (--option-value) + */ + int takesvalue; + int *iassignto; + const char **sassignto; + void (*call)(const struct cmdinfo*, const char *value); + + int arg_int; + void *arg_ptr; + + action_func *action; +}; + +void badusage(const char *fmt, ...) DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1); + +#define MAX_CONFIG_LINE 1024 + +void dpkg_options_load(const char *prog, const struct cmdinfo *cmdinfos); +void dpkg_options_parse(const char *const **argvp, + const struct cmdinfo *cmdinfos, const char *help_str); + +long dpkg_options_parse_arg_int(const struct cmdinfo *cmd, const char *str); + +struct pkginfo * +dpkg_options_parse_pkgname(const struct cmdinfo *cmd, const char *name); + +/** + * Current cmdinfo action. + */ +extern const struct cmdinfo *cipaction; + +void setaction(const struct cmdinfo *cip, const char *value); +void setobsolete(const struct cmdinfo *cip, const char *value); + +#define ACTION(longopt, shortopt, code, func) \ + { longopt, shortopt, 0, NULL, NULL, setaction, code, NULL, func } +#define ACTION_MUX(longopt, shortopt, code, func, strvar) \ + { longopt, shortopt, 2, NULL, strvar, setaction, code, NULL, func } +#define OBSOLETE(longopt, shortopt) \ + { longopt, shortopt, 0, NULL, NULL, setobsolete, 0, NULL, NULL } + +void +set_instdir(const struct cmdinfo *cip, const char *value); +void +set_admindir(const struct cmdinfo *cip, const char *value); +void +set_root(const struct cmdinfo *cip, const char *value); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_OPTIONS_H */ diff --git a/lib/dpkg/pager.c b/lib/dpkg/pager.c new file mode 100644 index 0000000..08eb8d0 --- /dev/null +++ b/lib/dpkg/pager.c @@ -0,0 +1,145 @@ +/* + * libdpkg - Debian packaging suite library routines + * pager.c - pager execution support + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/string.h> +#include <dpkg/subproc.h> +#include <dpkg/command.h> +#include <dpkg/pager.h> + +static bool pager_enabled = true; + +void +pager_enable(bool enable) +{ + pager_enabled = enable; +} + +/** + * Get a suitable pager. + * + * @return A string representing a pager. + */ +const char * +pager_get_exec(void) +{ + const char *pager; + + if (!isatty(0) || !isatty(1)) + return CAT; + + pager = getenv("DPKG_PAGER"); + if (str_is_unset(pager)) + pager = getenv("PAGER"); + if (str_is_unset(pager)) + pager = DPKG_DEFAULT_PAGER; + + return pager; +} + +struct pager { + bool used; + const char *desc; + pid_t pid; + struct sigaction sigpipe; + int stdout_old; + int pipe[2]; +}; + +struct pager * +pager_spawn(const char *desc) +{ + struct sigaction sa; + struct pager *pager; + const char *exec; + + pager = m_calloc(1, sizeof(*pager)); + pager->used = isatty(0) && isatty(1); + pager->desc = desc; + + exec = pager_get_exec(); + if (strcmp(exec, CAT) == 0) + pager->used = false; + + if (!pager_enabled) + pager->used = false; + + if (!pager->used) + return pager; + + m_pipe(pager->pipe); + + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + + sigaction(SIGPIPE, &sa, &pager->sigpipe); + + pager->pid = subproc_fork(); + if (pager->pid == 0) { + /* Set better defaults for less if not already set. */ + setenv("LESS", "-FRSXMQ", 0); + + m_dup2(pager->pipe[0], 0); + close(pager->pipe[0]); + close(pager->pipe[1]); + + command_shell(exec, desc); + } + + pager->stdout_old = m_dup(1); + m_dup2(pager->pipe[1], 1); + close(pager->pipe[0]); + close(pager->pipe[1]); + + /* Force the output to fully buffered, because originally stdout was + * a tty, so it was set as line buffered. This way we send as much as + * possible to the pager, which will handle the output by itself. */ + setvbuf(stdout, NULL, _IOFBF, 0); + + return pager; +} + +void +pager_reap(struct pager *pager) +{ + if (!pager->used) + return; + + m_dup2(pager->stdout_old, 1); + subproc_reap(pager->pid, pager->desc, SUBPROC_NOPIPE); + + sigaction(SIGPIPE, &pager->sigpipe, NULL); + + free(pager); +} diff --git a/lib/dpkg/pager.h b/lib/dpkg/pager.h new file mode 100644 index 0000000..f9029d0 --- /dev/null +++ b/lib/dpkg/pager.h @@ -0,0 +1,54 @@ +/* + * libdpkg - Debian packaging suite library routines + * pager.h - pager execution support + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PAGER_H +#define LIBDPKG_PAGER_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pager Pager execution + * @ingroup dpkg-internal + * @{ + */ + +struct pager; + +void +pager_enable(bool enable); + +const char * +pager_get_exec(void); + +struct pager * +pager_spawn(const char *desc); + +void +pager_reap(struct pager *pager); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PAGER_H */ diff --git a/lib/dpkg/parse.c b/lib/dpkg/parse.c new file mode 100644 index 0000000..a28983e --- /dev/null +++ b/lib/dpkg/parse.c @@ -0,0 +1,926 @@ +/* + * libdpkg - Debian packaging suite library routines + * parse.c - database file parsing, main package/field loop + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2006, 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef USE_MMAP +#include <sys/mman.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/pkg.h> +#include <dpkg/parsedump.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> + +/** + * Fields information. + */ +const struct fieldinfo fieldinfos[]= { + /* Note: Capitalization of field name strings is important. */ + { FIELD("Package"), f_name, w_name }, + { FIELD("Essential"), f_boolean, w_booleandefno, PKGIFPOFF(essential) }, + { FIELD("Protected"), f_boolean, w_booleandefno, PKGIFPOFF(is_protected) }, + { FIELD("Status"), f_status, w_status }, + { FIELD("Priority"), f_priority, w_priority }, + { FIELD("Section"), f_section, w_section }, + { FIELD("Installed-Size"), f_charfield, w_charfield, PKGIFPOFF(installedsize) }, + { FIELD("Origin"), f_charfield, w_charfield, PKGIFPOFF(origin) }, + { FIELD("Maintainer"), f_charfield, w_charfield, PKGIFPOFF(maintainer) }, + { FIELD("Bugs"), f_charfield, w_charfield, PKGIFPOFF(bugs) }, + { FIELD("Architecture"), f_architecture, w_architecture }, + { FIELD("Multi-Arch"), f_multiarch, w_multiarch, PKGIFPOFF(multiarch) }, + { FIELD("Source"), f_charfield, w_charfield, PKGIFPOFF(source) }, + { FIELD("Version"), f_version, w_version, PKGIFPOFF(version) }, + { FIELD("Config-Version"), f_configversion, w_configversion }, + { FIELD("Replaces"), f_dependency, w_dependency, dep_replaces }, + { FIELD("Provides"), f_dependency, w_dependency, dep_provides }, + { FIELD("Depends"), f_dependency, w_dependency, dep_depends }, + { FIELD("Pre-Depends"), f_dependency, w_dependency, dep_predepends }, + { FIELD("Recommends"), f_dependency, w_dependency, dep_recommends }, + { FIELD("Suggests"), f_dependency, w_dependency, dep_suggests }, + { FIELD("Breaks"), f_dependency, w_dependency, dep_breaks }, + { FIELD("Conflicts"), f_dependency, w_dependency, dep_conflicts }, + { FIELD("Enhances"), f_dependency, w_dependency, dep_enhances }, + { FIELD("Conffiles"), f_conffiles, w_conffiles }, + { FIELD("Filename"), f_archives, w_archives, ARCHIVEFOFF(name) }, + { FIELD("Size"), f_archives, w_archives, ARCHIVEFOFF(size) }, + { FIELD("MD5sum"), f_archives, w_archives, ARCHIVEFOFF(md5sum) }, + { FIELD("MSDOS-Filename"), f_archives, w_archives, ARCHIVEFOFF(msdosname) }, + { FIELD("Description"), f_charfield, w_charfield, PKGIFPOFF(description) }, + { FIELD("Triggers-Pending"), f_trigpend, w_trigpend }, + { FIELD("Triggers-Awaited"), f_trigaw, w_trigaw }, + + /* The following are the obsolete fields that get remapped to their + * modern forms, while emitting an obsolescence warning. */ + { FIELD("Recommended"), f_obs_dependency, w_null, dep_recommends }, + { FIELD("Optional"), f_obs_dependency, w_null, dep_suggests }, + { FIELD("Class"), f_obs_class, w_null }, + { FIELD("Revision"), f_obs_revision, w_null }, + { FIELD("Package-Revision"), f_obs_revision, w_null }, + { FIELD("Package_Revision"), f_obs_revision, w_null }, + { NULL } +}; + +/** + * Package object being parsed. + * + * Structure used to hold the parsed data for the package being constructed, + * before it gets properly inserted into the package database. + */ +struct pkg_parse_object { + struct pkginfo *pkg; + struct pkgbin *pkgbin; +}; + +/** + * Parse the field and value into the package being constructed. + */ +static void +pkg_parse_field(struct parsedb_state *ps, struct field_state *fs, + void *parse_obj) +{ + struct pkg_parse_object *pkg_obj = parse_obj; + const struct fieldinfo *fip; + int *ip; + + for (fip = fieldinfos, ip = fs->fieldencountered; fip->name; fip++, ip++) + if (fip->namelen == (size_t)fs->fieldlen && + strncasecmp(fip->name, fs->fieldstart, fs->fieldlen) == 0) + break; + if (fip->name) { + if ((*ip)++) + parse_error(ps, + _("duplicate value for '%s' field"), fip->name); + + varbuf_set_buf(&fs->value, fs->valuestart, fs->valuelen); + + fip->rcall(pkg_obj->pkg, pkg_obj->pkgbin, ps, fs->value.buf, fip); + } else { + struct arbitraryfield *arp, **larpp; + + if (fs->fieldlen < 2) + parse_error(ps, + _("user-defined field name '%.*s' too short"), + fs->fieldlen, fs->fieldstart); + larpp = &pkg_obj->pkgbin->arbs; + while ((arp = *larpp) != NULL) { + if (strncasecmp(arp->name, fs->fieldstart, fs->fieldlen) == 0 && + strlen(arp->name) == (size_t)fs->fieldlen) + parse_error(ps, + _("duplicate value for user-defined field '%.*s'"), + fs->fieldlen, fs->fieldstart); + larpp = &arp->next; + } + arp = nfmalloc(sizeof(*arp)); + arp->name = nfstrnsave(fs->fieldstart, fs->fieldlen); + arp->value = nfstrnsave(fs->valuestart, fs->valuelen); + arp->next = NULL; + *larpp = arp; + } +} + +/** + * Verify and fixup the package structure being constructed. + */ +static void +pkg_parse_verify(struct parsedb_state *ps, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + struct dependency *dep; + struct deppossi *dop; + + parse_must_have_field(ps, pkg->set->name, "Package"); + + /* XXX: We need to check for status != PKG_STAT_HALFINSTALLED as while + * unpacking an unselected package, it will not have yet all data in + * place. But we cannot check for > PKG_STAT_HALFINSTALLED as + * PKG_STAT_CONFIGFILES always should have those fields. */ + if ((ps->flags & pdb_recordavailable) || + (pkg->status != PKG_STAT_NOTINSTALLED && + pkg->status != PKG_STAT_HALFINSTALLED)) { + parse_ensure_have_field(ps, &pkgbin->description, "Description"); + parse_ensure_have_field(ps, &pkgbin->maintainer, "Maintainer"); + parse_must_have_field(ps, pkgbin->version.version, "Version"); + } + + /* XXX: Versions before dpkg 1.10.19 did not preserve the Architecture + * field in the status file. So there's still live systems with packages + * in PKG_STAT_CONFIGFILES, ignore those too for now. */ + if ((ps->flags & pdb_recordavailable) || + pkg->status > PKG_STAT_HALFINSTALLED) { + /* We always want usable architecture information (as long as the package + * is in such a state that it makes sense), so that it can be used safely + * on string comparisons and the like. */ + if (pkgbin->arch->type == DPKG_ARCH_NONE) + parse_warn(ps, _("missing '%s' field"), "Architecture"); + else if (pkgbin->arch->type == DPKG_ARCH_EMPTY) + parse_warn(ps, _("empty value for '%s' field"), "Architecture"); + } + /* Mark missing architectures as empty, to distinguish these from + * unused slots in the db. */ + if (pkgbin->arch->type == DPKG_ARCH_NONE) + pkgbin->arch = dpkg_arch_get(DPKG_ARCH_EMPTY); + + if (pkgbin->arch->type == DPKG_ARCH_EMPTY && + pkgbin->multiarch == PKG_MULTIARCH_SAME) + parse_error(ps, _("package has '%s' field but is missing architecture"), + "Multi-Arch: same"); + if (pkgbin->arch->type == DPKG_ARCH_ALL && + pkgbin->multiarch == PKG_MULTIARCH_SAME) + parse_error(ps, _("package has '%s' field but is architecture '%s'"), + "Multi-Arch: same", "all"); + + /* Generate the cached fully qualified package name representation. */ + pkgbin->pkgname_archqual = pkgbin_name_archqual(pkg, pkgbin); + + /* Initialize deps to be arch-specific unless stated otherwise. */ + for (dep = pkgbin->depends; dep; dep = dep->next) + for (dop = dep->list; dop; dop = dop->next) + if (!dop->arch) + dop->arch = pkgbin->arch; + + /* + * Check the Config-Version information: + * + * If there is a Config-Version it is definitely to be used, but there + * should not be one if the package is ‘installed’ or ‘triggers-pending’ + * (in which case the Version will be copied) or if the package is + * ‘not-installed’ (in which case there is no Config-Version). + */ + if (!(ps->flags & pdb_recordavailable)) { + if (pkg->configversion.version) { + if (pkg->status == PKG_STAT_INSTALLED || + pkg->status == PKG_STAT_NOTINSTALLED || + pkg->status == PKG_STAT_TRIGGERSPENDING) + parse_error(ps, + _("'%s' field present for package with inappropriate '%s' field"), + "Config-Version", "Status"); + } else { + if (pkg->status == PKG_STAT_INSTALLED || + pkg->status == PKG_STAT_TRIGGERSPENDING) + pkg->configversion = pkgbin->version; + } + } + + if (pkg->trigaw.head && + (pkg->status <= PKG_STAT_CONFIGFILES || + pkg->status >= PKG_STAT_TRIGGERSPENDING)) + parse_error(ps, + _("package has status %s but triggers are awaited"), + pkg_status_name(pkg)); + else if (pkg->status == PKG_STAT_TRIGGERSAWAITED && !pkg->trigaw.head) + parse_error(ps, + _("package has status %s but no triggers awaited"), + pkg_status_name(pkg)); + + if (pkg->trigpend_head && + !(pkg->status == PKG_STAT_TRIGGERSPENDING || + pkg->status == PKG_STAT_TRIGGERSAWAITED)) + parse_error(ps, + _("package has status %s but triggers are pending"), + pkg_status_name(pkg)); + else if (pkg->status == PKG_STAT_TRIGGERSPENDING && !pkg->trigpend_head) + parse_error(ps, + _("package has status %s but no triggers pending"), + pkg_status_name(pkg)); + + /* Note: There was a bug that could make a not-installed package have + * conffiles, so we check for them here and remove them (rather than + * calling it an error, which will do at some point). */ + if (!(ps->flags & pdb_recordavailable) && + pkg->status == PKG_STAT_NOTINSTALLED && + pkgbin->conffiles) { + parse_warn(ps, + _("package has status %s and has conffiles, forgetting them"), + pkg_status_name(pkg)); + pkgbin->conffiles = NULL; + } + + /* Note: Mark not-installed leftover packages for automatic removal on + * next database dump. */ + if (!(ps->flags & pdb_recordavailable) && + pkg->status == PKG_STAT_NOTINSTALLED && + pkg->eflag == PKG_EFLAG_OK && + (pkg->want == PKG_WANT_PURGE || + pkg->want == PKG_WANT_DEINSTALL)) { + pkg_set_want(pkg, PKG_WANT_UNKNOWN); + } + + /* Note: Mark not-installed non-arch-qualified selections for automatic + * removal, as they do not make sense in a multiarch enabled world, and + * might cause those selections to be unreferencable from command-line + * interfaces when there's other more specific selections. */ + if (ps->type == pdb_file_status && + pkg->status == PKG_STAT_NOTINSTALLED && + pkg->eflag == PKG_EFLAG_OK && + pkg->want == PKG_WANT_INSTALL && + pkgbin->arch->type == DPKG_ARCH_EMPTY) + pkg_set_want(pkg, PKG_WANT_UNKNOWN); + + /* Note: Versions before dpkg 1.13.10 did not blank the Origin and Bugs + * fields, so there can be packages that should be garbage collected but + * are lingering around. Blank them to make sure we will forget all about + * them on the next database dump. */ + if (!(ps->flags & pdb_recordavailable) && + pkg->status == PKG_STAT_NOTINSTALLED && + pkg->eflag == PKG_EFLAG_OK && + pkg->want == PKG_WANT_UNKNOWN) { + pkgbin_blank(pkgbin); + } +} + +struct pkgcount { + int single; + int multi; + int total; +}; + +static void +parse_count_pkg_instance(struct pkgcount *count, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + if (pkg->status == PKG_STAT_NOTINSTALLED) + return; + + if (pkgbin->multiarch == PKG_MULTIARCH_SAME) + count->multi++; + else + count->single++; + + count->total++; +} + +/** + * Lookup the package set slot for the parsed package. + * + * Perform various checks, to make sure the database is always in a sane + * state, and to not allow breaking it. + */ +static struct pkgset * +parse_find_set_slot(struct parsedb_state *ps, + struct pkginfo *new_pkg, struct pkgbin *new_pkgbin) +{ + struct pkgcount count = { .single = 0, .multi = 0, .total = 0 }; + struct pkgset *set; + struct pkginfo *pkg; + + set = pkg_hash_find_set(new_pkg->set->name); + + /* Sanity checks: verify that the db is in a consistent state. */ + + if (ps->type == pdb_file_status) + parse_count_pkg_instance(&count, new_pkg, new_pkgbin); + + count.total = 0; + + for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) + parse_count_pkg_instance(&count, pkg, &pkg->installed); + + if (count.single > 1) + parse_error(ps, _("multiple non-coinstallable package instances present; " + "most probably due to an upgrade from an unofficial dpkg")); + + if (count.single > 0 && count.multi > 0) + parse_error(ps, _("mixed non-coinstallable and coinstallable package " + "instances present; most probably due to an upgrade " + "from an unofficial dpkg")); + + if (pkgset_installed_instances(set) != count.total) + internerr("in-core pkgset '%s' with inconsistent number of instances", + set->name); + + return set; +} + +/** + * Lookup the package slot for the parsed package. + * + * Cross-grading (i.e. switching arch) is only possible when parsing an + * update entry or when installing a new package. + * + * Most of the time each pkginfo in a pkgset has the same architecture for + * both the installed and available pkgbin members. But when cross-grading + * there's going to be a temporary discrepancy, because we reuse the single + * instance and fill the available pkgbin with the candidate pkgbin, until + * that is copied over the installed pkgbin. + * + * If there's 0 or > 1 package instances, then we match against the pkginfo + * slot architecture, because cross-grading is just not possible. + * + * If there's 1 instance, we are cross-grading and both installed and + * candidate are not PKG_MULTIARCH_SAME, we have to reuse the existing single + * slot regardless of the arch differing between the two. If we are not + * cross-grading, then we use the entry with the matching arch. + */ +static struct pkginfo * +parse_find_pkg_slot(struct parsedb_state *ps, + struct pkginfo *new_pkg, struct pkgbin *new_pkgbin) +{ + struct pkgset *db_set; + + db_set = parse_find_set_slot(ps, new_pkg, new_pkgbin); + + if (ps->type == pdb_file_available) { + /* If there's a single package installed and the new package is not + * “Multi-Arch: same”, then we preserve the previous behavior of + * possible architecture switch, for example from native to all. */ + if (pkgset_installed_instances(db_set) == 1 && + new_pkgbin->multiarch != PKG_MULTIARCH_SAME) + return pkg_hash_get_singleton(db_set); + else + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); + } else { + bool selection = false; + + /* If the package is part of the status file, and it's not installed + * then this means it's just a selection. */ + if (ps->type == pdb_file_status && new_pkg->status == PKG_STAT_NOTINSTALLED) + selection = true; + + /* Verify we don't allow something that will mess up the db. */ + if (pkgset_installed_instances(db_set) > 1 && + !selection && new_pkgbin->multiarch != PKG_MULTIARCH_SAME) + ohshit(_("package %s (%s) with field '%s: %s' is not co-installable " + "with %s which has multiple installed instances"), + pkgbin_name(new_pkg, new_pkgbin, pnaw_always), + versiondescribe(&new_pkgbin->version, vdew_nonambig), + "Multi-Arch", multiarchinfos[new_pkgbin->multiarch].name, + db_set->name); + + /* If we are parsing the status file, use a slot per arch. */ + if (ps->type == pdb_file_status) + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); + + /* If we are doing an update, from the log or a new package, then + * handle cross-grades. */ + if (pkgset_installed_instances(db_set) == 1) { + struct pkginfo *db_pkg; + + db_pkg = pkg_hash_get_singleton(db_set); + + if (db_pkg->installed.multiarch == PKG_MULTIARCH_SAME && + new_pkgbin->multiarch == PKG_MULTIARCH_SAME) + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); + else + return db_pkg; + } else { + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); + } + } +} + +/** + * Copy into the in-core database the package being constructed. + */ +static void +pkg_parse_copy(struct parsedb_state *ps, + struct pkginfo *dst_pkg, struct pkgbin *dst_pkgbin, + struct pkginfo *src_pkg, struct pkgbin *src_pkgbin) +{ + /* Copy the priority and section across, but don't overwrite existing + * values if the pdb_weakclassification flag is set. */ + if (str_is_set(src_pkg->section) && + !((ps->flags & pdb_weakclassification) && + str_is_set(dst_pkg->section))) + dst_pkg->section = src_pkg->section; + if (src_pkg->priority != PKG_PRIO_UNKNOWN && + !((ps->flags & pdb_weakclassification) && + dst_pkg->priority != PKG_PRIO_UNKNOWN)) { + dst_pkg->priority = src_pkg->priority; + if (src_pkg->priority == PKG_PRIO_OTHER) + dst_pkg->otherpriority = src_pkg->otherpriority; + } + + /* Sort out the dependency mess. */ + copy_dependency_links(dst_pkg, &dst_pkgbin->depends, src_pkgbin->depends, + (ps->flags & pdb_recordavailable) ? true : false); + + /* Copy across data. */ + memcpy(dst_pkgbin, src_pkgbin, sizeof(struct pkgbin)); + if (!(ps->flags & pdb_recordavailable)) { + struct trigaw *ta; + + pkg_set_want(dst_pkg, src_pkg->want); + pkg_copy_eflags(dst_pkg, src_pkg); + pkg_set_status(dst_pkg, src_pkg->status); + dst_pkg->configversion = src_pkg->configversion; + dst_pkg->archives = NULL; + + dst_pkg->trigpend_head = src_pkg->trigpend_head; + dst_pkg->trigaw = src_pkg->trigaw; + for (ta = dst_pkg->trigaw.head; ta; ta = ta->sameaw.next) { + if (ta->aw != src_pkg) + internerr("trigger awaited package %s and origin package %s not linked properly", + pkg_name(ta->aw, pnaw_always), + pkgbin_name(src_pkg, src_pkgbin, pnaw_always)); + ta->aw = dst_pkg; + /* ->othertrigaw_head is updated by trig_note_aw in *(pkg_hash_find()) + * rather than in dst_pkg. */ + } + } else if (!(ps->flags & pdb_ignore_archives)) { + dst_pkg->archives = src_pkg->archives; + } +} + +/** + * Return a descriptive parser type. + */ +static enum parsedbtype +parse_get_type(struct parsedb_state *ps, enum parsedbflags flags) +{ + if (flags & pdb_recordavailable) { + if (flags & pdb_single_stanza) + return pdb_file_control; + else + return pdb_file_available; + } else { + if (flags & pdb_single_stanza) + return pdb_file_update; + else + return pdb_file_status; + } +} + +/** + * Create a new deb822 parser context. + */ +struct parsedb_state * +parsedb_new(const char *filename, int fd, enum parsedbflags flags) +{ + struct parsedb_state *ps; + + ps = m_malloc(sizeof(*ps)); + ps->err = DPKG_ERROR_OBJECT; + ps->errmsg = VARBUF_OBJECT; + ps->filename = filename; + ps->type = parse_get_type(ps, flags); + ps->flags = flags; + ps->fd = fd; + ps->lno = 0; + ps->data = NULL; + ps->dataptr = NULL; + ps->endptr = NULL; + ps->pkg = NULL; + ps->pkgbin = NULL; + + return ps; +} + +/** + * Open a file for deb822 parsing. + */ +struct parsedb_state * +parsedb_open(const char *filename, enum parsedbflags flags) +{ + struct parsedb_state *ps; + int fd; + + /* Special case stdin handling. */ + if (flags & pdb_dash_is_stdin && strcmp(filename, "-") == 0) + return parsedb_new(filename, STDIN_FILENO, flags); + + fd = open(filename, O_RDONLY); + if (fd == -1 && !(errno == ENOENT && (flags & pdb_allow_empty))) + ohshite(_("failed to open package info file '%.255s' for reading"), + filename); + + ps = parsedb_new(filename, fd, flags | pdb_close_fd); + + push_cleanup(cu_closefd, ~ehflag_normaltidy, 1, &ps->fd); + + return ps; +} + +/** + * Load data for package deb822 style parsing. + */ +void +parsedb_load(struct parsedb_state *ps) +{ + struct stat st; + + if (ps->fd < 0 && (ps->flags & pdb_allow_empty)) + return; + + if (fstat(ps->fd, &st) == -1) + ohshite(_("can't stat package info file '%.255s'"), ps->filename); + + if (S_ISFIFO(st.st_mode)) { + struct varbuf buf = VARBUF_INIT; + struct dpkg_error err; + off_t size; + + size = fd_vbuf_copy(ps->fd, &buf, -1, &err); + if (size < 0) + ohshit(_("reading package info file '%s': %s"), ps->filename, err.str); + + varbuf_end_str(&buf); + + ps->dataptr = varbuf_detach(&buf); + ps->endptr = ps->dataptr + size; + } else if (st.st_size > 0) { +#ifdef USE_MMAP + ps->dataptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, ps->fd, 0); + if (ps->dataptr == MAP_FAILED) + ohshite(_("can't mmap package info file '%.255s'"), ps->filename); +#else + ps->dataptr = m_malloc(st.st_size); + + if (fd_read(ps->fd, ps->dataptr, st.st_size) < 0) + ohshite(_("reading package info file '%.255s'"), ps->filename); +#endif + ps->endptr = ps->dataptr + st.st_size; + } else { + ps->dataptr = ps->endptr = NULL; + } + ps->data = ps->dataptr; +} + +/** + * Parse an RFC-822 style stanza. + */ +bool +parse_stanza(struct parsedb_state *ps, struct field_state *fs, + parse_field_func *parse_field, void *parse_obj) +{ + int c = '\0'; + + /* Skip adjacent new lines. */ + while (!parse_at_eof(ps)) { + c = parse_getc(ps); + if (c != '\n' && c != MSDOS_EOF_CHAR) + break; + ps->lno++; + } + + /* Nothing relevant parsed, bail out. */ + if (parse_at_eof(ps)) + return false; + + /* Loop per field. */ + for (;;) { + bool blank_line; + + /* Scan field name. */ + fs->fieldstart = ps->dataptr - 1; + while (!parse_at_eof(ps) && !c_isspace(c) && c != ':' && c != MSDOS_EOF_CHAR) + c = parse_getc(ps); + fs->fieldlen = ps->dataptr - fs->fieldstart - 1; + if (fs->fieldlen == 0) + parse_error(ps, _("empty field name")); + if (fs->fieldstart[0] == '-') + parse_error(ps, _("field name '%.*s' cannot start with hyphen"), + fs->fieldlen, fs->fieldstart); + + /* Skip spaces before ‘:’. */ + while (!parse_at_eof(ps) && c != '\n' && c_isspace(c)) + c = parse_getc(ps); + + /* Validate ‘:’. */ + if (parse_at_eof(ps)) + parse_error(ps, _("end of file after field name '%.*s'"), + fs->fieldlen, fs->fieldstart); + if (c == '\n') + parse_error(ps, + _("newline in field name '%.*s'"), fs->fieldlen, fs->fieldstart); + if (c == MSDOS_EOF_CHAR) + parse_error(ps, _("MSDOS end of file (^Z) in field name '%.*s'"), + fs->fieldlen, fs->fieldstart); + if (c != ':') + parse_error(ps, + _("field name '%.*s' must be followed by colon"), + fs->fieldlen, fs->fieldstart); + + /* Skip space after ‘:’ but before value and EOL. */ + while (!parse_at_eof(ps)) { + c = parse_getc(ps); + if (c == '\n' || !c_isspace(c)) + break; + } + if (parse_at_eof(ps)) + parse_error(ps, _("end of file before value of field '%.*s' (missing final newline)"), + fs->fieldlen, fs->fieldstart); + if (c == MSDOS_EOF_CHAR) + parse_error(ps, _("MSDOS end of file (^Z) in value of field '%.*s' (missing newline?)"), + fs->fieldlen, fs->fieldstart); + + blank_line = false; + + /* Scan field value. */ + fs->valuestart = ps->dataptr - 1; + for (;;) { + if (c == '\n' || c == MSDOS_EOF_CHAR) { + if (blank_line) { + if (ps->flags & pdb_lax_stanza_parser) + parse_warn(ps, _("blank line in value of field '%.*s'"), + fs->fieldlen, fs->fieldstart); + else + parse_error(ps, _("blank line in value of field '%.*s'"), + fs->fieldlen, fs->fieldstart); + } + ps->lno++; + + if (parse_at_eof(ps)) + break; + c = parse_getc(ps); + + /* Found double EOL, or start of new field. */ + if (parse_at_eof(ps) || c == '\n' || !c_isspace(c)) + break; + + parse_ungetc(c, ps); + blank_line = true; + } else if (blank_line && !c_isspace(c)) { + blank_line = false; + } + + if (parse_at_eof(ps)) + parse_error(ps, _("end of file during value of field '%.*s' (missing final newline)"), + fs->fieldlen, fs->fieldstart); + + c = parse_getc(ps); + } + fs->valuelen = ps->dataptr - fs->valuestart - 1; + + /* Trim ending space on value. */ + while (fs->valuelen && c_isspace(*(fs->valuestart + fs->valuelen - 1))) + fs->valuelen--; + + parse_field(ps, fs, parse_obj); + + if (parse_at_eof(ps) || c == '\n' || c == MSDOS_EOF_CHAR) + break; + } /* Loop per field. */ + + if (c == '\n') + ps->lno++; + + return true; +} + +/** + * Teardown a package deb822 parser context. + */ +void +parsedb_close(struct parsedb_state *ps) +{ + if (ps->flags & pdb_close_fd) { + pop_cleanup(ehflag_normaltidy); + + if (ps->fd >= 0 && close(ps->fd) < 0) + ohshite(_("failed to close after read: '%.255s'"), ps->filename); + } + + if (ps->data != NULL) { +#ifdef USE_MMAP + munmap(ps->data, ps->endptr - ps->data); +#else + free(ps->data); +#endif + } + dpkg_error_destroy(&ps->err); + varbuf_destroy(&ps->errmsg); + free(ps); +} + +/** + * Parse deb822 style package data from a buffer. + * + * donep may be NULL. + * If donep is not NULL only one package's information is expected. + */ +int +parsedb_parse(struct parsedb_state *ps, struct pkginfo **donep) +{ + struct pkgset tmp_set; + struct pkginfo *new_pkg, *db_pkg; + struct pkgbin *new_pkgbin, *db_pkgbin; + struct pkg_parse_object pkg_obj; + int fieldencountered[array_count(fieldinfos)]; + int pdone; + struct field_state fs; + + if (ps->data == NULL && (ps->flags & pdb_allow_empty)) + return 0; + + memset(&fs, 0, sizeof(fs)); + fs.fieldencountered = fieldencountered; + + new_pkg = &tmp_set.pkg; + if (ps->flags & pdb_recordavailable) + new_pkgbin = &new_pkg->available; + else + new_pkgbin = &new_pkg->installed; + + ps->pkg = new_pkg; + ps->pkgbin = new_pkgbin; + + pkg_obj.pkg = new_pkg; + pkg_obj.pkgbin = new_pkgbin; + + pdone= 0; + + /* Loop per package. */ + for (;;) { + memset(fieldencountered, 0, sizeof(fieldencountered)); + pkgset_blank(&tmp_set); + + if (!parse_stanza(ps, &fs, pkg_parse_field, &pkg_obj)) + break; + + if (pdone && donep) + parse_error(ps, + _("several package info entries found, only one allowed")); + + pkg_parse_verify(ps, new_pkg, new_pkgbin); + + db_pkg = parse_find_pkg_slot(ps, new_pkg, new_pkgbin); + if (ps->flags & pdb_recordavailable) + db_pkgbin = &db_pkg->available; + else + db_pkgbin = &db_pkg->installed; + + if (((ps->flags & pdb_ignoreolder) || ps->type == pdb_file_available) && + dpkg_version_is_informative(&db_pkgbin->version) && + dpkg_version_compare(&new_pkgbin->version, &db_pkgbin->version) < 0) + continue; + + pkg_parse_copy(ps, db_pkg, db_pkgbin, new_pkg, new_pkgbin); + + if (donep) + *donep = db_pkg; + pdone++; + if (parse_at_eof(ps)) + break; + } + + varbuf_destroy(&fs.value); + if (donep && !pdone) + ohshit(_("no package information in '%.255s'"), ps->filename); + + return pdone; +} + +/** + * Parse a deb822 style file. + * + * donep may be NULL. + * If donep is not NULL only one package's information is expected. + */ +int +parsedb(const char *filename, enum parsedbflags flags, struct pkginfo **pkgp) +{ + struct parsedb_state *ps; + int count; + + ps = parsedb_open(filename, flags); + parsedb_load(ps); + count = parsedb_parse(ps, pkgp); + parsedb_close(ps); + + return count; +} + +/** + * Copy dependency links structures. + * + * This routine is used to update the ‘reverse’ dependency pointers when + * new ‘forwards’ information has been constructed. It first removes all + * the links based on the old information. The old information starts in + * *updateme; after much brou-ha-ha the reverse structures are created + * and *updateme is set to the value from newdepends. + * + * @param pkg The package we're doing this for. This is used to construct + * correct uplinks. + * @param updateme The forwards dependency pointer that we are to update. + * This starts out containing the old forwards info, which we use to + * unthread the old reverse links. After we're done it is updated. + * @param newdepends The value that we ultimately want to have in updateme. + * @param available The pkgbin to modify, available or installed. + * + * It is likely that the backward pointer for the package in question + * (‘depended’) will be updated by this routine, but this will happen by + * the routine traversing the dependency data structures. It doesn't need + * to be told where to update that; just mentioned here as something that + * one should be cautious about. + */ +void copy_dependency_links(struct pkginfo *pkg, + struct dependency **updateme, + struct dependency *newdepends, + bool available) +{ + struct dependency *dyp; + struct deppossi *dop, **revdeps; + + /* Delete ‘backward’ (‘depended’) links from other packages to + * dependencies listed in old version of this one. We do this by + * going through all the dependencies in the old version of this + * one and following them down to find which deppossi nodes to + * remove. */ + for (dyp= *updateme; dyp; dyp= dyp->next) { + for (dop= dyp->list; dop; dop= dop->next) { + if (dop->rev_prev) + dop->rev_prev->rev_next = dop->rev_next; + else + if (available) + dop->ed->depended.available = dop->rev_next; + else + dop->ed->depended.installed = dop->rev_next; + if (dop->rev_next) + dop->rev_next->rev_prev = dop->rev_prev; + } + } + + /* Now fill in new ‘ed’ links from other packages to dependencies + * listed in new version of this one, and set our uplinks correctly. */ + for (dyp= newdepends; dyp; dyp= dyp->next) { + dyp->up= pkg; + for (dop= dyp->list; dop; dop= dop->next) { + revdeps = available ? &dop->ed->depended.available : + &dop->ed->depended.installed; + dop->rev_next = *revdeps; + dop->rev_prev = NULL; + if (*revdeps) + (*revdeps)->rev_prev = dop; + *revdeps = dop; + } + } + + /* Finally, we fill in the new value. */ + *updateme= newdepends; +} diff --git a/lib/dpkg/parsedump.h b/lib/dpkg/parsedump.h new file mode 100644 index 0000000..c43da7f --- /dev/null +++ b/lib/dpkg/parsedump.h @@ -0,0 +1,179 @@ +/* + * libdpkg - Debian packaging suite library routines + * parsedump.h - declarations for in-core database reading/writing + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2001 Wichert Akkerman + * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PARSEDUMP_H +#define LIBDPKG_PARSEDUMP_H + +#include <stdint.h> + +#include <dpkg/error.h> +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup parsedump In-core package database parsing and reading + * @ingroup dpkg-public + * @{ + */ + +struct fieldinfo; + +/** + * Parse action. + */ +enum parsedbtype { + pdb_file_update, + pdb_file_status, + pdb_file_control, + pdb_file_available, +}; + +struct parsedb_state { + enum parsedbtype type; + enum parsedbflags flags; + struct dpkg_error err; + struct varbuf errmsg; + struct pkginfo *pkg; + struct pkgbin *pkgbin; + char *data; + char *dataptr; + char *endptr; + const char *filename; + int fd; + int lno; +}; + +#define parse_at_eof(ps) ((ps)->dataptr >= (ps)->endptr) +#define parse_getc(ps) *(ps)->dataptr++ +#define parse_ungetc(c, ps) (ps)->dataptr-- + +struct field_state { + const char *fieldstart; + const char *valuestart; + struct varbuf value; + int fieldlen; + int valuelen; + int *fieldencountered; +}; + +struct parsedb_state * +parsedb_new(const char *filename, int fd, enum parsedbflags flags); +struct parsedb_state * +parsedb_open(const char *filename, enum parsedbflags flags); +void +parsedb_load(struct parsedb_state *ps); +int +parsedb_parse(struct parsedb_state *ps, struct pkginfo **pkgp); +void +parsedb_close(struct parsedb_state *ps); + +typedef void parse_field_func(struct parsedb_state *ps, struct field_state *fs, + void *parse_obj); + +bool parse_stanza(struct parsedb_state *ps, struct field_state *fs, + parse_field_func *parse_field, void *parse_obj); + +#define STRUCTFIELD(klass, off, type) (*(type *)((uintptr_t)(klass) + (off))) + +#define PKGIFPOFF(f) (offsetof(struct pkgbin, f)) +#define ARCHIVEFOFF(f) (offsetof(struct archivedetails, f)) + +typedef void freadfunction(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip); +freadfunction f_name; +freadfunction f_charfield; +freadfunction f_priority; +freadfunction f_obs_class; +freadfunction f_section; +freadfunction f_status; +freadfunction f_boolean; +freadfunction f_dependency; +freadfunction f_conffiles; +freadfunction f_version; +freadfunction f_obs_revision; +freadfunction f_obs_dependency; +freadfunction f_configversion; +freadfunction f_multiarch; +freadfunction f_architecture; +freadfunction f_trigpend, f_trigaw; +freadfunction f_archives; + +enum DPKG_ATTR_ENUM_FLAGS fwriteflags { + /** No flags. */ + fw_none = 0, + /** Print field header and trailing newline. */ + fw_printheader = DPKG_BIT(0), +}; + +typedef void fwritefunction(struct varbuf*, + const struct pkginfo *, const struct pkgbin *, + enum fwriteflags flags, const struct fieldinfo*); +fwritefunction w_name, w_charfield, w_priority, w_section, w_status, w_configversion; +fwritefunction w_version, w_null, w_booleandefno, w_dependency, w_conffiles; +fwritefunction w_multiarch; +fwritefunction w_architecture; +fwritefunction w_trigpend, w_trigaw; +fwritefunction w_archives; + +void +varbuf_add_arbfield(struct varbuf *vb, const struct arbitraryfield *arbfield, + enum fwriteflags flags); + +#define FIELD(name) name, sizeof(name) - 1 + +struct fieldinfo { + const char *name; + size_t namelen; + freadfunction *rcall; + fwritefunction *wcall; + size_t integer; +}; + +int +parse_db_version(struct parsedb_state *ps, + struct dpkg_version *version, const char *value) + DPKG_ATTR_REQRET; + +void parse_error(struct parsedb_state *ps, const char *fmt, ...) + DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2); +void parse_warn(struct parsedb_state *ps, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); +void +parse_problem(struct parsedb_state *ps, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); + +void parse_must_have_field(struct parsedb_state *ps, + const char *value, const char *what); +void parse_ensure_have_field(struct parsedb_state *ps, + const char **value, const char *what); + +#define MSDOS_EOF_CHAR '\032' /* ^Z */ + +extern const struct fieldinfo fieldinfos[]; + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PARSEDUMP_H */ diff --git a/lib/dpkg/parsehelp.c b/lib/dpkg/parsehelp.c new file mode 100644 index 0000000..63a36f5 --- /dev/null +++ b/lib/dpkg/parsehelp.c @@ -0,0 +1,343 @@ +/* + * libdpkg - Debian packaging suite library routines + * parsehelp.c - helpful routines for parsing and writing + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2006-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/error.h> +#include <dpkg/parsedump.h> + +static DPKG_ATTR_VPRINTF(2) const char * +parse_error_msg(struct parsedb_state *ps, const char *fmt, va_list args) +{ + struct varbuf *vb = &ps->errmsg; + + varbuf_reset(vb); + + if (ps->pkg && ps->pkg->set->name) + varbuf_printf(vb, _("parsing file '%s' near line %d package '%s':\n "), + ps->filename, ps->lno, + pkgbin_name(ps->pkg, ps->pkgbin, pnaw_nonambig)); + else + varbuf_printf(vb, _("parsing file '%.255s' near line %d:\n "), + ps->filename, ps->lno); + + varbuf_vprintf(vb, fmt, args); + + return vb->buf; +} + +void +parse_error(struct parsedb_state *ps, const char *fmt, ...) +{ + va_list args; + const char *str; + + va_start(args, fmt); + str = parse_error_msg(ps, fmt, args); + va_end(args); + + ohshit("%s", str); +} + +void +parse_warn(struct parsedb_state *ps, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + warning("%s", parse_error_msg(ps, fmt, args)); + va_end(args); +} + +void +parse_problem(struct parsedb_state *ps, const char *fmt, ...) +{ + va_list args; + const char *str; + + va_start(args, fmt); + str = parse_error_msg(ps, fmt, args); + va_end(args); + + if (ps->err.type == DPKG_MSG_WARN) + warning("%s: %s", str, ps->err.str); + else + ohshit("%s: %s", str, ps->err.str); +} + +const struct fieldinfo * +find_field_info(const struct fieldinfo *fields, const char *fieldname) +{ + const struct fieldinfo *field; + + for (field = fields; field->name; field++) + if (strcasecmp(field->name, fieldname) == 0) + return field; + + return NULL; +} + +const struct arbitraryfield * +find_arbfield_info(const struct arbitraryfield *arbs, const char *fieldname) +{ + const struct arbitraryfield *arbfield; + + for (arbfield = arbs; arbfield; arbfield = arbfield->next) + if (strcasecmp(arbfield->name, fieldname) == 0) + return arbfield; + + return NULL; +} + +const char * +pkg_name_is_illegal(const char *p) +{ + /* TODO: _ is deprecated, remove sometime. */ + static const char alsoallowed[] = "-+._"; + static char buf[150]; + int c; + + if (!*p) return _("may not be empty string"); + if (!c_isalnum(*p)) + return _("must start with an alphanumeric character"); + while ((c = *p++) != '\0') + if (!c_isalnum(c) && !strchr(alsoallowed, c)) + break; + if (!c) return NULL; + + snprintf(buf, sizeof(buf), _( + "character '%c' not allowed (only letters, digits and characters '%s')"), + c, alsoallowed); + return buf; +} + +void varbufversion +(struct varbuf *vb, + const struct dpkg_version *version, + enum versiondisplayepochwhen vdew) +{ + switch (vdew) { + case vdew_never: + break; + case vdew_nonambig: + if (!version->epoch && + (!version->version || !strchr(version->version,':')) && + (!version->revision || !strchr(version->revision,':'))) break; + /* Fall through. */ + case vdew_always: + varbuf_printf(vb, "%u:", version->epoch); + break; + default: + internerr("unknown versiondisplayepochwhen '%d'", vdew); + } + if (version->version) + varbuf_add_str(vb, version->version); + if (str_is_set(version->revision)) { + varbuf_add_char(vb, '-'); + varbuf_add_str(vb, version->revision); + } +} + +const char *versiondescribe +(const struct dpkg_version *version, + enum versiondisplayepochwhen vdew) +{ + static struct varbuf bufs[10]; + static int bufnum=0; + + struct varbuf *vb; + + if (!dpkg_version_is_informative(version)) + return C_("version", "<none>"); + + vb= &bufs[bufnum]; bufnum++; if (bufnum == 10) bufnum= 0; + varbuf_reset(vb); + varbufversion(vb,version,vdew); + varbuf_end_str(vb); + + return vb->buf; +} + +const char * +versiondescribe_c(const struct dpkg_version *version, + enum versiondisplayepochwhen vdew) +{ + struct dpkg_locale oldloc; + const char *str; + + oldloc = dpkg_locale_switch_C(); + str = versiondescribe(version, vdew); + dpkg_locale_switch_back(oldloc); + + return str; +} + +/** + * Parse a version string and check for invalid syntax. + * + * Distinguish between lax (warnings) and strict (error) parsing. + * + * @param rversion The parsed version. + * @param string The version string to parse. + * @param err The warning or error message if any. + * + * @retval 0 On success. + * @retval -1 On failure, and err is set accordingly. + */ +int +parseversion(struct dpkg_version *rversion, const char *string, + struct dpkg_error *err) +{ + char *hyphen, *colon, *eepochcolon; + const char *end, *ptr; + + /* Trim leading and trailing space. */ + while (*string && c_isblank(*string)) + string++; + + if (!*string) + return dpkg_put_error(err, _("version string is empty")); + + /* String now points to the first non-whitespace char. */ + end = string; + /* Find either the end of the string, or a whitespace char. */ + while (*end && !c_isblank(*end)) + end++; + /* Check for extra chars after trailing space. */ + ptr = end; + while (*ptr && c_isblank(*ptr)) + ptr++; + if (*ptr) + return dpkg_put_error(err, _("version string has embedded spaces")); + + colon= strchr(string,':'); + if (colon) { + long epoch; + + errno = 0; + epoch = strtol(string, &eepochcolon, 10); + if (string == eepochcolon) + return dpkg_put_error(err, _("epoch in version is empty")); + if (colon != eepochcolon) + return dpkg_put_error(err, _("epoch in version is not number")); + if (epoch < 0) + return dpkg_put_error(err, _("epoch in version is negative")); + if (epoch > INT_MAX || errno == ERANGE) + return dpkg_put_error(err, _("epoch in version is too big")); + if (!*++colon) + return dpkg_put_error(err, _("nothing after colon in version number")); + string= colon; + rversion->epoch= epoch; + } else { + rversion->epoch= 0; + } + rversion->version= nfstrnsave(string,end-string); + hyphen= strrchr(rversion->version,'-'); + if (hyphen) { + *hyphen++ = '\0'; + + if (*hyphen == '\0') + return dpkg_put_error(err, _("revision number is empty")); + } + rversion->revision= hyphen ? hyphen : ""; + + /* XXX: Would be faster to use something like cisversion and cisrevision. */ + ptr = rversion->version; + if (!*ptr) + return dpkg_put_error(err, _("version number is empty")); + if (!c_isdigit(*ptr++)) + return dpkg_put_warn(err, _("version number does not start with digit")); + for (; *ptr; ptr++) { + if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".-+~:", *ptr) == NULL) + return dpkg_put_warn(err, _("invalid character in version number")); + } + for (ptr = rversion->revision; *ptr; ptr++) { + if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".+~", *ptr) == NULL) + return dpkg_put_warn(err, _("invalid character in revision number")); + } + + return 0; +} + +/** + * Parse a version string coming from a database file. + * + * It parses a version string, and prints a warning or an error depending + * on the parse options. + * + * @param ps The parsedb state. + * @param version The version to parse into. + * @param value The version string to parse from. + * + * @retval 0 On success, and err is reset. + * @retval -1 On failure, and err is set accordingly. + */ +int +parse_db_version(struct parsedb_state *ps, struct dpkg_version *version, + const char *value) +{ + dpkg_error_destroy(&ps->err); + + if (parseversion(version, value, &ps->err) == 0) + return 0; + + /* If not in lax mode, turn everything into an error. */ + if (!(ps->flags & pdb_lax_version_parser)) + ps->err.type = DPKG_MSG_ERROR; + + return -1; +} + +void +parse_must_have_field(struct parsedb_state *ps, + const char *value, const char *what) +{ + if (!value) + parse_error(ps, _("missing '%s' field"), what); + else if (!*value) + parse_error(ps, _("empty value for '%s' field"), what); +} + +void +parse_ensure_have_field(struct parsedb_state *ps, + const char **value, const char *what) +{ + static const char empty[] = ""; + + if (!*value) { + parse_warn(ps, _("missing '%s' field"), what); + *value = empty; + } else if (!**value) { + parse_warn(ps, _("empty value for '%s' field"), what); + } +} diff --git a/lib/dpkg/path-remove.c b/lib/dpkg/path-remove.c new file mode 100644 index 0000000..6afb376 --- /dev/null +++ b/lib/dpkg/path-remove.c @@ -0,0 +1,158 @@ +/* + * libdpkg - Debian packaging suite library routines + * path-remove.c - path removal functions + * + * Copyright © 1994-1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/path.h> +#include <dpkg/debug.h> +#include <dpkg/subproc.h> + +int +secure_unlink_statted(const char *pathname, const struct stat *stab) +{ + if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) : + !(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) || + S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) { + if (chmod(pathname, 0600)) + return -1; + } + + if (unlink(pathname)) + return -1; + + return 0; +} + +/** + * Securely unlink a pathname. + * + * If the pathname to remove is: + * + * 1. a sticky or set-id file, or + * 2. an unknown object (i.e., not a file, link, directory, fifo or socket) + * + * we change its mode so that a malicious user cannot use it, even if it's + * linked to another file. + */ +int +secure_unlink(const char *pathname) +{ + struct stat stab; + + if (lstat(pathname, &stab)) + return -1; + + return secure_unlink_statted(pathname, &stab); +} + +/** + * Securely remove a pathname. + * + * This is a secure version of remove(3) using secure_unlink() instead of + * unlink(2). + * + * @retval 0 On success. + * @retval -1 On failure, just like unlink(2) & rmdir(2). + */ +int +secure_remove(const char *pathname) +{ + int rc, e; + + if (!rmdir(pathname)) { + debug(dbg_eachfiledetail, "secure_remove '%s' rmdir OK", + pathname); + return 0; + } + + if (errno != ENOTDIR) { + e = errno; + debug(dbg_eachfiledetail, "secure_remove '%s' rmdir %s", + pathname, strerror(e)); + errno = e; + return -1; + } + + rc = secure_unlink(pathname); + e = errno; + debug(dbg_eachfiledetail, "secure_remove '%s' unlink %s", + pathname, rc ? strerror(e) : "OK"); + errno = e; + + return rc; +} + +/** + * Remove a pathname and anything below it. + * + * This function removes pathname and all its contents recursively. + */ +void +path_remove_tree(const char *pathname) +{ + pid_t pid; + const char *u; + + u = path_skip_slash_dotslash(pathname); + if (u[0] == '\0') + internerr("pathname '%s' reduces to nothing", pathname); + + debug(dbg_eachfile, "%s '%s'", __func__, pathname); + if (!rmdir(pathname)) + return; /* Deleted it OK, it was a directory. */ + if (errno == ENOENT || errno == ELOOP) + return; + if (errno == ENOTDIR) { + /* Either it's a file, or one of the path components is. If + * one of the path components is this will fail again ... */ + if (secure_unlink(pathname) == 0) + return; /* OK, it was. */ + if (errno == ENOTDIR) + return; + } + /* Trying to remove a directory or a file on a read-only filesystem, + * even if non-existent, always returns EROFS. */ + if (errno == EROFS) { + if (access(pathname, F_OK) < 0 && errno == ENOENT) + return; + errno = EROFS; + } + if (errno != ENOTEMPTY && errno != EEXIST) /* Huh? */ + ohshite(_("unable to securely remove '%.255s'"), pathname); + + pid = subproc_fork(); + if (pid == 0) { + execlp(RM, "rm", "-rf", "--", pathname, NULL); + ohshite(_("unable to execute %s (%s)"), + _("rm command for cleanup"), RM); + } + debug(dbg_eachfile, "%s running rm -rf '%s'", __func__, pathname); + subproc_reap(pid, _("rm command for cleanup"), 0); +} diff --git a/lib/dpkg/path.c b/lib/dpkg/path.c new file mode 100644 index 0000000..d7b20d6 --- /dev/null +++ b/lib/dpkg/path.c @@ -0,0 +1,170 @@ +/* + * libdpkg - Debian packaging suite library routines + * path.c - path handling functions + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <dpkg/dpkg.h> +#include <dpkg/string.h> +#include <dpkg/path.h> + +/** + * Trim ‘/’ and ‘/.’ from the end of a pathname. + * + * The given string will get NUL-terminatd. + * + * @param path The pathname to trim. + * + * @return The size of the trimmed pathname. + */ +size_t +path_trim_slash_slashdot(char *path) +{ + char *end; + + if (str_is_unset(path)) + return 0; + + for (end = path + strlen(path) - 1; end - path >= 1; end--) { + if (*end == '/' || (*(end - 1) == '/' && *end == '.')) + *end = '\0'; + else + break; + } + + return end - path + 1; +} + +/** + * Skip ‘/’ and ‘./’ from the beginning of a pathname. + * + * @param path The pathname to skip. + * + * @return The new beginning of the pathname. + */ +const char * +path_skip_slash_dotslash(const char *path) +{ + while (path[0] == '/' || (path[0] == '.' && path[1] == '/')) + path++; + + return path; +} + +/** + * Return the last component of a pathname. + * + * @param path The pathname to get the base name from. + * + * @return A pointer to the last component inside pathname. + */ +const char * +path_basename(const char *path) +{ + const char *last_slash; + + last_slash = strrchr(path, '/'); + if (last_slash == NULL) + return path; + else + return last_slash + 1; +} + +/** + * Create a template for a temporary pathname. + * + * @param suffix The suffix to use for the template string. + * + * @return An allocated string with the created template. + */ +char * +path_make_temp_template(const char *suffix) +{ + const char *tmpdir; + + tmpdir = getenv("TMPDIR"); + if (!tmpdir) + tmpdir = P_tmpdir; + + return str_fmt("%s/%s.XXXXXX", tmpdir, suffix); +} + +/** + * Escape characters in a pathname for safe locale printing. + * + * We need to quote paths so that they do not cause problems when printing + * them, for example with snprintf(3) which does not work if the format + * string contains %s and an argument has invalid characters for the + * current locale, it will then return -1. + * + * To simplify things, we just escape all 8 bit characters, instead of + * just invalid characters. + * + * @param dst The escaped destination string. + * @param src The source string to escape. + * @param n The size of the destination buffer. + * + * @return The destination string. + */ +char * +path_quote_filename(char *dst, const char *src, size_t n) +{ + char *ret = dst; + ssize_t size = (ssize_t)n; + + if (size == 0) + return ret; + + while (*src) { + if (*src == '\\') { + size -= 2; + if (size <= 0) + break; + + *dst++ = '\\'; + *dst++ = '\\'; + src++; + } else if (((*src) & 0x80) == '\0') { + size--; + if (size <= 0) + break; + + *dst++ = *src++; + } else { + size -= 4; + if (size <= 0) + break; + + sprintf(dst, "\\%03o", + *(const unsigned char *)src); + dst += 4; + src++; + } + } + + *dst = '\0'; + + return ret; +} diff --git a/lib/dpkg/path.h b/lib/dpkg/path.h new file mode 100644 index 0000000..3479a8b --- /dev/null +++ b/lib/dpkg/path.h @@ -0,0 +1,55 @@ +/* + * libdpkg - Debian packaging suite library routines + * path.h - path handling routines + * + * Copyright © 2008-2012, 2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PATH_H +#define LIBDPKG_PATH_H + +#include <sys/stat.h> + +#include <stddef.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup path Path handling + * @ingroup dpkg-internal + * @{ + */ + +size_t path_trim_slash_slashdot(char *path); +const char *path_skip_slash_dotslash(const char *path); +const char *path_basename(const char *path); +char *path_quote_filename(char *dst, const char *src, size_t size); + +char *path_make_temp_template(const char *suffix); + +int secure_unlink_statted(const char *pathname, const struct stat *stab); +int secure_unlink(const char *pathname); +int secure_remove(const char *pathname); + +void path_remove_tree(const char *pathname); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PATH_H */ diff --git a/lib/dpkg/perf.h b/lib/dpkg/perf.h new file mode 100644 index 0000000..48e69cc --- /dev/null +++ b/lib/dpkg/perf.h @@ -0,0 +1,77 @@ +/* + * libdpkg - Debian packaging suite library routines + * perf.h - performance testing support + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PERF_H +#define LIBDPKG_PERF_H + +#include <config.h> +#include <compat.h> + +#include <time.h> +#include <stdio.h> + +#define TEST_OMIT_VARIABLES +#include <dpkg/test.h> + +DPKG_BEGIN_DECLS + +struct perf_slot { + struct timespec t_ini, t_end; +}; + +static inline void +perf_ts_sub(struct timespec *a, struct timespec *b, struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += 1000000000; + } +} + +static inline void +perf_ts_mark_print(const char *str) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + printf("%lu.%.9lu: %s\n", ts.tv_sec, ts.tv_nsec, str); +} + +static inline void +perf_ts_slot_print(struct perf_slot *ps, const char *str) +{ + struct timespec t_res; + + perf_ts_sub(&ps->t_end, &ps->t_ini, &t_res); + + printf("%lu.%.9lu: %s (%lu.%.9lu sec)\n", + ps->t_end.tv_sec, ps->t_end.tv_nsec, + str, t_res.tv_sec, t_res.tv_nsec); +} + +#define perf_ts_slot_start(ps) clock_gettime(CLOCK_MONOTONIC, &((ps)->t_ini)) +#define perf_ts_slot_stop(ps) clock_gettime(CLOCK_MONOTONIC, &((ps)->t_end)) + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/pkg-array.c b/lib/dpkg/pkg-array.c new file mode 100644 index 0000000..0ce285e --- /dev/null +++ b/lib/dpkg/pkg-array.c @@ -0,0 +1,129 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-array.c - primitives for pkg array handling + * + * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2009-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/pkg-array.h> + +/** + * Initialize a package array from package names. + * + * @param a The array to initialize. + * @param pkg_mapper A function that maps a package name to a package instance. + * @param pkg_names The package names list. + */ +void +pkg_array_init_from_names(struct pkg_array *a, pkg_mapper_func pkg_mapper, + const char **pkg_names) +{ + int i = 0; + + while (pkg_names[i]) + i++; + + a->n_pkgs = i; + a->pkgs = m_malloc(sizeof(a->pkgs[0]) * a->n_pkgs); + + for (i = 0; pkg_names[i]; i++) + a->pkgs[i] = pkg_mapper(pkg_names[i]); +} + +/** + * Initialize a package array from the package database. + * + * @param a The array to initialize. + */ +void +pkg_array_init_from_hash(struct pkg_array *a) +{ + struct pkg_hash_iter *iter; + struct pkginfo *pkg; + int i; + + a->n_pkgs = pkg_hash_count_pkg(); + a->pkgs = m_malloc(sizeof(a->pkgs[0]) * a->n_pkgs); + + iter = pkg_hash_iter_new(); + for (i = 0; (pkg = pkg_hash_iter_next_pkg(iter)); i++) + a->pkgs[i] = pkg; + pkg_hash_iter_free(iter); + + if (i != a->n_pkgs) + internerr("inconsistent state in pkg array: i=%d != npkgs=%d", + i, a->n_pkgs); +} + +/** + * Visit each non-NULL package in a package array. + * + * @param a The array to visit. + * @param pkg_visitor The function to visit each item of the array. + * @param pkg_data Data to pass pkg_visit for each package visited. + */ +void +pkg_array_foreach(struct pkg_array *a, pkg_array_visitor_func *pkg_visitor, + void *pkg_data) +{ + int i; + + for (i = 0; i < a->n_pkgs; i++) { + struct pkginfo *pkg = a->pkgs[i]; + + if (pkg == NULL) + continue; + + pkg_visitor(a, pkg, pkg_data); + } +} + +/** + * Sort a package array. + * + * @param a The array to sort. + * @param pkg_sort The function to sort the array. + */ +void +pkg_array_sort(struct pkg_array *a, pkg_sorter_func *pkg_sort) +{ + qsort(a->pkgs, a->n_pkgs, sizeof(a->pkgs[0]), pkg_sort); +} + +/** + * Destroy a package array. + * + * Frees the allocated memory and resets the members. + * + * @param a The array to destroy. + */ +void +pkg_array_destroy(struct pkg_array *a) +{ + a->n_pkgs = 0; + free(a->pkgs); + a->pkgs = NULL; +} diff --git a/lib/dpkg/pkg-array.h b/lib/dpkg/pkg-array.h new file mode 100644 index 0000000..f9ea385 --- /dev/null +++ b/lib/dpkg/pkg-array.h @@ -0,0 +1,59 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-array.h - primitives for pkg array handling + * + * Copyright © 2009-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_ARRAY_H +#define LIBDPKG_PKG_ARRAY_H + +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-array Package array primitives + * @ingroup dpkg-public + * @{ + */ + +/** + * Holds an array of pointers to package data. + */ +struct pkg_array { + int n_pkgs; + struct pkginfo **pkgs; +}; + +typedef struct pkginfo *pkg_mapper_func(const char *name); +typedef void pkg_array_visitor_func(struct pkg_array *a, struct pkginfo *pkg, + void *pkg_data); + +void pkg_array_init_from_hash(struct pkg_array *a); +void pkg_array_init_from_names(struct pkg_array *a, pkg_mapper_func *pkg_mapper, + const char **pkg_names); +void pkg_array_foreach(struct pkg_array *a, pkg_array_visitor_func *pkg_visitor, + void *pkg_data); +void pkg_array_sort(struct pkg_array *a, pkg_sorter_func *pkg_sort); +void pkg_array_destroy(struct pkg_array *a); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_ARRAY_H */ diff --git a/lib/dpkg/pkg-files.c b/lib/dpkg/pkg-files.c new file mode 100644 index 0000000..f022132 --- /dev/null +++ b/lib/dpkg/pkg-files.c @@ -0,0 +1,95 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-files.c - handle list of filesystem files per package + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> +#include <dpkg/pkg-files.h> + +/** + * Erase the files saved in pkg. + */ +void +pkg_files_blank(struct pkginfo *pkg) +{ + struct fsys_namenode_list *current; + + for (current = pkg->files; + current; + current = current->next) { + struct pkg_list **pkg_prev = ¤t->namenode->packages; + struct pkg_list *pkg_node; + + /* For each file that used to be in the package, go through + * looking for this package's entry in the list of packages + * containing this file, and blank it out. */ + for (pkg_node = current->namenode->packages; + pkg_node; + pkg_node = pkg_node->next) { + if (pkg_node->pkg == pkg) { + *pkg_prev = pkg_node->next; + + /* The actual filelist links were allocated + * w/ nfmalloc, so we should not free them. */ + break; + } + + pkg_prev = &pkg_node->next; + } + } + pkg->files = NULL; +} + +struct fsys_namenode_list ** +pkg_files_add_file(struct pkginfo *pkg, struct fsys_namenode *namenode, + struct fsys_namenode_list **file_tail) +{ + struct fsys_namenode_list *newent; + struct pkg_list *pkg_node; + + if (file_tail == NULL) + file_tail = &pkg->files; + + /* Make sure we're at the end. */ + while ((*file_tail) != NULL) + file_tail = &((*file_tail)->next); + + /* Create a new node. */ + newent = nfmalloc(sizeof(*newent)); + newent->namenode = namenode; + newent->next = NULL; + *file_tail = newent; + file_tail = &newent->next; + + /* Add pkg to newent's package list. */ + pkg_node = nfmalloc(sizeof(*pkg_node)); + pkg_node->pkg = pkg; + pkg_node->next = newent->namenode->packages; + newent->namenode->packages = pkg_node; + + /* Return the position for the next guy. */ + return file_tail; +} diff --git a/lib/dpkg/pkg-files.h b/lib/dpkg/pkg-files.h new file mode 100644 index 0000000..bee137a --- /dev/null +++ b/lib/dpkg/pkg-files.h @@ -0,0 +1,46 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-files.h - primitives for pkg files handling + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_FILES_H +#define LIBDPKG_PKG_FILES_H + +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-files Package files handling + * @ingroup dpkg-public + * @{ + */ + +void +pkg_files_blank(struct pkginfo *pkg); + +struct fsys_namenode_list ** +pkg_files_add_file(struct pkginfo *pkg, struct fsys_namenode *namenode, + struct fsys_namenode_list **file_tail); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_FILES_H */ diff --git a/lib/dpkg/pkg-format.c b/lib/dpkg/pkg-format.c new file mode 100644 index 0000000..b984012 --- /dev/null +++ b/lib/dpkg/pkg-format.c @@ -0,0 +1,475 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-format.c - customizable package formatting + * + * Copyright © 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/error.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> +#include <dpkg/parsedump.h> +#include <dpkg/pkg-show.h> +#include <dpkg/pkg-format.h> + +enum pkg_format_type { + PKG_FORMAT_INVALID, + PKG_FORMAT_STRING, + PKG_FORMAT_FIELD, +}; + +struct pkg_format_node { + struct pkg_format_node *next; + enum pkg_format_type type; + int width; + char *data; +}; + + +static struct pkg_format_node * +pkg_format_node_new(void) +{ + struct pkg_format_node *buf; + + buf = m_malloc(sizeof(*buf)); + buf->type = PKG_FORMAT_INVALID; + buf->next = NULL; + buf->data = NULL; + buf->width = 0; + + return buf; +} + +static bool +parsefield(struct pkg_format_node *node, const char *fmt, const char *fmtend, + struct dpkg_error *err) +{ + int len; + const char *ws; + + len = fmtend - fmt + 1; + + ws = memchr(fmt, ';', len); + if (ws) { + char *endptr; + long w; + + errno = 0; + w = strtol(ws + 1, &endptr, 0); + if (endptr[0] != '}') { + dpkg_put_error(err, + _("invalid character '%c' in field width"), + *endptr); + return false; + } + if (w < INT_MIN || w > INT_MAX || errno == ERANGE) { + dpkg_put_error(err, _("field width is out of range")); + return false; + } + + node->width = w; + + len = ws - fmt; + } + + node->type = PKG_FORMAT_FIELD; + node->data = m_malloc(len + 1); + memcpy(node->data, fmt, len); + node->data[len] = '\0'; + + return true; +} + +static bool +parsestring(struct pkg_format_node *node, const char *fmt, const char *fmtend, + struct dpkg_error *err) +{ + int len; + char *write; + + len = fmtend - fmt + 1; + + node->type = PKG_FORMAT_STRING; + node->data = write = m_malloc(len + 1); + while (fmt <= fmtend) { + if (*fmt == '\\') { + fmt++; + switch (*fmt) { + case 'n': + *write = '\n'; + break; + case 't': + *write = '\t'; + break; + case 'r': + *write = '\r'; + break; + case '\\': + default: + *write = *fmt; + break; + } + } else + *write = *fmt; + write++; + fmt++; + } + *write = '\0'; + + return true; +} + +void +pkg_format_free(struct pkg_format_node *head) +{ + struct pkg_format_node *node; + + while (head) { + node = head; + head = node->next; + + free(node->data); + free(node); + } +} + +struct pkg_format_node * +pkg_format_parse(const char *fmt, struct dpkg_error *err) +{ + struct pkg_format_node *head, *node; + const char *fmtend; + + head = node = NULL; + + while (*fmt) { + if (node) + node = node->next = pkg_format_node_new(); + else + head = node = pkg_format_node_new(); + + if (fmt[0] == '$' && fmt[1] == '{') { + fmtend = strchr(fmt, '}'); + if (!fmtend) { + dpkg_put_error(err, _("missing closing brace")); + pkg_format_free(head); + return NULL; + } + + if (!parsefield(node, fmt + 2, fmtend - 1, err)) { + pkg_format_free(head); + return NULL; + } + fmt = fmtend + 1; + } else { + fmtend = fmt; + do { + fmtend += 1; + fmtend = strchrnul(fmtend, '$'); + } while (fmtend[0] && fmtend[1] != '{'); + + if (!parsestring(node, fmt, fmtend - 1, err)) { + pkg_format_free(head); + return NULL; + } + fmt = fmtend; + } + } + + if (!head) + dpkg_put_error(err, _("may not be empty string")); + + return head; +} + +static void +virt_package(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + varbuf_add_pkgbin_name(vb, pkg, pkgbin, pnaw_nonambig); +} + +static void +virt_status_abbrev(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + + varbuf_add_char(vb, pkg_abbrev_want(pkg)); + varbuf_add_char(vb, pkg_abbrev_status(pkg)); + varbuf_add_char(vb, pkg_abbrev_eflag(pkg)); +} + +static void +virt_status_want(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + + varbuf_add_str(vb, pkg_want_name(pkg)); +} + +static void +virt_status_status(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + + varbuf_add_str(vb, pkg_status_name(pkg)); +} + +static void +virt_status_eflag(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + if (pkgbin != &pkg->installed) + return; + + varbuf_add_str(vb, pkg_eflag_name(pkg)); +} + +static void +virt_synopsis(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *desc; + int len; + + desc = pkgbin_synopsis(pkg, pkgbin, &len); + + varbuf_add_buf(vb, desc, len); +} + +static void +virt_fsys_last_modified(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *listfile; + struct stat st; + intmax_t mtime; + + if (pkg->status == PKG_STAT_NOTINSTALLED) + return; + + listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE); + + if (stat(listfile, &st) < 0) { + if (errno == ENOENT) + return; + + ohshite(_("cannot get package %s filesystem last modification time"), + pkgbin_name_const(pkg, pkgbin, pnaw_nonambig)); + } + + mtime = st.st_mtime; + varbuf_printf(vb, "%jd", mtime); +} + +/* + * This function requires the caller to have loaded the package fsys metadata, + * otherwise it will do nothing. + */ +static void +virt_fsys_files(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct fsys_namenode_list *node; + + if (!pkg->files_list_valid) + return; + + for (node = pkg->files; node; node = node->next) { + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, node->namenode->name); + varbuf_add_char(vb, '\n'); + } +} + +static void +virt_source_package(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *name; + size_t len; + + name = pkgbin->source; + if (name == NULL) + name = pkg->set->name; + + len = strcspn(name, " "); + + varbuf_add_buf(vb, name, len); +} + +static void +virt_source_version(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + varbuf_add_source_version(vb, pkg, pkgbin); +} + +static void +virt_source_upstream_version(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct dpkg_version version; + + pkg_source_version(&version, pkg, pkgbin); + + if (version.version) + varbuf_add_str(vb, version.version); + varbuf_end_str(vb); +} + +static const struct fieldinfo virtinfos[] = { + { FIELD("binary:Package"), NULL, virt_package }, + { FIELD("binary:Synopsis"), NULL, virt_synopsis }, + { FIELD("binary:Summary"), NULL, virt_synopsis }, + { FIELD("db:Status-Abbrev"), NULL, virt_status_abbrev }, + { FIELD("db:Status-Want"), NULL, virt_status_want }, + { FIELD("db:Status-Status"), NULL, virt_status_status }, + { FIELD("db:Status-Eflag"), NULL, virt_status_eflag }, + { FIELD("db-fsys:Files"), NULL, virt_fsys_files }, + { FIELD("db-fsys:Last-Modified"), NULL, virt_fsys_last_modified }, + { FIELD("source:Package"), NULL, virt_source_package }, + { FIELD("source:Version"), NULL, virt_source_version }, + { FIELD("source:Upstream-Version"), NULL, virt_source_upstream_version }, + { NULL }, +}; + +bool +pkg_format_needs_db_fsys(const struct pkg_format_node *head) +{ + const struct pkg_format_node *node; + + for (node = head; node; node = node->next) { + if (node->type != PKG_FORMAT_FIELD) + continue; + if (strcmp(node->data, "db-fsys:Files") == 0) + return true; + } + + return false; +} + +static void +pkg_format_item(struct varbuf *vb, + const struct pkg_format_node *node, const char *str) +{ + if (node->width == 0) + varbuf_add_str(vb, str); + else + varbuf_printf(vb, "%*s", node->width, str); +} + +void +pkg_format_print(struct varbuf *vb, const struct pkg_format_node *head, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + const struct pkg_format_node *node; + struct varbuf fb = VARBUF_INIT, wb = VARBUF_INIT; + + for (node = head; node; node = node->next) { + bool ok = false; + + if (node->type == PKG_FORMAT_STRING) { + pkg_format_item(&fb, node, node->data); + ok = true; + } else if (node->type == PKG_FORMAT_FIELD) { + const struct fieldinfo *fip; + + fip = find_field_info(fieldinfos, node->data); + if (fip == NULL) + fip = find_field_info(virtinfos, node->data); + + if (fip) { + fip->wcall(&wb, pkg, pkgbin, 0, fip); + + varbuf_end_str(&wb); + pkg_format_item(&fb, node, wb.buf); + varbuf_reset(&wb); + ok = true; + } else { + const struct arbitraryfield *afp; + + afp = find_arbfield_info(pkgbin->arbs, node->data); + if (afp) { + pkg_format_item(&fb, node, afp->value); + ok = true; + } + } + } + + if (ok) { + size_t len = fb.used; + size_t width = abs(node->width); + + if ((width != 0) && (len > width)) + len = width; + varbuf_add_buf(vb, fb.buf, len); + varbuf_end_str(vb); + } + + varbuf_reset(&fb); + } + + varbuf_destroy(&wb); + varbuf_destroy(&fb); +} + +void +pkg_format_show(const struct pkg_format_node *head, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + struct varbuf vb = VARBUF_INIT; + + pkg_format_print(&vb, head, pkg, pkgbin); + + if (vb.buf) + fputs(vb.buf, stdout); + + varbuf_destroy(&vb); +} diff --git a/lib/dpkg/pkg-format.h b/lib/dpkg/pkg-format.h new file mode 100644 index 0000000..40d41ff --- /dev/null +++ b/lib/dpkg/pkg-format.h @@ -0,0 +1,53 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-format.g - customizable package formatting + * + * Copyright © 2001 Wichert Akkerman <wakkerma@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_FORMAT_H +#define LIBDPKG_PKG_FORMAT_H + +#include <dpkg/macros.h> +#include <dpkg/error.h> +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-format Package information formatting + * @ingroup dpkg-public + * @{ + */ + +struct pkg_format_node; + +bool +pkg_format_needs_db_fsys(const struct pkg_format_node *head); + +struct pkg_format_node *pkg_format_parse(const char *fmt, + struct dpkg_error *err); +void pkg_format_free(struct pkg_format_node *head); +void pkg_format_print(struct varbuf *vb, const struct pkg_format_node *head, + struct pkginfo *pkg, struct pkgbin *pkgbin); +void pkg_format_show(const struct pkg_format_node *head, + struct pkginfo *pkg, struct pkgbin *pkgbin); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_FORMAT_H */ diff --git a/lib/dpkg/pkg-hash.c b/lib/dpkg/pkg-hash.c new file mode 100644 index 0000000..b6bc126 --- /dev/null +++ b/lib/dpkg/pkg-hash.c @@ -0,0 +1,409 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-hash.c - low level package database routines (hash tables, etc.) + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/arch.h> + +/* + * This must always be a prime for optimal performance. + * + * We use a number that is close to the amount of packages currently present + * in a Debian suite, so that installed and available packages do not add + * tons of collisions. + * + * The memory usage is «BINS * sizeof(void *)». + */ +#define BINS 65521 + +static struct pkgset *bins[BINS]; +static int npkg, nset; + +/** + * Return the package set with the given name. + * + * If the package already exists in the internal database, then it returns + * the existing structure. Otherwise it allocates a new one and will return + * it. The actual name associated to the package set is a lowercase version + * of the name given in parameter. + * + * A package set (struct pkgset) can be composed of multiple package instances + * (struct pkginfo) where each instance is distinguished by its architecture + * (as recorded in pkg.installed.arch and pkg.available.arch). + * + * @param inname Name of the package set. + * + * @return The package set. + */ +struct pkgset * +pkg_hash_find_set(const char *inname) +{ + struct pkgset **setp, *new_set; + char *name = m_strdup(inname), *p; + + p= name; + while (*p) { + *p = c_tolower(*p); + p++; + } + + setp = bins + (str_fnv_hash(name) % (BINS)); + while (*setp && strcasecmp((*setp)->name, name)) + setp = &(*setp)->next; + if (*setp) { + free(name); + return *setp; + } + + new_set = nfmalloc(sizeof(*new_set)); + pkgset_blank(new_set); + new_set->name = nfstrsave(name); + new_set->next = NULL; + *setp = new_set; + nset++; + npkg++; + + free(name); + + return new_set; +} + +/** + * Return the singleton package instance from a package set. + * + * This means, if none are installed either an instance with native or + * all arch or the first if none found, the single installed instance, + * or NULL if more than one instance is installed. + * + * @param set The package set to use. + * + * @return The singleton package instance. + */ +struct pkginfo * +pkg_hash_get_singleton(struct pkgset *set) +{ + struct pkginfo *pkg; + + switch (pkgset_installed_instances(set)) { + case 0: + /* Pick an available candidate. */ + for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) { + const struct dpkg_arch *arch = pkg->available.arch; + + if (arch->type == DPKG_ARCH_NATIVE || arch->type == DPKG_ARCH_ALL) + return pkg; + } + /* Or failing that, the first entry. */ + return &set->pkg; + case 1: + for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) { + if (pkg->status > PKG_STAT_NOTINSTALLED) + return pkg; + } + internerr("pkgset '%s' should have one installed instance", set->name); + default: + return NULL; + } +} + +/** + * Return the singleton package instance with the given name. + * + * @param name The package name. + * + * @return The package instance. + */ +struct pkginfo * +pkg_hash_find_singleton(const char *name) +{ + struct pkgset *set; + struct pkginfo *pkg; + + set = pkg_hash_find_set(name); + pkg = pkg_hash_get_singleton(set); + if (pkg == NULL) + ohshit(_("ambiguous package name '%s' with more " + "than one installed instance"), set->name); + + return pkg; +} + +/** + * Return the package instance in a set with the given architecture. + * + * It traverse the various instances to find out whether there's one + * matching the given architecture. If found, it returns it. Otherwise it + * allocates a new instance and registers it in the package set before + * returning it. + * + * @param set The package set to use. + * @param arch The requested architecture. + * + * @return The package instance. + */ +struct pkginfo * +pkg_hash_get_pkg(struct pkgset *set, const struct dpkg_arch *arch) +{ + struct pkginfo *pkg, **pkgp; + + if (arch == NULL) + internerr("arch argument is NULL"); + if (arch->type == DPKG_ARCH_NONE) + internerr("arch argument is none"); + + pkg = &set->pkg; + + /* If there's a single unused slot, let's use that. */ + if (pkg->installed.arch->type == DPKG_ARCH_NONE && pkg->arch_next == NULL) { + /* We can only initialize the arch pkgbin members, because those are used + * to find instances, anything else will be overwritten at parse time. */ + pkg->installed.arch = arch; + pkg->available.arch = arch; + return pkg; + } + + /* Match the slot with the most appropriate architecture. The installed + * architecture always has preference over the available one, as there's + * a small time window on cross-grades, where they might differ. */ + /* cppcheck-suppress[knownConditionTrueFalse,knownPointerToBool]: + * False positive, the condition always ends up being false. */ + for (pkgp = &pkg; *pkgp; pkgp = &(*pkgp)->arch_next) { + if ((*pkgp)->installed.arch == arch) + return *pkgp; + } + + /* Need to create a new instance for the wanted architecture. */ + pkg = nfmalloc(sizeof(*pkg)); + pkg_blank(pkg); + pkg->set = set; + pkg->arch_next = NULL; + /* We can only initialize the arch pkgbin members, because those are used + * to find instances, anything else will be overwritten at parse time. */ + pkg->installed.arch = arch; + pkg->available.arch = arch; + *pkgp = pkg; + npkg++; + + return pkg; +} + +/** + * Return the package instance with the given name and architecture. + * + * @param name The package name. + * @param arch The requested architecture. + * + * @return The package instance. + */ +struct pkginfo * +pkg_hash_find_pkg(const char *name, const struct dpkg_arch *arch) +{ + struct pkgset *set; + struct pkginfo *pkg; + + set = pkg_hash_find_set(name); + pkg = pkg_hash_get_pkg(set, arch); + + return pkg; +} + +/** + * Return the number of package sets available in the database. + * + * @return The number of package sets. + */ +int +pkg_hash_count_set(void) +{ + return nset; +} + +/** + * Return the number of package instances available in the database. + * + * @return The number of package instances. + */ +int +pkg_hash_count_pkg(void) +{ + return npkg; +} + +struct pkg_hash_iter { + struct pkginfo *pkg; + int nbinn; +}; + +/** + * Create a new package iterator. + * + * It can iterate either over package sets or over package instances. + * + * @return The iterator. + */ +struct pkg_hash_iter * +pkg_hash_iter_new(void) +{ + struct pkg_hash_iter *iter; + + iter = m_malloc(sizeof(*iter)); + iter->pkg = NULL; + iter->nbinn = 0; + + return iter; +} + +/** + * Return the next package set in the database. + * + * If no further package set is available, it will return NULL. + * + * @name iter The iterator. + * + * @return A package set. + */ +struct pkgset * +pkg_hash_iter_next_set(struct pkg_hash_iter *iter) +{ + struct pkgset *set; + + while (!iter->pkg) { + if (iter->nbinn >= BINS) + return NULL; + if (bins[iter->nbinn]) + iter->pkg = &bins[iter->nbinn]->pkg; + iter->nbinn++; + } + + set = iter->pkg->set; + if (set->next) + iter->pkg = &set->next->pkg; + else + iter->pkg = NULL; + + return set; +} + +/** + * Return the next package instance in the database. + * + * If no further package instance is available, it will return NULL. Note + * that it will return all instances of a given package set in sequential + * order. The first instance for a given package set will always correspond + * to the native architecture even if that package is not installed or + * available. + * + * @name iter The iterator. + * + * @return A package instance. + */ +struct pkginfo * +pkg_hash_iter_next_pkg(struct pkg_hash_iter *iter) +{ + struct pkginfo *pkg; + + while (!iter->pkg) { + if (iter->nbinn >= BINS) + return NULL; + if (bins[iter->nbinn]) + iter->pkg = &bins[iter->nbinn]->pkg; + iter->nbinn++; + } + + pkg = iter->pkg; + if (pkg->arch_next) + iter->pkg = pkg->arch_next; + else if (pkg->set->next) + iter->pkg = &pkg->set->next->pkg; + else + iter->pkg = NULL; + + return pkg; +} + +/** + * Free the package database iterator. + * + * @name iter The iterator. + */ +void +pkg_hash_iter_free(struct pkg_hash_iter *iter) +{ + free(iter); +} + +void +pkg_hash_reset(void) +{ + dpkg_arch_reset_list(); + nffreeall(); + nset = 0; + npkg = 0; + memset(bins, 0, sizeof(bins)); +} + +void +pkg_hash_report(FILE *file) +{ + int i, c; + struct pkgset *pkg; + int *freq; + int empty = 0, used = 0, collided = 0; + + freq = m_malloc(sizeof(int) * nset + 1); + for (i = 0; i <= nset; i++) + freq[i] = 0; + for (i=0; i<BINS; i++) { + for (c=0, pkg= bins[i]; pkg; c++, pkg= pkg->next); + fprintf(file, "pkg-hash: bin %5d has %7d\n", i, c); + if (c == 0) + empty++; + else if (c == 1) + used++; + else { + used++; + collided++; + } + freq[c]++; + } + for (i = nset; i > 0 && freq[i] == 0; i--); + while (i >= 0) { + fprintf(file, "pkg-hash: size %7d occurs %5d times\n", i, freq[i]); + i--; + } + fprintf(file, "pkg-hash: bins empty %d\n", empty); + fprintf(file, "pkg-hash: bins used %d (collided %d)\n", used, collided); + + m_output(file, "<hash report>"); + + free(freq); +} diff --git a/lib/dpkg/pkg-list.c b/lib/dpkg/pkg-list.c new file mode 100644 index 0000000..fa889b1 --- /dev/null +++ b/lib/dpkg/pkg-list.c @@ -0,0 +1,78 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-list.c - primitives for pkg linked list handling + * + * Copyright © 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> + +/** + * Create a new package list node. + * + * @param pkg The pkginfo to assign to the node. + * @param next The next package list node. + * + * @return The new package list node. + */ +struct pkg_list * +pkg_list_new(struct pkginfo *pkg, struct pkg_list *next) +{ + struct pkg_list *node; + + node = m_malloc(sizeof(*node)); + node->pkg = pkg; + node->next = next; + + return node; +} + +/** + * Free all nodes of a package list. + * + * @param head The head of the list to free. + */ +void +pkg_list_free(struct pkg_list *head) +{ + while (head) { + struct pkg_list *node; + + node = head; + head = head->next; + + free(node); + } +} + +/** + * Prepend a package list node to a package list. + * + * @param head The head of the list to prepend to. + * @param pkg The pkginfo to prepend to the list. + */ +void +pkg_list_prepend(struct pkg_list **head, struct pkginfo *pkg) +{ + *head = pkg_list_new(pkg, *head); +} diff --git a/lib/dpkg/pkg-list.h b/lib/dpkg/pkg-list.h new file mode 100644 index 0000000..6a34755 --- /dev/null +++ b/lib/dpkg/pkg-list.h @@ -0,0 +1,47 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-list.h - primitives for pkg linked list handling + * + * Copyright © 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_LIST_H +#define LIBDPKG_PKG_LIST_H + +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-list Package linked lists + * @ingroup dpkg-public + * @{ + */ + +struct pkg_list { + struct pkg_list *next; + struct pkginfo *pkg; +}; + +struct pkg_list *pkg_list_new(struct pkginfo *pkg, struct pkg_list *next); +void pkg_list_free(struct pkg_list *head); +void pkg_list_prepend(struct pkg_list **head, struct pkginfo *pkg); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_LIST_H */ diff --git a/lib/dpkg/pkg-namevalue.c b/lib/dpkg/pkg-namevalue.c new file mode 100644 index 0000000..5f6fcb7 --- /dev/null +++ b/lib/dpkg/pkg-namevalue.c @@ -0,0 +1,77 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-namevalue.c - name/value package tables + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/dpkg-db.h> +#include <dpkg/namevalue.h> + +const struct namevalue booleaninfos[] = { + NAMEVALUE_DEF("no", false), + NAMEVALUE_DEF("yes", true), + { .name = NULL } +}; + +const struct namevalue multiarchinfos[] = { + NAMEVALUE_DEF("no", PKG_MULTIARCH_NO), + NAMEVALUE_DEF("same", PKG_MULTIARCH_SAME), + NAMEVALUE_DEF("allowed", PKG_MULTIARCH_ALLOWED), + NAMEVALUE_DEF("foreign", PKG_MULTIARCH_FOREIGN), + { .name = NULL } +}; + +const struct namevalue priorityinfos[] = { + NAMEVALUE_DEF("required", PKG_PRIO_REQUIRED), + NAMEVALUE_DEF("important", PKG_PRIO_IMPORTANT), + NAMEVALUE_DEF("standard", PKG_PRIO_STANDARD), + NAMEVALUE_DEF("optional", PKG_PRIO_OPTIONAL), + NAMEVALUE_DEF("extra", PKG_PRIO_EXTRA), + NAMEVALUE_DEF("unknown", PKG_PRIO_UNKNOWN), + { .name = NULL } +}; + +const struct namevalue wantinfos[] = { + NAMEVALUE_DEF("unknown", PKG_WANT_UNKNOWN), + NAMEVALUE_DEF("install", PKG_WANT_INSTALL), + NAMEVALUE_DEF("hold", PKG_WANT_HOLD), + NAMEVALUE_DEF("deinstall", PKG_WANT_DEINSTALL), + NAMEVALUE_DEF("purge", PKG_WANT_PURGE), + { .name = NULL } +}; + +const struct namevalue eflaginfos[] = { + NAMEVALUE_DEF("ok", PKG_EFLAG_OK), + NAMEVALUE_DEF("reinstreq", PKG_EFLAG_REINSTREQ), + { .name = NULL } +}; + +const struct namevalue statusinfos[] = { + NAMEVALUE_DEF("not-installed", PKG_STAT_NOTINSTALLED), + NAMEVALUE_DEF("config-files", PKG_STAT_CONFIGFILES), + NAMEVALUE_DEF("half-installed", PKG_STAT_HALFINSTALLED), + NAMEVALUE_DEF("unpacked", PKG_STAT_UNPACKED), + NAMEVALUE_DEF("half-configured", PKG_STAT_HALFCONFIGURED), + NAMEVALUE_DEF("triggers-awaited", PKG_STAT_TRIGGERSAWAITED), + NAMEVALUE_DEF("triggers-pending", PKG_STAT_TRIGGERSPENDING), + NAMEVALUE_DEF("installed", PKG_STAT_INSTALLED), + { .name = NULL } +}; diff --git a/lib/dpkg/pkg-queue.c b/lib/dpkg/pkg-queue.c new file mode 100644 index 0000000..a807f1e --- /dev/null +++ b/lib/dpkg/pkg-queue.c @@ -0,0 +1,126 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-queue.c - primitives for pkg queue handling + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-queue.h> + +/** + * Initialize a package queue. + * + * @param queue The queue to initialize. + */ +void +pkg_queue_init(struct pkg_queue *queue) +{ + queue->head = NULL; + queue->tail = NULL; + queue->length = 0; +} + +/** + * Destroy a package queue. + * + * It frees the contained package list and resets the queue members. + * + * @param queue The queue to destroy. + */ +void +pkg_queue_destroy(struct pkg_queue *queue) +{ + pkg_list_free(queue->head); + pkg_queue_init(queue); +} + +/** + * Check if a package queue is empty. + * + * @param queue The queue to check. + * + * @return A boolean value. + */ +int +pkg_queue_is_empty(struct pkg_queue *queue) +{ + return (queue->head == NULL); +} + +/** + * Push a new node containing pkginfo to the tail of the queue. + * + * @param queue The queue to insert to. + * @param pkg The package to use fo the new node. + * + * @return The newly inserted pkg_list node. + */ +struct pkg_list * +pkg_queue_push(struct pkg_queue *queue, struct pkginfo *pkg) +{ + struct pkg_list *node; + + node = pkg_list_new(pkg, NULL); + + if (queue->tail == NULL) + queue->head = node; + else + queue->tail->next = node; + + queue->tail = node; + + queue->length++; + + return node; +} + +/** + * Pop a node containing pkginfo from the head of the queue. + * + * This removes and frees the node from the queue, effectively reducing its + * size. + * + * @param queue The queue to remove from. + * + * @return The pkginfo from the removed node, or NULL if the queue was empty. + */ +struct pkginfo * +pkg_queue_pop(struct pkg_queue *queue) +{ + struct pkg_list *node; + struct pkginfo *pkg; + + if (pkg_queue_is_empty(queue)) + return NULL; + + node = queue->head; + pkg = node->pkg; + + queue->head = node->next; + if (queue->head == NULL) + queue->tail = NULL; + + free(node); + queue->length--; + + return pkg; +} diff --git a/lib/dpkg/pkg-queue.h b/lib/dpkg/pkg-queue.h new file mode 100644 index 0000000..21460af --- /dev/null +++ b/lib/dpkg/pkg-queue.h @@ -0,0 +1,64 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-queue.h - primitives for pkg queue handling + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DPKG_PKG_QUEUE_H +#define DPKG_PKG_QUEUE_H + +#include <dpkg/macros.h> +#include <dpkg/pkg-list.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-queue Package queues + * @ingroup dpkg-public + * @{ + */ + +struct pkg_queue { + struct pkg_list *head, *tail; + int length; +}; + +/** + * Constant initializer for a package queue. + */ +#define PKG_QUEUE_INIT \ + { .head = NULL, .tail = NULL, .length = 0 } + +/** + * Compound literal for a package queue. + */ +#define PKG_QUEUE_OBJECT \ + (struct pkg_queue)PKG_QUEUE_INIT + +void pkg_queue_init(struct pkg_queue *queue); +void pkg_queue_destroy(struct pkg_queue *queue); + +int pkg_queue_is_empty(struct pkg_queue *queue); + +struct pkg_list *pkg_queue_push(struct pkg_queue *queue, struct pkginfo *pkg); +struct pkginfo *pkg_queue_pop(struct pkg_queue *queue); + +/** @} */ + +DPKG_END_DECLS + +#endif /* DPKG_PKG_QUEUE_H */ diff --git a/lib/dpkg/pkg-show.c b/lib/dpkg/pkg-show.c new file mode 100644 index 0000000..038343c --- /dev/null +++ b/lib/dpkg/pkg-show.c @@ -0,0 +1,445 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-show.c - primitives for pkg information display + * + * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/macros.h> +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-show.h> + +static bool +pkgbin_name_needs_arch(const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw) +{ + if (pkgbin->arch->type == DPKG_ARCH_NONE || + pkgbin->arch->type == DPKG_ARCH_EMPTY) + return false; + + switch (pnaw) { + case pnaw_never: + break; + case pnaw_same: + if (pkgbin->multiarch == PKG_MULTIARCH_SAME) + return true; + return false; + case pnaw_nonambig: + if (pkgbin->multiarch == PKG_MULTIARCH_SAME) + return true; + /* Fall through. */ + case pnaw_foreign: + if (pkgbin->arch->type == DPKG_ARCH_NATIVE || + pkgbin->arch->type == DPKG_ARCH_ALL) + break; + /* Fall through. */ + case pnaw_always: + return true; + } + + return false; +} + +/** + * Add a string representation of the package name to a varbuf. + * + * Works exactly like pkgbin_name() but acts on the varbuf instead of + * returning a string. It NUL terminates the varbuf. + * + * @param vb The varbuf struct to modify. + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + * @param pnaw When to display the architecture qualifier. + */ +void +varbuf_add_pkgbin_name(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw) +{ + varbuf_add_str(vb, pkg->set->name); + if (pkgbin_name_needs_arch(pkgbin, pnaw)) + varbuf_add_archqual(vb, pkgbin->arch); + varbuf_end_str(vb); +} + +const char * +pkgbin_name_archqual(const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + char *pkgname; + + if (pkgbin->arch->type == DPKG_ARCH_NONE || + pkgbin->arch->type == DPKG_ARCH_EMPTY) + return pkg->set->name; + + pkgname = nfmalloc(strlen(pkg->set->name) + 1 + + strlen(pkgbin->arch->name) + 1); + str_concat(pkgname, pkg->set->name, ":", + pkgbin->arch->name, NULL); + + return pkgname; +} + +/** + * Return a string representation of the package name. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * Note, that this const variant will "leak" a new non-freeing string on + * each call if the internal cache has not been previously initialized, + * so it is advised to use it only in error reporting code paths. + * + * The pnaw parameter should be one of pnaw_never (never print arch), + * pnaw_foreign (print arch for foreign packages only), pnaw_nonambig (print + * arch for non ambiguous cases) or pnaw_always (always print arch), + * + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkgbin_name_const(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw) +{ + if (!pkgbin_name_needs_arch(pkgbin, pnaw)) + return pkg->set->name; + + /* Return a non-freeing package name representation, which + * is intended to be used in error-handling code, as we will keep + * "leaking" them until the next memory pool flush. */ + if (pkgbin->pkgname_archqual == NULL) + return pkgbin_name_archqual(pkg, pkgbin); + + return pkgbin->pkgname_archqual; +} + +/** + * Return a string representation of the installed package name. + * + * This is equivalent to pkgbin_name_const() but just for its installed pkgbin. + * + * @param pkg The package to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkg_name_const(const struct pkginfo *pkg, enum pkg_name_arch_when pnaw) +{ + return pkgbin_name_const(pkg, &pkg->installed, pnaw); +} + +/** + * Return a string representation of the package name. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * The pnaw parameter should be one of pnaw_never (never print arch), + * pnaw_foreign (print arch for foreign packages only), pnaw_nonambig (print + * arch for non ambiguous cases) or pnaw_always (always print arch), + * + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw) +{ + if (!pkgbin_name_needs_arch(pkgbin, pnaw)) + return pkg->set->name; + + /* Cache the package name representation, for later reuse. */ + if (pkgbin->pkgname_archqual == NULL) + pkgbin->pkgname_archqual = pkgbin_name_archqual(pkg, pkgbin); + + return pkgbin->pkgname_archqual; +} + +/** + * Return a string representation of the installed package name. + * + * This is equivalent to pkgbin_name() but just for its installed pkgbin. + * + * @param pkg The package to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw) +{ + return pkgbin_name(pkg, &pkg->installed, pnaw); +} + +/** + * Return a string representation of the package synopsis. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * The package synopsis is the short description, but it is not NUL terminated, + * so the output len argument should be used to limit the string length. + * + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + * @param[out] len The length of the synopsis string within the description. + * + * @return The string representation. + */ +const char * +pkgbin_synopsis(const struct pkginfo *pkg, const struct pkgbin *pkgbin, int *len) +{ + const char *pdesc; + + pdesc = pkgbin->description; + if (!pdesc) + pdesc = _("(no description available)"); + + *len = strcspn(pdesc, "\n"); + + return pdesc; +} + +/** + * Return a string representation of the package synopsis. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * It will try to use the installed version, otherwise it will fallback to + * use the available version. + * + * The package synopsis is the short description, but it is not NUL terminated, + * so the output len argument should be used to limit the string length. + * + * @param pkg The package to consider. + * @param[out] len The length of the synopsis string within the description. + * + * @return The string representation. + */ +const char * +pkg_synopsis(const struct pkginfo *pkg, int *len) +{ + const char *pdesc; + + pdesc = pkg->installed.description; + if (!pdesc) + pdesc = pkg->available.description; + if (!pdesc) + pdesc = _("(no description available)"); + + *len = strcspn(pdesc, "\n"); + + return pdesc; +} + +/** + * Return a character abbreviated representation of the package want status. + * + * @param pkg The package to consider. + * + * @return The character abbreviated representation. + */ +int +pkg_abbrev_want(const struct pkginfo *pkg) +{ + return "uihrp"[pkg->want]; +} + +/** + * Return a character abbreviated representation of the package current status. + * + * @param pkg The package to consider. + * + * @return The character abbreviated representation. + */ +int +pkg_abbrev_status(const struct pkginfo *pkg) +{ + return "ncHUFWti"[pkg->status]; +} + +/** + * Return a character abbreviated representation of the package eflag status. + * + * @param pkg The package to consider. + * + * @return The character abbreviated representation. + */ +int +pkg_abbrev_eflag(const struct pkginfo *pkg) +{ + return " R"[pkg->eflag]; +} + +/** + * Return a string representation of the package want status name. + * + * @param pkg The package to consider. + * + * @return The string representation. + */ +const char * +pkg_want_name(const struct pkginfo *pkg) +{ + return wantinfos[pkg->want].name; +} + +/** + * Return a string representation of the package eflag status name. + * + * @param pkg The package to consider. + * + * @return The string representation. + */ +const char * +pkg_eflag_name(const struct pkginfo *pkg) +{ + return eflaginfos[pkg->eflag].name; +} + +/** + * Return a string representation of the package current status name. + * + * @param pkg The package to consider. + * + * @return The string representation. + */ +const char * +pkg_status_name(const struct pkginfo *pkg) +{ + return statusinfos[pkg->status].name; +} + +/** + * Return a string representation of the package priority name. + * + * @param pkg The package to consider. + * + * @return The string representation. + */ +const char * +pkg_priority_name(const struct pkginfo *pkg) +{ + if (pkg->priority == PKG_PRIO_OTHER) + return pkg->otherpriority; + else + return priorityinfos[pkg->priority].name; +} + +/** + * Compare a package to be sorted by non-ambiguous name and architecture. + * + * @param a A pointer of a pointer to a struct pkginfo. + * @param b A pointer of a pointer to a struct pkginfo. + * + * @return An integer with the result of the comparison. + * @retval -1 a is earlier than b. + * @retval 0 a is equal to b. + * @retval 1 a is later than b. + */ +int +pkg_sorter_by_nonambig_name_arch(const void *a, const void *b) +{ + const struct pkginfo *pa = *(const struct pkginfo **)a; + const struct pkginfo *pb = *(const struct pkginfo **)b; + const struct pkgbin *pbina = &pa->installed; + const struct pkgbin *pbinb = &pb->installed; + int res; + + res = strcmp(pa->set->name, pb->set->name); + if (res) + return res; + + if (pbina->arch == pbinb->arch) + return 0; + + if (pkgbin_name_needs_arch(pbina, pnaw_nonambig)) { + if (pkgbin_name_needs_arch(pbinb, pnaw_nonambig)) + return strcmp(pbina->arch->name, pbinb->arch->name); + else + return 1; + } else { + return -1; + } +} + +/** + * Add a string representation of the source package version to a varbuf. + * + * It parses the Source field (if present), and extracts the optional + * version enclosed in parenthesis. Otherwise it fallsback to use the + * binary package version. It NUL terminates the varbuf. + * + * @param vb The varbuf struct to modify. + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + */ +void +varbuf_add_source_version(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + struct dpkg_version version = DPKG_VERSION_INIT; + + pkg_source_version(&version, pkg, pkgbin); + varbufversion(vb, &version, vdew_nonambig); + varbuf_end_str(vb); +} + +void +pkg_source_version(struct dpkg_version *version, + const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + const char *version_str; + + if (pkgbin->source) + version_str = strchr(pkgbin->source, '('); + else + version_str = NULL; + + if (version_str == NULL) { + *version = pkgbin->version; + } else { + struct dpkg_error err; + struct varbuf vb = VARBUF_INIT; + size_t len; + + version_str++; + len = strcspn(version_str, ")"); + varbuf_add_buf(&vb, version_str, len); + varbuf_end_str(&vb); + + if (parseversion(version, vb.buf, &err) < 0) + ohshit(_("version '%s' has bad syntax: %s"), + vb.buf, err.str); + + varbuf_destroy(&vb); + } +} diff --git a/lib/dpkg/pkg-show.h b/lib/dpkg/pkg-show.h new file mode 100644 index 0000000..4ac3f5d --- /dev/null +++ b/lib/dpkg/pkg-show.h @@ -0,0 +1,49 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-show.h - primitives for pkg information display + * + * Copyright © 2010-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DPKG_PKG_SHOW_H +#define DPKG_PKG_SHOW_H + +#include <dpkg/macros.h> +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-show Package information display + * @ingroup dpkg-public + * @{ + */ + +int pkg_sorter_by_nonambig_name_arch(const void *a, const void *b); + +const char *pkgbin_synopsis(const struct pkginfo *pkg, + const struct pkgbin *pkgbin, int *len_ret); +const char * +pkg_synopsis(const struct pkginfo *pkg, int *len_ret); +int pkg_abbrev_want(const struct pkginfo *pkg); +int pkg_abbrev_status(const struct pkginfo *pkg); +int pkg_abbrev_eflag(const struct pkginfo *pkg); + +/** @} */ + +DPKG_END_DECLS + +#endif /* DPKG_PKG_SHOW_H */ diff --git a/lib/dpkg/pkg-spec.c b/lib/dpkg/pkg-spec.c new file mode 100644 index 0000000..3e3f59f --- /dev/null +++ b/lib/dpkg/pkg-spec.c @@ -0,0 +1,303 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-spec.c - primitives for pkg specifier handling + * + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * Copyright © 2011-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> +#include <fnmatch.h> +#include <string.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> +#include <dpkg/pkg-spec.h> + +static void +pkg_spec_blank(struct pkg_spec *ps) +{ + ps->name = NULL; + ps->arch = NULL; + + ps->name_is_pattern = false; + ps->arch_is_pattern = false; +} + +static void +pkg_spec_iter_blank(struct pkg_spec *ps) +{ + ps->pkg_iter = NULL; + ps->pkg_next = NULL; +} + +void +pkg_spec_init(struct pkg_spec *ps, enum pkg_spec_flags flags) +{ + ps->flags = flags; + + pkg_spec_blank(ps); + pkg_spec_iter_blank(ps); +} + +const char * +pkg_spec_is_illegal(struct pkg_spec *ps) +{ + static char msg[1024]; + const char *emsg; + + if (!ps->name_is_pattern && + (emsg = pkg_name_is_illegal(ps->name))) { + const char *arch_sep; + + /* Only check for DPKG_ARCH_NONE, because for everything else + * we want to see the passed package specification, even if + * the architecture is empty. */ + if (ps->arch->type == DPKG_ARCH_NONE) + arch_sep = ""; + else + arch_sep = ":"; + + snprintf(msg, sizeof(msg), + _("illegal package name in specifier '%s%s%s': %s"), + ps->name, arch_sep, ps->arch->name, emsg); + return msg; + } + + if ((!ps->arch_is_pattern && ps->arch->type == DPKG_ARCH_ILLEGAL) || + ps->arch->type == DPKG_ARCH_EMPTY) { + emsg = dpkg_arch_name_is_illegal(ps->arch->name); + snprintf(msg, sizeof(msg), + _("illegal architecture name in specifier '%s:%s': %s"), + ps->name, ps->arch->name, emsg); + return msg; + } + + /* If we have been requested a single instance, check that the + * package does not contain other instances. */ + if (!ps->arch_is_pattern && ps->flags & PKG_SPEC_ARCH_SINGLE) { + struct pkgset *set; + + set = pkg_hash_find_set(ps->name); + + /* Single instancing only applies with no architecture. */ + if (ps->arch->type == DPKG_ARCH_NONE && + pkgset_installed_instances(set) > 1) { + snprintf(msg, sizeof(msg), + _("ambiguous package name '%s' with more " + "than one installed instance"), ps->name); + return msg; + } + } + + return NULL; +} + +static const char * +pkg_spec_prep(struct pkg_spec *ps, char *pkgname, const char *archname) +{ + ps->name = pkgname; + ps->arch = dpkg_arch_find(archname); + + ps->name_is_pattern = false; + ps->arch_is_pattern = false; + + /* Detect if we have patterns and/or illegal names. */ + if ((ps->flags & PKG_SPEC_PATTERNS) && strpbrk(ps->name, "*[?\\")) + ps->name_is_pattern = true; + + if ((ps->flags & PKG_SPEC_PATTERNS) && strpbrk(ps->arch->name, "*[?\\")) + ps->arch_is_pattern = true; + + return pkg_spec_is_illegal(ps); +} + +const char * +pkg_spec_set(struct pkg_spec *ps, const char *pkgname, const char *archname) +{ + return pkg_spec_prep(ps, m_strdup(pkgname), archname); +} + +const char * +pkg_spec_parse(struct pkg_spec *ps, const char *str) +{ + char *pkgname, *archname; + + archname = strchr(str, ':'); + if (archname == NULL) { + pkgname = m_strdup(str); + } else { + pkgname = m_strndup(str, archname - str); + archname++; + } + + return pkg_spec_prep(ps, pkgname, archname); +} + +static bool +pkg_spec_match_name(struct pkg_spec *ps, const char *name) +{ + if (ps->name_is_pattern) + return (fnmatch(ps->name, name, 0) == 0); + else + return (strcmp(ps->name, name) == 0); +} + +static bool +pkg_spec_match_arch(struct pkg_spec *ps, struct pkginfo *pkg, + const struct dpkg_arch *arch) +{ + if (ps->arch_is_pattern) + return (fnmatch(ps->arch->name, arch->name, 0) == 0); + else if (ps->arch->type != DPKG_ARCH_NONE) /* !arch_is_pattern */ + return (ps->arch == arch); + + /* No arch specified. */ + switch (ps->flags & PKG_SPEC_ARCH_MASK) { + case PKG_SPEC_ARCH_SINGLE: + return pkgset_installed_instances(pkg->set) <= 1; + case PKG_SPEC_ARCH_WILDCARD: + return true; + default: + internerr("unknown PKG_SPEC_ARCH_* flags %d in pkg_spec", + ps->flags & PKG_SPEC_ARCH_MASK); + } +} + +bool +pkg_spec_match_pkg(struct pkg_spec *ps, struct pkginfo *pkg, + struct pkgbin *pkgbin) +{ + return (pkg_spec_match_name(ps, pkg->set->name) && + pkg_spec_match_arch(ps, pkg, pkgbin->arch)); +} + +static struct pkginfo * +pkg_spec_get_pkg(struct pkg_spec *ps) +{ + if (ps->arch->type == DPKG_ARCH_NONE) + return pkg_hash_find_singleton(ps->name); + else + return pkg_hash_find_pkg(ps->name, ps->arch); +} + +struct pkginfo * +pkg_spec_parse_pkg(const char *str, struct dpkg_error *err) +{ + struct pkg_spec ps; + struct pkginfo *pkg; + const char *emsg; + + pkg_spec_init(&ps, PKG_SPEC_ARCH_SINGLE); + emsg = pkg_spec_parse(&ps, str); + if (emsg) { + dpkg_put_error(err, "%s", emsg); + pkg = NULL; + } else { + pkg = pkg_spec_get_pkg(&ps); + } + pkg_spec_destroy(&ps); + + return pkg; +} + +struct pkginfo * +pkg_spec_find_pkg(const char *pkgname, const char *archname, + struct dpkg_error *err) +{ + struct pkg_spec ps; + struct pkginfo *pkg; + const char *emsg; + + pkg_spec_init(&ps, PKG_SPEC_ARCH_SINGLE); + emsg = pkg_spec_set(&ps, pkgname, archname); + if (emsg) { + dpkg_put_error(err, "%s", emsg); + pkg = NULL; + } else { + pkg = pkg_spec_get_pkg(&ps); + } + pkg_spec_destroy(&ps); + + return pkg; +} + +void +pkg_spec_iter_init(struct pkg_spec *ps) +{ + if (ps->name_is_pattern) + ps->pkg_iter = pkg_hash_iter_new(); + else + ps->pkg_next = &pkg_hash_find_set(ps->name)->pkg; +} + +static struct pkginfo * +pkg_spec_iter_next_pkgname(struct pkg_spec *ps) +{ + struct pkginfo *pkg; + + while ((pkg = pkg_hash_iter_next_pkg(ps->pkg_iter))) { + if (pkg_spec_match_pkg(ps, pkg, &pkg->installed)) + return pkg; + } + + return NULL; +} + +static struct pkginfo * +pkg_spec_iter_next_pkgarch(struct pkg_spec *ps) +{ + struct pkginfo *pkg; + + while ((pkg = ps->pkg_next)) { + ps->pkg_next = pkg->arch_next; + + if (pkg_spec_match_arch(ps, pkg, pkg->installed.arch)) + return pkg; + } + + return NULL; +} + +struct pkginfo * +pkg_spec_iter_next_pkg(struct pkg_spec *ps) +{ + if (ps->name_is_pattern) + return pkg_spec_iter_next_pkgname(ps); + else + return pkg_spec_iter_next_pkgarch(ps); +} + +void +pkg_spec_iter_destroy(struct pkg_spec *ps) +{ + pkg_hash_iter_free(ps->pkg_iter); + pkg_spec_iter_blank(ps); +} + +void +pkg_spec_destroy(struct pkg_spec *ps) +{ + free(ps->name); + pkg_spec_blank(ps); + pkg_spec_iter_destroy(ps); +} diff --git a/lib/dpkg/pkg-spec.h b/lib/dpkg/pkg-spec.h new file mode 100644 index 0000000..5415d85 --- /dev/null +++ b/lib/dpkg/pkg-spec.h @@ -0,0 +1,91 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-spec.h - primitives for pkg specifier handling + * + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_SPEC_H +#define LIBDPKG_PKG_SPEC_H + +#include <stdbool.h> + +#include <dpkg/macros.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/error.h> +#include <dpkg/arch.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-spec Package specifiers + * @ingroup dpkg-public + * @{ + */ + +enum DPKG_ATTR_ENUM_FLAGS pkg_spec_flags { + /** Recognize glob patterns. */ + PKG_SPEC_PATTERNS = DPKG_BIT(0), + + /* How to consider the lack of an arch qualifier. */ + PKG_SPEC_ARCH_SINGLE = DPKG_BIT(8), + PKG_SPEC_ARCH_WILDCARD = DPKG_BIT(9), + PKG_SPEC_ARCH_MASK = 0x0300, +}; + +struct pkg_spec { + char *name; + const struct dpkg_arch *arch; + + enum pkg_spec_flags flags; + + /* Members below are private state. */ + + bool name_is_pattern; + bool arch_is_pattern; + + /** Used for the pkg_db iterator. */ + struct pkg_hash_iter *pkg_iter; + /** Used for the pkgset iterator. */ + struct pkginfo *pkg_next; +}; + +void pkg_spec_init(struct pkg_spec *ps, enum pkg_spec_flags flags); +void pkg_spec_destroy(struct pkg_spec *ps); + +const char *pkg_spec_is_illegal(struct pkg_spec *ps); + +const char *pkg_spec_set(struct pkg_spec *ps, + const char *pkgname, const char *archname); +const char *pkg_spec_parse(struct pkg_spec *ps, const char *str); +bool pkg_spec_match_pkg(struct pkg_spec *ps, + struct pkginfo *pkg, struct pkgbin *pkgbin); + +struct pkginfo *pkg_spec_parse_pkg(const char *str, struct dpkg_error *err); +struct pkginfo *pkg_spec_find_pkg(const char *pkgname, const char *archname, + struct dpkg_error *err); + +void pkg_spec_iter_init(struct pkg_spec *ps); +struct pkginfo *pkg_spec_iter_next_pkg(struct pkg_spec *ps); +void pkg_spec_iter_destroy(struct pkg_spec *ps); + +/** @} */ + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/pkg.c b/lib/dpkg/pkg.c new file mode 100644 index 0000000..22bab5b --- /dev/null +++ b/lib/dpkg/pkg.c @@ -0,0 +1,217 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg.c - primitives for pkg handling + * + * Copyright © 1995, 1996 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/ehandle.h> +#include <dpkg/string.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> + +/** + * Set the package installation status. + */ +void +pkg_set_status(struct pkginfo *pkg, enum pkgstatus status) +{ + if (pkg->status == status) + return; + else if (pkg->status == PKG_STAT_NOTINSTALLED) + pkg->set->installed_instances++; + else if (status == PKG_STAT_NOTINSTALLED) + pkg->set->installed_instances--; + + if (pkg->set->installed_instances < 0) + internerr("pkgset %s went into negative installed instances %d", + pkg->set->name, pkg->set->installed_instances); + + pkg->status = status; + pkg->status_dirty = true; +} + +/** + * Set the specified flags to 1 in the package error flags. + */ +void +pkg_set_eflags(struct pkginfo *pkg, enum pkgeflag eflag) +{ + pkg->eflag |= eflag; +} + +/** + * Clear the specified flags to 0 in the package error flags. + */ +void +pkg_clear_eflags(struct pkginfo *pkg, enum pkgeflag eflag) +{ + pkg->eflag &= ~eflag; +} + +/** + * Reset the package error flags to 0. + */ +void +pkg_reset_eflags(struct pkginfo *pkg) +{ + pkg->eflag = PKG_EFLAG_OK; +} + +/** + * Copy the package error flags to another package. + */ +void +pkg_copy_eflags(struct pkginfo *pkg_dst, struct pkginfo *pkg_src) +{ + pkg_dst->eflag = pkg_src->eflag; +} + +/** + * Set the package selection status. + */ +void +pkg_set_want(struct pkginfo *pkg, enum pkgwant want) +{ + pkg->want = want; +} + +void +pkgbin_blank(struct pkgbin *pkgbin) +{ + pkgbin->essential = false; + pkgbin->is_protected = false; + pkgbin->depends = NULL; + pkgbin->pkgname_archqual = NULL; + pkgbin->description = NULL; + pkgbin->maintainer = NULL; + pkgbin->source = NULL; + pkgbin->installedsize = NULL; + pkgbin->bugs = NULL; + pkgbin->origin = NULL; + dpkg_version_blank(&pkgbin->version); + pkgbin->conffiles = NULL; + pkgbin->arbs = NULL; +} + +void +pkg_blank(struct pkginfo *pkg) +{ + pkg->status = PKG_STAT_NOTINSTALLED; + pkg->status_dirty = false; + pkg->eflag = PKG_EFLAG_OK; + pkg->want = PKG_WANT_UNKNOWN; + pkg->priority = PKG_PRIO_UNKNOWN; + pkg->otherpriority = NULL; + pkg->section = NULL; + dpkg_version_blank(&pkg->configversion); + pkg->files_list_valid = false; + pkg->files_list_phys_offs = 0; + pkg->files = NULL; + pkg->archives = NULL; + pkg->clientdata = NULL; + pkg->trigaw.head = NULL; + pkg->trigaw.tail = NULL; + pkg->othertrigaw_head = NULL; + pkg->trigpend_head = NULL; + pkgbin_blank(&pkg->installed); + pkgbin_blank(&pkg->available); + + /* The architectures are reset here (instead of in pkgbin_blank), + * because they are part of the package specification, and needed + * for selections. */ + pkg->installed.arch = dpkg_arch_get(DPKG_ARCH_NONE); + pkg->installed.multiarch = PKG_MULTIARCH_NO; + pkg->available.arch = dpkg_arch_get(DPKG_ARCH_NONE); + pkg->available.multiarch = PKG_MULTIARCH_NO; +} + +void +pkgset_blank(struct pkgset *set) +{ + set->name = NULL; + set->depended.available = NULL; + set->depended.installed = NULL; + pkg_blank(&set->pkg); + set->installed_instances = 0; + set->pkg.set = set; + set->pkg.arch_next = NULL; +} + +/** + * Link a pkginfo instance into a package set. + * + * @param set The package set to use. + * @param pkg The package to link into the set. + */ +void +pkgset_link_pkg(struct pkgset *set, struct pkginfo *pkg) +{ + pkg->set = set; + pkg->arch_next = set->pkg.arch_next; + set->pkg.arch_next = pkg; +} + +/** + * Get the number of installed package instances in a package set. + * + * @param set The package set to use. + * + * @return The count of installed packages. + */ +int +pkgset_installed_instances(struct pkgset *set) +{ + return set->installed_instances; +} + +/** + * Check if a pkg is informative. + * + * Used by dselect and dpkg query options as an aid to decide whether to + * display things, and by dump to decide whether to write them out. + */ +bool +pkg_is_informative(struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + /* We ignore Section and Priority, as these tend to hang around. */ + if (pkgbin == &pkg->installed && + (pkg->want != PKG_WANT_UNKNOWN || + pkg->eflag != PKG_EFLAG_OK || + pkg->status != PKG_STAT_NOTINSTALLED || + dpkg_version_is_informative(&pkg->configversion))) + return true; + + if (pkgbin->depends || + str_is_set(pkgbin->description) || + str_is_set(pkgbin->maintainer) || + str_is_set(pkgbin->origin) || + str_is_set(pkgbin->bugs) || + str_is_set(pkgbin->installedsize) || + str_is_set(pkgbin->source) || + dpkg_version_is_informative(&pkgbin->version) || + pkgbin->conffiles || + pkgbin->arbs) + return true; + + return false; +} diff --git a/lib/dpkg/pkg.h b/lib/dpkg/pkg.h new file mode 100644 index 0000000..766966f --- /dev/null +++ b/lib/dpkg/pkg.h @@ -0,0 +1,50 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg.h - primitives for pkg handling + * + * Copyright © 2009,2011-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_H +#define LIBDPKG_PKG_H + +#include <dpkg/macros.h> +#include <dpkg/dpkg-db.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg Package handling primitives + * @ingroup dpkg-public + * @{ + */ + +typedef int pkg_sorter_func(const void *a, const void *b); + +void pkgset_link_pkg(struct pkgset *set, struct pkginfo *pkg); + +void pkg_set_status(struct pkginfo *pkg, enum pkgstatus status); +void pkg_set_eflags(struct pkginfo *pkg, enum pkgeflag eflag); +void pkg_clear_eflags(struct pkginfo *pkg, enum pkgeflag eflag); +void pkg_reset_eflags(struct pkginfo *pkg); +void pkg_copy_eflags(struct pkginfo *pkg_dst, struct pkginfo *pkg_src); +void pkg_set_want(struct pkginfo *pkg, enum pkgwant want); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_H */ diff --git a/lib/dpkg/progname.c b/lib/dpkg/progname.c new file mode 100644 index 0000000..65592cc --- /dev/null +++ b/lib/dpkg/progname.c @@ -0,0 +1,89 @@ +/* + * libdpkg - Debian packaging suite library routines + * progname.c - program name handling functions + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#ifdef HAVE_GETPROCS64 +#include <sys/types.h> +#include <string.h> +#include <unistd.h> +#include <procinfo.h> +#endif +#include <errno.h> +#include <stdlib.h> + +#include <dpkg/path.h> +#include <dpkg/progname.h> + +static const char *progname; + +/** + * Set the program name. + * + * This function will set the program name which will be used by error + * reporting functions, among others. + * + * @param name The new program name. + */ +void +dpkg_set_progname(const char *name) +{ + progname = path_basename(name); +} + +#if defined(HAVE___PROGNAME) +extern const char *__progname; +#endif + +/** + * Get the program name. + * + * The program name might have been set previously by dpkg_set_progname(), + * but if not this function will try to initialize it by system dependent + * means, so it's half safe to not call dpkg_set_progname() at all. At worst + * the function might return NULL in that case. + * + * @return A pointer to a static buffer with the program name. + */ +const char * +dpkg_get_progname(void) +{ + if (progname == NULL) { +#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) + progname = program_invocation_short_name; +#elif defined(HAVE___PROGNAME) + progname = __progname; +#elif defined(HAVE_GETPROGNAME) + progname = getprogname(); +#elif defined(HAVE_GETEXECNAME) + /* getexecname(3) returns an absolute path, normalize it. */ + dpkg_set_progname(getexecname()); +#elif defined(HAVE_GETPROCS64) + struct procentry64 pe; + pid_t pid = getpid(); + + if (getprocs64(&pe, sizeof(pe), NULL, 0, &pid, 1) >= 0) + progname = strdup(pe.pi_comm); +#endif + } + + return progname; +} diff --git a/lib/dpkg/progname.h b/lib/dpkg/progname.h new file mode 100644 index 0000000..d7c6061 --- /dev/null +++ b/lib/dpkg/progname.h @@ -0,0 +1,41 @@ +/* + * libdpkg - Debian packaging suite library routines + * progname.h - program name handling functions + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PROGNAME_H +#define LIBDPKG_PROGNAME_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup progname Program name handling + * @ingroup dpkg-public + * @{ + */ + +void dpkg_set_progname(const char *name); +const char *dpkg_get_progname(void); + +/** @} */ + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/program.c b/lib/dpkg/program.c new file mode 100644 index 0000000..12226c0 --- /dev/null +++ b/lib/dpkg/program.c @@ -0,0 +1,59 @@ +/* + * libdpkg - Debian packaging suite library routines + * program.c - dpkg-based program support + * + * Copyright © 2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <stdio.h> + +#include <dpkg/progname.h> +#include <dpkg/debug.h> +#include <dpkg/report.h> +#include <dpkg/ehandle.h> +#include <dpkg/program.h> + +/** + * Standard initializations when starting a dpkg-based program. + * + * @param progname The program name. + */ +void +dpkg_program_init(const char *progname) +{ + dpkg_set_progname(progname); + dpkg_set_report_buffer(stdout); + dpkg_debug_init(); + + push_error_context(); + + /* Set sane default permissions for newly created files. */ + umask(022); +} + +/** + * Standard cleanups before terminating a dpkg-based program. + */ +void +dpkg_program_done(void) +{ + pop_error_context(ehflag_normaltidy); +} diff --git a/lib/dpkg/program.h b/lib/dpkg/program.h new file mode 100644 index 0000000..e4fe4f4 --- /dev/null +++ b/lib/dpkg/program.h @@ -0,0 +1,41 @@ +/* + * libdpkg - Debian packaging suite library routines + * program.h - dpkg-based program support + * + * Copyright © 2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PROGRAM_H +#define LIBDPKG_PROGRAM_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup program Program support + * @ingroup dpkg-public + * @{ + */ + +void dpkg_program_init(const char *progname); +void dpkg_program_done(void); + +/** @} */ + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/progress.c b/lib/dpkg/progress.c new file mode 100644 index 0000000..9b6032f --- /dev/null +++ b/lib/dpkg/progress.c @@ -0,0 +1,77 @@ +/* + * libdpkg - Debian packaging suite library routines + * progress.c - generic progress reporting + * + * Copyright © 2009 Romain Francoise <rfrancoise@debian.org> + * Copyright © 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <unistd.h> +#include <stdio.h> + +#include <dpkg/i18n.h> + +#include "progress.h" + +void +progress_init(struct progress *progress, const char *text, int max) +{ + progress->text = text; + progress->max = max; + progress->cur = 0; + progress->last_percent = 0; + + progress->on_tty = isatty(1); + + fputs(text, stdout); + if (progress->on_tty) + putchar('\r'); +} + +void +progress_step(struct progress *progress) +{ + int cur_percent; + + if (!progress->on_tty) + return; + + progress->cur++; + + cur_percent = (progress->cur * 100) / progress->max; + if (cur_percent <= progress->last_percent) + return; + if (cur_percent % 5) + return; + + progress->last_percent = cur_percent; + + fputs(progress->text, stdout); + /* TRANSLATORS: This is part of the progress output, it is a decimal + * percentage. */ + printf(_("%d%%"), cur_percent); + putchar('\r'); +} + +void +progress_done(struct progress *progress) +{ + if (progress->on_tty) + fputs(progress->text, stdout); +} diff --git a/lib/dpkg/progress.h b/lib/dpkg/progress.h new file mode 100644 index 0000000..c5450a5 --- /dev/null +++ b/lib/dpkg/progress.h @@ -0,0 +1,54 @@ +/* + * libdpkg - Debian packaging suite library routines + * progress.h - generic progress reporting + * + * Copyright © 2009 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PROGRESS_H +#define LIBDPKG_PROGRESS_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup progress Progress reporting + * @ingroup dpkg-internal + * @{ + */ + +struct progress { + const char *text; + + int max; + int cur; + int last_percent; + + bool on_tty; +}; + +void progress_init(struct progress *progress, const char *text, int max); +void progress_step(struct progress *progress); +void progress_done(struct progress *progress); + +/** @} */ + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/report.c b/lib/dpkg/report.c new file mode 100644 index 0000000..f7763b3 --- /dev/null +++ b/lib/dpkg/report.c @@ -0,0 +1,132 @@ +/* + * libdpkg - Debian packaging suite library routines + * report.c - message reporting + * + * Copyright © 2004-2005 Scott James Remnant <scott@netsplit.com> + * Copyright © 2008-2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/macros.h> +#include <dpkg/i18n.h> +#include <dpkg/progname.h> +#include <dpkg/color.h> +#include <dpkg/report.h> + +static int piped_mode = _IOLBF; + +void +dpkg_set_report_piped_mode(int mode) +{ + piped_mode = mode; +} + +void +dpkg_set_report_buffer(FILE *fp) +{ + if (isatty(fileno(fp))) + setvbuf(fp, NULL, _IONBF, 0); + else + setvbuf(fp, NULL, piped_mode, 0); +} + +void +dpkg_warning_printer(const char *msg, void *data) +{ + fprintf(stderr, "%s%s:%s %s%s:%s %s\n", + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), + color_get(COLOR_WARN), _("warning"), color_reset(), msg); +} + +static dpkg_warning_printer_func *warning_printer_func = dpkg_warning_printer; +static void *warning_printer_data; + +void +dpkg_set_warning_printer(dpkg_warning_printer_func *printer, void *data) +{ + warning_printer_func = printer; + warning_printer_data = data; +} + +static int warn_count = 0; + +int +warning_get_count(void) +{ + return warn_count; +} + +void +warningv(const char *fmt, va_list args) +{ + char *buf = NULL; + + warn_count++; + + m_vasprintf(&buf, fmt, args); + warning_printer_func(buf, warning_printer_data); + free(buf); +} + +void +warning(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + warningv(fmt, args); + va_end(args); +} + +void +notice(const char *fmt, ...) +{ + char *buf = NULL; + va_list args; + + va_start(args, fmt); + m_vasprintf(&buf, fmt, args); + va_end(args); + + fprintf(stderr, "%s%s:%s %s\n", + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), buf); + + free(buf); +} + +void +info(const char *fmt, ...) +{ + char *buf; + va_list args; + + va_start(args, fmt); + m_vasprintf(&buf, fmt, args); + va_end(args); + + printf("%s%s:%s %s\n", + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), buf); + + free(buf); +} diff --git a/lib/dpkg/report.h b/lib/dpkg/report.h new file mode 100644 index 0000000..862a842 --- /dev/null +++ b/lib/dpkg/report.h @@ -0,0 +1,58 @@ +/* + * libdpkg - Debian packaging suite library routines + * report.h - message reporting + * + * Copyright © 2004 Scott James Remnant <scott@netsplit.com> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_REPORT_H +#define LIBDPKG_REPORT_H + +#include <stdarg.h> +#include <stdio.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup report Message reporting + * @ingroup dpkg-internal + * @{ + */ + +void dpkg_set_report_piped_mode(int mode); +void dpkg_set_report_buffer(FILE *fp); + +typedef void dpkg_warning_printer_func(const char *msg, void *data); + +void dpkg_warning_printer(const char *msg, void *data); +void dpkg_set_warning_printer(dpkg_warning_printer_func *printer, void *data); + +int warning_get_count(void); +void warningv(const char *fmt, va_list args) DPKG_ATTR_VPRINTF(1); +void warning(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + +void notice(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + +void info(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_REPORT_H */ diff --git a/lib/dpkg/strhash.c b/lib/dpkg/strhash.c new file mode 100644 index 0000000..7fd51ee --- /dev/null +++ b/lib/dpkg/strhash.c @@ -0,0 +1,50 @@ +/* + * libdpkg - Debian packaging suite library routines + * strhash.c - FNV string hashing support + * + * Copyright © 2003 Daniel Silverstone <dsilvers@digital-scurf.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/string.h> + +#define FNV_OFFSET_BASIS 2166136261UL +#define FNV_MIXING_PRIME 16777619UL + +/** + * Fowler/Noll/Vo -- FNV-1a simple string hash. + * + * For more info, @see <http://www.isthe.com/chongo/tech/comp/fnv/index.html>. + * + * @param str The string to hash. + * + * @return The hashed value. + */ +unsigned int +str_fnv_hash(const char *str) +{ + unsigned int h = FNV_OFFSET_BASIS; + unsigned int p = FNV_MIXING_PRIME; + + while (*str) { + h ^= *str++; + h *= p; + } + + return h; +} diff --git a/lib/dpkg/string.c b/lib/dpkg/string.c new file mode 100644 index 0000000..de18297 --- /dev/null +++ b/lib/dpkg/string.c @@ -0,0 +1,196 @@ +/* + * libdpkg - Debian packaging suite library routines + * string.c - string handling routines + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/c-ctype.h> +#include <dpkg/string.h> +#include <dpkg/dpkg.h> + +char * +str_concat(char *dst, ...) +{ + va_list args; + const char *src; + + va_start(args, dst); + while ((src = va_arg(args, const char *))) { + size_t len; + + len = strlen(src); + memcpy(dst, src, len); + dst += len; + } + va_end(args); + *dst = '\0'; + + return dst; +} + +/** + * Match the end of a string. + * + * @param str The string. + * @param end The end to match in str. + * + * @return Whether the string was matched at the end. + */ +bool +str_match_end(const char *str, const char *end) +{ + size_t str_len = strlen(str); + size_t end_len = strlen(end); + const char *str_end = str + str_len - end_len; + + if (str_len >= end_len && strcmp(str_end, end) == 0) + return true; + else + return false; +} + +/** + * Print formatted output to an allocated string. + * + * @param fmt The format string. + * @param ... The format arguments. + * + * @return The new allocated formatted output string (never NULL). + */ +char * +str_fmt(const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + m_vasprintf(&str, fmt, args); + va_end(args); + + return str; +} + +/** + * Escape format characters from a string. + * + * @param dst The destination string. + * @param src The source string. + * @param n The size of the destination buffer. + * + * @return The end of the destination string. + */ +char * +str_escape_fmt(char *dst, const char *src, size_t n) +{ + char *d = dst; + const char *s = src; + + if (n == 0) + return d; + + while (*s) { + if (*s == '%') { + if (n-- <= 2) + break; + *d++ = '%'; + } + if (n-- <= 1) + break; + *d++ = *s++; + } + + *d = '\0'; + + return d; +} + +/** + * Quote shell metacharacters in a string. + * + * This function allows passing strings to commands without splitting the + * arguments, like in system(3) + * + * @param src The source string to escape. + * + * @return The new allocated string (never NULL). + */ +char * +str_quote_meta(const char *src) +{ + char *new_dst, *dst; + + new_dst = dst = m_malloc(strlen(src) * 2); + + while (*src) { + if (!c_isdigit(*src) && !c_isalpha(*src)) + *dst++ = '\\'; + + *dst++ = *src++; + } + + *dst = '\0'; + + return new_dst; +} + +/** + * Check and strip possible surrounding quotes in string. + * + * @param str The string to act on. + * + * @return A pointer to str or NULL if the quotes were unbalanced. + */ +char * +str_strip_quotes(char *str) +{ + if (str[0] == '"' || str[0] == '\'') { + size_t str_len = strlen(str); + + if (str[0] != str[str_len - 1]) + return NULL; + + /* Remove surrounding quotes. */ + str[str_len - 1] = '\0'; + str++; + } + + return str; +} + +/** + * Trim possible ending spaces in string. + * + * @param str The string to act on. + * @param str_end The end of the string to act on. + * + * @return A pointer to the end of the trimmed string. + */ +char * +str_rtrim_spaces(const char *str, char *str_end) +{ + while (str_end > str && c_isspace(str_end[-1])) + str_end--; + if (str_end >= str) + *str_end = '\0'; + return str_end; +} diff --git a/lib/dpkg/string.h b/lib/dpkg/string.h new file mode 100644 index 0000000..47ecd04 --- /dev/null +++ b/lib/dpkg/string.h @@ -0,0 +1,78 @@ +/* + * libdpkg - Debian packaging suite library routines + * string.h - string handling routines + * + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_STRING_H +#define LIBDPKG_STRING_H + +#include <stddef.h> +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup string String handling + * @ingroup dpkg-internal + * @{ + */ + +/** + * Check if a string is either null or empty. + */ +static inline bool +str_is_unset(const char *str) +{ + return str == DPKG_NULL || str[0] == '\0'; +} + +/** + * Check if a string has content. + */ +static inline bool +str_is_set(const char *str) +{ + return str != DPKG_NULL && str[0] != '\0'; +} + +bool str_match_end(const char *str, const char *end); + +unsigned int str_fnv_hash(const char *str); + +char *str_concat(char *dst, ...) DPKG_ATTR_SENTINEL; +char *str_fmt(const char *fmt, ...) DPKG_ATTR_PRINTF(1); +char *str_escape_fmt(char *dest, const char *src, size_t n); +char *str_quote_meta(const char *src); +char *str_strip_quotes(char *str); +char *str_rtrim_spaces(const char *str, char *str_end); + +struct str_crop_info { + int str_bytes; + int max_bytes; +}; + +int str_width(const char *str); +void str_gen_crop(const char *str, int max_width, struct str_crop_info *crop); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_STRING_H */ diff --git a/lib/dpkg/strwide.c b/lib/dpkg/strwide.c new file mode 100644 index 0000000..0a510b7 --- /dev/null +++ b/lib/dpkg/strwide.c @@ -0,0 +1,141 @@ +/* + * libdpkg - Debian packaging suite library routines + * strwide.c - wide character string handling routines + * + * Copyright © 2004 Changwoo Ryu <cwryu@debian.org> + * Copyright © 2012-2013 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> +#ifdef ENABLE_NLS +#include <wchar.h> +#endif + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/string.h> + +/** + * Compute the screen width of a string. + * + * @param str The multibyte string. + * + * @return The width of the string. + */ +int +str_width(const char *str) +{ +#ifdef ENABLE_NLS + mbstate_t state; + wchar_t *wcs; + const char *mbs = str; + size_t len, res; + int width; + + len = strlen(str) + 1; + wcs = m_malloc(sizeof(wcs[0]) * len); + + memset(&state, 0, sizeof(state)); + + res = mbsrtowcs(wcs, &mbs, len, &state); + if (res == (size_t)-1) { +#ifdef DPKG_UNIFORM_ENCODING + ohshit(_("cannot convert multibyte string '%s' " + "to a wide-character string"), str); +#else + /* Cannot convert, fallback to ASCII method. */ + free(wcs); + return strlen(str); +#endif + } + + width = wcswidth(wcs, res); + + free(wcs); + + return width; +#else + return strlen(str); +#endif +} + +/** + * Generate the crop values for a string given a maximum screen width. + * + * This function analyzes the string passed and computes the correct point + * where to crop the string, returning the amount of string and maximum + * bytes to use for padding for example. + * + * On NLS enabled builds, in addition the string will be cropped on any + * newline. + * + * @param str The string to crop. + * @param max_width The max screen width to use. + * @param[out] crop The generated crop values for the string. + */ +void +str_gen_crop(const char *str, int max_width, struct str_crop_info *crop) +{ +#ifdef ENABLE_NLS + mbstate_t state; + size_t str_bytes; + int mbs_bytes = 0; + int mbs_width = 0; + + str_bytes = strlen(str) + 1; + memset(&state, 0, sizeof(state)); + + for (;;) { + wchar_t wc; + int wc_width; + size_t mb_bytes; + + mb_bytes = mbrtowc(&wc, str, str_bytes, &state); + if (mb_bytes == (size_t)-1 || mb_bytes == (size_t)-2) { +#ifdef DPKG_UNIFORM_ENCODING + ohshit(_("cannot convert multibyte sequence '%s' " + "to a wide character"), str); +#else + /* Cannot convert, fallback to ASCII method. */ + crop->str_bytes = crop->max_bytes = max_width; + return; +#endif + } + if (mb_bytes == 0) + break; + + wc_width = wcwidth(wc); + if (wc_width < 0) + break; + if (mbs_width + wc_width > max_width) + break; + + mbs_width += wc_width; + mbs_bytes += mb_bytes; + str_bytes -= mb_bytes; + str += mb_bytes; + } + + crop->str_bytes = mbs_bytes; + crop->max_bytes = mbs_bytes + max_width - mbs_width; +#else + crop->str_bytes = crop->max_bytes = max_width; +#endif +} diff --git a/lib/dpkg/subproc.c b/lib/dpkg/subproc.c new file mode 100644 index 0000000..7b3fb99 --- /dev/null +++ b/lib/dpkg/subproc.c @@ -0,0 +1,195 @@ +/* + * libdpkg - Debian packaging suite library routines + * subproc.c - subprocess helper routines + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/subproc.h> + +static int signo_ignores[] = { SIGQUIT, SIGINT }; +static struct sigaction sa_save[array_count(signo_ignores)]; + +static void +subproc_reset_signal(int sig, struct sigaction *sa_old) +{ + if (sigaction(sig, sa_old, NULL)) { + fprintf(stderr, _("error un-catching signal %s: %s\n"), + strsignal(sig), strerror(errno)); + onerr_abort++; + } +} + +static void +subproc_set_signal(int sig, struct sigaction *sa, struct sigaction *sa_old, + const char *name) +{ + if (sigaction(sig, sa, sa_old)) + ohshite(_("unable to ignore signal %s before running %.250s"), + strsignal(sig), name); +} + +void +subproc_signals_ignore(const char *name) +{ + struct sigaction sa; + size_t i; + + onerr_abort++; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + + for (i = 0; i < array_count(signo_ignores); i++) + subproc_set_signal(signo_ignores[i], &sa, &sa_save[i], name); + + push_cleanup(subproc_signals_cleanup, ~0, 0); + onerr_abort--; +} + +void +subproc_signals_cleanup(int argc, void **argv) +{ + size_t i; + + for (i = 0; i < array_count(signo_ignores); i++) + subproc_reset_signal(signo_ignores[i], &sa_save[i]); +} + +void +subproc_signals_restore(void) +{ + pop_cleanup(ehflag_normaltidy); +} + +static void +print_subproc_error(const char *emsg, const void *data) +{ + fprintf(stderr, _("%s (subprocess): %s\n"), dpkg_get_progname(), emsg); +} + +pid_t +subproc_fork(void) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) { + onerr_abort++; + ohshite(_("fork failed")); + } + if (pid > 0) + return pid; + + /* Push a new error context, so that we don't do the other cleanups, + * because they'll be done by/in the parent process. */ + push_error_context_func(catch_fatal_error, print_subproc_error, NULL); + + return pid; +} + +static int +subproc_check(int status, const char *desc, enum subproc_flags flags) +{ + void (*out)(const char *fmt, ...) DPKG_ATTR_PRINTF(1); + int n; + + if (flags & SUBPROC_WARN) + out = warning; + else + out = ohshit; + + if (WIFEXITED(status)) { + n = WEXITSTATUS(status); + if (!n) + return 0; + if (flags & SUBPROC_RETERROR) + return n; + + out(_("%s subprocess returned error exit status %d"), desc, n); + } else if (WIFSIGNALED(status)) { + n = WTERMSIG(status); + if (!n) + return 0; + if ((flags & SUBPROC_NOPIPE) && n == SIGPIPE) + return 0; + if (flags & SUBPROC_RETSIGNO) + return n; + + if (n == SIGINT) + out(_("%s subprocess was interrupted"), desc); + else + out(_("%s subprocess was killed by signal (%s)%s"), + desc, strsignal(n), + WCOREDUMP(status) ? _(", core dumped") : ""); + } else { + if (flags & SUBPROC_RETERROR) + return -1; + + out(_("%s subprocess failed with wait status code %d"), desc, + status); + } + + return -1; +} + +static int +subproc_wait(pid_t pid, const char *desc) +{ + pid_t dead_pid; + int status; + + while ((dead_pid = waitpid(pid, &status, 0)) == -1 && errno == EINTR) ; + + if (dead_pid != pid) { + onerr_abort++; + ohshite(_("wait for %s subprocess failed"), desc); + } + + return status; +} + +int +subproc_reap(pid_t pid, const char *desc, enum subproc_flags flags) +{ + int status, rc; + + status = subproc_wait(pid, desc); + + if (flags & SUBPROC_NOCHECK) + rc = status; + else + rc = subproc_check(status, desc, flags); + + return rc; +} diff --git a/lib/dpkg/subproc.h b/lib/dpkg/subproc.h new file mode 100644 index 0000000..4bdf9f2 --- /dev/null +++ b/lib/dpkg/subproc.h @@ -0,0 +1,62 @@ +/* + * libdpkg - Debian packaging suite library routines + * subproc.h - sub-process handling routines + * + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_SUBPROC_H +#define LIBDPKG_SUBPROC_H + +#include <sys/types.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup subproc Sub-process handling + * @ingroup dpkg-internal + * @{ + */ + +enum DPKG_ATTR_ENUM_FLAGS subproc_flags { + /** Default subprocess flags. */ + SUBPROC_NORMAL = 0, + /** Emit a warning instead of an error. */ + SUBPROC_WARN = DPKG_BIT(0), + /** Ignore SIGPIPE, and make it return 0. */ + SUBPROC_NOPIPE = DPKG_BIT(1), + /** Do not check the subprocess status. */ + SUBPROC_NOCHECK = DPKG_BIT(2), + /** Do not emit errors, just return the exit status. */ + SUBPROC_RETERROR = DPKG_BIT(3), + /** Do not emit errors, just return the signal number. */ + SUBPROC_RETSIGNO = DPKG_BIT(3), +}; + +void subproc_signals_ignore(const char *name); +void subproc_signals_cleanup(int argc, void **argv); +void subproc_signals_restore(void); + +pid_t subproc_fork(void); +int subproc_reap(pid_t pid, const char *desc, enum subproc_flags flags); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_SUBPROC_H */ diff --git a/lib/dpkg/t/b-fsys-hash.c b/lib/dpkg/t/b-fsys-hash.c new file mode 100644 index 0000000..7ae8f9c --- /dev/null +++ b/lib/dpkg/t/b-fsys-hash.c @@ -0,0 +1,81 @@ +/* + * libdpkg - Debian packaging suite library routines + * b-fsys-hash.c - test fsys database load and hash performance + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#include <dpkg/perf.h> + +#include <dpkg/db-fsys.h> + +static const char *admindir; + +int +main(int argc, const char *const *argv) +{ + struct perf_slot ps; + + push_error_context(); + setvbuf(stdout, NULL, _IONBF, 0); + + admindir = dpkg_db_set_dir(admindir); + + perf_ts_mark_print("init"); + + perf_ts_slot_start(&ps); + fsys_hash_init(); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "fsys_hash_init"); + + perf_ts_slot_start(&ps); + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "modstatdb_init"); + + perf_ts_slot_start(&ps); + ensure_allinstfiles_available_quiet(); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "load .list"); + + if (test_is_verbose()) { + pkg_hash_report(stdout); + fsys_hash_report(stdout); + } + + modstatdb_shutdown(); + pop_error_context(ehflag_normaltidy); + + perf_ts_mark_print("shutdown"); + + return 0; +} diff --git a/lib/dpkg/t/b-pkg-hash.c b/lib/dpkg/t/b-pkg-hash.c new file mode 100644 index 0000000..26cdc12 --- /dev/null +++ b/lib/dpkg/t/b-pkg-hash.c @@ -0,0 +1,65 @@ +/* + * libdpkg - Debian packaging suite library routines + * b-pkg-hash.c - test pkg database load and hash performance + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#include <dpkg/perf.h> + +static const char *admindir; + +int +main(int argc, const char *const *argv) +{ + struct perf_slot ps; + + push_error_context(); + setvbuf(stdout, NULL, _IONBF, 0); + + admindir = dpkg_db_set_dir(admindir); + + perf_ts_mark_print("init"); + + perf_ts_slot_start(&ps); + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "modstatdb_init"); + + if (test_is_verbose()) + pkg_hash_report(stdout); + + modstatdb_shutdown(); + pop_error_context(ehflag_normaltidy); + + perf_ts_mark_print("shutdown"); + + return 0; +} diff --git a/lib/dpkg/t/c-tarextract.c b/lib/dpkg/t/c-tarextract.c new file mode 100644 index 0000000..82a8dab --- /dev/null +++ b/lib/dpkg/t/c-tarextract.c @@ -0,0 +1,154 @@ +/* + * libdpkg - Debian packaging suite library routines + * c-tarextract.c - test tar extractor + * + * Copyright © 2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#if HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> /* Needed on AIX for major()/minor(). */ +#endif +#if HAVE_SYS_MKDEV_H +#include <sys/mkdev.h> /* Needed on Solaris for major()/minor(). */ +#endif + +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/ehandle.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> +#include <dpkg/tarfn.h> + +struct tar_context { + int tar_fd; +}; + +static int +tar_read(struct tar_archive *tar, char *buffer, int size) +{ + struct tar_context *tc = tar->ctx; + + return fd_read(tc->tar_fd, buffer, size); +} + +static int +tar_object_skip(struct tar_archive *tar, struct tar_entry *te) +{ + struct tar_context *tc = tar->ctx; + off_t size; + + size = (te->size + TARBLKSZ - 1) / TARBLKSZ * TARBLKSZ; + if (size == 0) + return 0; + + return fd_skip(tc->tar_fd, size, NULL); +} + +static int +tar_object(struct tar_archive *tar, struct tar_entry *te) +{ + printf("%s mode=%o time=%jd.%.9d uid=%d gid=%d", te->name, + te->stat.mode, te->mtime, 0, te->stat.uid, te->stat.gid); + if (te->stat.uname) + printf(" uname=%s", te->stat.uname); + if (te->stat.gname) + printf(" gname=%s", te->stat.gname); + + switch (te->type) { + case TAR_FILETYPE_FILE0: + case TAR_FILETYPE_FILE: + tar_object_skip(tar, te); + printf(" type=file size=%jd", (intmax_t)te->size); + break; + case TAR_FILETYPE_HARDLINK: + printf(" type=hardlink linkto=%s size=%jd", + te->linkname, (intmax_t)te->size); + break; + case TAR_FILETYPE_SYMLINK: + printf(" type=symlink linkto=%s size=%jd", + te->linkname, (intmax_t)te->size); + break; + case TAR_FILETYPE_DIR: + printf(" type=dir"); + break; + case TAR_FILETYPE_CHARDEV: + case TAR_FILETYPE_BLOCKDEV: + printf(" type=device id=%d.%d", major(te->dev), minor(te->dev)); + break; + case TAR_FILETYPE_FIFO: + printf(" type=fifo"); + break; + default: + ohshit("unexpected tar entry type '%c'", te->type); + } + + printf("\n"); + + return 0; +} + +static struct tar_operations tar_ops = { + .read = tar_read, + .extract_file = tar_object, + .link = tar_object, + .symlink = tar_object, + .mkdir = tar_object, + .mknod = tar_object, +}; + +int +main(int argc, char **argv) +{ + struct tar_archive tar; + struct tar_context tar_ctx; + const char *tar_name = argv[1]; + + setvbuf(stdout, NULL, _IOLBF, 0); + + push_error_context(); + + if (tar_name) { + tar_ctx.tar_fd = open(tar_name, O_RDONLY); + if (tar_ctx.tar_fd < 0) + ohshite("cannot open file '%s'", tar_name); + } else { + tar_ctx.tar_fd = STDIN_FILENO; + } + + tar.err = DPKG_ERROR_OBJECT; + tar.ctx = &tar_ctx; + tar.ops = &tar_ops; + + if (tar_extractor(&tar)) + ohshite("extracting tar"); + + dpkg_error_destroy(&tar.err); + + if (tar_name) + close(tar_ctx.tar_fd); + + pop_error_context(ehflag_normaltidy); + + return 0; +} diff --git a/lib/dpkg/t/c-treewalk.c b/lib/dpkg/t/c-treewalk.c new file mode 100644 index 0000000..4cd2b8c --- /dev/null +++ b/lib/dpkg/t/c-treewalk.c @@ -0,0 +1,132 @@ +/* + * libdpkg - Debian packaging suite library routines + * c-treewalk.c - test tree walk implementation + * + * Copyright © 2013-2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/ehandle.h> +#include <dpkg/treewalk.h> + +static int +treenode_type(struct treenode *node) +{ + switch (treenode_get_mode(node) & S_IFMT) { + case S_IFREG: + return 'f'; + case S_IFDIR: + return 'd'; + case S_IFLNK: + return 'l'; + case S_IFIFO: + return 'p'; + case S_IFBLK: + return 'b'; + case S_IFCHR: + return 'c'; + case S_IFSOCK: + return 's'; + default: + return '?'; + } +} + +static int +treenode_visit_meta(struct treenode *node) +{ + printf("T=%c N=%s V=%s R=%s\n", + treenode_type(node), treenode_get_name(node), + treenode_get_virtname(node), treenode_get_pathname(node)); + + return 0; +} + +static int +treenode_visit_virt(struct treenode *node) +{ + printf("%s\n", treenode_get_virtname(node)); + + return 0; +} + +static int +treenode_visit_path(struct treenode *node) +{ + printf("%s\n", treenode_get_pathname(node)); + + return 0; +} + +static const char *skipname; + +static bool +treenode_skip(struct treenode *node) +{ + const char *absname = treenode_get_pathname(node); + + if (strcmp(absname, skipname) == 0) + return true; + + return false; +} + +static void +test_treewalk_list(const char *rootdir) +{ + const char *visitname = getenv("TREEWALK_VISIT"); + struct treewalk_funcs funcs = { NULL }; + + if (visitname == NULL || strcmp(visitname, "meta") == 0) + funcs.visit = treenode_visit_meta; + else if (strcmp(visitname, "virt") == 0) + funcs.visit = treenode_visit_virt; + else if (strcmp(visitname, "path") == 0) + funcs.visit = treenode_visit_path; + else + ohshit("unknown treewalk visit name"); + + skipname = getenv("TREEWALK_SKIP"); + if (skipname) + funcs.skip = treenode_skip; + + treewalk(rootdir, 0, &funcs); +} + +int +main(int argc, char **argv) +{ + const char *rootdir = argv[1]; + + setvbuf(stdout, NULL, _IOLBF, 0); + + push_error_context(); + + if (rootdir == NULL) + ohshit("missing treewalk root dir"); + + test_treewalk_list(rootdir); + + pop_error_context(ehflag_normaltidy); + + return 0; +} diff --git a/lib/dpkg/t/c-trigdeferred.c b/lib/dpkg/t/c-trigdeferred.c new file mode 100644 index 0000000..5c21ea3 --- /dev/null +++ b/lib/dpkg/t/c-trigdeferred.c @@ -0,0 +1,96 @@ +/* + * libdpkg - Debian packaging suite library routines + * c-trigdeferred.c - test triggered deferred file parser + * + * Copyright © 2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/dpkg-db.h> +#include <dpkg/ehandle.h> +#include <dpkg/trigdeferred.h> + +static void +test_tdm_begin(const char *trig) +{ + printf("<T='%s'", trig); +} + +static void +test_tdm_package(const char *awname) +{ + printf(" P='%s'", awname); +} + +static void +test_tdm_end(void) +{ + printf(" E>\n"); +} + +static const struct trigdefmeths test_tdm = { + .trig_begin = test_tdm_begin, + .package = test_tdm_package, + .trig_end = test_tdm_end, +}; + +static int +test_trigdeferred_parser(const char *admindir) +{ + enum trigdef_update_flags tduf; + enum trigdef_update_status tdus; + + dpkg_db_set_dir(admindir); + + trigdef_set_methods(&test_tdm); + + tduf = TDUF_NO_LOCK | TDUF_WRITE_IF_EMPTY | TDUF_WRITE_IF_ENOENT; + tdus = trigdef_update_start(tduf); + if (tdus < TDUS_OK) + return -tdus + TDUS_OK; + + trigdef_parse(); + + trigdef_process_done(); + + return 0; +} + +int +main(int argc, char **argv) +{ + const char *admindir = argv[1]; + int ret; + + setvbuf(stdout, NULL, _IOLBF, 0); + + push_error_context(); + + if (admindir == NULL) + ohshit("missing triggers deferred admindir"); + + ret = test_trigdeferred_parser(admindir); + + pop_error_context(ehflag_normaltidy); + + return ret; +} diff --git a/lib/dpkg/t/data/command/path-a/cmd b/lib/dpkg/t/data/command/path-a/cmd new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-a/cmd @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/command/path-a/cmd-a b/lib/dpkg/t/data/command/path-a/cmd-a new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-a/cmd-a @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/command/path-b/cmd b/lib/dpkg/t/data/command/path-b/cmd new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-b/cmd @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/command/path-b/cmd-b b/lib/dpkg/t/data/command/path-b/cmd-b new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-b/cmd-b @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/command/path-noexec/cmd b/lib/dpkg/t/data/command/path-noexec/cmd new file mode 100644 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-noexec/cmd @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/command/path-noexec/cmd-noexec b/lib/dpkg/t/data/command/path-noexec/cmd-noexec new file mode 100644 index 0000000..1a24852 --- /dev/null +++ b/lib/dpkg/t/data/command/path-noexec/cmd-noexec @@ -0,0 +1 @@ +#!/bin/sh diff --git a/lib/dpkg/t/data/meminfo-no-data b/lib/dpkg/t/data/meminfo-no-data new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/dpkg/t/data/meminfo-no-data diff --git a/lib/dpkg/t/data/meminfo-no-info b/lib/dpkg/t/data/meminfo-no-info new file mode 100644 index 0000000..e54196b --- /dev/null +++ b/lib/dpkg/t/data/meminfo-no-info @@ -0,0 +1,52 @@ +MemTotal: 3951508 kB +MemFree: 130976 kB +MemAvailable: 10584 kB +Buffers: 2448 kB +SwapCached: 12936 kB +Active: 3111920 kB +Inactive: 610376 kB +Active(anon): 3102668 kB +Inactive(anon): 606952 kB +Active(file): 9252 kB +Inactive(file): 3424 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 4194300 kB +SwapFree: 3777400 kB +Zswap: 0 kB +Zswapped: 0 kB +Dirty: 0 kB +Writeback: 0 kB +AnonPages: 12960 kB +Mapped: 6700 kB +Shmem: 3684416 kB +KReclaimable: 27616 kB +Slab: 54652 kB +SReclaimable: 27616 kB +SUnreclaim: 27036 kB +KernelStack: 2496 kB +PageTables: 1516 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 6170052 kB +Committed_AS: 4212940 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 16116 kB +VmallocChunk: 0 kB +Percpu: 2288 kB +HardwareCorrupted: 0 kB +AnonHugePages: 0 kB +ShmemHugePages: 0 kB +ShmemPmdMapped: 0 kB +FileHugePages: 0 kB +FilePmdMapped: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +Hugetlb: 0 kB +DirectMap4k: 110452 kB +DirectMap2M: 5132288 kB +DirectMap1G: 5242880 kB diff --git a/lib/dpkg/t/data/meminfo-no-unit b/lib/dpkg/t/data/meminfo-no-unit new file mode 100644 index 0000000..4db2d9c --- /dev/null +++ b/lib/dpkg/t/data/meminfo-no-unit @@ -0,0 +1,53 @@ +MemTotal: 3951508 +MemFree: 130976 +MemAvailable: 10584 +Buffers: 2448 +Cached: 3694676 +SwapCached: 12936 +Active: 3111920 +Inactive: 610376 +Active(anon): 3102668 +Inactive(anon): 606952 +Active(file): 9252 +Inactive(file): 3424 +Unevictable: 0 +Mlocked: 0 +SwapTotal: 4194300 +SwapFree: 3777400 +Zswap: 0 +Zswapped: 0 +Dirty: 0 +Writeback: 0 +AnonPages: 12960 +Mapped: 6700 +Shmem: 3684416 +KReclaimable: 27616 +Slab: 54652 +SReclaimable: 27616 +SUnreclaim: 27036 +KernelStack: 2496 +PageTables: 1516 +NFS_Unstable: 0 +Bounce: 0 +WritebackTmp: 0 +CommitLimit: 6170052 +Committed_AS: 4212940 +VmallocTotal: 34359738367 +VmallocUsed: 16116 +VmallocChunk: 0 +Percpu: 2288 +HardwareCorrupted: 0 +AnonHugePages: 0 +ShmemHugePages: 0 +ShmemPmdMapped: 0 +FileHugePages: 0 +FilePmdMapped: 0 +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 +Hugetlb: 0 +DirectMap4k: 110452 +DirectMap2M: 5132288 +DirectMap1G: 5242880 diff --git a/lib/dpkg/t/data/meminfo-ok b/lib/dpkg/t/data/meminfo-ok new file mode 100644 index 0000000..d4fdf0b --- /dev/null +++ b/lib/dpkg/t/data/meminfo-ok @@ -0,0 +1,53 @@ +MemTotal: 3951508 kB +MemFree: 130976 kB +MemAvailable: 10584 kB +Buffers: 2448 kB +Cached: 3694676 kB +SwapCached: 12936 kB +Active: 3111920 kB +Inactive: 610376 kB +Active(anon): 3102668 kB +Inactive(anon): 606952 kB +Active(file): 9252 kB +Inactive(file): 3424 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 4194300 kB +SwapFree: 3777400 kB +Zswap: 0 kB +Zswapped: 0 kB +Dirty: 0 kB +Writeback: 0 kB +AnonPages: 12960 kB +Mapped: 6700 kB +Shmem: 3684416 kB +KReclaimable: 27616 kB +Slab: 54652 kB +SReclaimable: 27616 kB +SUnreclaim: 27036 kB +KernelStack: 2496 kB +PageTables: 1516 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 6170052 kB +Committed_AS: 4212940 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 16116 kB +VmallocChunk: 0 kB +Percpu: 2288 kB +HardwareCorrupted: 0 kB +AnonHugePages: 0 kB +ShmemHugePages: 0 kB +ShmemPmdMapped: 0 kB +FileHugePages: 0 kB +FilePmdMapped: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +Hugetlb: 0 kB +DirectMap4k: 110452 kB +DirectMap2M: 5132288 kB +DirectMap1G: 5242880 kB diff --git a/lib/dpkg/t/t-ar.c b/lib/dpkg/t/t-ar.c new file mode 100644 index 0000000..88e9387 --- /dev/null +++ b/lib/dpkg/t/t-ar.c @@ -0,0 +1,61 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-ar.c - test ar implementation + * + * Copyright © 2010 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/test.h> +#include <dpkg/ar.h> + +static void +test_ar_normalize_name(void) +{ + struct dpkg_ar_hdr arh; + + memccpy(arh.ar_name, "member-name/ ", '\0', sizeof(arh.ar_name)); + dpkg_ar_normalize_name(&arh); + test_str(arh.ar_name, ==, "member-name"); + + memccpy(arh.ar_name, "member-name ", '\0', sizeof(arh.ar_name)); + dpkg_ar_normalize_name(&arh); + test_str(arh.ar_name, ==, "member-name"); +} + +static void +test_ar_member_is_illegal(void) +{ + struct dpkg_ar_hdr arh; + + memset(&arh, ' ', sizeof(arh)); + test_pass(dpkg_ar_member_is_illegal(&arh)); + + memcpy(arh.ar_fmag, DPKG_AR_FMAG, sizeof(arh.ar_fmag)); + test_fail(dpkg_ar_member_is_illegal(&arh)); +} + +TEST_ENTRY(test) +{ + test_plan(4); + + test_ar_normalize_name(); + test_ar_member_is_illegal(); +} diff --git a/lib/dpkg/t/t-arch.c b/lib/dpkg/t/t-arch.c new file mode 100644 index 0000000..d0b5735 --- /dev/null +++ b/lib/dpkg/t/t-arch.c @@ -0,0 +1,227 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-arch.c - test dpkg_arch implementation + * + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/varbuf.h> +#include <dpkg/arch.h> + +static void +test_dpkg_arch_name_is_illegal(void) +{ + /* Test invalid architecture names. */ + test_fail(dpkg_arch_name_is_illegal("") == NULL); + test_fail(dpkg_arch_name_is_illegal("-i386") == NULL); + test_fail(dpkg_arch_name_is_illegal(" i386") == NULL); + test_fail(dpkg_arch_name_is_illegal(":any") == NULL); + test_fail(dpkg_arch_name_is_illegal("amd64_test") == NULL); + test_fail(dpkg_arch_name_is_illegal("i386:test") == NULL); + test_fail(dpkg_arch_name_is_illegal("i386 amd64") == NULL); + test_fail(dpkg_arch_name_is_illegal("i386,amd64") == NULL); + test_fail(dpkg_arch_name_is_illegal("i386|amd64") == NULL); + + /* Test valid architecture names. */ + test_pass(dpkg_arch_name_is_illegal("i386") == NULL); + test_pass(dpkg_arch_name_is_illegal("amd64") == NULL); + test_pass(dpkg_arch_name_is_illegal("hurd-i386") == NULL); + test_pass(dpkg_arch_name_is_illegal("kfreebsd-i386") == NULL); + test_pass(dpkg_arch_name_is_illegal("kfreebsd-amd64") == NULL); + test_pass(dpkg_arch_name_is_illegal("ia64") == NULL); + test_pass(dpkg_arch_name_is_illegal("alpha") == NULL); + test_pass(dpkg_arch_name_is_illegal("armel") == NULL); + test_pass(dpkg_arch_name_is_illegal("hppa") == NULL); + test_pass(dpkg_arch_name_is_illegal("mips") == NULL); + test_pass(dpkg_arch_name_is_illegal("mipsel") == NULL); + test_pass(dpkg_arch_name_is_illegal("powerpc") == NULL); + test_pass(dpkg_arch_name_is_illegal("s390") == NULL); + test_pass(dpkg_arch_name_is_illegal("sparc") == NULL); +} + +static void +test_dpkg_arch_get_list(void) +{ + struct dpkg_arch *arch; + int count = 1; + + /* Must never return NULL. */ + arch = dpkg_arch_get_list(); + test_alloc(arch); + + while ((arch = arch->next)) + count++; + + /* The default list should contain 3 architectures. */ + test_pass(count == 3); +} + +static void +test_dpkg_arch_find(void) +{ + struct dpkg_arch *arch; + + /* Test existence and initial values of default architectures. */ + arch = dpkg_arch_find("all"); + test_pass(arch->type == DPKG_ARCH_ALL); + test_pass(dpkg_arch_get(DPKG_ARCH_ALL) == arch); + arch = dpkg_arch_find(ARCHITECTURE); + test_pass(arch->type == DPKG_ARCH_NATIVE); + test_pass(dpkg_arch_get(DPKG_ARCH_NATIVE) == arch); + arch = dpkg_arch_find("any"); + test_pass(arch->type == DPKG_ARCH_WILDCARD); + test_pass(dpkg_arch_get(DPKG_ARCH_WILDCARD) == arch); + + /* Test missing architecture. */ + arch = dpkg_arch_find(NULL); + test_pass(arch->type == DPKG_ARCH_NONE); + test_pass(dpkg_arch_get(DPKG_ARCH_NONE) == arch); + test_str(arch->name, ==, ""); + + /* Test empty architectures. */ + arch = dpkg_arch_find(""); + test_pass(arch->type == DPKG_ARCH_EMPTY); + test_pass(dpkg_arch_get(DPKG_ARCH_EMPTY) == arch); + test_str(arch->name, ==, ""); + + /* Test for an unknown type. */ + DPKG_IGNORE_WARNING_ASSIGN_ENUM(); + test_pass(dpkg_arch_get(1000) == NULL); + DPKG_ACCEPT_WARNING_ASSIGN_ENUM(); + + /* New valid architectures are marked unknown. */ + arch = dpkg_arch_find("foobar"); + test_pass(arch->type == DPKG_ARCH_UNKNOWN); + test_str(arch->name, ==, "foobar"); + + /* New illegal architectures are marked illegal. */ + arch = dpkg_arch_find("a:b"); + test_pass(arch->type == DPKG_ARCH_ILLEGAL); + test_str(arch->name, ==, "a:b"); +} + +static void +test_dpkg_arch_reset_list(void) +{ + dpkg_arch_reset_list(); + + test_dpkg_arch_get_list(); +} + +static void +test_dpkg_arch_modify(void) +{ + struct dpkg_arch *arch; + + dpkg_arch_reset_list(); + + /* Insert a new unknown arch. */ + arch = dpkg_arch_find("foo"); + test_pass(arch->type == DPKG_ARCH_UNKNOWN); + test_str(arch->name, ==, "foo"); + + /* Check that existing unknown arch gets tagged. */ + arch = dpkg_arch_add("foo"); + test_pass(arch->type == DPKG_ARCH_FOREIGN); + test_str(arch->name, ==, "foo"); + + /* Check that new unknown arch gets tagged. */ + arch = dpkg_arch_add("quux"); + test_pass(arch->type == DPKG_ARCH_FOREIGN); + test_str(arch->name, ==, "quux"); + + /* Unmark foreign architectures. */ + + arch = dpkg_arch_find("foo"); + dpkg_arch_unmark(arch); + test_pass(arch->type == DPKG_ARCH_UNKNOWN); + + arch = dpkg_arch_find("bar"); + dpkg_arch_unmark(arch); + test_pass(arch->type == DPKG_ARCH_UNKNOWN); + + arch = dpkg_arch_find("quux"); + dpkg_arch_unmark(arch); + test_pass(arch->type == DPKG_ARCH_UNKNOWN); +} + +static void +test_dpkg_arch_varbuf_archqual(void) +{ + struct varbuf vb = VARBUF_INIT; + + varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_NONE)); + varbuf_end_str(&vb); + test_str(vb.buf, ==, ""); + varbuf_reset(&vb); + + varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_EMPTY)); + varbuf_end_str(&vb); + test_str(vb.buf, ==, ""); + varbuf_reset(&vb); + + varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_ALL)); + varbuf_end_str(&vb); + test_str(vb.buf, ==, ":all"); + varbuf_reset(&vb); + + varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_WILDCARD)); + varbuf_end_str(&vb); + test_str(vb.buf, ==, ":any"); + varbuf_reset(&vb); + + varbuf_destroy(&vb); +} + +static void +test_dpkg_arch_describe(void) +{ + struct dpkg_arch *arch; + + arch = dpkg_arch_get(DPKG_ARCH_NONE); + test_str(dpkg_arch_describe(arch), ==, "<none>"); + + arch = dpkg_arch_get(DPKG_ARCH_EMPTY); + test_str(dpkg_arch_describe(arch), ==, "<empty>"); + + arch = dpkg_arch_get(DPKG_ARCH_ALL); + test_str(dpkg_arch_describe(arch), ==, "all"); + + arch = dpkg_arch_get(DPKG_ARCH_WILDCARD); + test_str(dpkg_arch_describe(arch), ==, "any"); + + arch = dpkg_arch_get(DPKG_ARCH_NATIVE); + test_str(dpkg_arch_describe(arch), ==, ARCHITECTURE); +} + +TEST_ENTRY(test) +{ + test_plan(60); + + test_dpkg_arch_name_is_illegal(); + test_dpkg_arch_get_list(); + test_dpkg_arch_find(); + test_dpkg_arch_reset_list(); + test_dpkg_arch_modify(); + test_dpkg_arch_varbuf_archqual(); + test_dpkg_arch_describe(); +} diff --git a/lib/dpkg/t/t-buffer.c b/lib/dpkg/t/t-buffer.c new file mode 100644 index 0000000..fa939e5 --- /dev/null +++ b/lib/dpkg/t/t-buffer.c @@ -0,0 +1,82 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-buffer.c - test buffer handling + * + * Copyright © 2009-2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/buffer.h> +#include <dpkg/dpkg.h> + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +static const char str_empty[] = ""; +static const char ref_hash_empty[] = "d41d8cd98f00b204e9800998ecf8427e"; +static const char str_test[] = "this is a test string\n"; +static const char ref_hash_test[] = "475aae3b885d70a9130eec23ab33f2b9"; + +static void +test_buffer_hash(void) +{ + char hash[MD5HASHLEN + 1]; + + buffer_md5(str_empty, hash, strlen(str_empty)); + test_str(hash, ==, ref_hash_empty); + + buffer_md5(str_test, hash, strlen(str_test)); + test_str(hash, ==, ref_hash_test); +} + +static void +test_fdio_hash(void) +{ + char hash[MD5HASHLEN + 1]; + char *test_file; + int fd; + + test_file = test_alloc(strdup("test.XXXXXX")); + fd = mkstemp(test_file); + test_pass(fd >= 0); + + test_pass(fd_md5(fd, hash, -1, NULL) >= 0); + test_str(hash, ==, ref_hash_empty); + + test_pass(write(fd, str_test, strlen(str_test)) == (ssize_t)strlen(str_test)); + test_pass(lseek(fd, 0, SEEK_SET) == 0); + + test_pass(fd_md5(fd, hash, -1, NULL) >= 0); + test_str(hash, ==, ref_hash_test); + + test_pass(unlink(test_file) == 0); + + free(test_file); +} + +TEST_ENTRY(test) +{ + test_plan(10); + + test_buffer_hash(); + test_fdio_hash(); +} diff --git a/lib/dpkg/t/t-c-ctype.c b/lib/dpkg/t/t-c-ctype.c new file mode 100644 index 0000000..2da0553 --- /dev/null +++ b/lib/dpkg/t/t-c-ctype.c @@ -0,0 +1,106 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-c-ctype.c - test C locale ctype functions + * + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/c-ctype.h> + +static void +test_ctype(void) +{ + int c; + + for (c = -1; c < 256; c++) { + /* Test blank. */ + if (c == '\t' || c == ' ') + test_pass(c_isblank(c)); + else + test_fail(c_isblank(c)); + + /* Test white. */ + if (c == '\t' || c == ' ' || c == '\n') + test_pass(c_iswhite(c)); + else + test_fail(c_iswhite(c)); + + /* Test space. */ + if (c == '\t' || c == '\v' || c == '\f' || + c == '\r' || c == '\n' || c == ' ') + test_pass(c_isspace(c)); + else + test_fail(c_isspace(c)); + + /* Test digit. */ + if (c >= '0' && c <= '9') + test_pass(c_isdigit(c)); + else + test_fail(c_isdigit(c)); + + /* Test lower case. */ + if (c >= 'a' && c <= 'z') + test_pass(c_islower(c)); + else + test_fail(c_islower(c)); + + /* Test upper case. */ + if (c >= 'A' && c <= 'Z') + test_pass(c_isupper(c)); + else + test_fail(c_isupper(c)); + + /* Test alpha. */ + if (c_islower(c) || c_isupper(c)) + test_pass(c_isalpha(c)); + else + test_fail(c_isalpha(c)); + + /* Test alphanumeric. */ + if (c_isdigit(c) || c_isalpha(c)) + test_pass(c_isalnum(c)); + else + test_fail(c_isalnum(c)); + } +} + +static void +test_casing(void) +{ + test_pass(c_tolower('A') == 'a'); + test_pass(c_tolower('Z') == 'z'); + + test_pass(c_tolower('a') == 'a'); + test_pass(c_tolower('z') == 'z'); + + test_pass(c_tolower('0') == '0'); + test_pass(c_tolower('9') == '9'); + + /* Test if we can handle the value for EOF. */ + test_pass(c_tolower(-1) == -1); +} + +TEST_ENTRY(test) +{ + test_plan(2063); + + test_ctype(); + test_casing(); +} diff --git a/lib/dpkg/t/t-command.c b/lib/dpkg/t/t-command.c new file mode 100644 index 0000000..3a2e64b --- /dev/null +++ b/lib/dpkg/t/t-command.c @@ -0,0 +1,311 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-command.c - test command implementation + * + * Copyright © 2010-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/test.h> +#include <dpkg/subproc.h> +#include <dpkg/command.h> +#include <dpkg/dpkg.h> + +static void +test_command_init(void) +{ + struct command cmd; + + command_init(&cmd, "/absolute/path/to/progname", NULL); + test_str(cmd.filename, ==, "/absolute/path/to/progname"); + test_str(cmd.name, ==, "progname"); + test_pass(cmd.argc == 0); + test_pass(cmd.argv[0] == NULL); + + command_destroy(&cmd); + test_pass(cmd.filename == NULL); + test_pass(cmd.name == NULL); + test_pass(cmd.argc == 0); + test_pass(cmd.argv == NULL); + + command_init(&cmd, "progname", NULL); + test_str(cmd.filename, ==, "progname"); + test_str(cmd.name, ==, "progname"); + test_pass(cmd.argc == 0); + test_pass(cmd.argv[0] == NULL); + + command_destroy(&cmd); + + command_init(&cmd, "progname", "description"); + test_str(cmd.filename, ==, "progname"); + test_str(cmd.name, ==, "description"); + test_pass(cmd.argc == 0); + test_pass(cmd.argv[0] == NULL); + + command_destroy(&cmd); +} + +static void +test_command_grow_argv(void) +{ + struct command cmd; + int argv_size, i; + + command_init(&cmd, "test", NULL); + + argv_size = cmd.argv_size + 4; + for (i = 0; i < argv_size; i++) + command_add_arg(&cmd, "arg"); + + test_pass(cmd.argc == argv_size); + test_pass(cmd.argv_size >= argv_size); + test_str(cmd.argv[0], ==, "arg"); + test_str(cmd.argv[argv_size - 1], ==, "arg"); + test_pass(cmd.argv[argv_size] == NULL); + + command_destroy(&cmd); +} + +static void +test_command_add_arg(void) +{ + struct command cmd; + + command_init(&cmd, "test", NULL); + + command_add_arg(&cmd, "arg 0"); + test_pass(cmd.argc == 1); + test_str(cmd.argv[0], ==, "arg 0"); + test_pass(cmd.argv[1] == NULL); + + command_add_arg(&cmd, "arg 1"); + test_pass(cmd.argc == 2); + test_str(cmd.argv[0], ==, "arg 0"); + test_str(cmd.argv[1], ==, "arg 1"); + test_pass(cmd.argv[2] == NULL); + + command_add_arg(&cmd, "arg 2"); + test_pass(cmd.argc == 3); + test_str(cmd.argv[0], ==, "arg 0"); + test_str(cmd.argv[1], ==, "arg 1"); + test_str(cmd.argv[2], ==, "arg 2"); + test_pass(cmd.argv[3] == NULL); + + command_destroy(&cmd); +} + +static void +test_command_add_argl(void) +{ + struct command cmd; + const char *args[] = { + "arg 1", + "arg 2", + "arg 3", + NULL, + }; + + command_init(&cmd, "test", NULL); + + command_add_arg(&cmd, "arg 0"); + + command_add_argl(&cmd, args); + test_pass(cmd.argc == 4); + test_str(cmd.argv[0], ==, "arg 0"); + test_str(cmd.argv[1], ==, "arg 1"); + test_str(cmd.argv[2], ==, "arg 2"); + test_str(cmd.argv[3], ==, "arg 3"); + test_pass(cmd.argv[4] == NULL); + + command_destroy(&cmd); +} + +static void +test_command_add_args(void) +{ + struct command cmd; + + command_init(&cmd, "test", NULL); + + command_add_arg(&cmd, "arg 0"); + + command_add_args(&cmd, "arg 1", "arg 2", "arg 3", NULL); + test_pass(cmd.argc == 4); + test_str(cmd.argv[0], ==, "arg 0"); + test_str(cmd.argv[1], ==, "arg 1"); + test_str(cmd.argv[2], ==, "arg 2"); + test_str(cmd.argv[3], ==, "arg 3"); + test_pass(cmd.argv[4] == NULL); + + command_destroy(&cmd); +} + +static void +test_command_exec(void) +{ + struct command cmd; + pid_t pid; + int ret; + + command_init(&cmd, "true", "exec test"); + + command_add_arg(&cmd, "true"); + command_add_arg(&cmd, "arg 0"); + command_add_arg(&cmd, "arg 1"); + + pid = subproc_fork(); + + if (pid == 0) + command_exec(&cmd); + + ret = subproc_reap(pid, "command exec test", 0); + test_pass(ret == 0); + + command_destroy(&cmd); +} + +static void +test_command_in_path(void) +{ + char *path_a, *path_b, *path_noexec; + char *oldpath, *newpath = NULL; + bool ret; + int rc; + + path_a = test_data_file("command/path-a"); + path_b = test_data_file("command/path-b"); + path_noexec = test_data_file("command/path-noexec"); + + oldpath = getenv("PATH"); + + setenv("PATH", "", 1); + ret = command_in_path("cmd"); + test_pass(ret == false); + ret = command_in_path("cmd-a"); + test_pass(ret == false); + ret = command_in_path("cmd-b"); + test_pass(ret == false); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + setenv("PATH", "/nonexistent", 1); + ret = command_in_path("cmd"); + test_pass(ret == false); + ret = command_in_path("cmd-a"); + test_pass(ret == false); + ret = command_in_path("cmd-b"); + test_pass(ret == false); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + setenv("PATH", path_a, 1); + ret = command_in_path("cmd"); + test_pass(ret == true); + ret = command_in_path("cmd-a"); + test_pass(ret == true); + ret = command_in_path("cmd-b"); + test_pass(ret == false); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + setenv("PATH", path_b, 1); + ret = command_in_path("cmd"); + test_pass(ret == true); + ret = command_in_path("cmd-a"); + test_pass(ret == false); + ret = command_in_path("cmd-b"); + test_pass(ret == true); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + setenv("PATH", path_noexec, 1); + ret = command_in_path("cmd"); + test_pass(ret == false); + ret = command_in_path("cmd-a"); + test_pass(ret == false); + ret = command_in_path("cmd-b"); + test_pass(ret == false); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + rc = asprintf(&newpath, "/nonexistent:%s:%s:%s", path_a, path_b, path_noexec); + if (rc < 0) + test_bail("cannot allocate new PATH variable"); + setenv("PATH", newpath, 1); + ret = command_in_path("cmd"); + test_pass(ret == true); + ret = command_in_path("cmd-a"); + test_pass(ret == true); + ret = command_in_path("cmd-b"); + test_pass(ret == true); + ret = command_in_path("cmd-noexec"); + test_pass(ret == false); + + setenv("PATH", oldpath, 1); + + free(path_a); + free(path_b); + free(path_noexec); + free(newpath); +} + +static void +test_command_shell(void) +{ + pid_t pid; + int ret; + + pid = subproc_fork(); + if (pid == 0) + command_shell("true", "command shell pass test"); + ret = subproc_reap(pid, "command shell pass test", 0); + test_pass(ret == 0); + + pid = subproc_fork(); + if (pid == 0) + command_shell("false", "command shell fail test"); + ret = subproc_reap(pid, "command shell fail test", SUBPROC_RETERROR); + test_fail(ret == 0); + + unsetenv("SHELL"); + pid = subproc_fork(); + if (pid == 0) + command_shell("true", "command default shell test"); + ret = subproc_reap(pid, "command default shell test", 0); + test_pass(ret == 0); +} + +TEST_ENTRY(test) +{ + test_plan(73); + + test_command_init(); + test_command_grow_argv(); + test_command_add_arg(); + test_command_add_argl(); + test_command_add_args(); + test_command_exec(); + test_command_in_path(); + test_command_shell(); +} diff --git a/lib/dpkg/t/t-deb-version.c b/lib/dpkg/t/t-deb-version.c new file mode 100644 index 0000000..38b4608 --- /dev/null +++ b/lib/dpkg/t/t-deb-version.c @@ -0,0 +1,90 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-deb-version.c - test deb version handling + * + * Copyright © 2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <limits.h> +#include <stdio.h> + +#include <dpkg/test.h> +#include <dpkg/deb-version.h> + +static void +test_deb_version_parse(void) +{ + struct deb_version v; + char *vs; + + /* Test valid versions. */ + test_pass(deb_version_parse(&v, "0.0") == NULL); + test_pass(v.major == 0 && v.minor == 0); + + test_pass(deb_version_parse(&v, "1.1") == NULL); + test_pass(v.major == 1 && v.minor == 1); + + test_pass(deb_version_parse(&v, "1.001") == NULL); + test_pass(v.major == 1 && v.minor == 1); + + test_pass(deb_version_parse(&v, "1.0010") == NULL); + test_pass(v.major == 1 && v.minor == 10); + + test_pass(deb_version_parse(&v, "0.939000") == NULL); + test_pass(v.major == 0 && v.minor == 939000); + + test_pass(deb_version_parse(&v, "1.1\n") == NULL); + test_pass(v.major == 1 && v.minor == 1); + + /* Test invalid versions. */ + test_fail(deb_version_parse(&v, "0") == NULL); + test_fail(deb_version_parse(&v, "a") == NULL); + test_fail(deb_version_parse(&v, "a.b") == NULL); + test_fail(deb_version_parse(&v, "a~b") == NULL); + test_fail(deb_version_parse(&v, " 1.1") == NULL); + test_fail(deb_version_parse(&v, "2 .2") == NULL); + test_fail(deb_version_parse(&v, "3. 3") == NULL); + test_fail(deb_version_parse(&v, "4.4 ") == NULL); + test_fail(deb_version_parse(&v, " 5.5 ") == NULL); + + /* Test integer limits. */ + if (asprintf(&vs, "%d.0", INT_MAX) < 0) + test_bail("cannot allocate memory for asprintf()"); + test_pass(deb_version_parse(&v, vs) == NULL); + free(vs); + + if (asprintf(&vs, "%d.0", INT_MAX - 1) < 0) + test_bail("cannot allocate memory for asprintf()"); + test_pass(deb_version_parse(&v, vs) == NULL); + free(vs); + + if (asprintf(&vs, "%u.0", 1U + (unsigned int)INT_MAX) < 0) + test_bail("cannot allocate memory for asprintf()"); + test_fail(deb_version_parse(&v, vs) == NULL); + free(vs); + + /* TODO: Complete. */ +} + +TEST_ENTRY(test) +{ + test_plan(24); + + test_deb_version_parse(); +} diff --git a/lib/dpkg/t/t-ehandle.c b/lib/dpkg/t/t-ehandle.c new file mode 100644 index 0000000..47ba58e --- /dev/null +++ b/lib/dpkg/t/t-ehandle.c @@ -0,0 +1,128 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-ehandle.c - test error handling implementation + * + * Copyright © 2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdbool.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dpkg/test.h> +#include <dpkg/ehandle.h> + +static jmp_buf global_handler_jump; + +static void +printer_empty(const char *msg, const void *data) +{ +} + +static void +handler_func(void) +{ + longjmp(global_handler_jump, 1); +} + +static void +test_error_handler_func(void) +{ + bool pass; + + if (setjmp(global_handler_jump)) { + pass = true; + pop_error_context(ehflag_normaltidy); + } else { + pass = false; + push_error_context_func(handler_func, printer_empty, "test func"); + ohshit("test func error"); + test_bail("ohshit() is not supposed to return"); + } + test_pass(pass); +} + +static void +test_error_handler_jump(void) +{ + jmp_buf handler_jump; + bool pass; + + if (setjmp(handler_jump)) { + pass = true; + pop_error_context(ehflag_normaltidy); + } else { + pass = false; + push_error_context_jump(&handler_jump, printer_empty, "test jump"); + ohshit("test jump error"); + test_bail("ohshit() is not supposed to return"); + } + test_pass(pass); +} + +static void +cleanup_error(int argc, void **argv) +{ + ohshit("cleanup error"); +} + +static void +test_cleanup_error(void) +{ + jmp_buf handler_jump; + bool pass; + + if (setjmp(handler_jump)) { + /* The ohshit() is not supposed to get us here, as it should + * be caught by the internal recursive error context. */ + pass = false; + + pop_cleanup(ehflag_normaltidy); + pop_error_context(ehflag_normaltidy); + } else { + push_error_context_jump(&handler_jump, printer_empty, "test cleanup"); + push_cleanup(cleanup_error, ~ehflag_normaltidy, 0); + pop_error_context(ehflag_bombout); + + /* We should have recovered from the cleanup handler failing, + * and arrived here correctly. */ + pass = true; + } + + test_pass(pass); +} + +TEST_ENTRY(test) +{ + test_plan(3); + + if (!test_is_verbose()) { + int fd; + + /* Shut up stderr, we do not want the error output. */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) + test_bail("cannot open /dev/null"); + dup2(fd, 2); + } + + test_error_handler_func(); + test_error_handler_jump(); + test_cleanup_error(); +} diff --git a/lib/dpkg/t/t-error.c b/lib/dpkg/t/t-error.c new file mode 100644 index 0000000..e93459e --- /dev/null +++ b/lib/dpkg/t/t-error.c @@ -0,0 +1,87 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-error.c - test error message reporting + * + * Copyright © 2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include <dpkg/test.h> +#include <dpkg/error.h> + +static void +test_dpkg_error_put(void) +{ + struct dpkg_error err = DPKG_ERROR_INIT; + char *errstr_ref = NULL; + + test_pass(err.type == DPKG_MSG_NONE); + test_pass(err.str == NULL); + + test_pass(dpkg_put_warn(NULL, "void error") < 0); + test_pass(dpkg_put_error(NULL, "void error") < 0); + test_pass(dpkg_put_errno(NULL, "void error") < 0); + + test_pass(dpkg_put_warn(&err, "test warning %d", 10) < 0); + test_pass(err.syserrno == 0); + test_str(err.str, ==, "test warning 10"); + test_warn(err); + + test_pass(dpkg_put_error(&err, "test error %d", 20) < 0); + test_pass(err.syserrno == 0); + test_str(err.str, ==, "test error 20"); + test_error(err); + + errno = ENOENT; + if (asprintf(&errstr_ref, "test errno 30 (%s)", strerror(errno)) < 0) + test_bail("cannot allocate string"); + test_pass(dpkg_put_errno(&err, "test errno %d", 30) < 0); + test_str(err.str, ==, errstr_ref); + test_pass(err.syserrno == ENOENT); + test_error(err); + free(errstr_ref); + errno = 0; +} + +static void +test_dpkg_error_destroy(void) +{ + struct dpkg_error err = DPKG_ERROR_INIT; + + errno = ENOENT; + test_pass(dpkg_put_errno(&err, "test destroy") < 0); + test_pass(err.syserrno == ENOENT); + test_pass(err.type == DPKG_MSG_ERROR); + test_pass(err.str != NULL); + dpkg_error_destroy(&err); + test_pass(err.type == DPKG_MSG_NONE); + test_pass(err.syserrno == 0); + test_pass(err.str == NULL); +} + +TEST_ENTRY(test) +{ + test_plan(24); + + test_dpkg_error_put(); + test_dpkg_error_destroy(); +} diff --git a/lib/dpkg/t/t-file.c b/lib/dpkg/t/t-file.c new file mode 100644 index 0000000..0004df4 --- /dev/null +++ b/lib/dpkg/t/t-file.c @@ -0,0 +1,106 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-file.c - test file functions + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/file.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +static const char ref_data[] = + "this is a test string\n" + "within a test file\n" + "containing multiple lines\n" +; + +static void +test_file_slurp(void) +{ + struct varbuf vb = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + char *test_file; + char *test_dir; + int fd; + + test_pass(file_slurp("/nonexistent", &vb, &err) < 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == ENOENT); + test_error(err); + varbuf_destroy(&vb); + + test_dir = test_alloc(strdup("test.XXXXXX")); + test_pass(mkdtemp(test_dir) != NULL); + test_pass(file_slurp(test_dir, &vb, &err) < 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == 0); + test_error(err); + varbuf_destroy(&vb); + test_pass(rmdir(test_dir) == 0); + + test_file = test_alloc(strdup("test.XXXXXX")); + fd = mkstemp(test_file); + test_pass(fd >= 0); + + test_pass(file_slurp(test_file, &vb, &err) == 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == 0); + test_pass(err.type == DPKG_MSG_NONE); + varbuf_destroy(&vb); + + test_pass(write(fd, ref_data, strlen(ref_data)) == (ssize_t)strlen(ref_data)); + test_pass(lseek(fd, 0, SEEK_SET) == 0); + + test_pass(file_slurp(test_file, &vb, &err) == 0); + test_pass(vb.used == strlen(ref_data)); + test_mem(vb.buf, ==, ref_data, min(vb.used, strlen(ref_data))); + test_pass(err.syserrno == 0); + test_pass(err.type == DPKG_MSG_NONE); + varbuf_destroy(&vb); + + test_fail(file_is_exec(test_dir)); + test_fail(file_is_exec(test_file)); + test_pass(chmod(test_file, 0755) == 0); + test_pass(file_is_exec(test_file)); + test_pass(chmod(test_file, 0750) == 0); + test_pass(file_is_exec(test_file)); + + test_pass(unlink(test_file) == 0); + free(test_file); + free(test_dir); +} + +TEST_ENTRY(test) +{ + test_plan(32); + + test_file_slurp(); +} diff --git a/lib/dpkg/t/t-fsys-dir.c b/lib/dpkg/t/t-fsys-dir.c new file mode 100644 index 0000000..32f4aab --- /dev/null +++ b/lib/dpkg/t/t-fsys-dir.c @@ -0,0 +1,91 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-fsys-dir.c - test filesystem handling + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/fsys.h> + +static void +test_fsys_dir(void) +{ + const char *newdir; + char *dir; + + test_str(dpkg_fsys_get_dir(), ==, ""); + + newdir = dpkg_fsys_set_dir("/testdir//./"); + test_str(newdir, ==, "/testdir"); + test_str(dpkg_fsys_get_dir(), ==, "/testdir"); + + newdir = dpkg_fsys_set_dir("/testdir"); + test_str(newdir, ==, "/testdir"); + test_str(dpkg_fsys_get_dir(), ==, "/testdir"); + + newdir = dpkg_fsys_set_dir(newdir); + test_str(newdir, ==, "/testdir"); + test_str(dpkg_fsys_get_dir(), ==, "/testdir"); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testdir/testfile"); + free(dir); + + dir = dpkg_fsys_get_path("/testfile"); + test_str(dir, ==, "/testdir/testfile"); + free(dir); + + setenv("DPKG_ROOT", "/testenvdir//./", 1); + dpkg_fsys_set_dir(NULL); + test_str(dpkg_fsys_get_dir(), ==, "/testenvdir"); + + setenv("DPKG_ROOT", "/testenvdir", 1); + dpkg_fsys_set_dir(NULL); + test_str(dpkg_fsys_get_dir(), ==, "/testenvdir"); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testenvdir/testfile"); + free(dir); + + dir = dpkg_fsys_get_path("/testfile"); + test_str(dir, ==, "/testenvdir/testfile"); + free(dir); + + unsetenv("DPKG_ROOT"); + dpkg_fsys_set_dir(NULL); + test_str(dpkg_fsys_get_dir(), ==, ""); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testfile"); + free(dir); + + dir = dpkg_fsys_get_path("/testfile"); + test_str(dir, ==, "/testfile"); + free(dir); +} + +TEST_ENTRY(test) +{ + test_plan(16); + + test_fsys_dir(); +} diff --git a/lib/dpkg/t/t-fsys-hash.c b/lib/dpkg/t/t-fsys-hash.c new file mode 100644 index 0000000..4318779 --- /dev/null +++ b/lib/dpkg/t/t-fsys-hash.c @@ -0,0 +1,108 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-fsys-hash.c - test fsys-hash implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/fsys.h> + +static void +test_fsys_nodes(void) +{ + struct fsys_namenode *fnn; + struct fsys_hash_iter *iter; + const char *name; + + test_pass(fsys_hash_entries() == 0); + + fsys_hash_init(); + + fnn = fsys_hash_find_node("/nonexistent", FHFF_NO_NEW); + test_pass(fnn == NULL); + test_pass(fsys_hash_entries() == 0); + + name = "/test/path/aa"; + fnn = fsys_hash_find_node(name, FHFF_NO_COPY); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 1); + test_pass(fnn->name == name); + test_str(fnn->name, ==, "/test/path/aa"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_pass(fnn->newhash == NULL); + + fnn = fsys_hash_find_node("//./test/path/bb", FHFF_NONE); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 2); + test_str(fnn->name, ==, "/test/path/bb"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_pass(fnn->newhash == NULL); + + fnn = fsys_hash_find_node("/test/path/cc", FHFF_NONE); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 3); + test_str(fnn->name, ==, "/test/path/cc"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_pass(fnn->newhash == NULL); + + iter = fsys_hash_iter_new(); + while ((fnn = fsys_hash_iter_next(iter))) { + if (strcmp(fnn->name, "/test/path/aa") == 0) + test_str(fnn->name, ==, "/test/path/aa"); + else if (strcmp(fnn->name, "/test/path/bb") == 0) + test_str(fnn->name, ==, "/test/path/bb"); + else if (strcmp(fnn->name, "/test/path/cc") == 0) + test_str(fnn->name, ==, "/test/path/cc"); + else + test_fail("unknown fsys_namenode"); + } + fsys_hash_iter_free(iter); + + fsys_hash_init(); + test_pass(fsys_hash_entries() == 3); + fnn = fsys_hash_find_node("/test/path/aa", FHFF_NO_NEW); + test_pass(fnn != NULL); + fnn = fsys_hash_find_node("/test/path/bb", FHFF_NO_NEW); + test_pass(fnn != NULL); + fnn = fsys_hash_find_node("/test/path/cc", FHFF_NO_NEW); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 3); + + fsys_hash_reset(); + test_pass(fsys_hash_entries() == 0); + fnn = fsys_hash_find_node("/test/path/aa", FHFF_NO_NEW); + test_pass(fnn == NULL); + fnn = fsys_hash_find_node("/test/path/bb", FHFF_NO_NEW); + test_pass(fnn == NULL); + fnn = fsys_hash_find_node("/test/path/cc", FHFF_NO_NEW); + test_pass(fnn == NULL); + test_pass(fsys_hash_entries() == 0); +} + +TEST_ENTRY(test) +{ + test_plan(35); + + test_fsys_nodes(); +} diff --git a/lib/dpkg/t/t-headers-cpp.cc b/lib/dpkg/t/t-headers-cpp.cc new file mode 100644 index 0000000..b5becf9 --- /dev/null +++ b/lib/dpkg/t/t-headers-cpp.cc @@ -0,0 +1,82 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-headers-cpp.cc - test C++ inclusion of headers + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <cstdbool> + +#include <dpkg/ar.h> +#include <dpkg/arch.h> +#include <dpkg/atomic-file.h> +#include <dpkg/buffer.h> +#include <dpkg/c-ctype.h> +#include <dpkg/color.h> +#include <dpkg/command.h> +#include <dpkg/compress.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> +#include <dpkg/deb-version.h> +#include <dpkg/debug.h> +#include <dpkg/dir.h> +#include <dpkg/dlist.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/dpkg.h> +#include <dpkg/ehandle.h> +#include <dpkg/error.h> +#include <dpkg/fdio.h> +#include <dpkg/file.h> +#include <dpkg/fsys.h> +#include <dpkg/glob.h> +#include <dpkg/i18n.h> +#include <dpkg/macros.h> +#include <dpkg/namevalue.h> +#include <dpkg/options.h> +#include <dpkg/pager.h> +#include <dpkg/parsedump.h> +#include <dpkg/path.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-files.h> +#include <dpkg/pkg-format.h> +#include <dpkg/pkg-list.h> +#include <dpkg/pkg-queue.h> +#include <dpkg/pkg-show.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/pkg.h> +#include <dpkg/progname.h> +#include <dpkg/program.h> +#include <dpkg/progress.h> +#include <dpkg/report.h> +#include <dpkg/string.h> +#include <dpkg/subproc.h> +#include <dpkg/tarfn.h> +#include <dpkg/test.h> +#include <dpkg/treewalk.h> +#include <dpkg/trigdeferred.h> +#include <dpkg/triglib.h> +#include <dpkg/varbuf.h> +#include <dpkg/version.h> + +TEST_ENTRY(test) +{ + test_plan(1); + + test_pass(true); +} diff --git a/lib/dpkg/t/t-macros.c b/lib/dpkg/t/t-macros.c new file mode 100644 index 0000000..9f4eaa1 --- /dev/null +++ b/lib/dpkg/t/t-macros.c @@ -0,0 +1,45 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-macros.c - test C support macros + * + * Copyright © 2009,2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/macros.h> + +TEST_ENTRY(test) +{ + test_plan(12); + + test_pass(min(10, 30) == 10); + test_pass(min(30, 10) == 10); + test_pass(min(0, 10) == 0); + test_pass(min(-10, 0) == -10); + + test_pass(max(10, 30) == 30); + test_pass(max(30, 10) == 30); + test_pass(max(0, 10) == 10); + test_pass(max(-10, 0) == 0); + + test_pass(clamp(0, 0, 0) == 0); + test_pass(clamp(0, -10, 10) == 0); + test_pass(clamp(20, -10, 10) == 10); + test_pass(clamp(-20, -10, 10) == -10); +} diff --git a/lib/dpkg/t/t-meminfo.c b/lib/dpkg/t/t-meminfo.c new file mode 100644 index 0000000..0c3e635 --- /dev/null +++ b/lib/dpkg/t/t-meminfo.c @@ -0,0 +1,75 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-meminfo.c - test memory information handling code + * + * Copyright © 2022 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/meminfo.h> + +static void +test_meminfo(void) +{ + char *pathname; + uint64_t mem; + int rc; + + mem = 0; + pathname = test_data_file("meminfo-no-file"); + rc = meminfo_get_available_from_file(pathname, &mem); + test_pass(rc == MEMINFO_NO_FILE); + test_pass(mem == 0); + free(pathname); + + mem = 0; + pathname = test_data_file("meminfo-no-data"); + rc = meminfo_get_available_from_file(pathname, &mem); + test_pass(rc == MEMINFO_NO_DATA); + test_pass(mem == 0); + free(pathname); + + mem = 0; + pathname = test_data_file("meminfo-no-unit"); + rc = meminfo_get_available_from_file(pathname, &mem); + test_pass(rc == MEMINFO_NO_UNIT); + test_pass(mem == 0); + free(pathname); + + mem = 0; + pathname = test_data_file("meminfo-no-info"); + rc = meminfo_get_available_from_file(pathname, &mem); + test_pass(rc == MEMINFO_NO_INFO); + test_pass(mem == 0); + free(pathname); + + mem = 0; + pathname = test_data_file("meminfo-ok"); + rc = meminfo_get_available_from_file(pathname, &mem); + test_pass(rc == MEMINFO_OK); + test_pass(mem == 3919974400UL); + free(pathname); +} + +TEST_ENTRY(test) +{ + test_plan(10); + + test_meminfo(); +} diff --git a/lib/dpkg/t/t-mod-db.c b/lib/dpkg/t/t-mod-db.c new file mode 100644 index 0000000..c40d8e8 --- /dev/null +++ b/lib/dpkg/t/t-mod-db.c @@ -0,0 +1,57 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-mod-db.c - test database implementation + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> + +static void +test_db_dir(void) +{ + char *dir; + + test_str(dpkg_db_get_dir(), ==, ADMINDIR); + + dpkg_db_set_dir("testdir"); + test_str(dpkg_db_get_dir(), ==, "testdir"); + + setenv("DPKG_ADMINDIR", "testenvdir", 1); + dpkg_db_set_dir(NULL); + test_str(dpkg_db_get_dir(), ==, "testenvdir"); + + unsetenv("DPKG_ADMINDIR"); + dpkg_db_set_dir(NULL); + test_str(dpkg_db_get_dir(), ==, ADMINDIR); + + dir = dpkg_db_get_path("testfile"); + test_str(dir, ==, ADMINDIR "/testfile"); + free(dir); +} + +TEST_ENTRY(test) +{ + test_plan(5); + + test_db_dir(); +} diff --git a/lib/dpkg/t/t-namevalue.c b/lib/dpkg/t/t-namevalue.c new file mode 100644 index 0000000..c864766 --- /dev/null +++ b/lib/dpkg/t/t-namevalue.c @@ -0,0 +1,48 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-namevalue.c - test name/value implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/namevalue.h> +#include <dpkg/dpkg-db.h> + +static void +test_namevalue(void) +{ + const struct namevalue *nv; + + nv = namevalue_find_by_name(booleaninfos, ""); + test_pass(nv == NULL); + + nv = namevalue_find_by_name(booleaninfos, "no"); + test_pass(nv != NULL); + test_pass(nv->value == false); + test_pass(nv->length == strlen("no")); + test_str(nv->name, ==, "no"); +} + +TEST_ENTRY(test) +{ + test_plan(5); + + test_namevalue(); +} diff --git a/lib/dpkg/t/t-pager.c b/lib/dpkg/t/t-pager.c new file mode 100644 index 0000000..d007dde --- /dev/null +++ b/lib/dpkg/t/t-pager.c @@ -0,0 +1,77 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pager.c - test pager implementation + * + * Copyright © 2010-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/test.h> +#include <dpkg/pager.h> +#include <dpkg/dpkg.h> + +static void +test_dup_file(int fd, const char *filename, int flags) +{ + int newfd; + + newfd = open(filename, flags); + dup2(newfd, fd); + close(newfd); +} + +static void +test_pager_get_exec(void) +{ + const char *pager; + int origfd = dup(STDOUT_FILENO); + + /* Test stdout being a tty. */ + test_todo_block("environment might not expose controlling terminal") { + const char *default_pager; + + test_dup_file(STDOUT_FILENO, "/dev/tty", O_WRONLY); + setenv("PAGER", "test-pager", 1); + pager = pager_get_exec(); + unsetenv("PAGER"); + default_pager = pager_get_exec(); + dup2(origfd, STDOUT_FILENO); + test_str(pager, ==, "test-pager"); + test_str(default_pager, ==, DPKG_DEFAULT_PAGER); + } + + /* Test stdout not being a tty. */ + test_dup_file(STDOUT_FILENO, "/dev/null", O_WRONLY); + pager = pager_get_exec(); + dup2(origfd, STDOUT_FILENO); + test_str(pager, ==, CAT); +} + +TEST_ENTRY(test) +{ + test_plan(3); + + test_pager_get_exec(); +} diff --git a/lib/dpkg/t/t-path.c b/lib/dpkg/t/t-path.c new file mode 100644 index 0000000..deb1b72 --- /dev/null +++ b/lib/dpkg/t/t-path.c @@ -0,0 +1,181 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-path.c - test path handling code + * + * Copyright © 2009-2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/path.h> + +/* Use the test_trim_eq_ref macro to avoid leaking the string and to get + * meaningful line numbers from assert. */ +#define test_trim_eq_ref(p, ref) \ +do { \ + char *t = test_alloc(strdup((p))); \ + path_trim_slash_slashdot(t); \ + test_str(t, ==, (ref)); \ + free(t); \ +} while (0) + +static void +test_path_trim(void) +{ + test_trim_eq_ref("/a", "/a"); + test_trim_eq_ref("./././.", "."); + test_trim_eq_ref("./././", "."); + test_trim_eq_ref("./.", "."); + test_trim_eq_ref("./", "."); + test_trim_eq_ref("/./././.", "/"); + test_trim_eq_ref("/./", "/"); + test_trim_eq_ref("/.", "/"); + test_trim_eq_ref("/", "/"); + test_trim_eq_ref("", ""); + test_trim_eq_ref("/./../.", "/./.."); + test_trim_eq_ref("/foo/bar/./", "/foo/bar"); + test_trim_eq_ref("./foo/bar/./", "./foo/bar"); + test_trim_eq_ref("/./foo/bar/./", "/./foo/bar"); +} + +static void +test_path_skip(void) +{ + test_str(path_skip_slash_dotslash("./././."), ==, "."); + test_str(path_skip_slash_dotslash("./././"), ==, ""); + test_str(path_skip_slash_dotslash("./."), ==, "."); + test_str(path_skip_slash_dotslash("./"), ==, ""); + test_str(path_skip_slash_dotslash("/./././."), ==, "."); + test_str(path_skip_slash_dotslash("/./"), ==, ""); + test_str(path_skip_slash_dotslash("/."), ==, "."); + test_str(path_skip_slash_dotslash("/"), ==, ""); + test_str(path_skip_slash_dotslash("/./../."), ==, "../."); + test_str(path_skip_slash_dotslash("/foo/bar/./"), ==, "foo/bar/./"); + test_str(path_skip_slash_dotslash("./foo/bar/./"), ==, "foo/bar/./"); + test_str(path_skip_slash_dotslash("/./foo/bar/./"), ==, "foo/bar/./"); +} + +static void +test_path_basename(void) +{ + test_str(path_basename("./."), ==, "."); + test_str(path_basename("./"), ==, ""); + test_str(path_basename("/."), ==, "."); + test_str(path_basename("/"), ==, ""); + test_str(path_basename("/foo"), ==, "foo"); + test_str(path_basename("/foo/bar"), ==, "bar"); + test_str(path_basename("/foo/bar/"), ==, ""); +} + +static void +test_path_temp(void) +{ + char *template; + + template = path_make_temp_template("test"); + + test_pass(strstr(template, "test") != NULL); + test_pass(strstr(template, "XXXXXX") != NULL); + + free(template); +} + +static bool +string_is_ascii(const char *str) +{ + while (*str) { + if (!isascii(*str)) + return false; + + str++; + } + + return true; +} + +static void +test_path_quote(void) +{ + const char src_7_bit[] = "string with 7-bit chars only"; + const char src_7_bit_trim[] = "string with 7-bit chars"; + const char src_8_bit[] = "text w/ 8-bit chars: \\ \370 \300 \342 end"; + const char src_8_bit_end[] = "text \370"; + const char src_bs_end[] = "text \\"; + char *dst; + size_t len; + + /* Test 0 length. */ + dst = NULL; + path_quote_filename(dst, src_7_bit, 0); + + /* Test no quoting. */ + len = strlen(src_7_bit) + 1; + dst = test_alloc(malloc(len)); + + path_quote_filename(dst, src_7_bit, len); + test_str(dst, ==, src_7_bit); + free(dst); + + /* Test no quoting with limit. */ + len = strlen(src_7_bit_trim) + 1; + dst = test_alloc(malloc(len)); + + path_quote_filename(dst, src_7_bit, len); + test_str(dst, ==, src_7_bit_trim); + free(dst); + + /* Test normal quoting. */ + len = strlen(src_8_bit) * 2 + 1; + dst = test_alloc(malloc(len)); + + path_quote_filename(dst, src_8_bit, len); + test_pass(strstr(dst, "end") != NULL); + test_pass(string_is_ascii(dst)); + free(dst); + + /* Test normal quoting with limit. */ + len = strlen(src_8_bit_end) + 1 + 2; + dst = test_alloc(malloc(len)); + + path_quote_filename(dst, src_8_bit_end, len); + test_str(dst, ==, "text "); + free(dst); + + /* Test backslash quoting with limit. */ + len = strlen(src_bs_end) + 1; + dst = test_alloc(malloc(len)); + + path_quote_filename(dst, src_bs_end, len); + test_str(dst, ==, "text "); + free(dst); +} + +TEST_ENTRY(test) +{ + test_plan(41); + + test_path_trim(); + test_path_skip(); + test_path_basename(); + test_path_temp(); + test_path_quote(); +} diff --git a/lib/dpkg/t/t-pkg-format.c b/lib/dpkg/t/t-pkg-format.c new file mode 100644 index 0000000..81e7ddb --- /dev/null +++ b/lib/dpkg/t/t-pkg-format.c @@ -0,0 +1,142 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-format.c - test pkg-format implementation + * + * Copyright © 2022 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> +#include <dpkg/pkg-format.h> + +static void +prep_pkg(struct pkgset *set, struct pkginfo *pkg) +{ + pkg_blank(pkg); + + pkgset_blank(set); + pkgset_link_pkg(set, pkg); + + set->name = "test-bin"; + pkg->installed.description = "short synopsis -- some package\n" + " This is the extended description for this package-\n" + " .\n" + " Potentially expanding multiple lines.\n"; + pkg->installed.arch = dpkg_arch_get(DPKG_ARCH_ALL); + pkg->installed.version = DPKG_VERSION_OBJECT(0, "4.5", "2"); +} + +static void +prep_virtpkg(struct pkgset *set, struct pkginfo *pkg) +{ + pkg_blank(pkg); + + pkgset_blank(set); + pkgset_link_pkg(set, pkg); + + set->name = "test-virt"; +} + +static void +test_field(struct pkginfo *pkg, const char *fmt, const char *exp) +{ + struct pkg_format_node *head; + struct varbuf vb = VARBUF_INIT; + + head = pkg_format_parse(fmt, NULL); + test_pass(head); + pkg_format_print(&vb, head, pkg, &pkg->installed); + test_str(vb.buf, ==, exp); + pkg_format_free(head); + varbuf_destroy(&vb); +} + +static void +test_pkg_format_real_fields(void) +{ + struct pkgset pkgset; + struct pkginfo pkg; + + prep_pkg(&pkgset, &pkg); + + test_field(&pkg, "${Package}_${Version}_${Architecture}", + "test-bin_4.5-2_all"); +} + +static void +test_pkg_format_virtual_fields(void) +{ + struct pkgset pkgset; + struct pkginfo pkg; + + prep_pkg(&pkgset, &pkg); + + test_field(&pkg, "${source:Package}_${source:Version}", + "test-bin_4.5-2"); + + pkg.installed.source = "test-src"; + test_field(&pkg, "${source:Package}_${source:Version}", + "test-src_4.5-2"); + test_field(&pkg, "${source:Upstream-Version}", + "4.5"); + + pkg.installed.source = "test-src (1:3.4-6)"; + test_field(&pkg, "${source:Package}_${source:Version}", + "test-src_1:3.4-6"); + test_field(&pkg, "${source:Upstream-Version}", + "3.4"); + + test_field(&pkg, "${binary:Synopsis}", + "short synopsis -- some package"); + + test_field(&pkg, "${binary:Summary}", + "short synopsis -- some package"); + + prep_virtpkg(&pkgset, &pkg); + test_field(&pkg, "${source:Package}_${source:Version}", + "test-virt_"); + test_field(&pkg, "${source:Upstream-Version}", + ""); +} + +static void +test_pkg_format_virtual_fields_db_fsys(void) +{ + struct pkg_format_node *head; + + head = pkg_format_parse("prefix ${unknown-variable} suffix", NULL); + test_pass(head); + test_fail(pkg_format_needs_db_fsys(head)); + pkg_format_free(head); + + head = pkg_format_parse("prefix ${db-fsys:Files} suffix", NULL); + test_pass(head); + test_pass(pkg_format_needs_db_fsys(head)); + pkg_format_free(head); +} + +TEST_ENTRY(test) +{ + test_plan(24); + + test_pkg_format_real_fields(); + test_pkg_format_virtual_fields(); + test_pkg_format_virtual_fields_db_fsys(); +} diff --git a/lib/dpkg/t/t-pkg-hash.c b/lib/dpkg/t/t-pkg-hash.c new file mode 100644 index 0000000..14935c2 --- /dev/null +++ b/lib/dpkg/t/t-pkg-hash.c @@ -0,0 +1,179 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-hash.c - test pkg-hash implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> + +static void +test_pkg_hash(void) +{ + struct dpkg_arch *arch; + struct pkgset *set; + struct pkginfo *pkg; + struct pkg_hash_iter *iter; + int pkginstance; + + test_pass(pkg_hash_count_set() == 0); + test_pass(pkg_hash_count_pkg() == 0); + + set = pkg_hash_find_set("pkg-aa"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("pkg-aa"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("Pkg-AA"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("pkg-bb"); + test_pass(set != NULL); + pkg_set_status(&set->pkg, PKG_STAT_INSTALLED); + test_str(set->name, ==, "pkg-bb"); + test_pass(pkg_hash_count_set() == 2); + test_pass(pkg_hash_count_pkg() == 2); + + set = pkg_hash_find_set("pkg-cc"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-cc"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 3); + + arch = dpkg_arch_find("arch-xx"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + pkg_set_status(pkg, PKG_STAT_INSTALLED); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-xx"); + test_str(pkg->available.arch->name, ==, "arch-xx"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 3); + + arch = dpkg_arch_find("arch-yy"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-yy"); + test_str(pkg->available.arch->name, ==, "arch-yy"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 4); + + arch = dpkg_arch_find("arch-zz"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + pkg_set_status(pkg, PKG_STAT_UNPACKED); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-zz"); + test_str(pkg->available.arch->name, ==, "arch-zz"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + arch = dpkg_arch_find("arch-xx"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-xx"); + test_str(pkg->available.arch->name, ==, "arch-xx"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + set = pkg_hash_find_set("pkg-aa"); + test_str(set->name, ==, "pkg-aa"); + pkg = pkg_hash_get_singleton(set); + test_pass(pkg == NULL); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + pkg = pkg_hash_find_singleton("pkg-bb"); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-bb"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + pkg = pkg_hash_find_singleton("pkg-cc"); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-cc"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + iter = pkg_hash_iter_new(); + while ((set = pkg_hash_iter_next_set(iter))) { + if (strcmp(set->name, "pkg-aa") == 0) + test_str(set->name, ==, "pkg-aa"); + else if (strcmp(set->name, "pkg-bb") == 0) + test_str(set->name, ==, "pkg-bb"); + else if (strcmp(set->name, "pkg-cc") == 0) + test_str(set->name, ==, "pkg-cc"); + else + test_fail("unknown fsys_namenode"); + } + pkg_hash_iter_free(iter); + + pkginstance = 0; + iter = pkg_hash_iter_new(); + while ((pkg = pkg_hash_iter_next_pkg(iter))) { + pkginstance++; + if (strcmp(pkg->set->name, "pkg-aa") == 0) { + struct pkgbin *pkgbin = &pkg->installed; + + test_str(pkg->set->name, ==, "pkg-aa"); + if (strcmp(pkgbin->arch->name, "arch-xx") == 0) + test_str(pkgbin->arch->name, ==, "arch-xx"); + else if (strcmp(pkgbin->arch->name, "arch-yy") == 0) + test_str(pkgbin->arch->name, ==, "arch-yy"); + else if (strcmp(pkgbin->arch->name, "arch-zz") == 0) + test_str(pkgbin->arch->name, ==, "arch-zz"); + else + test_fail("unknown pkginfo instance"); + } else if (strcmp(pkg->set->name, "pkg-bb") == 0) { + test_str(pkg->set->name, ==, "pkg-bb"); + } else if (strcmp(pkg->set->name, "pkg-cc") == 0) { + test_str(pkg->set->name, ==, "pkg-cc"); + } else { + test_fail("unknown fsys_namenode"); + } + } + pkg_hash_iter_free(iter); + test_pass(pkginstance == 5); + + pkg_hash_reset(); + test_pass(pkg_hash_count_set() == 0); + test_pass(pkg_hash_count_pkg() == 0); +} + +TEST_ENTRY(test) +{ + test_plan(72); + + test_pkg_hash(); +} diff --git a/lib/dpkg/t/t-pkg-list.c b/lib/dpkg/t/t-pkg-list.c new file mode 100644 index 0000000..722cf2e --- /dev/null +++ b/lib/dpkg/t/t-pkg-list.c @@ -0,0 +1,90 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-list.c - test pkg-list implementation + * + * Copyright © 2010,2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> + +static void +test_pkg_list_new(void) +{ + struct pkg_list *l1, *l2, *l3; + struct pkginfo pkg1, pkg2, pkg3; + + l1 = pkg_list_new(&pkg1, NULL); + test_alloc(l1); + test_pass(l1->next == NULL); + test_pass(l1->pkg == &pkg1); + + l2 = pkg_list_new(&pkg2, l1); + test_alloc(l2); + test_pass(l2->next == l1); + test_pass(l2->pkg == &pkg2); + + l3 = pkg_list_new(&pkg3, l2); + test_alloc(l3); + test_pass(l3->next == l2); + test_pass(l3->pkg == &pkg3); + + pkg_list_free(l3); +} + +static void +test_pkg_list_prepend(void) +{ + struct pkg_list *head = NULL, *l1, *l2, *l3; + struct pkginfo pkg1, pkg2, pkg3, pkg4; + + pkg_list_prepend(&head, &pkg1); + test_alloc(head); + test_pass(head->next == NULL); + test_pass(head->pkg == &pkg1); + l1 = head; + + pkg_list_prepend(&head, &pkg2); + test_alloc(head); + test_pass(head->next == l1); + test_pass(head->pkg == &pkg2); + l2 = head; + + pkg_list_prepend(&head, &pkg3); + test_alloc(head); + test_pass(head->next == l2); + test_pass(head->pkg == &pkg3); + l3 = head; + + pkg_list_prepend(&head, &pkg4); + test_alloc(head); + test_pass(head->next == l3); + test_pass(head->pkg == &pkg4); + + pkg_list_free(head); +} + +TEST_ENTRY(test) +{ + test_plan(14); + + test_pkg_list_new(); + test_pkg_list_prepend(); +} diff --git a/lib/dpkg/t/t-pkg-queue.c b/lib/dpkg/t/t-pkg-queue.c new file mode 100644 index 0000000..cf1e327 --- /dev/null +++ b/lib/dpkg/t/t-pkg-queue.c @@ -0,0 +1,116 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-queue.c - test pkg-queue implementation + * + * Copyright © 2010,2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-queue.h> + +static void +test_pkg_queue_init(void) +{ + struct pkg_queue q = PKG_QUEUE_INIT; + struct pkg_list l; + + test_pass(q.length == 0); + test_pass(q.head == NULL); + test_pass(q.tail == NULL); + + test_pass(pkg_queue_is_empty(&q)); + + q = (struct pkg_queue){ .length = 10, .head = &l, .tail = &l }; + + pkg_queue_init(&q); + test_pass(q.length == 0); + test_pass(q.head == NULL); + test_pass(q.tail == NULL); + + test_pass(pkg_queue_is_empty(&q)); +} + +static void +test_pkg_queue_push_pop(void) +{ + struct pkg_queue q = PKG_QUEUE_INIT; + struct pkg_list *l1, *l2, *l3; + struct pkginfo *pkgp, pkg1, pkg2, pkg3; + + test_pass(pkg_queue_is_empty(&q)); + + /* Test push operations. */ + + l1 = pkg_queue_push(&q, &pkg1); + test_pass(l1 != NULL); + test_pass(q.head == l1); + test_pass(q.tail == l1); + test_pass(q.length == 1); + + l2 = pkg_queue_push(&q, &pkg2); + test_pass(l2 != NULL); + test_pass(q.head == l1); + test_pass(q.tail == l2); + test_pass(q.length == 2); + + l3 = pkg_queue_push(&q, &pkg3); + test_pass(l3 != NULL); + test_pass(q.head == l1); + test_pass(q.tail == l3); + test_pass(q.length == 3); + + /* Test pop operations. */ + + pkgp = pkg_queue_pop(&q); + test_pass(pkgp == &pkg1); + test_pass(q.head == l2); + test_pass(q.tail == l3); + test_pass(q.length == 2); + + pkgp = pkg_queue_pop(&q); + test_pass(pkgp == &pkg2); + test_pass(q.head == l3); + test_pass(q.tail == l3); + test_pass(q.length == 1); + + pkgp = pkg_queue_pop(&q); + test_pass(pkgp == &pkg3); + test_pass(q.head == NULL); + test_pass(q.tail == NULL); + test_pass(q.length == 0); + + test_pass(pkg_queue_is_empty(&q)); + + pkgp = pkg_queue_pop(&q); + test_pass(pkgp == NULL); + test_pass(q.head == NULL); + test_pass(q.tail == NULL); + test_pass(q.length == 0); + + pkg_queue_destroy(&q); +} + +TEST_ENTRY(test) +{ + test_plan(38); + + test_pkg_queue_init(); + test_pkg_queue_push_pop(); +} diff --git a/lib/dpkg/t/t-pkg-show.c b/lib/dpkg/t/t-pkg-show.c new file mode 100644 index 0000000..6bb361d --- /dev/null +++ b/lib/dpkg/t/t-pkg-show.c @@ -0,0 +1,70 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-show.c - test pkg-show implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> + +static void +test_pkg_show_name(void) +{ + struct dpkg_arch *arch; + struct pkginfo *pkg; + const char *pkgname; + + arch = dpkg_arch_find("arch"); + test_pass(arch); + + pkg = pkg_hash_find_pkg("test", arch); + test_pass(pkg); + test_str(pkg->set->name, ==, "test"); + test_pass(pkg->installed.arch->type == DPKG_ARCH_UNKNOWN); + + pkgname = pkg_name(pkg, pnaw_never); + test_pass(pkgname); + test_str(pkgname, ==, "test"); + + pkgname = pkg_name(pkg, pnaw_nonambig); + test_pass(pkgname); + test_str(pkgname, ==, "test:arch"); + + pkgname = pkg_name(pkg, pnaw_always); + test_pass(pkgname); + test_str(pkgname, ==, "test:arch"); + + pkgname = pkg_name(pkg, pnaw_same); + test_pass(pkgname); + test_str(pkgname, ==, "test"); + + pkg->installed.multiarch = PKG_MULTIARCH_SAME; + pkgname = pkg_name(pkg, pnaw_same); + test_pass(pkgname); + test_str(pkgname, ==, "test:arch"); +} + +TEST_ENTRY(test) +{ + test_plan(14); + + test_pkg_show_name(); +} diff --git a/lib/dpkg/t/t-pkginfo.c b/lib/dpkg/t/t-pkginfo.c new file mode 100644 index 0000000..b974027 --- /dev/null +++ b/lib/dpkg/t/t-pkginfo.c @@ -0,0 +1,155 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkginfo.c - test pkginfo handling + * + * Copyright © 2009-2010,2012-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> + +static void +test_pkginfo_informative(void) +{ + struct pkginfo pkg; + + pkg_blank(&pkg); + test_fail(pkg_is_informative(&pkg, &pkg.installed)); + + pkg_set_want(&pkg, PKG_WANT_PURGE); + test_pass(pkg_is_informative(&pkg, &pkg.installed)); + + pkg_blank(&pkg); + pkg.installed.description = "test description"; + test_pass(pkg_is_informative(&pkg, &pkg.installed)); + + /* TODO: Complete. */ +} + +static void +test_pkginfo_eflags(void) +{ + struct pkginfo pkg; + + pkg_blank(&pkg); + test_pass(pkg.eflag == PKG_EFLAG_OK); + + pkg_set_eflags(&pkg, PKG_EFLAG_REINSTREQ); + test_pass(pkg.eflag == PKG_EFLAG_REINSTREQ); + + pkg_clear_eflags(&pkg, PKG_EFLAG_REINSTREQ); + test_pass(pkg.eflag == PKG_EFLAG_OK); + + DPKG_IGNORE_WARNING_ASSIGN_ENUM(); + pkg_set_eflags(&pkg, 0x11); + test_pass(pkg.eflag == 0x11); + DPKG_ACCEPT_WARNING_ASSIGN_ENUM(); + pkg_reset_eflags(&pkg); + test_pass(pkg.eflag == PKG_EFLAG_OK); +} + +static void +test_pkginfo_instance_tracking(void) +{ + struct pkgset set; + struct pkginfo pkg2, pkg3, pkg4; + + pkgset_blank(&set); + pkg_blank(&pkg2); + pkg_blank(&pkg3); + pkg_blank(&pkg4); + + test_pass(pkgset_installed_instances(&set) == 0); + + /* Link the other instances into the pkgset. */ + pkgset_link_pkg(&set, &pkg4); + pkgset_link_pkg(&set, &pkg3); + pkgset_link_pkg(&set, &pkg2); + + /* Test installation state transitions. */ + pkg_set_status(&pkg4, PKG_STAT_INSTALLED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_INSTALLED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_TRIGGERSPENDING); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_TRIGGERSAWAITED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_HALFCONFIGURED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_UNPACKED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_HALFINSTALLED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_CONFIGFILES); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 0); + + pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 0); + + /* Toggle installation states on various packages. */ + pkg_set_status(&pkg4, PKG_STAT_INSTALLED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg2, PKG_STAT_HALFINSTALLED); + test_pass(pkgset_installed_instances(&set) == 2); + + pkg_set_status(&set.pkg, PKG_STAT_CONFIGFILES); + test_pass(pkgset_installed_instances(&set) == 3); + + pkg_set_status(&pkg3, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 3); + + pkg_set_status(&pkg3, PKG_STAT_UNPACKED); + test_pass(pkgset_installed_instances(&set) == 4); + + pkg_set_status(&set.pkg, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 3); + + pkg_set_status(&pkg2, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 2); + + pkg_set_status(&pkg3, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 1); + + pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED); + test_pass(pkgset_installed_instances(&set) == 0); +} + +TEST_ENTRY(test) +{ + test_plan(28); + + test_pkginfo_informative(); + test_pkginfo_eflags(); + test_pkginfo_instance_tracking(); + + /* TODO: Complete. */ +} diff --git a/lib/dpkg/t/t-progname.c b/lib/dpkg/t/t-progname.c new file mode 100644 index 0000000..e90e923 --- /dev/null +++ b/lib/dpkg/t/t-progname.c @@ -0,0 +1,53 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-progname.c - test program name handling + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/progname.h> + +static void +test_progname(void) +{ + const char *progname; + + /* Test initially empty progname. */ + progname = dpkg_get_progname(); + /* Handle libtool executables. */ + if (strncmp(progname, "lt-", 3) == 0) + progname += 3; + test_str(progname, ==, "t-progname"); + + /* Test setting a new progname. */ + dpkg_set_progname("newname"); + test_str(dpkg_get_progname(), ==, "newname"); + + /* Test setting a new progname with path. */ + dpkg_set_progname("path/newprogname"); + test_str(dpkg_get_progname(), ==, "newprogname"); +} + +TEST_ENTRY(test) +{ + test_plan(3); + + test_progname(); +} diff --git a/lib/dpkg/t/t-string.c b/lib/dpkg/t/t-string.c new file mode 100644 index 0000000..7b4350d --- /dev/null +++ b/lib/dpkg/t/t-string.c @@ -0,0 +1,281 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-string.c - test string handling + * + * Copyright © 2009-2011, 2014-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/string.h> + +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> + +static void +test_str_is_set(void) +{ + /* Test if strings are unset. */ + test_pass(str_is_unset(NULL)); + test_pass(str_is_unset("")); + test_fail(str_is_unset("aaa")); + + /* Test if strings are set. */ + test_fail(str_is_set(NULL)); + test_fail(str_is_set("")); + test_pass(str_is_set("ccc")); +} + +static void +test_str_match_end(void) +{ + test_pass(str_match_end("foo bar quux", "quux")); + test_pass(str_match_end("foo bar quux", "bar quux")); + test_pass(str_match_end("foo bar quux", "foo bar quux")); + test_fail(str_match_end("foo bar quux", "foo bar quux zorg")); + test_fail(str_match_end("foo bar quux", "foo bar")); + test_fail(str_match_end("foo bar quux", "foo")); +} + +static void +test_str_fnv_hash(void) +{ + test_pass(str_fnv_hash("") == 0x811c9dc5U); + test_pass(str_fnv_hash("a") == 0xe40c292cUL); + test_pass(str_fnv_hash("b") == 0xe70c2de5UL); + test_pass(str_fnv_hash("c") == 0xe60c2c52UL); + test_pass(str_fnv_hash("d") == 0xe10c2473UL); + test_pass(str_fnv_hash("e") == 0xe00c22e0UL); + test_pass(str_fnv_hash("f") == 0xe30c2799UL); + test_pass(str_fnv_hash("fo") == 0x6222e842UL); + test_pass(str_fnv_hash("foo") == 0xa9f37ed7UL); + test_pass(str_fnv_hash("foob") == 0x3f5076efUL); + test_pass(str_fnv_hash("fooba") == 0x39aaa18aUL); + test_pass(str_fnv_hash("foobar") == 0xbf9cf968UL); + + test_pass(str_fnv_hash("test-string") == 0xd28f6e61UL); + test_pass(str_fnv_hash("Test-string") == 0x00a54b81UL); + test_pass(str_fnv_hash("rest-string") == 0x1cdeebffUL); + test_pass(str_fnv_hash("Rest-string") == 0x20464b9fUL); +} + +static void +test_str_concat(void) +{ + char buf[1024], *str; + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, NULL); + test_pass(str == buf); + test_str(buf, ==, ""); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "aaa", NULL); + test_str(buf, ==, "aaa"); + test_pass(str == buf + 3); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "zzzz", "yy", "xxxx", NULL); + test_str(buf, ==, "zzzzyyxxxx"); + test_pass(str == buf + 10); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "1234", "", "5678", NULL); + test_str(buf, ==, "12345678"); + test_pass(str == buf + 8); + + memset(buf, ' ', sizeof(buf)); + str = str_concat(buf, "eol", NULL, "bom", NULL); + test_str(buf, ==, "eol"); + test_pass(str == buf + 3); +} + +static void +test_str_fmt(void) +{ + char *str; + + str = str_fmt("%s", "abcde"); + test_str(str, ==, "abcde"); + free(str); + + str = str_fmt("%d", 15); + test_str(str, ==, "15"); + free(str); +} + +static void +test_str_escape_fmt(void) +{ + char buf[1024], *q; + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "", sizeof(buf)); + strcpy(q, " end"); + test_str(buf, ==, " end"); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "%", sizeof(buf)); + strcpy(q, " end"); + test_str(buf, ==, "%% end"); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "%%%", sizeof(buf)); + strcpy(q, " end"); + test_str(buf, ==, "%%%%%% end"); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "%b%b%c%c%%", sizeof(buf)); + strcpy(q, " end"); + test_str(buf, ==, "%%b%%b%%c%%c%%%% end"); + + /* Test delimited buffer. */ + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, NULL, 0); + test_mem(buf, ==, "aaaa", 4); + test_pass(buf == q); + test_pass(strnlen(buf, sizeof(buf)) == sizeof(buf)); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "b", 1); + test_str(q, ==, ""); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "%%%", 5); + strcpy(q, " end"); + test_str(buf, ==, "%%%% end"); + + memset(buf, 'a', sizeof(buf)); + q = str_escape_fmt(buf, "%%%", 4); + strcpy(q, " end"); + test_str(buf, ==, "%% end"); +} + +static void +test_str_rtrim_spaces(void) +{ + char buf[1024]; + char *str_end; + + strcpy(buf, ""); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf); + test_str(buf, ==, ""); + + strcpy(buf, " \t\t \r\n "); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf); + test_str(buf, ==, ""); + + strcpy(buf, "abcd"); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf + 4); + test_str(buf, ==, "abcd"); + + strcpy(buf, "abcd "); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf + 4); + test_str(buf, ==, "abcd"); + + strcpy(buf, "abcd\t \t "); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf + 4); + test_str(buf, ==, "abcd"); + + strcpy(buf, " \t \t abcd"); + str_end = str_rtrim_spaces(buf, buf + strlen(buf)); + test_pass(str_end == buf + 12); + test_str(buf, ==, " \t \t abcd"); +} + +static void +test_str_quote_meta(void) +{ + char *str; + + str = str_quote_meta("foo1 2bar"); + test_str(str, ==, "foo1\\ 2bar"); + free(str); + + str = str_quote_meta("foo1?2bar"); + test_str(str, ==, "foo1\\?2bar"); + free(str); + + str = str_quote_meta("foo1*2bar"); + test_str(str, ==, "foo1\\*2bar"); + free(str); +} + +static void +test_str_strip_quotes(void) +{ + char buf[1024], *str; + + strcpy(buf, "unquoted text"); + str = str_strip_quotes(buf); + test_str(str, ==, "unquoted text"); + + strcpy(buf, "contained 'quoted text'"); + str = str_strip_quotes(buf); + test_str(str, ==, "contained 'quoted text'"); + + strcpy(buf, "contained \"quoted text\""); + str = str_strip_quotes(buf); + test_str(str, ==, "contained \"quoted text\""); + + strcpy(buf, "'unbalanced quotes"); + str = str_strip_quotes(buf); + test_pass(str == NULL); + + strcpy(buf, "\"unbalanced quotes"); + str = str_strip_quotes(buf); + test_pass(str == NULL); + + strcpy(buf, "'mismatched quotes\""); + str = str_strip_quotes(buf); + test_pass(str == NULL); + + strcpy(buf, "\"mismatched quotes'"); + str = str_strip_quotes(buf); + test_pass(str == NULL); + + strcpy(buf, "'completely quoted text'"); + str = str_strip_quotes(buf); + test_str(str, ==, "completely quoted text"); + + strcpy(buf, "\"completely quoted text\""); + str = str_strip_quotes(buf); + test_str(str, ==, "completely quoted text"); +} + +TEST_ENTRY(test) +{ + test_plan(74); + + test_str_is_set(); + test_str_match_end(); + test_str_fnv_hash(); + test_str_concat(); + test_str_fmt(); + test_str_escape_fmt(); + test_str_quote_meta(); + test_str_strip_quotes(); + test_str_rtrim_spaces(); +} diff --git a/lib/dpkg/t/t-subproc.c b/lib/dpkg/t/t-subproc.c new file mode 100644 index 0000000..7ce610b --- /dev/null +++ b/lib/dpkg/t/t-subproc.c @@ -0,0 +1,100 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-subproc.c - test sub-process module + * + * Copyright © 2011 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/subproc.h> + +static void +test_subproc_fork(void) +{ + struct sigaction sa; + pid_t pid; + int ret; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + /* Test exit(). */ + pid = subproc_fork(); + if (pid == 0) + exit(0); + ret = subproc_reap(pid, "subproc exit pass", SUBPROC_RETERROR); + test_pass(ret == 0); + + pid = subproc_fork(); + if (pid == 0) + exit(128); + ret = subproc_reap(pid, "subproc exit fail", SUBPROC_RETERROR); + test_pass(ret == 128); + + /* Test signals. */ + pid = subproc_fork(); + if (pid == 0) + raise(SIGINT); + ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); + test_pass(ret == -1); + + pid = subproc_fork(); + if (pid == 0) + raise(SIGTERM); + ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); + test_pass(ret == -1); + + pid = subproc_fork(); + if (pid == 0) + raise(SIGPIPE); + ret = subproc_reap(pid, "subproc SIGPIPE", + SUBPROC_WARN | SUBPROC_NOPIPE); + test_pass(ret == 0); + + pid = subproc_fork(); + if (pid == 0) + raise(SIGPIPE); + ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN); + test_pass(ret == -1); +} + +TEST_ENTRY(test) +{ + int fd; + + test_plan(6); + + /* XXX: Shut up stderr, we don't want the error output. */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) + test_bail("cannot open /dev/null"); + dup2(fd, 2); + + test_subproc_fork(); +} diff --git a/lib/dpkg/t/t-tar.c b/lib/dpkg/t/t-tar.c new file mode 100644 index 0000000..6fa217d --- /dev/null +++ b/lib/dpkg/t/t-tar.c @@ -0,0 +1,148 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-tar.c - test tar implementation + * + * Copyright © 2017 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <errno.h> + +#include <dpkg/test.h> +#include <dpkg/tarfn.h> + +static void +test_tar_atol8(void) +{ + uintmax_t u; + + /* Test valid octal numbers. */ + u = tar_atoul("000000\0\0\0\0\0\0", 12, UINTMAX_MAX); + test_pass(u == 0); + u = tar_atoul("00000000000\0", 12, UINTMAX_MAX); + test_pass(u == 0); + u = tar_atoul("00000000001\0", 12, UINTMAX_MAX); + test_pass(u == 1); + u = tar_atoul("00000000777\0", 12, UINTMAX_MAX); + test_pass(u == 511); + u = tar_atoul("77777777777\0", 12, UINTMAX_MAX); + test_pass(u == 8589934591); + + /* Test legacy formatted octal numbers. */ + u = tar_atoul(" 0\0", 12, UINTMAX_MAX); + test_pass(u == 0); + u = tar_atoul(" 1\0", 12, UINTMAX_MAX); + test_pass(u == 1); + u = tar_atoul(" 777\0", 12, UINTMAX_MAX); + test_pass(u == 511); + + /* Test extended octal numbers not terminated by space or NUL, + * (as is required by POSIX), but accepted by several implementations + * to get one byte larger values. */ + u = tar_atoul("000000000000", 12, UINTMAX_MAX); + test_pass(u == 0); + u = tar_atoul("000000000001", 12, UINTMAX_MAX); + test_pass(u == 1); + u = tar_atoul("000000000777", 12, UINTMAX_MAX); + test_pass(u == 511); + u = tar_atoul("777777777777", 12, UINTMAX_MAX); + test_pass(u == 68719476735); + + /* Test invalid octal numbers. */ + errno = 0; + u = tar_atoul(" ", 12, UINTMAX_MAX); + test_pass(u == 0); + test_pass(errno == EINVAL); + + errno = 0; + u = tar_atoul(" 11111aaa ", 12, UINTMAX_MAX); + test_pass(u == 0); + test_pass(errno == ERANGE); + + errno = 0; + u = tar_atoul(" 8 ", 12, UINTMAX_MAX); + test_pass(u == 0); + test_pass(errno == ERANGE); + + errno = 0; + u = tar_atoul(" 18 ", 12, UINTMAX_MAX); + test_pass(u == 0); + test_pass(errno == ERANGE); + + errno = 0; + u = tar_atoul(" aa ", 12, UINTMAX_MAX); + test_pass(u == 0); + test_pass(errno == ERANGE); +} + +static void +test_tar_atol256(void) +{ + uintmax_t u; + intmax_t i; + + /* Test positive numbers. */ + u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX); + test_pass(u == 0); + u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 12, UINTMAX_MAX); + test_pass(u == 1); + u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00", 12, UINTMAX_MAX); + test_pass(u == 8589934592); + u = tar_atoul("\x80\x00\x00\x00\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 12, UINTMAX_MAX); + test_pass(u == INTMAX_MAX); + + /* Test overflow. */ + errno = 0; + u = tar_atoul("\x80\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX); + test_pass(u == UINTMAX_MAX); + test_pass(errno == ERANGE); + + errno = 0; + u = tar_atoul("\x80\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX); + test_pass(u == UINTMAX_MAX); + test_pass(errno == ERANGE); + + /* Test negative numbers. */ + i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == -1); + i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == -2); + i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == -8589934592); + i = tar_atosl("\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == INTMAX_MIN); + + /* Test underflow. */ + errno = 0; + i = tar_atosl("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == INTMAX_MIN); + test_pass(errno == ERANGE); + + errno = 0; + i = tar_atosl("\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX); + test_pass(i == INTMAX_MIN); + test_pass(errno == ERANGE); +} + +TEST_ENTRY(test) +{ + test_plan(38); + + test_tar_atol8(); + test_tar_atol256(); +} diff --git a/lib/dpkg/t/t-tarextract.t b/lib/dpkg/t/t-tarextract.t new file mode 100755 index 0000000..5499cdc --- /dev/null +++ b/lib/dpkg/t/t-tarextract.t @@ -0,0 +1,159 @@ +#!/usr/bin/perl +# +# Copyright © 2014 Guillem Jover <guillem@debian.org> +# +# 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 <https://www.gnu.org/licenses/>. + +use Test::More; +use Cwd; +use File::Path qw(make_path remove_tree); +use File::Temp qw(tempdir); +use File::Spec; +use File::Find; +use POSIX qw(mkfifo); + +use Dpkg (); +use Dpkg::File; +use Dpkg::IPC; + +use strict; +use warnings; +use version; + +my $srcdir = $ENV{srcdir} || '.'; +my $builddir = $ENV{builddir} || '.'; +my $tmpdir = 't.tmp/t-tarextract'; + +# We require GNU tar >= 1.27 for --owner=NAME:ID and --group=NAME:ID. +my $tar_version = qx($Dpkg::PROGTAR --version 2>/dev/null); +if ($tar_version and $tar_version =~ m/^tar \(GNU tar\) (\d+\.\d+)/ and + qv("v$1") >= qv('v1.27')) +{ + plan tests => 12; +} else { + plan skip_all => 'needs GNU tar >= 1.27'; +} + +# Set a known umask. +umask 0022; + +sub tar_create_tree { + my $type = shift; + + my $long_a = 'a' x 29; + my $long_b = 'b' x 29; + my $long_c = 'c' x 29; + my $long_d = 'd' x 29; + my $long_e = 'e' x 29; + my $long_f = 'f' x 22; + + # Populate tar hierarchy + file_touch('file'); + link 'file', 'hardlink'; + + make_path("$long_a/$long_b/$long_c/$long_d/$long_e/"); + make_path("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/"); + file_touch("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long"); + + # POSIX specifies that symlinks have undefined permissions in their + # mode, so their handling is system dependent. Linux does not honor + # the umask for symlinks, other systems like GNU/Hurd or kFreeBSD do, + # which means we get different results due to this. + my $umask = umask 0; + + symlink "$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long", + 'symlink-long'; + symlink 'file', 'symlink-a'; + symlink 'hardlink', 'symlink-b'; + symlink 'dangling', 'symlink-c'; + + umask $umask; + + mkdir 'directory'; + mkfifo('fifo', 0770); + + # TODO: Need root. + # system 'mknod', 'chardev', 'c', '1', '3'; + # system 'mknod', 'blockdev', 'b', '0', '0'; +} + +sub test_tar_extractor { + my $stdout; + my $stderr; + + my $expected_tar = <<'TAR'; +. mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./fifo mode=10750 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=fifo +./file mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0 +./hardlink mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=hardlink linkto=./file size=0 +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0 +./directory mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir +./symlink-a mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=file size=0 +./symlink-b mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=hardlink size=0 +./symlink-c mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=dangling size=0 +./symlink-long mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long size=0 +TAR + + make_path($tmpdir); + + my $cwd = getcwd(); + + # Check generated tarballs. + foreach my $type (qw(v7 ustar oldgnu gnu)) { + my $dirtree = "$tmpdir/$type"; + my @paths; + + mkdir $dirtree; + chdir $dirtree; + tar_create_tree($type); + find({ no_chdir => 1, wanted => sub { + return if $type eq 'v7' and length > 99; + return if $type eq 'v7' and -l and length readlink > 99; + return if $type eq 'v7' and not (-f or -l or -d); + return if $type eq 'ustar' and length > 256; + return if $type eq 'ustar' and -l and length readlink > 100; + push @paths, $_; + }, + preprocess => sub { my (@files) = sort @_; @files } }, '.'); + chdir $cwd; + + my $paths_list = join "\0", @paths; + spawn(exec => [ $Dpkg::PROGTAR, '-cf', "$dirtree.tar", + '--format', $type, + '-C', $dirtree, '--mtime=@100000000', + '--owner=user:100', '--group=group:200', + '--null', '--no-unquote', '--no-recursion', '-T-' ], + wait_child => 1, from_string => \$paths_list); + + my $expected = $expected_tar; + $expected =~ s/[ug]name=[^ ]+ //g if $type eq 'v7'; + $expected =~ s/\n^.*fifo.*$//mg if $type eq 'v7'; + $expected =~ s/\n^.*dddd.*$//mg if $type eq 'v7'; + $expected =~ s/\n^.*symlink-long.*$//mg if $type eq 'ustar'; + + spawn(exec => [ "$builddir/t/c-tarextract", "$dirtree.tar" ], + nocheck => 1, to_string => \$stdout, to_error => \$stderr); + ok($? == 0, "tar extractor $type should succeed"); + is($stderr, undef, "tar extractor $type stderr is empty"); + is($stdout, $expected, "tar extractor $type is ok"); + } +} + +test_tar_extractor(); diff --git a/lib/dpkg/t/t-test-skip.c b/lib/dpkg/t/t-test-skip.c new file mode 100644 index 0000000..972cdf1 --- /dev/null +++ b/lib/dpkg/t/t-test-skip.c @@ -0,0 +1,31 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-test-skip.c - test suite self tests, skip all + * + * Copyright © 2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> + +TEST_ENTRY(test) +{ + test_skip_all("ignore all tests"); + + test_fail(1); +} diff --git a/lib/dpkg/t/t-test.c b/lib/dpkg/t/t-test.c new file mode 100644 index 0000000..48ce872 --- /dev/null +++ b/lib/dpkg/t/t-test.c @@ -0,0 +1,66 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-test.c - test suite self tests + * + * Copyright © 2009, 2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> + +TEST_ENTRY(test) +{ + test_plan(22); + + test_pass(1); + test_fail(0); + + test_skip("ignore test"); + + test_skip_block(1) { + test_pass(0); + test_pass(1); + } + + test_todo(0, "unimplemented test", "failing test"); + + test_todo_block("unimplemented block") { + test_pass(0); + test_fail(1); + } + + test_str("aaa", ==, "aaa"); + test_str("aaa", <, "bbb"); + test_str("ccc", >, "bbb"); + test_str("ccc", !=, "bbb"); + + test_mem("aaa", ==, "aaa", 3); + test_mem("aaa", <, "bbb", 3); + test_mem("ccc", >, "bbb", 3); + test_mem("ccc", !=, "bbb", 3); + + test_mem("abcd", ==, "abcd", 4); + test_mem("abcd", ==, "abcd", 5); + test_mem("ababcd", ==, "ababff", 4); + test_mem("ababcd", !=, "ababff", 6); + + setenv("srcdir", "aaa", 1); + setenv("builddir", "bbb", 1); + test_str(test_get_srcdir(), ==, "aaa"); + test_str(test_get_builddir(), ==, "bbb"); +} diff --git a/lib/dpkg/t/t-treewalk.t b/lib/dpkg/t/t-treewalk.t new file mode 100755 index 0000000..573103d --- /dev/null +++ b/lib/dpkg/t/t-treewalk.t @@ -0,0 +1,154 @@ +#!/usr/bin/perl +# +# Copyright © 2016 Guillem Jover <guillem@debian.org> +# +# 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 <https://www.gnu.org/licenses/>. + +use strict; +use warnings; +use version; + +use Test::More tests => 6; +use Cwd; +use File::Path qw(make_path remove_tree); +use File::Temp qw(tempdir); +use File::Basename; +use File::Find; + +use Dpkg::File; +use Dpkg::IPC; + +my $srcdir = $ENV{srcdir} || '.'; +my $builddir = $ENV{builddir} || '.'; +my $tmpdir = 't.tmp/t-treewalk'; + +# Set a known umask. +umask 0022; + +# Populate the tree hierarchy. +sub make_tree { + my ($dirtree) = @_; + my $cwd = getcwd(); + + make_path($dirtree); + chdir $dirtree; + + # Deep tree. + make_path('aaaa/aaaa/aaaa/aaaa/'); + file_touch('aaaa/aaaa/aaaa/aaaa/abcde'); + file_touch('aaaa/aaaa/aaaa/aaaa/ddddd'); + file_touch('aaaa/aaaa/aaaa/aaaa/wwwwa'); + file_touch('aaaa/aaaa/aaaa/aaaa/wwwwz'); + file_touch('aaaa/aaaa/aaaa/aaaa/zzzzz'); + + # Shallow tree. + make_path('bbbb/'); + file_touch('bbbb/abcde'); + file_touch('bbbb/ddddd'); + file_touch('bbbb/wwwwa'); + file_touch('bbbb/wwwwz'); + file_touch('bbbb/zzzzz'); + + # Populated tree. + make_path('cccc/aa/aa/aa/'); + make_path('cccc/aa/aa/bb/aa/'); + file_touch('cccc/aa/aa/bb/aa/file-a'); + file_touch('cccc/aa/aa/bb/aa/file-z'); + make_path('cccc/aa/bb/'); + make_path('cccc/bb/aa/'); + make_path('cccc/bb/bb/aa/aa/'); + file_touch('cccc/bb/bb/aa/aa/file-a'); + file_touch('cccc/bb/bb/aa/aa/file-z'); + make_path('cccc/bb/bb/bb/'); + file_touch('cccc/bb/bb/bb/file-w'); + make_path('cccc/cc/aa/'); + make_path('cccc/cc/bb/aa/'); + file_touch('cccc/cc/bb/aa/file-t'); + make_path('cccc/cc/bb/bb/'); + file_touch('cccc/cc/bb/bb/file-x'); + make_path('cccc/cc/cc/'); + make_path('cccc/dd/aa/aa/aa/'); + file_touch('cccc/dd/aa/aa/aa/file-y'); + make_path('cccc/dd/aa/aa/bb/'); + file_touch('cccc/dd/aa/aa/bb/file-o'); + make_path('cccc/dd/aa/bb/aa/'); + file_touch('cccc/dd/aa/bb/aa/file-k'); + make_path('cccc/dd/aa/bb/bb/'); + file_touch('cccc/dd/aa/bb/bb/file-l'); + make_path('cccc/dd/aa/cc/aa/'); + file_touch('cccc/dd/aa/cc/aa/file-s'); + make_path('cccc/dd/aa/cc/bb/'); + file_touch('cccc/dd/aa/cc/bb/file-u'); + + # Tree with symlinks cycles. + make_path('llll/self/'); + file_touch('llll/file'); + symlink '..', 'llll/self/loop'; + make_path('llll/real/'); + file_touch('llll/real/file-r'); + symlink '../virt', 'llll/real/loop'; + make_path('llll/virt/'); + file_touch('llll/virt/file-v'); + symlink '../real', 'llll/virt/loop'; + + chdir $cwd; +} + +sub test_treewalker { + my $stdout; + my $stderr; + my $dirtree = $tmpdir; + + # Check generated tarballs. + foreach my $type (qw(full skip)) { + my @paths; + + make_tree($dirtree); + + find({ no_chdir => 1, wanted => sub { + return if $type eq 'skip' and m{^\Q$dirtree\E/cccc}; + push @paths, s{\./}{}r; + }, + }, $dirtree); + + my $expected; + + foreach my $path (sort @paths) { + lstat $path; + + my $ptype; + if (-f _) { + $ptype = 'f'; + } elsif (-l _) { + $ptype = 'l'; + } elsif (-d _) { + $ptype = 'd'; + } + my $pname = basename($path); + my $pvirt = $path =~ s{\Q$dirtree\E/?}{}r; + + $expected .= "T=$ptype N=$pname V=$pvirt R=$path\n"; + } + + $ENV{TREEWALK_SKIP} = $type eq 'skip' ? "$dirtree/cccc" : undef; + + spawn(exec => [ "$builddir/t/c-treewalk", $dirtree ], + nocheck => 1, to_string => \$stdout, to_error => \$stderr); + ok($? == 0, "tree walker $type should succeed"); + is($stderr, undef, "tree walker $type stderr is empty"); + is($stdout, $expected, "tree walker $type is ok"); + } +} + +test_treewalker(); diff --git a/lib/dpkg/t/t-trigdeferred.t b/lib/dpkg/t/t-trigdeferred.t new file mode 100755 index 0000000..bf73541 --- /dev/null +++ b/lib/dpkg/t/t-trigdeferred.t @@ -0,0 +1,116 @@ +#!/usr/bin/perl +# +# Copyright © 2016 Guillem Jover <guillem@debian.org> +# +# 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 <https://www.gnu.org/licenses/>. + +use strict; +use warnings; +use version; + +use Test::More; +use Cwd; +use File::Path qw(make_path remove_tree); +use File::Temp qw(tempdir); +use File::Basename; +use File::Find; + +use Dpkg::File; +use Dpkg::IPC; + +my $srcdir = $ENV{srcdir} || '.'; +my $builddir = $ENV{builddir} || '.'; +my $tmpdir = 't.tmp/t-trigdeferred'; + +my @deferred = ( + { + exitcode => 0, + original => <<'TEXT', + + # Comment + # Another Comment + /root/pathname/file-trigger pkg-a pkg-b pkg-c +named-trigger pkg-1 pkg-2 pkg-3 +parse-trigger pkg:a pkg+b pkg.0 0-pkg +:other-trigger - +TEXT + expected => <<'TEXT', +<T='/root/pathname/file-trigger' P='pkg-a' P='pkg-b' P='pkg-c' E> +<T='named-trigger' P='pkg-1' P='pkg-2' P='pkg-3' E> +<T='parse-trigger' P='pkg:a' P='pkg+b' P='pkg.0' P='0-pkg' E> +<T=':other-trigger' P='-' E> +TEXT + }, { + exitcode => 2, + original => <<"TEXT", +\b # invalid character +TEXT + }, { + exitcode => 2, + original => <<'TEXT', +trigger -pkg +TEXT + }, { + exitcode => 2, + original => <<'TEXT', +trigger +pkg +TEXT + }, { + exitcode => 2, + original => <<'TEXT', +trigger .pkg +TEXT + }, { + exitcode => 2, + original => <<'TEXT', +trigger :pkg +TEXT + }, { + exitcode => 2, + original => 'missing newline', + } +); + +plan tests => scalar(@deferred) * 3; + +# Set a known umask. +umask 0022; + +sub test_trigdeferred { + my $stdout; + my $stderr; + my $admindir = "$tmpdir"; + + # Check triggers deferred file parsing. + make_path("$admindir/triggers"); + + foreach my $test (@deferred) { + file_dump("$admindir/triggers/Unincorp", $test->{original}); + + spawn(exec => [ "$builddir/t/c-trigdeferred", $admindir ], + nocheck => 1, to_string => \$stdout, error_to_string => \$stderr); + my $exitcode = $? >> 8; + + is($exitcode, $test->{exitcode}, 'trigger deferred expected exitcode'); + if ($test->{exitcode} == 0) { + is($stderr, '', 'trigger deferred expected stderr'); + is($stdout, $test->{expected}, 'trigger deferred expected stdout'); + } else { + isnt($stderr, '', 'trigger deferred expected stderr'); + isnt($stdout, undef, 'trigger deferred expected stdout'); + } + } +} + +test_trigdeferred(); diff --git a/lib/dpkg/t/t-trigger.c b/lib/dpkg/t/t-trigger.c new file mode 100644 index 0000000..af78f23 --- /dev/null +++ b/lib/dpkg/t/t-trigger.c @@ -0,0 +1,49 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-trigger.c - test triggers + * + * Copyright © 2012 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/triglib.h> + +static void +test_trig_name_is_illegal(void) +{ + /* Test invalid trigger names. */ + test_fail(trig_name_is_illegal("") == NULL); + test_fail(trig_name_is_illegal("\a") == NULL); + test_fail(trig_name_is_illegal("\t") == NULL); + test_fail(trig_name_is_illegal("\200") == NULL); + test_fail(trig_name_is_illegal("trigger name") == NULL); + + /* Test valid trigger names. */ + test_pass(trig_name_is_illegal("TRIGGER") == NULL); + test_pass(trig_name_is_illegal("trigger") == NULL); + test_pass(trig_name_is_illegal("0123456789") == NULL); + test_pass(trig_name_is_illegal("/file/trigger") == NULL); +} + +TEST_ENTRY(test) +{ + test_plan(9); + + test_trig_name_is_illegal(); +} diff --git a/lib/dpkg/t/t-varbuf.c b/lib/dpkg/t/t-varbuf.c new file mode 100644 index 0000000..08998a5 --- /dev/null +++ b/lib/dpkg/t/t-varbuf.c @@ -0,0 +1,560 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-varbuf.c - test varbuf implementation + * + * Copyright © 2009-2011, 2013-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/varbuf.h> + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +static void +test_varbuf_init(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 0); + test_pass(vb.used == 0); + test_pass(vb.size == 0); + test_pass(vb.buf == NULL); + + varbuf_destroy(&vb); + test_pass(vb.used == 0); + test_pass(vb.size == 0); + test_pass(vb.buf == NULL); +} + +static void +test_varbuf_prealloc(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 10); + test_pass(vb.used == 0); + test_pass(vb.size >= 10); + test_pass(vb.buf != NULL); + + varbuf_destroy(&vb); + test_pass(vb.used == 0); + test_pass(vb.size == 0); + test_pass(vb.buf == NULL); +} + +static void +test_varbuf_new(void) +{ + struct varbuf *vb; + + vb = varbuf_new(0); + test_pass(vb != NULL); + test_pass(vb->used == 0); + test_pass(vb->size == 0); + test_pass(vb->buf == NULL); + varbuf_free(vb); + + vb = varbuf_new(10); + test_pass(vb != NULL); + test_pass(vb->used == 0); + test_pass(vb->size >= 10); + test_pass(vb->buf != NULL); + varbuf_free(vb); +} + +static void +test_varbuf_grow(void) +{ + struct varbuf vb; + jmp_buf grow_jump; + size_t old_size; + bool grow_overflow; + int i; + + varbuf_init(&vb, 10); + + /* Test that we grow when needed. */ + varbuf_grow(&vb, 100); + test_pass(vb.used == 0); + test_pass(vb.size >= 100); + + old_size = vb.size; + + /* Test that we are not leaking. */ + for (i = 0; i < 10; i++) { + varbuf_grow(&vb, 100); + test_pass(vb.used == 0); + test_pass(vb.size >= 100); + test_pass(vb.size == old_size); + } + + /* Test that we grow when needed, with used space. */ + vb.used = 10; + varbuf_grow(&vb, 100); + test_pass(vb.used == 10); + test_pass(vb.size >= 110); + + /* Test that we do not allow allocation overflows. */ + grow_overflow = false; + old_size = vb.size; + test_try(grow_jump) { + varbuf_grow(&vb, SIZE_MAX - vb.size + 2); + } test_catch { + grow_overflow = true; + } test_finally; + test_pass(vb.size == old_size && grow_overflow); + + grow_overflow = false; + old_size = vb.size; + test_try(grow_jump) { + varbuf_grow(&vb, (SIZE_MAX - vb.size - 2) / 2); + } test_catch { + grow_overflow = true; + } test_finally; + test_pass(vb.size == old_size && grow_overflow); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_trunc(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 50); + + /* Test that we truncate (grow). */ + varbuf_trunc(&vb, 20); + test_pass(vb.used == 20); + test_pass(vb.size >= 50); + + /* Test that we truncate (shrink). */ + varbuf_trunc(&vb, 10); + test_pass(vb.used == 10); + test_pass(vb.size >= 50); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_set(void) +{ + struct varbuf vb, cb; + + varbuf_init(&vb, 10); + varbuf_init(&cb, 10); + + varbuf_set_buf(&vb, "1234567890", 5); + test_pass(vb.used == 5); + test_mem(vb.buf, ==, "12345", 5); + + varbuf_set_buf(&vb, "abcd", 4); + test_pass(vb.used == 4); + test_mem(vb.buf, ==, "abcd", 4); + + varbuf_set_varbuf(&cb, &vb); + test_pass(cb.used == 4); + test_mem(cb.buf, ==, "abcd", 4); + + varbuf_set_str(&vb, "12345"); + test_pass(vb.used == 5); + test_str(vb.buf, ==, "12345"); + + varbuf_set_strn(&vb, "1234567890", 8); + test_pass(vb.used == 8); + test_str(vb.buf, ==, "12345678"); + + varbuf_destroy(&cb); + varbuf_destroy(&vb); +} + +static void +test_varbuf_add_varbuf(void) +{ + struct varbuf vb, cb; + + varbuf_init(&vb, 5); + varbuf_init(&cb, 0); + + varbuf_set_str(&vb, "1234567890"); + varbuf_add_varbuf(&cb, &vb); + test_pass(cb.used == 10); + test_pass(cb.size >= cb.used); + test_mem(cb.buf, ==, "1234567890", 10); + + varbuf_set_str(&vb, "abcde"); + varbuf_add_varbuf(&cb, &vb); + test_pass(cb.used == 15); + test_pass(cb.size >= cb.used); + test_mem(cb.buf, ==, "1234567890abcde", 15); + + varbuf_destroy(&cb); + varbuf_destroy(&vb); +} + +static void +test_varbuf_add_buf(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 5); + + varbuf_add_buf(&vb, "1234567890", 10); + test_pass(vb.used == 10); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "1234567890", 10); + + varbuf_add_buf(&vb, "abcde", 5); + test_pass(vb.used == 15); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "1234567890abcde", 15); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_add_str(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 5); + + varbuf_add_str(&vb, "1234567890"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "1234567890"); + + varbuf_add_str(&vb, "abcd"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "1234567890abcd"); + + varbuf_add_strn(&vb, "1234567890", 5); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "1234567890abcd12345"); + + varbuf_add_strn(&vb, "abcd", 0); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "1234567890abcd12345"); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_add_char(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 1); + + varbuf_add_char(&vb, 'a'); + test_pass(vb.used == 1); + test_pass(vb.size >= vb.used); + test_pass(vb.buf[0] == 'a'); + + varbuf_add_char(&vb, 'b'); + test_pass(vb.used == 2); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "ab", 2); + + varbuf_add_char(&vb, 'c'); + test_pass(vb.used == 3); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "abc", 3); + + varbuf_add_char(&vb, 'd'); + test_pass(vb.used == 4); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "abcd", 4); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_dup_char(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 5); + + varbuf_dup_char(&vb, 'z', 10); + test_pass(vb.used == 10); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "zzzzzzzzzz", 10); + + varbuf_dup_char(&vb, 'y', 5); + test_pass(vb.used == 15); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "zzzzzzzzzzyyyyy", 15); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_map_char(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 5); + + varbuf_add_buf(&vb, "1234a5678a9012a", 15); + + varbuf_map_char(&vb, 'a', 'z'); + test_pass(vb.used == 15); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "1234z5678z9012z", 15); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_add_dir(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 10); + + varbuf_add_dir(&vb, ""); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/"); + varbuf_add_dir(&vb, ""); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/"); + varbuf_add_dir(&vb, "aa"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/aa/"); + varbuf_add_dir(&vb, ""); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/aa/"); + + varbuf_reset(&vb); + + varbuf_add_dir(&vb, "/foo/bar"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/foo/bar/"); + + varbuf_reset(&vb); + + varbuf_add_dir(&vb, "/foo/bar/"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/foo/bar/"); + varbuf_add_dir(&vb, "quux"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/foo/bar/quux/"); + varbuf_add_dir(&vb, "zoo"); + varbuf_end_str(&vb); + test_str(vb.buf, ==, "/foo/bar/quux/zoo/"); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_end_str(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 10); + + varbuf_add_buf(&vb, "1234567890X", 11); + test_pass(vb.used == 11); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "1234567890X", 11); + + varbuf_trunc(&vb, 10); + + varbuf_end_str(&vb); + test_pass(vb.used == 10); + test_pass(vb.size >= vb.used + 1); + test_pass(vb.buf[10] == '\0'); + test_str(vb.buf, ==, "1234567890"); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_get_str(void) +{ + struct varbuf vb; + const char *str; + + varbuf_init(&vb, 10); + + varbuf_add_buf(&vb, "1234567890", 10); + str = varbuf_get_str(&vb); + test_pass(vb.buf == str); + test_pass(vb.used == 10); + test_pass(vb.buf[vb.used] == '\0'); + test_pass(str[vb.used] == '\0'); + test_str(vb.buf, ==, "1234567890"); + test_str(str, ==, "1234567890"); + + varbuf_add_buf(&vb, "abcde", 5); + str = varbuf_get_str(&vb); + test_pass(vb.buf == str); + test_pass(vb.used == 15); + test_pass(vb.buf[vb.used] == '\0'); + test_pass(str[vb.used] == '\0'); + test_str(vb.buf, ==, "1234567890abcde"); + test_str(str, ==, "1234567890abcde"); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_printf(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 5); + + /* Test normal format printing. */ + varbuf_printf(&vb, "format %s number %d", "string", 10); + test_pass(vb.used == strlen("format string number 10")); + test_pass(vb.size >= vb.used); + test_str(vb.buf, ==, "format string number 10"); + + varbuf_reset(&vb); + + /* Test concatenated format printing. */ + varbuf_printf(&vb, "format %s number %d", "string", 10); + varbuf_printf(&vb, " extra %s", "string"); + test_pass(vb.used == strlen("format string number 10 extra string")); + test_pass(vb.size >= vb.used); + test_str(vb.buf, ==, "format string number 10 extra string"); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_reset(void) +{ + struct varbuf vb; + + varbuf_init(&vb, 10); + + varbuf_add_buf(&vb, "1234567890", 10); + + varbuf_reset(&vb); + test_pass(vb.used == 0); + test_pass(vb.size >= vb.used); + + varbuf_add_buf(&vb, "abcdefghijklmno", 15); + test_pass(vb.used == 15); + test_pass(vb.size >= vb.used); + test_mem(vb.buf, ==, "abcdefghijklmno", 15); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_snapshot(void) +{ + struct varbuf vb; + struct varbuf_state vbs; + + varbuf_init(&vb, 0); + + test_pass(vb.used == 0); + varbuf_snapshot(&vb, &vbs); + test_pass(vb.used == 0); + test_pass(vb.used == vbs.used); + test_pass(varbuf_rollback_len(&vbs) == 0); + test_str(varbuf_rollback_start(&vbs), ==, ""); + + varbuf_add_buf(&vb, "1234567890", 10); + varbuf_end_str(&vb); + test_pass(vb.used == 10); + test_pass(varbuf_rollback_len(&vbs) == 10); + test_str(varbuf_rollback_start(&vbs), ==, "1234567890"); + varbuf_rollback(&vbs); + varbuf_end_str(&vb); + test_pass(vb.used == 0); + test_pass(varbuf_rollback_len(&vbs) == 0); + test_str(varbuf_rollback_start(&vbs), ==, ""); + + varbuf_add_buf(&vb, "1234567890", 10); + varbuf_end_str(&vb); + test_pass(vb.used == 10); + test_pass(varbuf_rollback_len(&vbs) == 10); + test_str(varbuf_rollback_start(&vbs), ==, "1234567890"); + varbuf_snapshot(&vb, &vbs); + test_pass(vb.used == 10); + test_pass(varbuf_rollback_len(&vbs) == 0); + test_str(varbuf_rollback_start(&vbs), ==, ""); + + varbuf_add_buf(&vb, "1234567890", 10); + varbuf_end_str(&vb); + test_pass(vb.used == 20); + test_pass(varbuf_rollback_len(&vbs) == 10); + test_str(varbuf_rollback_start(&vbs), ==, "1234567890"); + varbuf_rollback(&vbs); + varbuf_end_str(&vb); + test_pass(vb.used == 10); + test_pass(varbuf_rollback_len(&vbs) == 0); + test_str(varbuf_rollback_start(&vbs), ==, ""); + + varbuf_destroy(&vb); +} + +static void +test_varbuf_detach(void) +{ + struct varbuf vb; + char *str; + + varbuf_init(&vb, 0); + + varbuf_add_buf(&vb, "1234567890", 10); + + str = varbuf_detach(&vb); + + test_mem(str, ==, "1234567890", 10); + test_pass(vb.used == 0); + test_pass(vb.size == 0); + test_pass(vb.buf == NULL); + + free(str); +} + +TEST_ENTRY(test) +{ + test_plan(172); + + test_varbuf_init(); + test_varbuf_prealloc(); + test_varbuf_new(); + test_varbuf_grow(); + test_varbuf_trunc(); + test_varbuf_set(); + test_varbuf_add_varbuf(); + test_varbuf_add_buf(); + test_varbuf_add_str(); + test_varbuf_add_char(); + test_varbuf_dup_char(); + test_varbuf_map_char(); + test_varbuf_add_dir(); + test_varbuf_end_str(); + test_varbuf_get_str(); + test_varbuf_printf(); + test_varbuf_reset(); + test_varbuf_snapshot(); + test_varbuf_detach(); + + /* TODO: Complete. */ +} diff --git a/lib/dpkg/t/t-version.c b/lib/dpkg/t/t-version.c new file mode 100644 index 0000000..6a771ca --- /dev/null +++ b/lib/dpkg/t/t-version.c @@ -0,0 +1,308 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-version.c - test version handling + * + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +static void +test_version_blank(void) +{ + struct dpkg_version a; + + dpkg_version_blank(&a); + test_pass(a.epoch == 0); + test_pass(a.version == NULL); + test_pass(a.revision == NULL); +} + +static void +test_version_is_informative(void) +{ + struct dpkg_version a; + + dpkg_version_blank(&a); + test_fail(dpkg_version_is_informative(&a)); + + a.epoch = 1; + test_pass(dpkg_version_is_informative(&a)); + + dpkg_version_blank(&a); + a.version = "1"; + test_pass(dpkg_version_is_informative(&a)); + + dpkg_version_blank(&a); + a.revision = "1"; + test_pass(dpkg_version_is_informative(&a)); +} + +static void +test_version_compare(void) +{ + struct dpkg_version a, b; + + dpkg_version_blank(&a); + dpkg_version_blank(&b); + test_pass(dpkg_version_compare(&a, &b) == 0); + + a.epoch = 1; + b.epoch = 2; + test_fail(dpkg_version_compare(&a, &b) == 0); + + a = DPKG_VERSION_OBJECT(0, "1", "1"); + b = DPKG_VERSION_OBJECT(0, "2", "1"); + test_fail(dpkg_version_compare(&a, &b) == 0); + + a = DPKG_VERSION_OBJECT(0, "1", "1"); + b = DPKG_VERSION_OBJECT(0, "1", "2"); + test_fail(dpkg_version_compare(&a, &b) == 0); + + /* Test for version equality. */ + a = b = DPKG_VERSION_OBJECT(0, "0", "0"); + test_pass(dpkg_version_compare(&a, &b) == 0); + + a = DPKG_VERSION_OBJECT(0, "0", "00"); + b = DPKG_VERSION_OBJECT(0, "00", "0"); + test_pass(dpkg_version_compare(&a, &b) == 0); + + a = b = DPKG_VERSION_OBJECT(1, "2", "3"); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test for epoch difference. */ + a = DPKG_VERSION_OBJECT(0, "0", "0"); + b = DPKG_VERSION_OBJECT(1, "0", "0"); + test_pass(dpkg_version_compare(&a, &b) < 0); + test_pass(dpkg_version_compare(&b, &a) > 0); + + /* Test for version component difference. */ + a = DPKG_VERSION_OBJECT(0, "a", "0"); + b = DPKG_VERSION_OBJECT(0, "b", "0"); + test_pass(dpkg_version_compare(&a, &b) < 0); + test_pass(dpkg_version_compare(&b, &a) > 0); + + /* Test for revision component difference. */ + a = DPKG_VERSION_OBJECT(0, "0", "a"); + b = DPKG_VERSION_OBJECT(0, "0", "b"); + test_pass(dpkg_version_compare(&a, &b) < 0); + test_pass(dpkg_version_compare(&b, &a) > 0); + + /* TODO: Complete. */ +} + +static void +test_version_relate(void) +{ + struct dpkg_version a, b; + + dpkg_version_blank(&a); + dpkg_version_blank(&b); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_NONE, &b)); + + a = DPKG_VERSION_OBJECT(0, "1", "1"); + b = DPKG_VERSION_OBJECT(0, "1", "1"); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_LT, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_LE, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_GT, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_GE, &b)); + + a = DPKG_VERSION_OBJECT(0, "1", "1"); + b = DPKG_VERSION_OBJECT(0, "2", "1"); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_LT, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_LE, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_GT, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_GE, &b)); + + a = DPKG_VERSION_OBJECT(0, "2", "1"); + b = DPKG_VERSION_OBJECT(0, "1", "1"); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_LT, &b)); + test_fail(dpkg_version_relate(&a, DPKG_RELATION_LE, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_GT, &b)); + test_pass(dpkg_version_relate(&a, DPKG_RELATION_GE, &b)); +} + +static void +test_version_parse(void) +{ + struct dpkg_error err; + struct dpkg_version a, b; + const char *p; + char *verstr; + + /* Test 0 versions. */ + dpkg_version_blank(&a); + b = DPKG_VERSION_OBJECT(0, "0", ""); + + test_pass(parseversion(&a, "0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + test_pass(parseversion(&a, "0:0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(0, "0", "0"); + test_pass(parseversion(&a, "0:0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(0, "0.0", "0.0"); + test_pass(parseversion(&a, "0:0.0-0.0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test epoched versions. */ + b = DPKG_VERSION_OBJECT(1, "0", ""); + test_pass(parseversion(&a, "1:0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(5, "1", ""); + test_pass(parseversion(&a, "5:1", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test multiple hyphens. */ + b = DPKG_VERSION_OBJECT(0, "0-0", "0"); + test_pass(parseversion(&a, "0:0-0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(0, "0-0-0", "0"); + test_pass(parseversion(&a, "0:0-0-0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test multiple colons. */ + b = DPKG_VERSION_OBJECT(0, "0:0", "0"); + test_pass(parseversion(&a, "0:0:0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(0, "0:0:0", "0"); + test_pass(parseversion(&a, "0:0:0:0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test multiple hyphens and colons. */ + b = DPKG_VERSION_OBJECT(0, "0:0-0", "0"); + test_pass(parseversion(&a, "0:0:0-0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + b = DPKG_VERSION_OBJECT(0, "0-0:0", "0"); + test_pass(parseversion(&a, "0:0-0:0-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test valid characters in upstream version. */ + b = DPKG_VERSION_OBJECT(0, "09azAZ.-+~:", "0"); + test_pass(parseversion(&a, "0:09azAZ.-+~:-0", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test valid characters in revision. */ + b = DPKG_VERSION_OBJECT(0, "0", "azAZ09.+~"); + test_pass(parseversion(&a, "0:0-azAZ09.+~", NULL) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test version with leading and trailing spaces. */ + b = DPKG_VERSION_OBJECT(0, "0", "1"); + test_pass(parseversion(&a, " 0:0-1", &err) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + test_pass(parseversion(&a, "0:0-1 ", &err) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + test_pass(parseversion(&a, " 0:0-1 ", &err) == 0); + test_pass(dpkg_version_compare(&a, &b) == 0); + + /* Test empty version. */ + test_pass(parseversion(&a, "", &err) != 0); + test_error(err); + test_pass(parseversion(&a, " ", &err) != 0); + test_error(err); + + /* Test empty upstream version after epoch. */ + test_fail(parseversion(&a, "0:", &err) == 0); + test_error(err); + + /* Test empty epoch in version. */ + test_fail(parseversion(&a, ":1.0", &err) == 0); + test_error(err); + + /* Test empty revision in version. */ + test_fail(parseversion(&a, "1.0-", &err) == 0); + test_error(err); + + /* Test version with embedded spaces. */ + test_fail(parseversion(&a, "0:0 0-1", &err) == 0); + test_error(err); + + /* Test version with negative epoch. */ + test_fail(parseversion(&a, "-1:0-1", &err) == 0); + test_error(err); + + /* Test version with huge epoch. */ + test_fail(parseversion(&a, "999999999999999999999999:0-1", &err) == 0); + test_error(err); + + /* Test invalid characters in epoch. */ + test_fail(parseversion(&a, "a:0-0", &err) == 0); + test_error(err); + test_fail(parseversion(&a, "A:0-0", &err) == 0); + test_error(err); + + /* Test invalid empty upstream version. */ + test_fail(parseversion(&a, "-0", &err) == 0); + test_error(err); + test_fail(parseversion(&a, "0:-0", &err) == 0); + test_error(err); + + /* Test upstream version not starting with a digit */ + test_fail(parseversion(&a, "0:abc3-0", &err) == 0); + test_warn(err); + + /* Test invalid characters in upstream version. */ + verstr = test_alloc(strdup("0:0a-0")); + for (p = "!#@$%&/|\\<>()[]{};,_=*^'"; *p; p++) { + verstr[3] = *p; + test_fail(parseversion(&a, verstr, &err) == 0); + test_warn(err); + } + free(verstr); + + /* Test invalid characters in revision. */ + test_fail(parseversion(&a, "0:0-0:0", &err) == 0); + test_warn(err); + + verstr = test_alloc(strdup("0:0-0")); + for (p = "!#@$%&/|\\<>()[]{}:;,_=*^'"; *p; p++) { + verstr[4] = *p; + test_fail(parseversion(&a, verstr, &err) == 0); + test_warn(err); + } + free(verstr); + + /* TODO: Complete. */ +} + +TEST_ENTRY(test) +{ + test_plan(196); + + test_version_blank(); + test_version_is_informative(); + test_version_compare(); + test_version_relate(); + test_version_parse(); +} diff --git a/lib/dpkg/tarfn.c b/lib/dpkg/tarfn.c new file mode 100644 index 0000000..d999db6 --- /dev/null +++ b/lib/dpkg/tarfn.c @@ -0,0 +1,605 @@ +/* + * libdpkg - Debian packaging suite library routines + * tarfn.c - tar archive extraction functions + * + * Copyright © 1995 Bruce Perens + * Copyright © 2007-2011, 2013-2017 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#if HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/error.h> +#include <dpkg/tarfn.h> + +#define TAR_MAGIC_USTAR "ustar\0" "00" +#define TAR_MAGIC_GNU "ustar " " \0" + +#define TAR_TYPE_SIGNED(t) (!((t)0 < (t)-1)) + +#define TAR_TYPE_MIN(t) \ + (TAR_TYPE_SIGNED(t) ? \ + ~(t)TAR_TYPE_MAX(t) : \ + (t)0) +#define TAR_TYPE_MAX(t) \ + (TAR_TYPE_SIGNED(t) ? \ + ((((t)1 << (sizeof(t) * 8 - 2)) - 1) * 2 + 1) : \ + ~(t)0) + +#define TAR_ATOUL(str, type) \ + (type)tar_atoul(str, sizeof(str), TAR_TYPE_MAX(type)) +#define TAR_ATOSL(str, type) \ + (type)tar_atosl(str, sizeof(str), TAR_TYPE_MIN(type), TAR_TYPE_MAX(type)) + +struct tar_header { + char name[100] DPKG_ATTR_NONSTRING; + char mode[8] DPKG_ATTR_NONSTRING; + char uid[8] DPKG_ATTR_NONSTRING; + char gid[8] DPKG_ATTR_NONSTRING; + char size[12] DPKG_ATTR_NONSTRING; + char mtime[12] DPKG_ATTR_NONSTRING; + char checksum[8] DPKG_ATTR_NONSTRING; + char linkflag; + char linkname[100] DPKG_ATTR_NONSTRING; + + /* Only valid on ustar and gnu. */ + char magic[8] DPKG_ATTR_NONSTRING; + char user[32] DPKG_ATTR_NONSTRING; + char group[32] DPKG_ATTR_NONSTRING; + char devmajor[8] DPKG_ATTR_NONSTRING; + char devminor[8] DPKG_ATTR_NONSTRING; + + /* Only valid on ustar. */ + char prefix[155] DPKG_ATTR_NONSTRING; +}; + +static inline uintmax_t +tar_ret_errno(int err, uintmax_t ret) +{ + errno = err; + return ret; +} + +/** + * Convert an ASCII octal string to an intmax_t. + */ +static uintmax_t +tar_atol8(const char *s, size_t size) +{ + const char *end = s + size; + uintmax_t n = 0; + + /* Old implementations might precede the value with spaces. */ + while (s < end && *s == ' ') + s++; + + if (s == end) + return tar_ret_errno(EINVAL, 0); + + while (s < end) { + if (*s == '\0' || *s == ' ') + break; + if (*s < '0' || *s > '7') + return tar_ret_errno(ERANGE, 0); + n = (n * 010) + (*s++ - '0'); + } + + while (s < end) { + if (*s != '\0' && *s != ' ') + return tar_ret_errno(EINVAL, 0); + s++; + } + + return tar_ret_errno(0, n); +} + +/** + * Convert a base-256 two-complement number to an intmax_t. + */ +static uintmax_t +tar_atol256(const char *s, size_t size, intmax_t min, uintmax_t max) +{ + uintmax_t n = 0; + unsigned char c; + int sign; + + /* The encoding always sets the first bit to one, so that it can be + * distinguished from the ASCII encoding. For positive numbers we + * need to reset it. For negative numbers we initialize n to -1. */ + c = *s++; + if (c == 0x80) + c = 0; + else + n = ~(uintmax_t)0; + sign = c; + + /* Check for overflows. */ + while (size > sizeof(uintmax_t)) { + if (c != sign) + return tar_ret_errno(ERANGE, sign ? (uintmax_t)min : max); + c = *s++; + size--; + } + + if ((c & 0x80) != (sign & 0x80)) + return tar_ret_errno(ERANGE, sign ? (uintmax_t)min : max); + + for (;;) { + n = (n << 8) | c; + if (--size == 0) + break; + c = *s++; + } + + return tar_ret_errno(0, n); +} + +static uintmax_t +tar_atol(const char *s, size_t size, intmax_t min, uintmax_t max) +{ + const unsigned char *a = (const unsigned char *)s; + + /* Check if it is a long two-complement base-256 number, positive or + * negative. */ + if (*a == 0xff || *a == 0x80) + return tar_atol256(s, size, min, max); + else + return tar_atol8(s, size); +} + +uintmax_t +tar_atoul(const char *s, size_t size, uintmax_t max) +{ + uintmax_t n = tar_atol(s, size, 0, UINTMAX_MAX); + + if (n > max) + return tar_ret_errno(ERANGE, UINTMAX_MAX); + + return n; +} + +intmax_t +tar_atosl(const char *s, size_t size, intmax_t min, intmax_t max) +{ + intmax_t n = tar_atol(s, size, INTMAX_MIN, INTMAX_MAX); + + if (n < min) + return tar_ret_errno(ERANGE, INTMAX_MIN); + if (n > max) + return tar_ret_errno(ERANGE, INTMAX_MAX); + + return n; +} + +static char * +tar_header_get_prefix_name(struct tar_header *h) +{ + struct varbuf path = VARBUF_INIT; + + varbuf_add_strn(&path, h->prefix, sizeof(h->prefix)); + varbuf_add_char(&path, '/'); + varbuf_add_strn(&path, h->name, sizeof(h->name)); + varbuf_end_str(&path); + + return path.buf; +} + +static mode_t +tar_header_get_unix_mode(struct tar_header *h) +{ + mode_t mode; + enum tar_filetype type; + + type = (enum tar_filetype)h->linkflag; + + switch (type) { + case TAR_FILETYPE_FILE0: + case TAR_FILETYPE_FILE: + case TAR_FILETYPE_HARDLINK: + mode = S_IFREG; + break; + case TAR_FILETYPE_SYMLINK: + mode = S_IFLNK; + break; + case TAR_FILETYPE_DIR: + mode = S_IFDIR; + break; + case TAR_FILETYPE_CHARDEV: + mode = S_IFCHR; + break; + case TAR_FILETYPE_BLOCKDEV: + mode = S_IFBLK; + break; + case TAR_FILETYPE_FIFO: + mode = S_IFIFO; + break; + default: + mode = 0; + break; + } + + mode |= TAR_ATOUL(h->mode, mode_t) & 07777; + + return mode; +} + +static long +tar_header_checksum(struct tar_header *h) +{ + unsigned char *s = (unsigned char *)h; + unsigned int i; + const size_t checksum_offset = offsetof(struct tar_header, checksum); + long sum; + + /* Treat checksum field as all blank. */ + sum = ' ' * sizeof(h->checksum); + + for (i = checksum_offset; i > 0; i--) + sum += *s++; + + /* Skip the real checksum field. */ + s += sizeof(h->checksum); + + for (i = TARBLKSZ - checksum_offset - sizeof(h->checksum); i > 0; i--) + sum += *s++; + + return sum; +} + +static int +tar_header_decode(struct tar_header *h, struct tar_entry *d, struct dpkg_error *err) +{ + long checksum; + + errno = 0; + + if (memcmp(h->magic, TAR_MAGIC_GNU, 6) == 0) + d->format = TAR_FORMAT_GNU; + else if (memcmp(h->magic, TAR_MAGIC_USTAR, 6) == 0) + d->format = TAR_FORMAT_USTAR; + else + d->format = TAR_FORMAT_OLD; + + d->type = (enum tar_filetype)h->linkflag; + if (d->type == TAR_FILETYPE_FILE0) + d->type = TAR_FILETYPE_FILE; + + /* Concatenate prefix and name to support ustar style long names. */ + if (d->format == TAR_FORMAT_USTAR && h->prefix[0] != '\0') + d->name = tar_header_get_prefix_name(h); + else + d->name = m_strndup(h->name, sizeof(h->name)); + d->linkname = m_strndup(h->linkname, sizeof(h->linkname)); + d->stat.mode = tar_header_get_unix_mode(h); + /* Even though off_t is signed, we use an unsigned parser here because + * negative offsets are not allowed. */ + d->size = TAR_ATOUL(h->size, off_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header size field")); + d->mtime = TAR_ATOSL(h->mtime, time_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header mtime field")); + + if (d->type == TAR_FILETYPE_CHARDEV || d->type == TAR_FILETYPE_BLOCKDEV) + d->dev = makedev(TAR_ATOUL(h->devmajor, dev_t), + TAR_ATOUL(h->devminor, dev_t)); + else + d->dev = makedev(0, 0); + + if (*h->user) + d->stat.uname = m_strndup(h->user, sizeof(h->user)); + else + d->stat.uname = NULL; + d->stat.uid = TAR_ATOUL(h->uid, uid_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header uid field")); + + if (*h->group) + d->stat.gname = m_strndup(h->group, sizeof(h->group)); + else + d->stat.gname = NULL; + d->stat.gid = TAR_ATOUL(h->gid, gid_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header gid field")); + + checksum = tar_atol8(h->checksum, sizeof(h->checksum)); + if (errno) + return dpkg_put_errno(err, _("invalid tar header checksum field")); + + if (tar_header_checksum(h) != checksum) + return dpkg_put_error(err, _("invalid tar header checksum")); + + return 0; +} + +/** + * Decode a GNU longlink or longname from the tar archive. + * + * The way the GNU long{link,name} stuff works is like this: + * + * - The first header is a “dummy” header that contains the size of the + * filename. + * - The next N headers contain the filename. + * - After the headers with the filename comes the “real” header with a + * bogus name or link. + */ +static int +tar_gnu_long(struct tar_archive *tar, struct tar_entry *te, char **longp) +{ + char buf[TARBLKSZ]; + char *bp; + int status = 0; + int long_read; + + free(*longp); + *longp = bp = m_malloc(te->size + 1); + + for (long_read = te->size; long_read > 0; long_read -= TARBLKSZ) { + int copysize; + + status = tar->ops->read(tar, buf, TARBLKSZ); + if (status == TARBLKSZ) + status = 0; + else { + /* Read partial header record? */ + if (status > 0) { + errno = 0; + status = dpkg_put_error(&tar->err, + _("partially read tar header")); + } + + /* If we didn't get TARBLKSZ bytes read, punt. */ + break; + } + + copysize = min(long_read, TARBLKSZ); + memcpy(bp, buf, copysize); + bp += copysize; + } + *bp = '\0'; + + return status; +} + +static void +tar_entry_copy(struct tar_entry *dst, struct tar_entry *src) +{ + memcpy(dst, src, sizeof(struct tar_entry)); + + dst->name = m_strdup(src->name); + dst->linkname = m_strdup(src->linkname); + + if (src->stat.uname) + dst->stat.uname = m_strdup(src->stat.uname); + if (src->stat.gname) + dst->stat.gname = m_strdup(src->stat.gname); +} + +static void +tar_entry_destroy(struct tar_entry *te) +{ + free(te->name); + free(te->linkname); + free(te->stat.uname); + free(te->stat.gname); + + memset(te, 0, sizeof(*te)); +} + +struct tar_symlink_entry { + struct tar_symlink_entry *next; + struct tar_entry h; +}; + +/** + * Update the tar entry from system information. + * + * Normalize UID and GID relative to the current system. + */ +void +tar_entry_update_from_system(struct tar_entry *te) +{ + struct passwd *passwd; + struct group *group; + + if (te->stat.uname) { + passwd = getpwnam(te->stat.uname); + if (passwd) + te->stat.uid = passwd->pw_uid; + } + if (te->stat.gname) { + group = getgrnam(te->stat.gname); + if (group) + te->stat.gid = group->gr_gid; + } +} + +int +tar_extractor(struct tar_archive *tar) +{ + int status; + char buffer[TARBLKSZ]; + struct tar_entry h; + + char *next_long_name, *next_long_link; + struct tar_symlink_entry *symlink_head, *symlink_tail, *symlink_node; + + next_long_name = NULL; + next_long_link = NULL; + symlink_tail = symlink_head = NULL; + + h.name = NULL; + h.linkname = NULL; + h.stat.uname = NULL; + h.stat.gname = NULL; + + while ((status = tar->ops->read(tar, buffer, TARBLKSZ)) == TARBLKSZ) { + int name_len; + + if (tar_header_decode((struct tar_header *)buffer, &h, &tar->err) < 0) { + if (h.name[0] == '\0') { + /* The checksum failed on the terminating + * End Of Tape block entry of zeros. */ + dpkg_error_destroy(&tar->err); + + /* End Of Tape. */ + status = 0; + } else { + status = -1; + } + tar_entry_destroy(&h); + break; + } + if (h.type != TAR_FILETYPE_GNU_LONGLINK && + h.type != TAR_FILETYPE_GNU_LONGNAME) { + if (next_long_name) { + free(h.name); + h.name = next_long_name; + } + + if (next_long_link) { + free(h.linkname); + h.linkname = next_long_link; + } + + next_long_link = NULL; + next_long_name = NULL; + } + + if (h.name[0] == '\0') { + status = dpkg_put_error(&tar->err, + _("invalid tar header with empty name field")); + errno = 0; + tar_entry_destroy(&h); + break; + } + + name_len = strlen(h.name); + + switch (h.type) { + case TAR_FILETYPE_FILE: + /* Compatibility with pre-ANSI ustar. */ + if (h.name[name_len - 1] != '/') { + status = tar->ops->extract_file(tar, &h); + break; + } + /* Else, fall through. */ + case TAR_FILETYPE_DIR: + if (h.name[name_len - 1] == '/') { + h.name[name_len - 1] = '\0'; + } + status = tar->ops->mkdir(tar, &h); + break; + case TAR_FILETYPE_HARDLINK: + status = tar->ops->link(tar, &h); + break; + case TAR_FILETYPE_SYMLINK: + symlink_node = m_malloc(sizeof(*symlink_node)); + symlink_node->next = NULL; + tar_entry_copy(&symlink_node->h, &h); + + if (symlink_head) + symlink_tail->next = symlink_node; + else + symlink_head = symlink_node; + symlink_tail = symlink_node; + status = 0; + break; + case TAR_FILETYPE_CHARDEV: + case TAR_FILETYPE_BLOCKDEV: + case TAR_FILETYPE_FIFO: + status = tar->ops->mknod(tar, &h); + break; + case TAR_FILETYPE_GNU_LONGLINK: + status = tar_gnu_long(tar, &h, &next_long_link); + break; + case TAR_FILETYPE_GNU_LONGNAME: + status = tar_gnu_long(tar, &h, &next_long_name); + break; + case TAR_FILETYPE_GNU_VOLUME: + case TAR_FILETYPE_GNU_MULTIVOL: + case TAR_FILETYPE_GNU_SPARSE: + case TAR_FILETYPE_GNU_DUMPDIR: + status = dpkg_put_error(&tar->err, + _("unsupported GNU tar header type '%c'"), + h.type); + errno = 0; + break; + case TAR_FILETYPE_SOLARIS_EXTENDED: + case TAR_FILETYPE_SOLARIS_ACL: + status = dpkg_put_error(&tar->err, + _("unsupported Solaris tar header type '%c'"), + h.type); + errno = 0; + break; + case TAR_FILETYPE_PAX_GLOBAL: + case TAR_FILETYPE_PAX_EXTENDED: + status = dpkg_put_error(&tar->err, + _("unsupported PAX tar header type '%c'"), + h.type); + errno = 0; + break; + default: + status = dpkg_put_error(&tar->err, + _("unknown tar header type '%c'"), + h.type); + errno = 0; + } + tar_entry_destroy(&h); + if (status != 0) + /* Pass on status from coroutine. */ + break; + } + + while (symlink_head) { + symlink_node = symlink_head->next; + if (status == 0) + status = tar->ops->symlink(tar, &symlink_head->h); + tar_entry_destroy(&symlink_head->h); + free(symlink_head); + symlink_head = symlink_node; + } + /* Make sure we free the long names, in case of a bogus or truncated + * tar archive with long entries not followed by a normal entry. */ + free(next_long_name); + free(next_long_link); + + if (status > 0) { + status = dpkg_put_error(&tar->err, + _("partially read tar header")); + errno = 0; + } + + /* Return whatever I/O function returned. */ + return status; +} diff --git a/lib/dpkg/tarfn.h b/lib/dpkg/tarfn.h new file mode 100644 index 0000000..c03a593 --- /dev/null +++ b/lib/dpkg/tarfn.h @@ -0,0 +1,134 @@ +/* + * libdpkg - Debian packaging suite library routines + * tarfn.h - tar archive extraction functions + * + * Copyright © 1995 Bruce Perens + * Copyright © 2009-2014, 2017 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_TARFN_H +#define LIBDPKG_TARFN_H + +#include <sys/types.h> + +#include <stdint.h> + +#include <dpkg/error.h> +#include <dpkg/file.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup tar Tar archive handling + * @ingroup dpkg-public + * @{ + */ + +#define TARBLKSZ 512 + +enum tar_format { + TAR_FORMAT_UNKNOWN, + TAR_FORMAT_OLD, + TAR_FORMAT_GNU, + TAR_FORMAT_USTAR, + TAR_FORMAT_PAX, +}; + +enum tar_filetype { + /** For compatibility with decades-old bug. */ + TAR_FILETYPE_FILE0 = '\0', + TAR_FILETYPE_FILE = '0', + TAR_FILETYPE_HARDLINK = '1', + TAR_FILETYPE_SYMLINK = '2', + TAR_FILETYPE_CHARDEV = '3', + TAR_FILETYPE_BLOCKDEV = '4', + TAR_FILETYPE_DIR = '5', + TAR_FILETYPE_FIFO = '6', + TAR_FILETYPE_CONTIG = '7', + TAR_FILETYPE_GNU_LONGLINK = 'K', + TAR_FILETYPE_GNU_LONGNAME = 'L', + TAR_FILETYPE_GNU_VOLUME = 'V', + TAR_FILETYPE_GNU_MULTIVOL = 'M', + TAR_FILETYPE_GNU_DUMPDIR = 'D', + TAR_FILETYPE_GNU_SPARSE = 'S', + TAR_FILETYPE_PAX_GLOBAL = 'g', + TAR_FILETYPE_PAX_EXTENDED = 'x', + TAR_FILETYPE_SOLARIS_EXTENDED = 'X', + TAR_FILETYPE_SOLARIS_ACL = 'A', +}; + +struct tar_entry { + /** Tar entry format. */ + enum tar_format format; + /** File type. */ + enum tar_filetype type; + /** File name. */ + char *name; + /** Symlink or hardlink name. */ + char *linkname; + /** File size. */ + off_t size; + /** Last-modified time. */ + intmax_t mtime; + /** Special device for mknod(). */ + dev_t dev; + + struct file_stat stat; +}; + +struct tar_archive; + +typedef int tar_read_func(struct tar_archive *tar, char *buffer, int length); +typedef int tar_make_func(struct tar_archive *tar, struct tar_entry *h); + +struct tar_operations { + tar_read_func *read; + + tar_make_func *extract_file; + tar_make_func *link; + tar_make_func *symlink; + tar_make_func *mkdir; + tar_make_func *mknod; +}; + +struct tar_archive { + /* Global tar archive error. */ + struct dpkg_error err; + + /** Tar archive format. */ + enum tar_format format; + + /* Operation functions and context. */ + const struct tar_operations *ops; + void *ctx; +}; + +uintmax_t +tar_atoul(const char *s, size_t size, uintmax_t max); +intmax_t +tar_atosl(const char *s, size_t size, intmax_t min, intmax_t max); + +void +tar_entry_update_from_system(struct tar_entry *te); + +int +tar_extractor(struct tar_archive *tar); + +/** @} */ + +DPKG_END_DECLS + +#endif diff --git a/lib/dpkg/test.h b/lib/dpkg/test.h new file mode 100644 index 0000000..7bc0af1 --- /dev/null +++ b/lib/dpkg/test.h @@ -0,0 +1,190 @@ +/* + * libdpkg - Debian packaging suite library routines + * test.h - test suite support + * + * Copyright © 2009-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_TEST_H +#define LIBDPKG_TEST_H + +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#ifndef TEST_MAIN_CTOR +#include <dpkg/ehandle.h> +#define TEST_MAIN_CTOR push_error_context() +#define TEST_MAIN_DTOR pop_error_context(ehflag_normaltidy) +#endif + +DPKG_BEGIN_DECLS + +/** + * @defgroup dpkg_test Test suite support + * @ingroup dpkg-internal + * @{ + */ + +#define test_bail(reason) \ + do { \ + printf("Bail out! %s\n", (reason)); \ + exit(255); \ + } while (0) + +#define test_xstringify(str) \ + #str +#define test_stringify(str) \ + test_xstringify(str) + +static inline void * +test_alloc(void *ptr, const char *reason) +{ + if (ptr == DPKG_NULL) + test_bail(reason); + return ptr; +} + +#define test_alloc(ptr) \ + test_alloc((ptr), "cannot allocate memory for " #ptr " in " __FILE__ ":" test_stringify(__LINE__)) + +#define test_try(jmp) \ + push_error_context_jump(&(jmp), DPKG_NULL, "test try"); \ + if (!setjmp((jmp))) +#define test_catch \ + else +#define test_finally \ + pop_error_context(ehflag_normaltidy); + +static inline const char * +test_get_envdir(const char *envvar) +{ + const char *envdir = getenv(envvar); + return envdir ? envdir : "."; +} + +#define test_get_srcdir() \ + test_get_envdir("srcdir") +#define test_get_builddir() \ + test_get_envdir("builddir") + +static inline char * +test_data_file(const char *filename) +{ + char *pathname; + int rc; + + rc = asprintf(&pathname, "%s/t/data/%s", test_get_srcdir(), filename); + if (rc < 0) + test_bail("cannot allocate data filename"); + + return pathname; +} + +static inline bool +test_is_verbose(void) +{ + const char *verbose = getenv("TEST_VERBOSE"); + return verbose != DPKG_NULL && strcmp(verbose, "1") == 0; +} + +#ifndef TEST_OMIT_VARIABLES +static int test_id = 1; +static int test_skip_code; +static const char *test_skip_prefix; +static const char *test_skip_reason; +#endif + +#define test_plan(n) \ + printf("1..%d\n", n); + +#define test_skip_all(reason) \ + do { \ + printf("1..0 # SKIP %s\n", (reason)); \ + exit(0); \ + } while (0) +#define test_skip(reason) \ + printf("ok %d # SKIP %s\n", test_id++, (reason)) +#define test_skip_block(cond) \ + for (test_skip_prefix = " # SKIP ", \ + test_skip_reason = cond ? #cond : DPKG_NULL, \ + test_skip_code = 1; \ + test_skip_prefix; \ + test_skip_prefix = test_skip_reason = DPKG_NULL, \ + test_skip_code = 0) + +#define test_todo(a, reason, desc) \ + do { \ + test_skip_prefix = " # TODO "; \ + test_skip_reason = reason; \ + test_case(a, "%s", desc); \ + test_skip_prefix = test_skip_reason = DPKG_NULL; \ + } while(0) +#define test_todo_block(reason) \ + for (test_skip_prefix = " # TODO ", test_skip_reason = reason; \ + test_skip_prefix; \ + test_skip_prefix = test_skip_reason = DPKG_NULL) + +#define test_case(a, fmt, ...) \ + printf("%sok %d - " fmt "%s%s\n", \ + test_skip_code || (a) ? "" : "not ", \ + test_id++, __VA_ARGS__, \ + test_skip_reason ? test_skip_prefix : "", \ + test_skip_reason ? test_skip_reason : "") + +#define test_pass(a) \ + test_case((a), "pass %s", #a) +#define test_fail(a) \ + test_case(!(a), "fail %s", #a) +#define test_str(a, op, b) \ + test_case(strcmp((a), (b)) op 0, "strcmp '%s' %s '%s'", a, #op, b) +#define test_mem(a, op, b, size) \ + test_case(memcmp((a), (b), (size)) op 0, "memcmp %p %s %p", a, #op, b) + +/* Specific test macros. */ +#define test_warn(e) \ + do { \ + test_pass((e).type == DPKG_MSG_WARN); \ + dpkg_error_destroy(&(e)); \ + } while (0) +#define test_error(e) \ + do { \ + test_pass((e).type == DPKG_MSG_ERROR); \ + dpkg_error_destroy(&(e)); \ + } while (0) + +/** @} */ + +DPKG_END_DECLS + +#define TEST_ENTRY(name) \ +static void name(void); \ +int \ +main(int argc, char **argv) \ +{ \ + setvbuf(stdout, DPKG_NULL, _IOLBF, 0); \ + \ + TEST_MAIN_CTOR; \ + name(); \ + TEST_MAIN_DTOR; \ + return 0; \ +} \ +static void \ +name(void) + +#endif diff --git a/lib/dpkg/treewalk.c b/lib/dpkg/treewalk.c new file mode 100644 index 0000000..4dc62b3 --- /dev/null +++ b/lib/dpkg/treewalk.c @@ -0,0 +1,542 @@ +/* + * libdpkg - Debian packaging suite library routines + * treewalk.c - directory tree walk support + * + * Copyright © 2013-2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/treewalk.h> + +/* treenode functions. */ + +typedef int treewalk_stat_func(const char *pathname, struct stat *st); + +struct treenode { + struct treenode *up; /* Parent dir. */ + struct treenode *next; /* Next node in dir. */ + struct treenode **down; /* Dir contents. */ + + char *pathname; /* Node pathname. */ + char *virtname; /* Node virtname. */ + char *name; /* Node name. */ + + struct stat *stat; /* Node metadata. */ + mode_t mode; + + size_t pathname_len; /* Node pathname length. */ + + size_t down_used; /* Number of used nodes in dir. */ + size_t down_size; /* Number of allocated nodes in dir. */ +}; + +static inline struct treenode * +treenode_alloc(void) +{ + return m_calloc(1, sizeof(struct treenode)); +} + +static struct treenode * +treenode_root_new(const char *rootdir) +{ + struct treenode *node; + + node = treenode_alloc(); + node->up = NULL; + node->pathname = m_strdup(rootdir); + node->pathname_len = strlen(node->pathname); + node->virtname = node->pathname + node->pathname_len; + node->name = strrchr(node->pathname, '/'); + if (node->name == NULL) + node->name = node->pathname; + else + node->name++; + + return node; +} + +static struct treenode * +treenode_node_new(struct treenode *root, struct treenode *dir, const char *name) +{ + struct treenode *node; + + node = treenode_alloc(); + node->up = dir; + + node->pathname = str_fmt("%s/%s", node->up->pathname, name); + node->pathname_len = strlen(node->pathname); + node->virtname = node->pathname + root->pathname_len + 1; + node->name = node->pathname + node->up->pathname_len + 1; + + return node; +} + +static void +treenode_stat(struct treenode *node, treewalk_stat_func *stat_func) +{ + if (node->stat) + return; + + node->stat = m_malloc(sizeof(*node->stat)); + + if (stat_func(node->pathname, node->stat) < 0) + ohshite(_("cannot stat pathname '%s'"), node->pathname); + + node->mode = node->stat->st_mode; +} + +static mode_t +dirent_to_mode_type(struct dirent *e) +{ +#ifdef _DIRENT_HAVE_D_TYPE + switch (e->d_type) { + case DT_REG: + return S_IFREG; + case DT_DIR: + return S_IFDIR; + case DT_LNK: + return S_IFLNK; + case DT_CHR: + return S_IFCHR; + case DT_BLK: + return S_IFBLK; + case DT_FIFO: + return S_IFIFO; + case DT_SOCK: + return S_IFSOCK; + case DT_UNKNOWN: + default: + return 0; + } +#else + return 0; +#endif +} + +static void +treenode_stat_shallow(struct treenode *node, struct dirent *e, + treewalk_stat_func *stat_func) +{ + mode_t mode; + + mode = dirent_to_mode_type(e); + if (mode == 0 || S_ISDIR(mode) || S_ISLNK(mode)) + treenode_stat(node, stat_func); + else + node->mode |= mode; +} + +const char * +treenode_get_name(struct treenode *node) +{ + return node->name; +} + +const char * +treenode_get_pathname(struct treenode *node) +{ + return node->pathname; +} + +const char * +treenode_get_virtname(struct treenode *node) +{ + return node->virtname; +} + +mode_t +treenode_get_mode(struct treenode *node) +{ + return node->mode; +} + +struct stat * +treenode_get_stat(struct treenode *node) +{ + treenode_stat(node, lstat); + return node->stat; +} + +struct treenode * +treenode_get_parent(struct treenode *node) +{ + return node->up; +} + +static inline bool +treenode_is_dir(struct treenode *node) +{ + return node && S_ISDIR(node->mode); +} + +static void +treenode_resize_down(struct treenode *node) +{ + size_t new_size; + + if (node->down_size) + node->down_size *= 2; + else if (node->stat->st_nlink > 4) + node->down_size = node->stat->st_nlink * 2; + else + node->down_size = 8; + + new_size = node->down_size * sizeof(*node); + node->down = m_realloc(node->down, new_size); +} + +static int +treenode_cmp(const void *a, const void *b) +{ + return strcmp((*(const struct treenode **)a)->name, + (*(const struct treenode **)b)->name); +} + +static void +treenode_sort_down(struct treenode *dir) +{ + size_t i; + + qsort(dir->down, dir->down_used, sizeof(struct treenode *), treenode_cmp); + + /* Relink the nodes. */ + for (i = 0; i < dir->down_used - 1; i++) + dir->down[i]->next = dir->down[i + 1]; + dir->down[i]->next = NULL; +} + +static void +treenode_fill_down(struct treenode *root, struct treenode *dir, + enum treewalk_options options) +{ + DIR *d; + struct dirent *e; + treewalk_stat_func *stat_func; + + if (options & TREEWALK_FOLLOW_LINKS) + stat_func = stat; + else + stat_func = lstat; + + d = opendir(dir->pathname); + if (!d) + ohshite(_("cannot open directory '%s'"), dir->pathname); + + while ((e = readdir(d)) != NULL) { + struct treenode *node; + + if (strcmp(e->d_name, ".") == 0 || + strcmp(e->d_name, "..") == 0) + continue; + + if (dir->down_used >= dir->down_size) + treenode_resize_down(dir); + + node = treenode_node_new(root, dir, e->d_name); + if (options & TREEWALK_FORCE_STAT) + treenode_stat(node, stat_func); + else + treenode_stat_shallow(node, e, stat_func); + + dir->down[dir->down_used] = node; + dir->down_used++; + } + + closedir(d); +} + +static void +treenode_free_node(struct treenode *node) +{ + free(node->pathname); + free(node->stat); + free(node); +} + +static void +treenode_free_down(struct treenode *node) +{ + size_t i; + + if (!node->down_size) + return; + + for (i = 0; i < node->down_used; i++) + treenode_free_node(node->down[i]); + free(node->down); +} + + +/* treeroot functions. */ + +struct treeroot { + struct treenode *rootnode; + + struct treenode *curdir; + struct treenode *curnode; + + enum treewalk_options options; + struct treewalk_funcs func; +}; + +static inline void +treeroot_set_curdir(struct treeroot *tree, struct treenode *node) +{ + tree->curdir = node; +} + +static inline void +treeroot_set_curnode(struct treeroot *tree, struct treenode *node) +{ + tree->curnode = node; +} + +static bool +treeroot_skip_node(struct treeroot *tree, struct treenode *node) +{ + if (tree->func.skip) + return tree->func.skip(node); + + return false; +} + +static void +treeroot_fill_node(struct treeroot *tree, struct treenode *dir) +{ + treenode_fill_down(tree->rootnode, dir, tree->options); +} + +static void +treeroot_sort_node(struct treeroot *tree, struct treenode *dir) +{ + static struct treenode *down_empty[] = { NULL, NULL }; + + if (dir->down_used == 0) + dir->down = down_empty; + else if (tree->func.sort) + tree->func.sort(dir); + else + treenode_sort_down(dir); +} + +static void +treeroot_visit_node(struct treeroot *tree, struct treenode *node) +{ + if (tree->func.visit == NULL) + return; + + if (!treeroot_skip_node(tree, node)) + tree->func.visit(node); +} + +/** + * Open a new tree to be walked. + * + * @param rootdir The root directory to start walking the tree. + * @param options The options specifying how to walk the tree. + * @param func The functions callbacks. + */ +struct treeroot * +treewalk_open(const char *rootdir, enum treewalk_options options, + const struct treewalk_funcs *func) +{ + struct treeroot *tree; + struct treenode *root; + + tree = m_malloc(sizeof(*tree)); + + tree->options = options; + if (func) + tree->func = *func; + else + tree->func = TREEWALK_OBJECT; + + root = treenode_root_new(rootdir); + treenode_stat(root, lstat); + + if (!treenode_is_dir(root)) + ohshit(_("treewalk root %s is not a directory"), rootdir); + + treeroot_set_curnode(tree, root); + tree->rootnode = tree->curdir = root; + + return tree; +} + +/** + * Return the current node. + * + * This function is only needed if you want to start walking the tree from + * the root node. With something like: + * + * @code + * struct treeroot *tree; + * struct treenode *node; + * + * tree = treewalk_open(...); + * for (node = treewalk_node(tree); node; node = treewalk_next(tree)) + * visitor(node); + * treewalk_close(tree); + * @endcode + * + * @param tree The tree structure. + */ +struct treenode * +treewalk_node(struct treeroot *tree) +{ + return tree->curnode; +} + +/** + * Return the next node. + * + * This function works basically as an iterator. And will return NULL when + * the whole tree has been traversed. When starting it will skip the root + * node, so you might want to use treewalk_node() to get that, otherwise + * you could use it like this: + * + * @code + * struct treeroot *tree; + * struct treenode *node; + * + * tree = treewalk_open(...); + * while ((node = treewalk_next(tree)) + * visitor(node); + * treewalk_close(tree); + * @endcode + * + * @param tree The tree structure. + */ +struct treenode * +treewalk_next(struct treeroot *tree) +{ + struct treenode *node; + + /* Handle rootless trees, such as uninitialized or fully traversed. */ + if (tree->rootnode == NULL) + return NULL; + + node = tree->curnode; + + /* Handle end of tree. */ + if (node == NULL) + return NULL; + + /* Get next node, descending or sidewide. */ + if (treenode_is_dir(node) && !treeroot_skip_node(tree, node)) { + struct treenode *dir; + + treeroot_fill_node(tree, node); + treeroot_sort_node(tree, node); + treeroot_set_curdir(tree, node); + + /* Check for directory loops. */ + for (dir = node->up; dir; dir = dir->up) { + if (dir->stat->st_dev == node->stat->st_dev && + dir->stat->st_ino == node->stat->st_ino) + break; + } + + /* Skip directory loops. */ + if (dir) + node = node->next; + else + node = node->down[0]; + } else { + node = node->next; + } + + /* Back track node, ascending. */ + while (node == NULL) { + struct treenode *olddir = tree->curdir; + + if (tree->curdir->next) { + /* Next entry in the parent directory. */ + node = tree->curdir->next; + treeroot_set_curdir(tree, olddir->up); + treenode_free_down(olddir); + } else if (tree->curdir->up) { + /* Next entry in the grand-parent directory. */ + node = tree->curdir->up->next; + treeroot_set_curdir(tree, olddir->up->up); + treenode_free_down(olddir); + treenode_free_down(olddir->up); + } else { + /* Otherwise, we're in the rootnode. */ + treenode_free_down(olddir); + treenode_free_node(olddir); + break; + } + + if (tree->curdir == NULL) { + treenode_free_node(tree->rootnode); + tree->rootnode = NULL; + break; + } + } + + treeroot_set_curnode(tree, node); + + return node; +} + +/** + * Closes the tree being walked. + * + * It will free any resources previously allocated. + */ +void +treewalk_close(struct treeroot *tree) +{ + free(tree); +} + +/** + * Tree walker. + * + * @param rootdir The root directory to start walking the tree. + * @param options The options specifying how to walk the tree. + * @param func The function callbacks. + */ +int +treewalk(const char *rootdir, enum treewalk_options options, + struct treewalk_funcs *func) +{ + struct treeroot *tree; + struct treenode *node; + + tree = treewalk_open(rootdir, options, func); + + /* Breath first visit. */ + for (node = treewalk_node(tree); node; node = treewalk_next(tree)) + treeroot_visit_node(tree, node); + + treewalk_close(tree); + + return 0; +} diff --git a/lib/dpkg/treewalk.h b/lib/dpkg/treewalk.h new file mode 100644 index 0000000..8c14d80 --- /dev/null +++ b/lib/dpkg/treewalk.h @@ -0,0 +1,94 @@ +/* + * libdpkg - Debian packaging suite library routines + * treewalk.h - directory tree walk support + * + * Copyright © 2013-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_TREEWALK_H +#define LIBDPKG_TREEWALK_H + +#include <dpkg/macros.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#include <stdbool.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup treewalk Directory tree walking + * @ingroup dpkg-internal + * @{ + */ + +enum DPKG_ATTR_ENUM_FLAGS treewalk_options { + TREEWALK_NONE = 0, + TREEWALK_FORCE_STAT = DPKG_BIT(0), + TREEWALK_FOLLOW_LINKS = DPKG_BIT(1), +}; + +struct treenode; + +typedef int treenode_visit_func(struct treenode *node); +typedef bool treenode_skip_func(struct treenode *node); +typedef int treenode_sort_func(struct treenode *node); + +struct treewalk_funcs { + treenode_visit_func *visit; + treenode_sort_func *sort; + treenode_skip_func *skip; +}; + +#if __STDC_VERSION__ > 201710L +#define TREEWALK_OBJECT (struct treewalk_funcs){ } +#else +#define TREEWALK_OBJECT (struct treewalk_funcs){ 0 } +#endif + +struct treeroot * +treewalk_open(const char *rootdir, enum treewalk_options options, + const struct treewalk_funcs *funcs); +struct treenode * +treewalk_node(struct treeroot *tree); +struct treenode * +treewalk_next(struct treeroot *tree); +void +treewalk_close(struct treeroot *tree); + +int +treewalk(const char *rootdir, enum treewalk_options options, + struct treewalk_funcs *funcs); + +struct treenode * +treenode_get_parent(struct treenode *node); +const char * +treenode_get_name(struct treenode *node); +const char * +treenode_get_pathname(struct treenode *node); +const char * +treenode_get_virtname(struct treenode *node); +mode_t +treenode_get_mode(struct treenode *node); +struct stat * +treenode_get_stat(struct treenode *node); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_TREEWALK_H */ diff --git a/lib/dpkg/trigdeferred.c b/lib/dpkg/trigdeferred.c new file mode 100644 index 0000000..e19eca7 --- /dev/null +++ b/lib/dpkg/trigdeferred.c @@ -0,0 +1,285 @@ +/* + * libdpkg - Debian packaging suite library routines + * trigdeferred.c - parsing of triggers/Unincorp (was …/Deferred) + * + * Copyright © 2007 Canonical Ltd + * written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2016 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/c-ctype.h> +#include <dpkg/file.h> +#include <dpkg/dir.h> +#include <dpkg/trigdeferred.h> +#include <dpkg/triglib.h> + +static struct varbuf fn, newfn; + +static const struct trigdefmeths *trigdef; + +/*---------- Deferred file handling ----------*/ + +static char *triggersdir; +static int lock_fd = -1; +static FILE *old_deferred; +static FILE *trig_new_deferred; + +static void +constructfn(struct varbuf *vb, const char *dir, const char *tail) +{ + varbuf_reset(vb); + varbuf_add_dir(vb, dir); + varbuf_add_str(vb, tail); + varbuf_end_str(vb); +} + +/** + * Start processing of the triggers deferred file. + * + * @retval -1 Lock ENOENT with O_CREAT (directory does not exist). + * @retval -2 Unincorp empty, TDUF_WRITE_IF_EMPTY unset. + * @retval -3 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT unset. + * @retval 1 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT set. + * @retval 2 Ok. + * + * For positive return values the caller must call trigdef_update_done! + */ +enum trigdef_update_status +trigdef_update_start(enum trigdef_update_flags uf) +{ + free(triggersdir); + triggersdir = dpkg_db_get_path(TRIGGERSDIR); + + if (uf & TDUF_WRITE) { + constructfn(&fn, triggersdir, TRIGGERSLOCKFILE); + if (lock_fd == -1) { + lock_fd = open(fn.buf, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (lock_fd == -1) { + if (!(errno == ENOENT && (uf & TDUF_NO_LOCK_OK))) + ohshite(_("unable to open/create " + "triggers lock file '%.250s'"), + fn.buf); + return TDUS_ERROR_NO_DIR; + } + } + + file_lock(&lock_fd, FILE_LOCK_WAIT, fn.buf, + _("triggers database lock")); + } + + constructfn(&fn, triggersdir, TRIGGERSDEFERREDFILE); + + if (old_deferred) + fclose(old_deferred); + old_deferred = fopen(fn.buf, "r"); + if (!old_deferred) { + if (errno != ENOENT) + ohshite(_("unable to open triggers deferred file '%.250s'"), + fn.buf); + if (!(uf & TDUF_WRITE_IF_ENOENT)) { + if (uf & TDUF_WRITE) + pop_cleanup(ehflag_normaltidy); + return TDUS_ERROR_NO_DEFERRED; + } + } else { + struct stat stab; + int rc; + + setcloexec(fileno(old_deferred), fn.buf); + + rc = fstat(fileno(old_deferred), &stab); + if (rc < 0) + ohshite(_("unable to stat triggers deferred file '%.250s'"), + fn.buf); + + if (stab.st_size == 0 && !(uf & TDUF_WRITE_IF_EMPTY)) { + if (uf & TDUF_WRITE) + pop_cleanup(ehflag_normaltidy); + return TDUS_ERROR_EMPTY_DEFERRED; + } + } + + if (uf & TDUF_WRITE) { + constructfn(&newfn, triggersdir, TRIGGERSDEFERREDFILE ".new"); + if (trig_new_deferred) + fclose(trig_new_deferred); + trig_new_deferred = fopen(newfn.buf, "w"); + if (!trig_new_deferred) + ohshite(_("unable to open/create new triggers deferred file '%.250s'"), + newfn.buf); + + setcloexec(fileno(trig_new_deferred), newfn.buf); + } + + if (!old_deferred) + return TDUS_NO_DEFERRED; + + return TDUS_OK; +} + +void +trigdef_set_methods(const struct trigdefmeths *methods) +{ + trigdef = methods; +} + +void +trigdef_update_printf(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(trig_new_deferred, format, ap); + va_end(ap); +} + +static void +trigdef_parse_error(int line_num, const char *line, const char *ptr) +{ + ohshit(_("syntax error in triggers deferred file '%.250s' at " + "line %d character %zd '%s'"), + fn.buf, line_num, ptr - line + 1, ptr); +} + +/* Trim leading space. */ +static char * +trigdef_skip_whitespace(char *ptr) +{ + while (*ptr) { + if (!c_iswhite(*ptr)) + break; + ptr++; + } + + return ptr; +} + +int +trigdef_parse(void) +{ + char line[_POSIX2_LINE_MAX]; + char *ptr, *ptr_ini; + int line_num = 0; + + while (fgets_checked(line, sizeof(line), old_deferred, fn.buf) > 0) { + line_num++; + + ptr = trigdef_skip_whitespace(line); + + /* Skip comments and empty lines. */ + if (*ptr == '\0' || *ptr == '#') + continue; + + /* Parse the trigger directive. */ + ptr_ini = ptr; + while (*ptr) { + if (*ptr < 0x21 || *ptr > 0x7e) + break; + ptr++; + } + + if (*ptr == '\0' || ptr_ini == ptr) + trigdef_parse_error(line_num, line, ptr); + *ptr++ = '\0'; + + /* Set the trigger directive. */ + trigdef->trig_begin(ptr_ini); + + /* Parse the package names. */ + while (*ptr) { + ptr = trigdef_skip_whitespace(ptr); + + ptr_ini = ptr; + if (*ptr == '\0' || + !(c_isdigit(*ptr) || c_islower(*ptr) || *ptr == '-')) + trigdef_parse_error(line_num, line, ptr); + + while (*++ptr) { + if (!c_isdigit(*ptr) && !c_islower(*ptr) && + *ptr != '-' && *ptr != ':' && + *ptr != '+' && *ptr != '.') + break; + } + + if (*ptr != '\0' && *ptr != '#' && !c_iswhite(*ptr)) + trigdef_parse_error(line_num, line, ptr); + *ptr++ = '\0'; + + if (ptr_ini[0] == '-' && ptr_ini[1]) + ohshit(_("invalid package name '%.250s' in " + "triggers deferred file '%.250s'"), + ptr_ini, fn.buf); + + /* Set the package name. */ + trigdef->package(ptr_ini); + } + + trigdef->trig_end(); + } + + return 0; +} + +void +trigdef_process_done(void) +{ + if (old_deferred) { + if (ferror(old_deferred)) + ohshite(_("error reading triggers deferred file '%.250s'"), + fn.buf); + fclose(old_deferred); + old_deferred = NULL; + } + + if (trig_new_deferred) { + int rc; + + if (ferror(trig_new_deferred)) + ohshite(_("unable to write new triggers deferred " + "file '%.250s'"), newfn.buf); + rc = fclose(trig_new_deferred); + trig_new_deferred = NULL; + if (rc) + ohshite(_("unable to close new triggers deferred " + "file '%.250s'"), newfn.buf); + + if (rename(newfn.buf, fn.buf)) + ohshite(_("unable to install new triggers deferred " + "file '%.250s'"), fn.buf); + + dir_sync_path(triggersdir); + } + + free(triggersdir); + triggersdir = NULL; + + /* Unlock. */ + if (lock_fd >= 0) + pop_cleanup(ehflag_normaltidy); +} diff --git a/lib/dpkg/trigdeferred.h b/lib/dpkg/trigdeferred.h new file mode 100644 index 0000000..3832334 --- /dev/null +++ b/lib/dpkg/trigdeferred.h @@ -0,0 +1,70 @@ +/* + * libdpkg - Debian packaging suite library routines + * trigdeferred.h - parsing of triggers/Unincorp (was …/Deferred) + * + * Copyright © 2007 Canonical, Ltd. + * written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_TRIGDEFERRED_H +#define LIBDPKG_TRIGDEFERRED_H + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup trigdeferred Trigger deferred file handling + * @ingroup dpkg-internal + * @{ + */ + +enum DPKG_ATTR_ENUM_FLAGS trigdef_update_flags { + TDUF_NO_LOCK_OK = DPKG_BIT(0), + TDUF_WRITE = DPKG_BIT(1), + TDUF_NO_LOCK = TDUF_NO_LOCK_OK | TDUF_WRITE, + /** Should not be set unless TDUF_WRITE is. */ + TDUF_WRITE_IF_EMPTY = DPKG_BIT(3), + TDUF_WRITE_IF_ENOENT = DPKG_BIT(4), +}; + +enum trigdef_update_status { + TDUS_ERROR_NO_DIR = -1, + TDUS_ERROR_EMPTY_DEFERRED = -2, + TDUS_ERROR_NO_DEFERRED = -3, + TDUS_NO_DEFERRED = 1, + TDUS_OK = 2, +}; + +struct trigdefmeths { + void (*trig_begin)(const char *trig); + void (*package)(const char *awname); + void (*trig_end)(void); +}; + +void trigdef_set_methods(const struct trigdefmeths *methods); + +enum trigdef_update_status trigdef_update_start(enum trigdef_update_flags uf); +void trigdef_update_printf(const char *format, ...) DPKG_ATTR_PRINTF(1); +int trigdef_parse(void); +void trigdef_process_done(void); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_TRIGDEFERRED_H */ diff --git a/lib/dpkg/triglib.c b/lib/dpkg/triglib.c new file mode 100644 index 0000000..82cb298 --- /dev/null +++ b/lib/dpkg/triglib.c @@ -0,0 +1,863 @@ +/* + * libdpkg - Debian packaging suite library routines + * triglib.c - trigger handling + * + * Copyright © 2007 Canonical Ltd + * Written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> +#include <dpkg/dlist.h> +#include <dpkg/dir.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/trigdeferred.h> +#include <dpkg/triglib.h> + +/*========== Recording triggers. ==========*/ + +static char *triggersdir, *triggersfilefile; + +static char * +trig_get_filename(const char *dir, const char *filename) +{ + return str_fmt("%s/%s", dir, filename); +} + +static struct trig_hooks trigh; + +/*---------- Noting trigger activation in memory. ----------*/ + +/* + * Called via trig_*activate* et al from: + * - trig_incorporate: reading of Unincorp (explicit trigger activations) + * - various places: processing start (‘activate’ in triggers ci file) + * - namenodetouse: file triggers during unpack / remove + * - deferred_configure: file triggers during config file processing + * + * Not called from trig_transitional_activation; that runs + * trig_note_pend directly which means that (a) awaiters are not + * recorded (how would we know?) and (b) we don't enqueue them for + * deferred processing in this run. + * + * We add the trigger to Triggers-Pending first. This makes it + * harder to get into the state where Triggers-Awaited for aw lists + * pend but Triggers-Pending for pend is empty. (See also the + * comment in deppossi_ok_found regarding this situation.) + */ + +/* + * aw might be NULL. + * trig is not copied! + */ +static void +trig_record_activation(struct pkginfo *pend, struct pkginfo *aw, const char *trig) +{ + if (pend->status < PKG_STAT_TRIGGERSAWAITED) + return; /* Not interested then. */ + + if (trig_note_pend(pend, trig)) + modstatdb_note_ifwrite(pend); + + if (trigh.enqueue_deferred) + trigh.enqueue_deferred(pend); + + if (aw && pend->status > PKG_STAT_CONFIGFILES) + if (trig_note_aw(pend, aw)) { + if (aw->status > PKG_STAT_TRIGGERSAWAITED) + pkg_set_status(aw, PKG_STAT_TRIGGERSAWAITED); + modstatdb_note_ifwrite(aw); + } +} + +void +trig_clear_awaiters(struct pkginfo *notpend) +{ + struct trigaw *ta; + + if (notpend->trigpend_head) + internerr("package %s has pending triggers", + pkg_name(notpend, pnaw_always)); + + ta = notpend->othertrigaw_head; + notpend->othertrigaw_head = NULL; + for (; ta; ta = ta->samepend_next) { + struct pkginfo *aw; + + aw = ta->aw; + if (!aw) + continue; + LIST_UNLINK_PART(aw->trigaw, ta, sameaw); + if (!aw->trigaw.head && aw->status == PKG_STAT_TRIGGERSAWAITED) { + if (aw->trigpend_head) + pkg_set_status(aw, PKG_STAT_TRIGGERSPENDING); + else + pkg_set_status(aw, PKG_STAT_INSTALLED); + modstatdb_note(aw); + } + } +} + +/* + * Fix up packages in state triggers-awaited w/o the corresponding package + * with pending triggers. This can happen when dpkg was interrupted + * while in modstatdb_note, and the package in triggers-pending had its + * state modified but dpkg could not finish clearing the awaiters. + * + * XXX: Possibly get rid of some of the checks done somewhere else for + * this condition at run-time. + */ +void +trig_fixup_awaiters(enum modstatdb_rw cstatus) +{ + if (cstatus < msdbrw_write) + return; + + trig_awaited_pend_foreach(trig_clear_awaiters); + trig_awaited_pend_free(); +} + +/*---------- Generalized handling of trigger kinds. ----------*/ + +struct trigkindinfo { + /* Only for trig_activate_start. */ + void (*activate_start)(void); + + /* Rest are for everyone: */ + void (*activate_awaiter)(struct pkginfo *pkg /* may be NULL */); + void (*activate_done)(void); + void (*interest_change)(const char *name, struct pkginfo *pkg, + struct pkgbin *pkgbin, + int signum, enum trig_options opts); +}; + +static const struct trigkindinfo tki_explicit, tki_file, tki_unknown; +static const struct trigkindinfo *dtki; + +/* As passed into activate_start. */ +static char *trig_activating_name; + +static const struct trigkindinfo * +trig_classify_byname(const char *name) +{ + if (name[0] == '/') { + const char *slash; + + slash = name; + while (slash) { + if (slash[1] == '\0' || slash[1] == '/') + goto invalid; + + slash = strchr(slash + 2, '/'); + } + return &tki_file; + } + + if (!pkg_name_is_illegal(name) && !strchr(name, '_')) + return &tki_explicit; + +invalid: + return &tki_unknown; +} + +static const char * +trig_dump_trigger_options(enum trig_options opts) +{ + if (opts == TRIG_NOAWAIT) + return "/noawait"; + return ""; +} + +/* + * Modifies the trigger string by removing the options at the ‘/’ delimiter + * and truncating it with a NUL character. + */ +static enum trig_options +trig_parse_trigger_options(char *trigger) +{ + char *slash; + + slash = strchr(trigger, '/'); + if (slash == NULL) + return TRIG_AWAIT; + + *slash++ = '\0'; + if (strcmp("noawait", slash) == 0) + return TRIG_NOAWAIT; + if (strcmp("await", slash) == 0) + return TRIG_AWAIT; + + /* XXX: Should perhaps warn or error for unknown keywords. */ + + return TRIG_AWAIT; +} + +/* + * Calling sequence is: + * trig_activate_start(triggername); + * dtki->activate_awaiter(awaiting_package); } zero or more times + * dtki->activate_awaiter(NULL); } in any order + * dtki->activate_done(); + */ +static void +trig_activate_start(const char *name) +{ + dtki = trig_classify_byname(name); + trig_activating_name = nfstrsave(name); + dtki->activate_start(); +} + +/*---------- Unknown trigger kinds. ----------*/ + +static void +trk_unknown_activate_start(void) +{ +} + +static void +trk_unknown_activate_awaiter(struct pkginfo *aw) +{ +} + +static void +trk_unknown_activate_done(void) +{ +} + +static void DPKG_ATTR_NORET +trk_unknown_interest_change(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, int signum, + enum trig_options opts) +{ + ohshit(_("invalid or unknown syntax in trigger name '%.250s'" + " (in trigger interests for package '%.250s')"), + trig, pkgbin_name(pkg, pkgbin, pnaw_nonambig)); +} + +static const struct trigkindinfo tki_unknown = { + .activate_start = trk_unknown_activate_start, + .activate_awaiter = trk_unknown_activate_awaiter, + .activate_done = trk_unknown_activate_done, + .interest_change = trk_unknown_interest_change, +}; + +/*---------- Explicit triggers. ----------*/ + +static FILE *trk_explicit_f; +static struct varbuf trk_explicit_fn; +static char *trk_explicit_trig; + +static void +trk_explicit_activate_done(void) +{ + if (trk_explicit_f) { + fclose(trk_explicit_f); + trk_explicit_f = NULL; + } +} + +static void +trk_explicit_start(const char *trig) +{ + trk_explicit_activate_done(); + + varbuf_reset(&trk_explicit_fn); + varbuf_add_dir(&trk_explicit_fn, triggersdir); + varbuf_add_str(&trk_explicit_fn, trig); + varbuf_end_str(&trk_explicit_fn); + + trk_explicit_f = fopen(trk_explicit_fn.buf, "r"); + if (!trk_explicit_f) { + if (errno != ENOENT) + ohshite(_("failed to open trigger interest list file '%.250s'"), + trk_explicit_fn.buf); + } +} + +static int +trk_explicit_fgets(char *buf, size_t sz) +{ + return fgets_checked(buf, sz, trk_explicit_f, trk_explicit_fn.buf); +} + +static void +trk_explicit_activate_start(void) +{ + trk_explicit_start(trig_activating_name); + trk_explicit_trig = trig_activating_name; +} + +static void +trk_explicit_activate_awaiter(struct pkginfo *aw) +{ + char buf[1024]; + + if (!trk_explicit_f) + return; + + if (fseek(trk_explicit_f, 0, SEEK_SET)) + ohshite(_("failed to rewind trigger interest file '%.250s'"), + trk_explicit_fn.buf); + + while (trk_explicit_fgets(buf, sizeof(buf)) >= 0) { + struct pkginfo *pend; + struct dpkg_error err; + enum trig_options opts; + + opts = trig_parse_trigger_options(buf); + + pend = pkg_spec_parse_pkg(buf, &err); + if (pend == NULL) + ohshit(_("trigger interest file '%.250s' syntax error; " + "illegal package name '%.250s': %.250s"), + trk_explicit_fn.buf, buf, err.str); + + trig_record_activation(pend, opts == TRIG_NOAWAIT ? NULL : aw, + trk_explicit_trig); + } +} + +static void +trk_explicit_interest_change(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, int signum, + enum trig_options opts) +{ + char buf[1024]; + struct atomic_file *file; + bool empty = true; + + trk_explicit_start(trig); + file = atomic_file_new(trk_explicit_fn.buf, 0); + atomic_file_open(file); + + while (trk_explicit_f && trk_explicit_fgets(buf, sizeof(buf)) >= 0) { + enum trig_options trig_opts; + struct pkginfo *pkg_parsed; + struct dpkg_error err; + + trig_opts = trig_parse_trigger_options(buf); + + pkg_parsed = pkg_spec_parse_pkg(buf, &err); + if (pkg_parsed == NULL) + ohshit(_("trigger interest file '%.250s' syntax error; " + "illegal package name '%.250s': %.250s"), + trk_explicit_fn.buf, buf, err.str); + + if (pkg == pkg_parsed && + (pkgbin == &pkg_parsed->installed || + pkgbin == &pkg_parsed->available)) + continue; + + fprintf(file->fp, "%s%s\n", buf, + trig_dump_trigger_options(trig_opts)); + empty = false; + } + if (signum > 0) { + fprintf(file->fp, "%s%s\n", + pkgbin_name(pkg, pkgbin, pnaw_same), + trig_dump_trigger_options(opts)); + empty = false; + } + + if (!empty) + atomic_file_sync(file); + + atomic_file_close(file); + + if (empty) + atomic_file_remove(file); + else + atomic_file_commit(file); + + atomic_file_free(file); + + dir_sync_path(triggersdir); +} + +static const struct trigkindinfo tki_explicit = { + .activate_start = trk_explicit_activate_start, + .activate_awaiter = trk_explicit_activate_awaiter, + .activate_done = trk_explicit_activate_done, + .interest_change = trk_explicit_interest_change, +}; + +/*---------- File triggers. ----------*/ + +static struct { + struct trigfileint *head, *tail; +} filetriggers; + +/* + * Values: + * -1: Not read. + * 0: Not edited. + * 1: Edited + */ +static int filetriggers_edited = -1; + +/* + * Called by various people with signum -1 and +1 to mean remove and add + * and also by trig_file_interests_ensure() with signum +2 meaning add + * but die if already present. + */ +static void +trk_file_interest_change(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, int signum, + enum trig_options opts) +{ + struct fsys_namenode *fnn; + struct trigfileint **search, *tfi; + + fnn = trigh.namenode_find(trig, signum <= 0); + if (!fnn) { + if (signum >= 0) + internerr("lost filename node '%s' for package %s " + "triggered to add", trig, + pkgbin_name(pkg, pkgbin, pnaw_always)); + return; + } + + for (search = trigh.namenode_interested(fnn); + (tfi = *search); + search = &tfi->samefile_next) + if (tfi->pkg == pkg) + goto found; + + /* Not found. */ + if (signum < 0) + return; + + tfi = nfmalloc(sizeof(*tfi)); + tfi->pkg = pkg; + tfi->pkgbin = pkgbin; + tfi->fnn = fnn; + tfi->options = opts; + tfi->samefile_next = *trigh.namenode_interested(fnn); + *trigh.namenode_interested(fnn) = tfi; + + LIST_LINK_TAIL_PART(filetriggers, tfi, inoverall); + goto edited; + +found: + tfi->options = opts; + if (signum > 1) + ohshit(_("duplicate file trigger interest for filename '%.250s' " + "and package '%.250s'"), trig, + pkgbin_name(pkg, pkgbin, pnaw_nonambig)); + if (signum > 0) + return; + + /* Remove it: */ + *search = tfi->samefile_next; + LIST_UNLINK_PART(filetriggers, tfi, inoverall); +edited: + filetriggers_edited = 1; +} + +static void +trig_file_interests_remove(void) +{ + if (unlink(triggersfilefile) && errno != ENOENT) + ohshite(_("cannot remove '%.250s'"), triggersfilefile); +} + +static void +trig_file_interests_update(void) +{ + struct trigfileint *tfi; + struct atomic_file *file; + + file = atomic_file_new(triggersfilefile, 0); + atomic_file_open(file); + + for (tfi = filetriggers.head; tfi; tfi = tfi->inoverall.next) + fprintf(file->fp, "%s %s%s\n", trigh.namenode_name(tfi->fnn), + pkgbin_name(tfi->pkg, tfi->pkgbin, pnaw_same), + trig_dump_trigger_options(tfi->options)); + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); +} + +void +trig_file_interests_save(void) +{ + if (filetriggers_edited <= 0) + return; + + if (!filetriggers.head) + trig_file_interests_remove(); + else + trig_file_interests_update(); + + dir_sync_path(triggersdir); + + filetriggers_edited = 0; +} + +void +trig_file_interests_ensure(void) +{ + FILE *f; + char linebuf[1024], *space; + struct pkginfo *pkg; + struct pkgbin *pkgbin; + + if (filetriggers_edited >= 0) + return; + + f = fopen(triggersfilefile, "r"); + if (!f) { + if (errno == ENOENT) + goto ok; + ohshite(_("unable to read file triggers file '%.250s'"), + triggersfilefile); + } + + push_cleanup(cu_closestream, ~0, 1, f); + while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) { + struct dpkg_error err; + enum trig_options trig_opts; + + space = strchr(linebuf, ' '); + if (!space || linebuf[0] != '/') + ohshit(_("syntax error in file triggers file '%.250s'"), + triggersfilefile); + *space++ = '\0'; + + trig_opts = trig_parse_trigger_options(space); + + pkg = pkg_spec_parse_pkg(space, &err); + if (pkg == NULL) + ohshit(_("file triggers record mentions illegal " + "package name '%.250s' (for interest in file " + "'%.250s'): %.250s"), space, linebuf, err.str); + pkgbin = &pkg->installed; + + trk_file_interest_change(linebuf, pkg, pkgbin, +2, trig_opts); + } + pop_cleanup(ehflag_normaltidy); +ok: + filetriggers_edited = 0; +} + +void +trig_file_activate_byname(const char *trig, struct pkginfo *aw) +{ + struct fsys_namenode *fnn = trigh.namenode_find(trig, 1); + + if (fnn) + trig_file_activate(fnn, aw); +} + +void +trig_file_activate(struct fsys_namenode *trig, struct pkginfo *aw) +{ + struct trigfileint *tfi; + + for (tfi = *trigh.namenode_interested(trig); tfi; + tfi = tfi->samefile_next) + trig_record_activation(tfi->pkg, (tfi->options == TRIG_NOAWAIT) ? + NULL : aw, trigh.namenode_name(trig)); +} + +static void +trig_file_activate_parents(const char *trig, struct pkginfo *aw) +{ + char *path, *slash; + + /* Traverse the whole pathname to activate all of its components. */ + path = m_strdup(trig); + + while ((slash = strrchr(path, '/'))) { + *slash = '\0'; + trig_file_activate_byname(path, aw); + } + + free(path); +} + +void +trig_path_activate(struct fsys_namenode *trig, struct pkginfo *aw) +{ + trig_file_activate(trig, aw); + trig_file_activate_parents(trigh.namenode_name(trig), aw); +} + +static void +trig_path_activate_byname(const char *trig, struct pkginfo *aw) +{ + struct fsys_namenode *fnn = trigh.namenode_find(trig, 1); + + if (fnn) + trig_file_activate(fnn, aw); + + trig_file_activate_parents(trig, aw); +} + +static const char *trk_file_trig; + +static void +trk_file_activate_start(void) +{ + trk_file_trig = trig_activating_name; +} + +static void +trk_file_activate_awaiter(struct pkginfo *aw) +{ + trig_path_activate_byname(trk_file_trig, aw); +} + +static void +trk_file_activate_done(void) +{ +} + +static const struct trigkindinfo tki_file = { + .activate_start = trk_file_activate_start, + .activate_awaiter = trk_file_activate_awaiter, + .activate_done = trk_file_activate_done, + .interest_change = trk_file_interest_change, +}; + +/*---------- Trigger control info file. ----------*/ + +static void +trig_cicb_interest_change(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, int signum, + enum trig_options opts) +{ + const struct trigkindinfo *tki = trig_classify_byname(trig); + + if (filetriggers_edited < 0) + internerr("trigger control file for package %s not read", + pkgbin_name(pkg, pkgbin, pnaw_always)); + + tki->interest_change(trig, pkg, pkgbin, signum, opts); +} + +void +trig_cicb_interest_delete(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options opts) +{ + trig_cicb_interest_change(trig, pkg, pkgbin, -1, opts); +} + +void +trig_cicb_interest_add(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options opts) +{ + trig_cicb_interest_change(trig, pkg, pkgbin, +1, opts); +} + +void +trig_cicb_statuschange_activate(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options opts) +{ + struct pkginfo *aw = pkg; + + trig_activate_start(trig); + dtki->activate_awaiter((opts == TRIG_NOAWAIT) ? NULL : aw); + dtki->activate_done(); +} + +static void +parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb, + const char *trig, struct pkginfo *pkg, struct pkgbin *pkgbin, + enum trig_options opts) +{ + const char *emsg; + + emsg = trig_name_is_illegal(trig); + if (emsg) + ohshit(_("triggers ci file '%.250s' contains illegal trigger " + "syntax in trigger name '%.250s': %.250s"), + file, trig, emsg); + if (cb) + cb(trig, pkg, pkgbin, opts); +} + +void +trig_parse_ci(const char *file, trig_parse_cicb *interest, + trig_parse_cicb *activate, struct pkginfo *pkg, + struct pkgbin *pkgbin) +{ + FILE *f; + char linebuf[MAXTRIGDIRECTIVE], *cmd, *spc, *eol; + int l; + + f = fopen(file, "r"); + if (!f) { + if (errno == ENOENT) + return; /* No file is just like an empty one. */ + ohshite(_("unable to open triggers ci file '%.250s'"), file); + } + push_cleanup(cu_closestream, ~0, 1, f); + + while ((l = fgets_checked(linebuf, sizeof(linebuf), f, file)) >= 0) { + for (cmd = linebuf; c_iswhite(*cmd); cmd++) ; + if (*cmd == '#') + continue; + for (eol = linebuf + l; eol > cmd && c_iswhite(eol[-1]); eol--) ; + if (eol == cmd) + continue; + *eol = '\0'; + + for (spc = cmd; *spc && !c_iswhite(*spc); spc++) ; + if (!*spc) + ohshit(_("triggers ci file contains unknown directive syntax")); + *spc++ = '\0'; + while (c_iswhite(*spc)) + spc++; + if (strcmp(cmd, "interest") == 0 || + strcmp(cmd, "interest-await") == 0) { + parse_ci_call(file, cmd, interest, spc, pkg, pkgbin, TRIG_AWAIT); + } else if (strcmp(cmd, "interest-noawait") == 0) { + parse_ci_call(file, cmd, interest, spc, pkg, pkgbin, TRIG_NOAWAIT); + } else if (strcmp(cmd, "activate") == 0 || + strcmp(cmd, "activate-await") == 0) { + parse_ci_call(file, cmd, activate, spc, pkg, pkgbin, TRIG_AWAIT); + } else if (strcmp(cmd, "activate-noawait") == 0) { + parse_ci_call(file, cmd, activate, spc, pkg, pkgbin, TRIG_NOAWAIT); + } else { + ohshit(_("triggers ci file contains unknown directive '%.250s'"), + cmd); + } + } + pop_cleanup(ehflag_normaltidy); /* fclose() */ +} + +/*---------- Unincorp file incorporation. ----------*/ + +static void +tdm_incorp_trig_begin(const char *trig) +{ + trig_activate_start(trig); +} + +static void +tdm_incorp_package(const char *awname) +{ + struct pkginfo *aw; + + if (strcmp(awname, "-") == 0) + aw = NULL; + else + aw = pkg_spec_parse_pkg(awname, NULL); + + dtki->activate_awaiter(aw); +} + +static void +tdm_incorp_trig_end(void) +{ + dtki->activate_done(); +} + +static const struct trigdefmeths tdm_incorp = { + .trig_begin = tdm_incorp_trig_begin, + .package = tdm_incorp_package, + .trig_end = tdm_incorp_trig_end +}; + +void +trig_incorporate(enum modstatdb_rw cstatus) +{ + enum trigdef_update_status ur; + enum trigdef_update_flags tduf; + + free(triggersdir); + triggersdir = dpkg_db_get_path(TRIGGERSDIR); + + free(triggersfilefile); + triggersfilefile = trig_get_filename(triggersdir, TRIGGERSFILEFILE); + + trigdef_set_methods(&tdm_incorp); + trig_file_interests_ensure(); + + tduf = TDUF_NO_LOCK_OK; + if (cstatus >= msdbrw_write) { + tduf |= TDUF_WRITE; + if (trigh.transitional_activate) + tduf |= TDUF_WRITE_IF_ENOENT; + } + + ur = trigdef_update_start(tduf); + if (ur == TDUS_ERROR_NO_DIR && cstatus >= msdbrw_write) { + if (mkdir(triggersdir, 0755)) { + if (errno != EEXIST) + ohshite(_("unable to create triggers state" + " directory '%.250s'"), triggersdir); + } + ur = trigdef_update_start(tduf); + } + switch (ur) { + case TDUS_ERROR_EMPTY_DEFERRED: + return; + case TDUS_ERROR_NO_DIR: + case TDUS_ERROR_NO_DEFERRED: + if (!trigh.transitional_activate) + return; + /* Fall through. */ + case TDUS_NO_DEFERRED: + trigh.transitional_activate(cstatus); + break; + case TDUS_OK: + /* Read and incorporate triggers. */ + trigdef_parse(); + break; + default: + internerr("unknown trigdef_update_start return value '%d'", ur); + } + + /* Right, that's it. New (empty) Unincorp can be installed. */ + trigdef_process_done(); +} + +/*---------- Default hooks. ----------*/ + +TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS + +static struct trig_hooks trigh = { + .enqueue_deferred = NULL, + .transitional_activate = NULL, + .namenode_find = th_nn_find, + .namenode_interested = th_nn_interested, + .namenode_name = th_nn_name, +}; + +void +trig_override_hooks(const struct trig_hooks *hooks) +{ + trigh = *hooks; +} diff --git a/lib/dpkg/triglib.h b/lib/dpkg/triglib.h new file mode 100644 index 0000000..e4e101b --- /dev/null +++ b/lib/dpkg/triglib.h @@ -0,0 +1,129 @@ +/* + * libdpkg - Debian packaging suite library routines + * triglib.h - declarations for trigger handling + * + * Copyright © 2007 Canonical, Ltd. + * written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_TRIGLIB_H +#define LIBDPKG_TRIGLIB_H + +#include <dpkg/macros.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup triglib Trigger handling + * @ingroup dpkg-internal + * @{ + */ + +/* + * Hooks for more sophisticated processing in dpkg proper. + * + * We do things like this so we can get most of the trigger tracking + * in dpkg-query, dselect, and so on, but avoid the transitional + * processing and deferred trigproc queue management other than when + * we're actually doing real package management work. + */ + +const char *trig_name_is_illegal(const char *p); + +enum trig_options { + TRIG_AWAIT, + TRIG_NOAWAIT, +}; + +struct trigfileint { + struct pkginfo *pkg; + struct pkgbin *pkgbin; + struct fsys_namenode *fnn; + enum trig_options options; + struct trigfileint *samefile_next; + struct { + struct trigfileint *next, *prev; + } inoverall; +}; + +/** + * The first two hooks are normally NULL. + * If non-NULL, we're dpkg proper and we might need to invent trigger + * activations as the first run of a triggers-supporting dpkg. + */ +struct trig_hooks { + void (*enqueue_deferred)(struct pkginfo *pend); + void (*transitional_activate)(enum modstatdb_rw cstatus); + + struct fsys_namenode *(*namenode_find)(const char *filename, bool nonew); + struct trigfileint **(*namenode_interested)(struct fsys_namenode *fnn); + + /** Returns a pointer from nfmalloc. */ + const char *(*namenode_name)(struct fsys_namenode *fnn); +}; + +#define TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS \ + static struct fsys_namenode *th_nn_find(const char *name, bool nonew) \ + { return fsys_hash_find_node(name, nonew ? FHFF_NO_NEW : FHFF_NONE); } \ + static struct trigfileint **th_nn_interested(struct fsys_namenode *fnn) \ + { return &fnn->trig_interested; } \ + static const char *th_nn_name(struct fsys_namenode *fnn) \ + { return fnn->name; } + +void trig_override_hooks(const struct trig_hooks *hooks); + +void trig_file_activate_byname(const char *trig, struct pkginfo *aw); +void trig_file_activate(struct fsys_namenode *trig, struct pkginfo *aw); +void trig_path_activate(struct fsys_namenode *trig, struct pkginfo *aw); + +bool trig_note_pend_core(struct pkginfo *pend, const char *trig /*not copied!*/); +bool trig_note_pend(struct pkginfo *pend, const char *trig /*not copied!*/); +bool trig_note_aw(struct pkginfo *pend, struct pkginfo *aw); +void trig_clear_awaiters(struct pkginfo *notpend); + +typedef void trig_awaited_pend_foreach_func(struct pkginfo *pkg); + +void trig_awaited_pend_enqueue(struct pkginfo *pend); +void trig_awaited_pend_foreach(trig_awaited_pend_foreach_func *func); +void trig_awaited_pend_free(void); + +void trig_fixup_awaiters(enum modstatdb_rw cstatus); + +void trig_file_interests_ensure(void); +void trig_file_interests_save(void); + +typedef void trig_parse_cicb(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options to); +void trig_cicb_interest_delete(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options to); +void trig_cicb_interest_add(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options to); +void trig_cicb_statuschange_activate(const char *trig, struct pkginfo *pkg, + struct pkgbin *pkgbin, enum trig_options to); +void trig_parse_ci(const char *file, trig_parse_cicb *interest, + trig_parse_cicb *activate, struct pkginfo *pkg, + struct pkgbin *pkgbin); + +void trig_incorporate(enum modstatdb_rw cstatus); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_TRIGLIB_H */ diff --git a/lib/dpkg/trigname.c b/lib/dpkg/trigname.c new file mode 100644 index 0000000..d271140 --- /dev/null +++ b/lib/dpkg/trigname.c @@ -0,0 +1,42 @@ +/* + * libdpkg - Debian packaging suite library routines + * trigname.c - trigger name handling + * + * Copyright © 2007 Canonical Ltd + * Written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> +#include <dpkg/triglib.h> + +const char * +trig_name_is_illegal(const char *p) +{ + int c; + + if (!*p) + return _("empty trigger names are not permitted"); + + while ((c = *p++)) { + if (c <= ' ' || c >= 0177) + return _("trigger name contains invalid character"); + } + + return NULL; +} diff --git a/lib/dpkg/trignote.c b/lib/dpkg/trignote.c new file mode 100644 index 0000000..957e808 --- /dev/null +++ b/lib/dpkg/trignote.c @@ -0,0 +1,133 @@ +/* + * libdpkg - Debian packaging suite library routines + * trignote.c - trigger note handling + * + * Copyright © 2007 Canonical Ltd + * Written by Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> +#include <dpkg/pkg-list.h> +#include <dpkg/dlist.h> +#include <dpkg/triglib.h> + +/* + * Note: This is also called from fields.c where *pend is a temporary! + * + * trig is not copied! + */ +bool +trig_note_pend_core(struct pkginfo *pend, const char *trig) +{ + struct trigpend *tp; + + for (tp = pend->trigpend_head; tp; tp = tp->next) + if (strcmp(tp->name, trig) == 0) + return false; + + tp = nfmalloc(sizeof(*tp)); + tp->name = trig; + tp->next = pend->trigpend_head; + pend->trigpend_head = tp; + + return true; +} + +/* + * trig is not copied! + * + * @retval true For done. + * @retval false For already noted. + */ +bool +trig_note_pend(struct pkginfo *pend, const char *trig) +{ + if (!trig_note_pend_core(pend, trig)) + return false; + + if (pend->trigaw.head) + pkg_set_status(pend, PKG_STAT_TRIGGERSAWAITED); + else + pkg_set_status(pend, PKG_STAT_TRIGGERSPENDING); + + return true; +} + +/* + * Note: This is called also from fields.c where *aw is a temporary + * but pend is from pkg_hash_find()! + * + * @retval true For done. + * @retval false For already noted. + */ +bool +trig_note_aw(struct pkginfo *pend, struct pkginfo *aw) +{ + struct trigaw *ta; + + /* We search through aw's list because that's probably shorter. */ + for (ta = aw->trigaw.head; ta; ta = ta->sameaw.next) + if (ta->pend == pend) + return false; + + ta = nfmalloc(sizeof(*ta)); + ta->aw = aw; + ta->pend = pend; + ta->samepend_next = pend->othertrigaw_head; + pend->othertrigaw_head = ta; + LIST_LINK_TAIL_PART(aw->trigaw, ta, sameaw); + + return true; +} + +static struct pkg_list *trig_awaited_pend_head; + +void +trig_awaited_pend_enqueue(struct pkginfo *pend) +{ + struct pkg_list *tp; + + for (tp = trig_awaited_pend_head; tp; tp = tp->next) + if (tp->pkg == pend) + return; + + pkg_list_prepend(&trig_awaited_pend_head, pend); +} + +void +trig_awaited_pend_foreach(trig_awaited_pend_foreach_func *func) +{ + struct pkg_list *tp; + + for (tp = trig_awaited_pend_head; tp; tp = tp->next) + if (!tp->pkg->trigpend_head) + func(tp->pkg); +} + +void +trig_awaited_pend_free(void) +{ + pkg_list_free(trig_awaited_pend_head); + trig_awaited_pend_head = NULL; +} diff --git a/lib/dpkg/utils.c b/lib/dpkg/utils.c new file mode 100644 index 0000000..363624c --- /dev/null +++ b/lib/dpkg/utils.c @@ -0,0 +1,58 @@ +/* + * libdpkg - Debian packaging suite library routines + * utils.c - helper functions for dpkg + * + * Copyright © 1995, 2008 Ian Jackson <ijackson@chiark.greenend.org.uk> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> + +int +fgets_checked(char *buf, size_t bufsz, FILE *f, const char *fn) +{ + int l; + + if (!fgets(buf, bufsz, f)) { + if (ferror(f)) + ohshite(_("read error in '%.250s'"), fn); + return -1; + } + l = strlen(buf); + if (l == 0) + ohshit(_("fgets gave an empty string from '%.250s'"), fn); + if (buf[--l] != '\n') + ohshit(_("too-long line or missing newline in '%.250s'"), fn); + buf[l] = '\0'; + + return l; +} + +int +fgets_must(char *buf, size_t bufsz, FILE *f, const char *fn) +{ + int l = fgets_checked(buf, bufsz, f, fn); + + if (l < 0) + ohshit(_("unexpected end of file reading '%.250s'"), fn); + + return l; +} diff --git a/lib/dpkg/varbuf.c b/lib/dpkg/varbuf.c new file mode 100644 index 0000000..a5d3d31 --- /dev/null +++ b/lib/dpkg/varbuf.c @@ -0,0 +1,276 @@ +/* + * libdpkg - Debian packaging suite library routines + * varbuf.c - variable length expandable buffer handling + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +struct varbuf * +varbuf_new(size_t size) +{ + struct varbuf *v; + + v = m_malloc(sizeof(*v)); + varbuf_init(v, size); + + return v; +} + +void +varbuf_init(struct varbuf *v, size_t size) +{ + v->used = 0; + v->size = size; + if (size) + v->buf = m_malloc(size); + else + v->buf = NULL; +} + +void +varbuf_grow(struct varbuf *v, size_t need_size) +{ + size_t new_size; + + /* Make sure the varbuf is in a sane state. */ + if (v->size < v->used) + internerr("varbuf used(%zu) > size(%zu)", v->used, v->size); + + /* Check if we already have enough room. */ + if ((v->size - v->used) >= need_size) + return; + + /* Check if we overflow. */ + new_size = (v->size + need_size) * 2; + if (new_size < v->size) + ohshit(_("cannot grow varbuf to size %zu; it would overflow"), + need_size); + + v->size = new_size; + v->buf = m_realloc(v->buf, v->size); +} + +void +varbuf_trunc(struct varbuf *v, size_t used_size) +{ + /* Make sure the caller does not claim more than available. */ + if (v->size < used_size) + internerr("varbuf new_used(%zu) > size(%zu)", used_size, v->size); + + v->used = used_size; +} + +void +varbuf_reset(struct varbuf *v) +{ + v->used = 0; +} + +const char * +varbuf_get_str(struct varbuf *v) +{ + varbuf_end_str(v); + + return v->buf; +} + +void +varbuf_set_buf(struct varbuf *v, const void *buf, size_t size) +{ + varbuf_reset(v); + varbuf_add_buf(v, buf, size); + varbuf_end_str(v); +} + +void +varbuf_set_varbuf(struct varbuf *v, struct varbuf *other) +{ + varbuf_set_buf(v, other->buf, other->used); +} + +void +varbuf_add_varbuf(struct varbuf *v, const struct varbuf *other) +{ + varbuf_grow(v, other->used); + memcpy(v->buf + v->used, other->buf, other->used); + v->used += other->used; +} + +void +varbuf_add_char(struct varbuf *v, int c) +{ + varbuf_grow(v, 1); + v->buf[v->used++] = c; +} + +void +varbuf_dup_char(struct varbuf *v, int c, size_t n) +{ + if (n == 0) + return; + varbuf_grow(v, n); + memset(v->buf + v->used, c, n); + v->used += n; +} + +void +varbuf_map_char(struct varbuf *v, int c_src, int c_dst) +{ + size_t i; + + for (i = 0; i < v->used; i++) + if (v->buf[i] == c_src) + v->buf[i] = c_dst; +} + +void +varbuf_add_dir(struct varbuf *v, const char *dirname) +{ + varbuf_add_str(v, dirname); + if (v->used == 0 || v->buf[v->used - 1] != '/') + varbuf_add_char(v, '/'); +} + +void +varbuf_add_buf(struct varbuf *v, const void *s, size_t size) +{ + if (size == 0) + return; + varbuf_grow(v, size); + memcpy(v->buf + v->used, s, size); + v->used += size; +} + +void +varbuf_end_str(struct varbuf *v) +{ + varbuf_grow(v, 1); + v->buf[v->used] = '\0'; +} + +int +varbuf_vprintf(struct varbuf *v, const char *fmt, va_list args) +{ + va_list args_copy; + int needed, n; + + va_copy(args_copy, args); + needed = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + if (needed < 0) + ohshite(_("error formatting string into varbuf variable")); + + varbuf_grow(v, needed + 1); + + n = vsnprintf(v->buf + v->used, needed + 1, fmt, args); + if (n < 0) + ohshite(_("error formatting string into varbuf variable")); + + v->used += n; + + return n; +} + +int +varbuf_printf(struct varbuf *v, const char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = varbuf_vprintf(v, fmt, args); + va_end(args); + + return n; +} + +void +varbuf_snapshot(struct varbuf *v, struct varbuf_state *vs) +{ + vs->v = v; + vs->used = v->used; +} + +void +varbuf_rollback(struct varbuf_state *vs) +{ + varbuf_trunc(vs->v, vs->used); +} + +size_t +varbuf_rollback_len(struct varbuf_state *vs) +{ + if (vs->used > vs->v->used) + internerr("varbuf state_used(%zu) > used(%zu)", + vs->used, vs->v->used); + return vs->v->used - vs->used; +} + +const char * +varbuf_rollback_start(struct varbuf_state *vs) +{ + if (vs->v->buf == NULL) { + if (vs->used) + internerr("varbuf buf(NULL) state_used(%zu) > 0", + vs->used); + /* XXX: Ideally this would be handled by varbuf always having + * a valid buf or switching all users to the getter, but for + * now this will do. */ + return ""; + } + return vs->v->buf + vs->used; +} + +char * +varbuf_detach(struct varbuf *v) +{ + char *buf = v->buf; + + v->buf = NULL; + v->size = 0; + v->used = 0; + + return buf; +} + +void +varbuf_destroy(struct varbuf *v) +{ + free(v->buf); + v->buf = NULL; + v->size = 0; + v->used = 0; +} + +void +varbuf_free(struct varbuf *v) +{ + free(v->buf); + free(v); +} diff --git a/lib/dpkg/varbuf.h b/lib/dpkg/varbuf.h new file mode 100644 index 0000000..e58dac5 --- /dev/null +++ b/lib/dpkg/varbuf.h @@ -0,0 +1,189 @@ +/* + * libdpkg - Debian packaging suite library routines + * varbuf.h - variable length expandable buffer handling + * + * Copyright © 1994, 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2015 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_VARBUF_H +#define LIBDPKG_VARBUF_H + +#include <stddef.h> +#include <stdarg.h> +#include <string.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup varbuf Variable length buffers + * @ingroup dpkg-public + * @{ + */ + +/** + * varbuf_init must be called exactly once before the use of each varbuf + * (including before any call to varbuf_destroy), or the variable must be + * initialized with VARBUF_INIT. + * + * However, varbufs allocated ‘static’ are properly initialized anyway and + * do not need varbuf_init; multiple consecutive calls to varbuf_init before + * any use are allowed. + * + * varbuf_destroy must be called after a varbuf is finished with, if anything + * other than varbuf_init has been done. After this you are allowed but + * not required to call varbuf_init again if you want to start using the + * varbuf again. + * + * Callers using C++ need not worry about any of this. + */ +struct varbuf { + size_t used, size; + char *buf; + +#ifdef __cplusplus + explicit varbuf(size_t _size = 0); + ~varbuf(); + void init(size_t _size = 0); + void reset(); + void destroy(); + int fmt(const char *_fmt, ...) DPKG_ATTR_PRINTF(2); + int vfmt(const char *_fmt, va_list va) DPKG_ATTR_VPRINTF(2); + void operator()(int c); + void operator()(const char *s); + const char *string(); +#endif +}; + +#define VARBUF_INIT { 0, 0, NULL } + +#define VARBUF_OBJECT (struct varbuf)VARBUF_INIT + +struct varbuf *varbuf_new(size_t size); +void varbuf_init(struct varbuf *v, size_t size); +void varbuf_grow(struct varbuf *v, size_t need_size); +void varbuf_trunc(struct varbuf *v, size_t used_size); +char *varbuf_detach(struct varbuf *v); +void varbuf_reset(struct varbuf *v); +void varbuf_destroy(struct varbuf *v); +void varbuf_free(struct varbuf *v); + +void varbuf_set_varbuf(struct varbuf *v, struct varbuf *other); +void varbuf_set_buf(struct varbuf *v, const void *buf, size_t size); +#define varbuf_set_str(v, s) varbuf_set_buf(v, s, strlen(s)) +#define varbuf_set_strn(v, s, n) varbuf_set_buf(v, s, strnlen(s, n)) + +void varbuf_add_varbuf(struct varbuf *v, const struct varbuf *other); +void varbuf_add_char(struct varbuf *v, int c); +void varbuf_dup_char(struct varbuf *v, int c, size_t n); +void varbuf_map_char(struct varbuf *v, int c_src, int c_dst); +#define varbuf_add_str(v, s) varbuf_add_buf(v, s, strlen(s)) +#define varbuf_add_strn(v, s, n) varbuf_add_buf(v, s, strnlen(s, n)) +void varbuf_add_dir(struct varbuf *v, const char *dirname); +void varbuf_add_buf(struct varbuf *v, const void *s, size_t size); +void varbuf_end_str(struct varbuf *v); +const char *varbuf_get_str(struct varbuf *v); + +int varbuf_printf(struct varbuf *v, const char *fmt, ...) DPKG_ATTR_PRINTF(2); +int varbuf_vprintf(struct varbuf *v, const char *fmt, va_list va) + DPKG_ATTR_VPRINTF(2); + +struct varbuf_state { + struct varbuf *v; + size_t used; +}; + +void varbuf_snapshot(struct varbuf *v, struct varbuf_state *vs); +void varbuf_rollback(struct varbuf_state *vs); +size_t varbuf_rollback_len(struct varbuf_state *vs); +const char *varbuf_rollback_start(struct varbuf_state *vs); + +/** @} */ + +DPKG_END_DECLS + +#ifdef __cplusplus +inline +varbuf::varbuf(size_t _size) +{ + varbuf_init(this, _size); +} + +inline +varbuf::~varbuf() +{ + varbuf_destroy(this); +} + +inline void +varbuf::init(size_t _size) +{ + varbuf_init(this, _size); +} + +inline void +varbuf::reset() +{ + used = 0; +} + +inline void +varbuf::destroy() +{ + varbuf_destroy(this); +} + +inline int +varbuf::fmt(const char *_fmt, ...) +{ + va_list args; + int rc; + + va_start(args, _fmt); + rc = varbuf_vprintf(this, _fmt, args); + va_end(args); + + return rc; +} + +inline int +varbuf::vfmt(const char *_fmt, va_list va) +{ + return varbuf_vprintf(this, _fmt, va); +} + +inline void +varbuf::operator()(int c) +{ + varbuf_add_char(this, c); +} + +inline void +varbuf::operator()(const char *s) +{ + varbuf_add_str(this, s); +} + +inline const char * +varbuf::string() +{ + return varbuf_get_str(this); +} +#endif + +#endif /* LIBDPKG_VARBUF_H */ diff --git a/lib/dpkg/version.c b/lib/dpkg/version.c new file mode 100644 index 0000000..e9bc4bb --- /dev/null +++ b/lib/dpkg/version.c @@ -0,0 +1,198 @@ +/* + * libdpkg - Debian packaging suite library routines + * version.c - version handling functions + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/c-ctype.h> +#include <dpkg/ehandle.h> +#include <dpkg/string.h> +#include <dpkg/version.h> + +/** + * Turn the passed version into an empty version. + * + * This can be used to ensure the version is properly initialized. + * + * @param version The version to clear. + */ +void +dpkg_version_blank(struct dpkg_version *version) +{ + version->epoch = 0; + version->version = NULL; + version->revision = NULL; +} + +/** + * Test if a version is not empty. + * + * @param version The version to test. + * + * @retval true If the version is informative (i.e. not an empty version). + * @retval false If the version is empty. + */ +bool +dpkg_version_is_informative(const struct dpkg_version *version) +{ + return (version->epoch || + str_is_set(version->version) || + str_is_set(version->revision)); +} + +/** + * Give a weight to the character to order in the version comparison. + * + * @param c An ASCII character. + */ +static int +order(int c) +{ + if (c_isdigit(c)) + return 0; + else if (c_isalpha(c)) + return c; + else if (c == '~') + return -1; + else if (c) + return c + 256; + else + return 0; +} + +static int +verrevcmp(const char *a, const char *b) +{ + if (a == NULL) + a = ""; + if (b == NULL) + b = ""; + + while (*a || *b) { + int first_diff = 0; + + while ((*a && !c_isdigit(*a)) || (*b && !c_isdigit(*b))) { + int ac = order(*a); + int bc = order(*b); + + if (ac != bc) + return ac - bc; + + a++; + b++; + } + while (*a == '0') + a++; + while (*b == '0') + b++; + while (c_isdigit(*a) && c_isdigit(*b)) { + if (!first_diff) + first_diff = *a - *b; + a++; + b++; + } + + if (c_isdigit(*a)) + return 1; + if (c_isdigit(*b)) + return -1; + if (first_diff) + return first_diff; + } + + return 0; +} + +/** + * Compares two Debian versions. + * + * This function follows the convention of the comparator functions used by + * qsort(). + * + * @see deb-version(5) + * + * @param a The first version. + * @param b The second version. + * + * @retval 0 If a and b are equal. + * @retval <0 If a is smaller than b. + * @retval >0 If a is greater than b. + */ +int +dpkg_version_compare(const struct dpkg_version *a, + const struct dpkg_version *b) +{ + int rc; + + if (a->epoch > b->epoch) + return 1; + if (a->epoch < b->epoch) + return -1; + + rc = verrevcmp(a->version, b->version); + if (rc) + return rc; + + return verrevcmp(a->revision, b->revision); +} + +/** + * Check if two versions have a certain relation. + * + * @param a The first version. + * @param rel The relation. + * @param b The second version. + * + * @retval true If rel is #DPKG_RELATION_NONE or the expression “a rel b” is + * true. + * @retval false Otherwise. + * + * @warning If rel is not a valid relation, this function will terminate + * the program. + */ +bool +dpkg_version_relate(const struct dpkg_version *a, + enum dpkg_relation rel, + const struct dpkg_version *b) +{ + int rc; + + if (rel == DPKG_RELATION_NONE) + return true; + + rc = dpkg_version_compare(a, b); + + switch (rel) { + case DPKG_RELATION_EQ: + return rc == 0; + case DPKG_RELATION_LT: + return rc < 0; + case DPKG_RELATION_LE: + return rc <= 0; + case DPKG_RELATION_GT: + return rc > 0; + case DPKG_RELATION_GE: + return rc >= 0; + default: + internerr("unknown dpkg_relation %d", rel); + } + return false; +} diff --git a/lib/dpkg/version.h b/lib/dpkg/version.h new file mode 100644 index 0000000..7c3f027 --- /dev/null +++ b/lib/dpkg/version.h @@ -0,0 +1,91 @@ +/* + * libdpkg - Debian packaging suite library routines + * version.h - version handling routines + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_VERSION_H +#define LIBDPKG_VERSION_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup version Version handling + * @ingroup dpkg-public + * @{ + */ + +/** + * Data structure representing a Debian version. + * + * @see deb-version(5) + */ +struct dpkg_version { + /** The epoch. It will be zero if no epoch is present. */ + unsigned int epoch; + /** The upstream part of the version. */ + const char *version; + /** The Debian revision part of the version. */ + const char *revision; +}; + +/** + * Compound literal for a dpkg_version. + */ +#define DPKG_VERSION_OBJECT(e, v, r) \ + (struct dpkg_version){ .epoch = (e), .version = (v), .revision = (r) } + +#define DPKG_VERSION_INIT \ + DPKG_VERSION_OBJECT(0, NULL, NULL) + +/** + * Enum constants for the supported relation operations that can be done + * on Debian versions. + */ +enum DPKG_ATTR_ENUM_FLAGS dpkg_relation { + /** The “none” relation, sentinel value. */ + DPKG_RELATION_NONE = 0, + /** Equality relation (‘=’). */ + DPKG_RELATION_EQ = DPKG_BIT(0), + /** Less than relation (‘<<’). */ + DPKG_RELATION_LT = DPKG_BIT(1), + /** Less than or equal to relation (‘<=’). */ + DPKG_RELATION_LE = DPKG_RELATION_LT | DPKG_RELATION_EQ, + /** Greater than relation (‘>>’). */ + DPKG_RELATION_GT = DPKG_BIT(2), + /** Greater than or equal to relation (‘>=’). */ + DPKG_RELATION_GE = DPKG_RELATION_GT | DPKG_RELATION_EQ, +}; + +void dpkg_version_blank(struct dpkg_version *version); +bool dpkg_version_is_informative(const struct dpkg_version *version); +int dpkg_version_compare(const struct dpkg_version *a, + const struct dpkg_version *b); +bool dpkg_version_relate(const struct dpkg_version *a, + enum dpkg_relation rel, + const struct dpkg_version *b); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_VERSION_H */ |