From 11c606a6888dc269ef018359469a7276c3ad8f67 Mon Sep 17 00:00:00 2001 From: kx Date: Tue, 11 Apr 2023 01:18:34 +0300 Subject: Version 0.2.1 --- .gitignore | 45 + .svnignore | 42 + Makefile.am | 10 + README | 2 + README.md | 146 ++ acsite.m4 | 56 + bootstrap | 81 + configure.ac | 251 +++ doc/autogen-examples/auto-clean.sh | 27 + doc/autogen-examples/bootstrap-cross.sh | 6 + doc/autogen-examples/bootstrap.sh | 4 + doc/autogen-examples/configure-cross.sh | 22 + doc/autogen-examples/configure.sh | 9 + doc/dialog/dialog-1.3-20190211.patch | 453 ++++ doc/dialog/dialog-1.3-20190724.patch | 467 +++++ doc/dialog/dialog-1.3-20190728.patch | 467 +++++ doc/dialog/dialog-1.3-20190808.patch | 460 +++++ doc/dialog/dialog-1.3-20201126.patch | 462 +++++ doc/dialog/dialog-1.3-20210117.patch | 462 +++++ doc/dialog/dialog-1.3-20210621.patch | 454 ++++ src/.dialogrc | 144 ++ src/Makefile.am | 64 + src/btree.c | 1137 ++++++++++ src/btree.h | 93 + src/check-db-integrity.c | 3241 +++++++++++++++++++++++++++++ src/check-package.c | 1511 ++++++++++++++ src/check-requires.c | 2616 +++++++++++++++++++++++ src/chrefs.c | 1933 +++++++++++++++++ src/cmpvers.c | 110 + src/cmpvers.h | 35 + src/defs.h | 51 + src/dialog-ui.c | 401 ++++ src/dialog-ui.h | 59 + src/dlist.c | 680 ++++++ src/dlist.h | 83 + src/install-package.c | 2764 +++++++++++++++++++++++++ src/install-pkglist.c | 2033 ++++++++++++++++++ src/jsmin.c | 358 ++++ src/jsmin.h | 34 + src/make-package.c | 1983 ++++++++++++++++++ src/make-pkglist.c | 3155 ++++++++++++++++++++++++++++ src/make-pkglist.h | 36 + src/msglog.c | 67 + src/msglog.h | 98 + src/pkginfo.c | 1573 ++++++++++++++ src/pkglist.c | 2198 ++++++++++++++++++++ src/pkglist.h | 169 ++ src/pkglist.html.c | 1011 +++++++++ src/pkglist.html.v3.c | 992 +++++++++ src/pkglog.c | 1316 ++++++++++++ src/remove-package.c | 2234 ++++++++++++++++++++ src/system.c | 136 ++ src/system.h | 49 + src/update-package.c | 3425 +++++++++++++++++++++++++++++++ src/wrapper.c | 75 + src/wrapper.h | 36 + 56 files changed, 39826 insertions(+) create mode 100644 .gitignore create mode 100644 .svnignore create mode 100644 Makefile.am create mode 100644 README create mode 100644 README.md create mode 100644 acsite.m4 create mode 100755 bootstrap create mode 100644 configure.ac create mode 100755 doc/autogen-examples/auto-clean.sh create mode 100755 doc/autogen-examples/bootstrap-cross.sh create mode 100755 doc/autogen-examples/bootstrap.sh create mode 100755 doc/autogen-examples/configure-cross.sh create mode 100755 doc/autogen-examples/configure.sh create mode 100644 doc/dialog/dialog-1.3-20190211.patch create mode 100644 doc/dialog/dialog-1.3-20190724.patch create mode 100644 doc/dialog/dialog-1.3-20190728.patch create mode 100644 doc/dialog/dialog-1.3-20190808.patch create mode 100644 doc/dialog/dialog-1.3-20201126.patch create mode 100644 doc/dialog/dialog-1.3-20210117.patch create mode 100644 doc/dialog/dialog-1.3-20210621.patch create mode 100644 src/.dialogrc create mode 100644 src/Makefile.am create mode 100644 src/btree.c create mode 100644 src/btree.h create mode 100644 src/check-db-integrity.c create mode 100644 src/check-package.c create mode 100644 src/check-requires.c create mode 100644 src/chrefs.c create mode 100644 src/cmpvers.c create mode 100644 src/cmpvers.h create mode 100644 src/defs.h create mode 100644 src/dialog-ui.c create mode 100644 src/dialog-ui.h create mode 100644 src/dlist.c create mode 100644 src/dlist.h create mode 100644 src/install-package.c create mode 100644 src/install-pkglist.c create mode 100644 src/jsmin.c create mode 100644 src/jsmin.h create mode 100644 src/make-package.c create mode 100644 src/make-pkglist.c create mode 100644 src/make-pkglist.h create mode 100644 src/msglog.c create mode 100644 src/msglog.h create mode 100644 src/pkginfo.c create mode 100644 src/pkglist.c create mode 100644 src/pkglist.h create mode 100644 src/pkglist.html.c create mode 100644 src/pkglist.html.v3.c create mode 100644 src/pkglog.c create mode 100644 src/remove-package.c create mode 100644 src/system.c create mode 100644 src/system.h create mode 100644 src/update-package.c create mode 100644 src/wrapper.c create mode 100644 src/wrapper.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0298281 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# +# Generated by Autotools: +# ---------------------- +# +m4/ +autom4te.cache/ +aclocal.m4 +compile +config.status +config.guess +config.sub +config.log +configure +depcomp +install-sh +missing +stamp-h1 + +config.h +config.h.in + +Makefile +Makefile.in + +src/Makefile +src/Makefile.in +src/.deps/ + +# +# Object files and Binaries: +# ------------------------- +# +*.o +check-db-integrity +check-package +check-requires +chrefs +install-package +install-pkglist +make-package +make-pkglist +pkginfo +pkglog +remove-package +update-package diff --git a/.svnignore b/.svnignore new file mode 100644 index 0000000..d121d24 --- /dev/null +++ b/.svnignore @@ -0,0 +1,42 @@ +# +# Generated by Autotools: +# ---------------------- +# +m4 +autom4te.cache +aclocal.m4 +compile +config.status +config.guess +config.sub +config.log +configure +depcomp +install-sh +missing +stamp-h1 + +config.h +config.h.in + +Makefile +Makefile.in +.deps/ + +# +# Object files and Binaries: +# ------------------------- +# +*.o +check-db-integrity +check-package +check-requires +chrefs +install-package +install-pkglist +make-package +make-pkglist +pkginfo +pkglog +remove-package +update-package diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9877db3 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,10 @@ + +ACLOCAL_AMFLAGS = -I m4 + +# +# In build order: +# ============== +# +SUBDIRS = src + +EXTRA_DIST = doc LICENSE README.md acsite.m4 bootstrap src/.dialogrc src/pkglist.html.c src/pkglist.html.v3.c diff --git a/README b/README new file mode 100644 index 0000000..fa88f1c --- /dev/null +++ b/README @@ -0,0 +1,2 @@ + +see README.md instead diff --git a/README.md b/README.md new file mode 100644 index 0000000..571b711 --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ + +# [Package Tools](https://radix.pro/build-system/pkgtool/) + +**pkgtools** is a set of programs to create, install, remove, and update +packages on the root file system. + +## Table of contents + +* [Bootstrap script](#bootstrap-script) +* [Install](#install) +* [Configurations](#configurations) +* [Cross compilation example](#cross-compilation-example) +* [Dialog](#dialog) +* [License](#license) + + +## Bootstrap script + +The **bootstrap** script aspecialy created for autotools install automation. +To install autotools into source directory on build machine (i.e. when +**build == host**) the bootstrap script can be run without arguments. In this +case autotools will be installed from current root file system. + +For the cross environment the **--target-dest-dir** options allows to install +some stuf from development root file system: + +```Bash +$ TARGET_DEST_DIR=/home/developer/prog/trunk-672/dist/.s9xx-glibc/enybox-x2 \ + ./bootstrap --target-dest-dir=${TARGET_DEST_DIR} +``` + +For example, in this case the **dialog.m4** script will be taken from the +**${TARGET_DEST_DIR}/usr/share/aclocal** directory. + + +## Install + +On the build machine the installation process seems like that + +```Bash +$ tar xJvf pkgtools-0.2.1.tar.xz +$ mkdir build +$ cd build +$ ../pkgtools-0.2.1/configure --prefix=/usr +$ make +$ make install DESTDIR=$PKG exec_prefix=/ +``` + +Note that the **exec_prefix=/** used for canonical installation of +pkgtools utilities into **${DESTDIR}/sbin/** directory instead of +**${DESTDIR}/usr/sbin/** which is not corresponds to +[**FHS**](https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.pdf). + + +## Configurations + +Pkgtools support **GnuPG** signing of packages and also simple +user interface based on **dialog** library. + +#### OpenPGP support options: + +```Bash + --with-gpg2=no + --with-gpg2=yes + --with-gpg2=${TARGET_DEST_DIR}/usr +``` + +If the **--with-gpg2** option is not specified then **GnuPG** support +is disabled. + +#### Dialog options: + +```Bash + --with-dialog=no + --with-dialog=yes + --with-dialog=${TARGET_DEST_DIR}/usr + --with-dialog-test=no + --with-dialog-test=yes +``` + +Dialog support is enabled by default. The option **--with-dialog=no** +disables the dialog support. + + +#### Distribution options: + +```Bash + --with-distro-name[=NAME] The name of distribution + --with-distro-version[=VERSION] The distribution version +``` + +To show all available options you can make use of + +```Bash +$ ./configure --help +``` + + +## Cross compilation example + +```Bash +TARGET_DEST_DIR=/home/developer/prog/trunk-672/dist/.s9xx-glibc/enybox-x2 +TOOLCHAIN_PATH=/opt/toolchains/aarch64-S9XX-linux-glibc/1.1.4/bin +TARGET=aarch64-s9xx-linux-gnu + +DIALOG_CONFIG=${TARGET_DEST_DIR}/usr/bin/dialog-config \ +STRIP="${TOOLCHAIN_PATH}/${TARGET}-strip" \ +CC="${TOOLCHAIN_PATH}/${TARGET}-gcc --sysroot=${TARGET_DEST_DIR}" \ +./configure --prefix=/usr + --build=x86_64-pc-linux-gnu \ + --host=${TARGET} \ + --with-gpg2=${TARGET_DEST_DIR}/usr \ + --with-dialog=${TARGET_DEST_DIR}/usr \ + --with-dialog-test=yes +``` + +Also we can make use of additional variables such as **CFLAGS**, **LDFLAGS**: + +```Bash +LDFLAGS="-L${TARGET_DEST_DIR}/lib -L${TARGET_DEST_DIR}/usr/lib" +TARGET_INCPATH="-L${TARGET_DEST_DIR}/usr/include" +CFLAGS="${TARGET_INCPATH}" +CPPFLAGS="${TARGET_INCPATH}" +``` + + +## [Dialog](https://invisible-island.net/dialog/dialog.html) + +The original **dialog** sources have some bugs such as memory leaks and also +the **dialog** package doesn't have correct autotools scripts. If you want to +use **libdialog** with **pkgtools** then you have to install the dialog package +with our [patch](doc/dialog/dialog-1.3-20210621.patch). This patch provides +**dialog.m4** and more convenient **dialog-config** script for +[dialog-1.3-20210621.tgz](ftp://ftp.invisible-island.net/dialog/dialog-1.3-20210621.tgz) +source package. + + +## [License](https://radix.pro/legal/licenses/) + +Code and documentation copyright 2009-2023 Andrey V. Kosteltsev.
+Code and documentation released under [the **Radix.pro** License](https://cgit.radix.pro/radix/pkgtools.git/trunk/LICENSE). + +#### The text of this license can be found on our website at: + +> [https://radix.pro/licenses/LICENSE-1.0-en_US.txt](https://radix.pro/licenses/LICENSE-1.0-en_US.txt)
+> [https://radix.pro/licenses/LICENSE-1.0-en_US.txt](https://radix.pro/licenses/LICENSE-1.0-ru_RU.txt) diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 0000000..dd81602 --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,56 @@ + +dnl ============================================================ +dnl Display Configuration Headers +dnl +dnl configure.in: +dnl AC_MSG_CFG_PART() +dnl ============================================================ + +define(AC_MSG_CFG_PART,[dnl + AC_MSG_RESULT() + AC_MSG_RESULT(${TB}$1:${TN}) +])dnl + +AC_DEFUN(AC_PKGTOOLS_HEADLINE,[dnl + # Use a `Quadrigaph'. @<:@ gives you [ and @:>@ gives you ] : + TB=`echo -n -e '\033@<:@1m'` + TN=`echo -n -e '\033@<:@0m'` + echo "" + echo "Configuring ${TB}$1${TN} ($2), Version ${TB}${PACKAGE_VERSION}${TN}" + echo "$3" +])dnl + + +dnl ============================================================ +dnl Test for build_host `ln -s' . +dnl ============================ +dnl +dnl Usage: +dnl ----- +dnl AC_PATH_PROG_LN_S +dnl AC_SUBST(LN) +dnl AC_SUBST(LN_S) +dnl +dnl ============================================================ +AC_DEFUN(AC_PATH_PROG_LN_S, +[AC_PATH_PROG(LN, ln, no, /usr/local/bin:/usr/bin:/bin:$PATH) +AC_MSG_CHECKING(whether ln -s works on build host) +AC_CACHE_VAL(ac_cv_path_prog_LN_S, +[rm -f conftestdata +if $LN -s X conftestdata 2>/dev/null +then + rm -f conftestdata + ac_cv_path_prog_LN_S="$LN -s" +else + ac_cv_path_prog_LN_S="$LN" +fi])dnl +LN_S="$ac_cv_path_prog_LN_S" +if test "$ac_cv_path_prog_LN_S" = "$LN -s"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_SUBST(LN)dnl +AC_SUBST(LN_S)dnl +]) + diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..679cc4b --- /dev/null +++ b/bootstrap @@ -0,0 +1,81 @@ +#!/bin/sh + +CWD=`pwd` + +program=`basename $0` + +usage() { + cat << EOF + +Usage: $program [options] + +Options: + -h,--help Display this message. + -d,--target-dest-dir=DIR The target ROOTFS directory + [default: DIR=/]. + +EOF +} + +TARGET_DEST_DIR=/ +ACDIR=usr/share/aclocal +INCDIR=usr/include +SYSTEM_ACDIR= +SYSTEM_INCDIR= + +while [ 0 ] ; do + if [ "$1" = "-h" -o "$1" = "--help" ] ; then + usage + exit 0 + elif [ "$1" = "-d" -o "$1" = "--target-dest-dir" ] ; then + if [ "$2" = "" ] ; then + echo -e "\n${program}: ERROR: --target-dest-dir is not specified.\n" + usage + exit 1 + fi + TARGET_DEST_DIR="$2" + shift 2 + elif [[ $1 == --target-dest-dir=* ]] ; then + TARGET_DEST_DIR="`echo $1 | cut -f2 -d'='`" + shift 1 + else + if [ "$1" != "" ] ; then + echo -e "\n${program}: ERROR: Unknown argument: $1.\n" + usage + exit 1 + fi + break + fi +done + +if [ ! -d "${TARGET_DEST_DIR}" ] ; then + echo -e "\n${program}: ERROR: --target-dest-dir is not a directory.\n" + usage + exit 1 +fi + +# +# Absolute path: +# +if [ "${TARGET_DEST_DIR:0:1}" != "/" ] ; then + TARGET_DEST_DIR=${CWD}/${TARGET_DEST_DIR} +fi + +# +# Remove last '/' char: +# +if [ "${TARGET_DEST_DIR: -1}" = "/" ] ; then + len=${#TARGET_DEST_DIR} + let "len = len - 1" + tmp="${TARGET_DEST_DIR:0:$len}" + TARGET_DEST_DIR=${tmp} +fi + +SYSTEM_ACDIR="${TARGET_DEST_DIR}/${ACDIR}" +SYSTEM_INCDIR="${TARGET_DEST_DIR}/${INCDIR}" + + +aclocal --install -I m4 --force --system-acdir=${SYSTEM_ACDIR} +autoheader --include=${SYSTEM_INCDIR} +automake --gnu --add-missing --copy --force-missing +autoconf --force diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..89459a0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,251 @@ + +# ============================================================ +# Process this file with autoconf to produce +# a configure script. +# ============================================================ + +AC_PREREQ(2.71) # Minimum Autoconf version required. + + +AC_INIT([pkgtools], [0.2.1], + [support@radix.pro], [pkgtools], [https://radix.pro]) + +# ============================================================ +# m4's diversions: +# --------------- +# +# see: /use/share/autoconf/autoconf/general.m4 +# ============================================================ +m4_divert_push([M4SH-INIT]) +DISTRO_NAME=radix +DISTRO_CAPTION=Radix +DISTRO_VERSION=1.1 +DISTRO_LICENSE=Radix-1.0 +m4_divert_pop([M4SH-INIT]) + +AC_PKGTOOLS_HEADLINE([pkgtools], + [Package Tools], [Copyright (c) 2009-2023 Andrey V.Kosteltsev]) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: Init Automake environment $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(Init Automake environment) + +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects foreign no-dist-gzip dist-xz]) + +AC_CONFIG_HEADERS([config.h]) + +AC_PREFIX_DEFAULT(/usr/local) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: Test for Build Tools $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(Test for build tools) +AC_CHECK_TOOL([GCC], [gcc], [:]) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: Test for Libraries $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(Test for libraries) +AC_CHECK_DIALOG([1.3.20210621],yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") +AM_CONDITIONAL([USE_DIALOG], [test "x$HAVE_DIALOG" = "x1"]) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: Test for Runtime Tools $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(Test for runtime tools) + +# ============================================================ +# Check for GPG2 utility: +# ---------------------- +# Usage: +# not defined --with-gpg2 - Disable OpenPGP support by default +# --with-gpg2=no - Disable OpenPGP support by --with-gpg2=no option +# --with-gpg2, --with-gpg2=yes - Check whether the gpg2 program exists in path on the build machine +# --with-gpg2=${TARGET_DEST_DIR}/usr - Check whether the gpg2 program already installed on the target rootfs +# ============================================================ +AC_ARG_WITH([gpg2], [AS_HELP_STRING([--with-gpg2], + [support OpenPGP signatures (default=no)])], + [GPG2= + AS_IF([test "x$with_gpg2" != "xno"], + [AS_IF([test "x$with_gpg2" != "xyes"], + [AC_PATH_PROG([GPG2], [gpg2], [no], [${with_gpg2}/bin])], + [AC_PATH_PROG([GPG2], [gpg2], [no], [/usr/local/bin:/usr/bin:/bin:$PATH])]) + ], + [AC_MSG_CHECKING(for gpg2) + AC_MSG_RESULT([OpenPGP disabled by the --with-gpg2=no option]) + AC_SUBST(GPG2, [no]) + ]) + if test "x$GPG2" != "xno" ; then + AC_DEFINE([HAVE_GPG2], [1], [Define if you have OpenPGP program]) + else + if test "x$with_gpg2" != "xno" ; then + AC_MSG_FAILURE([--with-gpg2 was given, but test for gpg2 program failed], [1]) + fi + fi + ], + [AC_MSG_CHECKING(for gpg2) + AC_MSG_RESULT([OpenPGP disabled by default]) + AC_SUBST(GPG2, [no]) + ]) + +# ============================================================ +# m4's diversions: +# --------------- +# +# see: /use/share/autoconf/autoconf/general.m4 +# ============================================================ +m4_divert_push([HELP_WITH]) +_ACEOF + + cat <<_ACEOF + +Distribution Features: +m4_divert_pop([HELP_WITH]) + +AC_ARG_WITH([distro-name], [AS_HELP_STRING([--with-distro-name@<:@=NAME@:>@], + [Distribution Name @<:@default=${DISTRO_NAME}@:>@. Please note that the distribution NAME should not contain spaces],[30],[74])], + [AS_IF([test "x$with_distro_name" != "x"], + [DISTRO_NAME=${with_distro_name} + DISTRO_CAPTION=`echo ${with_distro_name:0:1} | tr '[a-z]' '[A-Z]'`${with_distro_name:1} + AC_DEFINE_UNQUOTED([DISTRO_NAME], ["$with_distro_name"], [Define the distribution name]) + AC_DEFINE_UNQUOTED([DISTRO_CAPTION], "$DISTRO_CAPTION", [Define the caption of the distribution]) + ], + [AC_DEFINE_UNQUOTED([DISTRO_NAME], ["$DISTRO_NAME"], [Define the distribution name]) + AC_DEFINE_UNQUOTED([DISTRO_CAPTION], ["$DISTRO_CAPTION"], [Define the caption of the distribution]) + ]) + ], + [AC_DEFINE_UNQUOTED([DISTRO_NAME], ["$DISTRO_NAME"], [Define the distribution name]) + AC_DEFINE_UNQUOTED([DISTRO_CAPTION], ["$DISTRO_CAPTION"], [Define the caption of the distribution]) + ]) + +AC_ARG_WITH([distro-version], [AS_HELP_STRING([--with-distro-version@<:@=VERSION@:>@], + [Distribution Version @<:@default=${DISTRO_VERSION}@:>@],[30],[79])], + [AS_IF([test "x$with_distro_version" != "x"], + [AC_DEFINE_UNQUOTED([DISTRO_VERSION], ["$with_distro_version"], [Define the version of distribution]) + ], + [AC_DEFINE_UNQUOTED([DISTRO_VERSION], ["$DISTRO_VERSION"], [Define the version of distribution]) + ]) + ], + [AC_DEFINE_UNQUOTED([DISTRO_VERSION], ["$DISTRO_VERSION"], [Define the version of distribution]) + ]) + +m4_divert_push([HELP_WITH]) +_ACEOF + + cat <<\_ACEOF +m4_divert_pop([HELP_WITH]) + +AC_SUBST(DISTRO_NAME) +AC_SUBST(DISTRO_CAPTION) +AC_SUBST(DISTRO_VERSION) +AC_SUBST(DISTRO_URL,[${PACKAGE_URL}]) +AC_SUBST(DISTRO_LICENSE,[${DISTRO_LICENSE}]) +AC_SUBST(PROGRAM_VERSION,[${PACKAGE_VERSION}]) + +AC_DEFINE_UNQUOTED([DISTRO_URL], ["$DISTRO_URL"], [Define the bug report URL]) +AC_DEFINE_UNQUOTED([DISTRO_LICENSE], ["$DISTRO_LICENSE"], [Define the bug report URL]) +AC_DEFINE_UNQUOTED([PROGRAM_VERSION], ["$PROGRAM_VERSION"], [Define the version of all programs in this package]) + + +# ============================================================ +# Environment Variables: +# --------------------- +# For 'Some influential environment variables:' help section +# ============================================================ +AC_ARG_VAR([STRIP], [strip command]) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: Test for Auxiliary (my be version sensitive) $$ +# $$ programs $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(Test for aux programs) +AC_PATH_PROG_LN_S +AC_PATH_PROG([TAR], [tar], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([SED], [sed], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([FIND], [find], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([CAT], [cat], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([CP], [cp], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([MV], [mv], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([RM], [rm], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([CHMOD], [chmod], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) +AC_PATH_PROG([SHA256SUM], [sha256sum], [no], [/usr/local/bin:/usr/bin:/bin:$PATH]) + + +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# $$ $$ +# $$ PART: OUTPUT Substitution $$ +# $$ $$ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +# ============================================================ +AC_MSG_CFG_PART(OUTPUT) + +AC_CONFIG_FILES([ +src/Makefile +Makefile +]) +AC_OUTPUT diff --git a/doc/autogen-examples/auto-clean.sh b/doc/autogen-examples/auto-clean.sh new file mode 100755 index 0000000..079b699 --- /dev/null +++ b/doc/autogen-examples/auto-clean.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +cd ../.. + +if [ -f "Makefile" ] ; then + make distclean +fi + +rm -rf autom4te.cache m4 + +rm -f Makefile +rm -f Makefile.in +rm -f config.h +rm -f config.h.in +rm -f config.log +rm -f config.status +rm -f compile config.guess config.sub +rm -f configure +rm -f install-sh +rm -f missing +rm -f stamp-h1 +rm -f aclocal.m4 +rm -f depcomp + +rm -rf src/.deps +rm -f src/Makefile +rm -f src/Makefile.in diff --git a/doc/autogen-examples/bootstrap-cross.sh b/doc/autogen-examples/bootstrap-cross.sh new file mode 100755 index 0000000..aa5e3f6 --- /dev/null +++ b/doc/autogen-examples/bootstrap-cross.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +TARGET_DEST_DIR=/home/kx/prog/scm/svn/platform/trunk-672/dist/.s9xx-glibc/enybox-x2 + +cd ../.. +./bootstrap --target-dest-dir=${TARGET_DEST_DIR} diff --git a/doc/autogen-examples/bootstrap.sh b/doc/autogen-examples/bootstrap.sh new file mode 100755 index 0000000..59b68f5 --- /dev/null +++ b/doc/autogen-examples/bootstrap.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd ../.. +./bootstrap diff --git a/doc/autogen-examples/configure-cross.sh b/doc/autogen-examples/configure-cross.sh new file mode 100755 index 0000000..9c74ac8 --- /dev/null +++ b/doc/autogen-examples/configure-cross.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +cd ../.. + +TARGET_DEST_DIR=/home/kx/prog/scm/svn/platform/trunk-672/dist/.s9xx-glibc/enybox-x2 +TOOLCHAIN_PATH=/opt/toolchain/aarch64-S9XX-linux-glibc/1.1.4/bin +TARGET=aarch64-s9xx-linux-gnu + +TARGET_INCPATH="-L${TARGET_DEST_DIR}/usr/include" +CFLAGS="${TARGET_INCPATH}" +CPPFLAGS="${TARGET_INCPATH}" +LDFLAGS="-L${TARGET_DEST_DIR}/lib -L${TARGET_DEST_DIR}/usr/lib" + + +DIALOG_CONFIG=${TARGET_DEST_DIR}/usr/bin/dialog-config \ +STRIP="${TOOLCHAIN_PATH}/${TARGET}-strip" \ +CC="${TOOLCHAIN_PATH}/${TARGET}-gcc --sysroot=${TARGET_DEST_DIR}" \ +./configure --prefix=/usr \ + --build=x86_64-pc-linux-gnu --host=${TARGET} \ + --with-gpg2=${TARGET_DEST_DIR}/usr \ + --with-dialog=${TARGET_DEST_DIR}/usr \ + --with-dialog-test=yes diff --git a/doc/autogen-examples/configure.sh b/doc/autogen-examples/configure.sh new file mode 100755 index 0000000..7c743fd --- /dev/null +++ b/doc/autogen-examples/configure.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +cd ../.. + +./configure --prefix=/usr \ + --with-distro-name=radix \ + --with-distro-version=1.1 \ + --with-gpg2=yes \ + --with-dialog=yes diff --git a/doc/dialog/dialog-1.3-20190211.patch b/doc/dialog/dialog-1.3-20190211.patch new file mode 100644 index 0000000..3a5ce88 --- /dev/null +++ b/doc/dialog/dialog-1.3-20190211.patch @@ -0,0 +1,453 @@ +diff -b --unified -Nr dialog-1.3-20190211-orig/checklist.c dialog-1.3-20190211/checklist.c +--- dialog-1.3-20190211-orig/checklist.c 2018-06-20 01:57:01.000000000 +0300 ++++ dialog-1.3-20190211/checklist.c 2019-07-20 15:48:25.753332500 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff -b --unified -Nr dialog-1.3-20190211-orig/dialog-config.in dialog-1.3-20190211/dialog-config.in +--- dialog-1.3-20190211-orig/dialog-config.in 2012-10-06 18:29:45.000000000 +0400 ++++ dialog-1.3-20190211/dialog-config.in 2019-07-20 15:48:25.753332500 +0300 +@@ -73,12 +73,17 @@ + INCS="-I${prefix}/include" + fi + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- $INCS ++ @CFLAGS@ $INCS ++ENDECHO ++ ;; ++ --ldflags) ++ sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO ++ -L@libdir@ + ENDECHO + ;; + --libs) + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- -L${exec_prefix}/lib -l${THIS} @LIBS@ ++ -l${THIS} @LIBS@ + ENDECHO + ;; + # identification +@@ -113,6 +118,7 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + --libs echos the libraries needed to link with ${THIS} + + --version echos the release+patchdate version of ${THIS} +diff -b --unified -Nr dialog-1.3-20190211-orig/dialog.m4 dialog-1.3-20190211/dialog.m4 +--- dialog-1.3-20190211-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20190211/dialog.m4 2019-07-20 15:48:25.749332413 +0300 +@@ -0,0 +1,333 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20190211) ++dnl AC_CHECK_DIALOG(1.3-20190211,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20190211,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) ++ +diff -b --unified -Nr dialog-1.3-20190211-orig/fselect.c dialog-1.3-20190211/fselect.c +--- dialog-1.3-20190211-orig/fselect.c 2018-06-22 02:28:04.000000000 +0300 ++++ dialog-1.3-20190211/fselect.c 2019-07-20 15:48:25.753332500 +0300 +@@ -631,7 +631,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -639,7 +639,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -648,7 +648,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -675,7 +675,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -735,7 +735,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff -b --unified -Nr dialog-1.3-20190211-orig/guage.c dialog-1.3-20190211/guage.c +--- dialog-1.3-20190211-orig/guage.c 2018-06-21 11:23:43.000000000 +0300 ++++ dialog-1.3-20190211/guage.c 2019-07-20 15:48:25.768332827 +0300 +@@ -377,6 +377,9 @@ + MY_OBJ *obj = (MY_OBJ *) objptr; + + if (valid(obj)) { ++ if (obj->title) free(obj->title); ++ if (obj->prompt) free(obj->prompt); ++ + obj->obj.keep_win = FALSE; + dlg_remove_callback(&(obj->obj)); + delink(obj); +diff -b --unified -Nr dialog-1.3-20190211-orig/menubox.c dialog-1.3-20190211/menubox.c +--- dialog-1.3-20190211-orig/menubox.c 2018-06-22 02:28:56.000000000 +0300 ++++ dialog-1.3-20190211/menubox.c 2019-07-20 15:48:25.753332500 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20190724.patch b/doc/dialog/dialog-1.3-20190724.patch new file mode 100644 index 0000000..e29bc55 --- /dev/null +++ b/doc/dialog/dialog-1.3-20190724.patch @@ -0,0 +1,467 @@ +diff -b --unified -Nr dialog-1.3-20190724-orig/checklist.c dialog-1.3-20190724/checklist.c +--- dialog-1.3-20190724-orig/checklist.c 2019-07-25 01:17:14.000000000 +0300 ++++ dialog-1.3-20190724/checklist.c 2019-07-26 14:14:01.063282363 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff -b --unified -Nr dialog-1.3-20190724-orig/dialog-config.in dialog-1.3-20190724/dialog-config.in +--- dialog-1.3-20190724-orig/dialog-config.in 2019-07-23 11:44:33.000000000 +0300 ++++ dialog-1.3-20190724/dialog-config.in 2019-07-26 14:14:01.063282363 +0300 +@@ -73,7 +73,12 @@ + INCS="-I${prefix}/include" + fi + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- $INCS ++ @CFLAGS@ $INCS ++ENDECHO ++ ;; ++ --ldflags) ++ sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO ++ -L@libdir@ + ENDECHO + ;; + --cflags-only-other) +@@ -81,12 +86,12 @@ + ;; + --libs) + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- -L${exec_prefix}/lib -l${THIS} @LIBS@ ++ -l${THIS} @LIBS@ + ENDECHO + ;; + --libs-only-L) + OPTS= +- for opt in "$LIBS" ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-L*) +@@ -98,7 +103,7 @@ + ;; + --libs-only-l) + OPTS= +- for opt in "$LIBS" ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-l*) +@@ -110,7 +115,7 @@ + ;; + --libs-only-other) + OPTS= +- for opt in "$LIBS" ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-[lL]*) +@@ -154,6 +159,7 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + --libs echos the libraries needed to link with ${THIS} + + --libs-only-L echos -L linker options (search path) for ${THIS} +diff -b --unified -Nr dialog-1.3-20190724-orig/dialog.m4 dialog-1.3-20190724/dialog.m4 +--- dialog-1.3-20190724-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20190724/dialog.m4 2019-07-26 14:14:01.063282363 +0300 +@@ -0,0 +1,333 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20190211) ++dnl AC_CHECK_DIALOG(1.3-20190211,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20190211,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) ++ +diff -b --unified -Nr dialog-1.3-20190724-orig/fselect.c dialog-1.3-20190724/fselect.c +--- dialog-1.3-20190724-orig/fselect.c 2019-07-25 02:40:15.000000000 +0300 ++++ dialog-1.3-20190724/fselect.c 2019-07-26 14:14:01.063282363 +0300 +@@ -639,7 +639,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -647,7 +647,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -656,7 +656,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -683,7 +683,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -743,7 +743,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff -b --unified -Nr dialog-1.3-20190724-orig/menubox.c dialog-1.3-20190724/menubox.c +--- dialog-1.3-20190724-orig/menubox.c 2019-07-25 02:42:20.000000000 +0300 ++++ dialog-1.3-20190724/menubox.c 2019-07-26 14:14:01.063282363 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20190728.patch b/doc/dialog/dialog-1.3-20190728.patch new file mode 100644 index 0000000..4d39715 --- /dev/null +++ b/doc/dialog/dialog-1.3-20190728.patch @@ -0,0 +1,467 @@ +diff -b --unified -Nr dialog-1.3-20190728-orig/checklist.c dialog-1.3-20190728/checklist.c +--- dialog-1.3-20190728-orig/checklist.c 2019-07-25 01:17:14.000000000 +0300 ++++ dialog-1.3-20190728/checklist.c 2019-08-01 22:20:16.712332775 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff -b --unified -Nr dialog-1.3-20190728-orig/dialog-config.in dialog-1.3-20190728/dialog-config.in +--- dialog-1.3-20190728-orig/dialog-config.in 2019-07-29 02:02:53.000000000 +0300 ++++ dialog-1.3-20190728/dialog-config.in 2019-08-01 22:20:16.712332775 +0300 +@@ -89,7 +89,12 @@ + INCS="-I${prefix}/include" + fi + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- $INCS ++ @CFLAGS@ $INCS ++ENDECHO ++ ;; ++ --ldflags) ++ sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO ++ -L@libdir@ + ENDECHO + ;; + --cflags-only-other) +@@ -97,12 +102,12 @@ + ;; + --libs) + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO +- -L${exec_prefix}/lib -l${THIS} @LIBS@ ++ -l${THIS} @LIBS@ + ENDECHO + ;; + --libs-only-L) + OPTS= +- for opt in @LIBS@ ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-L*) +@@ -114,7 +119,7 @@ + ;; + --libs-only-l) + OPTS= +- for opt in @LIBS@ ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-l*) +@@ -126,7 +131,7 @@ + ;; + --libs-only-other) + OPTS= +- for opt in @LIBS@ ++ for opt in -L@libdir@ -l${THIS} @LIBS@ + do + case "x$opt" in + x-[lL]*) +@@ -170,6 +175,7 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + --libs echos the libraries needed to link with ${THIS} + + --libs-only-L echos -L linker options (search path) for ${THIS} +diff -b --unified -Nr dialog-1.3-20190728-orig/dialog.m4 dialog-1.3-20190728/dialog.m4 +--- dialog-1.3-20190728-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20190728/dialog.m4 2019-08-01 22:20:16.712332775 +0300 +@@ -0,0 +1,333 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20190211) ++dnl AC_CHECK_DIALOG(1.3-20190211,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20190211,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) ++ +diff -b --unified -Nr dialog-1.3-20190728-orig/fselect.c dialog-1.3-20190728/fselect.c +--- dialog-1.3-20190728-orig/fselect.c 2019-07-25 02:40:15.000000000 +0300 ++++ dialog-1.3-20190728/fselect.c 2019-08-01 22:20:16.712332775 +0300 +@@ -639,7 +639,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -647,7 +647,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -656,7 +656,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -683,7 +683,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -743,7 +743,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff -b --unified -Nr dialog-1.3-20190728-orig/menubox.c dialog-1.3-20190728/menubox.c +--- dialog-1.3-20190728-orig/menubox.c 2019-07-25 02:42:20.000000000 +0300 ++++ dialog-1.3-20190728/menubox.c 2019-08-01 22:20:16.712332775 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20190808.patch b/doc/dialog/dialog-1.3-20190808.patch new file mode 100644 index 0000000..6c27a3b --- /dev/null +++ b/doc/dialog/dialog-1.3-20190808.patch @@ -0,0 +1,460 @@ +diff --unified -Nr dialog-1.3-20190808-orig/checklist.c dialog-1.3-20190808/checklist.c +--- dialog-1.3-20190808-orig/checklist.c 2019-08-05 12:14:59.000000000 +0300 ++++ dialog-1.3-20190808/checklist.c 2019-08-15 19:43:02.164788537 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff --unified -Nr dialog-1.3-20190808-orig/dialog-config.in dialog-1.3-20190808/dialog-config.in +--- dialog-1.3-20190808-orig/dialog-config.in 2019-08-02 03:20:15.000000000 +0300 ++++ dialog-1.3-20190808/dialog-config.in 2019-08-15 19:43:02.164788537 +0300 +@@ -60,7 +60,7 @@ + [ -z "$includedir" ] && includedir="${prefix}/include" + + eval LDFLAGS='"@LDFLAGS@"' +- [ -z "$LDFLAGS" ] && LDFLAGS="-L${exec_prefix}/lib" ++ [ -z "$LDFLAGS" ] && LDFLAGS="-L${libdir}" + + eval LIBS='"@LIBS@"' + LIBS="-l${THIS} $LIBS" +@@ -93,10 +93,7 @@ + ;; + # compile/link + --cflags|--cflags-only-I) +- INCS= +- if test "$includedir" != /usr/include ; then +- INCS="-I$includedir" +- fi ++ INCS="-I$includedir" + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO + $INCS + ENDECHO +@@ -110,6 +107,18 @@ + $LIBS + ENDECHO + ;; ++ --ldflags) ++ OPTS= ++ for opt in $LDFLAGS $LIBS ++ do ++ case "x$opt" in ++ x-[^l]*) ++ OPTS="$OPTS $opt" ++ ;; ++ esac ++ done ++ printf "%s\n" "$OPTS" ++ ;; + --libs-only-L) + OPTS= + for opt in $LDFLAGS $LIBS +@@ -182,6 +191,7 @@ + --cflags echos the C compiler flags needed to compile with ${THIS} + --libs echos the libraries needed to link with ${THIS} + ++ --ldflags echos the linker flags needed to link with ${THIS} + --libs-only-L echos -L linker options (search path) for ${THIS} + --libs-only-l echos -l linker options (libraries) for ${THIS} + --libs-only-other echos linker options other than -L/-l +diff --unified -Nr dialog-1.3-20190808-orig/dialog.m4 dialog-1.3-20190808/dialog.m4 +--- dialog-1.3-20190808-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20190808/dialog.m4 2019-08-15 19:43:02.164788537 +0300 +@@ -0,0 +1,333 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20190211) ++dnl AC_CHECK_DIALOG(1.3-20190211,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20190211,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) ++ +diff --unified -Nr dialog-1.3-20190808-orig/fselect.c dialog-1.3-20190808/fselect.c +--- dialog-1.3-20190808-orig/fselect.c 2019-08-09 00:28:56.000000000 +0300 ++++ dialog-1.3-20190808/fselect.c 2019-08-15 19:43:02.164788537 +0300 +@@ -639,7 +639,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -647,7 +647,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -656,7 +656,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -683,7 +683,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -743,7 +743,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff --unified -Nr dialog-1.3-20190808-orig/menubox.c dialog-1.3-20190808/menubox.c +--- dialog-1.3-20190808-orig/menubox.c 2019-08-09 00:00:23.000000000 +0300 ++++ dialog-1.3-20190808/menubox.c 2019-08-15 19:43:02.164788537 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20201126.patch b/doc/dialog/dialog-1.3-20201126.patch new file mode 100644 index 0000000..6832773 --- /dev/null +++ b/doc/dialog/dialog-1.3-20201126.patch @@ -0,0 +1,462 @@ +diff --unified -Nr dialog-1.3-20201126-orig/checklist.c dialog-1.3-20201126/checklist.c +--- dialog-1.3-20201126-orig/checklist.c 2020-11-23 03:37:47.000000000 +0300 ++++ dialog-1.3-20201126/checklist.c 2020-12-18 03:18:05.742635782 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff --unified -Nr dialog-1.3-20201126-orig/dialog-config.in dialog-1.3-20201126/dialog-config.in +--- dialog-1.3-20201126-orig/dialog-config.in 2019-09-26 03:50:46.000000000 +0300 ++++ dialog-1.3-20201126/dialog-config.in 2020-12-18 03:18:05.742635782 +0300 +@@ -79,7 +79,7 @@ + [ -n "$LFLAGS" ] && LDFLAGS=" $LFDLAGS" + LDFLAGS="-L${libdir}$LDFLAGS" + fi +- [ -z "$LDFLAGS" ] && LDFLAGS="-L${exec_prefix}/lib" ++ [ -z "$LDFLAGS" ] && LDFLAGS="-L${libdir}" + + # Ignore -L options which do not correspond to an actual directory, + # or which are standard library directories (i.e., the linker is +@@ -133,10 +133,7 @@ + ;; + # compile/link + --cflags|--cflags-only-I) +- INCS= +- if test "$includedir" != /usr/include ; then +- INCS="-I$includedir" +- fi ++ INCS="-I$includedir" + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO + $INCS + ENDECHO +@@ -145,6 +142,18 @@ + # no -D/-U options should be needed + echo + ;; ++ --ldflags) ++ OPTS= ++ for opt in $LDFLAGS $LIBS ++ do ++ case "x$opt" in ++ x-[^l]*) ++ OPTS="$OPTS $opt" ++ ;; ++ esac ++ done ++ printf "%s\n" "$OPTS" ++ ;; + --libs) + OPTS= + for opt in $lib_flags +@@ -227,8 +236,9 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} +- --libs echos the libraries needed to link with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + ++ --libs echos the libraries needed to link with ${THIS} + --libs-only-L echos -L linker options (search path) for ${THIS} + --libs-only-l echos -l linker options (libraries) for ${THIS} + --libs-only-other echos linker options other than -L/-l +diff --unified -Nr dialog-1.3-20201126-orig/dialog.m4 dialog-1.3-20201126/dialog.m4 +--- dialog-1.3-20201126-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20201126/dialog.m4 2020-12-18 03:18:05.742635782 +0300 +@@ -0,0 +1,332 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20201126) ++dnl AC_CHECK_DIALOG(1.3-20201126,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20201126,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) +diff --unified -Nr dialog-1.3-20201126-orig/fselect.c dialog-1.3-20201126/fselect.c +--- dialog-1.3-20201126-orig/fselect.c 2020-11-23 12:03:54.000000000 +0300 ++++ dialog-1.3-20201126/fselect.c 2020-12-18 03:18:05.742635782 +0300 +@@ -649,7 +649,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -657,7 +657,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -666,7 +666,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = dlg_der_window(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -692,7 +692,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = dlg_der_window(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -750,7 +750,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff --unified -Nr dialog-1.3-20201126-orig/menubox.c dialog-1.3-20201126/menubox.c +--- dialog-1.3-20201126-orig/menubox.c 2020-11-24 00:03:11.000000000 +0300 ++++ dialog-1.3-20201126/menubox.c 2020-12-18 03:18:05.742635782 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20210117.patch b/doc/dialog/dialog-1.3-20210117.patch new file mode 100644 index 0000000..68b1eba --- /dev/null +++ b/doc/dialog/dialog-1.3-20210117.patch @@ -0,0 +1,462 @@ +diff --unified -Nr dialog-1.3-20210117-orig/checklist.c dialog-1.3-20210117/checklist.c +--- dialog-1.3-20210117-orig/checklist.c 2020-11-23 03:37:47.000000000 +0300 ++++ dialog-1.3-20210117/checklist.c 2021-02-14 20:14:51.095326506 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff --unified -Nr dialog-1.3-20210117-orig/dialog-config.in dialog-1.3-20210117/dialog-config.in +--- dialog-1.3-20210117-orig/dialog-config.in 2019-09-26 03:50:46.000000000 +0300 ++++ dialog-1.3-20210117/dialog-config.in 2021-02-14 20:14:51.095326506 +0300 +@@ -79,7 +79,7 @@ + [ -n "$LFLAGS" ] && LDFLAGS=" $LFDLAGS" + LDFLAGS="-L${libdir}$LDFLAGS" + fi +- [ -z "$LDFLAGS" ] && LDFLAGS="-L${exec_prefix}/lib" ++ [ -z "$LDFLAGS" ] && LDFLAGS="-L${libdir}" + + # Ignore -L options which do not correspond to an actual directory, + # or which are standard library directories (i.e., the linker is +@@ -133,10 +133,7 @@ + ;; + # compile/link + --cflags|--cflags-only-I) +- INCS= +- if test "$includedir" != /usr/include ; then +- INCS="-I$includedir" +- fi ++ INCS="-I$includedir" + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO + $INCS + ENDECHO +@@ -145,6 +142,18 @@ + # no -D/-U options should be needed + echo + ;; ++ --ldflags) ++ OPTS= ++ for opt in $LDFLAGS $LIBS ++ do ++ case "x$opt" in ++ x-[^l]*) ++ OPTS="$OPTS $opt" ++ ;; ++ esac ++ done ++ printf "%s\n" "$OPTS" ++ ;; + --libs) + OPTS= + for opt in $lib_flags +@@ -227,8 +236,9 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} +- --libs echos the libraries needed to link with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + ++ --libs echos the libraries needed to link with ${THIS} + --libs-only-L echos -L linker options (search path) for ${THIS} + --libs-only-l echos -l linker options (libraries) for ${THIS} + --libs-only-other echos linker options other than -L/-l +diff --unified -Nr dialog-1.3-20210117-orig/dialog.m4 dialog-1.3-20210117/dialog.m4 +--- dialog-1.3-20210117-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20210117/dialog.m4 2021-02-14 20:14:51.095326506 +0300 +@@ -0,0 +1,332 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20210117) ++dnl AC_CHECK_DIALOG(1.3-20210117,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20210117,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o LIBS=\"$LIBS\"]) ++ cross_compile=no ++ define(_code1, [dnl ++ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ++ ]) ++ define(_code2, [dnl ++ ++int main( void ) ++{ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "\\Z7Test\\Zn \\Z1dialog\\Zn \\Z7Library\\Zn"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " \\Z4Dialog ==>\\Zn\\Z1libdialog\\Zn\\Z4<== [required]\\Zn ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ++ exit( 0 ); ++} ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_TRY_CPP(_code1, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_TRY_LINK(_code1, _code2, _ok=yes, _ok=no) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS=\"$DIALOG_CFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS=\"$DIALOG_LDFLAGS\"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS=\"$DIALOG_LIBS\"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) +diff --unified -Nr dialog-1.3-20210117-orig/fselect.c dialog-1.3-20210117/fselect.c +--- dialog-1.3-20210117-orig/fselect.c 2021-01-16 20:19:15.000000000 +0300 ++++ dialog-1.3-20210117/fselect.c 2021-02-14 20:14:51.095326506 +0300 +@@ -650,7 +650,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -658,7 +658,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -667,7 +667,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = dlg_der_window(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -693,7 +693,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = dlg_der_window(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -751,7 +751,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff --unified -Nr dialog-1.3-20210117-orig/menubox.c dialog-1.3-20210117/menubox.c +--- dialog-1.3-20210117-orig/menubox.c 2020-11-24 00:03:11.000000000 +0300 ++++ dialog-1.3-20210117/menubox.c 2021-02-14 20:14:51.095326506 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/doc/dialog/dialog-1.3-20210621.patch b/doc/dialog/dialog-1.3-20210621.patch new file mode 100644 index 0000000..7ffd1ae --- /dev/null +++ b/doc/dialog/dialog-1.3-20210621.patch @@ -0,0 +1,454 @@ +diff --unified -Nr dialog-1.3-20210621-orig/checklist.c dialog-1.3-20210621/checklist.c +--- dialog-1.3-20210621-orig/checklist.c 2020-11-23 03:37:47.000000000 +0300 ++++ dialog-1.3-20210621/checklist.c 2021-10-15 12:06:20.636637175 +0300 +@@ -29,7 +29,7 @@ + #include + #include + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + typedef struct { + /* the outer-window */ +diff --unified -Nr dialog-1.3-20210621-orig/dialog-config.in dialog-1.3-20210621/dialog-config.in +--- dialog-1.3-20210621-orig/dialog-config.in 2021-03-06 02:48:36.000000000 +0300 ++++ dialog-1.3-20210621/dialog-config.in 2021-10-15 12:06:20.635637175 +0300 +@@ -79,7 +79,7 @@ + [ -n "$LFLAGS" ] && LDFLAGS=" $LFDLAGS" + LDFLAGS="-L${libdir}$LDFLAGS" + fi +- [ -z "$LDFLAGS" ] && LDFLAGS="-L${exec_prefix}/lib" ++ [ -z "$LDFLAGS" ] && LDFLAGS="-L${libdir}" + + # Ignore -L options which do not correspond to an actual directory, + # or which are standard library directories (i.e., the linker is +@@ -134,10 +134,7 @@ + ;; + # compile/link + --cflags|--cflags-only-I) +- INCS= +- if test "$includedir" != /usr/include ; then +- INCS="-I$includedir" +- fi ++ INCS="-I$includedir" + sed -e 's,^[ ]*,,' -e 's, [ ]*, ,g' -e 's,[ ]*$,,' <<-ENDECHO + $INCS + ENDECHO +@@ -146,6 +143,18 @@ + # no -D/-U options should be needed + echo + ;; ++ --ldflags) ++ OPTS= ++ for opt in $LDFLAGS $LIBS ++ do ++ case "x$opt" in ++ x-[^l]*) ++ OPTS="$OPTS $opt" ++ ;; ++ esac ++ done ++ printf "%s\n" "$OPTS" ++ ;; + --libs) + OPTS= + for opt in $lib_flags +@@ -228,8 +237,9 @@ + --exec-prefix=ARG sets the executable-prefix of ${THIS} + + --cflags echos the C compiler flags needed to compile with ${THIS} +- --libs echos the libraries needed to link with ${THIS} ++ --ldflags echos the linker flags needed to link with ${THIS} + ++ --libs echos the libraries needed to link with ${THIS} + --libs-only-L echos -L linker options (search path) for ${THIS} + --libs-only-l echos -l linker options (libraries) for ${THIS} + --libs-only-other echos linker options other than -L/-l +diff --unified -Nr dialog-1.3-20210621-orig/dialog.m4 dialog-1.3-20210621/dialog.m4 +--- dialog-1.3-20210621-orig/dialog.m4 1970-01-01 03:00:00.000000000 +0300 ++++ dialog-1.3-20210621/dialog.m4 2021-10-15 12:06:20.635637175 +0300 +@@ -0,0 +1,324 @@ ++dnl # ++dnl # /usr/share/aclocal/dialog.m4 ++dnl # ++dnl # Configure paths for dialog ++dnl # Andrew V.Kosteltsev ++ ++dnl ============================================================ ++dnl ++dnl Synopsis: ++dnl AC_CHECK_DIALOG([MIN-VERSION [, # minimum dialog version, e.g. 1.3-20190211 ++dnl DEFAULT-WITH-DIALOG [, # default value for --with-dialog option ++dnl DEFAULT-WITH-DIALOG-TEST [,# default value for --with-dialog-test option ++dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended ++dnl ACTION-IF-FOUND [, # action to perform if dialog was found ++dnl ACTION-IF-NOT-FOUND # action to perform if dialog was not found ++dnl ]]]]]]) ++dnl Examples: ++dnl AC_CHECK_DIALOG(1.3-20210621) ++dnl AC_CHECK_DIALOG(1.3-20210621,,,no,CFLAGS="$CFLAGS -DHAVE_DIALOG $DIALOG_CFLAGS") ++dnl AC_CHECK_DIALOG(1.3-20210621,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_DIALOG") ++dnl ++dnl ++dnl If you have to change prefix returned by dialog-config script or change ++dnl location of dialog-config, you may set environment variable DIALOG_CONFIG, ++dnl for example: ++dnl ++dnl # export DIALOG_CONFIG="dialog-config --prefix=/usr/local" ++dnl # export DIALOG_CONFIG="/usr/bin/dialog-config --prefix=/usr/local" ++dnl ++dnl ============================================================ ++dnl ++dnl ============================================================ ++dnl auxilliary macros ++dnl ============================================================ ++AC_DEFUN([_AC_DIALOG_ERROR], [dnl ++AC_MSG_RESULT([*FAILED*]) ++cat <>/ /' 1>&2 ++$1 ++EOT ++exit 1 ++]) ++ ++AC_DEFUN([_AC_DIALOG_VERBOSE], [dnl ++if test ".$verbose" = .yes; then ++ AC_MSG_RESULT([ $1]) ++fi ++]) ++ ++dnl ============================================================ ++dnl the user macro ++dnl ============================================================ ++AC_DEFUN([AC_CHECK_DIALOG], [dnl ++dnl ++dnl ============================================================ ++dnl prerequisites ++dnl ============================================================ ++AC_REQUIRE([AC_PROG_CC])dnl ++AC_REQUIRE([AC_PROG_CPP])dnl ++dnl ++dnl ============================================================ ++dnl set DIALOG_CONFIG variable ++dnl ============================================================ ++if test -z "$DIALOG_CONFIG"; then ++ DIALOG_CONFIG='dialog-config' ++fi ++dnl ++DIALOG_CFLAGS='' ++DIALOG_LDFLAGS='' ++DIALOG_LIBS='' ++AC_SUBST(DIALOG_CFLAGS) ++AC_SUBST(DIALOG_LDFLAGS) ++AC_SUBST(DIALOG_LIBS) ++dnl ++dnl ============================================================ ++dnl command line options ++dnl ============================================================ ++_AC_DIALOG_VERBOSE([]) ++AC_ARG_WITH(dialog,dnl ++[ --with-dialog[=ARG] Build with dialog Library (default=]ifelse([$2],,yes,$2)[)],dnl ++,dnl ++with_dialog="ifelse([$2],,yes,$2)" ++)dnl ++AC_ARG_WITH(dialog-test,dnl ++[ --with-dialog-test Perform dialog Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl ++,dnl ++with_dialog_test="ifelse([$3],,yes,$3)" ++)dnl ++_AC_DIALOG_VERBOSE([+ Command Line Options:]) ++_AC_DIALOG_VERBOSE([ o --with-dialog=$with_dialog]) ++_AC_DIALOG_VERBOSE([ o --with-dialog-test=$with_dialog_test]) ++dnl ++dnl ============================================================ ++dnl configuration ++dnl ============================================================ ++if test ".$with_dialog" != .no; then ++ dialog_subdir=no ++ dialog_subdir_opts='' ++ case "$with_dialog" in ++ subdir:* ) ++ dialog_subdir=yes ++ changequote(, )dnl ++ dialog_subdir_opts=`echo $with_dialog | sed -e 's/^subdir:[^ ]*[ ]*//'` ++ with_dialog=`echo $with_dialog | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` ++ changequote([, ])dnl ++ ;; ++ esac ++ dialog_version="" ++ dialog_location="" ++ dialog_type="" ++ dialog_cflags="" ++ dialog_ldflags="" ++ dialog_libs="" ++ if test ".$with_dialog" = .yes; then ++ # via config script in $PATH ++ changequote(, )dnl ++ dialog_version=`($DIALOG_CONFIG --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[-][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$DIALOG_CONFIG --prefix` ++ dialog_type='installed' ++ dialog_cflags=`$DIALOG_CONFIG --cflags` ++ dialog_ldflags=`$DIALOG_CONFIG --ldflags` ++ dialog_libs=`$DIALOG_CONFIG --libs` ++ fi ++ elif test -d "$with_dialog"; then ++ with_dialog=`echo $with_dialog | sed -e 's;/*$;;'` ++ dialog_found=no ++ # via config script under a specified directory ++ # (a standard installation, but not a source tree) ++ if test ".$dialog_found" = .no; then ++ for _dir in $with_dialog/bin $with_dialog; do ++ if test -f "$_dir/dialog-config"; then ++ test -f "$_dir/dialog-config.in" && continue # dialog-config in source tree! ++ changequote(, )dnl ++ dialog_version=`($_dir/dialog-config --version) 2>/dev/null |\ ++ sed -e 's/^.*\([0-9]\.[0-9]*[.][0-9]*\).*$/\1/'` ++ changequote([, ])dnl ++ if test ".$dialog_version" != .; then ++ dialog_location=`$_dir/dialog-config --prefix` ++ dialog_type="installed" ++ dialog_cflags=`$_dir/dialog-config --cflags` ++ dialog_ldflags=`$_dir/dialog-config --ldflags` ++ dialog_libs=`$_dir/dialog-config --libs` ++ dialog_found=yes ++ break ++ fi ++ fi ++ done ++ fi ++ fi ++ _AC_DIALOG_VERBOSE([+ Determined Location:]) ++ _AC_DIALOG_VERBOSE([ o path: $dialog_location]) ++ _AC_DIALOG_VERBOSE([ o type: $dialog_type]) ++ if test ".$dialog_version" = .; then ++ if test ".$with_dialog" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog under $with_dialog. ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past).]) ++ else ++ _AC_DIALOG_ERROR([dnl ++ Unable to locate dialog in any system-wide location (see \$PATH). ++ Please specify the correct path to either a dialog installation tree ++ (use --with-dialog=DIR if you used --prefix=DIR for installing dialog in ++ the past, or set the DIALOG_CONFIG environment variable to the full path ++ to dialog-config).]) ++ fi ++ fi ++ dnl ======================================================== ++ dnl Check whether the found version is sufficiently new ++ dnl ======================================================== ++ _req_version="ifelse([$1],,1.0.0,$1)" ++ for _var in dialog_version _req_version; do ++ eval "_val=\"\$${_var}\"" ++ _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\1/'` ++ _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\2/'` ++ _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[.]]\)\([[0-9]]*\)/\4/'` ++ _hex=`echo dummy | awk '{ printf("%d%02d%02d", major, minor, micro); }' \ ++ "major=$_major" "minor=$_minor" "micro=$_micro"` ++ eval "${_var}_hex=\"\$_hex\"" ++ done ++ _AC_DIALOG_VERBOSE([+ Determined Versions:]) ++ _AC_DIALOG_VERBOSE([ o existing: $dialog_version -> 0x$dialog_version_hex]) ++ _AC_DIALOG_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) ++ _ok=0 ++ if test ".$dialog_version_hex" != .; then ++ if test ".$_req_version_hex" != .; then ++ if test $dialog_version_hex -ge $_req_version_hex; then ++ _ok=1 ++ fi ++ fi ++ fi ++ if test ".$_ok" = .0; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog version $dialog_version, but required at least version $_req_version. ++ Upgrade dialog under $dialog_location to $_req_version or higher first, please.]) ++ fi ++ dnl ======================================================== ++ dnl Perform dialog Sanity Compile Check ++ dnl ======================================================== ++ if test ".$with_dialog_test" = .yes; then ++ _ac_save_CFLAGS="$CFLAGS" ++ _ac_save_LDFLAGS="$LDFLAGS" ++ _ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ _AC_DIALOG_VERBOSE([+ Test Build Environment:]) ++ _AC_DIALOG_VERBOSE([ o CFLAGS="$CFLAGS"]) ++ _AC_DIALOG_VERBOSE([ o LDFLAGS="$LDFLAGS"]) ++ _AC_DIALOG_VERBOSE([ o LIBS="$LIBS"]) ++ cross_compile=no ++ define([_code1], [ ++#include ++#include ++#include /* index(3) */ ++ ++#include ++#include ++#include ++ ]) ++ define([_code2], [ ++ int status = 0; ++ ++ bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); ++ ++ init_dialog(stdin, stdout); ++ ++ dialog_vars.colors = 1; ++ dialog_vars.backtitle = "Test dialog Library"; ++ dialog_vars.dlg_clear_screen = 1; ++ dialog_vars.sleep_secs = 1; ++ ++ ++ dlg_put_backtitle(); ++ ++ /************************************************* ++ Ruler: 68 characters + 2 spaces left and right: ++ ++ | ----handy-ruler----------------------------------------------------- | */ ++ status = dialog_msgbox( " Dialog ==>libdialog<== [required] ", ++ "\nPackage is installed and corect.\n", ++ 5, 72, 0 ); ++ ++ if( dialog_vars.sleep_secs ) ++ (void)napms(dialog_vars.sleep_secs * 1000); ++ ++ if( dialog_vars.dlg_clear_screen ) ++ { ++ dlg_clear(); ++ (void)refresh(); ++ } ++ end_dialog(); ++ ]) ++ _AC_DIALOG_VERBOSE([+ Performing Sanity Checks:]) ++ _AC_DIALOG_VERBOSE([ o pre-processor test]) ++ AC_PREPROC_IFELSE([AC_LANG_PROGRAM([_code1], [_code2])], [_ok=yes], [_ok=no]) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity pre-processor check. This means ++ the dialog header dialog.h was not found. ++ We used the following build environment: ++ >> CPP="$CPP" ++ See config.log for possibly more details.]) ++ fi ++ _AC_DIALOG_VERBOSE([ o link check]) ++ AC_LINK_IFELSE([AC_LANG_PROGRAM([_code1], [_code2])], [_ok=yes], [_ok=no]) ++ if test ".$_ok" != .yes; then ++ _AC_DIALOG_ERROR([dnl ++ Found dialog $dialog_version under $dialog_location, but ++ was unable to perform a sanity linker check. This means ++ the dialog library libdialog.a was not found. ++ We used the following build environment: ++ >> CC="$CC" ++ >> CFLAGS="$CFLAGS" ++ >> LDFLAGS="$LDFLAGS" ++ >> LIBS="$LIBS" ++ See config.log for possibly more details.]) ++ fi ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" != .yes; then ++ CFLAGS="$_ac_save_CFLAGS" ++ LDFLAGS="$_ac_save_LDFLAGS" ++ LIBS="$_ac_save_LIBS" ++ fi ++ else ++ _extendvars="ifelse([$4],,yes,$4)" ++ if test ".$_extendvars" = .yes; then ++ if test ".$dialog_subdir" = .yes; then ++ CFLAGS="$CFLAGS $dialog_cflags" ++ LDFLAGS="$LDFLAGS $dialog_ldflags" ++ LIBS="$LIBS $dialog_libs" ++ fi ++ fi ++ fi ++ DIALOG_CFLAGS="$dialog_cflags" ++ DIALOG_LDFLAGS="$dialog_ldflags" ++ DIALOG_LIBS="$dialog_libs" ++ AC_SUBST(DIALOG_CFLAGS) ++ AC_SUBST(DIALOG_LDFLAGS) ++ AC_SUBST(DIALOG_LIBS) ++ ++ AC_SUBST(HAVE_DIALOG, [1]) ++ ++ AC_CHECK_HEADERS(dialog.h dlg_colors.h dlg_keys.h) ++ ++ _AC_DIALOG_VERBOSE([+ Final Results:]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_CFLAGS="$DIALOG_CFLAGS"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LDFLAGS="$DIALOG_LDFLAGS"]) ++ _AC_DIALOG_VERBOSE([ o DIALOG_LIBS="$DIALOG_LIBS"]) ++fi ++if test ".$with_dialog" != .no; then ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([version $dialog_version, $dialog_type under $dialog_location]) ++ ifelse([$5], , :, [$5]) ++else ++ AC_MSG_CHECKING(for libdialog) ++ AC_MSG_RESULT([no]) ++ ifelse([$6], , :, [$6]) ++fi ++]) +diff --unified -Nr dialog-1.3-20210621-orig/fselect.c dialog-1.3-20210621/fselect.c +--- dialog-1.3-20210621-orig/fselect.c 2021-06-21 22:50:35.000000000 +0300 ++++ dialog-1.3-20210621/fselect.c 2021-10-15 12:06:20.635637175 +0300 +@@ -659,7 +659,7 @@ + dlg_print_size(height, width); + dlg_ctl_size(height, width); + +- dialog = dlg_new_window(height, width, ++ dialog = dlg_new_window(height + 1, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "fselect", binding); +@@ -667,7 +667,7 @@ + + dlg_mouse_setbase(0, 0); + +- dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); ++ dlg_draw_box2(dialog, 0, 0, height + 1, width, dialog_attr, border_attr, border2_attr); + dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); + dlg_draw_title(dialog, title); + +@@ -676,7 +676,7 @@ + /* Draw the input field box */ + tbox_height = 1; + tbox_width = width - (4 * MARGIN + 2); +- tbox_y = height - (BTN_HIGH * 2) + MARGIN; ++ tbox_y = height - (BTN_HIGH * 2) + MARGIN + 1; + tbox_x = (width - tbox_width) / 2; + + w_text = dlg_der_window(dialog, tbox_height, tbox_width, tbox_y, tbox_x); +@@ -702,7 +702,7 @@ + else + dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; + dbox_height = height - MIN_HIGH; +- dbox_y = (2 * MARGIN + 1); ++ dbox_y = (2 * MARGIN + 2); + dbox_x = tbox_x; + + w_work = dlg_der_window(dialog, dbox_height, dbox_width, dbox_y, dbox_x); +@@ -760,7 +760,7 @@ + if (show_buttons) { + show_buttons = FALSE; + button = (state < 0) ? 0 : state; +- dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); ++ dlg_draw_buttons(dialog, height - 1, 0, buttons, button, FALSE, width); + } + + if (first_trace) { +diff --unified -Nr dialog-1.3-20210621-orig/menubox.c dialog-1.3-20210621/menubox.c +--- dialog-1.3-20210621-orig/menubox.c 2020-11-24 00:03:11.000000000 +0300 ++++ dialog-1.3-20210621/menubox.c 2021-10-15 12:06:20.636637175 +0300 +@@ -48,7 +48,7 @@ + int item_no; + } ALL_DATA; + +-#define MIN_HIGH (1 + (5 * MARGIN)) ++#define MIN_HIGH 4 + + #define INPUT_ROWS 3 /* rows per inputmenu entry */ + diff --git a/src/.dialogrc b/src/.dialogrc new file mode 100644 index 0000000..50ed037 --- /dev/null +++ b/src/.dialogrc @@ -0,0 +1,144 @@ +# +# Run-time configuration file for dialog, matches Radix color scheme. +# +# Types of values: +# +# Number - +# String - "string" +# Boolean - +# Attribute - (foreground,background,highlight?) + +# Set aspect-ration. +aspect = 0 + +# Set separator (for multiple widgets output). +separate_widget = "" + +# Set tab-length (for textbox tab-conversion). +tab_len = 0 + +# Make tab-traversal for checklist, etc., include the list. +visit_items = OFF + +# Shadow dialog boxes? This also turns on color. +use_shadow = ON + +# Turn color support ON or OFF +use_colors = ON + +# Screen color +screen_color = (WHITE,BLACK,ON) + +# Shadow color +shadow_color = (BLACK,BLACK,OFF) + +# Dialog box color +dialog_color = (BLACK,WHITE,OFF) + +# Dialog box title color +title_color = (BLACK,WHITE,ON) + +# Dialog box border color +border_color = (WHITE,WHITE,ON) + + +# Active button color +button_active_color = (WHITE,BLACK,ON) + +# Inactive button color +button_inactive_color = (BLACK,WHITE,OFF) + +# Active button key color +button_key_active_color = (YELLOW,BLACK,ON) + +# Inactive button key color +button_key_inactive_color = (RED,WHITE,ON) + +# Active button label color +button_label_active_color = (WHITE,BLACK,ON) + +# Inactive button label color +button_label_inactive_color = (BLACK,WHITE,ON) + +# Input box color +inputbox_color = (BLUE,WHITE,ON) + +# Input box border color +inputbox_border_color = (WHITE,WHITE,ON) + +# Search box color +searchbox_color = (YELLOW,WHITE,ON) + +# Search box title color +searchbox_title_color = (WHITE,WHITE,ON) + +# Search box border color +searchbox_border_color = (RED,WHITE,OFF) + +# File position indicator color +position_indicator_color = (RED,WHITE,ON) + +# Menu box color +menubox_color = dialog_color + +# Menu box border color +menubox_border_color = border_color + +# Item color +item_color = (BLACK,WHITE,ON) + +# Selected item color +item_selected_color = (BLACK,WHITE,OFF) + +# Tag color +tag_color = (BLACK,WHITE,ON) + +# Selected tag color +tag_selected_color = (BLACK,WHITE,OFF) + +# Tag key color +tag_key_color = (RED,WHITE,ON) + +# Selected tag key color +tag_key_selected_color = (YELLOW,BLACK,ON) + +# Check box color +check_color = dialog_color + +# Selected check box color +check_selected_color = (RED,WHITE,ON) + + +# Up arrow color +uarrow_color = (RED,WHITE,OFF) + +# Down arrow color +darrow_color = uarrow_color + + +# Item help-text color +itemhelp_color = shadow_color + +# Active form text color +form_active_text_color = inputbox_color + +# Form text color +form_text_color = (BLACK,WHITE,ON) + +# Readonly form item color +form_item_readonly_color = (CYAN,WHITE,ON) + +# Dialog box gauge color +gauge_color = (BLACK,WHITE,ON) + +# Dialog box border2 color +border2_color = dialog_color + +# Input box border2 color +inputbox_border2_color = border2_color + +# Search box border2 color +searchbox_border2_color = border2_color + +# Menu box border2 color +menubox_border2_color = border2_color diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..656ef2f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,64 @@ + +noinst_HEADERS = defs.h cmpvers.h dlist.h btree.h jsmin.h make-pkglist.h msglog.h wrapper.h \ + pkglist.h system.h dialog-ui.h + +sbin_PROGRAMS = chrefs pkginfo pkglog make-package make-pkglist check-db-integrity check-package check-requires \ + install-package remove-package update-package install-pkglist + + +chrefs_SOURCES = chrefs.c system.c msglog.c wrapper.c +pkginfo_SOURCES = pkginfo.c system.c msglog.c wrapper.c +pkglog_SOURCES = pkglog.c system.c msglog.c wrapper.c + +check_db_integrity_SOURCES = check-db-integrity.c system.c msglog.c wrapper.c cmpvers.c dlist.c btree.c jsmin.c pkglist.c +check_db_integrity_LDADD = -lm + +check_requires_SOURCES = check-requires.c system.c msglog.c wrapper.c cmpvers.c dlist.c btree.c jsmin.c pkglist.c +check_requires_LDADD = -lm + +check_package_SOURCES = check-package.c system.c msglog.c wrapper.c cmpvers.c + +make_pkglist_SOURCES = make-pkglist.c system.c msglog.c wrapper.c cmpvers.c dlist.c btree.c jsmin.c pkglist.c +make_pkglist_LDADD = -lm + +make_package_SOURCES = make-package.c system.c msglog.c wrapper.c dlist.c +make_package_LDADD = -lm + +install_package_SOURCES = install-package.c system.c msglog.c wrapper.c cmpvers.c dlist.c +install_package_LDADD = -lm +if USE_DIALOG + install_package_SOURCES += dialog-ui.c + install_package_CFLAFS = $(DIALOG_CFLAGS) + install_package_LDFLAGS = $(DIALOG_LDFLAGS) + install_package_LDADD += $(DIALOG_LIBS) +endif + +remove_package_SOURCES = remove-package.c system.c msglog.c wrapper.c cmpvers.c dlist.c +remove_package_LDADD = -lm +if USE_DIALOG + remove_package_SOURCES += dialog-ui.c + remove_package_CFLAFS = $(DIALOG_CFLAGS) + remove_package_LDFLAGS = $(DIALOG_LDFLAGS) + remove_package_LDADD += $(DIALOG_LIBS) +endif + +update_package_SOURCES = update-package.c system.c msglog.c wrapper.c cmpvers.c dlist.c +update_package_LDADD = -lm +if USE_DIALOG + update_package_SOURCES += dialog-ui.c + update_package_CFLAFS = $(DIALOG_CFLAGS) + update_package_LDFLAGS = $(DIALOG_LDFLAGS) + update_package_LDADD += $(DIALOG_LIBS) +endif + +install_pkglist_SOURCES = install-pkglist.c system.c msglog.c wrapper.c cmpvers.c dlist.c +install_pkglist_LDADD = -lm -lpthread +if USE_DIALOG + install_pkglist_SOURCES += dialog-ui.c + install_pkglist_CFLAFS = $(DIALOG_CFLAGS) + install_pkglist_LDFLAGS = $(DIALOG_LDFLAGS) + install_pkglist_LDADD += $(DIALOG_LIBS) +endif + + +pkgdata_DATA = .dialogrc diff --git a/src/btree.c b/src/btree.c new file mode 100644 index 0000000..e64ef7e --- /dev/null +++ b/src/btree.c @@ -0,0 +1,1137 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +struct btree *__btree_alloc( void *data ) +{ + struct btree *node = NULL; + + node = (struct btree *)malloc( sizeof( struct btree ) ); + if( !node ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)node, sizeof( struct btree ) ); + node->left = node->right = node; + + if( data ) node->data = data; + + return node; +} + +struct btree *btree_insert_left( struct btree *tree, struct btree *node ) +{ + if( !tree ) return node; + if( !node ) return tree; + + node->left = tree->left; + node->ltag = tree->ltag; + tree->left = node; + tree->ltag = 1; + node->right = tree; + node->rtag = 0; + + node->parent = tree; + + if( node->ltag ) + { + node->left->right = node; + } + + return node; +} + +struct btree *btree_insert_right( struct btree *tree, struct btree *node ) +{ + if( !tree ) return node; + if( !node ) return tree; + + node->right = tree->right; + node->rtag = tree->rtag; + tree->right = node; + tree->rtag = 1; + node->left = tree; + node->ltag = 0; + + node->parent = tree; + + if( node->rtag ) + { + node->right->left = node; + } + + return node; +} + + +static struct btree *__next_preorder( struct btree *node ) +{ + struct btree *next = node; + + if( !next ) return next; + + if( next->ltag ) + return next->left; + + if( next->rtag ) + return next->right; + + while( !next->rtag && next != next->right ) + next = next->right; + + if( next->ltag && next->rtag ) + next = next->right; + + return next; +} + +void btree_preorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ) +{ + struct btree *next = root; + + if( !next ) return; + + do + { + if( func ) { func( next->data, user_data ); } + + next = __next_preorder( next ); + + if( next == root || next == root->right ) break; + + } while( next ); + + if( next == root ) return; + + do + { + if( func ) { func( next->data, user_data ); } + + next = __next_preorder( next ); + + if( next == root || next == root->right ) break; + + } while( next ); +} + + +static struct btree *__next_postorder( struct btree *node ) +{ + struct btree *next = NULL; + + if( !node ) return next; + + next = node->right; + + if( !node->rtag ) + return next; + + while( next->ltag ) + next = next->left; + + return next; +} + +void btree_postorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ) +{ + struct btree *next = root; + + if( !next ) return; + + while( next->ltag ) + next = next->left; + + for( ; next ; next = __next_postorder( next ) ) + { + if( func ) { func( next->data, user_data ); } + + if( next == root ) break; + } + + next = __next_postorder( next ); + + for( ; next ; next = __next_postorder( next ) ) + { + if( next == root ) break; + + if( func ) { func( next->data, user_data ); } + } + +} + + +static struct btree *__start_endorder( struct btree *node ) +{ + struct btree *next = node; + + if( !next ) return next; + + do + { + while( next->ltag ) + next = next->left; + + if( !next->rtag ) + return next; + else + next = next->right; + + while( next->ltag ) + next = next->left; + + } while( next->rtag ); + + return next; +} + +static struct btree *__next_endorder( struct btree *node ) +{ + struct btree *next = node; + + if( !next ) return next; + + if( next->parent->rtag && (next != next->parent->right) ) + next = __start_endorder( next->parent->right ); + else + next = next->parent; + + return next; +} + +void btree_endorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ) +{ + struct btree *next = root; + + if( !next ) return; + + next = __start_endorder( next ); + + do + { + if( func ) { func( next->data, user_data ); } + + if( next == root ) break; + + next = __next_endorder( next ); + + } while( next ); +} + + +#if ! defined( max ) +#define max(a,b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) +#endif + +/************************************ + Tree height and width calculation: + ───────────────────────────────── + + height: + ┬ + A │ 1 + | \ ├ + B D │ 2 + | | ├ + C E │ 3 + | | \ ├ + K H F │ 4 + | \ ├ + J G │ 5 + ├──┬──┬──┬──┼ + width: 1 2 3 4 + + ************************************/ + +int btree_height( struct btree *root ) +{ + struct btree *next = root; + int height = 0; + + if( !next ) return height; + + next = __start_endorder( next ); + + do + { + struct btree *p = next; + int h = 0; + + while( p->parent ) { ++h; p = p->parent; } + height = max( height, h ); + + if( next == root ) break; + + next = __next_endorder( next ); + + } while( next ); + + return height + 1; +} + +int btree_width( struct btree *root ) +{ + int ret = 0, lw = 0, rw = 0; + struct btree *next = NULL, *left = NULL, *right = NULL; + + if( !root ) return ret; + + left = next = ( root->ltag ) ? root->left : NULL; + + if( next ) + { + ++lw; + + next = __start_endorder( next ); + + do + { + if( next->ltag && next->rtag ) + ++lw; + + if( next == left ) break; + + next = __next_endorder( next ); + + } while( next ); + } + + right = next = ( root->rtag ) ? root->right : NULL; + + if( next ) + { + ++rw; + + next = __start_endorder( next ); + + do + { + if( next->ltag && next->rtag ) + ++rw; + + if( next == right ) break; + + next = __next_endorder( next ); + + } while( next ); + } + + ret = lw + rw; + + return (ret) ? ret : 1; +} + +int btree_left_width( struct btree *root ) +{ + int lw = 0; + struct btree *next = NULL, *left = NULL; + + if( !root ) return lw; + + left = next = ( root->ltag ) ? root->left : NULL; + + if( next ) + { + ++lw; + + next = __start_endorder( next ); + + do + { + if( next->ltag && next->rtag ) + ++lw; + + if( next == left ) break; + + next = __next_endorder( next ); + + } while( next ); + } + + return (lw) ? lw : 1; +} + +int btree_right_width( struct btree *root ) +{ + int rw = 0; + struct btree *next = NULL, *right = NULL; + + if( !root ) return rw; + + right = next = ( root->rtag ) ? root->right : NULL; + + if( next ) + { + ++rw; + + next = __start_endorder( next ); + + do + { + if( next->ltag && next->rtag ) + ++rw; + + if( next == right ) break; + + next = __next_endorder( next ); + + } while( next ); + } + + return (rw) ? rw : 1; +} + + +struct btree *btree_detach( struct btree *node ) +{ + struct btree *parent = node->parent; + + if( !node ) return node; + + if( parent->right == node ) + { + struct btree *rlink = node; + + while( rlink->rtag ) + rlink = rlink->right; + rlink = rlink->right; + + parent->right = rlink; + parent->rtag = 0; + } + else + { + struct btree *llink = node; + + while( llink->ltag ) + llink = llink->left; + llink = llink->left; + + parent->left = llink; + parent->ltag = 0; + } + + return node; +} + + +void __btree_free( struct btree *root ) +{ + struct btree *next = root; + + if( !next ) return; + + next = __start_endorder( next ); + + do + { + struct btree *tmp = next; + + if( next == root ) + { + free( tmp ); + break; + } + next = __next_endorder( next ); + free( tmp ); + + } while( next ); +} + +void btree_free( struct btree *root, TREE_FUNC free_func ) +{ + btree_endorder_traversal( root, free_func, NULL ); + __btree_free( root ); +} + + +/******************************************************************* + Stack functions: + */ +struct btree_stack *btree_stack_alloc( const size_t n, const size_t u ) +{ + struct btree_stack *stack = NULL; + + if( !n || !u ) return stack; + + stack = (struct btree_stack *)malloc( sizeof( struct btree_stack ) ); + if( !stack ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)stack, sizeof( struct btree_stack ) ); + + stack->__mem_size = n * u; + stack->__unit_size = u; + + stack->__mem = malloc( stack->__mem_size ); + if( !stack->__mem ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( stack->__mem, stack->__mem_size ); + stack->__cur_brk = stack->__mem; + + return stack; +} + +void btree_stack_free( struct btree_stack **pstack ) +{ + struct btree_stack *stack = NULL; + + if( !pstack ) return; + + stack = *pstack; + + if( !stack ) return; + if( stack->__mem ) free( stack->__mem ); + + free( stack ); + + *pstack = (struct btree_stack *)NULL; +} + +int btree_stack_is_empty( struct btree_stack *stack ) +{ + if( !stack ) return 1; + if( stack->__mem == stack->__cur_brk ) return 1; + return 0; +} + +int btree_stack_depth( struct btree_stack *stack ) +{ + if( !stack ) return -1; + if( btree_stack_is_empty( stack ) ) + return 0; + + return (stack->__cur_brk - stack->__mem) / stack->__unit_size; +} + +static int __stack_brk( struct btree_stack *stack, void *end_d ) +{ + void *ptr = NULL; + + if( !stack ) return -1; + + ptr = stack->__mem; + if( !ptr ) return -1; + + if( end_d < ptr ) + { + return -1; + } + if( end_d > ptr + stack->__mem_size ) + { + size_t size = stack->__mem_size + stack->__mem_size; + + if( (end_d - (ptr + stack->__mem_size)) < stack->__mem_size ) + { + ptrdiff_t offset = stack->__cur_brk - stack->__mem; + stack->__mem = realloc( stack->__mem, size ); + if( !stack->__mem ) { FATAL_ERROR( "Cannot allocate memory" ); } + stack->__mem_size = size; + stack->__cur_brk = stack->__mem + offset; + ptr = stack->__mem; + return 0; + } + else + return -1; + } + + /* + __cur_brk = end_d; + + The function __stack_brk() only checks boundaries of + memory. The value of __cur_brk is set by __stack_sbrk() + function. + *********************************************************/ + + return 0; + +} /* End of __stack_brk() */ + +static void *__stack_sbrk( struct btree_stack *stack, int incr ) +{ + void *ptr = NULL; + int rc; + + if( !stack ) return ptr; + + ptr = stack->__cur_brk; + + if( incr == 0 ) return( ptr ); + + rc = __stack_brk( stack, ptr + incr ); + if( rc == -1 ) + { + /* errno is set into __mpu_brk() */ + return NULL; + } + + ptr = stack->__cur_brk; + stack->__cur_brk = ptr + (int)incr; + + return ptr; + +} /* End of __stack_sbrk() */ + + +int btree_stack_push( struct btree_stack *stack, const void *unit ) +{ + void *uptr, *ptr = NULL; + + if( !stack ) return -1; + + ptr = __stack_sbrk( stack, stack->__unit_size ); + + if( ptr ) + { + uptr = memcpy( ptr, unit, stack->__unit_size ); + if( !uptr ) + { + return -1; + } + } + else + { + return -1; + } + + return 0; +} + +int btree_stack_pop( struct btree_stack *stack, void *unit ) +{ + void *uptr, *ptr = NULL; + + if( !stack ) return -1; + + ptr = __stack_sbrk( stack, -(int)stack->__unit_size ); + + if( ptr ) + { + ptr -= stack->__unit_size; + uptr = memcpy( unit, (const void *)ptr, stack->__unit_size ); + if( !uptr ) + { + bzero( unit, stack->__unit_size ); + return -1; + } + } + else + { + bzero( unit, stack->__unit_size ); + return -1; + } + + return 0; +} +/* + End of stack functions. + *******************************************************************/ + +static void btree_print_line( const char *line, int indent, const struct _bctx *ctx ) +{ + char *p, buf[PATH_MAX*2]; + int depth = 0, max_depth = PATH_MAX + PATH_MAX / 2; + + if( !line || !ctx ) return; + + if( !indent ) + { + buf[0] = '\0'; + p = (char *)&buf[0]; + depth = 0; + } + else + { + buf[0] = ' '; + buf[1] = '\0'; + p = (char *)&buf[1]; + depth = ctx->indent; + } + + if( depth < 1 ) depth = 0; + if( depth > max_depth ) depth = max_depth; + + while( depth ) + { + (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; + } + + (void)sprintf( p, "%s", line ); + + fprintf( ctx->output, (char *)&buf[0] ); + fflush( ctx->output ); +} + + +static void __btree_clean_prn_flags( struct btree *root ) +{ + struct btree *next = root; + + if( !next ) return; + + next = __start_endorder( next ); + + do + { + next->lprn = 0; + next->rprn = 0; + + if( next == root ) break; + + next = __next_endorder( next ); + + } while( next ); +} + +void btree_print_json( FILE *output, struct btree *root, TREE_FUNC func ) +{ + struct btree *next = root; + struct _bctx ctx; + + struct btree_stack *stack; + struct btree_stack *lstack; + + if( !output || !next || !func ) return; + + bzero( (void *)&ctx, sizeof(struct _bctx) ); + + stack = btree_stack_alloc( (const size_t)btree_height(next), sizeof(struct _bctx) ); + + ctx.output = output; + ctx.node = next; + ctx.indent = 0; + + __btree_clean_prn_flags( root ); + + btree_stack_push( stack, (const void *)&ctx ); + + do + { + + btree_stack_pop( stack, (void *)&ctx ); + + func( next->data, (void *)&ctx ); + + if( ctx.node->ltag || ctx.node->rtag ) + { + btree_print_line( ",\n", 0, &ctx ); + btree_print_line( "\"children\": [\n", 1, &ctx ); + btree_print_line( " {\n", 1, &ctx ); + ctx.indent += 2; + btree_stack_push( stack, (const void *)&ctx ); + } + else + { + btree_stack_push( stack, (const void *)&ctx ); + + if( !ctx.node->ltag && !ctx.node->rtag ) + { + if( ctx.node->parent ) + { + struct _bctx cctx; + + btree_stack_pop( stack, (void *)&ctx ); + + memcpy( (void *)&cctx, (const void *)&ctx, sizeof(struct _bctx) ); + + if( (ctx.node->lprn == ctx.node->ltag) && (ctx.node->rprn == ctx.node->rtag) ) + { + if( ctx.node->parent->ltag && ctx.node->parent->left == ctx.node ) + ctx.node->parent->lprn = 1; + if( ctx.node->parent->rtag && ctx.node->parent->right == ctx.node ) + ctx.node->parent->rprn = 1; + } + + if( btree_stack_depth( stack ) > 0 ) + { + struct btree_stack *pstack = btree_stack_alloc( (const size_t)btree_height(root), sizeof(struct _bctx) ); + struct _bctx pctx; + + do + { + btree_stack_pop( stack, (void *)&pctx ); + + if( (cctx.node->lprn == cctx.node->ltag) && (cctx.node->rprn == cctx.node->rtag) ) + { + if( cctx.node->parent && cctx.node->parent->ltag && cctx.node->parent->left == cctx.node ) + { + cctx.node->parent->lprn = 1; + + if( cctx.node->parent->rtag ) + { + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "},\n", 1, &ctx ); + btree_print_line( "{\n", 1, &ctx ); + } + else + { + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "}\n", 1, &ctx ); + --ctx.indent; + btree_print_line( "]\n", 1, &ctx ); + } + } + if( cctx.node->parent && cctx.node->parent->rtag && cctx.node->parent->right == cctx.node ) + { + cctx.node->parent->rprn = 1; + + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "}\n", 1, &ctx ); + --ctx.indent; + btree_print_line( "]\n", 1, &ctx ); + } + + } + + memcpy( (void *)&cctx, (const void *)&pctx, sizeof(struct _bctx) ); + + if( (pctx.node->ltag && (pctx.node->lprn < pctx.node->ltag)) || (pctx.node->rtag && (pctx.node->rprn < pctx.node->rtag)) ) + { + btree_stack_push( pstack, (const void *)&pctx ); + } + + } while( !btree_stack_is_empty( stack ) ); + + while( btree_stack_pop( pstack, (void *)&pctx ) == 0 ) + { + btree_stack_push( stack, (const void *)&pctx ); + } + + btree_stack_free( &pstack ); + } + else + { + btree_print_line( "\n", 0, &ctx ); + } + + } /* End if( parent ) */ + else + { + btree_stack_pop( stack, (void *)&ctx ); + } + + } /* End if( no children ) */ + + } /* End if( any child ) */ + + + next = __next_preorder( next ); + + + if( next != root ) + { + btree_stack_pop( stack, (void *)&ctx ); + btree_stack_push( stack, (const void *)&ctx ); + + ctx.output = output; + ctx.node = next; + + if( btree_stack_push( stack, (const void *)&ctx ) == -1 ) FATAL_ERROR( "btree stack is destroyed" ); + } + + if( next == root || next == root->right ) break; + + } while( next ); + + + if( next == root ) + { + struct _bctx ctx; + + ctx.output = output; + ctx.node = root; + ctx.indent = 2; + + /* If we in root node then there is no right subtree */ + if( !root->ltag && !root->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + + if( root->ltag && (root->lprn < root->ltag) ) + { + root->lprn = 1; + + --ctx.indent; + btree_print_line( "}\n", 1, &ctx ); + --ctx.indent; + btree_print_line( "]\n", 1, &ctx ); + } + } + + if( next == root ) + { + btree_stack_free( &stack ); + return; + } + + do + { + + btree_stack_pop( stack, (void *)&ctx ); + + func( next->data, (void *)&ctx ); + + if( ctx.node->ltag || ctx.node->rtag ) + { + btree_print_line( ",\n", 0, &ctx ); + btree_print_line( "\"children\": [\n", 1, &ctx ); + btree_print_line( " {\n", 1, &ctx ); + ctx.indent += 2; + btree_stack_push( stack, (const void *)&ctx ); + } + else + { + btree_stack_push( stack, (const void *)&ctx ); + + if( !ctx.node->ltag && !ctx.node->rtag ) + { + if( ctx.node->parent ) + { + struct _bctx cctx; + + btree_stack_pop( stack, (void *)&ctx ); + + memcpy( (void *)&cctx, (const void *)&ctx, sizeof(struct _bctx) ); + + if( (ctx.node->lprn == ctx.node->ltag) && (ctx.node->rprn == ctx.node->rtag) ) + { + if( ctx.node->parent->ltag && ctx.node->parent->left == ctx.node ) + ctx.node->parent->lprn = 1; + if( ctx.node->parent->rtag && ctx.node->parent->right == ctx.node ) + ctx.node->parent->rprn = 1; + } + + if( btree_stack_depth( stack ) > 0 ) + { + struct btree_stack *pstack = btree_stack_alloc( (const size_t)btree_height(root), sizeof(struct _bctx) ); + struct _bctx pctx; + + do + { + btree_stack_pop( stack, (void *)&pctx ); + + if( (cctx.node->lprn == cctx.node->ltag) && (cctx.node->rprn == cctx.node->rtag) ) + { + if( cctx.node->parent && cctx.node->parent->ltag && cctx.node->parent->left == cctx.node ) + { + cctx.node->parent->lprn = 1; + + if( cctx.node->parent->rtag ) + { + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "},\n", 1, &ctx ); + btree_print_line( "{\n", 1, &ctx ); + } + else + { + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "}\n", 1, &ctx ); + --ctx.indent; + btree_print_line( "]\n", 1, &ctx ); + } + } + if( cctx.node->parent && cctx.node->parent->rtag && cctx.node->parent->right == cctx.node ) + { + cctx.node->parent->rprn = 1; + + if( !cctx.node->ltag && !cctx.node->rtag ) + { + btree_print_line( "\n", 0, &ctx ); + } + --ctx.indent; + btree_print_line( "}\n", 1, &ctx ); + --ctx.indent; + btree_print_line( "]\n", 1, &ctx ); + } + + } + + memcpy( (void *)&cctx, (const void *)&pctx, sizeof(struct _bctx) ); + + if( (pctx.node->ltag && (pctx.node->lprn < pctx.node->ltag)) || (pctx.node->rtag && (pctx.node->rprn < pctx.node->rtag)) ) + { + btree_stack_push( pstack, (const void *)&pctx ); + } + + } while( !btree_stack_is_empty( stack ) ); + + while( btree_stack_pop( pstack, (void *)&pctx ) == 0 ) + { + btree_stack_push( stack, (const void *)&pctx ); + } + + btree_stack_free( &pstack ); + } + else + { + btree_print_line( "\n", 0, &ctx ); + } + + } /* End if( parent ) */ + else + { + btree_stack_pop( stack, (void *)&ctx ); + } + + } /* End if( no children ) */ + + } /* End if( any child ) */ + + + next = __next_preorder( next ); + + + if( next != root ) + { + btree_stack_pop( stack, (void *)&ctx ); + btree_stack_push( stack, (const void *)&ctx ); + + ctx.output = output; + ctx.node = next; + + if( btree_stack_push( stack, (const void *)&ctx ) == -1 ) FATAL_ERROR( "btree stack is destroyed" ); + } + + if( next == root || next == root->right ) break; + + } while( next ); + + btree_stack_free( &stack ); +} + + +int btree_compare( struct btree *root_a, struct btree *root_b, TREE_CMPF cmp_func ) +{ + struct btree *next_a = root_a, *next_b = root_b; + + if( !next_a || !next_b || !cmp_func ) return 1; + + next_a = __start_endorder( next_a ); + next_b = __start_endorder( next_b ); + + do + { + if( cmp_func( next_a->data, next_b->data ) ) + { + return 1; + } + + if( next_a == root_a || next_b == root_b ) + { + if( (next_a == root_a) && (next_b == root_b) ) + break; + else + return 1; + } + + next_a = __next_endorder( next_a ); + next_b = __next_endorder( next_b ); + + } while( next_a && next_b ); + + return 0; +} + + +static void __remove_duplicates( struct btree *root, struct btree *node, TREE_CMPF cmp_func, TREE_FUNC free_func ) +{ + struct btree *next = NULL, *rem = NULL; + + if( !root || !node || !cmp_func || node == root ) return; + + + next = __next_endorder( node ); + + if( next == root ) return; + + do + { + if( !cmp_func( node->data, next->data ) ) + rem = next; + else + rem = NULL; + + if( next == root ) break; + + next = __next_endorder( next ); + + if( rem && !rem->ltag && !rem->rtag ) + { + struct btree *node = btree_detach( rem ); + + if( free_func ) + { + btree_free( node, free_func ); + } + else + { + __btree_free( node ); + } + } + + } while( next ); +} + +void btree_reduce( struct btree *root, TREE_CMPF cmp_func, TREE_FUNC free_func ) +{ + struct btree *next = root; + + if( !next || ! cmp_func ) return; + + next = __start_endorder( next ); + + do + { + __remove_duplicates( root, next, cmp_func, free_func ); + + if( next == root ) break; + + next = __next_endorder( next ); + + } while( next ); +} diff --git a/src/btree.h b/src/btree.h new file mode 100644 index 0000000..982e57a --- /dev/null +++ b/src/btree.h @@ -0,0 +1,93 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _BTREE_H_ +#define _BTREE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +struct btree { + struct btree *left, *right; + int ltag, rtag; + int lprn, rprn; + + struct btree *parent; + + void *data; +}; + +typedef void (*TREE_FUNC) ( void *data, void *user_data ); +typedef int (*TREE_CMPF) ( const void *a, const void *b ); + + +extern struct btree *__btree_alloc( void *data ); +extern struct btree *btree_insert_left( struct btree *tree, struct btree *node ); +extern struct btree *btree_insert_right( struct btree *tree, struct btree *node ); + +extern void btree_preorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ); +extern void btree_postorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ); +extern void btree_endorder_traversal( struct btree *root, TREE_FUNC func, void *user_data ); + +extern int btree_height( struct btree *root ); +extern int btree_width( struct btree *root ); +extern int btree_left_width( struct btree *root ); +extern int btree_right_width( struct btree *root ); + +extern struct btree *btree_detach( struct btree *node ); + +extern int btree_compare( struct btree *root_a, struct btree *root_b, TREE_CMPF cmp_func ); + +void __btree_free( struct btree *root ); +void btree_free( struct btree *root, TREE_FUNC free_func ); + + +struct btree_stack { + void *__mem; + void *__cur_brk; + size_t __mem_size; + size_t __unit_size; +}; + +extern struct btree_stack *btree_stack_alloc( const size_t n, const size_t u ); +extern void btree_stack_free( struct btree_stack **pstack ); +extern int btree_stack_push( struct btree_stack *stack, const void *unit ); +extern int btree_stack_pop( struct btree_stack *stack, void *unit ); + +extern int btree_stack_is_empty( struct btree_stack *stack ); +extern int btree_stack_depth( struct btree_stack *stack ); + + +struct _bctx +{ + FILE *output; + struct btree *node; + int indent; +}; + +extern void btree_reduce( struct btree *root, TREE_CMPF cmp_func, TREE_FUNC func ); +extern void btree_print_json( FILE *output, struct btree *root, TREE_FUNC func ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _BTREE_H_ */ diff --git a/src/check-db-integrity.c b/src/check-db-integrity.c new file mode 100644 index 0000000..87120b8 --- /dev/null +++ b/src/check-db-integrity.c @@ -0,0 +1,3241 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include + +#define PROGRAM_NAME "check-db-integrity" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *errlog_fname = NULL, + *tmpdir = NULL; + +int close_log_file = 0; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int __done = 0, __child = 0; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +enum _priority priority = REQUIRED; + + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( errlog_fname ) { free( errlog_fname ); errlog_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + if( close_log_file ) + { + (void)fflush( errlog ); + fclose( errlog ); + } + + free_tarballs(); + free_packages(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] [pkglogs path]\n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Check Setup Database integrity - is a procedure for checking data\n" ); + fprintf( stdout, "integrity and correcting errors. This procedure removes invalid\n" ); + fprintf( stdout, "inter-package links, and also outputs the lists of packages that\n" ); + fprintf( stdout, "need to be installed to restore system health.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + fprintf( stdout, " -l,--log= Log file name.\n" ); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Optional parameter:\n" ); + fprintf( stdout, " [pkglogs path] The PKGLOGs path in the Setup Database.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "If the [pkglogs path] is defined, then LOG information outputs to\n" ); + fprintf( stdout, "stderr and options --root, and --log are ignored.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "By default, the Setup Database is located in the\n" ); + fprintf( stdout, " '/%s/'\n", SETUP_DB_PATH ); + fprintf( stdout, "directory, the PKGLOGs files of installed packages are located in the\n" ); + fprintf( stdout, " '/%s/'\n", PACKAGES_PATH ); + fprintf( stdout, "directory; the log of this procedure is written to the\n" ); + fprintf( stdout, " '/%s/%s.log'\n", LOG_PATH, program ); + fprintf( stdout, "file.\n" ); + fprintf( stdout, "\n" ); +/* + |==================================================================| + Check Setup Database integrity - это процедура проверки целостности + данных и исправления ошибок. Данная процедура удаляет невалидные + межпакетные ссылки, а также выдает список пакетов, которые необходимо + инсталлировать для восстановления работоспособности системы. + + По умолчанию инсталляционная база находится в каталоге + + '/var/log/radix/' , + + описания инсталлированных пакетов находятся в каталоге + + '/var/log/radix/packages/' ; + + лог записывается в файл + + '/var/log/radix/check-db-integrity.log' . + |==================================================================| + */ + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +/******************************************** + LOCK FILE functions: + */ +static int __lock_file( FILE *fp ) +{ + int fd = fileno( fp ); + + if( flock( fd, LOCK_EX ) == -1 ) + { + return -1; + /* + Мы не проверяем errno == EWOULDBLOCK, так какданная ошибка + говорит о том что файл заблокирован другим процессом с флагом + LOCK_NB, а мы не собираемся циклически проверять блокировку. + У нас все просто: процесс просто ждет освобождения дескриптора + и не пытается во время ожидания выполнять другие задачи. + */ + } + return fd; +} + +static void __unlock_file( int fd ) +{ + if( fd != -1 ) flock( fd, LOCK_UN ); + /* + Здесь, в случае ошибки, мы не будем выводить + никаких сообщений. Наш процесс выполняет простую + атомарную задачу и, наверное, завершится в скором + времени, освободив все дескрипторы. + */ +} +/* + End of LOCK FILE functions. + ********************************************/ + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigchld( int signum ) +{ + pid_t pid = 0; + int status; + + (void)signum; + + while( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) + { + ; /* One of children with 'pid' is terminated */ + + if( WIFEXITED( status ) ) + { + if( (int) WEXITSTATUS (status) > 0 ) + { + ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + else + { + ; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + } + else if( WIFSIGNALED( status ) ) + { + ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */ + } + else + { + ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */ + } + + } + + if( pid == -1 && errno == ECHILD ) + { + /* No child processes: */ + __done = 1; + } + return; +} + + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigchld; /* CHLD */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + sa.sa_mask = set; + sigaction( SIGCHLD, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvr:l:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "root", required_argument, NULL, 'r' }, + { "log", required_argument, NULL, 'l' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + root = xstrdup( (const char *)optarg ); + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'l': + { + if( optarg != NULL ) + { + errlog_fname = xstrdup( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + /* + last command line argument assumes as the packages directory + in the SETUP_DB_PATH. If this argument is defined then we + ignore --root, --log options. + */ + pkgs_path = xstrdup( (const char *)argv[optind] ); + remove_trailing_slash( pkgs_path ); + + /* output LOG into stderr*/ + if( root ) { free( root ); root = NULL; } + if( errlog_fname ) { free( errlog_fname ); errlog_fname = NULL; } + errlog_fname = xstrdup( "-" ); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = xstrdup( (const char *)&buf[0] ); + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, PACKAGES_PATH ); + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + + if( S_ISDIR(st.st_mode) ) + { + pkgs_path = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + } /* End if( !pkgs_path ) */ + + if( !errlog_fname ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, LOG_PATH ); + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + + if( S_ISDIR(st.st_mode) ) + { + (void)strcat( buf, LOG_FILE ); + errlog_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "The path '%s' is not a directory", buf ); + } + } + else /* errlog_fname is defined */ + { + struct stat st; + char *buf = NULL, *dir = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + strncpy( buf, (const char *)errlog_fname, (size_t)PATH_MAX ); + buf[ PATH_MAX - 1] = '\0'; + + dir = dirname( buf ); + + if( _mkdir_p( (const char *)dir, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '%s' directory", buf ); + } + + free( buf ); + + } /* End if( !errlog_fname ) */ + +} + + +/*************************************************************** + Copy functions: + */ +static void _copy_pkglog( const char *group, const char *fname ) +{ + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_input_file( &uncompress, fname ); + + if( type == IFMT_LOG ) + { + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) { (void)sprintf( &tmp[0], "%s/%s", tmpdir, group ); } + else { (void)sprintf( &tmp[0], "%s", tmpdir ); } + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + LOG( "ERROR: Cannot copy '%s' PKGLOG file", basename( (char *)fname ) ); + exit_status += 1; + free( tmp ); + return; + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", fname, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + (void)sys_exec_command( cmd ); + ++__child; + + free( tmp ); + free( cmd ); + } +} + +static void _search_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + _copy_pkglog( grp, (const char *)path ); + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + copy_pkglogs() - returns number of copied PKGLOGS or 0 if + no PKGLOGS found in the destination + directory (SETUP_DB_PATH). + The exit_status has been set. + */ +int copy_pkglogs( void ) +{ + int ret = 0; + + __done = 0; __child = 0; + + _search_pkglogs( (const char *)pkgs_path, NULL ); + + if( __child > 0 ) + { + while( !__done ) usleep( 1 ); + ret = __child; + } + + __done = 0; __child = 0; + + return ret; +} +/* + Enf of Copy functions. + ***************************************************************/ + + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + +static size_t read_usize( char *s ) +{ + size_t size = 0; + size_t mult = 1; + double sz = 0.0; + + char suffix; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return size; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return size; + + --q; + suffix = *q; + switch( suffix ) + { + /* by default size calculates in KiB - 1024 Bytes (du -s -h .) */ + case 'G': + case 'g': + mult = 1024 * 1024; + *q = '\0'; + break; + case 'M': + case 'm': + mult = 1024; + *q = '\0'; + break; + case 'K': + case 'k': + *q = '\0'; + break; + default: + break; + } + + if( sscanf( p, "%lg", &sz ) != 1 ) return size; + + return (size_t)round( sz * (double)mult ); +} + +static int read_total_files( char *s ) +{ + int n = 0; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return n; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return n; + + if( sscanf( p, "%u", &n ) != 1 ) return 0; + + return n; +} + +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s = '\0'; + } + else + { + /* + If short description declaration is incorrect at first line + of description; then we take whole first line of description: + */ + p = index( line, ':' ); ++p; + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } + strcpy( buf, p ); + } +} + + +static int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop || !cnt ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + + /* Get reference counter */ + { + unsigned int count; + int rc; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + rc = sscanf( ln, "REFERENCE COUNTER: %u", &count ); + if( rc == 1 && cnt != NULL ) + { + *cnt = count; + } + } + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_requires_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_description_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_restore_links_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_install_script_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_file_list_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +int read_pkginfo( FILE *log, struct package *package ) +{ + int ret = -1; + + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:", + *group_pattern = "GROUP:", + *url_pattern = "URL:", + *license_pattern = "LICENSE:", + *uncompressed_size_pattern = "UNCOMPRESSED SIZE:", + *total_files_pattern = "TOTAL FILES:"; + + + if( !log || !package ) return ret; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname_pattern )) && match == ln ) /* at start of line only */ + { + package->pkginfo->name = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + package->pkginfo->version = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + package->pkginfo->arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + package->pkginfo->distro_name = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + package->pkginfo->distro_version = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + package->pkginfo->group = skip_spaces( ln + strlen( group_pattern ) ); + } + if( (match = strstr( ln, url_pattern )) && match == ln ) + { + package->pkginfo->url = skip_spaces( ln + strlen( url_pattern ) ); + } + if( (match = strstr( ln, license_pattern )) && match == ln ) + { + package->pkginfo->license = skip_spaces( ln + strlen( license_pattern ) ); + } + if( (match = strstr( ln, uncompressed_size_pattern )) && match == ln ) + { + package->pkginfo->uncompressed_size = read_usize( ln + strlen( uncompressed_size_pattern ) ); + } + if( (match = strstr( ln, total_files_pattern )) && match == ln ) + { + package->pkginfo->total_files = read_total_files( ln + strlen( total_files_pattern ) ); + } + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, log ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + bzero( (void *)buf, PATH_MAX ); + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + package->pkginfo->short_description = xstrdup( (const char *)buf ); + } + free( buf ); + } + + } /* End of while() */ + + free( line ); + + if( package->pkginfo->name == NULL ) ++ret; + if( package->pkginfo->version == NULL ) ++ret; + if( package->pkginfo->arch == NULL ) ++ret; + if( package->pkginfo->distro_name == NULL ) ++ret; + if( package->pkginfo->distro_version == NULL ) ++ret; + /* group can be equal to NULL */ + + fseek( log, 0, SEEK_SET ); + + return( ret ); +} + + +static unsigned int read_references( FILE *log, int start, unsigned int *cnt, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int counter, pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_reference( package, pkg ); + ++pkgs; + } + ++n; + } + else + break; + } + + free( line ); + + fseek( log, 0, SEEK_SET ); + + *cnt = pkgs; + + return pkgs; +} + + +static unsigned int read_requires( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "PACKAGE DESCRIPTION:" ) ) break; /* if (stop - start - 1) greater than real number of requiress */ + + if( (n > start) && (n < stop) ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_required( package, pkg ); + ++pkgs; + } + + } + ++n; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return pkgs; +} + + +static unsigned int read_description( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *pattern = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.DESCRIPTION", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + pattern = (char *)malloc( (size_t)strlen( package->pkginfo->name ) + 2 ); + if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( pattern, "%s:", package->pkginfo->name ); + + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "RESTORE LINKS:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + /* + skip non-significant spaces at beginning of line + and print lines started with 'pkgname:' + */ + if( (match = strstr( ln, pattern )) && lines < DESCRIPTION_NUMBER_OF_LINES ) + { + int mlen = strlen( match ), plen = strlen( pattern ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + fprintf( tmp, "%s\n", match ); + ++lines; + } + + } + ++n; + } + + if( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */ + while( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + fprintf( tmp, "%s\n", pattern ); + ++lines; + } + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( pattern ); + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *desc = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + desc = (char *)malloc( size + 1 ); + if( !desc ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)desc, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)desc, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->description = desc; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_restore_links( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.RESTORELINKS", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "INSTALL SCRIPT:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *links = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + links = (char *)malloc( size + 1 ); + if( !links ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)links, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)links, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->restore_links = links; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_install_script( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.INSTALL", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "FILE LIST:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *install = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + install = (char *)malloc( size + 1 ); + if( !install ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)install, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)install, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->install_script = install; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_file_list( FILE *log, int start, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + unsigned int files = 0; + + if( !log || !package ) return files; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start ) + { + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + add_file( package, (const char *)ln ); + ++files; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return files; +} + + + +static void _read_pkglog( const char *group, const char *fname ) +{ + FILE *log = NULL; + char *bname = NULL; + + if( fname != NULL ) + { + log = fopen( (const char *)fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", fname ); + } + bname = (char *)fname + strlen( tmpdir ) + 1; + } + + if( log != NULL ) + { + struct package *package = NULL; + int rc, start, stop; + unsigned int counter; + + package = package_alloc(); + + if( read_pkginfo( log, package ) != 0 ) + { + LOG( "ERROR: %s: Invalid PKGLOG file", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + + if( hardware ) package->hardware = xstrdup( (const char *)hardware ); + if( tarballs ) /* find tarball and allocate package->tarball */ + { + struct pkginfo *info = package->pkginfo; + const char *tgz = NULL; + char *buf = NULL; + struct stat sb; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( buf, "%s/%s-%s-%s-%s-%s", + info->group, info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( buf, "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + tgz = find_tarball( (const char *)&buf[0] ); + if( tgz ) + { + package->tarball = xstrdup( (const char *)tgz ); + + bzero( (void *)&buf[0], PATH_MAX ); + (void)sprintf( buf, "%s/%s", pkgs_path, tgz ); + if( stat( buf, &sb ) != -1 ) + { + info->compressed_size = (size_t)sb.st_size; + } + } + free( buf ); + } + package->procedure = INSTALL; + package->priority = priority; + + if( package->pkginfo->group && group && strcmp( package->pkginfo->group, group ) != 0 ) + { + char *tgz; + + if( package->tarball ) { tgz = package->tarball; } + else { tgz = basename( (char *)fname ); } + + WARNING( "%s: Should be moved into '%s' subdir", tgz, package->pkginfo->group ); + } + + /****************** + read references: + */ + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( counter > 0 ) + { + unsigned int pkgs = counter; + + if( read_references( log, start, &counter, package ) != pkgs ) + { + LOG( "ERROR: %s: Invalid REFERENCE COUNTER section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /****************** + read requires: + */ + rc = get_requires_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REQUIRES section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + unsigned int pkgs = (unsigned int)(stop - start - 1); /* -1 skips section header */ + + if( read_requires( log, start, stop, package ) != pkgs ) + { + LOG( "ERROR: %s: Invalid REQUIRES section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /******************* + read description: + */ + rc = get_description_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains PACKAGE DESCRIPTION section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + if( read_description( log, start, stop, package ) != (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + LOG( "ERROR: %s: Invalid DESCRIPTION section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /********************* + read restore links: + */ + rc = get_restore_links_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains RESTORE LINKS section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_restore_links( log, start, stop, package ); + } + + /********************* + read install script: + */ + rc = get_install_script_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains INSTALL SCRIPT section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_install_script( log, start, stop, package ); + } + + /***************** + read file_list: + */ + rc = get_file_list_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains FILE LIST section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( start ) + { + unsigned int files = read_file_list( log, start, package ); + if( files == (unsigned int)0 ) + { + /* + Packages that do not contain regular files are ignored. + For example, service package base/init-devices-1.2.3-s9xx-glibc-radix-1.1.txz + */ + if( ! DO_NOT_PRINTOUT_INFO ) + { + LOG( "INFO: %s: PKGLOG contains empty FILE LIST section", bname ); + } + package_free( package ); + fclose( log ); + return; + } + package->pkginfo->total_files = (int)files; + } + + /* + Здесь можно организовать проверку пакета на предмет его + целостности и правильности установки (когда будет готова + утилита check-package). + */ + add_package( package ); + + ++__child; + fclose( log ); + + /*************************************************** + Incremet REFERENCE COUNTERs of required packages: + */ + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *buf = NULL; + char *cmd = NULL, *errmsg = NULL, *wmsg = NULL; + + struct pkginfo *info = package->pkginfo; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + + errmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + wmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !wmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( &buf[0], "%s/%s-%s-%s-%s-%s", info->group, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( &buf[0], "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + + (void)sprintf( &errmsg[0], "Cannot update REFERENCE COUNTERs for '%s' package", buf ); + + len = sprintf( &cmd[0], "%s/chrefs -d %s -o inc %s > /dev/null 2>&1", selfdir, pkgs_path, buf ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + LOG( "WARNING: %s", errmsg ); + } + + if( buf ) free( buf ); + if( cmd ) free( cmd ); + if( errmsg ) free( errmsg ); + if( wmsg ) free( wmsg ); + } + /* + End of Incremet REFERENCE COUNTERs. + ***************************************************/ + } +} + +static void _read_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + if( check_input_file( NULL, (const char *)path ) == IFMT_LOG ) + { + _read_pkglog( grp, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _read_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +int read_pkglogs( void ) +{ + int ret = 0; + + __child = 0; + + _read_pkglogs( (const char *)tmpdir, NULL ); + + ret = __child; + + __child = 0; + + return ret; +} + +/*************************************************** + Decremet REFERENCE COUNTERs functions: + */ +static int save_tmp_head( FILE *log, int stop, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !stop || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( n < stop ) + { + fprintf( fp, "%s\n", ln ); + ++n; ++lines; + } + else + break; + } + + ret = lines; /* number of lines in the HEAD */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +static int save_tmp_tail( FILE *log, int start, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !start || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + fprintf( fp, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of lines in the TAIL */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +static int write_tmp_part( FILE *log, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int lines = 0; + + if( !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "r" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + fprintf( log, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of written lines */ + + free( line ); + + fclose( fp ); + + return ret; +} + + + +static char **create_references( size_t size ) +{ + char **references = (char **)0; + + if( size > 0 ) + { + references = (char **)malloc( size * sizeof(char *) ); + bzero( (void *)references, size * sizeof(char *) ); + } + + return( references ); +} + +static void free_references( char **references ) +{ + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) free( *ptr ); + ptr++; + } + free( references ); + } +} + + +static char **get_references( FILE *log, int start, unsigned int *cnt, char *grp, char *name, char *version ) +{ + char **refs = (char **)0; + char **ptr; + + char *ln = NULL; + char *line = NULL; + int n = 1; + + size_t len = 0; + + unsigned int counter, pkgs; + + char *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !name || !version ) return refs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + pkg = (char *)malloc( (size_t)PATH_MAX ); + if( !pkg ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + if( grp && *grp != '\0' ) { (void)sprintf( pkg, "%s/%s=", grp, name ); } + else { (void)sprintf( pkg, "%s=", name ); } + + len = strlen( pkg ); + + refs = ptr = create_references( counter + 1 ); /* null terminated char *references[] */ + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; pkgs = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( strncmp( ln, pkg, len ) ) /* always remove 'name=version' from list */ + { + if( refs ) + { + *ptr = xstrdup( (const char *)ln ); ++ptr; + *ptr = (char *)0; + ++pkgs; + } + } + ++n; + } + else + break; + } + + free( line ); free( pkg ); + + fseek( log, 0, SEEK_SET ); + + if( pkgs == 0 ) + { + free_references( refs ); + refs = (char **)0; + } + + *cnt = pkgs; + + return refs; +} + +static void _change_references( char *grp, char *name, char *version, const char *log_fname ) +{ + int fd; + FILE *log; + + char uncompress = '\0'; + + char *head_fname = NULL, *tail_fname = NULL; + int head_lines, tail_lines; + + int rc, start, stop; + unsigned int counter; + + int inc = 0; + + char **references = NULL; + + char *bname = (char *)log_fname + strlen( pkgs_path ) + 1; + + if( !name || !version || log_fname == NULL ) return; + if( check_input_file( &uncompress, log_fname ) != IFMT_LOG ) return; + + log = fopen( (const char *)log_fname, "r+" ); + if( !log ) + { + LOG( "ERROR: Cannot access %s file: %s", bname, strerror( errno ) ); + exit_status += 1; + return; + } + + fd = __lock_file( log ); + + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + exit_status += 1; + __unlock_file( fd ); fclose( log ); + return; + } + + head_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( head_fname, "%s/.HEAD", tmpdir ); + + tail_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( tail_fname, "%s/.TAIL", tmpdir ); + + head_lines = save_tmp_head( log, start, (const char *)head_fname ); + tail_lines = save_tmp_tail( log, stop - 1, (const char *)tail_fname ); + + if( head_lines < 10 && tail_lines < 12 ) + { + LOG( "ERROR: %s: Invalid PKGLOG file", bname ); + exit_status += 1; + __unlock_file( fd ); fclose( log ); + return; + } + + references = get_references( log, start, &counter, grp, name, version ); + + if( ftruncate( fd, 0 ) != 0 ) + { + LOG( "ERROR: Cannot change REFERENCE COUNTER in the %s file: %s", bname, strerror( errno ) ); + exit_status += 1; + free_references( references ); + __unlock_file( fd ); fclose( log ); + return; + } + + head_lines = write_tmp_part( log, (const char *)head_fname ); + + if( inc ) ++counter; + fprintf( log, "REFERENCE COUNTER: %u\n", counter ); + if( inc ) + { + if( grp && *grp != '\0' ) + { + fprintf( log, "%s/%s=%s\n", grp, name, version ); + } + else + { + fprintf( log, "%s=%s\n", name, version ); + } + } + + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) fprintf( log, "%s\n", *ptr ); + ptr++; + } + + free_references( references ); + } + + tail_lines = write_tmp_part( log, (const char *)tail_fname ); + + __unlock_file( fd ); + fclose( log ); +} + +static void _decrement_references( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + const char *fname = (const char *)user_data; + + if( pkg && fname ) + { + _change_references( pkg->group, pkg->name, pkg->version, fname ); + } +} + +static void decrement_references( const char *fname ) +{ + dlist_foreach( extern_requires, _decrement_references, (void *)fname ); +} + +static void _decrement_reference_counters( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + + if( package && package->pkginfo ) + { + char *buf = NULL; + struct pkginfo *info = package->pkginfo; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( &buf[0], "%s/%s/%s-%s-%s-%s-%s", pkgs_path, + info->group, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( &buf[0], "%s/%s-%s-%s-%s-%s", pkgs_path, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + + decrement_references( (const char *)&buf[0] ); + + free( buf ); + } +} + +static void decrement_reference_counters( void ) +{ + dlist_foreach( provides, _decrement_reference_counters, NULL ); +} + +/**************************************************************** + Если после апдейта пакет просто сменил группу, то его надо + удалить из списка внешних зависимостей, ведь он предоставляет + нужную функциональность. + + Например: libs/libspectre требует libs/cairo, а xlibs/cairo + требует libs/libspectre, однако libs/libspectre может + использовать как libs/cairo так и xlibs/cairo . + + Search installed package with specified name within any group: + */ +static char *pkglog_fname = NULL; + +static void _probe_pkglog( const char *pname, const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( pkglog_fname ) return; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL; + char *pkglog = basename( path ); + + if( (match = strstr( pkglog, pname )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = xstrdup( (const char *)pkglog ); + ++p; + while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) ) + { + /* package version starts with a number and separated by '-' */ + ++p; ++q; + } + *(--p) = '\0'; + + /******************************************************* + We have to make sure that the name we are looking for + is not shorter than the name of the found package. + */ + if( strlen(pname) >= strlen(buf) ) + { + + pkglog_fname = xstrdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( pname, (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/****************** + probe_package(): + --------------- + */ +static char *probe_package( const char *name ) +{ + char *ret = NULL; + + _probe_pkglog( name, (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + ret = pkglog_fname; + } + + return ret; +} + +static int check_installed_pkgname( const char *name ) +{ + int ret = 0; + char *fname = NULL; + + if( !name ) return ret; + + fname = probe_package( name ); + if( fname ) + { + ret = 1; + free( pkglog_fname ); + pkglog_fname = NULL; + } + + return ret; +} +/* + End of search installed package. + ****************************************************************/ + +static int logged_out = 0; + +static void _log_extern_requires( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg && !check_installed_pkgname( (const char *)pkg->name ) ) + { + if( !logged_out ) + { + /* LOG( "Система требует инсталляции следующих пакетов:" ); */ + LOG( "The System requires following packages:" ); + ++logged_out; + } + if( pkg->group ) + LOG( " %s/%s-%s : have to be %s", pkg->group, pkg->name, pkg->version, strproc( pkg->procedure ) ); + else + LOG( " %s-%s : have to be %s", pkg->name, pkg->version, strproc( pkg->procedure ) ); + } +} + +static void log_extern_requires( void ) +{ + dlist_foreach( extern_requires, _log_extern_requires, NULL ); + + if( logged_out ) { LOG( "End of requires list." ); } +} +/* + End of decremet REFERENCE COUNTERs functions. + ***************************************************/ + +/*************************************************** + Remove empty DB directories: + */ +static int is_dir_empty( const char *dirpath ) +{ + int ret = 0; + + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) return ret; /* stat returns error code; errno is set */ + if( S_ISDIR(path_sb.st_mode) == 0 ) return ret; /* dirpath is not a directory */ + if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */ + + ret = 1; + + len = strlen( dirpath ); + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + ret = 0; + break; + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + closedir( dir ); + + return ret; +} + +static void _remove_empty_dirs( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + (( root ) ? strlen( root ) : 0) ; /* do not remove leading '/' */ + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or group is not a directory", pname ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + if( is_dir_empty( (const char *)path ) ) + { + (void)rmdir( (const char *)path ); + } + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static void remove_empty_dirs( const char *path ) +{ + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( root ) + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + (void)strcat( buf, (const char *)path ); + } + else + { + (void)strcpy( buf, path ); + } + + _remove_empty_dirs( (const char *)&buf[0] ); + + free( buf ); +} +/* + End of remove empty DB directories. + ***************************************************/ + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + +static void open_errlog_file( void ) +{ + if( errlog_fname && (strcmp( errlog_fname, "-" ) != 0) ) + { + errlog = fopen( (const char *)errlog_fname, "w" ); + if( !errlog ) + { + FATAL_ERROR( "Cannot create LOG '%s' file", basename( errlog_fname ) ); + } + close_log_file = 1; + } + else + { + errlog = stderr; + close_log_file = 0; + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + open_errlog_file(); + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + LOG( "Check Setup Database Integrity:" ); + + /* Copy PKGLOGs into TMPDIR: */ + { + int pkgs = copy_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", pkgs_path ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot copy some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, pkgs_path ); + } + } + + /* Read PKGLOGs from TMPDIR and create Double Linked List of PACKAGES: */ + { + int pkgs = read_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", tmpdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot read some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + /* INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, tmpdir ); */ + } + } + + + { + int extern_pkgs = create_provides_list( NULL ); + + /* + На данном этапе, для каждого пакета, добавленного в список + packages, уже выполнена операция 'chrefs -d pkgs_path -o inc PKGLOG' + остается только обработать внешние зависимости, если таковые есть. + */ + if( extern_pkgs ) + { + /* + 1. для каждого элемента сиска provides проходим по extern_requires и делаем + crefs dec (функцию надо взять из chrefs.c так как в списке extern_requires + структуры имеют только 3 поля: group, name, version) + */ + decrement_reference_counters(); + + /* + 2. напечатать список требуемых пакетов: + */ + log_extern_requires(); + } + + free_provides_list(); + } + + + if( exit_status == 0 ) + { + LOG( "Setup Database is clean." ); + } + else + { + LOG( "Setup Database is not fully clean. See the LOG mesages above." ); + } + + if( root ) + { + remove_empty_dirs( PACKAGES_PATH ); + remove_empty_dirs( REMOVED_PKGS_PATH ); + } + else + { + remove_empty_dirs( (const char *)pkgs_path ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/check-package.c b/src/check-package.c new file mode 100644 index 0000000..8997d64 --- /dev/null +++ b/src/check-package.c @@ -0,0 +1,1511 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include + +#define PROGRAM_NAME "check-package" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *pkg_fname = NULL, *pkg_found = NULL, + *tmpdir = NULL; + +int quiet = 0, print_broken_files = 0; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL; + +static char *installed_version = NULL; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( installed_version ) { free( installed_version ); } installed_version = NULL + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "This utility checks if specified package is installed. If package\n" ); + fprintf( stdout, "or some another version of this package is already installed then\n" ); + fprintf( stdout, "this utility checks if installed package is correct.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -p,--print-broken-files Print the list of broken directories,\n" ); + fprintf( stdout, " files, or symbolic links to stdout.\n" ); + fprintf( stdout, " -q,--quiet Do not display explanations for\n" ); + fprintf( stdout, " return codes.\n" ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " The PKGNAME, PACKAGE tarball or PKGLOG.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Return codes:\n" ); + fprintf( stdout, " ------+---------------------------+--------------------\n" ); + fprintf( stdout, " code | status | what can be done\n" ); + fprintf( stdout, " ------+---------------------------+--------------------\n" ); + fprintf( stdout, " 30 | not installed | install\n" ); + fprintf( stdout, " 31 | installed correctly | nothing to do\n" ); + fprintf( stdout, " 32 | installed but not correct | repair, re-install\n" ); + fprintf( stdout, " 33 | installed correctly | upgrade\n" ); + fprintf( stdout, " 34 | installed but not correct | repair, upgrade\n" ); + fprintf( stdout, " 35 | installed correctly | downgrade\n" ); + fprintf( stdout, " 36 | installed but not correct | repair, downgrade\n" ); + fprintf( stdout, " ------+---------------------------+--------------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Other non-zero codes assumes that this utility faced to errors not\n" ); + fprintf( stdout, "related to quality of installed package.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "If package specified by short name (probably with version) instead\n" ); + fprintf( stdout, "of regular files such as package tarball or pkglog file then this\n" ); + fprintf( stdout, "utility doesn't check other installed versions of this package.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvpqr:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "print-broken-files", no_argument, NULL, 'p' }, + { "quiet", no_argument, NULL, 'q' }, + { "root", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'p': + { + print_broken_files = 1; + break; + } + case 'q': + { + quiet = 1; + break; + } + + case 'r': + { + if( optarg != NULL ) + { + root = xstrdup( (const char *)optarg ); + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + pkg_fname = xstrdup( (const char *)argv[optind] ); + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, PACKAGES_PATH ); + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + + if( S_ISDIR(st.st_mode) ) + { + pkgs_path = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + } /* End if( !pkgs_path ) */ +} + + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +/*************************************************************** + Probe functions: + */ +static void _probe_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( pkg_found ) return; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL; + char *pkglog = basename( path ); + + if( (match = strstr( pkglog, (const char *)basename( pkg_fname ) )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = xstrdup( (const char *)pkglog ); + ++p; + while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) ) + { + /* package version starts with a number and separated by '-' */ + ++p; ++q; + } + *(--p) = '\0'; + + /******************************************************* + We have to make sure that the name we are looking for + is not shorter than the name of the found package. + */ + if( strlen(pkg_fname) >= strlen(buf) ) + { + + pkg_found = xstrdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + probe_package(): + --------------- + */ +char *probe_package( void ) +{ + char *ret = NULL; + + _probe_pkglog( (const char *)pkgs_path, NULL ); + if( pkg_found ) + { + free( pkg_fname ); + ret = pkg_fname = pkg_found; + } + + return ret; +} +/* + Enf of Probe functions. + ***********************************************************/ + + + +static void read_input_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) arch = skip_spaces( p ); + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distroname = skip_spaces( p ); + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distrover = skip_spaces( p ); + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); +} + +static void read_found_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + char *pn = NULL, *pv = NULL, *ar = NULL, *dn = NULL, *dv = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pn = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pv = skip_spaces( p ); + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) ar = skip_spaces( p ); + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) dn = skip_spaces( p ); + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) dv = skip_spaces( p ); + } + } + + free( line ); + + if( !pn || !pv || !ar || !dn || !dv ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + else + { + if( pn ) free( pn ); + if( pv ) installed_version = pv; + if( ar ) free( ar ); + if( dn ) free( dn ); + if( dv ) free( dv ); + } + + fclose( pkginfo ); +} + +static void _get_found_pkginfo( const char *pkglog ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)pkglog ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo,restore-links,filelist %s > /dev/null 2>&1", + selfdir, tmp, pkglog ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)pkglog ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)pkglog ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_found_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + free( tmp ); + free( cmd ); +} + +static void _search_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */ + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or group is not a directory", pname ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL, *name = basename( path ); + + if( (match = strstr( name, pkgname )) && match == name ) + { + /**************************************************************** + Здесь мы еще должны проверить, что найденный пакет не имеет + более длинное имя, которое начинается с имени искомого пакета. + Полагаясь на факт, что версия может начинаться только с цифры, + мы пропускаем символ '-', разделяющий имя и версию пакета, + а затем проверяем начальный символ версии: + */ + if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) ) + { + _get_found_pkginfo( (const char *)path ); + } + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + /************************************************************************** + NOTE: + In the Setup Database can be only one package with the same pkgname + but in different groups. For example, the package named 'cairo' + has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During + system installation the package libs/cairo-1.14.6 installed first + and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6 + moved from /var/log/radix/packages to /var/log/radix/removed-packages. + + So here we have to look for the PKGLOG in all group directories: + */ + _search_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static void find_installed_package( void ) +{ + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", pkgs_path ); + + _search_pkglog( (const char *)&tmp[0], NULL ); + + free( tmp ); +} + +/*************************************************************** + check_input_package(): + --------------------- + + Возвращает: + -1 если пакет установлен, но его версия меньше + запрашиваемого, + 0 если пакет не установлен или версия установленного и + запрашиваемого равны, + 1 если пакет установлен, но его версия больше + запрашиваемого. + + В случае возврата -1 или 1, устанавливается переменная + installed_version, равная версии уже установленного пакета. + + В случае возврата нуля есть два варанта: + а) пакет установлен и его надо проверить на целостность + (переменная installed_version != NULL ); + б) пакет не установлен. + + Если installed_version != NULL, проверка целостности + пакета будет осуществлена, вне зависимости от его версии. + */ +static int check_input_package( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + int ret = 0; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + /************************************************* + Specified pkg_fname is not a file or directory. + Try to find installed package with name equal + to pkg_fname: + */ + fname = NULL; + fname = probe_package(); + if( !fname ) + { + if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", pkg_fname ); + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + } + + /* check pkg_fname again: */ + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type == IFMT_UNKNOWN ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo,restore-links,filelist %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_input_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + find_installed_package(); + + free( cmd ); + free( tmp ); + + if( installed_version ) + { + ret = cmp_version( (const char *)installed_version, (const char *)pkgver ); + } + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + + return ret; +} + +static int check_package_integrity( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL; + + char *buf = NULL, *tmp = NULL; + + int restore_links = 0; + int ret = 1; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* Check if .RESTORELINKS is present */ + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == 0) && (st.st_size > 8) ) + { + restore_links = 1; + } + + (void)sprintf( &tmp[0], "%s/.FILELIST", tmpdir ); + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + int dir = 0; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) { dir = 1; *(ln + strlen(ln) - 1) = '\0'; } + else { dir = 0; } + + if( !dir ) + { + char *p = rindex( ln, '.' ); + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + /************************** + Do not check .new files: + */ + *p = '\0'; + } + } + + (void)sprintf( &buf[0], "%s/%s", root, ln ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( lstat( (const char *)&buf[0], &st ) == -1 ) + { + /* cannot access file list entry */ + if( dir ) + { + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s: no such directory\n", pkgname, installed_version, ln ); + } + else + { + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s: no such file\n", pkgname, installed_version, ln ); + } + + ret = 0; continue; + } + + if( dir ) + { + if( S_ISDIR(st.st_mode) == 0 ) + { + /* not a directory */ + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s: not a directory\n", pkgname, installed_version, ln ); + ret = 0; continue; + } + } + else + { + if( S_ISREG(st.st_mode) == 0 ) + { + /* not a regular file */ + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s: not a regular file\n", pkgname, installed_version, ln ); + ret = 0; continue; + } + if( !restore_links ) + { + if( S_ISLNK(st.st_mode) == 0 ) + { + /* not a symbolic link */ + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s: not a symbolic link\n", pkgname, installed_version, ln ); + ret = 0; continue; + } + } + } + } /* End of while( file list entry ) */ + fclose( fp ); + + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)&tmp[0], &st ) == 0 ) + { + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &buf[0], "%s/%s/%s", root, p, f ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( lstat( (const char *)&buf[0], &st ) == -1 ) + { + /* cannot access restore links entry */ + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s/%s: no such file or directory\n", pkgname, installed_version, p, f ); + ret = 0; continue; + } + + if( S_ISLNK(st.st_mode) == 0 ) + { + /* not a symbolic link */ + if( print_broken_files ) + fprintf( stdout, "%s-%s: /%s/%s: not a symbolic link\n", pkgname, installed_version, p, f ); + ret = 0; continue; + } + } + } + } /* End of while( restore links entry ) */ + fclose( fp ); + } + + free( line ); + free( tmp ); + free( buf ); + + return ret; +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + + { + int status = 0, correctly = 0; + + /********************************************************** + Fill pkginfo data and put or replace pkglog into tmpdir: + */ + status = check_input_package(); + + if( installed_version ) + { + /* In this case we have to check package integrity */ + correctly = check_package_integrity(); /* returns 1 if correct */ + } + + if( exit_status == EXIT_SUCCESS ) + { + if( status < 0 ) + { + if( !correctly ) + { + if( !quiet ) + fprintf( stdout, + "Previous version '%s' of specified package '%s-%s' is installed but not correct.\n\n", + installed_version, pkgname, pkgver ); + exit_status = 34; /* Package is installed but not correct: repair, upgrade */ + } + else + { + if( !quiet ) + fprintf( stdout, + "Previous version '%s' of specified package '%s-%s' is installed.\n\n", + installed_version, pkgname, pkgver ); + exit_status = 33; /* Package is installed correctly: upgrade */ + } + } + else if( status > 0 ) + { + if( !correctly ) + { + if( !quiet ) + fprintf( stdout, + "A newer version '%s' of specified package '%s-%s' is installed but not correct.\n\n", + installed_version, pkgname, pkgver ); + exit_status = 36; /* Package is installed but not correct: repair, downgrade */ + } + else + { + if( !quiet ) + fprintf( stdout, + "A newer version '%s' of specified package '%s-%s' is installed.\n\n", + installed_version, pkgname, pkgver ); + exit_status = 35; /* Package is installed correctly: downgrade */ + } + } + else + { + if( installed_version ) + { + if( !correctly ) + { + if( !quiet ) + fprintf( stdout, + "Specified package '%s-%s' is already installed but not correct.\n\n", + pkgname, pkgver ); + exit_status = 32; /* Package is installed but not correct: repair, re-install */ + } + else + { + if( !quiet ) + fprintf( stdout, + "Specified package '%s-%s' is already installed.\n\n", + pkgname, pkgver ); + exit_status = 31; /* Package is installed correctly: nothing to do */ + } + } + else + { + if( !quiet ) + fprintf( stdout, + "Specified package '%s-%s' is not installed.\n\n", + pkgname, pkgver ); + exit_status = 30; /* Package is not installed: install */ + } + } + } + + } + + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/check-requires.c b/src/check-requires.c new file mode 100644 index 0000000..396b491 --- /dev/null +++ b/src/check-requires.c @@ -0,0 +1,2616 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include + +#define PROGRAM_NAME "check-requires" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *pkg_fname = NULL, + *tmpdir = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int __done = 0, __child = 0; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +enum _priority priority = REQUIRED; + + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + free_tarballs(); + free_packages(); + free_srcpkgs(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "This utility checks if packages required by requested package are\n" ); + fprintf( stdout, "instaled. If there are non-installed packages or packages have to\n" ); + fprintf( stdout, "updated then the list of non-installed packages is output to the\n" ); + fprintf( stdout, "standard error stream.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " The PACKAGE tarball or PKGLOG.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "The list of required packages prints out in following format:\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "app/attr:2.4.47:PROCEDURE\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "where PROCEDURE is:\n" ); + fprintf( stdout, " install - package should be installed; or\n" ); + fprintf( stdout, " update - the old version of required package already instaled\n" ); + fprintf( stdout, " but should be updated to the new version presented\n" ); + fprintf( stdout, " between colon characters.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigchld( int signum ) +{ + pid_t pid = 0; + int status; + + (void)signum; + + while( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) + { + ; /* One of children with 'pid' is terminated */ + + if( WIFEXITED( status ) ) + { + if( (int) WEXITSTATUS (status) > 0 ) + { + ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + else + { + ; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + } + else if( WIFSIGNALED( status ) ) + { + ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */ + } + else + { + ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */ + } + + } + + if( pid == -1 && errno == ECHILD ) + { + /* No child processes: */ + __done = 1; + } + return; +} + + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigchld; /* CHLD */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + sa.sa_mask = set; + sigaction( SIGCHLD, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvr:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "root", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + root = xstrdup( (const char *)optarg ); + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)argv[optind] ); + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file: %s", buf, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + pkg_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Input package '%s' is not a regular file", buf ); + } + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, PACKAGES_PATH ); + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + + if( S_ISDIR(st.st_mode) ) + { + pkgs_path = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + } /* End if( !pkgs_path ) */ +} + + +/*************************************************************** + Copy functions: + */ +static void _copy_pkglog( const char *group, const char *fname ) +{ + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_input_file( &uncompress, fname ); + + if( type == IFMT_LOG ) + { + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) { (void)sprintf( &tmp[0], "%s/%s", tmpdir, group ); } + else { (void)sprintf( &tmp[0], "%s", tmpdir ); } + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + ERROR( "Cannot copy '%s' PKGLOG file", basename( (char *)fname ) ); + free( tmp ); + return; + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", fname, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + (void)sys_exec_command( cmd ); + ++__child; + + free( tmp ); + free( cmd ); + } +} + +static void _search_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + _copy_pkglog( grp, (const char *)path ); + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + copy_pkglogs() - returns number of copied PKGLOGS or 0 if + no PKGLOGS found in the destination + directory (SETUP_DB_PATH). + The exit_status has been set. + */ +int copy_pkglogs( void ) +{ + int ret = 0; + + __done = 0; __child = 0; + + _search_pkglogs( (const char *)pkgs_path, NULL ); + + if( __child > 0 ) + { + while( !__done ) usleep( 1 ); + ret = __child; + } + + __done = 0; __child = 0; + + return ret; +} +/* + Enf of Copy functions. + ***********************************************************/ + + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + +static size_t read_usize( char *s ) +{ + size_t size = 0; + size_t mult = 1; + double sz = 0.0; + + char suffix; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return size; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return size; + + --q; + suffix = *q; + switch( suffix ) + { + /* by default size calculates in KiB - 1024 Bytes (du -s -h .) */ + case 'G': + case 'g': + mult = 1024 * 1024; + *q = '\0'; + break; + case 'M': + case 'm': + mult = 1024; + *q = '\0'; + break; + case 'K': + case 'k': + *q = '\0'; + break; + default: + break; + } + + if( sscanf( p, "%lg", &sz ) != 1 ) return size; + + return (size_t)round( sz * (double)mult ); +} + +static int read_total_files( char *s ) +{ + int n = 0; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return n; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return n; + + if( sscanf( p, "%u", &n ) != 1 ) return 0; + + return n; +} + +static struct pkg *input_package( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + struct pkg *pkg = NULL; + char *pkgname = NULL, *pkgver = NULL, *group = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + } + + free( line ); + + if( pkgname && pkgver ) + { + pkg = pkg_alloc(); + if( pkg ) + { + if( group ) + { + pkg->group = group; + } + pkg->name = pkgname; + pkg->version = pkgver; + } + + } + else + { + if( group ) free( group ); + if( pkgname ) free( pkgname ); + if( pkgver ) free( pkgver ); + + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); + + return( pkg ); +} + + +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s = '\0'; + } + else + { + /* + If short description declaration is incorrect at first line + of description; then we take whole first line of description: + */ + p = index( line, ':' ); ++p; + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } + strcpy( buf, p ); + } +} + + +static int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop || !cnt ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + + /* Get reference counter */ + { + unsigned int count; + int rc; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + rc = sscanf( ln, "REFERENCE COUNTER: %u", &count ); + if( rc == 1 && cnt != NULL ) + { + *cnt = count; + } + } + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_requires_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_description_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_restore_links_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_install_script_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_file_list_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +int read_pkginfo( FILE *log, struct package *package ) +{ + int ret = -1; + + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:", + *group_pattern = "GROUP:", + *url_pattern = "URL:", + *license_pattern = "LICENSE:", + *uncompressed_size_pattern = "UNCOMPRESSED SIZE:", + *total_files_pattern = "TOTAL FILES:"; + + + if( !log || !package ) return ret; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname_pattern )) && match == ln ) /* at start of line only */ + { + package->pkginfo->name = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + package->pkginfo->version = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + package->pkginfo->arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + package->pkginfo->distro_name = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + package->pkginfo->distro_version = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + package->pkginfo->group = skip_spaces( ln + strlen( group_pattern ) ); + } + if( (match = strstr( ln, url_pattern )) && match == ln ) + { + package->pkginfo->url = skip_spaces( ln + strlen( url_pattern ) ); + } + if( (match = strstr( ln, license_pattern )) && match == ln ) + { + package->pkginfo->license = skip_spaces( ln + strlen( license_pattern ) ); + } + if( (match = strstr( ln, uncompressed_size_pattern )) && match == ln ) + { + package->pkginfo->uncompressed_size = read_usize( ln + strlen( uncompressed_size_pattern ) ); + } + if( (match = strstr( ln, total_files_pattern )) && match == ln ) + { + package->pkginfo->total_files = read_total_files( ln + strlen( total_files_pattern ) ); + } + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, log ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + bzero( (void *)buf, PATH_MAX ); + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + package->pkginfo->short_description = xstrdup( (const char *)buf ); + } + free( buf ); + } + + } /* End of while() */ + + free( line ); + + if( package->pkginfo->name == NULL ) ++ret; + if( package->pkginfo->version == NULL ) ++ret; + if( package->pkginfo->arch == NULL ) ++ret; + if( package->pkginfo->distro_name == NULL ) ++ret; + if( package->pkginfo->distro_version == NULL ) ++ret; + /* group can be equal to NULL */ + + fseek( log, 0, SEEK_SET ); + + return( ret ); +} + + +static unsigned int read_references( FILE *log, int start, unsigned int *cnt, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int counter, pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_reference( package, pkg ); + ++pkgs; + } + ++n; + } + else + break; + } + + free( line ); + + fseek( log, 0, SEEK_SET ); + + *cnt = pkgs; + + return pkgs; +} + + +static unsigned int read_requires( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "PACKAGE DESCRIPTION:" ) ) break; /* if (stop - start - 1) greater than real number of requiress */ + + if( (n > start) && (n < stop) ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_required( package, pkg ); + ++pkgs; + } + + } + ++n; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return pkgs; +} + + +static unsigned int read_description( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *pattern = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.DESCRIPTION", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + pattern = (char *)malloc( (size_t)strlen( package->pkginfo->name ) + 2 ); + if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( pattern, "%s:", package->pkginfo->name ); + + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "RESTORE LINKS:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + /* + skip non-significant spaces at beginning of line + and print lines started with 'pkgname:' + */ + if( (match = strstr( ln, pattern )) && lines < DESCRIPTION_NUMBER_OF_LINES ) + { + int mlen = strlen( match ), plen = strlen( pattern ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + fprintf( tmp, "%s\n", match ); + ++lines; + } + + } + ++n; + } + + if( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */ + while( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + fprintf( tmp, "%s\n", pattern ); + ++lines; + } + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( pattern ); + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *desc = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + desc = (char *)malloc( size + 1 ); + if( !desc ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)desc, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)desc, size ); + if( rc != (ssize_t)size ) + { + ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + } + + package->description = desc; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_restore_links( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.RESTORELINKS", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "INSTALL SCRIPT:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *links = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + links = (char *)malloc( size + 1 ); + if( !links ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)links, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)links, size ); + if( rc != (ssize_t)size ) + { + ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + } + + package->restore_links = links; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_install_script( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.INSTALL", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "FILE LIST:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *install = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + install = (char *)malloc( size + 1 ); + if( !install ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)install, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)install, size ); + if( rc != (ssize_t)size ) + { + ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + } + + package->install_script = install; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_file_list( FILE *log, int start, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + unsigned int files = 0; + + if( !log || !package ) return files; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start ) + { + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + add_file( package, (const char *)ln ); + ++files; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return files; +} + + + +static void _read_pkglog( const char *group, const char *fname ) +{ + FILE *log = NULL; + char *bname = NULL; + + if( fname != NULL ) + { + log = fopen( (const char *)fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", fname ); + } + bname = (char *)fname + strlen( tmpdir ) + 1; + } + + if( log != NULL ) + { + struct package *package = NULL; + int rc, start, stop; + unsigned int counter; + + package = package_alloc(); + + if( read_pkginfo( log, package ) != 0 ) + { + ERROR( "%s: Invalid PKGLOG file", bname ); + package_free( package ); + fclose( log ); + return; + } + + if( hardware ) package->hardware = xstrdup( (const char *)hardware ); + if( tarballs ) /* find tarball and allocate package->tarball */ + { + struct pkginfo *info = package->pkginfo; + const char *tgz = NULL; + char *buf = NULL; + struct stat sb; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( buf, "%s/%s-%s-%s-%s-%s", + info->group, info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( buf, "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + tgz = find_tarball( (const char *)&buf[0] ); + if( tgz ) + { + package->tarball = xstrdup( (const char *)tgz ); + + bzero( (void *)&buf[0], PATH_MAX ); + (void)sprintf( buf, "%s/%s", pkgs_path, tgz ); + if( stat( buf, &sb ) != -1 ) + { + info->compressed_size = (size_t)sb.st_size; + } + } + free( buf ); + } + package->procedure = INSTALL; + package->priority = priority; + + if( package->pkginfo->group && group && strcmp( package->pkginfo->group, group ) != 0 ) + { + char *tgz; + + if( package->tarball ) { tgz = package->tarball; } + else { tgz = basename( (char *)fname ); } + + WARNING( "%s: Should be moved into '%s' subdir", tgz, package->pkginfo->group ); + } + + /****************** + read references: + */ + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( counter > 0 ) + { + unsigned int pkgs = counter; + + if( read_references( log, start, &counter, package ) != pkgs ) + { + ERROR( "%s: Invalid REFERENCE COUNTER section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /****************** + read requires: + */ + rc = get_requires_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REQUIRES section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + unsigned int pkgs = (unsigned int)(stop - start - 1); /* -1 skips section header */ + + if( read_requires( log, start, stop, package ) != pkgs ) + { + ERROR( "%s: Invalid REQUIRES section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /******************* + read description: + */ + rc = get_description_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains PACKAGE DESCRIPTION section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + if( read_description( log, start, stop, package ) != (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + ERROR( "%s: Invalid DESCRIPTION section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /********************* + read restore links: + */ + rc = get_restore_links_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains RESTORE LINKS section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_restore_links( log, start, stop, package ); + } + + /********************* + read install script: + */ + rc = get_install_script_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains INSTALL SCRIPT section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_install_script( log, start, stop, package ); + } + + /***************** + read file_list: + */ + rc = get_file_list_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains FILE LIST section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( start ) + { + unsigned int files = read_file_list( log, start, package ); + if( files == (unsigned int)0 ) + { + /* + Packages that do not contain regular files are ignored. + For example, service package base/init-devices-1.2.3-s9xx-glibc-radix-1.1.txz + */ + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "%s: PKGLOG contains empty FILE LIST section", bname ); + } + package_free( package ); + fclose( log ); + return; + } + package->pkginfo->total_files = (int)files; + } + + /* + Здесь можно организовать проверку пакета на предмет его + целостности и правильности установки (когда будет готова + утилита check-package). + */ + add_package( package ); + + ++__child; + fclose( log ); + } +} + +static void _read_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + if( check_input_file( NULL, (const char *)path ) == IFMT_LOG ) + { + _read_pkglog( grp, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _read_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +int read_pkglogs( void ) +{ + int ret = 0; + + __child = 0; + + _read_pkglogs( (const char *)tmpdir, NULL ); + + ret = __child; + + __child = 0; + + return ret; +} + + +static void check_pkg_fname( void ) +{ + struct stat st; + char *fname = pkg_fname; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + struct pkg *srcpkg = NULL; + + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + srcpkg = input_package( (const char *)&tmp[0] ); + add_srcpkg( srcpkg ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + if( check_input_file( NULL, (const char *)fname ) == IFMT_PKG ) + { + bzero( (void *)cmd, PATH_MAX ); + len = snprintf( &cmd[0], PATH_MAX, "%s/pkglog -m -d %s %s > /dev/null 2>&1", selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGLOG from '%s' file", basename( (char *)fname ) ); + } + } + else + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( srcpkg->group ) { (void)sprintf( &buf[0], "%s/%s", tmp, srcpkg->group ); } + else { (void)sprintf( &buf[0], "%s", tmp ); } + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot copy '%s' PKGLOG file", basename( (char *)fname ) ); + } + + bzero( (void *)cmd, PATH_MAX ); + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", fname, buf ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + + free( buf ); + } + + free( tmp ); + free( cmd ); + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } +} + +/**************************************************************** + Если после апдейта пакет просто сменил группу, то его надо + удалить из списка внешних зависимостей, ведь он предоставляет + нужную функциональность. + + Например: libs/libspectre требует libs/cairo, а xlibs/cairo + требует libs/libspectre, однако libs/libspectre может + использовать как libs/cairo так и xlibs/cairo . + + Search installed package with specified name within any group: + */ +static char *pkglog_fname = NULL; + +static void _probe_pkglog( const char *pname, const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( pkglog_fname ) return; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL; + char *pkglog = basename( path ); + + if( (match = strstr( pkglog, pname )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = xstrdup( (const char *)pkglog ); + ++p; + while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) ) + { + /* package version starts with a number and separated by '-' */ + ++p; ++q; + } + *(--p) = '\0'; + + /******************************************************* + We have to make sure that the name we are looking for + is not shorter than the name of the found package. + */ + if( strlen(pname) >= strlen(buf) ) + { + + pkglog_fname = xstrdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( pname, (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/****************** + probe_package(): + --------------- + */ +static char *probe_package( const char *name ) +{ + char *ret = NULL; + + _probe_pkglog( name, (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + ret = pkglog_fname; + } + + return ret; +} + +static int check_installed_pkgname( const char *name ) +{ + int ret = 0; + char *fname = NULL; + + if( !name ) return ret; + + fname = probe_package( name ); + if( fname ) + { + ret = 1; + free( pkglog_fname ); + pkglog_fname = NULL; + } + + return ret; +} +/* + End of search installed package. + ****************************************************************/ + +static int __extern_requires = 0; + +static void _print_extern_requires( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg && !check_installed_pkgname( (const char *)pkg->name ) ) + { + if( pkg->group ) + fprintf( stderr, "%s/%s:%s:%s\n", pkg->group, pkg->name, pkg->version, strproc( pkg->procedure ) ); + else + fprintf( stderr, "%s:%s:%s\n", pkg->name, pkg->version, strproc( pkg->procedure ) ); + + ++__extern_requires; + } +} + +static void print_extern_requires( void ) +{ + dlist_foreach( extern_requires, _print_extern_requires, NULL ); +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + /* Copy PKGLOGs into TMPDIR: */ + { + int pkgs = copy_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", pkgs_path ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot copy some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, pkgs_path ); + } + } + + /*********************************************************** + Fill srcpkg struct and put or replace pkglog into tmpdir: + */ + check_pkg_fname(); + + /* Read PKGLOGs from TMPDIR and create Double Linked List of PACKAGES: */ + { + int pkgs = read_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", tmpdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot read some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + /* INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, tmpdir ); */ + } + } + + { + int extern_pkgs = create_provides_list( srcpkgs ); + if( extern_pkgs ) + { + print_extern_requires(); + if( __extern_requires ) exit_status += 1; + } + free_provides_list(); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/chrefs.c b/src/chrefs.c new file mode 100644 index 0000000..8820aea --- /dev/null +++ b/src/chrefs.c @@ -0,0 +1,1933 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include + +#define PROGRAM_NAME "chrefs" + +#include + + +char *program = PROGRAM_NAME; +char *destination = NULL, *operation = NULL, *pkglog_fname = NULL, + *tmpdir = NULL, *requires_fname = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL; + + +/******************************************** + *requires[] declarations: + */ +struct package { + char *name; + char *version; + char *arch; + char *distro_name; + char *distro_version; +}; + +static struct package *create_package( char *name, char *version, + char *arch, char *distro_name, + char *distro_version ); +static void free_package( struct package *pkg ); +static struct package **create_requires( size_t size ); +static void free_requires( struct package **requires ); +/* + End of *requires[] declarations. + ********************************************/ + +struct package **requires = NULL; + + +/******************************************** + LOCK FILE declarations: + */ +static int __lock_file( FILE *fp ); +static void __unlock_file( int fd ); +/* + End of LOCK FILE declarations. + ********************************************/ + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( requires ) { free_requires( requires ); } requires = NULL + +void free_resources() +{ + if( selfdir ) { free( selfdir ); selfdir = NULL; } + if( destination ) { free( destination ); destination = NULL; } + if( operation ) { free( operation ); operation = NULL; } + if( pkglog_fname ) { free( pkglog_fname ); pkglog_fname = NULL; } + if( requires_fname ) { free( requires_fname ); requires_fname = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Increment or Decrement reference counters in required PKGLOG files\n" ); + fprintf( stdout, "in the Setup Database packages directory, which is determined by\n" ); + fprintf( stdout, "option --destination OR by the directory where the input \n" ); + fprintf( stdout, "file is located. If destination is defined then file name\n" ); + fprintf( stdout, "should be defined relative to destination.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -d,--destination= Setup Database packages directory.\n" ); + fprintf( stdout, " -o,--operation= Operation: Increment or Decrement.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " Input PKGLOG file (TEXT format).\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +/******************************************** + *requires[] functions: + */ +static struct package *create_package( char *name, char *version, + char *arch, char *distro_name, + char *distro_version ) +{ + struct package *pkg = (struct package *)0; + + if( !name || *name == '\0' ) return pkg; + if( !version || *version == '\0' ) return pkg; + if( !arch || *arch == '\0' ) return pkg; + if( !distro_name || *distro_name == '\0' ) return pkg; + if( !distro_version || *distro_version == '\0' ) return pkg; + + pkg = (struct package *)malloc( sizeof(struct package) ); + if( pkg ) + { + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + pkg->arch = xstrdup( (const char *)arch ); + pkg->distro_name = xstrdup( (const char *)distro_name ); + pkg->distro_version = xstrdup( (const char *)distro_version ); + } + + return pkg; +} + +static void free_package( struct package *pkg ) +{ + if( pkg ) + { + if( pkg->name ) free( pkg->name ); + if( pkg->version ) free( pkg->version ); + if( pkg->arch ) free( pkg->arch ); + if( pkg->distro_name ) free( pkg->distro_name ); + if( pkg->distro_version ) free( pkg->distro_version ); + + free( pkg ); + } +} + +static struct package **create_requires( size_t size ) +{ + struct package **requires = (struct package **)0; + + if( size > 0 ) + { + requires = (struct package **)malloc( size * sizeof(struct package *) ); + bzero( (void *)requires, size * sizeof(struct package *) ); + } + + return( requires ); +} + +static void free_requires( struct package **requires ) +{ + if( requires ) + { + struct package **ptr = requires; + + while( *ptr ) + { + if( *ptr ) free_package( *ptr ); + ptr++; + } + free( requires ); + } +} +/* + End of *requires[] functions. + ********************************************/ + + +/******************************************** + LOCK FILE functions: + */ +static int __lock_file( FILE *fp ) +{ + int fd = fileno( fp ); + + if( flock( fd, LOCK_EX ) == -1 ) + { + return -1; + /* + Мы не проверяем errno == EWOULDBLOCK, так какданная ошибка + говорит о том что файл заблокирован другим процессом с флагом + LOCK_NB, а мы не собираемся циклически проверять блокировку. + У нас все просто: процесс просто ждет освобождения дескриптора + и не пытается во время ожидания выполнять другие задачи. + */ + } + return fd; +} + +static void __unlock_file( int fd ) +{ + if( fd != -1 ) flock( fd, LOCK_UN ); + /* + Здесь, в случае ошибки, мы не будем выводить + никаких сообщений. Наш процесс выполняет простую + атомарную задачу и, наверное, завершится в скором + времени, освободив все дескрипторы. + */ +} +/* + End of LOCK FILE functions. + ********************************************/ + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + +enum _pkglog_type +{ + PKGLOG_TEXT = 0, + PKGLOG_GZ, + PKGLOG_BZ2, + PKGLOG_XZ, + PKGLOG_TAR, + + PKGLOG_UNKNOWN +}; + +static enum _pkglog_type pkglog_type = PKGLOG_UNKNOWN; +static char uncompress[2] = { 0, 0 }; + + +static enum _pkglog_type check_pkglog_file( const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + uncompress[0] = '\0'; + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) ); + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return PKGLOG_TEXT; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + uncompress[0] = 'x'; + close( fd ); return PKGLOG_GZ; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + uncompress[0] = 'j'; + close( fd ); return PKGLOG_BZ2; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + uncompress[0] = 'J'; + close( fd ); return PKGLOG_XZ; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return PKGLOG_TAR; + } + } + + close( fd ); return PKGLOG_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvd:o:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "destination", required_argument, NULL, 'd' }, + { "operation", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'd': + { + if( optarg != NULL ) + { + destination = xstrdup( (const char *)optarg ); + remove_trailing_slash( destination ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'o': + { + operation = xstrdup( (const char *)optarg ); + to_lowercase( operation ); + if( strcmp( operation, "inc" ) != 0 && strcmp( operation, "dec" ) != 0 ) + { + ERROR( "Invalid '%s' operation requested", operation ); + usage(); + } + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( operation == NULL ) + { + usage(); + } + + /* last command line argument is the LOGFILE */ + if( optind < argc ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( destination == NULL ) + { + pkglog_fname = xstrdup( (const char *)argv[optind++] ); + if( pkglog_fname == NULL ) + { + FATAL_ERROR( "Unable to set input PKGLOG file name" ); + } + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( buf, "%s", pkglog_fname ); + destination = xstrdup( (const char *)dirname( buf ) ); + if( destination == NULL ) + { + FATAL_ERROR( "Unable to set destination directory" ); + } + + } + else + { + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( buf, "%s/%s", destination, argv[optind++] ); + pkglog_fname = xstrdup( (const char *)buf ); + if( pkglog_fname == NULL ) + { + FATAL_ERROR( "Unable to set inpit PKGLOG file name" ); + } + } + + free( buf ); + + pkglog_type = check_pkglog_file( (const char *)pkglog_fname ); + if( pkglog_type != PKGLOG_TEXT ) + { + ERROR( "%s: Unknown input file format", basename( pkglog_fname ) ); + usage(); + } + + } + else + { + usage(); + } +} + + +/* + Especialy for pkginfo lines. + Remove leading spaces and take non-space characters only: + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + + +int get_pkginfo() +{ + int ret = -1; + FILE *log = NULL; + + if( pkglog_fname != NULL ) + { + log = fopen( (const char *)pkglog_fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *group_pattern = "GROUP:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:"; + + int last = 10; /* read first 10 lines only */ + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( last ) + { + int n = 1; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname_pattern )) && match == ln ) /* at start of line only */ + { + pkgname = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + pkgver = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + distroname = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + distrover = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + group = skip_spaces( ln + strlen( group_pattern ) ); + } + + if( n < last ) ++n; + else break; + + } /* End of while() */ + + } + + free( line ); + + if( pkgname == NULL ) ++ret; + if( pkgver == NULL ) ++ret; + if( arch == NULL ) ++ret; + if( distroname == NULL ) ++ret; + if( distrover == NULL ) ++ret; + /* group can be equal to NULL */ + + fclose( log ); + } + + return( ret ); +} + + + +int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop || !cnt ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + + /* Get reference counter */ + { + unsigned int count; + int rc; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + rc = sscanf( ln, "REFERENCE COUNTER: %u", &count ); + if( rc == 1 && cnt != NULL ) + { + *cnt = count; + } + } + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + + +int get_requires_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + return ret; + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_description_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + return ret; + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_restore_links_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + return ret; + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_install_script_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + return ret; + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_file_list_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + return ret; + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fclose( log ); + } + + return( ret ); +} + + + + +/*********************************************************** + get_requires(): + -------------- + if( success ) - returns number of requires + if( error ) - returns -1 + */ +int get_requires( const char *requires, const char *log_fname ) +{ + int ret = -1; + FILE *req = NULL, *log = NULL; + + int start = 0, stop = 0; + + if( get_requires_section( &start, &stop, log_fname ) != 0 ) + { + return ret; + } + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( ! log ) + { + return ret; + } + } + + if( requires != NULL ) + { + req = fopen( (const char *)requires, "w" ); + if( ! req ) + { + return ret; + } + } + + /* get PKGLOG sections */ + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( start && start < stop ) + { + int n = 1, lines = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > start) && (n < stop) ) + { + fprintf( req, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( log ); + fflush( req ); fclose( req ); + } + + return( ret ); +} + + +struct package **read_requires( const char *fname, int n ) +{ + struct package **requires = (struct package **)0; + struct package **ptr = (struct package **)0; + + FILE *file; + + if( ! fname ) return NULL; + + requires = create_requires( n + 1 ); + if( requires ) + { + int i; + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + struct package *package; + + file = fopen( fname, "r" ); + if( !file ) + { + free( line ); + free_requires( requires ); + return NULL; + } + + ptr = requires; + for( i = 0; i < n; ++i ) + { + if( (ln = fgets( line, PATH_MAX, file )) ) + { + char *d, *name, *version; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* сut 'name=version' by delimiter '=': */ + d = index( ln, '=' ); *d = '\0'; + version = ++d; name = ln; + + package = create_package( name, version, arch, DISTRO_NAME, DISTRO_VERSION ); + if( package ) + { + *ptr = package; + ptr++; + } + } + } + *ptr = (struct package *)0; + + free( line ); + } + + return requires; +} + +int find_requires( char *pname, const char *grp ) +{ + char *name = NULL; + char *buf = NULL; + + struct package **ptr = requires; + + buf = (char *)malloc( (size_t)PATH_MAX ); + + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( *ptr ) + { + if( grp && *grp != '\0' ) + { + char *p = NULL; + size_t len = strlen( (*ptr)->name ); + + if( (p = strstr( (*ptr)->name, grp )) && (p == (*ptr)->name) ) + { + /* if group is equal to required package's group then remove group from the name */ + name = alloca( len + 1 ); + strcpy( name, (const char *)(*ptr)->name ); + + name = index( name, '/' ) + 1; + } + else + { + /* if group is not equal to required package's group then add group to the name */ + name = alloca( len + strlen( grp ) + 2 ); + (void)sprintf( name, "%s/%s", grp, (const char *)(*ptr)->name ); + } + } + else + { + name = (*ptr)->name; + } + + (void)sprintf( buf, "%s-%s-%s-%s-%s", + name, (*ptr)->version, (*ptr)->arch, + (*ptr)->distro_name, (*ptr)->distro_version ); + + if( ! strcmp( buf, pname ) ) + { + free( buf ); return 1; + } + ptr++; + } + + free( buf ); + + return 0; +} + + +int save_tmp_head( FILE *log, int stop, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !stop || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( n < stop ) + { + fprintf( fp, "%s\n", ln ); + ++n; ++lines; + } + else + break; + } + + ret = lines; /* number of lines in the HEAD */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +int save_tmp_tail( FILE *log, int start, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !start || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + fprintf( fp, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of lines in the TAIL */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +int write_tmp_part( FILE *log, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int lines = 0; + + if( !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "r" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + fprintf( log, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of written lines */ + + free( line ); + + fclose( fp ); + + return ret; +} + + + +static char **create_references( size_t size ) +{ + char **references = (char **)0; + + if( size > 0 ) + { + references = (char **)malloc( size * sizeof(char *) ); + bzero( (void *)references, size * sizeof(char *) ); + } + + return( references ); +} + +static void free_references( char **references ) +{ + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) free( *ptr ); + ptr++; + } + free( references ); + } +} + + +static char **get_references( FILE *log, int start, unsigned int *cnt, char *grp, char *name, char *version ) +{ + char **refs = (char **)0; + char **ptr; + + char *ln = NULL; + char *line = NULL; + int n = 1; + + size_t len = 0; + + unsigned int counter, pkgs; + + char *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !name || !version ) return refs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + pkg = (char *)malloc( (size_t)PATH_MAX ); + if( !pkg ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + if( grp && *grp != '\0' ) { (void)sprintf( pkg, "%s/%s=", grp, name ); } + else { (void)sprintf( pkg, "%s=", name ); } + + len = strlen( pkg ); + + refs = ptr = create_references( counter + 1 ); /* null terminated char *references[] */ + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; pkgs = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( strncmp( ln, pkg, len ) ) /* always remove 'name=version' from list */ + { + if( refs ) + { + *ptr = xstrdup( (const char *)ln ); ++ptr; + *ptr = (char *)0; + ++pkgs; + } + } + ++n; + } + else + break; + } + + free( line ); free( pkg ); + + fseek( log, 0, SEEK_SET ); + + if( pkgs == 0 ) + { + free_references( refs ); + refs = (char **)0; + } + + *cnt = pkgs; + + return refs; +} + + +static void _change_references( char *grp, char *name, char *version, const char *log_fname ) +{ + int fd; + FILE *log; + + char *head_fname = NULL, *tail_fname = NULL; + int head_lines, tail_lines; + + int rc, start, stop; + unsigned int counter; + + int inc = ( strcmp( operation, "inc" ) == 0 ) ? 1 : 0; + + char **references = NULL; + + if( !name || !version || log_fname == NULL ) return; + if( check_pkglog_file( log_fname ) != PKGLOG_TEXT ) return; + + log = fopen( (const char *)log_fname, "r+" ); + if( !log ) + { + ERROR( "Cannot access %s file: %s", log_fname, strerror( errno ) ); + return; + } + + fd = __lock_file( log ); + + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REFERENCE COUNTER section", log_fname ); + __unlock_file( fd ); fclose( log ); + return; + } + + head_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( head_fname, "%s/.HEAD", tmpdir ); + + tail_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( tail_fname, "%s/.TAIL", tmpdir ); + + head_lines = save_tmp_head( log, start, (const char *)head_fname ); + tail_lines = save_tmp_tail( log, stop - 1, (const char *)tail_fname ); + + if( head_lines < 10 && tail_lines < 12 ) + { + ERROR( "%s: Invalid PKGLOG file", log_fname ); + __unlock_file( fd ); fclose( log ); + return; + } + + references = get_references( log, start, &counter, grp, name, version ); + + if( ftruncate( fd, 0 ) != 0 ) + { + ERROR( "Cannot change REFERENCE COUNTER in the %s file: %s", log_fname, strerror( errno ) ); + free_references( references ); + __unlock_file( fd ); fclose( log ); + return; + } + + head_lines = write_tmp_part( log, (const char *)head_fname ); + + if( inc ) ++counter; + fprintf( log, "REFERENCE COUNTER: %u\n", counter ); + if( inc ) + { + if( grp && *grp != '\0' ) + { + fprintf( log, "%s/%s=%s\n", grp, name, version ); + } + else + { + fprintf( log, "%s=%s\n", name, version ); + } + } + + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) fprintf( log, "%s\n", *ptr ); + ptr++; + } + + free_references( references ); + } + + tail_lines = write_tmp_part( log, (const char *)tail_fname ); + + __unlock_file( fd ); + fclose( log ); +} + + +static void _search_required_packages( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + if( find_requires( entry->d_name, grp ) ) + { + _change_references( group, pkgname, pkgver, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_required_packages( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + closedir( dir ); +} + + + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + int ret; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + exit_status = get_pkginfo(); + if( exit_status != 0 ) + { + FATAL_ERROR( "%s: Invalid input PKGLOG file", basename( pkglog_fname ) ); + } + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + else + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)strlen( tmpdir ) + 11 ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", tmpdir ); + requires_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + + /******************* + Getting REQUIRES: + */ + if( (ret = get_requires( (const char *)requires_fname, (const char *)pkglog_fname )) > 0 ) + { + /* We have non-empty list of REQUIRES in the 'requires_fname' file */ + requires = read_requires( (const char *)requires_fname, ret ); + _search_required_packages( (const char *)destination, NULL ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/cmpvers.c b/src/cmpvers.c new file mode 100644 index 0000000..42014e8 --- /dev/null +++ b/src/cmpvers.c @@ -0,0 +1,110 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractionnal parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x3 +#define S_F 0x6 +#define S_Z 0x9 + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the texinfo doc). +*/ + +int cmp_version( const char *s1, const char *s2 ) +{ + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + /* Symbol(s) 0 [1-9] others + Transition (10) 0 (01) d (00) x */ + static const uint8_t next_state[] = + { + /* state x d 0 */ + /* S_N */ S_N, S_I, S_Z, + /* S_I */ S_N, S_I, S_I, + /* S_F */ S_N, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z + }; + + static const int8_t result_type[] = + { + /* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */ + + /* S_N */ CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, +1, LEN, LEN, +1, LEN, LEN, + /* S_F */ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, -1, CMP, CMP, -1, CMP, CMP + }; + + if( p1 == p2 ) return 0; + + unsigned char c1 = *p1++; + unsigned char c2 = *p2++; + /* Hint: '0' is a digit too. */ + int state = S_N + ((c1 == '0') + (isdigit (c1) != 0)); + + int diff; + while( (diff = c1 - c2) == 0 ) + { + if( c1 == '\0' ) return diff; + + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state += (c1 == '0') + (isdigit (c1) != 0); + } + + state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while( isdigit (*p1++) ) + if( !isdigit (*p2++) ) + return 1; + + return isdigit (*p2) ? -1 : diff; + + default: + return state; + } +} + + +const char *max_version( const char *s1, const char *s2 ) +{ + if( cmp_version( s1, s2 ) < 0 ) return s2; + else return s1; +} diff --git a/src/cmpvers.h b/src/cmpvers.h new file mode 100644 index 0000000..1d3c642 --- /dev/null +++ b/src/cmpvers.h @@ -0,0 +1,35 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _CMP_VERSION_H_ +#define _CMP_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int cmp_version( const char *s1, const char *s2 ); +extern const char *max_version( const char *s1, const char *s2 ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _CMP_VERSION_H_ */ diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..08fe4b2 --- /dev/null +++ b/src/defs.h @@ -0,0 +1,51 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _DEFS_H_ +#define _DEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SETUP_DB_PATH "var/lib/" DISTRO_NAME +#define PACKAGES_PATH SETUP_DB_PATH "/packages" +#define REMOVED_PKGS_PATH SETUP_DB_PATH "/removed-packages" +#define SETUP_PATH SETUP_DB_PATH "/setup" +#define LOG_PATH SETUP_DB_PATH +#define SETUP_LOG_FILE "/setup.log" /* : used by: update-package, remove-package, install-package. */ +#define LOG_FILE "/" PROGRAM_NAME ".log" /* : used by: check-requires, check-package, check-db-integrity. */ + + +#define DO_NOT_WARN_ABOUT_EMPTY_REQUIRES (1) +#define DO_NOT_WARN_ABOUT_EMPTY_RESTORE_LINKS (1) +#define DO_NOT_WARN_ABOUT_SERVICE_FILES (1) +#define DO_NOT_WARN_ABOUT_OPT_PKGINFO_ITEMS (1) + +#define DO_NOT_PRINTOUT_INFO (1) + +#define DESCRIPTION_NUMBER_OF_LINES 11 +#define DESCRIPTION_LENGTH_OF_LINE 68 + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _DEFS_H_ */ diff --git a/src/dialog-ui.c b/src/dialog-ui.c new file mode 100644 index 0000000..01c5686 --- /dev/null +++ b/src/dialog-ui.c @@ -0,0 +1,401 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include +#include +#include +#include /* index(3) */ + +#include +#include +#include + +#include +#include + + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + +int info_box( const char *title, const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dlg_put_backtitle(); + + status = dialog_msgbox( title, message, height, 74, 0 ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int info_pkg_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + char *tmp = NULL; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dlg_put_backtitle(); + + if( pkgver ) + (void)sprintf( &tmp[0], " %s \\Z1%s-%s\\Zn ", title, pkgname, pkgver ); + else + (void)sprintf( &tmp[0], " %s \\Z1%s\\Zn ",title, pkgname ); + + if( priority ) + { + (void)strcat( &tmp[0], "[" ); + (void)strcat( &tmp[0], priority ); + (void)strcat( &tmp[0], "] " ); + } + status = dialog_msgbox( (const char *)&tmp[0], message, height, 74, 0 ); + + free( tmp ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int ask_install_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + char *tmp = NULL; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dialog_vars.yes_label = "Install"; + dialog_vars.no_label = "Cancel"; + + dlg_put_backtitle(); + + (void)sprintf( &tmp[0], + " %s \\Z1%s-%s\\Zn [%s] ", + title, pkgname, pkgver, priority ); + status = dialog_yesno( (const char *)&tmp[0], message, height, 74 ); + + free( tmp ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int ask_remove_box( const char *title, const char *pkgname, const char *pkgver, + const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + char *tmp = NULL; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dialog_vars.yes_label = "Remove"; + dialog_vars.no_label = "Cancel"; + + dlg_put_backtitle(); + + (void)sprintf( &tmp[0], + " %s \\Z1%s-%s\\Zn ", + title, pkgname, pkgver ); + status = dialog_yesno( (const char *)&tmp[0], message, height, 74 ); + + free( tmp ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int ask_reinstall_box( const char *title, const char *pkgname, const char *pkgver, + const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + char *tmp = NULL; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dialog_vars.yes_label = "Re-install"; + dialog_vars.no_label = "Cancel"; + + dlg_put_backtitle(); + + (void)sprintf( &tmp[0], + " %s \\Z1%s-%s\\Zn ", + title, pkgname, pkgver ); + status = dialog_yesno( (const char *)&tmp[0], message, height, 74 ); + + free( tmp ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int ask_update_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ) +{ + int status = 0; + + char *tmp = NULL; + + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dialog_vars.yes_label = "Update"; + dialog_vars.no_label = "Cancel"; + + dlg_put_backtitle(); + + (void)sprintf( &tmp[0], + " %s \\Z1%s-%s\\Zn [%s] ", + title, pkgname, pkgver, priority ); + status = dialog_yesno( (const char *)&tmp[0], message, height, 74 ); + + free( tmp ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + +int select_packages_box( DIALOG_LISTITEM *items, int items_num, int sleep, int clear_screen ) +{ + int status = 0; + + int current = 0; + const char *states = " *"; + FILE *in = stdin, *out = stdout; + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + init_dialog( in, out ); + + dialog_vars.colors = 1; + dialog_vars.column_separator = " "; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + if( clear_screen ) dialog_vars.dlg_clear_screen = 1; + dialog_vars.sleep_secs = sleep; + + dialog_vars.yes_label = "Install"; + dialog_vars.no_label = "Cancel"; + + dlg_put_backtitle(); + + status = dlg_checklist( " \\Z0SELECT PACKAGES TO INSTALL\\Zn ", + "\n" + " Please confirm the packages you wish to install. Use the UP/DOWN\n" + " keys to scroll through the list, and the SPACE key to deselect any\n" + " items you don't want to install.\n" + "\n" + " Press ENTER when you are done.\n", + 19, 74 /* min 73 */, 7, items_num, items, states, 1, ¤t ); + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + + return status; +} + + + +void show_install_dlg_progress( int percent ) +{ + static void *gauge = NULL; + + + if( percent < 1 ) + { + if( gauge ) return; /* only one instance of progress box */ + + bzero( (void *)&dialog_vars, sizeof(DIALOG_VARS) ); + + init_dialog( stdin, stdout ); + + dialog_vars.colors = 1; + dialog_vars.backtitle = "\\Z7Radix\\Zn \\Z1cross\\Zn \\Z7Linux\\Zn"; + dialog_vars.dlg_clear_screen = 0; + dialog_vars.sleep_secs = 0; + + dlg_put_backtitle(); + gauge = dlg_allocate_gauge( " \\Z0INSTALL PACKAGES\\Zn ", + "\n" + " Please wait for install all specified packages:\n" + "\n\n", 8, 74, 0 ); + } + + if( gauge ) + dlg_update_gauge( gauge, percent ); + + if( percent > 99 ) + { + if( gauge ) + { + dlg_free_gauge( gauge ); + gauge = NULL; + + if( dialog_vars.sleep_secs ) + (void)napms(dialog_vars.sleep_secs * 1000); + + if( dialog_vars.dlg_clear_screen ) + { + dlg_clear(); + (void)refresh(); + } + end_dialog(); + } + } +} diff --git a/src/dialog-ui.h b/src/dialog-ui.h new file mode 100644 index 0000000..fb135da --- /dev/null +++ b/src/dialog-ui.h @@ -0,0 +1,59 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _DIALOG_UI_H_ +#define _DIALOG_UI_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + +extern int info_box( const char *title, const char *message, int height, int sleep, int clear_screen ); + +extern int info_pkg_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ); + +extern int ask_install_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ); + +extern int ask_remove_box( const char *title, const char *pkgname, const char *pkgver, + const char *message, int height, int sleep, int clear_screen ); + +extern int ask_reinstall_box( const char *title, const char *pkgname, const char *pkgver, + const char *message, int height, int sleep, int clear_screen ); + +extern int ask_update_box( const char *title, const char *pkgname, const char *pkgver, const char *priority, + const char *message, int height, int sleep, int clear_screen ); + +extern int select_packages_box( DIALOG_LISTITEM *items, int items_num, int sleep, int clear_screen ); + +extern void show_install_dlg_progress( int percent ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _DIALOG_UI_H_ */ diff --git a/src/dlist.c b/src/dlist.c new file mode 100644 index 0000000..3c97c5b --- /dev/null +++ b/src/dlist.c @@ -0,0 +1,680 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include + +#include + +#include + +struct dlist *__dlist_alloc( void ) +{ + struct dlist *list = NULL; + + list = (struct dlist *)malloc( sizeof( struct dlist ) ); + if( !list ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)list, sizeof( struct dlist ) ); + + return list; +} + + + +struct dlist *dlist_first( struct dlist *list ) +{ + while( list && dlist_prev( list ) ) list = dlist_prev( list ); + + return list; +} + +struct dlist *dlist_last( struct dlist *list ) +{ + while( list && dlist_next( list ) ) list = dlist_next( list ); + + return list; +} + +int dlist_length( struct dlist *list ) +{ + int n = 0; + + while( list ) + { + list = dlist_next( list ); + ++n; + } + + return n; +} + +struct dlist *dlist_nth( struct dlist *list, int n ) +{ + while( list && (n-- > 0) ) + { + list = dlist_next( list ); + } + + return list; +} + +void *dlist_nth_data( struct dlist *list, int n ) +{ + while( list && (n-- > 0) ) + { + list = dlist_next( list ); + } + + return list ? list->data : NULL; +} + +int dlist_position( struct dlist *list, struct dlist *link ) +{ + int n = 0; + + while( list ) + { + if( list == link ) return n; + ++n; + list = dlist_next( list ); + } + + return -1; +} + +int dlist_index( struct dlist *list, const void *data ) +{ + int n = 0; + + while( list ) + { + if( list->data == data ) return n; + ++n; + list = dlist_next( list ); + } + + return -1; +} + +struct dlist *dlist_find( struct dlist *list, const void *data ) +{ + while( list ) + { + if( list->data == data ) { break; } + list = dlist_next( list ); + } + + return list; +} + + +struct dlist *dlist_find_data( struct dlist *list, DLCMPF cmp_func, const void *data ) +{ + if( !list || !cmp_func ) return NULL; + + while( list ) + { + if( ! cmp_func( list->data, data ) ) { return list; } + list = dlist_next( list ); + } + + return NULL; +} + + +struct dlist *dlist_append( struct dlist *list, void *data ) +{ + struct dlist *node = NULL; + struct dlist *last = NULL; + + node = __dlist_alloc(); + node->data = data; + + if( list ) + { + last = dlist_last( list ); + + dlist_next( last ) = node; + dlist_prev( node ) = last; + + return list; + } + + return node; +} + +struct dlist *dlist_prepend( struct dlist *list, void *data ) +{ + struct dlist *node = NULL; + struct dlist *first = NULL; + + node = __dlist_alloc(); + node->data = data; + + if( list ) + { + first = dlist_first( list ); + + dlist_prev( first ) = node; + dlist_next( node ) = first; + + return node; + } + + return node; +} + +struct dlist *dlist_insert( struct dlist *list, void *data, int position ) +{ + struct dlist *node = NULL; + struct dlist *ptr = NULL; + + if( position < 0 ) { return dlist_append( list, data ); } + if( position == 0 ) { return dlist_prepend( list, data ); } + + ptr = dlist_nth( list, position ); + if( !ptr ) { return dlist_append( list, data ); } + + node = __dlist_alloc(); + node->data = data; + + node->prev = ptr->prev; + ptr->prev->next = node; + node->next = ptr; + ptr->prev = node; + + return list; +} + +struct dlist *dlist_insert_sorted( struct dlist *list, DLCMPF cmp_func, void *data ) +{ + struct dlist *node = NULL; + struct dlist *ptr = list; + int cmp; + + if( !cmp_func ) return list; + + if( !list ) + { + node = __dlist_alloc(); + node->data = data; + return node; + } + + cmp = cmp_func( data, ptr->data ); + + while( (ptr->next) && (cmp > 0) ) + { + ptr = ptr->next; + cmp = cmp_func( data, ptr->data ); + } + + node = __dlist_alloc(); + node->data = data; + + if( (!ptr->next) && (cmp > 0) ) + { + ptr->next = node; + node->prev = ptr; + return list; + } + + if( ptr->prev ) + { + ptr->prev->next = node; + node->prev = ptr->prev; + } + node->next = ptr; + ptr->prev = node; + + if( ptr == list ) + return node; + else + return list; +} + +struct dlist *dlist_insert_sorted_with_data( struct dlist *list, DLCMPDF cmp_func, void *data, void *user_data ) +{ + struct dlist *node = NULL; + struct dlist *ptr = list; + int cmp; + + if( !cmp_func ) return list; + + if( !list ) + { + node = __dlist_alloc(); + node->data = data; + return node; + } + + cmp = cmp_func( data, ptr->data, user_data ); + + while( (ptr->next) && (cmp > 0) ) + { + ptr = ptr->next; + cmp = cmp_func( data, ptr->data, user_data ); + } + + node = __dlist_alloc(); + node->data = data; + + if( (!ptr->next) && (cmp > 0) ) + { + ptr->next = node; + node->prev = ptr; + return list; + } + + if( ptr->prev ) + { + ptr->prev->next = node; + node->prev = ptr->prev; + } + node->next = ptr; + ptr->prev = node; + + if( ptr == list ) + return node; + else + return list; +} + +struct dlist *dlist_concat( struct dlist *list1, struct dlist *list2 ) +{ + struct dlist *ptr = NULL; + + if( list2 ) + { + ptr = dlist_last( list1 ); + if( ptr ) + ptr->next = list2; + else + list1 = list2; + + list2->prev = ptr; + } + + return list1; +} + +struct dlist *dlist_insert_list( struct dlist *list1, struct dlist *list2, int position ) +{ + struct dlist *last = NULL; + struct dlist *ptr = NULL; + + if( position < 0 ) { return dlist_concat( list1, list2 ); } + if( position == 0 ) { return dlist_concat( list2, list1 ); } + + ptr = dlist_nth( list1, position ); + if( !ptr ) { return dlist_concat( list1, list2 ); } + + last = dlist_last( list2 ); + if( last ) + { + list2->prev = ptr->prev; + ptr->prev->next = list2; + last->next = ptr; + ptr->prev = last; + } + + return list1; +} + +struct dlist *__dlist_remove_link( struct dlist *list, struct dlist *link ) +{ + if( link == NULL ) return list; + + if( link->prev ) + { + if( link->prev->next == link ) + { + link->prev->next = link->next; + } + else + { + WARNING( "Corrupted double-linked list detected" ); + } + } + if( link->next ) + { + if( link->next->prev == link ) + { + link->next->prev = link->prev; + } + else + { + WARNING( "Corrupted double-linked list detected" ); + } + } + + if( link == list ) list = list->next; + + link->next = NULL; + link->prev = NULL; + + return list; +} + +struct dlist *dlist_remove( struct dlist *list, const void *data ) +{ + struct dlist *ptr = list; + + while( ptr ) + { + if( ptr->data != data ) + { + ptr = ptr->next; + } + else + { + list = __dlist_remove_link( list, ptr ); + free( ptr ); + + break; + } + } + + return list; +} + +struct dlist *dlist_remove_all( struct dlist *list, const void *data ) +{ + struct dlist *ptr = list; + + while( ptr ) + { + if( ptr->data != data ) + { + ptr = ptr->next; + } + else + { + struct dlist *next = ptr->next; + + if( ptr->prev ) + ptr->prev->next = next; + else + list = next; + + if( next ) + next->prev = ptr->prev; + + free( ptr ); + + ptr = next; + } + } + + return list; +} + +struct dlist *dlist_remove_data( struct dlist *list, DLCMPF cmp_func, DLFUNC free_func, const void *data ) +{ + struct dlist *ptr = list; + + if( !cmp_func ) return list; + + while( ptr ) + { + if( cmp_func( ptr->data, data ) != 0 ) + { + ptr = ptr->next; + } + else + { + list = __dlist_remove_link( list, ptr ); + if( free_func ) free_func( ptr->data, (void *)data ); /* free_func() can compare pointers */ + free( ptr ); + + break; + } + } + + return list; +} + +struct dlist *dlist_remove_data_all( struct dlist *list, DLCMPF cmp_func, DLFUNC free_func, const void *data ) +{ + struct dlist *ptr = list; + + if( !cmp_func ) return list; + + while( ptr ) + { + if( cmp_func( ptr->data, data ) != 0 ) + { + ptr = ptr->next; + } + else + { + struct dlist *next = ptr->next; + + if( ptr->prev ) + ptr->prev->next = next; + else + list = next; + + if( next ) + next->prev = ptr->prev; + + if( free_func ) free_func( ptr->data, (void *)data ); /* free_func() can compare pointers */ + free( ptr ); + + ptr = next; + } + } + + return list; +} + + +struct dlist *dlist_copy( struct dlist *list ) +{ + struct dlist *copy = NULL; + + while( list ) + { + copy = dlist_append( copy, list->data ); + list = dlist_next( list ); + } + + return copy; +} + +/* It simply switches the next and prev pointers of each element. */ +struct dlist *dlist_reverse( struct dlist *list ) +{ + struct dlist *last = NULL; + + while( list ) + { + last = list; + list = last->next; + last->next = last->prev; + last->prev = list; + } + + return last; +} + + +static struct dlist *__dlist_sort_merge( struct dlist *l1, struct dlist *l2, DLCMPF cmp_func ) +{ + struct dlist list, *l, *lprev; + int cmp; + + l = &list; + lprev = NULL; + + while( l1 && l2 ) + { + cmp = ((DLCMPF) cmp_func)( l1->data, l2->data ); + + if( cmp <= 0 ) + { + l->next = l1; + l1 = l1->next; + } + else + { + l->next = l2; + l2 = l2->next; + } + l = l->next; + l->prev = lprev; + lprev = l; + } + l->next = l1 ? l1 : l2; + l->next->prev = l; + + return list.next; +} + +static struct dlist *__dlist_sort_merge_with_data( struct dlist *l1, struct dlist *l2, DLCMPDF cmp_func, void *user_data ) +{ + struct dlist list, *l, *lprev; + int cmp; + + l = &list; + lprev = NULL; + + while( l1 && l2 ) + { + cmp = ((DLCMPDF) cmp_func)( l1->data, l2->data, user_data ); + + if( cmp <= 0 ) + { + l->next = l1; + l1 = l1->next; + } + else + { + l->next = l2; + l2 = l2->next; + } + l = l->next; + l->prev = lprev; + lprev = l; + } + l->next = l1 ? l1 : l2; + l->next->prev = l; + + return list.next; +} + + +static struct dlist *__dlist_sort_real( struct dlist *list, DLCMPF cmp_func ) +{ + struct dlist *l1, *l2; + + if( !list ) + return NULL; + if( !list->next ) + return list; + + l1 = list; + l2 = list->next; + + while( (l2 = l2->next) != NULL ) + { + if( (l2 = l2->next) == NULL ) + break; + l1 = l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return __dlist_sort_merge( __dlist_sort_real( list, cmp_func ), + __dlist_sort_real( l2, cmp_func ), + cmp_func ); +} + +static struct dlist *__dlist_sort_real_with_data( struct dlist *list, DLCMPDF cmp_func, void *user_data ) +{ + struct dlist *l1, *l2; + + if( !list ) + return NULL; + if( !list->next ) + return list; + + l1 = list; + l2 = list->next; + + while( (l2 = l2->next) != NULL ) + { + if( (l2 = l2->next) == NULL ) + break; + l1 = l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return __dlist_sort_merge_with_data( __dlist_sort_real_with_data( list, cmp_func, user_data ), + __dlist_sort_real_with_data( l2, cmp_func, user_data ), + cmp_func, + user_data ); +} + + +struct dlist *dlist_sort( struct dlist *list, DLCMPF cmp_func ) +{ + return __dlist_sort_real( list, cmp_func ); +} + +struct dlist *dlist_sort_with_data( struct dlist *list, DLCMPDF cmp_func, void *user_data ) +{ + return __dlist_sort_real_with_data( list, cmp_func, user_data ); +} + + +void dlist_foreach( struct dlist *list, DLFUNC func, void *user_data ) +{ + struct dlist *next = NULL; + + while( list ) + { + next = dlist_next( list ); + if( func ) { func( list->data, user_data ); } + list = next; + } +} + + +void __dlist_free( struct dlist *list ) +{ + struct dlist *next = NULL; + + while( list ) + { + next = dlist_next( list ); + free( list ); list = NULL; + list = next; + } +} + +void dlist_free( struct dlist *list, DLFUNC free_func ) +{ + dlist_foreach( list, free_func, NULL ); + __dlist_free( list ); +} diff --git a/src/dlist.h b/src/dlist.h new file mode 100644 index 0000000..0a84683 --- /dev/null +++ b/src/dlist.h @@ -0,0 +1,83 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _DLIST_H_ +#define _DLIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +struct dlist { + struct dlist *prev; + struct dlist *next; + + void *data; +}; + +typedef void (*DLFUNC) ( void *data, void *user_data ); +typedef int (*DLCMPF) ( const void *a, const void *b ); +typedef int (*DLCMPDF) ( const void *a, const void *b, void *user_data ); + +#define dlist_prev( list ) ( (list)->prev ) +#define dlist_next( list ) ( (list)->next ) + +extern struct dlist *__dlist_alloc( void ); +extern struct dlist *dlist_first( struct dlist *list ); +extern struct dlist *dlist_last( struct dlist *list ); +extern int dlist_length( struct dlist *list ); +extern struct dlist *dlist_nth( struct dlist *list, int n ); +extern void *dlist_nth_data( struct dlist *list, int n ); +extern int dlist_position( struct dlist *list, struct dlist *link ); +extern int dlist_index( struct dlist *list, const void *data ); +extern struct dlist *dlist_find( struct dlist *list, const void *data ); +extern struct dlist *dlist_find_data( struct dlist *list, DLCMPF func, const void *data ); + +extern struct dlist *dlist_append( struct dlist *list, void *data ); +extern struct dlist *dlist_prepend( struct dlist *list, void *data ); +extern struct dlist *dlist_insert( struct dlist *list, void *data, int position ); +extern struct dlist *dlist_insert_sorted( struct dlist *list, DLCMPF cmp_func, void *data ); +extern struct dlist *dlist_insert_sorted_with_data( struct dlist *list, DLCMPDF cmp_func, void *data, void *user_data ); +extern struct dlist *dlist_concat( struct dlist *list1, struct dlist *list2 ); +extern struct dlist *dlist_insert_list( struct dlist *list1, struct dlist *list2, int position ); + +extern struct dlist *__dlist_remove_link( struct dlist *list, struct dlist *link ); +extern struct dlist *dlist_remove( struct dlist *list, const void *data ); +extern struct dlist *dlist_remove_all( struct dlist *list, const void *data ); +extern struct dlist *dlist_remove_data( struct dlist *list, DLCMPF cmp_func, DLFUNC free_func, const void *data ); +extern struct dlist *dlist_remove_data_all( struct dlist *list, DLCMPF cmp_func, DLFUNC free_func, const void *data ); + +extern struct dlist *dlist_copy( struct dlist *list ); +extern struct dlist *dlist_reverse( struct dlist *list ); + +extern struct dlist *dlist_sort( struct dlist *list, DLCMPF cmp_func ); +extern struct dlist *dlist_sort_with_data( struct dlist *list, DLCMPDF cmp_func, void *user_data ); + +extern void dlist_foreach( struct dlist *list, DLFUNC func, void *user_data ); + + +extern void __dlist_free( struct dlist *list ); +extern void dlist_free( struct dlist *list, DLFUNC free_func ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _DLIST_H_ */ diff --git a/src/install-package.c b/src/install-package.c new file mode 100644 index 0000000..9b73bf7 --- /dev/null +++ b/src/install-package.c @@ -0,0 +1,2764 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include + +#include +#include + +#if defined( HAVE_DIALOG ) +#include +#endif + +#define PROGRAM_NAME "install-package" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *rempkgs_path = NULL, + *pkg_fname = NULL, *asc_fname = NULL, *pkglog_fname = NULL, *pkglist_fname = NULL, + *tmpdir = NULL, *curdir = NULL, *log_fname = NULL; + +int ask = 0, rqck = 0, gpgck = 0, ignore_chrefs_errors = 0; +char *description = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *short_description = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *compressed_size = NULL, + *total_files = NULL; + +enum _install_mode { + CONSOLE = 0, + INFODIALOG, + MENUDIALOG +} install_mode = CONSOLE; + +enum _priority { + REQUIRED = 0, /* synonims: REQUIRED | required | REQ | req */ + RECOMMENDED, /* synonims: RECOMMENDED | recommended | REC | rec */ + OPTIONAL, /* synonims: OPTIONAL | optional | OPT | opt */ + SKIP /* synonims: SKIP | skip | SKP | skp */ +} priority = REQUIRED; + +enum _procedure +{ + INSTALL = 0, /* 'install' */ + UPDATE /* 'update' */ +} procedure = INSTALL; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +char uncompress = '\0'; + +static struct dlist *dirs = NULL; +static struct dlist *files = NULL; +static struct dlist *links = NULL; + +static void free_list( struct dlist *list ); + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( short_description ) { free( short_description ); } short_description = NULL; \ + if( description ) { free( description ); } description = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( compressed_size ) { free( compressed_size ); } compressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( rempkgs_path ) { free( rempkgs_path ); rempkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + if( asc_fname ) { free( asc_fname ); asc_fname = NULL; } + if( pkglog_fname ) { free( pkglog_fname ); pkglog_fname = NULL; } + + if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; } + + if( dirs ) { free_list( dirs ); dirs = NULL; } + if( files ) { free_list( files ); files = NULL; } + if( links ) { free_list( links ); links = NULL; } + + if( curdir ) { free( curdir ); curdir = NULL; } + if( log_fname ) { free( log_fname ); log_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Install package.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -a,--always-ask Used with menudialog mode: always ask\n" ); + fprintf( stdout, " if a package should be installed regardless\n" ); + fprintf( stdout, " of what the package priority is. Without\n" ); + fprintf( stdout, " this option, if the priority is equal to\n" ); + fprintf( stdout, " REQUIRED, the package is installed without\n" ); + fprintf( stdout, " asking for confirmation the installation.\n" ); + fprintf( stdout, " -c,--check-requires Check package requires before install.\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, " -g,--gpg-verify Verify GPG2 signature. The signature must be\n" ); + fprintf( stdout, " saved in a file whose name is the same as the\n" ); + fprintf( stdout, " package file name, but with the extension '.asc'\n" ); + fprintf( stdout, " and located in the same directory as the package.\n" ); +#endif + fprintf( stdout, " --ignore-chrefs-errors Ignore change references errors (code: 48).\n" ); +#if defined( HAVE_DIALOG ) + fprintf( stdout, " -i,--info-dialog Show package description during install\n" ); + fprintf( stdout, " process using ncurses dialog.\n" ); + fprintf( stdout, " -m,--menu-dialog Ask for confirmation the inatallation,\n" ); + fprintf( stdout, " unless the priority is REQUIRED.\n" ); +#endif + fprintf( stdout, " -l,--pkglist= Specify a different package list file\n" ); + fprintf( stdout, " to use for read package priority and type\n" ); + fprintf( stdout, " of install procedure. By default used the\n" ); + fprintf( stdout, " '.pkglist' file found in the directory\n" ); + fprintf( stdout, " where source package is placed.\n" ); + fprintf( stdout, " -p,--priority=\n" ); + fprintf( stdout, " Provides a priority of package instead of\n" ); + fprintf( stdout, " the priority defined in the .pkglist file.\n" ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " The PACKAGE tarball.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Return codes:\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " code | status\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " 31 | package is already installed\n" ); + fprintf( stdout, " 32 | package is already installed but not correct\n" ); + fprintf( stdout, " 33 | previous version is already installed\n" ); + fprintf( stdout, " 34 | previous version is already installed but not correct\n" ); + fprintf( stdout, " 35 | a newer version is already installed\n" ); + fprintf( stdout, " 36 | a newer version is already installed but not correct\n" ); + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 41 | installation is aborted due to priority=SKIP\n" ); + fprintf( stdout, " 42 | .pkglist appointed the 'update' procedure instead\n" ); + fprintf( stdout, " 43 | pre-install script returned error status\n" ); + fprintf( stdout, " 44 | uncompress process returned error status\n" ); + fprintf( stdout, " 45 | restore-links script returned error status\n" ); + fprintf( stdout, " 46 | post-install script returned error status\n" ); + fprintf( stdout, " 47 | PKGLOG cannot be stored in the Setup Database\n" ); + fprintf( stdout, " 48 | references cannot be updated in Setup Database\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 51 | signature verification returned error status\n" ); +#endif + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Upon successful completion zero is returned. Other non-zero return\n" ); + fprintf( stdout, "codes imply incorrect completion of the installation.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static void bind_asc_extention( char *name ) +{ + char *p = NULL, *q = NULL; + + if( (p = rindex( name, '.' )) && (strlen(p) < 5) ) + { + if( !strncmp( p, ".gz", 3 ) || + !strncmp( p, ".bz2", 4 ) || + !strncmp( p, ".xz", 3 ) ) + { + *p = '\0'; + q = rindex( name, '.' ); + if( q && (strlen(q) < 5) && !strncmp( q, ".tar", 4 ) ) + { + *q = '\0'; + } + } + else if( !strncmp( p, ".tar", 4 ) || + !strncmp( p, ".tbz", 4 ) || + !strncmp( p, ".tgz", 4 ) || + !strncmp( p, ".txz", 4 ) ) + { + *p = '\0'; + } + } + + (void)strcat( name, ".asc" ); +} + +//////////////////////////////////////////////////// +//static char *strmode( enum _install_mode mode ) +//{ +// char *p = NULL; +// +// switch( mode ) +// { +// case CONSOLE: +// p = "CONSOLE"; +// break; +// case INFODIALOG: +// p = "INFODIALOG"; +// break; +// case MENUDIALOG: +// p = "MENUDIALOG"; +// break; +// } +// return p; +//} +//////////////////////////////////////////////////// + +static char *strprio( enum _priority priority, int short_name ) +{ + char *p = NULL; + + switch( priority ) + { + case REQUIRED: + p = ( short_name ) ? "REQ" : "required"; + break; + case RECOMMENDED: + p = ( short_name ) ? "REC" : "recommended"; + break; + case OPTIONAL: + p = ( short_name ) ? "OPT" : "optional"; + break; + case SKIP: + p = ( short_name ) ? "SKP" : "skip"; + break; + } + return p; +} + +static char *strproc( enum _procedure procedure ) +{ + char *p = NULL; + + switch( procedure ) + { + case INSTALL: + p = "install"; + break; + case UPDATE: + p = "update"; + break; + } + return p; +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ +#if defined( HAVE_GPG2 ) +#if defined( HAVE_DIALOG ) + const char* short_options = "hvacgiml:p:r:"; +#else + const char* short_options = "hvacgl:p:r:"; +#endif +#else +#if defined( HAVE_DIALOG ) + const char* short_options = "hvaciml:p:r:"; +#else + const char* short_options = "hvacl:p:r:"; +#endif +#endif + +#define IGNORE_CHREFS_ERRORS 872 + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "always-ask", no_argument, NULL, 'a' }, + { "check-requires", no_argument, NULL, 'c' }, +#if defined( HAVE_GPG2 ) + { "gpg-verify", no_argument, NULL, 'g' }, +#endif + { "ignore-chrefs-errors", no_argument, NULL, IGNORE_CHREFS_ERRORS }, +#if defined( HAVE_DIALOG ) + { "info-dialog", no_argument, NULL, 'i' }, + { "menu-dialog", no_argument, NULL, 'm' }, +#endif + { "pkglist", required_argument, NULL, 'l' }, + { "priority", required_argument, NULL, 'p' }, + { "root", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'a': + { + ask = 1; + break; + } + case 'c': + { + rqck = 1; + break; + } +#if defined( HAVE_GPG2 ) + case 'g': + { + gpgck = 1; + break; + } +#endif + +#if defined( HAVE_DIALOG ) + case 'i': + { + install_mode = INFODIALOG; + break; + } + case 'm': + { + install_mode = MENUDIALOG; + break; + } +#endif + + case IGNORE_CHREFS_ERRORS: + { + ignore_chrefs_errors = 1; + break; + } + + case 'p': + { + if( optarg != NULL ) + { + char *match = NULL; + + if( strlen( (const char *)optarg ) > 2 ) + { + to_lowercase( optarg ); + if( (match = strstr( optarg, "req" )) && match == optarg ) { + priority = REQUIRED; + } + else if( (match = strstr( optarg, "rec" )) && match == optarg ) { + priority = RECOMMENDED; + } + else if( (match = strstr( optarg, "opt" )) && match == optarg ) { + priority = OPTIONAL; + } + else if( (match = strstr( optarg, "sk" )) && match == optarg ) { + priority = SKIP; + } + else { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'l': + { + if( optarg != NULL ) + { + pkglist_fname = xstrdup( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + char cwd[PATH_MAX]; + + bzero( (void *)cwd, PATH_MAX ); + if( optarg[0] != '/' && curdir ) + { + /* skip current directory definition './' at start of argument: */ + if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) ) + (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 ); + else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) ) + (void)sprintf( cwd, "%s", curdir ); + else + (void)sprintf( cwd, "%s/%s", curdir, optarg ); + root = xstrdup( (const char *)cwd ); + } + else + { + root = xstrdup( (const char *)optarg ); + } + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + /* absolute path to input package: */ + if( argv[optind][0] != '/' && curdir ) + (void)sprintf( buf, "%s/%s", curdir, (const char *)argv[optind] ); + else + (void)strcpy( buf, (const char *)argv[optind] ); + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file: %s", buf, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + pkg_fname = xstrdup( (const char *)&buf[0] ); + bind_asc_extention( buf ); + asc_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Input package '%s' is not a regular file", (const char *)argv[optind] ); + } + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + int len; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = xstrdup( (const char *)buf ); + } + else + { + len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + free( root ); root = xstrdup( (const char *)buf ); + } + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + if( !S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + len = strlen( (const char *)buf ); + + (void)strcat( buf, PACKAGES_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + pkgs_path = xstrdup( (const char *)&buf[0] ); + + /********************************************* + Create other directories of Setup Database: + */ + buf[len] = '\0'; + (void)strcat( buf, REMOVED_PKGS_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + rempkgs_path = xstrdup( (const char *)&buf[0] ); + + buf[len] = '\0'; + (void)strcat( buf, SETUP_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", SETUP_PATH ); + } + + /********************************************* + Allocate memory for Setup LOG File name: + */ + buf[len] = '\0'; + (void)strcat( buf, LOG_PATH ); + (void)strcat( buf, SETUP_LOG_FILE ); + log_fname = xstrdup( (const char *)&buf[0] ); + + free( buf ); + + } /* End if( !pkgs_path ) */ +} + +static void setup_log( char *format, ... ) +{ + FILE *fp = NULL; + + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + va_list argp; + + if( ! format ) return; + + fp = fopen( (const char *)log_fname, "a" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open /%s%s file", LOG_PATH, SETUP_LOG_FILE ); + } + + fprintf( fp, "[%04d-%02d-%02d %02d:%02d:%02d]: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + + va_start( argp, format ); + vfprintf( fp, format, argp ); + fprintf( fp, "\n" ); + + fflush( fp ); + fclose( fp ); +} + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +static char *trim( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } + p = s; while( isspace( *p ) ) { ++p; } + + return( p ); +} + + +static char *size_to_string( size_t pkgsize ) +{ + int nd; + double sz = (double)pkgsize / (double)1024; + + char *ret = NULL; + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( sz > (double)1048576 ) + { + sz = sz / (double)1048576; + /* + NOTE: + ---- + Операция округления до одного знака после десятичной точки: sz = round(sz*10.0)/10.0; + здесь не нужна; можно обойтись вычислением количества цифр, выводимых на печать с помощью + формата '%.*g': + + Количество десятичных цифр, необходимое для предстваления целой части числа + 1(одна) + десятичная цифра после десятичной точки. Формат %.*g не будет выводить дробную часть + числа, если после округления, до одного знака после десятичной точки, дробная часть + равна нулю: + */ + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gG", nd, sz ); + } + else if( sz > (double)1024 ) + { + sz = sz / (double)1024; + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gM", nd, sz ); + } + else + { + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gK", nd, sz ); + } + + ret = xstrdup( (const char *)&tmp[0] ); + free( tmp ); + + return ret; +} + +static void read_input_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) arch = skip_spaces( p ); + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distroname = skip_spaces( p ); + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distrover = skip_spaces( p ); + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + + if( (match = strstr( ln, "short_description" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + char *b = index( p, '"'), + *e = rindex( p, '"'); + if( b && e && ( b != e ) ) + { + p = ++b; *e = '\0'; + short_description = xstrdup( (const char *)p ); + } + } + } + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) url = skip_spaces( p ); + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) license = skip_spaces( p ); + } + + if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) uncompressed_size = skip_spaces( p ); + } + if( (match = strstr( ln, "total_files" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) total_files = skip_spaces( p ); + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); +} + + +static void read_service_files( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type != IFMT_PKG ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s" + " -o pkginfo,description,requires,restore-links,install-script,filelist" + " %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_input_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + compressed_size = size_to_string( st.st_size ); + + /****************** + Get PKGLOG file: + */ + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkglog -m -d %s %s > /dev/null 2>&1", + selfdir, tmp, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGLOG from '%s' file", basename( (char *)fname ) ); + } + + if( group ) + (void)sprintf( cmd, "%s/%s/%s-%s-%s-%s-%s", tmp, group, pkgname, pkgver, arch, distroname, distrover ); + else + (void)sprintf( cmd, "%s/%s-%s-%s-%s-%s", tmp, pkgname, pkgver, arch, distroname, distrover ); + + bzero( (void *)&st, sizeof( struct stat ) ); + if( stat( (const char *)cmd, &st ) == -1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from '%s' file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_fname = xstrdup( (const char *)cmd ); + + /************************************* + Attempt to read packages list file: + */ + { + if( !pkglist_fname ) + { + /***************************************** + Get source packages path if applicable: + */ + (void)strcpy( cmd, (const char *)fname ); + (void)strcpy( tmp, dirname( cmd ) ); + + if( group && !strcmp( group, basename( tmp ) ) ) + (void)strcpy( cmd, (const char *)dirname( tmp ) ); + else + (void)strcpy( cmd, (const char *)tmp ); + + /***************************************** + Save default packages list file name: + */ + (void)strcat( cmd, "/.pkglist" ); + pkglist_fname = xstrdup( (const char *)cmd ); + } + + /************************** + read .pkglist if exists: + */ + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)pkglist_fname, &st ) == 0) && S_ISREG(st.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + + FILE *pkglist = NULL; + + pkglist = fopen( (const char *)pkglist_fname, "r" ); + if( !pkglist ) + { + FATAL_ERROR( "Cannot open %s file", pkglist_fname ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkglist )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname )) && match == ln ) + { + char *p = NULL; + char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL; + + name = ln; + if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue; + if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue; + if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue; + if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue; + if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue; + prio = trim( prio ); + + if( name && vers && desc && ball && proc && prio ) + { + char *grp = index( (const char *)ball, '/' ); + if( grp ) + { + *grp = '\0'; grp = ball; grp = trim( grp ); + if( strcmp( group, grp ) ) continue; + } + + /* read priority: */ + if( strlen( (const char*)prio ) > 2 ) + { + char *m = NULL; + + to_lowercase( prio ); + if( (m = strstr( prio, "req" )) && m == prio ) { + priority = REQUIRED; + } + else if( (m = strstr( prio, "rec" )) && m == prio ) { + priority = RECOMMENDED; + } + else if( (m = strstr( prio, "opt" )) && m == prio ) { + priority = OPTIONAL; + } + else if( (m = strstr( prio, "sk" )) && m == prio ) { + priority = SKIP; + } + else { + priority = REQUIRED; + } + } + else + { + priority = REQUIRED; + } + + /* read procedure: */ + if( strlen( (const char*)proc ) > 5 ) + { + char *m = NULL; + + to_lowercase( proc ); + if( (m = strstr( proc, "install" )) && m == proc ) { + procedure = INSTALL; + } + else if( (m = strstr( proc, "update" )) && m == proc ) { + procedure = UPDATE; + } + else { + procedure = INSTALL; + } + } + else + { + procedure = INSTALL; + } + } + + } /* End if( match ) */ + + } /* End of while( ln = fgets() ) */ + + free( line ); + fclose( pkglist ); + + } /* End of reading .pkglist */ + + } /* End of attemption of reading .pkflist file */ + + free( cmd ); + free( tmp ); + + if( priority == SKIP ) + { + exit_status = 41; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], + "\nInstall procedure is skipped due to specified\nthe '%s' priority.\n", + strprio( priority, 0 ) ); + + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + (const char *)&tmp[0], 6, 0, 0 ); + + free( tmp ); +#else + fprintf( stdout, + "\nInstall procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n", + pkgname, pkgver, strprio( priority, 0 ) ); +#endif + } + else + { + fprintf( stdout, + "\nInstall procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n", + pkgname, pkgver, strprio( priority, 0 ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + if( procedure != INSTALL ) + { + exit_status = 42; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], + "\nInstall procedure is skipped because the '%s' procedure\nis specified.\n", + strproc( procedure ) ); + + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + (const char *)&tmp[0], 6, 0, 0 ); + + free( tmp ); +#else + fprintf( stdout, + "\nInstall procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n", + pkgname, pkgver, strproc( procedure ) ); +#endif + } + else + { + fprintf( stdout, + "\nInstall procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n", + pkgname, pkgver, strproc( procedure ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } +} + +static void check_package( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/check-package --quiet --root=%s %s > /dev/null 2>&1", + selfdir, root, pkglog_fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot check whether the package '%s-%s' is already installed", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + switch( rc ) + { + case 30: + /* Package is not installed. Continue the installation. */ + break; + case 31: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage is already installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' is already installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPackage '%s-%s' is already installed.\n\n", pkgname, pkgver ); + } + break; + case 32: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage is already installed but not correct.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' is already installed but not correct.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPackage '%s-%s' is already installed but not correct.\n\n", pkgname, pkgver ); + } + break; + case 33: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPrevious version of package is installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPrevious version of package '%s-%s' is installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPrevious version of package '%s-%s' is installed.\n\n", pkgname, pkgver ); + } + break; + case 34: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPrevious version of package is installed but not correct.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPrevious version of package '%s-%s' is installed but not correct.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPrevious version of package '%s-%s' is installed but not correct.\n\n", pkgname, pkgver ); + } + break; + case 35: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nA newer version of package is already installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nA newer version of package '%s-%s' is already installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nA newer version of package '%s-%s' is already installed.\n\n", pkgname, pkgver ); + } + break; + case 36: + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nA newer version of package is installed but not correct.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nA newer version of package '%s-%s' is installed but not correct.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nA newer version of package '%s-%s' is installed but not correct.\n\n", pkgname, pkgver ); + } + break; + default: + FATAL_ERROR( "Cannot check whether the package '%s-%s' is already installed", pkgname, pkgver ); + break; + } + + free( cmd ); + + if( rc != 30 ) + { + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( rc ); + } +} + + +static void check_requires( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/check-requires --root=%s %s > /dev/null 2>&1", + selfdir, root, pkglog_fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot check required packages for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage requires other packages to be installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( rc ); + } +} + +/******************************************************** + Read .FILELIST and .RESTORELINKS functions used for + roolback in case postinstall errors: + */ +static int __cmp_list_items( const void *a, const void *b ) +{ + if( a && b ) + return strcmp( (const char *)a, (const char *)b ); + else if( a ) + return 1; + else + return -1; +} + +static void __free_list( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void free_list( struct dlist *list ) +{ + if( list ) { dlist_free( list, __free_list ); } +} + +//////////////////////////////////////////////////// +//static void __print_list( void *data, void *user_data ) +//{ +// int *counter = (int *)user_data; +// +// if( counter ) { fprintf( stdout, "item[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } +// else { fprintf( stdout, "item: %s\n", (char *)data ); } +//} +// +//static void print_list( struct dlist *list ) +//{ +// int cnt = 0; +// if( list ) { dlist_foreach( list, __print_list, (void *)&cnt ); } +//} +//////////////////////////////////////////////////// + +static void read_filelist( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.FILELIST", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) ) + { + FATAL_ERROR( "Cannot get .FILELIST from '%s' file", basename( (char *)pkg_fname ) ); + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) + { + *(ln + strlen(ln) - 1) = '\0'; + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + dirs = dlist_append( dirs, xstrdup( (const char *)&tmp[0] ) ); + } + else + { + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + files = dlist_append( files, xstrdup( (const char *)&tmp[0] ) ); + } + + } /* End of while( file list entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} + +static void read_restorelinks( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &tmp[0], "%s%s/%s", (const char *)root, p, f ); + links = dlist_append( links, xstrdup( (const char *)&tmp[0] ) ); + } + } + } /* End of while( restore links entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} + +/* + End of read .FILELIST and .RESTORELINKS functions. + ********************************************************/ + +/******************************************************** + Rollback functions: + */ +static void __remove_link( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + (void)unlink( fname ); + } +} + +static void __remove_file( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + char *p = rindex( fname, '.' ); + /* + Если .new файл остался с тем же именем, это значит что до инсталляции + в системе существовал такой же файл но без расширения .new и при этом + он отличался от нового. В данном случае надо удалять только файл .new. + + Если же файл .new не существует, то надо удалять такой же файл но без + расширения .new . + */ + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + struct stat st; + + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( fname, &st ) == -1) ) *p = '\0'; + } + + (void)unlink( fname ); + } +} + +static int is_dir_empty( const char *dirpath ) +{ + int ret = 0; + + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) return ret; /* stat returns error code; errno is set */ + if( S_ISDIR(path_sb.st_mode) == 0 ) return ret; /* dirpath is not a directory */ + if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */ + + ret = 1; + + len = strlen( dirpath ); + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + ret = 0; + break; + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + closedir( dir ); + + return ret; +} + +static void __remove_dir( void *data, void *user_data ) +{ + const char *dname = (const char *)data; + + if( dname && is_dir_empty( (const char *)dname ) ) + { + (void)rmdir( dname ); + } +} + +static void rollback( void ) +{ + /* Try to change CWD to the ROOT directory: */ + (void)chdir( (const char *)root ); + + if( links ) { dlist_foreach( links, __remove_link, NULL ); } + + if( files ) { dlist_foreach( files, __remove_file, NULL ); } + + if( dirs ) + { + dirs = dlist_sort( dirs, __cmp_list_items ); + dirs = dlist_reverse( dirs ); + dlist_foreach( dirs, __remove_dir, NULL ); + } + + /* Try to remove PKGLOG file */ + { + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( tmp ) + { + const char *fname = basename( (char *)pkglog_fname ); + + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/%s", pkgs_path, group, fname ); + else + (void)sprintf( &tmp[0], "%s/%s", pkgs_path, fname ); + + (void)unlink( (const char *)&tmp[0] ); + + if( group ) + { + const char *dir = (const char *)dirname( (char *)&tmp[0] ); + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + free( tmp ); + } + } + + /* Try to change CWD to the CURRENT directory: */ + (void)chdir( (const char *)curdir ); +} +/* + End of rollback functions. + ********************************************************/ + +static void read_description( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *buf = NULL, *tmp = NULL; + char *lp = NULL; + int n = 0; + + char *ln = NULL; + char *line = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.DESCRIPTION", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .DESCRIPTION file" ); + } + + (void)sprintf( (char *)&buf[0], "%s:", pkgname ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + lp = (char *)&tmp[0]; + bzero( (void *)tmp, PATH_MAX ); + (void)sprintf( (char *)&tmp[0], "\n" ); + ++lp; + + while( (ln = fgets( line, PATH_MAX, fp )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */ + { + int mlen = strlen( match ), plen = strlen( buf ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + + match += plen + 1; + if( match[0] != '\0' ) { (void)sprintf( lp, " %s\n", match ); lp += strlen( match ) + 2; } + else { (void)sprintf( lp, "\n" ); ++lp; } + ++n; + } + } /* End of while( ln = fgets() ) */ + + fclose( fp ); + + (void)sprintf( lp, " Uncompressed Size: %s\n", uncompressed_size ); + lp += strlen( uncompressed_size ) + 21; + (void)sprintf( lp, " Compressed Size: %s\n", compressed_size ); + lp += strlen( compressed_size ) + 21; + + description = xstrdup( (const char *)&tmp[0] ); + + free( buf ); + free( line ); + free( tmp ); +} + +static int ask_for_install( void ) +{ + int ret = 0; /* continue installation */ +#if defined( HAVE_DIALOG ) + /****************************************************** + Ask for install dialog shown only in MENUDIALOG mode + when priority != REQUIRED or --always-ask=yes: + */ + if( (install_mode == MENUDIALOG) && (((priority == REQUIRED) && ask) || (priority != REQUIRED)) ) + { + ret = ask_install_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + description, 18, 0, 0 ); + } + + if( ret ) + { + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nInstallation terminated by user.\n", 5, 0, 0 ); + } +#endif + return ret; +} + + +static void show_install_progress( void ) +{ + fprintf( stdout, "\033[2J" ); /* clear screen */ + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + description, 16, 0, 0 ); +#else + fprintf( stdout, "\n Install: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 )); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */ +#endif + } + else + { + fprintf( stdout, "\n Install: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 )); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */ + } +} + + +static void pre_install_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL pre_install %s > /dev/null 2>&1", + root, tmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run pre-install script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 43; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Pre-install script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPre-install script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPre-install script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static const char *fill_decompressor( char *buffer, char compressor ) +{ + switch( compressor ) + { + case 'J': + (void)sprintf( buffer, "xz --threads=%d -dc", get_nprocs() ); + break; + case 'j': + (void)sprintf( buffer, "bzip2 -dc" ); + break; + case 'z': + (void)sprintf( buffer, "gzip -dc" ); + break; + default: + (void)sprintf( buffer, "cat -" ); + break; + } + return (const char *)buffer; +} + +static void uncompress_package( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + char decompressor[64]; + + (void)fill_decompressor( (char *)&decompressor[0], uncompress ); + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cat %s | %s | tar -C %s " + "--exclude='.DESCRIPTION' " + "--exclude='.FILELIST' " + "--exclude='.INSTALL' " + "--exclude='.PKGINFO' " + "--exclude='.REQUIRES' " + "--exclude='.RESTORELINKS' " + "-xf - > /dev/null 2>&1", + pkg_fname, decompressor, root ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot uncompress '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 44; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot uncompress package.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void restore_links( void ) +{ + struct stat st; + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + (void)sprintf( &cmd[0], "%s/.RESTORELINKS", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&cmd[0], &st ) == -1) || (st.st_size < 8) ) + { + free( cmd ); + return; + } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && sh %s/.RESTORELINKS > /dev/null 2>&1", + root, tmpdir ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot restore links for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + rollback(); + + exit_status = 45; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Restore-links script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void post_install_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL post_install %s > /dev/null 2>&1", + root, tmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run post-install script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + rollback(); + + exit_status = 46; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Post-install script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPost-install script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPost-install script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void finalize_installation( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *tmp = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, group ); + else + (void)sprintf( &tmp[0], "%s/", pkgs_path ); + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + + /**************************************** + Store PKGLOG file into Setup Database: + */ + len = snprintf( &cmd[0], PATH_MAX, + "cp %s %s > /dev/null 2>&1", + pkglog_fname, (const char *)&tmp[0] ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot store '%s' pkglog file", basename( (char *)pkglog_fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + rollback(); + + free( cmd ); + free( tmp ); + + exit_status = 47; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot store PKGLOG file into Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) ); +#endif + } + else + { + fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /********************************************* + Increment references in the Setup Database: + */ + if( group ) + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=inc --destination=%s %s/%s > /dev/null 2>&1", + selfdir, pkgs_path, group, basename( (char *)pkglog_fname ) ); + else + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=inc --destination=%s %s > /dev/null 2>&1", + selfdir, pkgs_path, basename( (char *)pkglog_fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot increment '%s-%s' package references", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( (rc != 0) && !ignore_chrefs_errors ) + { + free( tmp ); + + rollback(); + + exit_status = 48; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot increment package references in Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /************************************************* + Remove backup PKGLOG file from removed-packages + directory if exists: + */ + bzero( (void *)tmp, PATH_MAX ); + { + const char *fname = basename( (char *)pkglog_fname ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/%s", rempkgs_path, group, fname ); + else + (void)sprintf( &tmp[0], "%s/%s", rempkgs_path, fname ); + + (void)unlink( (const char *)&tmp[0] ); + + if( group ) + { + const char *dir = (const char *)dirname( (char *)&tmp[0] ); + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + } + + free( tmp ); +} + +#if defined( HAVE_GPG2 ) +static void verify_gpg_signature( void ) +{ + struct stat st; + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + /****************************************************************** + Do not try to verify signature if '.asc' file is not accessible: + */ + if( stat( (const char *)asc_fname, &st ) == -1 ) return; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "gpg2 --verify %s %s > /dev/null 2>&1", + asc_fname, pkg_fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot verify GPG2 signature of '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 51; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot verify GPG2 signature of the package.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} +#endif + + +static void dialogrc( void ) +{ + struct stat st; + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* imagine that the utility is in /sbin directory: */ + (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + if( stat( (const char *)&tmp[0], &st ) == -1 ) + { + /* finaly assume that /usr/sbin is a sbindir: */ + (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + } + + setenv( "DIALOGRC", (const char *)&tmp[0], 1 ); + + free( tmp ); +} + +static char *get_curdir( void ) +{ + char *cwd = NULL; + + cwd = (char *)malloc( PATH_MAX ); + if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cwd, PATH_MAX ); + + if( getcwd( cwd, (size_t)PATH_MAX ) != NULL ) + { + char *p = NULL; + remove_trailing_slash( cwd ); + p = xstrdup( (const char *)cwd ); + free( cwd ); + return p; + } + else + { + FATAL_ERROR( "Cannot get absolute path to current directory" ); + } + + return (char *)NULL; +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + curdir = get_curdir(); + dialogrc(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + + /************************************************************ + Getting Service Files, reading pkginfo, preserving pkglog: + */ + read_service_files(); + + /**************************************************** + Checking whether the package is already installed: + */ + check_package(); + + if( rqck ) check_requires(); +#if defined( HAVE_GPG2 ) + if( gpgck ) verify_gpg_signature(); +#endif + + read_filelist(); + read_restorelinks(); + + read_description(); + + if( ask_for_install() ) + { + /* Terminate installation: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + show_install_progress(); + + /************* + DO INSTALL: + */ + pre_install_routine(); + uncompress_package(); + restore_links(); + post_install_routine(); + finalize_installation(); + + fprintf( stdout, "\033[3A" ); /* move cursor up 3 lines */ + + if( (install_mode != CONSOLE) && (install_mode == MENUDIALOG) ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Install:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage has been installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' has been installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( (install_mode != INFODIALOG) ) + { + fprintf( stdout, "\nPackage '%s-%s' has been installed.\n\n", pkgname, pkgver ); + } + } + + setup_log( "Package '%s-%s' has been installed", pkgname, pkgver ); + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/install-pkglist.c b/src/install-pkglist.c new file mode 100644 index 0000000..7e0cc00 --- /dev/null +++ b/src/install-pkglist.c @@ -0,0 +1,2033 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include + +#if defined( HAVE_DIALOG ) +#include +#endif + +#define PROGRAM_NAME "install-pkglist" + +#include + +#define WAIT_USEC_FOR_CHILD 10000 + +char *program = PROGRAM_NAME; +char *root = NULL, *srcdir = NULL, *pkglist_fname = NULL, + *tmpdir = NULL, *curdir = NULL; + +int rqck = 0, gpgck = 0, progress = 0, parallel = 0, error_pkgs_list = 0, ncpus = 0; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int __done = 0, __child = 0, __terminated = 0, __successful = 0, __all = 0; + +enum _install_mode { + CONSOLE = 0, + INFODIALOG, + MENUDIALOG +} install_mode = CONSOLE; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +}; + +/********************************************* + Package structures and declarations: + */ +enum _procedure +{ + INSTALL = 0, /* 'install' */ + UPDATE /* 'update' */ +}; + +enum _priority +{ + REQUIRED = 0, /* synonims: REQUIRED | required | REQ | req */ + RECOMMENDED, /* synonims: RECOMMENDED | recommended | REC | rec */ + OPTIONAL, /* synonims: OPTIONAL | optional | OPT | opt */ + SKIP /* synonims: SKIP | skip | SKP | skp */ +}; + +struct pkg +{ + char *group; + char *name; + char *version; +}; + +struct package +{ + char *name; + char *version; + char *group; + char *description; + + char *tarball; + + enum _procedure procedure; /* install procedure */ + enum _priority priority; /* install user priority */ +}; + +enum _priority install_priority = OPTIONAL; /* by default allow all packages exept 'SKIP' */ + +struct dlist *requires = NULL; /* list of pkg structs */ +struct dlist *packages = NULL; /* list of package structs */ + +static void free_requires( void ); +static void free_packages( void ); + +/* + End of package structures and declarations. + *********************************************/ + +/********************************************* + Return status declarations: + */ +struct pkgrc +{ + pid_t pid; + int status; + char *name; + char *version; + char *group; +}; + +struct dlist *pkgrcl = NULL; /* list of pkgrc structs */ + +static void free_pkgrcl( void ); +static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid ); +/* + End of return status declarations. + *********************************************/ + + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( srcdir ) { free( srcdir ); srcdir = NULL; } + if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; } + + if( requires ) free_requires(); + if( packages ) free_packages(); + if( pkgrcl ) free_pkgrcl(); + + if( curdir ) { free( curdir ); curdir = NULL; } + if( selfdir ) { free( selfdir ); selfdir = NULL; } +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Create Packages List in the installation order from set of PKGLOGs\n" ); + fprintf( stdout, "or set of PACKAGEs placed in the source directory. If the source\n" ); + fprintf( stdout, "directory is not defined then directory of will be used\n" ); + fprintf( stdout, "as source PACKAGE directory.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + + fprintf( stdout, " -c,--check-requires Check the list of requires before install.\n" ); + fprintf( stdout, " -g,--gpg-verify Verify GPG2 signature. The signature must be\n" ); + fprintf( stdout, " saved in a file whose name is the same as the\n" ); + fprintf( stdout, " package file name, but with the extension '.asc'\n" ); + fprintf( stdout, " and located in the same directory as the package.\n" ); +#if defined( HAVE_DIALOG ) + fprintf( stdout, " -i,--info-dialog Show package description during install\n" ); + fprintf( stdout, " process using ncurses dialog.\n" ); + fprintf( stdout, " -m,--menu-dialog Ask for confirmation the inatallation.\n" ); +#endif + fprintf( stdout, " --parallel Parallel installation (dangerous; required the\n" ); + fprintf( stdout, " checking of DB integrity after installation).\n" ); + fprintf( stdout, " --errlist Print the list of not installed packages to the\n" ); + fprintf( stdout, " stderr in following format:\n" ); + fprintf( stdout, " group/name:version:status\n" ); + fprintf( stdout, " This option is applicable only for parallel\n" ); + fprintf( stdout, " installations.\n" ); + fprintf( stdout, " --progress Show progress bar instead of packages information.\n" ); + + fprintf( stdout, " -p,--priority=\n" ); + fprintf( stdout, " Рackage priorities allowed for installation:\n" ); + fprintf( stdout, " - optional | all ) install all packages;\n" ); + fprintf( stdout, " - recommended ) install required and recommended;\n" ); + fprintf( stdout, " - required ) install only required packages.\n" ); + + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + + fprintf( stdout, " -s,--source= Packages source directory.\n" ); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " Input PKGLIST file name or a source\n" ); + fprintf( stdout, " directory to find default PKGLIST.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "If sourse packages directory is defined by option -s,--source then\n" ); + fprintf( stdout, "specified argumet will be considered relative to the\n" ); + fprintf( stdout, "source directory.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigchld( int signum ) +{ + pid_t pid = 0; + int status; + + (void)signum; + + while( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) + { + struct pkgrc *pkgrc = find_pkgrc( pkgrcl, pid ); + + ++__terminated; /* One of children with 'pid' is terminated */ + + if( WIFEXITED( status ) ) + { + if( (int)WEXITSTATUS( status ) > 0 ) + { + ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status); + } + else + { + ++__successful; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status); + } + } + else if( WIFSIGNALED( status ) ) + { + ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */ + if( pkgrc ) pkgrc->status = 253; + } + else + { + ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */ + if( pkgrc ) pkgrc->status = 254; + } + + } + + if( pid == -1 && errno == ECHILD ) + { + /* No child processes: */ + __done = 1; + } + return; +} + + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigchld; /* CHLD */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + sa.sa_mask = set; + sigaction( SIGCHLD, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); +} + + +static enum _input_type check_package_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ +#if defined( HAVE_DIALOG ) + const char* short_options = "hvcgimp:r:s:"; +#else + const char* short_options = "hvcgp:r:s:"; +#endif + +#define PROGRESS 812 +#define PARALLEL 872 +#define _ERRLIST 873 + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "check-requires", no_argument, NULL, 'c' }, + { "gpg-verify", no_argument, NULL, 'g' }, +#if defined( HAVE_DIALOG ) + { "info-dialog", no_argument, NULL, 'i' }, + { "menu-dialog", no_argument, NULL, 'm' }, +#endif + { "parallel", no_argument, NULL, PARALLEL }, + { "errlist", no_argument, NULL, _ERRLIST }, + { "progress", no_argument, NULL, PROGRESS }, + { "priority", required_argument, NULL, 'p' }, + { "root", required_argument, NULL, 'r' }, + { "source", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'c': + { + rqck = 1; + break; + } + case 'g': + { + gpgck = 1; + break; + } + +#if defined( HAVE_DIALOG ) + case 'i': + { + install_mode = INFODIALOG; + break; + } + case 'm': + { + install_mode = MENUDIALOG; + break; + } +#endif + + case PARALLEL: + { + parallel = 1; + break; + } + case _ERRLIST: + { + error_pkgs_list = 1; + break; + } + case PROGRESS: + { + progress = 1; + break; + } + + case 'p': + { + if( optarg != NULL ) + { + char *match = NULL; + + if( strlen( (const char *)optarg ) > 2 ) + { + to_lowercase( optarg ); + if( (match = strstr( optarg, "req" )) && match == optarg ) { + install_priority = REQUIRED; + } + else if( (match = strstr( optarg, "rec" )) && match == optarg ) { + install_priority = RECOMMENDED; + } + else if( (match = strstr( optarg, "opt" )) && match == optarg ) { + install_priority = OPTIONAL; + } + else if( (match = strstr( optarg, "all" )) && match == optarg ) { + install_priority = OPTIONAL; + } + else { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + char cwd[PATH_MAX]; + + bzero( (void *)cwd, PATH_MAX ); + if( optarg[0] != '/' && curdir ) + { + /* skip current directory definition './' at start of argument: */ + if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) ) + (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 ); + else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) ) + (void)sprintf( cwd, "%s", curdir ); + else + (void)sprintf( cwd, "%s/%s", curdir, optarg ); + root = xstrdup( (const char *)cwd ); + } + else + { + root = xstrdup( (const char *)optarg ); + } + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 's': + { + if( optarg != NULL ) + { + char cwd[PATH_MAX]; + + bzero( (void *)cwd, PATH_MAX ); + if( optarg[0] != '/' && curdir ) + { + (void)sprintf( cwd, "%s/%s", curdir, optarg ); + srcdir = xstrdup( (const char *)cwd ); + } + else + { + srcdir = xstrdup( (const char *)optarg ); + } + remove_trailing_slash( srcdir ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + /* last command line argument is the intput PKGLIST file */ + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)argv[optind++] ); + remove_trailing_slash( (char *)&buf[0] ); + + if( srcdir) + { + char *tmp = xstrdup( (const char *)&buf[0] ); + + /* Ignore already defined srcdir if absolute path is specified: */ + if( buf[0] != '/' ) + { + if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) ) + (void)sprintf( buf, "%s/%s", srcdir, tmp + 2 ); + else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) ) + (void)sprintf( buf, "%s", srcdir ); + else + (void)sprintf( buf, "%s/%s", srcdir, tmp ); + } + free( tmp ); + + free( srcdir ); srcdir = xstrdup( (const char *)buf ); + } + else + { + char *tmp = xstrdup( (const char *)&buf[0] ); + + if( buf[0] != '/' && curdir ) + { + if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) ) + (void)sprintf( buf, "%s/%s", curdir, tmp + 2 ); + else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) ) + (void)sprintf( buf, "%s", curdir ); + else + (void)sprintf( buf, "%s/%s", curdir, tmp ); + } + free( tmp ); + + srcdir = xstrdup( (const char *)buf ); + } + + stat( (const char *)&buf[0], &st ); /* Do not check return status */ + + if( S_ISDIR(st.st_mode) ) + { + /********************************************************** + Add default '.pkglist' file name to the source dir name: + */ + (void)sprintf( buf, "%s/.pkglist", srcdir ); + pkglist_fname = xstrdup( (const char *)buf ); + } + else + { + if( S_ISREG(st.st_mode) ) + { + pkglist_fname = xstrdup( (const char *)buf ); + free( srcdir ); srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) ); + } + else + { + FATAL_ERROR( "Specified '%s' PKGLIST is not a regular file", basename( (char *)&buf[0] ) ); + } + } + + free( buf ); + } + else + { + usage(); + } + + /********************************************* + root is always have the trailing slash '/': + */ + { + struct stat st; + char *buf = NULL; + int len; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = xstrdup( (const char *)buf ); + } + else + { + len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + free( root ); root = xstrdup( (const char *)buf ); + } + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + if( !S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + free( buf ); + } + /* + End of set root path routine. + *********************************************/ +} + + + + +/********************************************* + Package functions: + */ +static char *strprio( enum _priority priority, int short_name ) +{ + char *p = NULL; + + switch( priority ) + { + case REQUIRED: + p = ( short_name ) ? "REQ" : "REQUIRED"; + break; + case RECOMMENDED: + p = ( short_name ) ? "REC" : "RECOMMENDED"; + break; + case OPTIONAL: + p = ( short_name ) ? "OPT" : "OPTIONAL"; + break; + case SKIP: + p = ( short_name ) ? "SKP" : "SKIP"; + break; + } + return p; +} + +static char *strproc( enum _procedure procedure ) +{ + char *p = NULL; + + switch( procedure ) + { + case INSTALL: + p = "install"; + break; + case UPDATE: + p = "update"; + break; + } + return p; +} + + +static struct pkg *pkg_alloc( void ) +{ + struct pkg *pkg = NULL; + + pkg = (struct pkg *)malloc( sizeof( struct pkg ) ); + if( !pkg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)pkg, sizeof( struct pkg ) ); + + return pkg; +} + +static void pkg_free( struct pkg *pkg ) +{ + if( pkg ) + { + if( pkg->name ) { free( pkg->name ); pkg->name = NULL; } + if( pkg->version ) { free( pkg->version ); pkg->version = NULL; } + if( pkg->group ) { free( pkg->group ); pkg->group = NULL; } + + free( pkg ); + } +} + +static void __pkg_free_func( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + if( pkg ) { pkg_free( pkg ); } +} + +static void free_requires( void ) +{ + if( requires ) { dlist_free( requires, __pkg_free_func ); requires = NULL; } +} + +static void add_required( struct pkg *pkg ) +{ + requires = dlist_append( requires, (void *)pkg ); +} + +///////////////////// only if we deside to print requires list +//static void _print_requires( void *data, void *user_data ) +//{ +// struct pkg *pkg = (struct pkg *)data; +// +// if( pkg ) +// { +// if( pkg->group ) +// fprintf( stderr, "%s/%s:%s\n", pkg->group, pkg->name, pkg->version ); +// else +// fprintf( stderr, "%s:%s\n", pkg->name, pkg->version ); +// } +//} +// +//static void print_requires( void ) +//{ +// dlist_foreach( requires, _print_requires, NULL ); +//} +///////////////////// + +static struct package *package_alloc( void ) +{ + struct package *package = NULL; + + package = (struct package *)malloc( sizeof( struct package ) ); + if( !package ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)package, sizeof( struct package ) ); + + return package; +} + +static void package_free( struct package *package ) +{ + if( package ) + { + if( package->name ) { free( package->name ); package->name = NULL; } + if( package->version ) { free( package->version ); package->version = NULL; } + if( package->group ) { free( package->group ); package->group = NULL; } + + if( package->description ) { free( package->description ); package->description = NULL; } + if( package->tarball ) { free( package->tarball ); package->tarball = NULL; } + + free( package ); + } +} + +static void __package_free_func( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + if( package ) { package_free( package ); } +} + +static void free_packages( void ) +{ + if( packages ) { dlist_free( packages, __package_free_func ); packages = NULL; } +} + +static void add_package( struct package *package ) +{ + packages = dlist_append( packages, (void *)package ); +} + +////////////////////////////// just for testing +//static void _print_packages( void *data, void *user_data ) +//{ +// struct package *package = (struct package *)data; +// +// if( package ) +// { +// if( package->group ) +// fprintf( stderr, "%s/%s:%s:%s:%s:%s:%s\n", package->group, package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ), +// package->description, package->tarball ); +// else +// fprintf( stderr, "%s:%s:%s:%s:%s:%s\n", package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ), +// package->description, package->tarball ); +// } +//} +// +//static void print_packages( void ) +//{ +// dlist_foreach( packages, _print_packages, NULL ); +//} +////////////////////////////// + +/* + End of package functions. + *********************************************/ + + +/********************************************* + Return status functions: + */ +static struct pkgrc *pkgrc_alloc( void ) +{ + struct pkgrc *pkgrc = NULL; + + pkgrc = (struct pkgrc *)malloc( sizeof( struct pkgrc ) ); + if( !pkgrc ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)pkgrc, sizeof( struct pkgrc ) ); + + return pkgrc; +} + +static void pkgrc_free( struct pkgrc *pkgrc ) +{ + if( pkgrc ) + { + if( pkgrc->name ) { free( pkgrc->name ); pkgrc->name = NULL; } + if( pkgrc->version ) { free( pkgrc->version ); pkgrc->version = NULL; } + if( pkgrc->group ) { free( pkgrc->group ); pkgrc->group = NULL; } + + free( pkgrc ); + } +} + +static void __pkgrc_free_func( void *data, void *user_data ) +{ + struct pkgrc *pkgrc = (struct pkgrc *)data; + if( pkgrc ) { pkgrc_free( pkgrc ); } +} + +static void free_pkgrcl( void ) +{ + if( pkgrcl ) { dlist_free( pkgrcl, __pkgrc_free_func ); pkgrcl = NULL; } +} + +static void add_pkgrc( struct pkgrc *pkgrc ) +{ + pkgrcl = dlist_append( pkgrcl, (void *)pkgrc ); +} + +static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid ) +{ + if( !list ) return NULL; + + while( list && list->data ) + { + if( ((struct pkgrc *)list->data)->pid == pid ) { return (struct pkgrc *)list->data; } + list = dlist_next( list ); + } + + return NULL; +} + +static void __remove_success_pkgrc( void *data, void *user_data ) +{ + struct pkgrc *pkgrc = (struct pkgrc *)data; + + if( pkgrc && pkgrc->status == 0 ) + { + pkgrcl = dlist_remove( pkgrcl, (const void *)data ); + pkgrc_free( pkgrc ); + } +} + +static void cleanup_pkgrcl( void ) +{ + dlist_foreach( pkgrcl, __remove_success_pkgrc, NULL ); +} + +static void _print_pkgrcl( void *data, void *user_data ) +{ + struct pkgrc *pkgrc = (struct pkgrc *)data; + + if( pkgrc ) + { + if( pkgrc->group ) + fprintf( stderr, "%s/%s:%s:%d\n", pkgrc->group, pkgrc->name, pkgrc->version, pkgrc->status ); + else + fprintf( stderr, "%s:%s:%d\n", pkgrc->name, pkgrc->version, pkgrc->status ); + } +} + +static void return_codes_list( void ) +{ + if( pkgrcl ) + { + dlist_foreach( pkgrcl, _print_pkgrcl, NULL ); + } +} +/* + End of return status functions. + *********************************************/ + + +/******************************* + remove spaces at end of line: + */ +//static void skip_eol_spaces( char *s ) +//{ +// char *p = (char *)0; +// +// if( !s || *s == '\0' ) return; +// +// p = s + strlen( s ) - 1; +// while( isspace( *p ) ) { *p-- = '\0'; } +//} +// +//static char *skip_lead_spaces( char *s ) +//{ +// char *p = (char *)0; +// +// if( !s || *s == '\0' ) return p; +// +// p = s; while( isspace( *p ) ) { ++p; } +// +// return( p ); +//} + +static char *trim( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } + p = s; while( isspace( *p ) ) { ++p; } + + return( p ); +} + + +static void read_pkglist_file( const char *fname ) +{ + char *ln = NULL; + char *line = NULL; + int lnum = 0; + + FILE *fp = NULL; + + if( !fname || (*fname == '\0') ) return; + + fp = fopen( fname, "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open '%s' PKGLIST file", basename( (char *)fname ) ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *p = NULL; + char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL; + + ++lnum; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + ln = trim( ln ); /* remove leading and trailing spaces */ + + if( *ln == '#' ) + { + if( !strncmp( ln, "# required:", 11 ) ) + { + char *n = NULL, *v = NULL, *g = NULL, *q = NULL; + char *rq = ln + 11; + rq = trim( rq ); + + n = rq; + if( (q = index( (const char *)n, '/' )) ) { *q = '\0'; g = n; n = ++q; g = trim( g ); } + if( (q = index( (const char *)n, '=' )) ) { *q = '\0'; v = ++q; n = trim( n ); } + v = trim( v ); + + if( n && v ) + { + struct pkg *pkg = pkg_alloc(); + + pkg->name = xstrdup( (const char *)n ); + pkg->version = xstrdup( (const char *)v ); + if( g ) + pkg->group = xstrdup( (const char *)g ); + + add_required( pkg ); + } + } + continue; /* skip commented lines */ + } + + name = ln; + if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue; + if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue; + if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue; + if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue; + if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue; + prio = trim( prio ); + + if( name && vers && desc && ball && proc && prio ) + { + char *buf = NULL; + struct package *package = NULL; + char *group = index( (const char *)ball, '/' ); + enum _priority priority = OPTIONAL; + + /******************* + Package priority: + */ + if( strlen( (const char*)prio ) > 2 ) + { + char *match = NULL; + + to_lowercase( prio ); + if( (match = strstr( prio, "req" )) && match == prio ) { + priority = REQUIRED; + } + else if( (match = strstr( prio, "rec" )) && match == prio ) { + priority = RECOMMENDED; + } + else if( (match = strstr( prio, "opt" )) && match == prio ) { + priority = OPTIONAL; + } + else if( (match = strstr( prio, "sk" )) && match == prio ) { + priority = SKIP; + } + else { + FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio ); + } + } + else + { + FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio ); + } + + if( priority > install_priority ) continue; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + package = package_alloc(); + + package->name = xstrdup( (const char *)name ); + package->version = xstrdup( (const char *)vers ); + package->description = xstrdup( (const char *)desc ); + + (void)sprintf( buf, "%s/%s", (const char *)srcdir, (const char *)ball ); + { + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_package_file( &uncompress, (const char *)&buf[0] ); + if( type != IFMT_PKG ) + { + FATAL_ERROR( "Unknown format of '%s' package file", (const char *)&buf[0] ); + } + } + package->tarball = xstrdup( (const char *)&buf[0] ); + + free( buf ); + + package->priority = priority; + + /******************** + Install procedure: + */ + if( strlen( (const char*)proc ) > 5 ) + { + char *match = NULL; + + to_lowercase( proc ); + if( (match = strstr( proc, "install" )) && match == proc ) { + package->procedure = INSTALL; + } + else if( (match = strstr( proc, "update" )) && match == proc ) { + package->procedure = UPDATE; + } + else { + FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc ); + } + } + else + { + FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc ); + } + + if( group ) + { + *group = '\0'; + group = ball; + package->group = xstrdup( (const char *)group ); + } + + add_package( package ); + } + + } /* End of while( ln = fgets( line ) ) */ + + free( line ); + fclose( fp ); +} + + +static void check_requires( void ) +{ + if( requires ) + { + exit_status = 1; + + fprintf( stdout, "\nThe input '%s' has the list of %d required packages.\n\n", basename( pkglist_fname ), dlist_length( requires ) ); + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +#if defined( HAVE_DIALOG ) +static DIALOG_LISTITEM *alloc_select_items( void ) +{ + DIALOG_LISTITEM *items = NULL; + int i = 0, num = dlist_length( packages ); + struct dlist *list = packages, *next = NULL; + + items = (DIALOG_LISTITEM *)malloc( (num + 1) * sizeof(DIALOG_LISTITEM) ); + if( !items ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)items, (num + 1) * sizeof(DIALOG_LISTITEM) ); + + while( list ) + { + struct package *package = NULL; + + next = dlist_next( list ); + package = (struct package *)list->data; + if( package ) + { +#define COLUMN_LENGHT 23 /* 22 symbols for name + " [UP] " */ +#define NAME_LENGHT (COLUMN_LENGHT - 7) /* strlen(" [UP] ") + 1; */ +#define UPDATE_SUFFIX " [UP] " +#define INSTALL_SUFFIX " [in] " + + char *name = (char *)malloc( (size_t)COLUMN_LENGHT ); + if( !name ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)name, (size_t)COLUMN_LENGHT ); + + if( strlen( package->name ) > (size_t)NAME_LENGHT ) + { + (void)strncpy( name, (const char *)package->name, NAME_LENGHT - 2 ); + (void)strcat( name, ".." ); + } + else + { + (void)strcpy( name, (const char *)package->name ); + } + + if( package->procedure == UPDATE ) + { + int p = strlen( name ); + while( p < NAME_LENGHT ) { name[p] = ' '; ++p; } + name[p] = '\0'; + (void)strcat( name, UPDATE_SUFFIX ); + } + else + { + int p = strlen( name ); + /* while( p < (COLUMN_LENGHT - 1) ) { name[p] = ' '; ++p; } */ + while( p < NAME_LENGHT ) { name[p] = ' '; ++p; } + name[p] = '\0'; + (void)strcat( name, INSTALL_SUFFIX ); + } + + items[i].name = name; + items[i].text = xstrdup( (const char *)package->description ); + if( package->group ) + items[i].help = xstrdup( (const char *)package->group ); + items[i].state = 1; + } + ++i; + list = next; + } + return items; +} + +static void free_select_items( DIALOG_LISTITEM *items ) +{ + DIALOG_LISTITEM *p = items; + + if( !p ) return; + + while( p->name ) { free( p->name ); free( p->text ); if( p->help ) free( p->help ); ++p; } + + free( items ); +} + +static void remove_unselected_packages( DIALOG_LISTITEM *items ) +{ + DIALOG_LISTITEM *p = items; + struct dlist *rem = NULL, *list = NULL, *next = NULL; + int n = 0; + + if( !p ) return; + + while( p->name ) + { + /* copy packages list item to the local list: */ + if( !p->state ) { list = dlist_append( list, dlist_nth_data( packages, n ) ); } + ++p; ++n; + } + + /**************************************************** + remove items of the local list from packages list: + */ + rem = list; + while( rem ) + { + next = dlist_next( rem ); + packages = dlist_remove( packages, rem->data ); + rem = next; + } + + /*************************** + free unselected packages: + */ + dlist_free( list, __package_free_func ); +} +#endif + + +/********************************************* + Progress functions: + */ +static void show_install_con_progress( const char *title, int percent ) +{ +#define GAUGE_LENGTH 68 + size_t prefix = strlen( title ) + 2; /* title + ' [' */ + size_t suffix = 6; /* '] 100%' */ + size_t length = prefix + GAUGE_LENGTH + suffix; + int i, ctx = GAUGE_LENGTH * percent / 100; + + if( percent < 0 ) percent = 0; + if( percent > 100 ) percent = 100; + + printf( "\033[1A" ); /* move the cursor up 1 line */ + printf( "\033[%dD", (int)length ); /* move cursor to start of line */ + printf( "%s [", title ); + + for( i = 0; i < ctx; ++i ) { fprintf( stdout, "\u2588" ); } + for( ; i < GAUGE_LENGTH; ++i ) { fprintf( stdout, " " ); } + + printf( "] %3d%%\n", percent ); + fflush( stdout ); +} + +static void show_progress( void ) +{ + const char *title = "Install:"; + const char *message = "\n" + "Please wait for install all specified packages:\n" + "\n\n"; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + show_install_dlg_progress( 0 ); +#else + fprintf( stdout, "%s", message ); + show_install_con_progress( title, 0 ); +#endif + } + else + { + fprintf( stdout, "%s", message ); + show_install_con_progress( title, 0 ); + } + +} + +static void update_progress( int percent ) +{ + const char *title = "Install:"; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + show_install_dlg_progress( percent ); +#else + show_install_con_progress( title, percent ); +#endif + } + else + { + show_install_con_progress( title, percent ); + } +} + +static void stop_progress( void ) +{ + const char *title = "Install:"; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + show_install_dlg_progress( 100 ); +#else + show_install_con_progress( title, 100 ); + fprintf( stdout, "\n" ); +#endif + } + else + { + show_install_con_progress( title, 100 ); + fprintf( stdout, "\n" ); + } +} +/* + End of progress functions. + *********************************************/ + + + +/********************************************* + Install functions. + */ +static void install_package( struct package *package ) +{ + int len = 0; + char *cmd = NULL, *opt = NULL, *out = "> /dev/null 2>&1"; + + if( !package ) return; + + opt = (char *)malloc( (size_t)PATH_MAX ); + if( !opt ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)opt, PATH_MAX ); + opt[0] = ' '; + + if( gpgck ) (void)sprintf( opt, "--gpg-verify " ); + if( (install_mode != CONSOLE) && !parallel && !progress ) (void)strcat( opt, "--info-dialog " ); + if( parallel ) (void)strcat( opt, "--ignore-chrefs-errors " ); + if( (install_mode == CONSOLE) && !parallel && !progress ) out = " "; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/%s-package %s --priority=%s --root=%s %s %s", + selfdir, strproc( package->procedure ), opt, + strprio( package->priority, 1 ), + root, package->tarball, out ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot install %s-%s package", package->name, package->version ); + } + if( parallel ) + { + struct pkgrc *pkgrc = pkgrc_alloc(); + + pkgrc->name = xstrdup( (const char *)package->name ); + pkgrc->version = xstrdup( (const char *)package->version ); + if( package->group ) + pkgrc->group = xstrdup( (const char *)package->group ); + pkgrc->pid = sys_exec_command( cmd ); + + add_pkgrc( pkgrc ); + ++__child; + } + else + { + pid_t p = (pid_t) -1; + int rc = 0; + + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 && rc != 31 ) + { + FATAL_ERROR( "Cannot install '%s-%s' package", package->name, package->version ); + } + ++__successful; + } + + free( cmd ); + free( opt ); +} + +static void _serial_install_package( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + int percent; + + if( package ) { install_package( package ); } + + if( progress ) + { + percent = ( __successful < __all ) ? __successful * 100 / __all : 100; + update_progress( percent ); + } +} + +static void serial_install_packages( void ) +{ + if( progress ) + { + show_progress(); + } + + dlist_foreach( packages, _serial_install_package, NULL ); + + if( progress ) + { + stop_progress(); + } +} + + +static void *install_process( void *args ) +{ + struct dlist *list = packages, *next = NULL; + + int nstreams = ncpus * 2; /* two concurents for CPU */ + + while( list ) + { + struct package *package = NULL; + + next = dlist_next( list ); + package = (struct package *)list->data; + if( package ) + { + install_package( package ); + } + list = next; + + /* wait for available CPU: */ + while( (__child - __terminated) > nstreams ) usleep( WAIT_USEC_FOR_CHILD ); + } + + return NULL; +} + +static void parallel_install_packages( void ) +{ + pthread_t install_process_id; + int status; + + /* Start the parallel installation process: */ + status = pthread_create( &install_process_id, NULL, install_process, NULL ); + if( status != 0 ) + { + FATAL_ERROR( "Cannot start parallel installation process" ); + } + (void)pthread_detach( install_process_id ); +} + +/* + End of install functions. + *********************************************/ + + + + +static void dialogrc( void ) +{ + struct stat st; + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* imagine that the utility is in /sbin directory: */ + (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + if( stat( (const char *)&tmp[0], &st ) == -1 ) + { + /* finaly assume that /usr/sbin is a sbindir: */ + (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + } + + setenv( "DIALOGRC", (const char *)&tmp[0], 1 ); + + free( tmp ); +} + +static char *get_curdir( void ) +{ + char *cwd = NULL; + + cwd = (char *)malloc( PATH_MAX ); + if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cwd, PATH_MAX ); + + if( getcwd( cwd, (size_t)PATH_MAX ) != NULL ) + { + char *p = NULL; + remove_trailing_slash( cwd ); + p = xstrdup( (const char *)cwd ); + free( cwd ); + return p; + } + else + { + FATAL_ERROR( "Cannot get absolute path to current directory" ); + } + + return (char *)NULL; +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + ncpus = get_nprocs(); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + curdir = get_curdir(); + dialogrc(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + /*********************************************************** + Fill requires and packages lists, check package tarballs, + skip unnesessary packages according to --priority option: + */ + read_pkglist_file( (const char *)pkglist_fname ); + + /* check only the list of requires in the input PKGLIST: */ + if( rqck ) check_requires(); + +#if defined( HAVE_DIALOG ) + if( install_mode == MENUDIALOG ) + { + int status = 0, num = dlist_length( packages ); + DIALOG_LISTITEM *items = alloc_select_items(); + + status = select_packages_box( items, num, 0, 0 ); + if( !status ) + { + remove_unselected_packages( items ); + free_select_items( items ); + } + else + { + /* Abort installation: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + } +#endif + + + if( parallel ) + { + /************************ + parallel installation: + */ + int percent = 0; + + __all = dlist_length( packages ); + __done = 0; __child = 0; + + __terminated = 0; __successful = 0; + + show_progress(); + + parallel_install_packages(); + + if( __terminated < __all ) + { + while( !__done ) + { + percent = ( __terminated < __all ) ? __terminated * 100 / __all : 100; + + update_progress( percent ); + usleep( WAIT_USEC_FOR_CHILD ); + } + } + + __done = 0; __child = 0; + + stop_progress(); + + if( __successful < __terminated ) { percent = __successful * 100 / __terminated; } + else { percent = 100; } + + __terminated = 0; __successful = 0; + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + char *msg = NULL; + + msg = (char *)malloc( (size_t)80 ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, 80 ); + + (void)sprintf( msg, "\nSuccessfully installed %d%% of %d specified packages.\n", percent, __all ); + + (void)info_box( " \\Z0INSTALL PACKAGES\\Zn ", msg, 5, 0, 0 ); + + free( msg ); +#else + fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all ); +#endif + } + else + { + fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all ); + } + + cleanup_pkgrcl(); /* remove successfully installed packages from return codes list */ + if( pkgrcl && error_pkgs_list ) + { + return_codes_list(); + } + } + else + { + /********************** + serial installation: + */ + + __successful = 0; + __all = dlist_length( packages ); + + serial_install_packages(); + + if( install_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_box( " \\Z4INSTALL PACKAGES\\Zn ", + "\nAll of specified packages have been installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nAll of specified packages have been installed.\n\n" ); +#endif + } + else + { + fprintf( stdout, "\nAll of specified packages have been installed.\n\n" ); + } + + } + + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/jsmin.c b/src/jsmin.c new file mode 100644 index 0000000..f286e7b --- /dev/null +++ b/src/jsmin.c @@ -0,0 +1,358 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include /* basename(3) */ + +#include + +#include + +static FILE *ifile; +static FILE *ofile; + +static const char *input_fname = NULL; + +static void error( const char *fname, char *s ) +{ + if( fname ) + ERROR( "JSMIN: %s: %s", basename( (char *)fname ), s ); + else + ERROR( "JSMIN: %s", s ); +} + +static int is_alpha_or_num( int c ) +{ + return( (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c > 126 ); +} + +static int a; +static int b; +static int lookahead = EOF; +static int x = EOF; +static int y = EOF; + +/* + get - return the next character from stdin. Watch out for lookahead. If + the character is a control character, translate it to a space or + linefeed. + */ +static int get() +{ + int c = lookahead; + lookahead = EOF; + if( c == EOF ) + { + c = getc( ifile ); + } + if( c >= ' ' || c == '\n' || c == EOF ) + { + return c; + } + if( c == '\r' ) + { + return '\n'; + } + return ' '; +} + + +/* + peek - get the next character without getting it. + */ +static int peek() +{ + lookahead = get(); + return lookahead; +} + + +/* + next - get the next character, excluding comments. peek() is used to see + if a '/' is followed by a '/' or '*'. + */ +static int next() +{ + int c = get(); + if ( c == '/' ) + { + switch( peek() ) + { + case '/': + for( ;; ) + { + c = get(); + if( c <= '\n' ) + { + break; + } + } + break; + case '*': + get(); + while( c != ' ' ) + { + switch( get() ) + { + case '*': + if( peek() == '/' ) + { + get(); + c = ' '; + } + break; + case EOF: + error( input_fname, "Unterminated comment" ); + return EOF; + } + } + break; + } + } + y = x; + x = c; + return c; +} + + +/* + action - do something! What you do is determined by the argument: + 1 Output A. Copy B to A. Get the next B. + 2 Copy B to A. Get the next B. (Delete A). + 3 Get the next B. (Delete B). + action treats a string as a single character. Wow! + action recognizes a regular expression if it is preceded by ( or , or =. + */ +static void action( int d ) +{ + switch( d ) + { + case 1: + /* skip first carriage return */ + if( a != '\n' ) putc( a, ofile ); + if( (y == '\n' || y == ' ') && + (a == '+' || a == '-' || a == '*' || a == '/') && + (b == '+' || b == '-' || b == '*' || b == '/') ) + { + putc( y, ofile ); + } + case 2: + a = b; + if( a == '\'' || a == '"' || a == '`' ) + { + for( ;; ) + { + putc( a, ofile ); + a = get(); + if( a == b ) + { + break; + } + if( a == '\\' ) + { + putc( a, ofile ); + a = get(); + } + if( a == EOF ) + { + error( input_fname, "Unterminated string literal" ); + return; + } + } + } + case 3: + b = next(); + if( b == '/' && + ( a == '(' || a == ',' || a == '=' || a == ':' || + a == '[' || a == '!' || a == '&' || a == '|' || + a == '?' || a == '+' || a == '-' || a == '~' || + a == '*' || a == '/' || a == '{' || a == '\n' ) ) + { + putc( a, ofile ); + if( a == '/' || a == '*' ) + { + putc( ' ', ofile ); + } + putc( b, ofile ); + for( ;; ) + { + a = get(); + if( a == '[' ) + { + for( ;; ) + { + putc( a, ofile ); + a = get(); + if( a == ']' ) + { + break; + } + if( a == '\\' ) + { + putc( a, ofile ); + a = get(); + } + if( a == EOF ) + { + error( input_fname, "Unterminated set in Regular Expression literal" ); + return; + } + } + } + else if( a == '/' ) + { + switch( peek() ) + { + case '/': + case '*': + error( input_fname, "Unterminated set in Regular Expression literal" ); + return; + } + break; + } + else if( a =='\\' ) + { + putc( a, ofile ); + a = get(); + } + if( a == EOF ) + { + error( input_fname, "Unterminated Regular Expression literal" ); + return; + } + putc( a, ofile ); + } + b = next(); + } + } +} + + +/* + jsmin - Copy the input to the output, deleting the characters which are + insignificant to JavaScript. Comments will be removed. Tabs will be + replaced with spaces. Carriage returns will be replaced with linefeeds. + Most spaces and linefeeds will be removed. +*/ +static void jsmin() +{ + if( peek() == 0xEF ) { get(); get(); get(); } + a = '\n'; + action( 3 ); + + while( a != EOF ) + { + switch( a ) + { + case ' ': + action(is_alpha_or_num(b) ? 1 : 2); + break; + case '\n': + switch( b ) + { + case '{': case '[': case '(': + case '+': case '-': case '!': + case '~': + action( 1 ); + break; + case ' ': + action( 3 ); + break; + default: + action( is_alpha_or_num(b) ? 1 : 2 ); + } + break; + default: + switch( b ) + { + case ' ': + action( is_alpha_or_num(a) ? 1 : 3 ); + break; + case '\n': + switch( a ) + { + case '}': case ']': case ')': + case '+': case '-': case '"': + case '\'': case '`': + action( 1 ); + break; + default: + action( is_alpha_or_num(a) ? 1 : 3 ); + } + break; + default: + action( 1 ); + break; + } + } + } + /* lats carriage return */ + putc( '\n', ofile ); +} + + +int minimize_json( const char *ifname, const char *ofname ) +{ + int status, ret = -1; + + if( !ifname || !ofname ) return ret; + + status = exit_status; exit_status = 0; + + input_fname = ifname; + + ret = 0; + + ifile = fopen( ifname, "r" ); + if( ifile == NULL ) + { + ERROR( "JSMIN: Can't open '%s' file", ifname ); + exit_status = status + exit_status; + return ret; + } + + ofile = fopen( ofname, "w+" ); + if( ofile == NULL ) + { + ERROR( "JSMIN: Can't open '%s' file", ofname ); + exit_status = status + exit_status; + return ret; + } + + jsmin(); + + fclose( ifile ); ifile = NULL; + fflush( ofile ); fclose( ofile ); ofile = NULL; + + if( exit_status == 0 ) + { + ret = 1; + exit_status = status; + } + else + { + exit_status = status + exit_status; + } + + return ret; +} diff --git a/src/jsmin.h b/src/jsmin.h new file mode 100644 index 0000000..b0a34df --- /dev/null +++ b/src/jsmin.h @@ -0,0 +1,34 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _MINIMIZE_JSON_H_ +#define _MINIMIZE_JSON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int minimize_json( const char *ifname, const char *ofname ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _MINIMIZE_JSON_H_ */ diff --git a/src/make-package.c b/src/make-package.c new file mode 100644 index 0000000..e68e0e2 --- /dev/null +++ b/src/make-package.c @@ -0,0 +1,1983 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include + +#define PROGRAM_NAME "make-package" + +#include + + +char *program = PROGRAM_NAME; +char *destination = NULL, *srcdir = NULL, *flavour = NULL, + *tmpdir = NULL; + +FILE *rlinks = NULL; +size_t pkgsize = 0; +int nfiles = 0; + +const char *txz_suffix = ".txz"; +char compress = 'J'; + +#if defined( HAVE_GPG2 ) +char *gnupghome = NULL, *passphrase = NULL, *key_id = NULL; +#endif + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int mkgroupdir = 0; +int linkadd = 1; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *short_description = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *total_files = NULL; + +struct dlist *filelist = NULL; + +static void create_file_list( void ); +static void free_file_list( void ); + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( short_description ) { free( short_description ); } short_description = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL + +void free_resources() +{ + if( srcdir ) { free( srcdir ); srcdir = NULL; } + if( destination ) { free( destination ); destination = NULL; } + if( flavour ) { free( flavour ); flavour = NULL; } + if( filelist ) { free_file_list(); filelist = NULL; } + +#if defined( HAVE_GPG2 ) + if( gnupghome ) { free( gnupghome ); gnupghome = NULL; } + if( passphrase ) { free( passphrase ); passphrase = NULL; } + if( key_id ) { free( key_id ); key_id = NULL; } +#endif + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Create PACKAGE from SRCPKGDIR where package is installed. The source\n" ); + fprintf( stdout, "directory should content the package service files:\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, " .PKGINFO, .INSTALL, .DESCRIPTION, and .REQUIRES (if applicable)\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "The '.PKGINFO' file is obligatory and should content declarations of\n" ); + fprintf( stdout, "following variables: pkgname, pkgver, arch, distroname, distrover.\n" ); + fprintf( stdout, "Also in the .PKGINFO file can be defined additional and recommended\n" ); + fprintf( stdout, "variables: group, short_description, url, license.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -d,--destination= Target directory to save output PACKAGE.\n" ); + fprintf( stdout, " -m,--mkgroupdir Create GROUP subdirectory in the PACKAGE\n" ); + fprintf( stdout, " target directory.\n" ); + fprintf( stdout, " -l,--linkadd={y|n} Create .RESTORELINKS scrypt (default yes).\n" ); + fprintf( stdout, " -f,--flavour= The name of additional subdirectory in the\n" ); + fprintf( stdout, " GROUP directory to save target PACKAGE.\n" ); + fprintf( stdout, "\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, "OpenPGP options:\n" ); + fprintf( stdout, " -g,--gnupghome= Set the name of the GnuPG home directory\n" ); + fprintf( stdout, " to . If this option is not used it\n" ); + fprintf( stdout, " defaults to '~/.gnupg'. This also overrides\n" ); + fprintf( stdout, " the environment variable 'GNUPGHOME'.\n" ); + fprintf( stdout, " -p,--passphrase= File with passphrase of private certificate\n" ); + fprintf( stdout, " for signing package. For example:\n" ); + fprintf( stdout, " ~/.gnupg/.passphrase\n" ); + fprintf( stdout, " Passphrase should be placed in the first\n" ); + fprintf( stdout, " line of the file (the new-line symbol at\n" ); + fprintf( stdout, " end of passphrase is allowed). File must\n" ); + fprintf( stdout, " have access mode 600.\n" ); + fprintf( stdout, " -k,--key-id= Use USER-ID to sign package, for example,\n" ); + fprintf( stdout, " --key-id=0xA5ED710298807270\n" ); + fprintf( stdout, "\n" ); +#endif + fprintf( stdout, "Compression options:\n" ); + fprintf( stdout, " -J,--xz Filter the package archive through xz(1).\n" ); + fprintf( stdout, " -j,--bzip2 Filter the package archive through bzip2(1).\n" ); + fprintf( stdout, " -z,--gzip Filter the package archive through gzip(1).\n" ); + fprintf( stdout, "\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, " If one of arguments: passphrase or key-id is not specified, then\n" ); + fprintf( stdout, " signature will not be created and utility doesn't return any error\n" ); + fprintf( stdout, " code. If error occurs during the creation of a package signature,\n" ); + fprintf( stdout, " the utility returns error code, but continue to create the package\n" ); + fprintf( stdout, " (in this case the signature will not be created).\n" ); + fprintf( stdout, "\n" ); +#endif + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " Directory wich contains source package\n" ); + fprintf( stdout, " and package service files.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + + +void get_args( int argc, char *argv[] ) +{ +#if defined( HAVE_GPG2 ) + const char* short_options = "hvmd:l:f:g:p:k:Jjz"; +#else + const char* short_options = "hvmd:l:f:Jjz"; +#endif + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "destination", required_argument, NULL, 'd' }, + { "mkgroupdir", no_argument, NULL, 'm' }, + { "linkadd", required_argument, NULL, 'l' }, + { "flavour", required_argument, NULL, 'f' }, +#if defined( HAVE_GPG2 ) + { "gnupghome", required_argument, NULL, 'g' }, + { "passphrase", required_argument, NULL, 'p' }, + { "key-id", required_argument, NULL, 'k' }, +#endif + { "xz", no_argument, NULL, 'J' }, + { "bzip2", no_argument, NULL, 'j' }, + { "gzip", no_argument, NULL, 'z' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'd': + { + if( optarg != NULL ) + { + destination = xstrdup( (const char *)optarg ); + remove_trailing_slash( destination ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'm': + { + mkgroupdir = 1; + break; + } + + case 'J': + { + compress = 'J'; + txz_suffix = ".txz"; + break; + } + case 'j': + { + compress = 'j'; + txz_suffix = ".tbz"; + break; + } + case 'z': + { + compress = 'z'; + txz_suffix = ".tgz"; + break; + } + + case 'l': + { + if( optarg != NULL ) + { + char *buf = NULL; + size_t len = strlen( optarg ) + 1; + + buf = (char *)malloc( len ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, len ); + + (void)strcpy( buf, (const char *)optarg ); + to_lowercase( buf ); + if( !strncmp( (const char *)&buf[0], "n", 1 ) ) + { + linkadd = 0; + } + free( buf ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'f': + { + if( optarg != NULL ) + { + char *buf = NULL; + size_t len = strlen( optarg ) + 1; + + buf = (char *)malloc( len ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, len ); + + (void)strcpy( buf, (const char *)optarg ); + to_lowercase( buf ); + + flavour = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + /* option is present but without value */ + usage(); + break; + } + +#if defined( HAVE_GPG2 ) + case 'g': + { + if( optarg != NULL ) + { + struct stat st; + char *buf = NULL; + char *home = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( *optarg == '~' ) + { + home = getenv( "HOME" ); + if( home ) + { + (void)sprintf( buf, "%s/%s", home, (const char *)((char *)optarg + 2) ); + } + else + { + FATAL_ERROR( "Cannot get HOME directory" ); + } + } + else + { + (void)strcpy( buf, (const char *)optarg ); + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' GnuPG home directory: %s", buf, strerror( errno ) ); + } + if( !S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "The GNUPGHOME '%s' is not a directory", buf ); + } + + remove_trailing_slash( buf ); + gnupghome = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'p': + { + if( optarg != NULL ) + { + struct stat st; + char *buf = NULL; + char *home = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( *optarg == '~' ) + { + home = getenv( "HOME" ); + if( home ) + { + (void)sprintf( buf, "%s/%s", home, (const char *)((char *)optarg + 2) ); + } + else + { + FATAL_ERROR( "Cannot get HOME directory" ); + } + } + else + { + (void)strcpy( buf, (const char *)optarg ); + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' passphrase source file: %s", basename( buf ), strerror( errno ) ); + } + if( !S_ISREG(st.st_mode) ) + { + FATAL_ERROR( "The passphrase '%s' is not a regular file", basename( buf ) ); + } + + passphrase = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'k': + { + if( optarg != NULL ) + { + key_id = xstrdup( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } +#endif + + case '?': default: + { + usage(); + break; + } + } + } + + /* last command line argument is the PACKAGE source directory */ + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)argv[optind] ); + remove_trailing_slash( (char *)&buf[0] ); + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' PACKAGE source directory: %s", basename( buf ), strerror( errno ) ); + } + + if( ! S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "The PACKAGE source '%s' is not a directory", basename( buf ) ); + } + + /* Add .PKGINFO to the input dir name: */ + (void)strcat( buf, "/.PKGINFO" ); + if( stat( (const char *)&buf[0], &st ) == -1 ) { + FATAL_ERROR( "The defined SRCPKGDIR doesn't contain a valid package" ); + } + *(strstr( buf, "/.PKGINFO" )) = '\0'; /* restore tmpdir in tmp[] buffer */ + + /* Add .DESCRIPTION to the input dir name: */ + (void)strcat( buf, "/.DESCRIPTION" ); + if( stat( (const char *)&buf[0], &st ) == -1 ) { + FATAL_ERROR( "The defined SRCPKGDIR doesn't contain package '.DESCRIPTION' file" ); + } + *(strstr( buf, "/.DESCRIPTION" )) = '\0'; /* restore tmpdir in tmp[] buffer */ + + /* Add .INSTALL to the input dir name: */ + (void)strcat( buf, "/.INSTALL" ); + if( stat( (const char *)&buf[0], &st ) == -1 ) { + FATAL_ERROR( "The defined SRCPKGDIR doesn't contain package '.INSTALL' script" ); + } + *(strstr( buf, "/.INSTALL" )) = '\0'; /* restore tmpdir in tmp[] buffer */ + + + srcdir = xstrdup( (const char *)&buf[0] ); + if( srcdir == NULL ) + { + usage(); + } + + free( buf ); + } + else + { + usage(); + } + + if( destination == NULL ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)srcdir ); + remove_trailing_slash( (char *)&buf[0] ); + + destination = xstrdup( (const char *)dirname( (char *)&buf[0] ) ); + + free( buf ); + } +} + + +/* + Especialy for pkginfo lines. + Remove leading spaces and take non-space characters only: + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + +/* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +static void read_pkginfo( void ) +{ + struct stat st; + char *buf = NULL; + FILE *pkginfo; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)srcdir ); + (void)strcat( buf, "/.PKGINFO" ); + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access .PKGINFO file: %s", strerror( errno ) ); + } + if( !S_ISREG(st.st_mode) ) + { + FATAL_ERROR( "The '%s' is not a regular file", basename( buf ) ); + } + + pkginfo = fopen( (const char *)&buf[0], "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open '%s' file", basename( buf ) ); + } + + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) arch = skip_spaces( p ); + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distroname = skip_spaces( p ); + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distrover = skip_spaces( p ); + } + + if( (match = strstr( ln, "short_description" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + char *b = index( p, '"'), + *e = rindex( p, '"'); + if( b && e && ( b != e ) ) + { + p = ++b; *e = '\0'; + short_description = xstrdup( (const char *)p ); + } + } + } + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) url = skip_spaces( p ); + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) license = skip_spaces( p ); + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + if( !url ) { url = xstrdup( DISTRO_URL ); } + if( !license ) { license = xstrdup( DISTRO_LICENSE ); } + } + + fclose( pkginfo ); + free( buf ); +} + +static void tune_destinations( void ) +{ + if( mkgroupdir && group ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)strcpy( buf, (const char *)destination ); + (void)strcat( buf, "/" ); + (void)strcat( buf, (const char *)group ); + if( flavour ) + { + (void)strcat( buf, "/" ); + (void)strcat( buf, (const char *)flavour ); + } + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot create target directory" ); + } + + free( destination ); + destination = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + + /* here we can allocate memory for output filenames */ +} + +/********************************************* + .RESTORELINKS functions: + */ +static void start_restorelinks_file( void ) +{ + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".RESTORELINKS" ); + + rlinks = fopen( (const char *)&tmp[0], "w" ); + if( !rlinks ) + { + FATAL_ERROR( "Cannot create '.RESTORELINKS' file" ); + } + free( tmp ); +} + +static void stop_restorelinks_file( void ) +{ + struct stat sb; + char *tmp = NULL, *cmd = NULL; + int len = 0; + + fflush( rlinks ); fclose( rlinks ); + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".RESTORELINKS" ); + + if( stat( tmp, &sb ) == 0 ) + { + if( S_ISREG(sb.st_mode) && sb.st_size != 0 ) + { + pid_t p = (pid_t) -1; + int rc; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", tmp, srcdir ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot create '.RESTORELINKS' file" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot create '.RESTORELINKS' file" ); + } + + free( cmd ); + } + } + + free( tmp ); +} + + +static void save_link( const char *name_in, const char *name_to, const char *name_is ) +{ + if( rlinks ) + { + fprintf( rlinks, "( cd %s ; rm -rf %s )\n", name_in, name_is ); + fprintf( rlinks, "( cd %s ; ln -sf %s %s )\n", name_in, name_to, name_is ); + } +} + +/* + End of .RESTORELINKS functions. + *********************************************/ + +/********************************************* + File list functions: + */ +static void _print_filelist_entry( void *data, void *user_data ) +{ + const char *path = (const char *)data; + FILE *output = (FILE *)user_data; + + if( !output ) output = stdout; + + fprintf( output, "%s\n", path ); +} + +static void _free_filelist_entry( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static int _compare_fnames( const void *a, const void *b ) +{ + const char *s1 = (const char *)a; + const char *s2 = (const char *)b; + + return strcmp( s1, s2 ); +} + +static void _push_file( const char *name ) +{ + char *fname = (char *)name + strlen( srcdir ) + 1; + filelist = dlist_append( filelist, (void *)xstrdup( (const char *)fname ) ); +} + +static void _push_dir( const char *name ) +{ + char *buf = NULL; + char *dname = (char *)name + strlen( srcdir ) + 1; + + buf = (char *)malloc( strlen( dname ) + 2 ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + (void)sprintf( &buf[0], "%s/", dname ); + + filelist = dlist_append( filelist, (void *)xstrdup( (const char *)&buf[0] ) ); + free( buf ); +} + +static void _list_files( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat source directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Source path is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access '%s' directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( lstat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + _push_dir( (const char *)path ); + pkgsize += (size_t)entry_sb.st_size; + _list_files( (const char *)path ); + } + else + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *service = basename( path ); + + /* skip service files: */ + if( strcmp( service, ".DESCRIPTION" ) && + strcmp( service, ".FILELIST" ) && + strcmp( service, ".INSTALL" ) && + strcmp( service, ".PKGINFO" ) && + strcmp( service, ".REQUIRES" ) && + strcmp( service, ".RESTORELINKS" ) ) + { + _push_file( (const char *)path ); + pkgsize += (size_t)entry_sb.st_size; + ++nfiles; + } + } + if( S_ISBLK(entry_sb.st_mode) || + S_ISCHR(entry_sb.st_mode) || + S_ISFIFO(entry_sb.st_mode) || + S_ISSOCK(entry_sb.st_mode) ) + { + _push_file( (const char *)path ); + ++nfiles; + } + if( S_ISLNK(entry_sb.st_mode) ) + { + if( linkadd ) + { + char *buf = NULL; + ssize_t len = 0; + + const char *in = (char *)dirpath + strlen( srcdir ) + 1; + const char *is = (char *)path + strlen( dirpath ) + 1; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( (len = readlink( (const char *)path, buf, PATH_MAX - 1 )) == -1 ) + { + FATAL_ERROR( "%s: Cannot read link: %s", is, strerror( errno ) ); + } + buf[len] = '\0'; + save_link( in, (const char *)&buf[0], is ); + free( buf ); + + /* remove the link: */ + (void)unlink( (const char *)path ); + } + else + { + _push_file( (const char *)path ); + ++nfiles; + } + } + + } /* End if( S_ISDIR(entry_sb.st_mode) ) */ + + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static void create_file_list( void ) +{ + start_restorelinks_file(); + _list_files( (const char *)srcdir ); + stop_restorelinks_file(); + + if( filelist ) + { + FILE *flist = NULL; + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".FILELIST" ); + + flist = fopen( (const char *)&tmp[0], "w" ); + if( !flist ) + { + FATAL_ERROR( "Cannot create '.FILELIST' file" ); + } + + /********************************* + save total number of files: + */ + (void)sprintf( (char *)&tmp[0], "%d", nfiles ); + total_files = xstrdup( (const char *)&tmp[0] ); + + /********************************* + save uncompressed package size: + */ + { + int nd; + double sz = (double)pkgsize / (double)1024; + + if( sz > (double)1048576 ) + { + sz = sz / (double)1048576; + /* + NOTE: + ---- + Операция округления до одного знака после десятичной точки: sz = round(sz*10.0)/10.0; + здесь не нужна; можно обойтись вычислением количества цифр, выводимых на печать с помощью + формата '%.*g': + + Количество десятичных цифр, необходимое для предстваления целой части числа + 1(одна) + десятичная цифра после десятичной точки. Формат %.*g не будет выводить дробную часть + числа, если после округления, до одного знака после десятичной точки, дробная часть + равна нулю: + */ + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gG", nd, sz ); + } + else if( sz > (double)1024 ) + { + sz = sz / (double)1024; + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gM", nd, sz ); + } + else + { + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gK", nd, sz ); + } + } + uncompressed_size = xstrdup( (const char *)&tmp[0] ); + + free( tmp ); + + filelist = dlist_sort( filelist, _compare_fnames ); + dlist_foreach( filelist, _print_filelist_entry, flist ); + + fflush( flist ); + fclose( flist ); + } + else + { + FATAL_ERROR( "There are no files in the source package" ); + } +} + +static void free_file_list( void ) +{ + if( filelist ) { dlist_free( filelist, _free_filelist_entry ); filelist = NULL; } +} +/* + End of file list functions. + *********************************************/ + +/********************************************* + Description functions. + */ +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s = '\0'; + } + else + { + /* + If short description declaration is incorrect at first line + of description; then we take whole first line of description: + */ + p = index( line, ':' ); ++p; + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } + strcpy( buf, p ); + } +} + +static char *read_short_description( FILE *fh ) +{ + char *ret = NULL; + char *buf = NULL; + + char *ln = NULL; + char *line = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, fh ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + ret = xstrdup( (const char *)buf ); + } + free( buf ); + + return ret; +} + +static void create_description_file( void ) +{ + struct stat sb; + char *buf = NULL, *tmp = NULL; + int n = 0; + + char *ln = NULL; + char *line = NULL; + + FILE *srcdesc = NULL; + FILE *tmpdesc = NULL; + FILE *outdesc = NULL; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".DESCRIPTION" ); + + if( stat( tmp, &sb ) == 0 ) + { + if( S_ISREG(sb.st_mode) && sb.st_size != 0 ) + { + srcdesc = fopen( (const char *)&tmp[0], "r" ); + if( !srcdesc ) + { + FATAL_ERROR( "Cannot read source '.DESCRIPTION' file" ); + } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".DESCRIPTION" ); + tmpdesc = fopen( (const char *)&tmp[0], "w+" ); + if( !tmpdesc ) + { + FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" ); + } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s-%s-%s-%s-%s.txt", + destination, pkgname, pkgver, arch, distroname, distrover ); + outdesc = fopen( (const char *)&tmp[0], "w" ); + if( !outdesc ) + { + FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" ); + } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&buf[0], "%s:", pkgname ); + + fprintf( outdesc, "\n/* begin *\n\n" ); + + while( (ln = fgets( line, PATH_MAX, srcdesc )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */ + { + int mlen = strlen( match ), plen = strlen( buf ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + + fprintf( tmpdesc, "%s\n", match ); + match += plen + 1; + if( match[0] != '\0' ) { fprintf( outdesc, " %s\n", match ); } + else { fprintf( outdesc, "\n" ); } + ++n; + } + } + + fprintf( outdesc, " * end */\n" ); + + if( !short_description ) + { + /* try to get short description from .DESCRIPTION file */ + fseek( tmpdesc, 0, SEEK_SET ); + short_description = read_short_description( tmpdesc ); + } + + fflush( tmpdesc ); fclose( tmpdesc ); + fflush( outdesc ); fclose( outdesc ); + fclose( srcdesc ); + + /* Copy tmpdesc file to the source package directory: */ + { + char *cmd = NULL; + + bzero( (void *)tmp, PATH_MAX ); + (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".DESCRIPTION" ); + + if( stat( tmp, &sb ) == 0 ) + { + if( S_ISREG(sb.st_mode) && sb.st_size != 0 ) + { + pid_t p = (pid_t) -1; + int rc, len = 0; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", tmp, srcdir ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" ); + } + + free( cmd ); + } + } + } /* End of copy tmpdesc file. */ + } + } + + free( line ); + free( tmp ); + free( buf ); +} +/* + End of description functions. + *********************************************/ + +static void rewrite_pkginfo_file( void ) +{ + FILE *info = NULL; + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".PKGINFO" ); + + info = fopen( (const char *)&tmp[0], "w" ); + if( !info ) + { + FATAL_ERROR( "Cannot create '.PKGINFO' file" ); + } + + if( pkgname ) + { + (void)sprintf( (char *)&tmp[0], "pkgname=%s\n", pkgname ); + fprintf( info, (const char *)&tmp[0] ); + } + if( pkgver ) + { + (void)sprintf( (char *)&tmp[0], "pkgver=%s\n", pkgver ); + fprintf( info, (const char *)&tmp[0] ); + } + if( arch ) + { + (void)sprintf( (char *)&tmp[0], "arch=%s\n", arch ); + fprintf( info, (const char *)&tmp[0] ); + } + if( distroname ) + { + (void)sprintf( (char *)&tmp[0], "distroname=%s\n", distroname ); + fprintf( info, (const char *)&tmp[0] ); + } + if( distrover ) + { + (void)sprintf( (char *)&tmp[0], "distrover=%s\n", distrover ); + fprintf( info, (const char *)&tmp[0] ); + } + if( group ) + { + (void)sprintf( (char *)&tmp[0], "group=%s\n", group ); + fprintf( info, (const char *)&tmp[0] ); + } + if( short_description ) + { + (void)sprintf( (char *)&tmp[0], "short_description=\"%s\"\n", short_description ); + fprintf( info, (const char *)&tmp[0] ); + } + if( url ) + { + (void)sprintf( (char *)&tmp[0], "url=%s\n", url ); + fprintf( info, (const char *)&tmp[0] ); + } + if( license ) + { + (void)sprintf( (char *)&tmp[0], "license=%s\n", license ); + fprintf( info, (const char *)&tmp[0] ); + } + if( uncompressed_size ) + { + (void)sprintf( (char *)&tmp[0], "uncompressed_size=%s\n", uncompressed_size ); + fprintf( info, (const char *)&tmp[0] ); + } + if( total_files ) + { + (void)sprintf( (char *)&tmp[0], "total_files=%s\n", total_files ); + fprintf( info, (const char *)&tmp[0] ); + } + + free( tmp ); + + fflush( info ); + fclose( info ); +} + +static const char *fill_compressor( char *buffer, char compressor ) +{ + switch( compressor ) + { + default: + case 'J': + (void)sprintf( buffer, "xz -9 --threads=%d -c", get_nprocs() ); + break; + case 'j': + (void)sprintf( buffer, "bzip2 -9 -c" ); + break; + case 'z': + (void)sprintf( buffer, "gzip -9 -c" ); + break; + } + return (const char *)buffer; +} + +static void create_package( void ) +{ + pid_t p = (pid_t) -1; + int rc, len = 0; + + char *tmp = NULL, *cwd = NULL, *dst = NULL, *cmd = NULL; + +#define tar_suffix ".tar" +#define sha_suffix ".sha" +#define asc_suffix ".asc" +#define ACLS "--acls" +#define XATTRS "--xattrs" +#define HASHER "sha256sum -b" + + char compressor[64]; + (void)fill_compressor( (char *)&compressor[0], compress ); + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + cwd = (char *)malloc( PATH_MAX ); + if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cwd, PATH_MAX ); + + dst = (char *)malloc( PATH_MAX ); + if( !dst ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)dst, PATH_MAX ); + + cmd = (char *)malloc( PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + /* absolute current directory path: */ + if( getcwd( cwd, (size_t)PATH_MAX ) == NULL ) + { + FATAL_ERROR( "%s-%s-%s-%s-%s%s: Cannot create PACKAGE: %s", + pkgname, pkgver, arch, distroname, distrover, txz_suffix, strerror( errno ) ); + } + remove_trailing_slash( cwd ); + + /**************************** + absolute destination path: + */ + (void)sprintf( (char *)&tmp[0], "%s", destination ); + if( tmp[0] != '/' ) + { + (void)sprintf( (char *)&dst[0], "%s/%s/%s", cwd, dirname( (char *)&tmp[0] ), basename( destination ) ); + } + else + { + (void)sprintf( (char *)&dst[0], "%s", destination ); + } + + /***************************************** + change CWD to source package directory: + */ + if( chdir( (const char *)srcdir ) == -1 ) + { + FATAL_ERROR( "Cannot change CWD to the package source directory" ); + } + + /************************************ + Set mode 0755 for .INSTALL script: + */ + (void)sprintf( (char *)&cmd[0], "chmod 0755 ./.INSTALL" ); + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot make .INSTAL script executable" ); + } + + /********************************** + push package files into tarball: + */ + len = snprintf( (char *)&cmd[0], PATH_MAX, + "find ./ | sed 's,^\\./,,' | " + "sed 's,\\.DESCRIPTION,,' | " + "sed 's,\\.FILELIST,,' | " + "sed 's,\\.INSTALL,,' | " + "sed 's,\\.PKGINFO,,' | " + "sed 's,\\.REQUIRES,,' | " + "sed 's,\\.RESTORELINKS,,' | " + "tar --no-recursion %s %s -T - -cvf %s/%s-%s-%s-%s-%s%s", + ACLS, XATTRS, dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot push package files into tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot push package files into tarball" ); + } + + /********************************** + push service files into tarball: + */ + len = snprintf( (char *)&cmd[0], PATH_MAX, + "find ./ -type f \\( -name '.DESCRIPTION' -o " + "-name '.FILELIST' -o " + "-name '.INSTALL' -o " + "-name '.PKGINFO' -o " + "-name '.REQUIRES' -o " + "-name '.RESTORELINKS' \\) | " + "sed 's,^\\./,,' | " + "tar --no-recursion %s %s -T - --append -f %s/%s-%s-%s-%s-%s%s", + ACLS, XATTRS, dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot push service files into tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot push service files into tarball" ); + } + + /********************************** + push service files into tarball: + */ + len = snprintf( (char *)&cmd[0], PATH_MAX, + "cat %s/%s-%s-%s-%s-%s%s | %s > %s/%s-%s-%s-%s-%s%s", + dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix, + compressor, dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot compress tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot compress tarball" ); + } + + /****************************** + remove uncompressed tarball: + */ + len = snprintf( (char *)&cmd[0], PATH_MAX, + "rm -f %s/%s-%s-%s-%s-%s%s", + dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot remove umcompressed tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot remove umcompressed tarball" ); + } + + /*********************************** + NOTE: + To check SHA sum we can make use 'shasum' utility: + $ ( cd destination ; shasum --check filename.sha ) + without explicitly indicated algorithm [-a 256]. + + generate SHA-256 sum of tarball: + */ + len = snprintf( (char *)&cmd[0], PATH_MAX, + "%s %s/%s-%s-%s-%s-%s%s | sed 's,%s/,,' > %s/%s-%s-%s-%s-%s%s", HASHER, + dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix, + dst, dst, pkgname, pkgver, arch, distroname, distrover, sha_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot generate SHA-256 sum of package tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot generate SHA-256 sum of package tarball" ); + } + +#if defined( HAVE_GPG2 ) + /******************************* + generate GPG ascii-signature: + */ + if( !gnupghome ) + { + struct stat st; + char *home = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + home = getenv( "GNUPGHOME" ); + if( home ) + { + (void)sprintf( tmp, "%s", home ); + if( (stat( (const char *)&tmp[0], &st ) == 0) && S_ISDIR(st.st_mode) ) + { + gnupghome = xstrdup( (const char *)&tmp[0] ); + } + } + else + { + home = getenv( "HOME" ); + if( home ) + { + (void)sprintf( tmp, "%s/.gnupg", home ); + if( (stat( (const char *)&tmp[0], &st ) == 0) && S_ISDIR(st.st_mode) ) + { + gnupghome = xstrdup( (const char *)&tmp[0] ); + } + } + } + /* Check GNUPGHOME directory: */ + while( gnupghome ) + { + (void)sprintf( tmp, "%s/%s", gnupghome, "private-keys-v1.d" ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISDIR(st.st_mode) ) + { + free( gnupghome ); gnupghome = NULL; + break; + } + (void)sprintf( tmp, "%s/pubring.kbx", gnupghome ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISREG(st.st_mode) ) + { + free( gnupghome ); gnupghome = NULL; + break; + } + (void)sprintf( tmp, "%s/trustdb.gpg", gnupghome ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISREG(st.st_mode) ) + { + free( gnupghome ); gnupghome = NULL; + break; + } + break; + } + } + if( gnupghome && passphrase && key_id ) + { + (void)sprintf( tmp, "%s/%s", tmpdir, ".gnupg" ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) == -1 ) + { + FATAL_ERROR( "Cannot create temporary GnuPG home directory" ); + } + + len = snprintf( (char *)&cmd[0], PATH_MAX, + "chmod 700 %s ;" + " cp %s/trustdb.gpg %s/ ;" + " cp %s/pubring.kbx %s/ ;" + " cp -r %s/private-keys-v1.d %s/", + tmp, + gnupghome, tmp, + gnupghome, tmp, + gnupghome, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot prepare temporary GnuPG home for signing" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + ERROR( "Cannot prepare temporary GnuPG home for signing" ); + } + + len = snprintf( (char *)&cmd[0], PATH_MAX, + "cat %s | GNUPGHOME=%s gpg2 -u %s --batch --passphrase-fd=0" + " --pinentry-mode=loopback" + " --armor --yes --emit-version" + " --comment %s-%s" + " -o %s/%s-%s-%s-%s-%s%s" + " --detach-sign %s/%s-%s-%s-%s-%s%s 2>/dev/null 1>/dev/null", + passphrase, tmp, key_id, + pkgname, pkgver, + dst, pkgname, pkgver, arch, distroname, distrover, asc_suffix, + dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot generate GPG signature of package tarball" ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + ERROR( "Cannot generate GPG signature of package tarball" ); + } + } +#endif + + /****************** + change CWD back: + */ + if( chdir( (const char *)&cwd[0] ) == -1 ) + { + FATAL_ERROR( "Cannot change CWD back" ); + } + + fprintf( stdout, "\n%s package %s/%s-%s-%s-%s-%s%s has been created.\n\n", + DISTRO_CAPTION, + destination, pkgname, pkgver, arch, distroname, distrover, txz_suffix ); + + free( cmd ); + free( dst ); + free( cwd ); + free( tmp ); +} + + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + read_pkginfo(); + tune_destinations(); + create_file_list(); + create_description_file(); + /* + NOTE: + rewrite_pkginfo_file() should be called after create_description_file() + because if there is no short description in the source .PKGINFO file + then we can try to get the short description from the first line of + .DESCRIPTION file (between opening and closing round brackets). + */ + rewrite_pkginfo_file(); + create_package(); + + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/make-pkglist.c b/src/make-pkglist.c new file mode 100644 index 0000000..103d540 --- /dev/null +++ b/src/make-pkglist.c @@ -0,0 +1,3155 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + + +#include +#include +#include + +#include +#include + +#define PROGRAM_NAME "make-pkglist" + +#include + + +char *program = PROGRAM_NAME; +char *srcdir = NULL, *pkglist_fname = NULL, + *tmpdir = NULL; + +char *srclist_fname = NULL; + +struct srcpkg_fname +{ + char *name; + int line; +}; + +struct dlist *srcpkg_fnames = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int __done = 0, __child = 0; + +enum _output_format { + OFMT_LIST = 0, + OFMT_JSON, + + OFMT_UNKNOWN +} output_format = OFMT_LIST; + +enum _tree_format tree_format = TFMT_BIN; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_UNKNOWN; + +enum _priority priority = REQUIRED; + +/*************************************************************** + Exclude declarations: + */ +static struct dlist *exclude = NULL; + +static void add_exclude( const char *name ); +static void free_exclude( void ); + +static void read_exclude_list( const char *optarg ); +/* + End of exclude declarstions. + ***************************************************************/ + +/*************************************************************** + Source file names functions: + */ +static struct srcpkg_fname *srcpkg_fname_alloc( const char *name, int line ) +{ + struct srcpkg_fname *fname = NULL; + + fname = (struct srcpkg_fname *)malloc( sizeof( struct srcpkg_fname ) ); + if( !fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + fname->name = xstrdup( name ); + fname->line = line; + + return fname; +} + +static void srcpkg_fname_free( struct srcpkg_fname *fname ) +{ + if( fname ) + { + if( fname->name ) { free( fname->name ); fname->name = NULL; } + free( fname ); + } +} + +static void __srcpkg_fname_free_func( void *data, void *user_data ) +{ + struct srcpkg_fname *fname = (struct srcpkg_fname *)data; + if( fname ) { srcpkg_fname_free( fname ); } +} + +static void free_srcpkg_fnames( void ) +{ + if( srcpkg_fnames ) { dlist_free( srcpkg_fnames, __srcpkg_fname_free_func ); srcpkg_fnames = NULL; } +} + +static void add_srcpkg_fname( struct srcpkg_fname *fname ) +{ + if( fname ) + srcpkg_fnames = dlist_append( srcpkg_fnames, (void *)fname ); +} +/* + End of source file names functions. + ***************************************************************/ + + +void free_resources() +{ + if( selfdir ) { free( selfdir ); selfdir = NULL; } + if( srcdir ) { free( srcdir ); srcdir = NULL; } + if( srclist_fname ) { free( srclist_fname ); srclist_fname = NULL; } + if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; } + + free_exclude(); + + free_tarballs(); + free_packages(); + free_srcpkg_fnames(); + free_srcpkgs(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Create Packages List in the installation order from set of PKGLOGs\n" ); + fprintf( stdout, "or set of PACKAGEs placed in the source directory. If the source\n" ); + fprintf( stdout, "directory is not defined then directory of will be used\n" ); + fprintf( stdout, "as source PACKAGE directory.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -s,--source= Setup Database packages directory or directory\n" ); + fprintf( stdout, " where set of PACKAGEs is placed.\n" ); + fprintf( stdout, " If the source argument is a PACKAGE or PKGLOG\n" ); + fprintf( stdout, " file, then the source directory will be represented\n" ); + fprintf( stdout, " as the base directory of the source file excluding\n" ); + fprintf( stdout, " the group directory if defined.\n" ); + fprintf( stdout, " -F,--files-from= Get file names of PACKAGEs or PKGLOGs from FILE.\n" ); + fprintf( stdout, " Each line of the FILE must contain a path to the\n" ); + fprintf( stdout, " PACKAGE or PKGLOG file relative to the directory\n" ); + fprintf( stdout, " specified by option --source.\n" ); + fprintf( stdout, " -e,--exclude= Ignore package with or the comma\n" ); + fprintf( stdout, " separated list of package names.\n" ); + fprintf( stdout, " -i,--iformat= Input format: PACKAGEs or PKGLOGs.\n" ); + fprintf( stdout, " -o,--oformat= Output format: LIST or JSON.\n" ); + + fprintf( stdout, " -t,--tformat= Tree format: DAG or BIN (default BIN).\n" ); + + fprintf( stdout, " -m,--minimize Create .min.json files. Applicable\n" ); + fprintf( stdout, " for JSON output format.\n" ); + + fprintf( stdout, " -p,--prioriy= Default install priority: REQ|REC|OPT|SKP.\n" ); + fprintf( stdout, " -w,--hardware= Hardware Name used for JSON output format.\n" ); + fprintf( stdout, " -H,--htmlroot= Optional Requires tree HTMLROOT name used\n" ); + fprintf( stdout, " for JSON output format.\n" ); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " Output PKGLIST file name or a target\n" ); + fprintf( stdout, " directory to save output PKGLIST.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "If HTMLROOT is not defined by option --htmlroot then HTMLROOT will be set\n" ); + fprintf( stdout, "as basename of target file in lower case without extension.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigchld( int signum ) +{ + pid_t pid = 0; + int status; + + (void)signum; + + while( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) + { + ; /* One of children with 'pid' is terminated */ + + if( WIFEXITED( status ) ) + { + if( (int) WEXITSTATUS (status) > 0 ) + { + ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + else + { + ; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + } + else if( WIFSIGNALED( status ) ) + { + ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */ + } + else + { + ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */ + } + + } + + if( pid == -1 && errno == ECHILD ) + { + /* No child processes: */ + __done = 1; + } + return; +} + + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigchld; /* CHLD */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + sa.sa_mask = set; + sigaction( SIGCHLD, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvme:s:F:o:i:t:p:w:H:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "minimize", no_argument, NULL, 'm' }, + { "exclude", required_argument, NULL, 'e' }, + { "source", required_argument, NULL, 's' }, + { "files-from", required_argument, NULL, 'F' }, + { "oformat", required_argument, NULL, 'o' }, + { "iformat", required_argument, NULL, 'i' }, + { "tformat", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "hardware", required_argument, NULL, 'w' }, + { "htmlroot", required_argument, NULL, 'H' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'm': + { + minimize = 1; + break; + } + + case 'e': + { + if( optarg != NULL ) + { + read_exclude_list( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 's': + { + if( optarg != NULL ) + { + srcdir = xstrdup( (const char *)optarg ); + remove_trailing_slash( srcdir ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'F': + { + if( optarg != NULL ) + { + srclist_fname = xstrdup( (const char *)optarg ); + remove_trailing_slash( srclist_fname ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'o': + { + char *fmt = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)fmt, (const char *)optarg ); + to_lowercase( fmt ); + + if( !strcmp( fmt, "list" ) ) { output_format = OFMT_LIST; } + else if( !strcmp( fmt, "json" ) ) { output_format = OFMT_JSON; } + else + { + ERROR( "Invalid output format: %s", fmt ); + usage(); + } + break; + } + case 'i': + { + char *fmt = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)fmt, (const char *)optarg ); + to_lowercase( fmt ); + + if( !strcmp( fmt, "pkg" ) ) { input_format = IFMT_PKG; } + else if( !strcmp( fmt, "log" ) ) { input_format = IFMT_LOG; } + else + { + ERROR( "Invalid input format: %s", fmt ); + usage(); + } + break; + } + case 't': + { + char *fmt = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)fmt, (const char *)optarg ); + to_lowercase( fmt ); + + if( !strcmp( fmt, "bin" ) ) { tree_format = TFMT_BIN; } + else if( !strcmp( fmt, "dag" ) ) { tree_format = TFMT_DAG; } + else + { + ERROR( "Invalid tree format: %s", fmt ); + usage(); + } + break; + } + case 'p': + { + char *p = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)p, (const char *)optarg ); + to_lowercase( p ); + + if( !strcmp( p, "required" ) || !strcmp( p, "req" ) ) { priority = REQUIRED; } + else if( !strcmp( p, "recommended" ) || !strcmp( p, "rec" ) ) { priority = RECOMMENDED; } + else if( !strcmp( p, "optional" ) || !strcmp( p, "opt" ) ) { priority = OPTIONAL; } + else if( !strcmp( p, "skip" ) || !strcmp( p, "skp" ) ) { priority = SKIP; } + else + { + ERROR( "Invalid default install priority: %s", p ); + usage(); + } + break; + } + case 'w': + { + char *hw = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)hw, (const char *)optarg ); + to_lowercase( hw ); + + hardware = xstrdup( (const char *)hw ); + break; + } + case 'H': + { + char *root = (char *)alloca( strlen( optarg ) + 1 ); + + strcpy( (char *)root, (const char *)optarg ); + to_lowercase( root ); + + htmlroot = xstrdup( (const char *)root ); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + /* last command line argument is the output PKGLIST file */ + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + size_t hrl = 0; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( htmlroot ) { hrl = strlen( htmlroot ); } + + buf = (char *)malloc( strlen( (const char *)argv[optind] ) + hrl + 10 ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + (void)strcpy( buf, (const char *)argv[optind++] ); + remove_trailing_slash( (char *)&buf[0] ); + + stat( (const char *)&buf[0], &st ); /* Do not check return status */ + + if( S_ISDIR(st.st_mode) ) + { + if( ! srcdir ) + { + srcdir = xstrdup( (const char *)&buf[0] ); + } + /* Add .pkglist or .json to the output dir name: */ + if( htmlroot ) + { + (void)strcat( buf, "/" ); + (void)strcat( buf, htmlroot ); + + if( output_format == OFMT_LIST ) + (void)strcat( buf, ".pkglist" ); + else + (void)strcat( buf, ".json" ); + } + else + { + if( output_format == OFMT_LIST ) + (void)strcat( buf, "/.pkglist" ); + else + (void)strcat( buf, "/.json" ); + } + } + + if( !hardware ) + hardware = xstrdup( "unknown" ); + + /* Check output directory; set srcdir and htmlroot if needed */ + { + char *d, *f, *fname; + size_t len; + + if( !rindex( (const char *)&buf[0], '/' ) ) + { + d = "."; + f = (char *)&buf[0]; + len = strlen( f ) + 3; + } + else + { + f = basename( (char *)&buf[0] ); + d = dirname( (char *)&buf[0] ); /* dirname() cuts the filename from buf[] */ + len = strlen( d ) + strlen( f ) + 2; + } + + fname = (char *)alloca( len ); + (void)sprintf( fname, "%s/%s", d, f ); + + if( stat( (const char *)d, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access output '%s' directory: %s", d, strerror( errno ) ); + } + + pkglist_fname = xstrdup( (const char *)fname ); + if( pkglist_fname == NULL ) { usage(); } + + if( !srcdir ) { srcdir = xstrdup( (const char *)d ); } + + if( !htmlroot ) + { + char *p = NULL; + if( (p = rindex( (const char *)f, '.' )) && p != f ) + { + *p = '\0'; + to_lowercase( f ); + htmlroot = xstrdup( (const char *)f ); + } + else if( *f != '.' ) + { + to_lowercase( f ); + htmlroot = xstrdup( (const char *)f ); + } + else + { + htmlroot = xstrdup( (const char *)hardware ); + } + } + } + + free( buf ); + } + else + { + usage(); + } +} + + +/*************************************************************** + Exclude functions: + */ +static void add_exclude( const char *name ) +{ + exclude = dlist_append( exclude, (void *)xstrdup( name ) ); +} + +static void __free_exclude( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void free_exclude( void ) +{ + if( exclude ) { dlist_free( exclude, __free_exclude ); exclude = NULL; } +} + +static int __compare_exclude( const void *a, const void *b ) +{ + return strncmp( (const char *)a, (const char *)b, (size_t)strlen((const char *)b) ); +} + +static const char *find_exclude( const char *name ) +{ + struct dlist *node = NULL; + + if( !exclude || !name ) return NULL; + + node = dlist_find_data( exclude, __compare_exclude, (const void *)name ); + if( node ) + { + return (const char *)node->data; + } + + return NULL; +} + +static void read_exclude_list( const char *optarg ) +{ + char *name = NULL, *p = NULL; + + name = p = (char *)optarg; + + if( !p || *p == '\0' ) return; + + while( *p == ',' ) { *p = '\0'; ++p; } + name = p; + + while( p && *p != '\0' ) + { + ++p; + + if( *p == ',' ) + { + while( *p == ',' ) { *p = '\0'; ++p; } + if( name && *name != '\0' ) { add_exclude( (const char *)name ); } + name = p; + } + + if( *p == '\0' ) + { + if( name && *name != '\0' ) { add_exclude( (const char *)name ); } + } + } +} +/* + End of exclude functions. + ***************************************************************/ + + +/*************************************************************** + Extract functions: + */ +static void _extract_pkglog( const char *group, const char *fname ) +{ + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_input_file( &uncompress, fname ); + + if( type == IFMT_PKG ) + { + int len = 0; + char *tmp= NULL, *cmd = NULL, *tgz = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) { (void)sprintf( &tmp[0], "%s/%s", tmpdir, group ); } + else { (void)sprintf( &tmp[0], "%s", tmpdir ); } + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + ERROR( "Cannot save PKGLOG from '%s' file", basename( (char *)fname ) ); + free( tmp ); + return; + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "%s/pkglog -d %s %s > /dev/null 2>&1", selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from %s file", basename( (char *)fname ) ); + } + (void)sys_exec_command( cmd ); + ++__child; + + tgz = (char *)malloc( (size_t)PATH_MAX ); + if( !tgz ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tgz, PATH_MAX ); + (void)sprintf( &tgz[0], "%s/%s", group, basename( (char *)fname ) ); + add_tarball( (char *)&tgz[0] ); + + free( tmp ); + free( cmd ); + free( tgz ); + } +} + +static void _search_packages( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + _extract_pkglog( grp, (const char *)path ); + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_packages( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/************************************************************** + extract_pkglogs() - returns number of extracted PKGLOGS + or 0 if no packages found in the srcdir. + The exit_status has been set. + */ +int extract_pkglogs( void ) +{ + int ret = 0; + + __done = 0; __child = 0; + + _search_packages( (const char *)srcdir, NULL ); + + if( __child > 0 ) + { + while( !__done ) usleep( 1 ); + ret = __child; + } + + __done = 0; __child = 0; + + return ret; +} +/* + End of Extract functions. + ***************************************************************/ + + +/*************************************************************** + Copy functions: + */ +static void _copy_pkglog( const char *group, const char *fname ) +{ + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_input_file( &uncompress, fname ); + + if( type == IFMT_LOG ) + { + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) { (void)sprintf( &tmp[0], "%s/%s", tmpdir, group ); } + else { (void)sprintf( &tmp[0], "%s", tmpdir ); } + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + ERROR( "Cannot copy '%s' PKGLOG file", basename( (char *)fname ) ); + free( tmp ); + return; + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", fname, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + (void)sys_exec_command( cmd ); + ++__child; + + free( tmp ); + free( cmd ); + } +} + +static void _search_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + _copy_pkglog( grp, (const char *)path ); + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + copy_pkglogs() - returns number of copied PKGLOGS + or 0 if no packages found in the srcdir. + The exit_status has been set. + */ +int copy_pkglogs( void ) +{ + int ret = 0; + + __done = 0; __child = 0; + + _search_pkglogs( (const char *)srcdir, NULL ); + + if( __child > 0 ) + { + while( !__done ) usleep( 1 ); + ret = __child; + } + + __done = 0; __child = 0; + + return ret; +} +/* + Enf of Copy functions. + ***************************************************************/ + + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + +static size_t read_usize( char *s ) +{ + size_t size = 0; + size_t mult = 1; + double sz = 0.0; + + char suffix; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return size; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return size; + + --q; + suffix = *q; + switch( suffix ) + { + /* by default size calculates in KiB - 1024 Bytes (du -s -h .) */ + case 'G': + case 'g': + mult = 1024 * 1024; + *q = '\0'; + break; + case 'M': + case 'm': + mult = 1024; + *q = '\0'; + break; + case 'K': + case 'k': + *q = '\0'; + break; + default: + break; + } + + if( sscanf( p, "%lg", &sz ) != 1 ) return size; + + return (size_t)round( sz * (double)mult ); +} + +static int read_total_files( char *s ) +{ + int n = 0; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return n; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return n; + + if( sscanf( p, "%u", &n ) != 1 ) return 0; + + return n; +} + + +static struct pkg *input_package( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + struct pkg *pkg = NULL; + char *pkgname = NULL, *pkgver = NULL, *group = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + } + + free( line ); + + if( pkgname && pkgver ) + { + pkg = pkg_alloc(); + if( pkg ) + { + if( group ) + { + pkg->group = group; + } + pkg->name = pkgname; + pkg->version = pkgver; + } + + } + else + { + if( group ) free( group ); + if( pkgname ) free( pkgname ); + if( pkgver ) free( pkgver ); + + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); + + return( pkg ); +} + + + +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s = '\0'; + } + else + { + /* + If short description declaration is incorrect at first line + of description; then we take whole first line of description: + */ + p = index( line, ':' ); ++p; + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } + strcpy( buf, p ); + } +} + + +static int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop || !cnt ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + + /* Get reference counter */ + { + unsigned int count; + int rc; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + rc = sscanf( ln, "REFERENCE COUNTER: %u", &count ); + if( rc == 1 && cnt != NULL ) + { + *cnt = count; + } + } + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_requires_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_description_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_restore_links_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_install_script_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_file_list_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +int read_pkginfo( FILE *log, struct package *package ) +{ + int ret = -1; + + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:", + *group_pattern = "GROUP:", + *url_pattern = "URL:", + *license_pattern = "LICENSE:", + *uncompressed_size_pattern = "UNCOMPRESSED SIZE:", + *total_files_pattern = "TOTAL FILES:"; + + + if( !log || !package ) return ret; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname_pattern )) && match == ln ) /* at start of line only */ + { + package->pkginfo->name = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + package->pkginfo->version = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + package->pkginfo->arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + package->pkginfo->distro_name = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + package->pkginfo->distro_version = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + package->pkginfo->group = skip_spaces( ln + strlen( group_pattern ) ); + } + if( (match = strstr( ln, url_pattern )) && match == ln ) + { + package->pkginfo->url = skip_spaces( ln + strlen( url_pattern ) ); + } + if( (match = strstr( ln, license_pattern )) && match == ln ) + { + package->pkginfo->license = skip_spaces( ln + strlen( license_pattern ) ); + } + if( (match = strstr( ln, uncompressed_size_pattern )) && match == ln ) + { + package->pkginfo->uncompressed_size = read_usize( ln + strlen( uncompressed_size_pattern ) ); + } + if( (match = strstr( ln, total_files_pattern )) && match == ln ) + { + package->pkginfo->total_files = read_total_files( ln + strlen( total_files_pattern ) ); + } + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, log ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + bzero( (void *)buf, PATH_MAX ); + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + package->pkginfo->short_description = xstrdup( (const char *)buf ); + } + free( buf ); + } + + } /* End of while() */ + + free( line ); + + if( package->pkginfo->name == NULL ) ++ret; + if( package->pkginfo->version == NULL ) ++ret; + if( package->pkginfo->arch == NULL ) ++ret; + if( package->pkginfo->distro_name == NULL ) ++ret; + if( package->pkginfo->distro_version == NULL ) ++ret; + /* group can be equal to NULL */ + + fseek( log, 0, SEEK_SET ); + + return( ret ); +} + + +static unsigned int read_references( FILE *log, int start, unsigned int *cnt, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int counter, pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_reference( package, pkg ); + ++pkgs; + } + ++n; + } + else + break; + } + + free( line ); + + fseek( log, 0, SEEK_SET ); + + *cnt = pkgs; + + return pkgs; +} + + +static unsigned int read_requires( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "PACKAGE DESCRIPTION:" ) ) break; /* if (stop - start - 1) greater than real number of requiress */ + + if( (n > start) && (n < stop) ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_required( package, pkg ); + ++pkgs; + } + + } + ++n; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return pkgs; +} + + +static unsigned int read_description( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *pattern = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.DESCRIPTION", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + pattern = (char *)malloc( (size_t)strlen( package->pkginfo->name ) + 2 ); + if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( pattern, "%s:", package->pkginfo->name ); + + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "RESTORE LINKS:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + /* + skip non-significant spaces at beginning of line + and print lines started with 'pkgname:' + */ + if( (match = strstr( ln, pattern )) && lines < DESCRIPTION_NUMBER_OF_LINES ) + { + int mlen = strlen( match ), plen = strlen( pattern ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + fprintf( tmp, "%s\n", match ); + ++lines; + } + + } + ++n; + } + + if( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */ + while( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + fprintf( tmp, "%s\n", pattern ); + ++lines; + } + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( pattern ); + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *desc = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + desc = (char *)malloc( size + 1 ); + if( !desc ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)desc, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)desc, size ); + if( rc != (ssize_t)size ) { ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); } + + package->description = desc; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_restore_links( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.RESTORELINKS", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "INSTALL SCRIPT:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *links = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + links = (char *)malloc( size + 1 ); + if( !links ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)links, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)links, size ); + if( rc != (ssize_t)size ) { ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); } + + package->restore_links = links; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_install_script( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.INSTALL", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "FILE LIST:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *install = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + install = (char *)malloc( size + 1 ); + if( !install ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)install, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)install, size ); + if( rc != (ssize_t)size ) { ERROR( "The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); } + + package->install_script = install; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_file_list( FILE *log, int start, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + unsigned int files = 0; + + if( !log || !package ) return files; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start ) + { + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + add_file( package, (const char *)ln ); + ++files; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return files; +} + + +static void _read_pkglog( const char *group, const char *fname ) +{ + FILE *log = NULL; + char *bname = NULL; + + if( fname != NULL ) + { + log = fopen( (const char *)fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", fname ); + } + bname = (char *)fname + strlen( tmpdir ) + 1; + } + + if( log != NULL ) + { + struct package *package = NULL; + int rc, start, stop; + unsigned int counter; + + package = package_alloc(); + + if( read_pkginfo( log, package ) != 0 ) + { + ERROR( "%s: Invalid PKGLOG file", bname ); + package_free( package ); + fclose( log ); + return; + } + + if( hardware ) package->hardware = xstrdup( (const char *)hardware ); + if( tarballs ) /* find tarball and allocate package->tarball */ + { + struct pkginfo *info = package->pkginfo; + const char *tgz = NULL; + char *buf = NULL; + struct stat sb; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( buf, "%s/%s-%s-%s-%s-%s", + info->group, info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( buf, "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + tgz = find_tarball( (const char *)&buf[0] ); + if( tgz ) + { + package->tarball = xstrdup( (const char *)tgz ); + + bzero( (void *)&buf[0], PATH_MAX ); + (void)sprintf( buf, "%s/%s", srcdir, tgz ); + if( stat( buf, &sb ) != -1 ) + { + info->compressed_size = (size_t)sb.st_size; + } + } + free( buf ); + } + package->procedure = INSTALL; + package->priority = priority; + + if( package->pkginfo->group && group && strcmp( package->pkginfo->group, group ) != 0 ) + { + char *tgz; + + if( package->tarball ) { tgz = package->tarball; } + else { tgz = basename( (char *)fname ); } + + WARNING( "%s: Should be moved into '%s' subdir", tgz, package->pkginfo->group ); + } + + /****************** + read references: + */ + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( counter > 0 ) + { + unsigned int pkgs = counter; + + if( read_references( log, start, &counter, package ) != pkgs ) + { + ERROR( "%s: Invalid REFERENCE COUNTER section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /****************** + read requires: + */ + rc = get_requires_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REQUIRES section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + unsigned int pkgs = (unsigned int)(stop - start - 1); /* -1 skips section header */ + + if( read_requires( log, start, stop, package ) != pkgs ) + { + ERROR( "%s: Invalid REQUIRES section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /******************* + read description: + */ + rc = get_description_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains PACKAGE DESCRIPTION section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + if( read_description( log, start, stop, package ) != (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + ERROR( "%s: Invalid DESCRIPTION section", bname ); + package_free( package ); + fclose( log ); + return; + } + } + + /********************* + read restore links: + */ + rc = get_restore_links_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains RESTORE LINKS section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_restore_links( log, start, stop, package ); + } + + /********************* + read install script: + */ + rc = get_install_script_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains INSTALL SCRIPT section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_install_script( log, start, stop, package ); + } + + /***************** + read file_list: + */ + rc = get_file_list_section( &start, &stop, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains FILE LIST section", bname ); + package_free( package ); + fclose( log ); + return; + } + if( start ) + { + unsigned int files = read_file_list( log, start, package ); + if( files == (unsigned int)0 ) + { + /* + Packages that do not contain regular files are ignored. + For example, service package base/init-devices-1.2.3-s9xx-glibc-radix-1.1.txz + */ + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "%s: PKGLOG contains empty FILE LIST section", bname ); + } + package_free( package ); + fclose( log ); + return; + } + package->pkginfo->total_files = (int)files; + } + + /* Skip excluded package: */ + { + const char *name = find_exclude( (const char *)package->pkginfo->name ); + + if( name && !strcmp( name, package->pkginfo->name ) ) + { + package_free( package ); + fclose( log ); + return; + } + } + + add_package( package ); + + ++__child; + fclose( log ); + } +} + +static void _read_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + if( check_input_file( NULL, (const char *)path ) == IFMT_LOG ) + { + _read_pkglog( grp, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _read_pkglogs( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +int read_pkglogs( void ) +{ + int ret = 0; + + __child = 0; + + _read_pkglogs( (const char *)tmpdir, NULL ); + + ret = __child; + + __child = 0; + + return ret; +} + + + +/***************************** + Count slashes in file name: + */ +static int count_slashes( char *s ) +{ + int cnt = 0; + char *p = (char *)0; + + if( !s || *s == '\0' ) return cnt; + + p = s; + while( *p ) + { + if( *p == '/' ) { + ++cnt; + } + ++p; + } + + return cnt; +} + +static void read_srcpkg_fnames( const char *flist ) +{ + struct stat st; + FILE *fp = NULL; + + if( !flist ) return; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( flist, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file or directory: %s", flist, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + int lnum = 0; + + fp = fopen( flist, "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open %s file", flist ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + struct srcpkg_fname *fname = NULL; + + ++lnum; + if( strlen( ln ) == 0 ) continue; + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + /* remove leading spaces: */ + while( (*ln == ' ' || *ln == '\t') && *ln != '\0' ) { ++ln; } + /* skip comments and empty lines: */ + if( *ln == '\0' || *ln == '#' ) continue; + + if( count_slashes( ln ) > 1 ) + { + FATAL_ERROR( "%s:%d: file name contains more than one slash symbols", flist, lnum ); + } + + if( ! isalpha( *ln ) ) + { + FATAL_ERROR( "%s:%d: file path must be relative and start with a letter (as a group name)", flist, lnum ); + } + + fname = srcpkg_fname_alloc( (const char *)ln, lnum ); + add_srcpkg_fname( fname ); + } + + free( line ); + fclose( fp ); + } + else + { + FATAL_ERROR( "Source file names list '%s' is not a regular file" ); + } +} + +static void check_srcfile( void *data, void *user_data ) +{ + struct srcpkg_fname *srcfile = (struct srcpkg_fname *)data; + + if( srcfile ) + { + struct stat st; + char *fname = NULL; + struct pkg *srcpkg = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + fname = (char *)malloc( (size_t)PATH_MAX ); + if( !fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)fname, PATH_MAX ); + + (void)sprintf( &fname[0], "%s/%s", srcdir, srcfile->name ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "%s:%d: Cannot access input '%s' file or directory: %s", + srcfile->name, srcfile->line, fname, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + enum _input_type format = IFMT_UNKNOWN; + + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from '%s' file", + srcfile->name, srcfile->line, basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from %s file", + srcfile->name, srcfile->line, basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from '%s' file", + srcfile->name, srcfile->line, basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + + bzero( (void *)&st, sizeof( struct stat ) ); + if( stat( (const char *)tmp, &st ) == -1 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from %s file: %s", + srcfile->name, srcfile->line, basename( (char *)fname ), strerror( errno ) ); + } + + srcpkg = input_package( (const char *)&tmp[0] ); + bzero( (void *)tmp, PATH_MAX ); + if( srcpkg ) + { + char *e = NULL; + + if( srcpkg->group ) + { + len = snprintf( &tmp[0], PATH_MAX, "%s/%s", srcpkg->group, basename( (char *)fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from %s file", + srcfile->name, srcfile->line, basename( (char *)fname ) ); + } + } + else + { + len = snprintf( &tmp[0], PATH_MAX, "%s", basename( (char *)fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "%s:%d: Cannot get PKGINFO from %s file", + srcfile->name, srcfile->line, basename( (char *)fname ) ); + } + } + + add_srcpkg( srcpkg ); + + remove_trailing_slash( srcdir ); + } + + free( tmp ); + free( cmd ); + } + + free( fname ); + } +} + +static void check_srcdir( void ) +{ + struct stat st; + char *fname = srcdir; + struct pkg *srcpkg = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)srcdir, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file or directory: %s", srcdir, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + enum _input_type format = IFMT_UNKNOWN; + + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + + bzero( (void *)&st, sizeof( struct stat ) ); + if( stat( (const char *)tmp, &st ) == -1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + /* keep or set input file format: */ + format = check_input_file( NULL, fname ); + if( input_format == IFMT_UNKNOWN ) input_format = format; + + srcpkg = input_package( (const char *)&tmp[0] ); + bzero( (void *)tmp, PATH_MAX ); + if( srcpkg ) + { + char *e = NULL; + + if( srcpkg->group ) + { + len = snprintf( &tmp[0], PATH_MAX, "%s/%s", srcpkg->group, basename( (char *)fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + } + else + { + len = snprintf( &tmp[0], PATH_MAX, "%s", basename( (char *)fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + } + + add_srcpkg( srcpkg ); + + /* remove source file name (with group directory if defined) from srcdir path */ + e = strstr( (const char *)srcdir, (const char *)&tmp[0] ); + if( !e ) { e = strstr( (const char *)srcdir, (const char *)basename( (char *)fname ) ); } + *e = '\0'; + + remove_trailing_slash( srcdir ); + } + + free( tmp ); + free( cmd ); + } + + if( S_ISDIR(st.st_mode) ) + { + if( srclist_fname ) + { + read_srcpkg_fnames( srclist_fname ); + dlist_foreach( srcpkg_fnames, check_srcfile, NULL ); + } + } +} + + +static void _print_extern_requires( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + if( pkg->group ) + fprintf( stderr, "%s/%s:%s:%s\n", pkg->group, pkg->name, pkg->version, strproc( pkg->procedure ) ); + else + fprintf( stderr, "%s:%s:%s\n", pkg->name, pkg->version, strproc( pkg->procedure ) ); + } +} + +static void print_extern_requires( void ) +{ + dlist_foreach( extern_requires, _print_extern_requires, NULL ); +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + /******************************************************** + Check if the --source argument is a file or directory: + */ + check_srcdir(); + + /* Extract or Copy PKGLOGs into TMPDIR: */ + if( input_format == IFMT_PKG ) + { + int pkgs = extract_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no packages in the '%s' directory", srcdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot extract PKGLOG from some package" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "Found %d packages in the '%s' directory", pkgs, srcdir ); + } + } + else + { + int pkgs = copy_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", srcdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot copy some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, srcdir ); + } + } + + /* Read PKGLOGs from TMPDIR and create Double Linked List of PACKAGES: */ + { + int pkgs = read_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", tmpdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot read some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + /* INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, tmpdir ); */ + } + } + + { + int extern_pkgs = create_provides_list( srcpkgs ); + if( output_format == OFMT_LIST ) + { + print_provides_list( (const char *)pkglist_fname ); + if( extern_pkgs ) + { + /* Output machine readable list of requires to stderr: */ + print_extern_requires(); + exit_status += 1; + } + } + if( output_format == OFMT_JSON ) print_provides_tree( (const char *)pkglist_fname, tree_format ); + free_provides_list(); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/make-pkglist.h b/src/make-pkglist.h new file mode 100644 index 0000000..ac0d713 --- /dev/null +++ b/src/make-pkglist.h @@ -0,0 +1,36 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _MK_PKGLIST_H_ +#define _MK_PKGLIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern char *htmlroot; +extern char *hardware; +extern int minimize; + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _MK_PKGLIST_H_ */ diff --git a/src/msglog.c b/src/msglog.c new file mode 100644 index 0000000..f9b9e03 --- /dev/null +++ b/src/msglog.c @@ -0,0 +1,67 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +FILE *errlog; + +void (*fatal_error_hook)( void ); + +void logmsg( FILE *logfile, enum _msg_type type, char *format, ... ) +{ + va_list argp; + + if( ! format ) return; + + switch( type ) + { + case MSG_FATAL: fprintf( logfile, "%s: FATAL: ", program ); break; + case MSG_ERROR: fprintf( logfile, "%s: ERROR: ", program ); break; + case MSG_WARNING: fprintf( logfile, "%s: WARNING: ", program ); break; + case MSG_NOTICE: fprintf( logfile, "%s: NOTE: ", program ); break; + case MSG_INFO: fprintf( logfile, "%s: INFO: ", program ); break; + case MSG_DEBUG: fprintf( logfile, "%s: DEBUG: ", program ); break; + case MSG_LOG: + { + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + fprintf( logfile, "[%04d-%02d-%02d %02d:%02d:%02d]: %s: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + program ); + break; + } + default: + fprintf( logfile, "%s: ", program ); + break; + } + va_start( argp, format ); + vfprintf( errlog, format, argp ); + fprintf( errlog, "\n" ); + fflush( errlog ); +} diff --git a/src/msglog.h b/src/msglog.h new file mode 100644 index 0000000..fc256f0 --- /dev/null +++ b/src/msglog.h @@ -0,0 +1,98 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _MSG_LOG_H_ +#define _MSG_LOG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern FILE *errlog; + +extern void (*fatal_error_hook)( void ); + +extern char *program; +extern int exit_status; + +enum _msg_type +{ + MSG_FATAL = 0, + MSG_ERROR, + MSG_WARNING, + MSG_NOTICE, + MSG_INFO, + MSG_DEBUG, + + MSG_LOG +}; + +#define FATAL_ERROR( ... ) \ + do \ + { \ + logmsg( errlog, MSG_FATAL, __VA_ARGS__ ); \ + if( fatal_error_hook) fatal_error_hook(); \ + exit( EXIT_FAILURE ); \ + } while (0) + +#define ERROR( ... ) \ + do \ + { \ + logmsg( errlog, MSG_ERROR, __VA_ARGS__ ); \ + ++exit_status; \ + } while (0) + +#define WARNING( ... ) \ + do \ + { \ + logmsg( errlog, MSG_WARNING, __VA_ARGS__ ); \ + } while (0) + +#define NOTICE( ... ) \ + do \ + { \ + logmsg( errlog, MSG_NOTICE, __VA_ARGS__ ); \ + } while (0) + +#define INFO( ... ) \ + do \ + { \ + logmsg( errlog, MSG_INFO, __VA_ARGS__ ); \ + } while (0) + +#define DEBUG( ... ) \ + do \ + { \ + logmsg( errlog, MSG_DEBUG, __VA_ARGS__ ); \ + } while (0) + +#define LOG( ... ) \ + do \ + { \ + logmsg( errlog, MSG_LOG, __VA_ARGS__ ); \ + } while (0) + + +extern void logmsg( FILE *logfile, enum _msg_type type, char *format, ... ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _MSG_LOG_H_ */ diff --git a/src/pkginfo.c b/src/pkginfo.c new file mode 100644 index 0000000..2e93a7c --- /dev/null +++ b/src/pkginfo.c @@ -0,0 +1,1573 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + + +#include +#include +#include + +#define PROGRAM_NAME "pkginfo" + +#include + + +char *program = PROGRAM_NAME; +char *destination = NULL, *operation = NULL, *pkglog_fname = NULL; +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *total_files = NULL; + +FILE *pkglog = NULL; +FILE *output = NULL; + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL + +void free_resources() +{ + if( selfdir ) { free( selfdir ); selfdir = NULL; } + if( destination ) { free( destination ); destination = NULL; } + if( operation ) { free( operation ); operation = NULL; } + if( pkglog_fname ) { free( pkglog_fname ); pkglog_fname = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Read information from or file and create\n" ); + fprintf( stdout, "requested package's service files in the destination directory.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -d,--destination= Target directory to save output files.\n" ); + fprintf( stdout, " -o,--operations= Comma separated list of:\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Operations:\n" ); + fprintf( stdout, " ----------------+----------------\n" ); + fprintf( stdout, " operation name | output file\n" ); + fprintf( stdout, " ----------------+----------------\n" ); + fprintf( stdout, " pkginfo | .PKGINFO\n" ); + fprintf( stdout, " references | .REFERENCES\n" ); + fprintf( stdout, " requires | .REQUIRES\n" ); + fprintf( stdout, " description | .DESCRIPTION\n" ); + fprintf( stdout, " restore-links | .RESTORELINKS\n" ); + fprintf( stdout, " install-script | .INSTALL\n" ); + fprintf( stdout, " filelist | .FILELIST\n" ); + fprintf( stdout, " ----------------+----------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " PKGLOG file or package tarball.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + +enum _pkglog_type +{ + PKGLOG_TEXT = 0, + PKGLOG_GZ, + PKGLOG_BZ2, + PKGLOG_XZ, + PKGLOG_TAR, + + PKGLOG_UNKNOWN +}; + +static enum _pkglog_type pkglog_type = PKGLOG_UNKNOWN; +static char uncompress[2] = { 0, 0 }; + + +static enum _pkglog_type check_pkglog_file( const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + uncompress[0] = '\0'; + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) ); + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return PKGLOG_TEXT; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + uncompress[0] = 'x'; + close( fd ); return PKGLOG_GZ; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + uncompress[0] = 'j'; + close( fd ); return PKGLOG_BZ2; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + uncompress[0] = 'J'; + close( fd ); return PKGLOG_XZ; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return PKGLOG_TAR; + } + } + + close( fd ); return PKGLOG_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvd:o:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "destination", required_argument, NULL, 'd' }, + { "operations", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'd': + { + if( optarg != NULL ) + { + destination = xstrdup( (const char *)optarg ); + remove_trailing_slash( destination ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'o': + { + operation = xstrdup( (const char *)optarg ); + to_lowercase( operation ); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + if( destination == NULL ) + { + char cwd[PATH_MAX]; + if( getcwd( cwd, sizeof(cwd) ) != NULL ) + destination = xstrdup( (const char *)cwd ); + else + destination = xstrdup( "." ); + } + + if( operation == NULL ) usage(); + + /* last command line argument is the LOGFILE */ + if( optind < argc ) + { + pkglog_fname = xstrdup( (const char *)argv[optind++] ); + if( pkglog_fname == NULL ) + { + usage(); + } + pkglog_type = check_pkglog_file( (const char *)pkglog_fname ); + if( pkglog_type == PKGLOG_UNKNOWN ) + { + ERROR( "%s: Unknown input file format", basename( pkglog_fname ) ); + usage(); + } + } + else + { + usage(); + } +} + + +/* + Especialy for pkginfo lines. + Remove leading spaces and take non-space characters only: + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + +/* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +int printf_variable( FILE *output, char *log_fname, char *name, char *value, char *pattern, int error ) +{ + int exit_status = 0; /* local errors counter */ + char buf[24]; + char *p; + + bzero( (void *)buf, 24 ); + + if( pattern ) + (void)sprintf( (char *)&buf[0], "%s", pattern ); + + p = (char *)&buf[0]; + p[strlen(buf) - 1] = '\0'; /* skip colon at end of pattern */ + + if( value ) + { + fprintf( output, "%s=%s\n", name, value ); + } + else + { + if( error ) + { + ERROR( "There is no %s declaration in the %s file", p, log_fname ); + } + else + { + if( ! DO_NOT_WARN_ABOUT_OPT_PKGINFO_ITEMS ) + WARNING( "There is no %s declaration in the %s file", p, log_fname ); + } + } + + return( exit_status ); +} + + +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + *s = '"'; ++s; /* start " */ + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s++ = '"'; /* stop " */ + *s = '\0'; + } +} + +int write_pkginfo() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 10 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.PKGINFO" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + char *desc = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:", + *group_pattern = "GROUP:", + *url_pattern = "URL:", + *license_pattern = "LICENSE:", + *uncompressed_size_pattern = "UNCOMPRESSED SIZE:", + *total_files_pattern = "TOTAL FILES:"; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname_pattern )) && match == ln ) /* at start of line only */ + { + pkgname = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + pkgver = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + distroname = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + distrover = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + group = skip_spaces( ln + strlen( group_pattern ) ); + } + if( (match = strstr( ln, url_pattern )) && match == ln ) + { + url = skip_spaces( ln + strlen( url_pattern ) ); + } + if( (match = strstr( ln, license_pattern )) && match == ln ) + { + license = skip_spaces( ln + strlen( license_pattern ) ); + } + if( (match = strstr( ln, uncompressed_size_pattern )) && match == ln ) + { + uncompressed_size = skip_spaces( ln + strlen( uncompressed_size_pattern ) ); + } + if( (match = strstr( ln, total_files_pattern )) && match == ln ) + { + total_files = skip_spaces( ln + strlen( total_files_pattern ) ); + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, pkglog ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + bzero( (void *)buf, PATH_MAX ); + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + desc = xstrdup( (const char *)buf ); + } + free( buf ); + } + } + + free( line ); + + ret += printf_variable( output, pkglog_fname, "pkgname", pkgname, pkgname_pattern, 1 ); + ret += printf_variable( output, pkglog_fname, "pkgver", pkgver, pkgver_pattern, 1 ); + ret += printf_variable( output, pkglog_fname, "arch", arch, arch_pattern, 1 ); + ret += printf_variable( output, pkglog_fname, "distroname", distroname, distroname_pattern, 1 ); + ret += printf_variable( output, pkglog_fname, "distrover", distrover, distrover_pattern, 1 ); + ret += printf_variable( output, pkglog_fname, "group", group, group_pattern, 0 ); + if( desc != NULL ) + { + ret += printf_variable( output, pkglog_fname, "short_description", desc, "SHORT DESCRIPTION:", 0 ); + free( desc ); desc = NULL; + } + ret += printf_variable( output, pkglog_fname, "url", url, url_pattern, 0 ); + ret += printf_variable( output, pkglog_fname, "license", license, license_pattern, 0 ); + ret += printf_variable( output, pkglog_fname, "uncompressed_size", uncompressed_size, uncompressed_size_pattern, 0 ); + ret += printf_variable( output, pkglog_fname, "total_files", total_files, total_files_pattern, 0 ); + + FREE_PKGINFO_VARIABLES(); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +/* + NOTE: + 1. first line has number 1. + 2. sections are ordered according to following list: + */ +int reference_counter = 0; +int requires = 0; +int package_description = 0; +int restore_links = 0; +int install_script = 0; +int file_list = 0; + +int refcount = 0; + + +int get_pkglog_sections() +{ + int ret = -1, found = 0; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( pkglog != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + reference_counter = ret + 1; + ++found; + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + requires = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + package_description = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + restore_links = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + install_script = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + file_list = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = found; + + fclose( pkglog ); pkglog = NULL; + } + + return( ret ); +} + + +int get_pkglog_line( char *pattern ) +{ + int ret = -1, found = 0; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( pkglog != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + char *match = NULL; + + if( (match = strstr( ln, pattern )) && match == ln ) /* at start of line only */ + { + ++ret; + ++found; + break; + } + ++ret; + } + if( !found ) ret = 0; + + free( line ); + + fclose( pkglog ); pkglog = NULL; + } + return( ret ); +} + + +int get_ref_cnt() +{ + int ret = -1, found = 0; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( pkglog != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + char *cnt = skip_spaces( ln + strlen( "REFERENCE COUNTER:" ) ); + ret = atoi( cnt ); + free( cnt ); + ++found; + break; + } + } + if( !found ) ret = -1; + + free( line ); + + fclose( pkglog ); pkglog = NULL; + } + return( ret ); +} + + +int write_references() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 13 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.REFERENCES" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( reference_counter && reference_counter < requires ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > reference_counter) && (n < requires) ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +int write_requires() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 11 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.REQUIRES" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( requires && requires < package_description ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > requires) && (n < package_description) ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +int write_package_description() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 14 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.DESCRIPTION" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( package_description && package_description < restore_links ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > package_description) && (n < restore_links) ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +int write_restore_links() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 15 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.RESTORELINKS" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( restore_links && restore_links < install_script ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > restore_links) && (n < install_script) ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +int write_install_script() +{ + int ret = -1; + char *output_fname = NULL; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + output_fname = (char *)alloca( strlen( destination ) + 10 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.INSTALL" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( install_script && install_script < file_list ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (n > install_script) && (n < file_list) ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + chmod( (const char *)output_fname, (mode_t)0755 ); + + return( ret ); +} + + +int write_filelist() +{ + int ret = -1; + + if( pkglog_fname != NULL ) + { + pkglog = fopen( (const char *)pkglog_fname, "r" ); + if( !pkglog ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( destination != NULL ) + { + char *output_fname = NULL; + + output_fname = (char *)alloca( strlen( destination ) + 11 ); + strcpy( output_fname, destination ); + strcat( output_fname, "/.FILELIST" ); + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + if( (pkglog != NULL) && (output != NULL) ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( file_list ) + { + int n = 1, lines = 0; + + ++ret; + + while( (ln = fgets( line, PATH_MAX, pkglog )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( n > file_list ) + { + fprintf( output, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( pkglog ); pkglog = NULL; + fclose( output ); output = NULL; + } + + return( ret ); +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + int sections = 0; + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + if( pkglog_type == PKGLOG_TEXT ) + { + sections = get_pkglog_sections(); + if( sections < 3 ) + { + FATAL_ERROR( "%s: Wrong PKGLOG file format", basename( pkglog_fname) ); + } + + refcount = get_ref_cnt(); + + if( strstr( operation, "pkginfo" ) ) exit_status += write_pkginfo(); + + if( strstr( operation, "references" ) ) + { + if( !reference_counter ) + { + WARNING( "The REFERENCE COUNTER is not present in %s file", basename( pkglog_fname ) ); + } + else + { + if( write_references() != refcount ) + { + WARNING( "The REFERENCE COUNTER invalid in %s file", basename( pkglog_fname ) ); + } + } + } + + if( strstr( operation, "requires" ) ) + { + if( write_requires() <= 0 ) + { + if( ! DO_NOT_WARN_ABOUT_EMPTY_REQUIRES ) + WARNING( "The REQUIRES is not present in %s file", basename( pkglog_fname ) ); + } + } + + if( strstr( operation, "description" ) ) + { + if( write_package_description() <= 0 ) + { + WARNING( "The PACKAGE DESCRIPTION is not present in %s file", basename( pkglog_fname ) ); + } + } + + if( strstr( operation, "restore-links" ) ) + { + if( write_restore_links() <= 0 ) + { + if( ! DO_NOT_WARN_ABOUT_EMPTY_RESTORE_LINKS ) + WARNING( "The RESTORE LINKS is not present in %s file", basename( pkglog_fname ) ); + } + } + + if( strstr( operation, "install-script" ) ) + { + if( write_install_script() <= 0 ) + { + ERROR( "The INSTALL SCRIPT is not present in %s file", basename( pkglog_fname ) ); + } + } + + if( strstr( operation, "filelist" ) ) + { + if( write_filelist() <= 0 ) + { + ERROR( "The FILE LIST is not present in %s file", basename( pkglog_fname ) ); + } + } + + } + else /* TARBALL: */ + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *errmsg = NULL, *wmsg = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + + errmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + wmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !wmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + + if( strstr( operation, "pkginfo" ) ) /* strongly required */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get .PKGINFO from %s file", basename( pkglog_fname ) ); + + len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".PKGINFO" ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + /***************************************** + if( rc > 0 ) { return TAR exit status } + else { return EXIT_FAILURE } + */ + if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */ + ERROR( errmsg ); + if( fatal_error_hook) fatal_error_hook(); + exit( exit_status ); + } + } + + /* .REFERENCES is not present in package tarball */ + + if( strstr( operation, "requires" ) ) /* optional; may be warning */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get .REQUIRES from %s file", basename( pkglog_fname ) ); + + (void)sprintf( &cmd[0], "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".REQUIRES" ); + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 && DO_NOT_WARN_ABOUT_EMPTY_REQUIRES == 0 ) + { + WARNING( errmsg ); + } + } + + if( strstr( operation, "description" ) ) /* optional; always warning */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &cmd[0], "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".DESCRIPTION" ); + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + WARNING( "Cannot get package .DESCRIPTION from %s file", basename( pkglog_fname ) ); + } + } + + if( strstr( operation, "restore-links" ) ) /* optional; may be warning */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get .RESTORELINKS script from %s file", basename( pkglog_fname ) ); + + (void)sprintf( &cmd[0], "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".RESTORELINKS" ); + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 && DO_NOT_WARN_ABOUT_EMPTY_RESTORE_LINKS == 0 ) + { + WARNING( errmsg ); + } + } + + if( strstr( operation, "install-script" ) ) /* strongly required */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get .INSTALL script from %s file", basename( pkglog_fname ) ); + + len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".INSTALL" ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + /***************************************** + if( rc > 0 ) { return TAR exit status } + else { return EXIT_FAILURE } + */ + if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */ + ERROR( errmsg ); + if( fatal_error_hook) fatal_error_hook(); + exit( exit_status ); + } + } + + if( strstr( operation, "filelist" ) ) /* strongly required */ + { + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get .FILELIST from %s file", basename( pkglog_fname ) ); + + len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1", destination, uncompress, pkglog_fname, ".FILELIST" ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + /***************************************** + if( rc > 0 ) { return TAR exit status } + else { return EXIT_FAILURE } + */ + if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */ + ERROR( errmsg ); + if( fatal_error_hook) fatal_error_hook(); + exit( exit_status ); + } + } + + if( cmd ) free( cmd ); + if( errmsg ) free( errmsg ); + if( wmsg ) free( wmsg ); + } + + + free_resources(); + + exit( exit_status ); +} diff --git a/src/pkglist.c b/src/pkglist.c new file mode 100644 index 0000000..23b080d --- /dev/null +++ b/src/pkglist.c @@ -0,0 +1,2198 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include /* basename(3) */ +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + + +char *htmlroot = NULL; +char *hardware = NULL; +int minimize = 0; + +struct dlist *srcpkgs = NULL; + +struct dlist *packages = NULL; +struct dlist *tarballs = NULL; + +struct dlist *provides = NULL; +struct dlist *extern_requires = NULL; + +static struct dlist *tree = NULL; + +static char *pkgs_fname = NULL, + *tree_fname = NULL, + *html_fname = NULL; + +static char *pkgs_min_fname = NULL, + *tree_min_fname = NULL; + +static const char *tarball_suffix = "txz"; + +/*************************************************************** + tarballs List functions: + ======================= + + NOTE: + ---- + TARBALLS is an optional list created in case when we have + a set of PACKAGES as input of make-pkglist utility. When we + are working with a set of input PKGLOGs the TARBALLS list + is not chreated and pointer to the tarballs == NULL. + */ +void add_tarball( char *tarball ) +{ + tarballs = dlist_append( tarballs, (void *)xstrdup( (const char *)tarball ) ); +} + +static void __free_tarball( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +void free_tarballs( void ) +{ + if( tarballs ) { dlist_free( tarballs, __free_tarball ); tarballs = NULL; } +} + +static int __compare_tarballs( const void *a, const void *b ) +{ + return strncmp( (const char *)a, (const char *)b, (size_t)strlen((const char *)b) ); +} + +const char *find_tarball( const char *name ) +{ + struct dlist *node = NULL; + + if( !tarballs || !name ) return NULL; + + node = dlist_find_data( tarballs, __compare_tarballs, (const void *)name ); + if( node ) + { + return (const char *)node->data; + } + + return NULL; +} + +/********************* + Just for debugging: + */ +static void __print_tarball( void *data, void *user_data ) +{ + int *counter = (int *)user_data; + + if( counter ) { fprintf( stdout, "tarball[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } + else { fprintf( stdout, "tarball: %s\n", (char *)data ); } +} + +void print_tarballs( void ) +{ + int cnt = 0; + if( tarballs ) { dlist_foreach( tarballs, __print_tarball, (void *)&cnt ); } +} +/* + End of tarballs List functions. + ***************************************************************/ + + + +char *strprio( enum _priority priority, int short_name ) +{ + char *p = NULL; + + switch( priority ) + { + case REQUIRED: + p = ( short_name ) ? "REQ" : "REQUIRED"; + break; + case RECOMMENDED: + p = ( short_name ) ? "REC" : "RECOMMENDED"; + break; + case OPTIONAL: + p = ( short_name ) ? "OPT" : "OPTIONAL"; + break; + case SKIP: + p = ( short_name ) ? "SKP" : "SKIP"; + break; + } + return p; +} + +char *strproc( enum _procedure procedure ) +{ + char *p = NULL; + + switch( procedure ) + { + case INSTALL: + p = "install"; + break; + case UPDATE: + p = "update"; + break; + } + return p; +} + + +/*************************************************************** + PACKAGE functions: + */ + +struct pkg *pkg_alloc( void ) +{ + struct pkg *pkg = NULL; + + pkg = (struct pkg *)malloc( sizeof( struct pkg ) ); + if( !pkg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)pkg, sizeof( struct pkg ) ); + + return pkg; +} + +void pkg_free( struct pkg *pkg ) +{ + if( pkg ) + { + if( pkg->group ) { free( pkg->group ); pkg->group = NULL; } + if( pkg->name ) { free( pkg->name ); pkg->name = NULL; } + if( pkg->version ) { free( pkg->version ); pkg->version = NULL; } + + free( pkg ); + } +} + +static void __pkg_free_func( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + if( pkg ) { pkg_free( pkg ); } +} + +void free_srcpkgs( void ) +{ + if( srcpkgs ) { dlist_free( srcpkgs, __pkg_free_func ); srcpkgs = NULL; } +} + +void add_srcpkg( struct pkg *pkg ) +{ + srcpkgs = dlist_append( srcpkgs, (void *)pkg ); +} + + +static struct pkginfo *__pkginfo_alloc( void ) +{ + struct pkginfo *pkginfo = NULL; + + pkginfo = (struct pkginfo *)malloc( sizeof( struct pkginfo ) ); + if( !pkginfo ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)pkginfo, sizeof( struct pkginfo ) ); + + return pkginfo; +} + +static void __pkginfo_free( struct pkginfo *pkginfo ) +{ + if( pkginfo ) + { + if( pkginfo->name ) { free( pkginfo->name ); pkginfo->name = NULL; } + if( pkginfo->version ) { free( pkginfo->version ); pkginfo->version = NULL; } + if( pkginfo->arch ) { free( pkginfo->arch ); pkginfo->arch = NULL; } + if( pkginfo->distro_name ) { free( pkginfo->distro_name ); pkginfo->distro_name = NULL; } + if( pkginfo->distro_version ) { free( pkginfo->distro_version ); pkginfo->distro_version = NULL; } + if( pkginfo->group ) { free( pkginfo->group ); pkginfo->group = NULL; } + if( pkginfo->short_description ) { free( pkginfo->short_description ); pkginfo->short_description = NULL; } + if( pkginfo->url ) { free( pkginfo->url ); pkginfo->url = NULL; } + if( pkginfo->license ) { free( pkginfo->license ); pkginfo->license = NULL; } + + free( pkginfo ); + } +} + + +static struct references *__references_alloc( void ) +{ + struct references *references = NULL; + + references = (struct references *)malloc( sizeof( struct references ) ); + if( !references ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)references, sizeof( struct references ) ); + + return references; +} + +static void __references_free( struct references *references ) +{ + if( references ) + { + if( references->list ) { dlist_free( references->list, __pkg_free_func ); references->list = NULL; } + free( references ); + } +} + + +static struct requires *__requires_alloc( void ) +{ + struct requires *requires = NULL; + + requires = (struct requires *)malloc( sizeof( struct requires ) ); + if( !requires ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)requires, sizeof( struct requires ) ); + + return requires; +} + +static void __requires_free( struct requires *requires ) +{ + if( requires ) + { + if( requires->list ) { dlist_free( requires->list, __pkg_free_func ); requires->list = NULL; } + free( requires ); + } +} + + +static struct files *__files_alloc( void ) +{ + struct files *files = NULL; + + files = (struct files *)malloc( sizeof( struct files ) ); + if( !files ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)files, sizeof( struct files ) ); + + return files; +} + +static void __files_free_func( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void __files_free( struct files *files ) +{ + if( files ) + { + if( files->list ) { dlist_free( files->list, __files_free_func ); files->list = NULL; } + free( files ); + } +} + + +struct package *package_alloc( void ) +{ + struct package *package = NULL; + struct pkginfo *pkginfo = __pkginfo_alloc(); + struct references *references = __references_alloc(); + struct requires *requires = __requires_alloc(); + struct files *files = __files_alloc(); + + package = (struct package *)malloc( sizeof( struct package ) ); + if( !package ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)package, sizeof( struct package ) ); + + package->pkginfo = pkginfo; + package->references = references; + package->requires = requires; + package->files = files; + + return package; +} + +void package_free( struct package *package ) +{ + if( package ) + { + if( package->pkginfo ) { __pkginfo_free( package->pkginfo ); package->pkginfo = NULL; } + if( package->references ) { __references_free( package->references ); package->references = NULL; } + if( package->requires ) { __requires_free( package->requires ); package->requires = NULL; } + if( package->files ) { __files_free( package->files ); package->files = NULL; } + + if( package->description ) { free( package->description ); package->description = NULL; } + if( package->restore_links ) { free( package->restore_links ); package->restore_links = NULL; } + if( package->install_script ) { free( package->install_script ); package->install_script = NULL; } + if( package->hardware ) { free( package->hardware ); package->hardware = NULL; } + if( package->tarball ) { free( package->tarball ); package->tarball = NULL; } + + free( package ); + } +} + +static void __package_free_func( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + if( package ) { package_free( package ); } +} + +void free_packages( void ) +{ + if( packages ) { dlist_free( packages, __package_free_func ); packages = NULL; } +} + + +static int __compare_packages( const void *a, const void *b ) +{ + int ret = -1; + + struct package *pkg1 = (struct package *)a; + struct package *pkg2 = (struct package *)b; + + if( pkg1->pkginfo->group && pkg2->pkginfo->group ) + { + ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group ); + } + else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group ) + { + ret = 0; + } + else if( pkg1->pkginfo->group ) + { + ret = 1; + } + + if( ! ret ) + { + return strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name ); + } + return ret; +} + +static int __compare_packages_with_version( const void *a, const void *b ) +{ + int ret = -1; + + struct package *pkg1 = (struct package *)a; + struct package *pkg2 = (struct package *)b; + + if( pkg1->pkginfo->group && pkg2->pkginfo->group ) + { + ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group ); + } + else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group ) + { + ret = 0; + } + else if( pkg1->pkginfo->group ) + { + ret = 1; + } + + if( ! ret ) + { + ret = strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name ); + if( ! ret ) + { + return cmp_version( (const char *)pkg1->pkginfo->version, (const char *)pkg2->pkginfo->version ); + } + } + return ret; +} + + +void add_package( struct package *package ) +{ + packages = dlist_append( packages, (void *)package ); +} + +void add_reference( struct package *package, struct pkg *pkg ) +{ + if( package && package->references && pkg ) + { + package->references->list = dlist_append( package->references->list, (void *)pkg ); + package->references->size = dlist_length( package->references->list ); + } +} + +void add_required( struct package *package, struct pkg *pkg ) +{ + if( package && package->requires && pkg ) + { + package->requires->list = dlist_append( package->requires->list, (void *)pkg ); + package->requires->size = dlist_length( package->requires->list ); + } +} + +void add_file( struct package *package, const char *fname ) +{ + if( package && package->files && fname ) + { + package->files->list = dlist_append( package->files->list, (void *)xstrdup( (const char *)fname ) ); + package->files->size = dlist_length( package->files->list ); + } +} + +/********************* + Just for debugging: + */ +static void __print_reference( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + if( pkg->group ) { fprintf( stdout, "reference: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); } + else { fprintf( stdout, "reference: %s=%s\n", pkg->name, pkg->version ); } + } +} + +void package_print_references( struct package *package ) +{ + if( !package ) return; + + if( package->references->list ) + { + dlist_foreach( package->references->list, __print_reference, NULL ); + } +} + +static void __print_required( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + if( pkg->group ) { fprintf( stdout, "required: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); } + else { fprintf( stdout, "required: %s=%s\n", pkg->name, pkg->version ); } + } +} + +void package_print_requires( struct package *package ) +{ + if( !package ) return; + + if( package->requires->list ) + { + dlist_foreach( package->requires->list, __print_required, NULL ); + } +} + +static void __print_file( void *data, void *user_data ) +{ + int *counter = (int *)user_data; + + if( counter ) { fprintf( stdout, "file[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } + else { fprintf( stdout, "file: %s\n", (char *)data ); } +} + +void package_print_files( struct package *package ) +{ + int cnt = 0; + + if( !package ) return; + + if( package->files->list ) + { + dlist_foreach( package->files->list, __print_file, (void *)&cnt ); + } +} + +/* + End of PACKAGES functions. + ***************************************************************/ + +/*************************************************************** + Extern REQUIRES list functions: + */ + +static int __compare_required( const void *a, const void *b ) +{ + int ret = -1; + + struct pkg *pkg1 = (struct pkg *)a; + struct pkg *pkg2 = (struct pkg *)b; + + if( pkg1->group && pkg2->group ) + { + ret = strcmp( pkg1->group, pkg2->group ); + } + else if( !pkg1->group && !pkg2->group ) + { + ret = 0; + } + else if( pkg1->group ) + { + ret = 1; + } + + if( ! ret ) + { + return strcmp( pkg1->name, pkg2->name ); + } + return ret; +} + +static int __compare_required_with_version( const void *a, const void *b ) +{ + int ret = -1; + + struct pkg *pkg1 = (struct pkg *)a; + struct pkg *pkg2 = (struct pkg *)b; + + if( pkg1->group && pkg2->group ) + { + ret = strcmp( pkg1->group, pkg2->group ); + } + else if( !pkg1->group && !pkg2->group ) + { + ret = 0; + } + else if( pkg1->group ) + { + ret = 1; + } + + if( ! ret ) + { + ret = strcmp( pkg1->name, pkg2->name ); + if( ! ret ) + { + return cmp_version( (const char *)pkg1->version, (const char *)pkg2->version ); + } + } + return ret; +} + +static void __add_unique_required( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + struct dlist *found = dlist_find_data( extern_requires, __compare_required, (const void *)data ); + + if( found ) + { + if( cmp_version( (const char *)((struct pkg *)found->data)->version, (const char *)pkg->version ) ) + { + char *s = ((struct pkg *)found->data)->version; + ((struct pkg *)found->data)->version = + xstrdup( (const char *)max_version( (const char *)((struct pkg *)found->data)->version, + (const char *)pkg->version ) ); + free( s ); + } + } + else + { + struct pkg *req = pkg_alloc(); + if( req ) + { + if( pkg->group ) + { + req->group = xstrdup( (const char *)pkg->group ); + } + req->name = xstrdup( (const char *)pkg->name ); + req->version = xstrdup( (const char *)pkg->version ); + + extern_requires = dlist_append( extern_requires, (void *)req ); + } + } + } +} + +static void __fill_extern_requires( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + + if( package ) + { + struct pkg *provide = pkg_alloc(); + + if( provide ) + { + if( package->pkginfo->group ) + { + provide->group = xstrdup( (const char *)package->pkginfo->group ); + } + provide->name = xstrdup( (const char *)package->pkginfo->name ); + provide->version = xstrdup( (const char *)package->pkginfo->version ); + + provides = dlist_append( provides, (void *)provide ); + } + + if( package->requires->list ) + { + dlist_foreach( package->requires->list, __add_unique_required, NULL ); + } + } +} + +static void __clean_extern_requires( void *data, void *user_data ) +{ + if( data ) + { + extern_requires = dlist_remove_data( extern_requires, __compare_required_with_version, __pkg_free_func, (const void *)data ); + } +} + +static int __compare_provided_old_package( const void *a, const void *b ) +{ + int ret = -1; + + struct package *pkg1 = (struct package *)a; + struct pkg *pkg2 = (struct pkg *)b; + + if( pkg1->pkginfo->group && pkg2->group ) + { + ret = strcmp( pkg1->pkginfo->group, pkg2->group ); + } + else if( !pkg1->pkginfo->group && !pkg2->group ) + { + ret = 0; + } + else if( pkg1->pkginfo->group ) + { + ret = 1; + } + + if( ! ret ) + { + ret = strcmp( pkg1->pkginfo->name, pkg2->name ); + if( ! ret ) + { + pkg2->procedure = UPDATE; /* mark as too old */ + return ret; + } + } + return ret; +} + +static void __remove_old_package( void *data, void *user_data ) +{ + packages = dlist_remove_data( packages, __compare_provided_old_package, __package_free_func, (const void *)data ); +} + +static void remove_old_packages( void ) +{ + dlist_foreach( extern_requires, __remove_old_package, NULL ); +} +/* + End of Extern REQUIRES list functions. + ***************************************************************/ + + +/*************************************************************** + Check REQUIRES functions: + */ +static int __compare_provided( const void *a, const void *b ) +{ + int ret = -1; + + struct package *pkg1 = (struct package *)a; + struct pkg *pkg2 = (struct pkg *)b; + + if( pkg1->pkginfo->group && pkg2->group ) + { + ret = strcmp( pkg1->pkginfo->group, pkg2->group ); + } + else if( !pkg1->pkginfo->group && !pkg2->group ) + { + ret = 0; + } + else if( pkg1->pkginfo->group ) + { + ret = 1; + } + + if( ! ret ) + { + return strcmp( pkg1->pkginfo->name, pkg2->name ); + } + return ret; +} + +static int __compare_packages_by_name( const void *a, const void *b ) +{ + int ret = -1; + + struct package *pkg1 = (struct package *)a; + struct package *pkg2 = (struct package *)b; + + if( !strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name ) ) + { + if( pkg1->pkginfo->group && pkg2->pkginfo->group ) + { + ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group ); + } + else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group ) + { + ret = 0; + } + else if( pkg1->pkginfo->group ) + { + ret = 1; + } + + /* returns equal only if groups are not equal */ + if( ret ) return 0; + } + + return ret; +} + +static int check_dependencies( struct package *package ) +{ + struct dlist *list = NULL, *next = NULL, *update = NULL; + int depended = -1; + + if( !package ) return depended; + depended = 0; + + if( !(list = package->requires->list) ) return depended; + + while( list ) + { + next = dlist_next( list ); + { + int has_extern_dependencies = 0, already_provided = 0; + + struct pkg *pkg = (struct pkg *)list->data; + struct dlist *found = dlist_find_data( extern_requires, __compare_required, (const void *)pkg ); + + if( found ) + { + if( cmp_version( (const char *)((struct pkg *)found->data)->version, (const char *)pkg->version ) >= 0 ) + { + /* required package is found in the extern_requires list */ + has_extern_dependencies += 1; + } + } + + found = dlist_find_data( provides, __compare_provided, (const void *)pkg ); + if( found ) + { + if( cmp_version( (const char *)((struct package *)found->data)->pkginfo->version, (const char *)pkg->version ) >= 0 ) + { + /* required package is found in the extern_requires list */ + already_provided += 1; + } + } + + if( !already_provided && !has_extern_dependencies ) depended += 1; + } + list = next; + } + + /* Check if the package with the same name already exists in the provides list */ + update = dlist_find_data( provides, __compare_packages_by_name, (const void *)package ); + if( update ) + { + /* Set install procedure to UPDATE: */ + package->procedure = UPDATE; + } + + return depended; +} +/* + End of Check REQUIRES functions. + ***************************************************************/ + +static void __fill_provides_list( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + + if( package ) + { + if( !check_dependencies( package ) ) + { + /* move independed package to the provides list */ + packages = dlist_remove( packages, (const void *)data ); + provides = dlist_append( provides, (void *)package ); + } + } +} + + +static void __print_extern_package( void *data, void *user_data ) +{ + FILE *output = (FILE *)user_data; + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + if( pkg->group ) { fprintf( output, "# required: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); } + else { fprintf( output, "# required: %s=%s\n", pkg->name, pkg->version ); } + } +} + +static void __print_provided_package( void *data, void *user_data ) +{ + FILE *output = (FILE *)user_data; + struct package *package = (struct package *)data; + + if( package ) + { + fprintf( output, "%s:", package->pkginfo->name ); + fprintf( output, "%s:", package->pkginfo->version ); + fprintf( output, "%s:", package->pkginfo->short_description ); + if( package->tarball ) + { + fprintf( output, "%s:", package->tarball ); + } + else + { + if( package->pkginfo->group ) fprintf( output, "%s/", package->pkginfo->group ); + + fprintf( output, "%s-", package->pkginfo->name ); + fprintf( output, "%s-", package->pkginfo->version ); + fprintf( output, "%s-", package->pkginfo->arch ); + fprintf( output, "%s-", package->pkginfo->distro_name ); + fprintf( output, "%s.", package->pkginfo->distro_version ); + fprintf( output, "%s:", tarball_suffix ); /* default is '.txz' */ + } + fprintf( output, "%s:", strproc( package->procedure ) ); + fprintf( output, "%s\n", strprio( package->priority, 0 ) ); + } +} + + +static void __reduce_packages_list( struct pkg *pkg ) +{ + struct package *package = NULL; + struct dlist *found = NULL; + + if( !pkg ) return; + + found = dlist_find_data( packages, __compare_provided, (const void *)pkg ); + if( found && found->data ) + { + struct dlist *list = NULL, *next = NULL; + + package = (struct package *)found->data; + + packages = dlist_remove( packages, (const void *)package ); + provides = dlist_append( provides, (void *)package ); + + if( !(list = package->requires->list) ) return; + + while( list ) + { + next = dlist_next( list ); + { + __reduce_packages_list( (struct pkg *)list->data ); + } + list = next; + } + } +} + +static void __reduce_packages_list_single( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) { __reduce_packages_list( pkg ); } +} + + +static void reduce_packages_list( struct dlist *srcpkgs ) +{ + if( ! srcpkgs ) return; + + dlist_foreach( srcpkgs, __reduce_packages_list_single, NULL ); + + dlist_free( packages, __package_free_func ); + if( dlist_length( provides ) != 0 ) + { + packages = provides; + provides = NULL; + } +} + +int create_provides_list( struct dlist *srcpkgs ) +{ + int ret = 0; + + if( !packages ) return ret; + + if( srcpkgs && dlist_length( srcpkgs ) > 0 ) + { + /****************************************************************** + Reduce packages list to the list of requires of source packages: + */ + reduce_packages_list( srcpkgs ); + } + + /* Fill two lists: provides and extern_requires: */ + dlist_foreach( packages, __fill_extern_requires, NULL ); + + /* Remove packages from extern_requires list which present in the provides list: */ + dlist_foreach( provides, __clean_extern_requires, NULL ); + + /* Now we don't need previous contents of provides list: */ + dlist_free( provides, __pkg_free_func ); + provides = NULL; + + /* Remove old packages if required new version of them */ + remove_old_packages(); + + /* move packages into provides list in order of installation: */ + while( dlist_length( packages ) != 0 ) + { + dlist_foreach( packages, __fill_provides_list, NULL ); + } + + return dlist_length( extern_requires ); +} + +void free_provides_list( void ) +{ + if( htmlroot ) { free( htmlroot ); htmlroot = NULL; } + if( hardware ) { free( hardware ); hardware = NULL; } + + dlist_free( extern_requires, __pkg_free_func ); + dlist_free( provides, __package_free_func ); +} + +void print_provides_list( const char *plist_fname ) +{ + FILE *plist = NULL; + + if( !plist_fname || !provides ) return; + + plist = fopen( plist_fname, "w" ); + if( !plist ) + { + FATAL_ERROR( "Cannot create output %s file", basename( (char *)plist_fname ) ); + } + + fprintf( plist, "#\n" ); + fprintf( plist, "# file format:\n" ); + fprintf( plist, "# ===========\n" ); + fprintf( plist, "#\n" ); + fprintf( plist, "# Each line contains six fields separated by colon symbol ':' like following.\n" ); + fprintf( plist, "#\n" ); + fprintf( plist, "# pkgname:version:description:tarball:procedure:priority\n" ); + fprintf( plist, "#\n" ); + fprintf( plist, "# where:\n" ); + fprintf( plist, "#\n" ); + fprintf( plist, "# pkgname - should be the same as the value of pkgname in the '.DESCRIPTION' file;\n" ); + fprintf( plist, "# version - package version for showing in check list dialog box if this file is\n" ); + fprintf( plist, "# used to complete common check dialog for installing group of packages;\n" ); + fprintf( plist, "# description - short description for showing in check list dialog box if this file is\n" ); + fprintf( plist, "# used to complete common check dialog for installing group of packages;\n" ); + fprintf( plist, "# tarball - should end in '.txz';\n" ); + fprintf( plist, "# procedure - installation procedure {install | update}:\n" ); + fprintf( plist, "# * 'install' - if package requires normal installation,\n" ); + fprintf( plist, "# * 'update' - if already installed package should be updated by this\n" ); + fprintf( plist, "# package archive;\n" ); + fprintf( plist, "# priority - { REQUIRED|RECOMMENDED|OPTIONAL|SKIP }\n" ); + fprintf( plist, "# synonims:\n" ); + fprintf( plist, "# { REQUIRED | required | REQ | req }\n" ); + fprintf( plist, "# { RECOMMENDED | recommended | REC | rec }\n" ); + fprintf( plist, "# { OPTIONAL | optional | OPT | opt }\n" ); + fprintf( plist, "# { SKIP | skip | SKP | skp }\n" ); + fprintf( plist, "#\n" ); + + if( extern_requires ) + { + dlist_foreach( extern_requires, __print_extern_package, plist ); + fprintf( plist, "#\n" ); + } + dlist_foreach( provides, __print_provided_package, plist ); + + fflush( plist ); + fclose( plist ); +} + + +/*************************************************************** + Requires TREE functions: + */ + +struct _ctx +{ + FILE *output; + int index, size, depth; +}; + +/************************** + HTML Template Variables: + */ +static char *root = NULL; +static char *bug_url = NULL; + +static int svg_width = 2; +static int svg_height = 2; + +static char *json_pkgs_file = NULL; +static char *json_tree_file = NULL; + +static char *copying = "Radix cross Linux"; + +#define max(a,b) ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; }) + +/* + формирование имен файлов для вывода REQUIRES tree: + + json_fname | last argument of make-pkglist | last argument type + -------------------------+-------------------------------+-------------------- + './a.txt' | a.txt | regular file + './a.json' | a.json | regular file + './.json' | .json | regular file + './khadas-vim.json' | . | directory + './tmp/khadas-vim.json' | tmp | directory + -------------------------+-------------------------------+-------------------- + + - если есть основное базовое имя файла и расширение, то расширение + заменяем на: '.pkgs.json', '.tree.json', '.tree.html'; + + - если есть основное базовое имя файла без расширения, то добавляем + расширение: '.pkgs.json', '.tree.json', '.tree.html'; + + - если основное базовое имя файла начинается с точки, то расширение + заменяем на: 'pkgs.json', 'tree.json', 'tree.html'. +*/ +static void allocate_fnames( const char *json_fname ) +{ + char *p, *e, *f = NULL; + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &buf[0], "%s", json_fname ); + p = rindex( (const char *)&buf[0], '/' ); + if( p ) + { + if( p != &buf[0] ) f = ++p; + else f = &buf[0]; + } + e = rindex( (const char *)f, '.' ); + if( e ) + { + if( e != f ) + { + (void)sprintf( e, ".pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.html" ); html_fname = xstrdup( (const char *)&buf[0] ); + + (void)sprintf( e, ".pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] ); + } + else + { + (void)sprintf( e, "pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, "tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, "tree.html" ); html_fname = xstrdup( (const char *)&buf[0] ); + + (void)sprintf( e, "pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, "tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] ); + } + } + else + { + e = f + strlen( f ); + + (void)sprintf( e, ".pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.html" ); html_fname = xstrdup( (const char *)&buf[0] ); + + (void)sprintf( e, ".pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] ); + (void)sprintf( e, ".tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] ); + } + + if( minimize ) + { + json_pkgs_file = xstrdup( (const char *)basename( pkgs_min_fname ) ); + json_tree_file = xstrdup( (const char *)basename( tree_min_fname ) ); + } + else + { + json_pkgs_file = xstrdup( (const char *)basename( pkgs_fname ) ); + json_tree_file = xstrdup( (const char *)basename( tree_fname ) ); + } + + free( buf ); +} + + +/******************************************************************* + find_pkg(): + ---------- + Returns package found in packages list coresponded to pkg. + */ +static struct package *find_pkg( struct dlist *list, struct pkg *pkg ) +{ + struct package *package = NULL; + struct dlist *found = NULL; + + if( !pkg ) return package; + + found = dlist_find_data( list, __compare_provided, (const void *)pkg ); + if( found ) + { + return (struct package *)found->data; + } + + return package; +} + +/******************************************************************* + find_package(): + -------------- + Returns package found in packages list coresponded to package. + */ +static struct package *find_package( struct dlist *list, struct package *pkg ) +{ + struct package *package = NULL; + struct dlist *found = NULL; + + if( !pkg ) return package; + + found = dlist_find_data( list, __compare_packages, (const void *)pkg ); + if( found ) + { + return (struct package *)found->data; + } + + return package; +} + +static void __print_package_data( FILE *output, struct package *package ) +{ + if( !output || !package ) return; + + /* "id": "net:bind-9.10.1", */ + if( package->pkginfo->group ) { + fprintf( output, " \"id\": \"%s:%s-%s\",\n", package->pkginfo->group, + package->pkginfo->name, + package->pkginfo->version ); + } else { + fprintf( output, " \"id\": \"%s-%s\",\n", package->pkginfo->name, + package->pkginfo->version ); + } + /* "name": "bind", */ + fprintf( output, " \"name\": \"%s\",\n", package->pkginfo->name ); + /* "version": "9.10.1", */ + fprintf( output, " \"version\": \"%s\",\n", package->pkginfo->version ); + /* "group": "net", */ + if( package->pkginfo->group ) { + fprintf( output, " \"group\": \"%s\",\n", package->pkginfo->group ); + } else { + fprintf( output, " \"group\": \"\",\n" ); + } + /* "arch": "omap543x-eglibc", */ + fprintf( output, " \"arch\": \"%s\",\n", package->pkginfo->arch ); + /* "hardware": "omap5uevm", */ + fprintf( output, " \"hardware\": \"%s\",\n", hardware ); + /* "license": "custom", */ + fprintf( output, " \"license\": \"%s\",\n", package->pkginfo->license ); + /* "description": "bind 9.10.1 (DNS server and utilities)", */ + fprintf( output, " \"description\": \"%s %s (%s)\",\n", package->pkginfo->name, + package->pkginfo->version, + package->pkginfo->short_description ); + /* "uncompressed_size": "17M", */ + fprintf( output, " \"uncompressed_size\": \"" ); + if( package->pkginfo->uncompressed_size > 1048576 ) { + fprintf( output, "%ldG\",\n", package->pkginfo->uncompressed_size / 1048576 ); + } else if( package->pkginfo->uncompressed_size > 1024 ) { + fprintf( output, "%ldM\",\n", package->pkginfo->uncompressed_size / 1024 ); + } else { + fprintf( output, "%ldK\",\n", package->pkginfo->uncompressed_size ); + } + /* "total_files": "421" */ + fprintf( output, " \"total_files\": \"%d\"\n", package->pkginfo->total_files ); +} + +static void __print_pkgs_node( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + struct _ctx *ctx = (struct _ctx *)user_data; + + if( !package || !ctx ) return; + + if( ctx->index != 0 ) + { + fprintf( ctx->output, " },\n {\n" ); + } + __print_package_data( ctx->output, package ); + ++ctx->index; +} + +static void print_pkgs_json( FILE *output, struct dlist *list ) +{ + struct _ctx ctx; + + if( !output ) return; + + bzero( (void *)&ctx, sizeof(struct _ctx) ); + + ctx.output = output; + ctx.index = 0; + + fprintf( output, "[{\n" ); + + dlist_foreach( list, __print_pkgs_node, (void *)&ctx ); + + fprintf( output, " }]\n" ); +} + +static void __remove_required_package( void *data, void *user_data ) +{ + struct package *package = NULL; + struct pkg *pkg = (struct pkg *)data; + + if( pkg ) + { + package = find_pkg( tree, pkg ); + if( package ) + { + /******************************************* + if package reqired for some other package + we have to remove it from tree list: + */ + tree = dlist_remove_data( tree, __compare_packages, NULL, (const void *)package ); + } + } +} + +static void __remove_required_packages( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + struct dlist *list = NULL; + + if( !package ) return; + + if( !(list = package->requires->list) ) return; + + dlist_foreach( list, __remove_required_package, NULL ); +} + +static void remove_required_packages( struct dlist *list ) +{ + dlist_foreach( list, __remove_required_packages, NULL ); +} + + +static void __check_pkg_requires( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + int *counter = (int *)user_data; + + if( pkg ) + { + struct package *package = find_pkg( provides, pkg ); + if( package ) { ++(*counter); } + } +} + +static int check_pkg_requires( struct dlist *list ) +{ + int cnt = 0; + dlist_foreach( list, __check_pkg_requires, (void *)&cnt ); + return cnt; +} + + +/*************************************************************** + Sort Requires Tree functions: + ---------------------------- + + NOTE: + Requires sorted in reverse installation order according to + provides list. + */ + +static int __install_pkg_index( struct dlist *list, struct pkg *pkg ) +{ + int index = -1; + + if( !pkg ) return index; + + if( list ) + { + struct package *package = find_pkg( list, pkg ); + + index = dlist_index( list, package ); + } + + return index; +} + +static int __install_package_index( struct dlist *list, struct package *package ) +{ + int index = -1; + + if( !package ) return index; + + if( list ) + index = dlist_index( list, package ); + + return index; +} + +static int __compare_pkg_order( const void *a, const void *b ) +{ + int ret = 0; + int ia = -1, ib = -1; + + ia = __install_pkg_index( provides, (struct pkg *)a ); + ib = __install_pkg_index( provides, (struct pkg *)b ); + + if( ia < ib ) ret = -1; + if( ia > ib ) ret = 1; + + return ret; +} + +static int __compare_package_order( const void *a, const void *b ) +{ + int ret = 0; + int ia = -1, ib = -1; + + ia = __install_package_index( provides, (struct package *)a ); + ib = __install_package_index( provides, (struct package *)b ); + + if( ia < ib ) ret = -1; + if( ia > ib ) ret = 1; + + return ret; +} + +static void __sort_requires( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + struct dlist *reqs = NULL; + + if( !package ) return; + + if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 ) + { + package->requires->list = reqs = dlist_sort( reqs, __compare_pkg_order ); + } +} + +static struct dlist *sort_requires_tree( struct dlist *list ) +{ + int lenght = 0; + + if( !list ) return list; + + lenght = dlist_length( list ); + + if( lenght > 1 ) + list = dlist_sort( list, __compare_package_order ); + + return list; +} + +static void sort_requires( struct dlist *list ) +{ + int lenght = 0; + + if( !list ) return; + + dlist_foreach( list, __sort_requires, NULL ); +} +/* + End of Sort Requires Tree functions: + ***************************************************************/ + +/*************************************************************** + Binary Tree functions: + --------------------- + */ +static struct dlist *pkgs = NULL; +static struct btree *btree = NULL; + +static struct pkg *duplicate_pkg( struct package *package ) +{ + struct pkg *pkg = NULL; + + if( !package ) return pkg; + + pkg = pkg_alloc(); + pkg->name = xstrdup( (const char *)package->pkginfo->name ); + pkg->group = xstrdup( (const char *)package->pkginfo->group ); + pkg->version = xstrdup( (const char *)package->pkginfo->version ); + pkg->procedure = package->procedure; + + return pkg; +} + +static void __fill_pkgs_list( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + struct pkg *pkg = NULL; + + if( !package ) return; + + pkg = duplicate_pkg( package ); + pkgs = dlist_append( pkgs, (void *)pkg ); +} + +/******************************************* + create_pkgs_list(): + ------------------ + Creates pkgs list from provides list. + */ +static void create_pkgs_list( struct dlist *list ) +{ + if( !list ) return; + + dlist_foreach( list, __fill_pkgs_list, NULL ); +} + +static void free_pkgs_list() +{ + if( pkgs ) + { + dlist_free( pkgs, __pkg_free_func ); + pkgs = NULL; + } +} + +static int __compare_pkg( const void *a, const void *b ) +{ + int ret = -1; + + struct pkg *pkg1 = (struct pkg *)a; + struct pkg *pkg2 = (struct pkg *)b; + + if( pkg1->group && pkg2->group ) + { + ret = strcmp( pkg1->group, pkg2->group ); + } + else if( !pkg1->group && !pkg2->group ) + { + ret = 0; + } + else if( pkg1->group ) + { + ret = 1; + } + + if( ! ret ) + { + return strcmp( pkg1->name, pkg2->name ); + } + return ret; +} + +static struct pkg *__find_pkg( struct dlist *list, struct pkg *package ) +{ + struct pkg *pkg = NULL; + struct dlist *found = NULL; + + if( !package ) return pkg; + + found = dlist_find_data( list, __compare_pkg, (const void *)package ); + if( found ) + { + return (struct pkg *)found->data; + } + + return pkg; +} + +static int __compare_pkg_with_package( const void *a, const void *b ) +{ + int ret = -1; + + struct pkg *pkg1 = (struct pkg *)a; + struct package *pkg2 = (struct package *)b; + + if( pkg1->group && pkg2->pkginfo->group ) + { + ret = strcmp( pkg1->group, pkg2->pkginfo->group ); + } + else if( !pkg1->group && !pkg2->pkginfo->group ) + { + ret = 0; + } + else if( pkg2->pkginfo->group ) + { + ret = 1; + } + + if( ! ret ) + { + return strcmp( pkg1->name, pkg2->pkginfo->name ); + } + return ret; +} + +static struct pkg *__find_pkg_by_package( struct dlist *list, struct package *package ) +{ + struct pkg *pkg = NULL; + struct dlist *found = NULL; + + if( !package ) return pkg; + + found = dlist_find_data( list, __compare_pkg_with_package, (const void *)package ); + if( found ) + { + return (struct pkg *)found->data; + } + + return pkg; +} + +static struct pkg *__find_pkg_by_pkg( struct dlist *list, struct pkg *package ) +{ + struct pkg *pkg = NULL; + struct dlist *found = NULL; + + if( !package ) return pkg; + + found = dlist_find_data( list, __compare_pkg, (const void *)package ); + if( found ) + { + return (struct pkg *)found->data; + } + + return pkg; +} + +static void __btree_add_requires( struct btree *tree ); + +static struct btree *__btree_add_left( void *data, void *user_data ) +{ + struct btree *tree = (struct btree *)user_data; + struct pkg *left = (struct pkg *)data; + struct pkg *pkg = NULL; + struct btree *node = NULL; + + if( !tree || !left ) return node; + + pkg = __find_pkg_by_pkg( pkgs, left ); + + node = btree_insert_left( tree, __btree_alloc( (void *)pkg ) ); + __btree_add_requires( node ); + + return node; +} + +static void __btree_add_right( void *data, void *user_data ) +{ + struct btree *tree = *((struct btree **)user_data); + struct pkg *right = (struct pkg *)data; + struct pkg *pkg = NULL; + struct btree *node = NULL; + + if( !tree || !right ) return; + + pkg = __find_pkg_by_pkg( pkgs, right ); + + node = btree_insert_right( tree, __btree_alloc( (void *)pkg ) ); + __btree_add_requires( node ); + + *((struct btree **)user_data) = node; +} + +static void __btree_add_requires( struct btree *tree ) +{ + struct package *package = NULL; + + if( !tree ) return; + + package = find_pkg( provides, (struct pkg *)tree->data ); + if( package ) + { + struct dlist *list = NULL; + + if( (list = package->requires->list) ) + { + struct pkg *pkg = NULL; + struct btree *node = NULL; + + pkg = __find_pkg_by_pkg( pkgs, (struct pkg *)list->data ); + + node = __btree_add_left( (void *)pkg, (void *)tree ); + + if( dlist_length( list ) > 1 ) + { + dlist_foreach( list->next, __btree_add_right, (void *)&node ); + } + } + } +} + +static void __fill_btree( void *data, void *user_data ) +{ + struct btree *tree = *((struct btree **)user_data); + struct package *package = (struct package *)data; + struct pkg *pkg = NULL; + struct btree *node = NULL; + + if( !tree || !package ) return; + + pkg = __find_pkg_by_package( pkgs, package ); + + node = btree_insert_right( tree, __btree_alloc( (void *)pkg ) ); + __btree_add_requires( node ); + + *((struct btree **)user_data) = node; +} + +/******************************************************************* + __print_btree_pkg(): + ------------------- + Print out package "group/name-version". Used for debuging only. + */ +static void __print_btree_pkg( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( !pkg ) return; + + fprintf( stdout, "%s/%s-%s\n", pkg->group, pkg->name, pkg->version ); +} + +/***************************************************************** + __print_btree_node(): + -------------------- + Print out package in JSON format. Used by btree_print_json(). + */ +static void __print_btree_node( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + struct _bctx *ctx = (struct _bctx *)user_data; + + char *p, buf[PATH_MAX*2]; + int depth = 0, max_depth = PATH_MAX + PATH_MAX / 2; + + if( !pkg || !ctx ) return; + + buf[0] = ' '; + buf[1] = '\0'; + + p = (char *)&buf[1]; + depth = ctx->indent; + + if( depth < 1 ) depth = 0; + if( depth > max_depth ) depth = max_depth; + + while( depth ) + { + (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; + } + + if( pkg->group ) + (void)sprintf( p, "\"name\": \"%s:%s-%s\"", pkg->group, + pkg->name, + pkg->version ); + else + (void)sprintf( p, "\"name\": \"%s-%s\"", pkg->name, + pkg->version ); + + fprintf( ctx->output, (char *)&buf[0] ); +} + +static int __width_factor( int width ) +{ + double w, x = (double)width; + + if( width < 2 ) return 48; + + if( width > 1 && width < 150 ) + { + w = floor( (38.399 - 7.4262 * log( x )) + 8.0 ); + + return (int)w; + } + else + { + return 8; + } +} + +/******************************************************************* + __print_btree_header(): + ---------------------- + Print out Binary Tree Header. + */ +static void __print_btree_header( FILE *fp, struct btree *btree, struct dlist *tree ) +{ + struct package *package = NULL; + + if( !fp || !btree || !tree ) return; + + package = find_pkg( provides, (struct pkg *)btree->data ); + + if( package ) + { + char *buf = NULL; + + if( dlist_length( tree ) > 1 ) + { + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &buf[0], "%s", htmlroot ); + root = xstrdup( (const char *)&buf[0] ); + (void)sprintf( &buf[0], "%s", package->pkginfo->url ); + bug_url = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( package->pkginfo->group ) + (void)sprintf( &buf[0], "%s/%s-%s", package->pkginfo->group, + package->pkginfo->name, + package->pkginfo->version ); + else + (void)sprintf( &buf[0], "%s-%s", package->pkginfo->name, + package->pkginfo->version ); + + root = xstrdup( (const char *)&buf[0] ); + (void)sprintf( &buf[0], "%s", package->pkginfo->url ); + bug_url = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + + fprintf( fp, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n", + package->pkginfo->distro_name, + package->pkginfo->distro_version, + package->pkginfo->url ); + } +} + + +/*************************************** + create_btree(): + -------------- + Creates btree from tree list (DAG). + */ +static void create_btree( struct dlist *dag, struct dlist *install ) +{ + struct pkg *pkg = NULL; + struct package *package = NULL; + struct dlist *list = NULL; + struct btree *node = NULL; + + if( !dag || !install ) return; + + /* first package: */ + package = (struct package *)dag->data; + pkg = __find_pkg_by_package( pkgs, package ); + + node = btree = __btree_alloc( (void *)pkg ); + + __btree_add_requires( node ); + + if( dlist_length( dag ) > 1 ) + dlist_foreach( dag->next, __fill_btree, (void *)&node ); + + btree_reduce( btree, __compare_pkg, NULL ); +} +/* + End of Binary Tree functions: + ***************************************************************/ + + +/*************************************************************** + Print json format of DAG functions: + */ +static void __print_pkg_tree( struct _ctx *ctx, struct dlist *list ) +{ + struct dlist *next = NULL; + + if( !ctx || !list ) return; + + ctx->depth += 2; + svg_width = max( svg_width, ctx->depth ); + + while( list ) + { + next = dlist_next( list ); + { + struct pkg *pkg = (struct pkg *)list->data; + struct package *package = find_pkg( provides, pkg ); + + if( package ) + { + char *p, *buf = NULL; + int depth = 0; + + struct dlist *reqs = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + buf[0] = ' '; + buf[1] = '\0'; + + p = (char *)&buf[1]; + depth = ctx->depth; + + while( depth ) { (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; } + + (void)sprintf( p - 1, "{\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + *(p - 1) = ' '; *p = '\0'; + + if( pkg->group ) + (void)sprintf( p, "\"name\": \"%s:%s-%s\"", pkg->group, + pkg->name, + pkg->version ); + else + (void)sprintf( p, "\"name\": \"%s-%s\"", pkg->name, + pkg->version ); + + fprintf( ctx->output, (char *)&buf[0] ); + + if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 ) + { + fprintf( ctx->output, ",\n" ); + + (void)sprintf( p, "\"children\": [\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + + __print_pkg_tree( ctx, reqs ); + + (void)sprintf( p, "]\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + } + else + { + fprintf( ctx->output, "\n" ); + } + + (void)sprintf( p - 1, "}" ); + fprintf( ctx->output, (char *)&buf[0] ); + *(p - 1) = ' '; *p = '\0'; + + if( next ) { fprintf( ctx->output, ",\n" ); } + else { fprintf( ctx->output, "\n" ); } + + free( buf ); + } /* End if( package ) */ + } + list = next; + } /* End of while( list ) */ + + ctx->depth -= 2; +} + +static void __print_package_node( struct _ctx *ctx, struct package *package ) +{ + char *p, *buf = NULL; + int depth = 0; + + struct dlist *list = NULL; + + if( !package || !ctx ) return; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + buf[0] = ' '; + buf[1] = '\0'; + + p = (char *)&buf[1]; + depth = ctx->depth; + + while( depth ) { (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; } + + (void)sprintf( p - 1, "{\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + *(p - 1) = ' '; *p = '\0'; + + if( package->pkginfo->group ) + (void)sprintf( p, "\"name\": \"%s:%s-%s\"", package->pkginfo->group, + package->pkginfo->name, + package->pkginfo->version ); + else + (void)sprintf( p, "\"name\": \"%s-%s\"", package->pkginfo->name, + package->pkginfo->version ); + + fprintf( ctx->output, (char *)&buf[0] ); + + if( (list = package->requires->list) && check_pkg_requires( list ) > 0 ) + { + fprintf( ctx->output, ",\n" ); + + (void)sprintf( p, "\"children\": [\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + + __print_pkg_tree( ctx, list ); + + (void)sprintf( p, "]\n" ); + fprintf( ctx->output, (char *)&buf[0] ); + } + else + { + fprintf( ctx->output, "\n" ); + } + + (void)sprintf( p - 1, "}" ); + fprintf( ctx->output, (char *)&buf[0] ); + *(p - 1) = ' '; *p = '\0'; + + free( buf ); +} + +static void __print_tree_node( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + struct _ctx *ctx = (struct _ctx *)user_data; + + if( !package || !ctx ) return; + + if( ctx->size > 1 ) + { + if( ctx->index == 0 ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &buf[0], "%s", htmlroot ); + root = xstrdup( (const char *)&buf[0] ); + (void)sprintf( &buf[0], "%s", package->pkginfo->url ); + bug_url = xstrdup( (const char *)&buf[0] ); + free( buf ); + + fprintf( ctx->output, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n", + package->pkginfo->distro_name, + package->pkginfo->distro_version, + package->pkginfo->url ); + fprintf( ctx->output, " \"name\": \"%s\",\n", htmlroot ); + fprintf( ctx->output, " \"children\": [\n" ); + } + + + __print_package_node( ctx, package ); + svg_height += 2; + + + if( ctx->index < ctx->size - 1 ) fprintf( ctx->output, "," ); + else fprintf( ctx->output, "\n ]" ); + + fprintf( ctx->output, "\n" ); + } + else + { + struct dlist *reqs = NULL; + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( package->pkginfo->group ) + (void)sprintf( &buf[0], "%s/%s-%s", package->pkginfo->group, + package->pkginfo->name, + package->pkginfo->version ); + else + (void)sprintf( &buf[0], "%s-%s", package->pkginfo->name, + package->pkginfo->version ); + + root = xstrdup( (const char *)&buf[0] ); + (void)sprintf( &buf[0], "%s", package->pkginfo->url ); + bug_url = xstrdup( (const char *)&buf[0] ); + free( buf ); + + fprintf( ctx->output, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n", + package->pkginfo->distro_name, + package->pkginfo->distro_version, + package->pkginfo->url ); + if( package->pkginfo->group ) + fprintf( ctx->output, " \"name\": \"%s:%s-%s\"", package->pkginfo->group, + package->pkginfo->name, + package->pkginfo->version ); + else + fprintf( ctx->output, " \"name\": \"%s-%s\"", package->pkginfo->name, + package->pkginfo->version ); + + + svg_height += 2; + ctx->depth -=2; + + if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 ) + { + fprintf( ctx->output, ",\n" ); + + fprintf( ctx->output, " \"children\": [\n" ); + + __print_pkg_tree( ctx, reqs ); + + fprintf( ctx->output, " ]\n" ); + } + + } + + ++ctx->index; +} + +static void print_tree_json( FILE *output, struct dlist *list ) +{ + struct _ctx ctx; + + if( !output || !list ) return; + + bzero( (void *)&ctx, sizeof(struct _ctx) ); + + ctx.output = output; + ctx.index = 0; + ctx.size = dlist_length( list ); + ctx.depth = 2; + + fprintf( output, "{\n" ); + dlist_foreach( list, __print_tree_node, (void *)&ctx ); + fprintf( output, "}\n" ); + + svg_height += svg_width / 2; + + svg_width = (svg_width + 4) * 160; + svg_height = (svg_height + 4) * 24; +} +/* + End of print json format of DAG functions. + ***************************************************************/ + +#include + +void print_provides_tree( const char *json_fname, enum _tree_format tree_format ) +{ + FILE *pkgs_fp = NULL, *tree_fp = NULL, *html_fp = NULL; + + allocate_fnames( json_fname ); + + pkgs_fp = fopen( (const char *)pkgs_fname, "w" ); + if( !pkgs_fp ) { FATAL_ERROR( "Cannot create %s file", basename( pkgs_fname ) ); } + tree_fp = fopen( (const char *)tree_fname, "w" ); + if( !tree_fp ) { FATAL_ERROR( "Cannot create %s file", basename( tree_fname ) ); } + html_fp = fopen( (const char *)html_fname, "w" ); + if( !html_fp ) { FATAL_ERROR( "Cannot create %s file", basename( html_fname ) ); } + + tree = dlist_copy( provides ); + + /***************************************************** + print out the array of all packages in JSON format: + */ + print_pkgs_json( pkgs_fp, provides ); + fflush( pkgs_fp ); fclose( pkgs_fp ); + + provides = dlist_reverse( provides ); + sort_requires( provides ); /* sort requires in reverse installation order */ + + /******************************************************** + Sort the REQUIRES TREE in reverse installation order. + */ + tree = sort_requires_tree( tree ); + + /******************************************************** + remove unneded packages from tree list to leave the + last installation layer of packages presented in DAG: + */ + remove_required_packages( provides ); + + + if( tree_format == TFMT_BIN ) + { + int width = 0, height = 0; + + /******************************************************************** + print out the REQUIRES TREE in JSON format as reduced binary tree: + */ + create_pkgs_list( provides ); + create_btree( tree, provides ); + + width = btree_width( btree ); + height = btree_height( btree ); + + svg_width = (height + 4) * 240; + svg_height = (width + 4) * __width_factor( width ); + + fprintf( tree_fp, "{\n" ); + __print_btree_header( tree_fp, btree, tree ); + btree_print_json( tree_fp, btree, __print_btree_node ); + fprintf( tree_fp, "}\n" ); + + __btree_free( btree ); + free_pkgs_list(); + } + else + { + /**************************************************** + print out the REQUIRES TREE in JSON format as DAG: + */ + print_tree_json( tree_fp, tree ); + } + + fflush( tree_fp ); fclose( tree_fp ); + + if( minimize ) + { + if( minimize_json( (const char *)pkgs_fname, (const char *)pkgs_min_fname ) < 1 ) + { + (void)unlink( (const char *)pkgs_min_fname ); + } + if( minimize_json( (const char *)tree_fname, (const char *)tree_min_fname ) < 1 ) + { + (void)unlink( (const char *)tree_min_fname ); + } + } + + + /*********************************************** + print out the HTML to view REQIIRES TREE: + */ + print_tree_html( html_fp ); + fflush( html_fp ); fclose( html_fp ); + + + /***************** + free resources: + */ + if( root ) { free( root ); root = NULL; } + if( bug_url ) { free( bug_url ); bug_url = NULL; } + + if( pkgs_fname ) { free( pkgs_fname ); pkgs_fname = NULL; } + if( tree_fname ) { free( tree_fname ); tree_fname = NULL; } + if( html_fname ) { free( html_fname ); html_fname = NULL; } + + if( pkgs_min_fname ) { free( pkgs_min_fname ); pkgs_min_fname = NULL; } + if( tree_min_fname ) { free( tree_min_fname ); tree_min_fname = NULL; } + + if( json_pkgs_file ) { free( json_pkgs_file ); json_pkgs_file = NULL; } + if( json_tree_file ) { free( json_tree_file ); json_tree_file = NULL; } + + __dlist_free( tree ); /* do not free node data */ +} +/* + End of Requires TREE functions. + ***************************************************************/ diff --git a/src/pkglist.h b/src/pkglist.h new file mode 100644 index 0000000..2688a70 --- /dev/null +++ b/src/pkglist.h @@ -0,0 +1,169 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _PKG_LIST_H_ +#define _PKG_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +enum _tree_format { + TFMT_BIN = 0, + TFMT_DAG, + + TFMT_UNKNOWN +}; + +enum _procedure +{ + INSTALL = 0, /* 'install' */ + UPDATE /* 'update' */ +}; + +enum _priority +{ + REQUIRED = 0, /* synonims: REQUIRED | required | REQ | req */ + RECOMMENDED, /* synonims: RECOMMENDED | recommended | REC | rec */ + OPTIONAL, /* synonims: OPTIONAL | optional | OPT | opt */ + SKIP /* synonims: SKIP | skip | SKP | skp */ +}; + + +struct pkginfo +{ + char *name; + char *version; + char *arch; + char *distro_name; + char *distro_version; + char *group; + char *short_description; + char *url; + char *license; + size_t uncompressed_size; /* size in 1024-byte blocks */ + size_t compressed_size; /* size in bytes */ + int total_files; +}; + +struct pkg +{ + char *group; + char *name; + char *version; + + enum _procedure procedure; /* install procedure */ +}; + +struct references +{ + int size; + struct dlist *list; /* list of pkg structs */ +}; + +struct requires +{ + int size; + struct dlist *list; /* list of pkg structs */ +}; + +struct files +{ + int size; + struct dlist *list; /* list of strings */ +}; + + +struct package +{ + struct pkginfo *pkginfo; + + char *hardware; /* optional parameter for JSON */ + + char *tarball; + enum _procedure procedure; /* install procedure */ + enum _priority priority; /* install user priority */ + + struct references *references; + struct requires *requires; + + char *description; + + char *restore_links; + char *install_script; + + struct files *files; +}; + + +extern char *htmlroot; +extern char *hardware; +extern int minimize; + +extern char *strprio( enum _priority priority, int short_name ); +extern char *strproc( enum _procedure procedure ); + +extern struct dlist *tarballs; + +extern void add_tarball( char *tarball ); /* append the tarballs list */ +extern void free_tarballs( void ); +extern const char *find_tarball( const char *name ); +extern void print_tarballs( void ); + + +extern struct dlist *srcpkgs; + +extern struct pkg *pkg_alloc( void ); +extern void pkg_free( struct pkg *pkg ); + +extern void add_srcpkg( struct pkg *pkg ); +extern void free_srcpkgs( void ); + +extern struct dlist *packages; + +extern struct package *package_alloc( void ); +extern void package_free( struct package *package ); + +extern void add_reference( struct package *package, struct pkg *pkg ); +extern void add_required( struct package *package, struct pkg *pkg ); +extern void add_file( struct package *package, const char *fname ); +extern void package_print_references( struct package *package ); +extern void package_print_requires( struct package *package ); +extern void package_print_files( struct package *package ); + +extern void add_package( struct package *package ); /* append the packages list */ +extern void free_packages( void ); + + +extern struct dlist *provides; +extern struct dlist *extern_requires; + +extern int create_provides_list( struct dlist *srcpkgs ); +extern void print_provides_list( const char *plist_fname ); +extern void print_provides_tree( const char *json_fname, enum _tree_format tree_format ); +extern void free_provides_list( void ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _PKG_LIST_H_ */ diff --git a/src/pkglist.html.c b/src/pkglist.html.c new file mode 100644 index 0000000..f4fc8b8 --- /dev/null +++ b/src/pkglist.html.c @@ -0,0 +1,1011 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +static void print_tree_html( FILE *output ) +{ + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + if( !output ) return; + + fprintf( output, "\n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " %s – Requires Tree\n", root ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
%04d-%02d-%02d  %02d:%02d:%02d
\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + fprintf( output, "
\n" ); + fprintf( output, " HARDWARE: %s\n", hardware ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " %s – Requires Tree\n", root ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " \n" ); + fprintf( output, "
Loading ...
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " © %s\n", bug_url, copying ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); +} diff --git a/src/pkglist.html.v3.c b/src/pkglist.html.v3.c new file mode 100644 index 0000000..32dc9f9 --- /dev/null +++ b/src/pkglist.html.v3.c @@ -0,0 +1,992 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +static void print_tree_html( FILE *output ) +{ + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + if( !output ) return; + + fprintf( output, "\n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " %s – Requires Tree\n", hardware ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
%04d-%02d-%02d  %02d:%02d:%02d
\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + fprintf( output, "
\n" ); + fprintf( output, " HARDWARE: %s\n", hardware ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " %s – Requires Tree\n", root ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " \n" ); + fprintf( output, "
Loading ...
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, " © %s\n", bug_url, copying ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "
\n" ); + fprintf( output, "\n" ); + fprintf( output, " \n" ); + fprintf( output, " \n" ); + fprintf( output, "\n" ); +} diff --git a/src/pkglog.c b/src/pkglog.c new file mode 100644 index 0000000..12ab381 --- /dev/null +++ b/src/pkglog.c @@ -0,0 +1,1316 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include + +#define PROGRAM_NAME "pkglog" + +#include + + +char *program = PROGRAM_NAME; +char *destination = NULL, *srcdir = NULL, *pkginfo_fname = NULL, *output_fname = NULL; +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int mkgroupdir = 0; + +int rm_srcdir_at_exit = 0; + +char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *total_files = NULL; + +FILE *pkginfo = NULL; +FILE *output = NULL; + + +#define FREE_PKGLOG_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL + +void free_resources() +{ + if( selfdir ) { free( selfdir ); selfdir = NULL; } + if( srcdir ) { free( srcdir ); srcdir = NULL; } + if( destination ) { free( destination ); destination = NULL; } + if( pkginfo_fname ) { free( pkginfo_fname ); pkginfo_fname = NULL; } + if( output_fname ) + { + if( output ) { (void)fflush( output ); fclose( output ); output = NULL; } + free( output_fname ); output_fname = NULL; + } + + FREE_PKGLOG_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Read information from package's service files and create PKGLOG file\n" ); + fprintf( stdout, "in the destination directory. is a full name of '.PKGINFO'\n" ); + fprintf( stdout, "service file of package. Rest of package's serfice files should be\n" ); + fprintf( stdout, "present in the directory of '.PKGINFO'. If last argument is \n" ); + fprintf( stdout, "then pkglog utility will try to read '.PKGINFO' and rest of service\n" ); + fprintf( stdout, "files from directory given by argument.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -d,--destination= Target directory to save output PKGLOG.\n" ); + fprintf( stdout, " -m,--mkgroupdir Create group subdirectory in the PKGLOG\n" ); + fprintf( stdout, " target directory.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " Directory wich contains the package's\n" ); + fprintf( stdout, " service files or path to .PKGINFO file\n" ); + fprintf( stdout, " or package tarball.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir ); + if( output_fname ) + { + if( output ) { (void)fflush( output ); fclose( output ); output = NULL; } + (void)unlink( output_fname ); + free( output_fname ); output_fname = NULL; + } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir ); + if( output_fname ) + { + if( output ) { (void)fflush( output ); fclose( output ); output = NULL; } + (void)unlink( output_fname ); + free( output_fname ); output_fname = NULL; + } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + +enum _pkginfo_type +{ + PKGINFO_TEXT = 0, + PKGINFO_GZ, + PKGINFO_BZ2, + PKGINFO_XZ, + PKGINFO_TAR, + + PKGINFO_UNKNOWN +}; + +static enum _pkginfo_type pkginfo_type = PKGINFO_UNKNOWN; +static char uncompress[2] = { 0, 0 }; + + +static enum _pkginfo_type check_pkginfo_file( const char *fname ) +{ + struct stat st; + size_t pkginfo_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + uncompress[0] = '\0'; + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkginfo_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) ); + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "pkgname", 7 ) ) + { + close( fd ); return PKGINFO_TEXT; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + uncompress[0] = 'x'; + close( fd ); return PKGINFO_GZ; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + uncompress[0] = 'j'; + close( fd ); return PKGINFO_BZ2; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + uncompress[0] = 'J'; + close( fd ); return PKGINFO_XZ; + } + + if( pkginfo_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return PKGINFO_TAR; + } + } + + close( fd ); return PKGINFO_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvmd:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "destination", required_argument, NULL, 'd' }, + { "mkgroupdir", no_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'd': + { + if( optarg != NULL ) + { + destination = xstrdup( (const char *)optarg ); + remove_trailing_slash( destination ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'm': + { + mkgroupdir = 1; + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + if( destination == NULL ) + { + char cwd[PATH_MAX]; + if( getcwd( cwd, sizeof(cwd) ) != NULL ) + destination = xstrdup( (const char *)cwd ); + else + destination = xstrdup( "." ); + } + + /* last command line argument is the PKGLOG file */ + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + buf = (char *)malloc( strlen( (const char *)argv[optind] ) + 10 ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + (void)strcpy( buf, (const char *)argv[optind++] ); + remove_trailing_slash( (char *)&buf[0] ); + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file: %s", basename( buf ), strerror( errno ) ); + } + + if( S_ISDIR(st.st_mode) ) + { + /* Add .PKGINFO to the input dir name: */ + (void)strcat( buf, "/.PKGINFO" ); + } + + pkginfo_fname = xstrdup( (const char *)&buf[0] ); + if( pkginfo_fname == NULL ) + { + usage(); + } + + free( buf ); + + pkginfo_type = check_pkginfo_file( (const char *)pkginfo_fname ); + if( pkginfo_type == PKGINFO_UNKNOWN ) + { + ERROR( "%s: Unknown input file format", basename( pkginfo_fname ) ); + usage(); + } + } + else + { + usage(); + } +} + + +/* + Especialy for pkginfo lines. + Remove leading spaces and take non-space characters only: + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + +/* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +void write_pkginfo( void ) +{ + char *ln = NULL; + char *line = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgname = skip_spaces( p ); + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) pkgver = skip_spaces( p ); + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) arch = skip_spaces( p ); + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distroname = skip_spaces( p ); + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) distrover = skip_spaces( p ); + } + + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) group = skip_spaces( p ); + } + /* variable short_description="..." is not stored in the PKGLOG file */ + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) url = skip_spaces( p ); + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) license = skip_spaces( p ); + } + if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) uncompressed_size = skip_spaces( p ); + } + if( (match = strstr( ln, "total_files" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) total_files = skip_spaces( p ); + } + } + + free( line ); + + if( pkgname && pkgver && arch && distroname && distrover ) + { + int len; + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + if( mkgroupdir && group ) + { + len = snprintf( buf, PATH_MAX, "%s/%s", + destination, group ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot create output file" ); + } + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot create output directory" ); + } + + len = snprintf( buf, PATH_MAX, "%s/%s/%s-%s-%s-%s-%s", + destination, group, pkgname, pkgver, arch, distroname, distrover ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot create output file" ); + } + } + else + { + len = snprintf( buf, PATH_MAX, "%s/%s-%s-%s-%s-%s", + destination, pkgname, pkgver, arch, distroname, distrover ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot create output file" ); + } + } + output_fname = xstrdup( (const char *)&buf[0] ); + if( output_fname ) + { + output = fopen( (const char *)output_fname, "w" ); + if( !output ) + { + FATAL_ERROR( "Cannot create %s file", output_fname ); + } + } + + free( buf ); + + fprintf( output, "PACKAGE NAME: %s\n", pkgname ); + fprintf( output, "PACKAGE VERSION: %s\n", pkgver ); + fprintf( output, "ARCH: %s\n", arch ); + fprintf( output, "DISTRO: %s\n", distroname ); + fprintf( output, "DISTRO VERSION: %s\n", distrover ); + } + else + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + if( group ) + fprintf( output, "GROUP: %s\n", group ); + if( url ) + fprintf( output, "URL: %s\n", url ); + if( license ) + fprintf( output, "LICENSE: %s\n", license ); + if( uncompressed_size ) + fprintf( output, "UNCOMPRESSED SIZE: %s\n", uncompressed_size ); + if( total_files ) + fprintf( output, "TOTAL FILES: %s\n", total_files ); + + /* reference counter of not installed package is always zero */ + fprintf( output, "REFERENCE COUNTER: 0\n" ); +} + + +void write_requires( void ) +{ + struct stat sb; + char *buf = NULL; + + if( output == NULL && output_fname == NULL ) + { + FATAL_ERROR( "Unable to access output file" ); + } + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + fprintf( output, "REQUIRES:\n" ); + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", srcdir ); + + /* check if path exists and is a regular file */ + if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + + FILE *input; + + input = fopen( (const char *)&buf[0], "r" ); + if( !input ) + { + FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* cat .REQUIRES >> PKGLOG */ + while( (ln = fgets( line, PATH_MAX, input )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* print non-empty lines */ + if( *ln ) + { + fprintf( output, "%s\n", ln ); + } + } + free( line ); + } + free( buf ); +} + + +void write_description( void ) +{ + struct stat sb; + char *buf = NULL; + + if( output == NULL && output_fname == NULL ) + { + FATAL_ERROR( "Unable to access output file" ); + } + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + fprintf( output, "PACKAGE DESCRIPTION:\n" ); + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( (char *)&buf[0], "%s/.DESCRIPTION", srcdir ); + + /* check if path exists and is a regular file */ + if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + char *pattern = NULL; + int n = 0; + + FILE *input; + + input = fopen( (const char *)&buf[0], "r" ); + if( !input ) + { + FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + pattern = (char *)malloc( (size_t)strlen( pkgname ) + 2 ); + if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( pattern, "%s:", pkgname ); + + /* cat .DESCRIPTION >> PKGLOG */ + while( (ln = fgets( line, PATH_MAX, input )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* + skip non-significant spaces at beginning of line + and print lines started with 'pkgname:' + */ + if( (match = strstr( ln, pattern )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + int mlen = strlen( match ), plen = strlen( pattern ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + fprintf( output, "%s\n", match ); + ++n; + } + } + + if( n < DESCRIPTION_NUMBER_OF_LINES ) + { + /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */ + while( n < DESCRIPTION_NUMBER_OF_LINES ) + { + fprintf( output, "%s\n", pattern ); + ++n; + } + } + + free( pattern ); + free( line ); + } + + free( buf ); +} + + +void write_restore_links( void ) +{ + struct stat sb; + char *buf = NULL; + + if( output == NULL && output_fname == NULL ) + { + FATAL_ERROR( "Unable to access output file" ); + } + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + fprintf( output, "RESTORE LINKS:\n" ); + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( (char *)&buf[0], "%s/.RESTORELINKS", srcdir ); + + /* check if path exists and is a regular file */ + if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + + FILE *input; + + input = fopen( (const char *)&buf[0], "r" ); + if( !input ) + { + FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* cat .REQUIRES >> PKGLOG */ + while( (ln = fgets( line, PATH_MAX, input )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* print non-empty lines */ + if( *ln ) + { + fprintf( output, "%s\n", ln ); + } + } + + free( line ); + } + + free( buf ); +} + + +void write_install_script( void ) +{ + struct stat sb; + char *buf = NULL; + + if( output == NULL && output_fname == NULL ) + { + FATAL_ERROR( "Unable to access output file" ); + } + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + fprintf( output, "INSTALL SCRIPT:\n" ); + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( (char *)&buf[0], "%s/.INSTALL", srcdir ); + + /* check if path exists and is a regular file */ + if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + + FILE *input; + + input = fopen( (const char *)&buf[0], "r" ); + if( !input ) + { + FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* cat .REQUIRES >> PKGLOG */ + while( (ln = fgets( line, PATH_MAX, input )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* print all lines */ + fprintf( output, "%s\n", ln ); + } + free( line ); + } + else + { + FATAL_ERROR( "Package doesn't contains the INSTALL script" ); + } + + free( buf ); +} + + +void write_file_list( void ) +{ + struct stat sb; + char *buf = NULL; + + if( output == NULL && output_fname == NULL ) + { + FATAL_ERROR( "Unable to access output file" ); + } + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + fprintf( output, "FILE LIST:\n" ); + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( (char *)&buf[0], "%s/.FILELIST", srcdir ); + + /* check if path exists and is a regular file */ + if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) ) + { + char *ln; + char *line = NULL; + + FILE *input; + + input = fopen( (const char *)&buf[0], "r" ); + if( !input ) + { + FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* cat .REQUIRES >> PKGLOG */ + while( (ln = fgets( line, PATH_MAX, input )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* print non-empty lines */ + if( *ln ) + { + fprintf( output, "%s\n", ln ); + } + } + + free( line ); + } + else + { + FATAL_ERROR( "Package doesn't contains the FILE list" ); + } + + free( buf ); +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + if( pkginfo_type != PKGINFO_TEXT ) + { + /* Create tmpdir */ + srcdir = _mk_tmpdir(); + if( !srcdir ) + { + FATAL_ERROR( "Cannot create temporary dir" ); + } + rm_srcdir_at_exit = 1; + + /* Unpack SERVICE files */ + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *errmsg = NULL, *wmsg = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + + errmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + wmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !wmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)cmd, PATH_MAX ); + bzero( (void *)errmsg, PATH_MAX ); + bzero( (void *)wmsg, PATH_MAX ); + + (void)sprintf( &errmsg[0], "Cannot get SERVICE files from %s file", basename( pkginfo_fname ) ); + + len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1", + srcdir, uncompress, pkginfo_fname, + ".PKGINFO .REQUIRES .DESCRIPTION .RESTORELINKS .INSTALL .FILELIST" ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + if( ! DO_NOT_WARN_ABOUT_SERVICE_FILES ) + { + /***************************************** + if( rc > 0 ) { return TAR exit status } + else { return EXIT_FAILURE } + */ + if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */ + ERROR( errmsg ); + if( fatal_error_hook) fatal_error_hook(); + exit( exit_status ); + } + } + + if( cmd ) free( cmd ); + if( errmsg ) free( errmsg ); + if( wmsg ) free( wmsg ); + } + + /* Change input pkginfo file name and type */ + if( pkginfo_fname ) + { + char *buf = NULL; + + buf = (char *)malloc( strlen( pkginfo_fname ) + 10 ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + (void)sprintf( &buf[0], "%s/.PKGINFO", srcdir ); + + free( pkginfo_fname ); pkginfo_fname = NULL; + + pkginfo_fname = xstrdup( (const char *)buf ); + free( buf ); + pkginfo_type = PKGINFO_TEXT; + } + + } + else /* TEXT: */ + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)strlen( pkginfo_fname ) + 1 ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* function dirname() spoils the source contents: */ + (void)sprintf( buf, "%s", pkginfo_fname ); + + srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) ); + free( buf ); + rm_srcdir_at_exit = 0; + } + + + write_pkginfo(); + write_requires(); + write_description(); + write_restore_links(); + write_install_script(); + write_file_list(); + + + if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir ); + free_resources(); + + exit( exit_status ); +} diff --git a/src/remove-package.c b/src/remove-package.c new file mode 100644 index 0000000..ca83621 --- /dev/null +++ b/src/remove-package.c @@ -0,0 +1,2234 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include + +#include +#include + +#include +#include + +#if defined( HAVE_DIALOG ) +#include +#endif + +#define PROGRAM_NAME "remove-package" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *rempkgs_path = NULL, + *pkg_fname = NULL, *pkglog_fname = NULL, + *tmpdir = NULL, *rtmpdir = NULL, *curdir = NULL, *log_fname = NULL; + +int quiet = 0, ignore_chrefs_errors = 0; +char *description = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *short_description = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *compressed_size = NULL, + *total_files = NULL; + +static char *requested_version = NULL; + +enum _remove_mode { + CONSOLE = 0, + INFODIALOG, + MENUDIALOG +} remove_mode = CONSOLE; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +char uncompress = '\0'; + +static struct dlist *dirs = NULL; +static struct dlist *files = NULL; +static struct dlist *links = NULL; + +static void free_list( struct dlist *list ); + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( short_description ) { free( short_description ); } short_description = NULL; \ + if( description ) { free( description ); } description = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( compressed_size ) { free( compressed_size ); } compressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL; \ + if( requested_version ) { free( requested_version ); } requested_version = NULL + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( rempkgs_path ) { free( rempkgs_path ); rempkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + + if( dirs ) { free_list( dirs ); dirs = NULL; } + if( files ) { free_list( files ); files = NULL; } + if( links ) { free_list( links ); links = NULL; } + + if( rtmpdir ) { free( rtmpdir ); rtmpdir = NULL; } + if( curdir ) { free( curdir ); curdir = NULL; } + if( log_fname ) { free( log_fname ); log_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Remove installed package.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " --ignore-chrefs-errors Ignore change references errors (code: 48).\n" ); +#if defined( HAVE_DIALOG ) + fprintf( stdout, " -i,--info-dialog Show package description during remove\n" ); + fprintf( stdout, " process using ncurses dialog.\n" ); + fprintf( stdout, " -m,--menu-dialog Ask for confirmation the removal.\n" ); +#endif + fprintf( stdout, " -q,--quiet Do not display results. This option\n" ); + fprintf( stdout, " works unless options -i, -m\n" ); + fprintf( stdout, " are enabled.\n" ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " The PKGNAME, PACKAGE tarball or PKGLOG.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Return codes:\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " code | status\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " 30 | package is not installed\n" ); + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 47 | cannot backup PKGLOG file in the Setup Database\n" ); + fprintf( stdout, " 43 | pre-remove script returned error status\n" ); + fprintf( stdout, " 46 | post-remove script returned error status\n" ); + fprintf( stdout, " 48 | references cannot be updated in Setup Database\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Upon successful completion zero is returned. Other non-zero return\n" ); + fprintf( stdout, "codes imply incorrect completion of the deinstallation.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ +#if defined( HAVE_DIALOG ) + const char* short_options = "hvimqr:"; +#else + const char* short_options = "hvqr:"; +#endif + +#define IGNORE_CHREFS_ERRORS 872 + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "ignore-chrefs-errors", no_argument, NULL, IGNORE_CHREFS_ERRORS }, +#if defined( HAVE_DIALOG ) + { "info-dialog", no_argument, NULL, 'i' }, + { "menu-dialog", no_argument, NULL, 'm' }, +#endif + { "quiet", no_argument, NULL, 'q' }, + { "root", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + +#if defined( HAVE_DIALOG ) + case 'i': + { + remove_mode = INFODIALOG; + break; + } + case 'm': + { + remove_mode = MENUDIALOG; + break; + } +#endif + case 'q': + { + quiet = 1; + break; + } + + case IGNORE_CHREFS_ERRORS: + { + ignore_chrefs_errors = 1; + break; + } + + case 'r': + { + if( optarg != NULL ) + { + char cwd[PATH_MAX]; + + bzero( (void *)cwd, PATH_MAX ); + if( optarg[0] != '/' && curdir ) + { + /* skip current directory definition './' at start of argument: */ + if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) ) + (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 ); + else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) ) + (void)sprintf( cwd, "%s", curdir ); + else + (void)sprintf( cwd, "%s/%s", curdir, optarg ); + root = strdup( (const char *)cwd ); + } + else + { + root = strdup( (const char *)optarg ); + } + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + /* absolute path to input package: */ + if( argv[optind][0] != '/' && curdir ) + (void)sprintf( buf, "%s/%s", curdir, (const char *)argv[optind] ); + else + (void)strcpy( buf, (const char *)argv[optind] ); + + pkg_fname = strdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + int len; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = strdup( (const char *)buf ); + } + else + { + len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + free( root ); root = strdup( (const char *)buf ); + } + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + if( !S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + len = strlen( (const char *)buf ); + + (void)strcat( buf, PACKAGES_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + pkgs_path = strdup( (const char *)&buf[0] ); + + /********************************************* + Create other directories of Setup Database: + */ + buf[len] = '\0'; + (void)strcat( buf, REMOVED_PKGS_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + rempkgs_path = strdup( (const char *)&buf[0] ); + + buf[len] = '\0'; + (void)strcat( buf, SETUP_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", SETUP_PATH ); + } + + /********************************************* + Allocate memory for Setup LOG File name: + */ + buf[len] = '\0'; + (void)strcat( buf, LOG_PATH ); + (void)strcat( buf, SETUP_LOG_FILE ); + log_fname = strdup( (const char *)&buf[0] ); + + free( buf ); + + } /* End if( !pkgs_path ) */ +} + +static void setup_log( char *format, ... ) +{ + FILE *fp = NULL; + + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + va_list argp; + + if( ! format ) return; + + fp = fopen( (const char *)log_fname, "a" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open /%s%s file", LOG_PATH, SETUP_LOG_FILE ); + } + + fprintf( fp, "[%04d-%02d-%02d %02d:%02d:%02d]: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + + va_start( argp, format ); + vfprintf( fp, format, argp ); + fprintf( fp, "\n" ); + + fflush( fp ); + fclose( fp ); +} + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( strdup( p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + + +/******************************************************** + Read .FILELIST and .RESTORELINKS functions: + */ +static int __cmp_list_items( const void *a, const void *b ) +{ + if( a && b ) + return strcmp( (const char *)a, (const char *)b ); + else if( a ) + return 1; + else + return -1; +} + +static void __free_list( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void free_list( struct dlist *list ) +{ + if( list ) { dlist_free( list, __free_list ); } +} + +//////////////////////////////////////////////////// +//static void __print_list( void *data, void *user_data ) +//{ +// int *counter = (int *)user_data; +// +// if( counter ) { fprintf( stdout, "item[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } +// else { fprintf( stdout, "item: %s\n", (char *)data ); } +//} +// +//static void print_list( struct dlist *list ) +//{ +// int cnt = 0; +// if( list ) { dlist_foreach( list, __print_list, (void *)&cnt ); } +//} +//////////////////////////////////////////////////// + +static void read_filelist( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.FILELIST", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) ) + { + FATAL_ERROR( "Cannot get .FILELIST from '%s' file", basename( (char *)pkglog_fname ) ); + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) + { + *(ln + strlen(ln) - 1) = '\0'; + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + dirs = dlist_append( dirs, strdup( (const char *)&tmp[0] ) ); + } + else + { + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + files = dlist_append( files, strdup( (const char *)&tmp[0] ) ); + } + + } /* End of while( file list entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} + +static void read_restorelinks( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &tmp[0], "%s%s/%s", (const char *)root, p, f ); + links = dlist_append( links, strdup( (const char *)&tmp[0] ) ); + } + } + } /* End of while( restore links entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} +/* + End of read .FILELIST and .RESTORELINKS functions. + ********************************************************/ + +static void read_description( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *buf = NULL, *tmp = NULL; + char *lp = NULL; + int n = 0; + + char *ln = NULL; + char *line = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.DESCRIPTION", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .DESCRIPTION file" ); + } + + (void)sprintf( (char *)&buf[0], "%s:", pkgname ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + lp = (char *)&tmp[0]; + bzero( (void *)tmp, PATH_MAX ); + (void)sprintf( (char *)&tmp[0], "\n" ); + ++lp; + + while( (ln = fgets( line, PATH_MAX, fp )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */ + { + int mlen = strlen( match ), plen = strlen( buf ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + + match += plen + 1; + if( match[0] != '\0' ) { (void)sprintf( lp, " %s\n", match ); lp += strlen( match ) + 2; } + else { (void)sprintf( lp, "\n" ); ++lp; } + ++n; + } + } /* End of while( ln = fgets() ) */ + + fclose( fp ); + + (void)sprintf( lp, " Uncompressed Size: %s\n", uncompressed_size ); + lp += strlen( uncompressed_size ) + 21; + + description = strdup( (const char *)&tmp[0] ); + + free( buf ); + free( line ); + free( tmp ); +} + +static void pre_remove_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL pre_remove %s > /dev/null 2>&1", + root, rtmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run pre-remove script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 43; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Pre-remove script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPre-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPre-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +/******************************************************** + Removal functions: + */ +static void __remove_link( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + (void)unlink( fname ); + } +} + +static void __remove_file( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + char *p = rindex( fname, '.' ); + /* + Если .new файл остался с тем же именем, это значит что до инсталляции + в системе существовал такой же файл но без расширения .new и при этом + он отличался от нового. В данном случае надо удалять только файл .new. + + Если же файл .new не существует, то надо удалять такой же файл но без + расширения .new . + */ + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + struct stat st; + + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( fname, &st ) == -1) ) *p = '\0'; + } + + (void)unlink( fname ); + } +} + +static int is_dir_empty( const char *dirpath ) +{ + int ret = 0; + + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) return ret; /* stat returns error code; errno is set */ + if( S_ISDIR(path_sb.st_mode) == 0 ) return ret; /* dirpath is not a directory */ + if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */ + + ret = 1; + + len = strlen( dirpath ); + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + ret = 0; + break; + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + closedir( dir ); + + return ret; +} + +static void __remove_dir( void *data, void *user_data ) +{ + const char *dname = (const char *)data; + + if( dname && is_dir_empty( (const char *)dname ) ) + { + (void)rmdir( dname ); + } +} + +static void remove_package( void ) +{ + /* Try to change CWD to the ROOT directory: */ + (void)chdir( (const char *)root ); + + if( links ) { dlist_foreach( links, __remove_link, NULL ); } + + if( files ) { dlist_foreach( files, __remove_file, NULL ); } + + if( dirs ) + { + dirs = dlist_sort( dirs, __cmp_list_items ); + dirs = dlist_reverse( dirs ); + dlist_foreach( dirs, __remove_dir, NULL ); + } + + /* Try to change CWD to the CURRENT directory: */ + (void)chdir( (const char *)curdir ); +} +/* + End of removal functions. + ********************************************************/ + +static void post_remove_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL post_remove %s > /dev/null 2>&1", + root, rtmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run post-remove script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 46; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Post-remove script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPost-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPost-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void finalize_removal( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *tmp = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /********************************************* + Decrement references in the Setup Database: + */ + if( group ) + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s/%s > /dev/null 2>&1", + selfdir, pkgs_path, group, basename( (char *)pkglog_fname ) ); + else + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s > /dev/null 2>&1", + selfdir, pkgs_path, basename( (char *)pkglog_fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot decrement '%s-%s' package references", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( (rc != 0) && !ignore_chrefs_errors ) + { + free( cmd ); + free( tmp ); + + exit_status = 48; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Cannot decrement package references in Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); + } + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /***************************************************** + Backup PKGLOG file into removed-packages directory: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/", rempkgs_path, group ); + else + (void)sprintf( &tmp[0], "%s/", rempkgs_path ); + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "mv %s %s > /dev/null 2>&1", + pkglog_fname, (const char *)&tmp[0] ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot backup '%s' pkglog file", basename( (char *)pkglog_fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + free( tmp ); + + exit_status = 47; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Cannot backup PKGLOG file.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)pkglog_fname ) ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)pkglog_fname ) ); + } + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /**************************************** + Remove group directory if it is empty: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + { + (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, group ); + + const char *dir = (const char *)&tmp[0]; + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + + free( tmp ); +} + + +static int ask_for_remove( int prev ) +{ + int ret = 0; /* continue removal */ +#if defined( HAVE_DIALOG ) + /****************************************************** + Ask for remove dialog shown only in MENUDIALOG mode: + */ + if( (remove_mode == MENUDIALOG) ) + { + if( prev < 0 ) + { + char *msg = NULL; + + msg = (char *)malloc( (size_t)PATH_MAX ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, PATH_MAX ); + + (void)sprintf( &msg[0], "\nPrevious version '\\Z4%s\\Zn' of requested package installed.\n" + "\n\\Z1Remove a previous vesion?\\Zn\n", pkgver ); + + ret = ask_remove_box( "Remove:", pkgname, requested_version, (const char *)&msg[0], 9, 0, 0 ); + + free( msg ); + } + else if( prev > 0 ) + { + char *msg = NULL; + + msg = (char *)malloc( (size_t)PATH_MAX ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, PATH_MAX ); + + (void)sprintf( &msg[0], "\nA newer version '\\Z4%s\\Zn' of requested package installed.\n" + "\n\\Z1Remove a newer vesion?\\Zn\n", pkgver ); + + ret = ask_remove_box( "Remove:", pkgname, requested_version, (const char *)&msg[0], 9, 0, 0 ); + + free( msg ); + } + else + { + ret = ask_remove_box( "Remove:", pkgname, pkgver, description, 18, 0, 0 ); + } + } + + if( ret ) + { + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\nPackage removal terminated by user.\n", 5, 0, 0 ); + } +#endif + return ret; +} + + +static void show_removal_progress( void ) +{ + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + description, 16, 1, 0 ); +#else + fprintf( stdout, "\n Remobe: %s-%s ...\n", pkgname, pkgver ); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\n Remove: %s-%s ...\n", pkgname, pkgver ); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + } + } +} + + +static void read_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgname ) { free( pkgname ); } + pkgname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgver ) { free( pkgver ); } + pkgver = skip_spaces( p ); + } + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( arch ) { free( arch ); } + arch = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distroname ) { free( distroname ); } + distroname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distrover ) { free( distrover ); } + distrover = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( group ) { free( group ); } + group = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "short_description" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + char *b = index( p, '"'), + *e = rindex( p, '"'); + if( b && e && ( b != e ) ) + { + p = ++b; *e = '\0'; + if( short_description ) { free( short_description ); } + short_description = strdup( (const char *)p ); + } + } + } + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( url ) { free( url ); } + url = skip_spaces( p ); + } + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( license ) { free( license ); } + license = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( uncompressed_size ) { free( uncompressed_size ); } + uncompressed_size = skip_spaces( p ); + } + } + if( (match = strstr( ln, "total_files" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( total_files ) { free( total_files ); } + total_files = skip_spaces( p ); + } + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); +} + + +/*************************************************************** + Probe functions: + */ +static void _probe_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( pkglog_fname ) return; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL; + char *pkglog = basename( path ); + + if( (match = strstr( pkglog, (const char *)basename( pkg_fname ) )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = strdup( (const char *)pkglog ); + ++p; + while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) ) + { + /* package version starts with a number and separated by '-' */ + ++p; ++q; + } + *(--p) = '\0'; + + /******************************************************* + We have to make sure that the name we are looking for + is not shorter than the name of the found package. + */ + if( strlen(pkg_fname) >= strlen(buf) ) + { + + pkglog_fname = strdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + probe_package(): + --------------- + */ +static char *probe_package( void ) +{ + char *ret = NULL; + + _probe_pkglog( (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + free( pkg_fname ); + ret = pkg_fname = pkglog_fname; + } + + return ret; +} +/* + Enf of Probe functions. + ***********************************************************/ + +/*********************************************************** + Find functions: + */ +static void _search_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */ + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or group is not a directory", pname ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL, *name = basename( path ); + + if( (match = strstr( name, pkgname )) && match == name ) + { + /**************************************************************** + Здесь мы еще должны проверить, что найденный пакет не имеет + более длинное имя, которое начинается с имени искомого пакета. + Полагаясь на факт, что версия может начинаться только с цифры, + мы пропускаем символ '-', разделяющий имя и версию пакета, + а затем проверяем начальный символ версии: + */ + if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) ) + { + pkglog_fname = strdup( (const char *)path ); + closedir( dir ); + return; + } + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + /************************************************************************** + NOTE: + In the Setup Database can be only one package with the same pkgname + but in different groups. For example, the package named 'cairo' + has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During + system installation the package libs/cairo-1.14.6 installed first + and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6 + moved from /var/log/radix/packages to /var/log/radix/removed-packages. + + So here we have to look for the PKGLOG in all group directories: + */ + _search_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static char *find_package( void ) +{ + char *ret = NULL; + + _search_pkglog( (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + free( pkg_fname ); + ret = pkg_fname = pkglog_fname; + } + + return ret; +} +/* + Enf of Find functions. + ***********************************************************/ + + +/*********************************************************** + check_input_package(): + --------------------- + + Возвращает: + -1 если пакет установлен, но его версия меньше + запрашиваемого, + 0 если версия установленного и запрашиваемого равны, + 1 если пакет установлен, но его версия больше + запрашиваемого. + + В случае возврата -1 или 1, устанавливается переменная + requested_version, равная версии пакета который запросили + на удаление. + + Если пакет не установлен, осуществляется выход со статусом 30. + */ +static int check_input_package( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + int ret = 0; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + /************************************************* + Specified pkg_fname is not a file or directory. + Try to find installed package with name equal + to pkg_fname: + */ + fname = NULL; + fname = probe_package(); + if( !fname ) + { + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", basename( pkg_fname ), NULL, NULL, + "\nPackage is not installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) ); +#endif + } + else + { + if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) ); + } + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + } + else + { + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + (void)unlink( (const char *)&tmp[0] ); /* :remove unnecessary .PKGINFO file */ + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore 'tmpdir' in tmp[] buffer */ + + requested_version = strdup( (const char *)pkgver ); + + fname = NULL; + fname = find_package(); + if( !fname ) + { + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", basename( pkg_fname ), NULL, NULL, + "\nPackage is not installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) ); +#endif + } + else + { + if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) ); + } + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + free( cmd ); + free( tmp ); + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + } + + /* check pkg_fname again: */ + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type == IFMT_UNKNOWN ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/to-remove", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + rtmpdir = strdup( (const char *)&tmp[0] ); + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo,description,install-script,restore-links,filelist %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + free( cmd ); + free( tmp ); + + if( requested_version ) + { + ret = cmp_version( (const char *)pkgver, (const char *)requested_version ); + } + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + + return ret; +} + + + +static void dialogrc( void ) +{ + struct stat st; + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* imagine that the utility is in /sbin directory: */ + (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + if( stat( (const char *)&tmp[0], &st ) == -1 ) + { + /* finaly assume that /usr/sbin is a sbindir: */ + (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + } + + setenv( "DIALOGRC", (const char *)&tmp[0], 1 ); + + free( tmp ); +} + +static char *get_curdir( void ) +{ + char *cwd = NULL; + + cwd = (char *)malloc( PATH_MAX ); + if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cwd, PATH_MAX ); + + if( getcwd( cwd, (size_t)PATH_MAX ) != NULL ) + { + char *p = NULL; + remove_trailing_slash( cwd ); + p = strdup( cwd ); + free( cwd ); + return p; + } + else + { + FATAL_ERROR( "Cannot get absolute path to current directory" ); + } + + return (char *)NULL; +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = strdup( dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + curdir = get_curdir(); + dialogrc(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + { + int status = 0; + + /********************************************************** + Fill pkginfo data and put or replace pkglog into tmpdir: + */ + status = check_input_package(); + + read_filelist(); + read_restorelinks(); + read_description(); + + if( ask_for_remove( status ) ) + { + /* Terminate removal: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + } + + show_removal_progress(); + + /************ + DO REMOVE: + */ + pre_remove_routine(); + remove_package(); + post_remove_routine(); + finalize_removal(); + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\nPackage has been removed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' has been removed.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nPackage '%s-%s' has been removed.\n\n", pkgname, pkgver ); + } + } + + setup_log( "Package '%s-%s' has been removed", pkgname, pkgver ); + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..c6e7b21 --- /dev/null +++ b/src/system.c @@ -0,0 +1,136 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +static void xexec( const char *cmd ) +{ + char *argv[4]; + const char *shell = getenv ("SHELL"); + + if( !shell ) shell = "/bin/sh"; + + argv[0] = (char *) shell; + argv[1] = (char *) "-c"; + argv[2] = (char *) cmd; + argv[3] = NULL; + + execv( shell, argv ); + + /****************************************** + xexec() is called by child process, and + here child process faced to FATAL error: + */ + logmsg( errlog, MSG_FATAL, "%s: Cannot exec", cmd ); + exit( EXIT_FAILURE ); +} + +static pid_t xfork( void ) +{ + pid_t p = fork(); + + if( p == (pid_t) -1 ) + { + FATAL_ERROR( "Cannot %s", "fork" ); + } + + return p; +} + +pid_t sys_exec_command( const char *cmd ) +{ + pid_t pid = xfork(); + + if( pid != 0 ) + { + return pid; + } + + xexec( cmd ); + return pid; /* only to avoid compilaton warning */ +} + + +/***************************************************************** + sys_wait_command() - Wait for pid. + + Return values: + ------------- + 0 - SUCCESS + >=1 - status returned by child process + -1 - Child terminated on signal + -2 - Child terminated on unknown reason + -3 - Cannot waitpid: waitpid() retusrs -1 + + Error message with SIZE length saved into *ERRMSG buffer. + *****************************************************************/ +int sys_wait_command( pid_t pid, char *errmsg, size_t size ) +{ + int status; + + if( pid < 0 ) return (pid_t) -1; + + while( waitpid( pid, &status, 0 ) == -1 ) + if( errno != EINTR ) + { + if( errmsg && size ) { + (void)snprintf( errmsg, size, "PID %lu: Cannot %s", (unsigned long)pid, "waitpid" ); + } + return (int) -3; + } + + if( WIFEXITED( status ) ) + { + if( WEXITSTATUS (status) ) + { + if( errmsg && size ) { + (void)snprintf( errmsg, size, "PID %lu: Child returned status %d", (unsigned long)pid, WEXITSTATUS( status ) ); + } + return (int) WEXITSTATUS( status ); + } + } + else if( WIFSIGNALED( status ) ) + { + if( errmsg && size ) { + (void)snprintf( errmsg, size, "PID %lu: Child terminated on signal %d", (unsigned long)pid, WTERMSIG( status ) ); + } + return (int) -1; + } + else + { + if( errmsg && size ) { + (void)snprintf( errmsg, size, "PID %lu: Child terminated on unknown reason", (unsigned long)pid ); + } + return (int) -2; + } + + return 0; +} diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..8da6590 --- /dev/null +++ b/src/system.h @@ -0,0 +1,49 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _SYSTEM_H_ +#define _SYSTEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern pid_t sys_exec_command( const char *cmd ); + +/***************************************************************** + sys_wait_command() - Wait for pid. + + Return values: + ------------- + 0 - SUCCESS + >=1 - status returned by child process + -1 - Child terminated on signal + -2 - Child terminated on unknown reason + -3 - Cannot waitpid: waitpid() retusrs -1 + + Error message with SIZE length saved into *ERRMSG buffer. + *****************************************************************/ +extern int sys_wait_command( pid_t pid, char *errmsg, size_t size ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _SYSTEM_H_ */ diff --git a/src/update-package.c b/src/update-package.c new file mode 100644 index 0000000..d11a97c --- /dev/null +++ b/src/update-package.c @@ -0,0 +1,3425 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include /* flock(2) */ +#include +#include +#include /* alloca(3) */ +#include /* strdup(3) */ +#include /* index(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include + +#include +#include + +#if defined( HAVE_DIALOG ) +#include +#endif + +#define PROGRAM_NAME "update-package" + +#include + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *rempkgs_path = NULL, *remlog_fname = NULL, + *pkg_fname = NULL, *asc_fname = NULL, *pkglog_fname = NULL, *pkglist_fname = NULL, + *tmpdir = NULL, *rtmpdir = NULL, *curdir = NULL, *log_fname = NULL; + +int ask = 0, rqck = 0, gpgck = 0, reinstall = 0, ignore_chrefs_errors = 0; +char *description = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *short_description = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *compressed_size = NULL, + *total_files = NULL; + +static char *installed_version = NULL; +static char *installed_group = NULL; + +enum _update_mode { + CONSOLE = 0, + INFODIALOG, + MENUDIALOG +} update_mode = CONSOLE; + +enum _priority { + REQUIRED = 0, /* synonims: REQUIRED | required | REQ | req */ + RECOMMENDED, /* synonims: RECOMMENDED | recommended | REC | rec */ + OPTIONAL, /* synonims: OPTIONAL | optional | OPT | opt */ + SKIP /* synonims: SKIP | skip | SKP | skp */ +} priority = REQUIRED; + +enum _procedure +{ + INSTALL = 0, /* 'install' */ + UPDATE /* 'update' */ +} procedure = UPDATE; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +char uncompress = '\0'; + +static struct dlist *rdirs = NULL; +static struct dlist *rfiles = NULL; +static struct dlist *rlinks = NULL; + +static struct dlist *dirs = NULL; +static struct dlist *files = NULL; +static struct dlist *links = NULL; + +static void free_list( struct dlist *list ); + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( short_description ) { free( short_description ); } short_description = NULL; \ + if( description ) { free( description ); } description = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( compressed_size ) { free( compressed_size ); } compressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL; \ + if( installed_version ) { free( installed_version ); } installed_version = NULL; \ + if( installed_group ) { free( installed_group ); } installed_group = NULL + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( rempkgs_path ) { free( rempkgs_path ); rempkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + if( asc_fname ) { free( asc_fname ); asc_fname = NULL; } + if( pkglog_fname ) { free( pkglog_fname ); pkglog_fname = NULL; } + if( remlog_fname ) { free( remlog_fname ); remlog_fname = NULL; } + + if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; } + + if( rdirs ) { free_list( rdirs ); rdirs = NULL; } + if( rfiles ) { free_list( rfiles ); rfiles = NULL; } + if( rlinks ) { free_list( rlinks ); rlinks = NULL; } + + if( dirs ) { free_list( dirs ); dirs = NULL; } + if( files ) { free_list( files ); files = NULL; } + if( links ) { free_list( links ); links = NULL; } + + if( rtmpdir ) { free( rtmpdir ); rtmpdir = NULL; } + if( curdir ) { free( curdir ); curdir = NULL; } + if( log_fname ) { free( log_fname ); log_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] \n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Update package.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -a,--always-ask Used with menudialog mode: always ask\n" ); + fprintf( stdout, " if a package should be updated regardless\n" ); + fprintf( stdout, " of what the package priority is. Without\n" ); + fprintf( stdout, " this option, if the priority is equal to\n" ); + fprintf( stdout, " REQUIRED, the package is updateded without\n" ); + fprintf( stdout, " asking for confirmation the update.\n" ); + fprintf( stdout, " -c,--check-requires Check package requires before update.\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, " -g,--gpg-verify Verify GPG2 signature. The signature must be\n" ); + fprintf( stdout, " saved in a file whose name is the same as the\n" ); + fprintf( stdout, " package file name, but with the extension '.asc'\n" ); + fprintf( stdout, " and located in the same directory as the package.\n" ); +#endif + fprintf( stdout, " --ignore-chrefs-errors Ignore change references errors (code: 48).\n" ); +#if defined( HAVE_DIALOG ) + fprintf( stdout, " -i,--info-dialog Show package description during update\n" ); + fprintf( stdout, " process using ncurses dialog.\n" ); + fprintf( stdout, " -m,--menu-dialog Ask for confirmation the update,\n" ); + fprintf( stdout, " unless the priority is REQUIRED.\n" ); +#endif + fprintf( stdout, " -l,--pkglist= Specify a different package list file\n" ); + fprintf( stdout, " to use for read package priority and type\n" ); + fprintf( stdout, " of install procedure. By default used the\n" ); + fprintf( stdout, " '.pkglist' file found in the directory\n" ); + fprintf( stdout, " where source package is placed.\n" ); + fprintf( stdout, " -p,--priority=\n" ); + fprintf( stdout, " Provides a priority of package instead of\n" ); + fprintf( stdout, " the priority defined in the .pkglist file.\n" ); + fprintf( stdout, " --reinstall Reinstall even if the package is already\n" ); + fprintf( stdout, " installed correctly.\n" ); + fprintf( stdout, " Without this option, the already installed package\n" ); + fprintf( stdout, " will not be updated. The update procedure will be\n" ); + fprintf( stdout, " stopped with success return code.\n" ); + fprintf( stdout, " -r,--root= Target rootfs path.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " The PACKAGE tarball.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Return codes:\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " code | status\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " 30 | package is not installed\n" ); + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 41 | update is aborted due to priority=SKIP\n" ); + fprintf( stdout, " 42 | .pkglist appointed the 'install' procedure instead\n" ); + fprintf( stdout, " 43 | pre-update script returned error status\n" ); + fprintf( stdout, " 44 | uncompress process returned error status\n" ); + fprintf( stdout, " 45 | restore-links script returned error status\n" ); + fprintf( stdout, " 46 | post-update script returned error status\n" ); + fprintf( stdout, " 47 | PKGLOG cannot be stored in the Setup Database\n" ); + fprintf( stdout, " 48 | references cannot be updated in Setup Database\n" ); + fprintf( stdout, " 49 | requires cannot be updated in Setup Database\n" ); +#if defined( HAVE_GPG2 ) + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 51 | signature verification returned error status\n" ); +#endif + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Upon successful completion zero is returned. Other non-zero return\n" ); + fprintf( stdout, "codes imply incorrect completion of the update procedure.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + + +static void remove_trailing_slash( char *dir ) +{ + char *s; + + if( !dir || dir[0] == '\0' ) return; + + s = dir + strlen( dir ) - 1; + while( *s == '/' ) + { + *s = '\0'; --s; + } +} + + +static void bind_asc_extention( char *name ) +{ + char *p = NULL, *q = NULL; + + if( (p = rindex( name, '.' )) && (strlen(p) < 5) ) + { + if( !strncmp( p, ".gz", 3 ) || + !strncmp( p, ".bz2", 4 ) || + !strncmp( p, ".xz", 3 ) ) + { + *p = '\0'; + q = rindex( name, '.' ); + if( q && (strlen(q) < 5) && !strncmp( q, ".tar", 4 ) ) + { + *q = '\0'; + } + } + else if( !strncmp( p, ".tar", 4 ) || + !strncmp( p, ".tbz", 4 ) || + !strncmp( p, ".tgz", 4 ) || + !strncmp( p, ".txz", 4 ) ) + { + *p = '\0'; + } + } + + (void)strcat( name, ".asc" ); +} + +//////////////////////////////////////////////////// +//static char *strmode( enum _update_mode mode ) +//{ +// char *p = NULL; +// +// switch( mode ) +// { +// case CONSOLE: +// p = "CONSOLE"; +// break; +// case INFODIALOG: +// p = "INFODIALOG"; +// break; +// case MENUDIALOG: +// p = "MENUDIALOG"; +// break; +// } +// return p; +//} +//////////////////////////////////////////////////// + +static char *strprio( enum _priority priority, int short_name ) +{ + char *p = NULL; + + switch( priority ) + { + case REQUIRED: + p = ( short_name ) ? "REQ" : "required"; + break; + case RECOMMENDED: + p = ( short_name ) ? "REC" : "recommended"; + break; + case OPTIONAL: + p = ( short_name ) ? "OPT" : "optional"; + break; + case SKIP: + p = ( short_name ) ? "SKP" : "skip"; + break; + } + return p; +} + +static char *strproc( enum _procedure procedure ) +{ + char *p = NULL; + + switch( procedure ) + { + case INSTALL: + p = "install"; + break; + case UPDATE: + p = "update"; + break; + } + return p; +} + + +static int _mkdir_p( const char *dir, const mode_t mode ) +{ + char *buf; + char *p = NULL; + struct stat sb; + + if( !dir ) return -1; + + buf = (char *)alloca( strlen( dir ) + 1 ); + strcpy( buf, dir ); + + remove_trailing_slash( buf ); + + /* check if path exists and is a directory */ + if( stat( buf, &sb ) == 0 ) + { + if( S_ISDIR(sb.st_mode) ) + { + return 0; + } + } + + /* mkdir -p */ + for( p = buf + 1; *p; ++p ) + { + if( *p == '/' ) + { + *p = 0; + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + *p = '/'; + } + } + + /* test path */ + if( stat( buf, &sb ) != 0 ) + { + /* path does not exist - create directory */ + if( mkdir( buf, mode ) < 0 ) + { + return -1; + } + } else if( !S_ISDIR(sb.st_mode) ) + { + /* not a directory */ + return -1; + } + + return 0; +} + +static void _rm_tmpdir( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) + { + return; /* stat returns error code; errno is set */ + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + return; /* dirpath is not a directory */ + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + return; /* Cannot open direcroty; errno is set */ + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISDIR(entry_sb.st_mode) ) + { + /* recursively remove a nested directory */ + _rm_tmpdir( path ); + } + else + { + /* remove a file object */ + (void)unlink( path ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + + } + + /* remove the devastated directory and close the object of this directory */ + (void)rmdir( dirpath ); + + closedir( dir ); +} + +static char *_mk_tmpdir( void ) +{ + char *buf = NULL, *p, *tmp = "/tmp"; + size_t len = 0, size = 0; + + (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */ + + /* Get preferred directory for tmp files */ + if( (p = getenv( "TMP" )) != NULL ) { + tmp = p; + } + else if( (p = getenv( "TEMP" )) != NULL ) { + tmp = p; + } + + size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12; + + buf = (char *)malloc( size ); + if( !buf ) return NULL; + + len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() ); + if( len == 0 || len == size - 1 ) + { + free( buf ); return NULL; + } + + _rm_tmpdir( (const char *)&buf[0] ); + + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 ) + { + return buf; + } + + free( buf ); return NULL; +} + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + +static enum _input_type check_input_file( char *uncompress, const char *fname ) +{ + struct stat st; + size_t pkglog_size = 0; + unsigned char buf[8]; + int rc, fd; + + /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */ + + if( uncompress ) + { + *uncompress = '\0'; + } + + if( stat( fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_size = st.st_size; + + if( (fd = open( fname, O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + rc = (int)read( fd, (void *)&buf[0], 7 ); + if( rc != 7 ) + { + close( fd ); return IFMT_UNKNOWN; + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return IFMT_LOG; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + if( uncompress ) { *uncompress = 'x'; } + close( fd ); return IFMT_PKG; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + if( uncompress ) { *uncompress = 'j'; } + close( fd ); return IFMT_PKG; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + if( uncompress ) { *uncompress = 'J'; } + close( fd ); return IFMT_PKG; + } + + if( pkglog_size > 262 ) + { + if( lseek( fd, 257, SEEK_SET ) == -1 ) + { + FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) ); + } + rc = (int)read( fd, &buf[0], 5 ); + if( rc != 5 ) + { + FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) ); + } + /* TAR */ + if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 ) + { + close( fd ); return IFMT_PKG; + } + } + + close( fd ); return IFMT_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ +#if defined( HAVE_GPG2 ) +#if defined( HAVE_DIALOG ) + const char* short_options = "hvacgiml:p:r:"; +#else + const char* short_options = "hvacgl:p:r:"; +#endif +#else +#if defined( HAVE_DIALOG ) + const char* short_options = "hvaciml:p:r:"; +#else + const char* short_options = "hvacl:p:r:"; +#endif +#endif + +#define REINSTALL 812 +#define IGNORE_CHREFS_ERRORS 872 + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "always-ask", no_argument, NULL, 'a' }, + { "check-requires", no_argument, NULL, 'c' }, +#if defined( HAVE_GPG2 ) + { "gpg-verify", no_argument, NULL, 'g' }, +#endif + { "ignore-chrefs-errors", no_argument, NULL, IGNORE_CHREFS_ERRORS }, +#if defined( HAVE_DIALOG ) + { "info-dialog", no_argument, NULL, 'i' }, + { "menu-dialog", no_argument, NULL, 'm' }, +#endif + { "pkglist", required_argument, NULL, 'l' }, + { "priority", required_argument, NULL, 'p' }, + { "reinstall", no_argument, NULL, REINSTALL }, + { "root", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'a': + { + ask = 1; + break; + } + case 'c': + { + rqck = 1; + break; + } +#if defined( HAVE_GPG2 ) + case 'g': + { + gpgck = 1; + break; + } +#endif + +#if defined( HAVE_DIALOG ) + case 'i': + { + update_mode = INFODIALOG; + break; + } + case 'm': + { + update_mode = MENUDIALOG; + break; + } +#endif + + case REINSTALL: + { + reinstall = 1; + break; + } + case IGNORE_CHREFS_ERRORS: + { + ignore_chrefs_errors = 1; + break; + } + + case 'p': + { + if( optarg != NULL ) + { + char *match = NULL; + + if( strlen( (const char *)optarg ) > 2 ) + { + to_lowercase( optarg ); + if( (match = strstr( optarg, "req" )) && match == optarg ) { + priority = REQUIRED; + } + else if( (match = strstr( optarg, "rec" )) && match == optarg ) { + priority = RECOMMENDED; + } + + else if( (match = strstr( optarg, "opt" )) && match == optarg ) { + priority = OPTIONAL; + } + else if( (match = strstr( optarg, "sk" )) && match == optarg ) { + priority = SKIP; + } + else { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + { + FATAL_ERROR( "Unknown --priority '%s' value", optarg ); + } + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'l': + { + if( optarg != NULL ) + { + pkglist_fname = xstrdup( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + char cwd[PATH_MAX]; + + bzero( (void *)cwd, PATH_MAX ); + if( optarg[0] != '/' && curdir ) + { + /* skip current directory definition './' at start of argument: */ + if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) ) + (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 ); + else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) ) + (void)sprintf( cwd, "%s", curdir ); + else + (void)sprintf( cwd, "%s/%s", curdir, optarg ); + root = xstrdup( (const char *)cwd ); + } + else + { + root = xstrdup( (const char *)optarg ); + } + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + struct stat st; + char *buf = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + /* absolute path to input package: */ + if( argv[optind][0] != '/' && curdir ) + (void)sprintf( buf, "%s/%s", curdir, (const char *)argv[optind] ); + else + (void)strcpy( buf, (const char *)argv[optind] ); + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file: %s", buf, strerror( errno ) ); + } + + if( S_ISREG(st.st_mode) ) + { + pkg_fname = xstrdup( (const char *)&buf[0] ); + bind_asc_extention( buf ); + asc_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Input package '%s' is not a regular file", (const char *)argv[optind] ); + } + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + struct stat st; + char *buf = NULL; + int len; + + bzero( (void *)&st, sizeof( struct stat ) ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = xstrdup( (const char *)buf ); + } + else + { + len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + free( root ); root = xstrdup( (const char *)buf ); + } + } + + if( stat( (const char *)&buf[0], &st ) == -1 ) + { + FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) ); + } + if( !S_ISDIR(st.st_mode) ) + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + len = strlen( (const char *)buf ); + + (void)strcat( buf, PACKAGES_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + pkgs_path = xstrdup( (const char *)&buf[0] ); + + /********************************************* + Create other directories of Setup Database: + */ + buf[len] = '\0'; + (void)strcat( buf, REMOVED_PKGS_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + rempkgs_path = xstrdup( (const char *)&buf[0] ); + + buf[len] = '\0'; + (void)strcat( buf, SETUP_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", SETUP_PATH ); + } + + /********************************************* + Allocate memory for Setup LOG File name: + */ + buf[len] = '\0'; + (void)strcat( buf, LOG_PATH ); + (void)strcat( buf, SETUP_LOG_FILE ); + log_fname = xstrdup( (const char *)&buf[0] ); + + free( buf ); + + } /* End if( !pkgs_path ) */ +} + +static void setup_log( char *format, ... ) +{ + FILE *fp = NULL; + + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + va_list argp; + + if( ! format ) return; + + fp = fopen( (const char *)log_fname, "a" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open /%s%s file", LOG_PATH, SETUP_LOG_FILE ); + } + + fprintf( fp, "[%04d-%02d-%02d %02d:%02d:%02d]: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + + va_start( argp, format ); + vfprintf( fp, format, argp ); + fprintf( fp, "\n" ); + + fflush( fp ); + fclose( fp ); +} + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( xstrdup( (const char *)p ) ); +} + + +/******************************* + remove spaces at end of line: + */ +static void skip_eol_spaces( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } +} + + +static char *trim( char *s ) +{ + char *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s + strlen( s ) - 1; + while( isspace( *p ) ) { *p-- = '\0'; } + p = s; while( isspace( *p ) ) { ++p; } + + return( p ); +} + + +static char *size_to_string( size_t pkgsize ) +{ + int nd; + double sz = (double)pkgsize / (double)1024; + + char *ret = NULL; + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( sz > (double)1048576 ) + { + sz = sz / (double)1048576; + /* + NOTE: + ---- + Операция округления до одного знака после десятичной точки: sz = round(sz*10.0)/10.0; + здесь не нужна; можно обойтись вычислением количества цифр, выводимых на печать с помощью + формата '%.*g': + + Количество десятичных цифр, необходимое для предстваления целой части числа + 1(одна) + десятичная цифра после десятичной точки. Формат %.*g не будет выводить дробную часть + числа, если после округления, до одного знака после десятичной точки, дробная часть + равна нулю: + */ + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gG", nd, sz ); + } + else if( sz > (double)1024 ) + { + sz = sz / (double)1024; + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gM", nd, sz ); + } + else + { + nd = (int)ceil(log10(floor(sz) + 1.0)) + 1; + (void)sprintf( (char *)&tmp[0], "%.*gK", nd, sz ); + } + + ret = xstrdup( (const char *)&tmp[0] ); + free( tmp ); + + return ret; +} + +static void read_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgname ) { free( pkgname ); } + pkgname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgver ) { free( pkgver ); } + pkgver = skip_spaces( p ); + } + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( arch ) { free( arch ); } + arch = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distroname ) { free( distroname ); } + distroname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distrover ) { free( distrover ); } + distrover = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( group ) { free( group ); } + group = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "short_description" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + char *b = index( p, '"'), + *e = rindex( p, '"'); + if( b && e && ( b != e ) ) + { + p = ++b; *e = '\0'; + if( short_description ) { free( short_description ); } + short_description = xstrdup( (const char *)p ); + } + } + } + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( url ) { free( url ); } + url = skip_spaces( p ); + } + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( license ) { free( license ); } + license = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( uncompressed_size ) { free( uncompressed_size ); } + uncompressed_size = skip_spaces( p ); + } + } + if( (match = strstr( ln, "total_files" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( total_files ) { free( total_files ); } + total_files = skip_spaces( p ); + } + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); +} + + +/*********************************************************** + Find functions: + */ +static void _search_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */ + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or group is not a directory", pname ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) ); + } + + len = strlen( dirpath ); + + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + if( S_ISREG(entry_sb.st_mode) ) + { + char *match = NULL, *name = basename( path ); + + if( (match = strstr( name, pkgname )) && match == name ) + { + /**************************************************************** + Здесь мы еще должны проверить, что найденный пакет не имеет + более длинное имя, которое начинается с имени искомого пакета. + Полагаясь на факт, что версия может начинаться только с цифры, + мы пропускаем символ '-', разделяющий имя и версию пакета, + а затем проверяем начальный символ версии: + */ + if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) ) + { + remlog_fname = xstrdup( (const char *)path ); + closedir( dir ); + return; + } + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + /************************************************************************** + NOTE: + In the Setup Database can be only one package with the same pkgname + but in different groups. For example, the package named 'cairo' + has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During + system installation the package libs/cairo-1.14.6 installed first + and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6 + moved from /var/log/radix/packages to /var/log/radix/removed-packages. + + So here we have to look for the PKGLOG in all group directories: + */ + _search_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static char *find_package( void ) +{ + char *ret = NULL; + + _search_pkglog( (const char *)pkgs_path, NULL ); + if( remlog_fname ) + { + ret = remlog_fname; + } + + return ret; +} +/* + Enf of Find functions. + ***********************************************************/ + +/*********************************************************** + check_installed_package(): + --------------------- + + Возвращает: + -1 если пакет установлен, но его версия меньше + запрашиваемого, + 0 если версия установленного и запрашиваемого равны, + 1 если пакет установлен, но его версия больше + запрашиваемого. + + В случае возврата -1 или 1, устанавливается переменная + instaled_version, равная версии существующего пакета. + + Если пакет не установлен, осуществляется выход со статусом 30. + */ +static int check_installed_package( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + char *requested_version = NULL; + char *requested_group = NULL; + + int ret = 0; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type != IFMT_PKG ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + (void)unlink( (const char *)&tmp[0] ); /* :remove unnecessary .PKGINFO file */ + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore 'tmpdir' in tmp[] buffer */ + + requested_version = xstrdup( (const char *)pkgver ); + requested_group = ( group ) ? xstrdup( (const char *)group ) : NULL; + + fname = NULL; + fname = find_package(); + if( !fname ) + { + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", basename( pkg_fname ), NULL, NULL, + "\nPackage is not installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) ); +#endif + } + else + { + fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) ); + } + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + free( cmd ); + free( tmp ); + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + + + /* Now fname is a name of installed PKGLOG file; check fname again: */ + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type != IFMT_LOG ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/to-remove", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + rtmpdir = xstrdup( (const char *)&tmp[0] ); + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s -o pkginfo,description,restore-links,filelist %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + installed_version = xstrdup( (const char *)pkgver ); + installed_group = ( group ) ? xstrdup( (const char *)group ) : NULL; + + free( cmd ); + free( tmp ); + + if( requested_group && installed_group ) + { + if( !(ret = strcmp( (const char *)installed_group, (const char *)requested_group )) ) + { + ret = cmp_version( (const char *)installed_version, (const char *)requested_version ); + } + } + else if( !requested_group && !installed_group ) + { + ret = cmp_version( (const char *)installed_version, (const char *)requested_version ); + } + else if( requested_group ) + { + ret = -1; + } + else + { + ret = 1; + } + + if( requested_version ) { free( requested_version ); } + if( requested_group ) { free( requested_group ); } + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + + return ret; +} + +static int check_installed_pkg_integrity( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL; + + char *buf = NULL, *tmp = NULL; + + int restore_links = 0; + int ret = 1; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* Check if .RESTORELINKS is present */ + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == 0) && (st.st_size > 8) ) + { + restore_links = 1; + } + + (void)sprintf( &tmp[0], "%s/.FILELIST", rtmpdir ); + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + int dir = 0; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) { dir = 1; *(ln + strlen(ln) - 1) = '\0'; } + else { dir = 0; } + + if( !dir ) + { + char *p = rindex( ln, '.' ); + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + /************************** + Do not check .new files: + */ + *p = '\0'; + } + } + + (void)sprintf( &buf[0], "%s%s", root, ln ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( lstat( (const char *)&buf[0], &st ) == -1 ) + { + /* cannot access file list entry */ + ret = 0; continue; + } + + if( dir ) + { + if( S_ISDIR(st.st_mode) == 0 ) + { + /* not a directory */ + ret = 0; continue; + } + } + else + { + if( S_ISREG(st.st_mode) == 0 ) + { + /* not a regular file */ + ret = 0; continue; + } + if( !restore_links ) + { + if( S_ISLNK(st.st_mode) == 0 ) + { + /* not a symbolic link */ + ret = 0; continue; + } + } + } + } /* End of while( file list entry ) */ + fclose( fp ); + + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)&tmp[0], &st ) == 0 ) + { + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &buf[0], "%s/%s/%s", root, p, f ); + bzero( (void *)&st, sizeof( struct stat ) ); + + if( lstat( (const char *)&buf[0], &st ) == -1 ) + { + /* cannot access restore links entry */ + ret = 0; continue; + } + + if( S_ISLNK(st.st_mode) == 0 ) + { + /* not a symbolic link */ + ret = 0; continue; + } + } + } + } /* End of while( restore links entry ) */ + fclose( fp ); + } + + free( line ); + free( tmp ); + free( buf ); + + return ret; +} + + +static void read_service_files( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type != IFMT_PKG ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkginfo -d %s" + " -o pkginfo,description,requires,restore-links,install-script,filelist" + " %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + compressed_size = size_to_string( st.st_size ); + + /****************** + Get PKGLOG file: + */ + len = snprintf( &cmd[0], PATH_MAX, + "%s/pkglog -m -d %s %s > /dev/null 2>&1", + selfdir, tmp, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGLOG from '%s' file", basename( (char *)fname ) ); + } + + if( group ) + (void)sprintf( cmd, "%s/%s/%s-%s-%s-%s-%s", tmp, group, pkgname, pkgver, arch, distroname, distrover ); + else + (void)sprintf( cmd, "%s/%s-%s-%s-%s-%s", tmp, pkgname, pkgver, arch, distroname, distrover ); + + bzero( (void *)&st, sizeof( struct stat ) ); + if( stat( (const char *)cmd, &st ) == -1 ) + { + FATAL_ERROR( "Cannot get PKGLOG from '%s' file: %s", basename( (char *)fname ), strerror( errno ) ); + } + + pkglog_fname = xstrdup( (const char *)cmd ); + + /************************************* + Attempt to read packages list file: + */ + { + if( !pkglist_fname ) + { + /***************************************** + Get source packages path if applicable: + */ + (void)strcpy( cmd, (const char *)fname ); + (void)strcpy( tmp, dirname( cmd ) ); + + if( group && !strcmp( group, basename( tmp ) ) ) + (void)strcpy( cmd, (const char *)dirname( tmp ) ); + else + (void)strcpy( cmd, (const char *)tmp ); + + /***************************************** + Save default packages list file name: + */ + (void)strcat( cmd, "/.pkglist" ); + pkglist_fname = xstrdup( (const char *)cmd ); + } + + /************************** + read .pkglist if exists: + */ + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)pkglist_fname, &st ) == 0) && S_ISREG(st.st_mode) ) + { + char *ln = NULL; + char *line = NULL; + + FILE *pkglist = NULL; + + pkglist = fopen( (const char *)pkglist_fname, "r" ); + if( !pkglist ) + { + FATAL_ERROR( "Cannot open %s file", pkglist_fname ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkglist )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, pkgname )) && match == ln ) + { + char *p = NULL; + char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL; + + name = ln; + if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue; + if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue; + if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue; + if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue; + if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue; + prio = trim( prio ); + + if( name && vers && desc && ball && proc && prio ) + { + char *grp = index( (const char *)ball, '/' ); + if( grp ) + { + *grp = '\0'; grp = ball; grp = trim( grp ); + if( strcmp( group, grp ) ) continue; + } + + /* read priority: */ + if( strlen( (const char*)prio ) > 2 ) + { + char *m = NULL; + + to_lowercase( prio ); + if( (m = strstr( prio, "req" )) && m == prio ) { + priority = REQUIRED; + } + else if( (m = strstr( prio, "rec" )) && m == prio ) { + priority = RECOMMENDED; + } + else if( (m = strstr( prio, "opt" )) && m == prio ) { + priority = OPTIONAL; + } + else if( (m = strstr( prio, "sk" )) && m == prio ) { + priority = SKIP; + } + else { + priority = REQUIRED; + } + } + else + { + priority = REQUIRED; + } + + /* read procedure: */ + if( strlen( (const char*)proc ) > 5 ) + { + char *m = NULL; + + to_lowercase( proc ); + if( (m = strstr( proc, "install" )) && m == proc ) { + procedure = INSTALL; + } + else if( (m = strstr( proc, "update" )) && m == proc ) { + procedure = UPDATE; + } + else { + procedure = UPDATE; + } + } + else + { + procedure = UPDATE; + } + } + + } /* End if( match ) */ + + } /* End of while( ln = fgets() ) */ + + free( line ); + fclose( pkglist ); + + } /* End of reading .pkglist */ + + } /* End of attemption of reading .pkflist file */ + + free( cmd ); + free( tmp ); + + if( priority == SKIP ) + { + exit_status = 41; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], + "\nUpdate procedure is skipped due to specified '%s' priority.\n", + strprio( priority, 0 ) ); + + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + (const char *)&tmp[0], 5, 0, 0 ); + + free( tmp ); +#else + fprintf( stdout, + "\nUpdate procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n", + pkgname, pkgver, strprio( priority, 0 ) ); +#endif + } + else + { + fprintf( stdout, + "\nUpdate procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n", + pkgname, pkgver, strprio( priority, 0 ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + if( procedure != UPDATE ) + { + exit_status = 42; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], + "\nUpdate is skipped because the '%s' procedure is specified.\n", + strproc( procedure ) ); + + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + (const char *)&tmp[0], 6, 0, 0 ); + + free( tmp ); +#else + fprintf( stdout, + "\nUpdate procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n", + pkgname, pkgver, strproc( procedure ) ); +#endif + } + else + { + fprintf( stdout, + "\nUpdate procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n", + pkgname, pkgver, strproc( procedure ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } +} + + +static void check_requires( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "%s/check-requires --root=%s %s > /dev/null 2>&1", + selfdir, root, pkglog_fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot check required packages for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage requires other packages to be installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( rc ); + } +} + +/******************************************************** + Read .FILELIST and .RESTORELINKS functions used for + roolback/remove in case post-update errors: + */ +static int __cmp_list_items( const void *a, const void *b ) +{ + if( a && b ) + return strcmp( (const char *)a, (const char *)b ); + else if( a ) + return 1; + else + return -1; +} + +static void __free_list( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void free_list( struct dlist *list ) +{ + if( list ) { dlist_free( list, __free_list ); } +} + +//////////////////////////////////////////////////// +//static void __print_list( void *data, void *user_data ) +//{ +// int *counter = (int *)user_data; +// +// if( counter ) { fprintf( stdout, "item[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } +// else { fprintf( stdout, "item: %s\n", (char *)data ); } +//} +// +//static void print_list( struct dlist *list ) +//{ +// int cnt = 0; +// if( list ) { dlist_foreach( list, __print_list, (void *)&cnt ); } +//} +//////////////////////////////////////////////////// + +static void read_filelist( void **d, void **f, const char *path ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + struct dlist *dirs = (struct dlist *)(*d); + struct dlist *files = (struct dlist *)(*f); + + if( !dirs || !files || !path ) return; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.FILELIST", path ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) ) + { + FATAL_ERROR( "Cannot get .FILELIST from '%s' file", basename( (char *)pkglog_fname ) ); + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) + { + *(ln + strlen(ln) - 1) = '\0'; + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + dirs = dlist_append( dirs, xstrdup( (const char *)&tmp[0] ) ); + } + else + { + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + files = dlist_append( files, xstrdup( (const char *)&tmp[0] ) ); + } + + } /* End of while( file list entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} + +static void read_restorelinks( void **l, const char *path ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + struct dlist *links = (struct dlist *)(*l); + + if( !links || !path ) return; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", path ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + while( (ln = fgets( line, PATH_MAX, fp )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &tmp[0], "%s%s/%s", (const char *)root, p, f ); + links = dlist_append( links, xstrdup( (const char *)&tmp[0] ) ); + } + } + } /* End of while( restore links entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} +/* + End of read .FILELIST and .RESTORELINKS functions. + ********************************************************/ + +/******************************************************** + Rollback/Remove functions: + */ +static void __remove_link( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + (void)unlink( fname ); + } +} + +static void __remove_file( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + char *p = rindex( fname, '.' ); + /* + Если .new файл остался с тем же именем, это значит что до инсталляции + в системе существовал такой же файл но без расширения .new и при этом + он отличался от нового. В данном случае надо удалять только файл .new. + + Если же файл .new не существует, то надо удалять такой же файл но без + расширения .new . + */ + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + struct stat st; + + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( fname, &st ) == -1) ) *p = '\0'; + } + + (void)unlink( fname ); + } +} + +static int is_dir_empty( const char *dirpath ) +{ + int ret = 0; + + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) return ret; /* stat returns error code; errno is set */ + if( S_ISDIR(path_sb.st_mode) == 0 ) return ret; /* dirpath is not a directory */ + if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */ + + ret = 1; + + len = strlen( dirpath ); + while( (entry = readdir( dir )) != NULL) + { + /* skip entries '.' and '..' */ + if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue; + + /* determinate a full name of an entry */ + path = alloca( len + strlen( entry->d_name ) + 2 ); + strcpy( path, dirpath ); + strcat( path, "/" ); + strcat( path, entry->d_name ); + + if( stat( path, &entry_sb ) == 0 ) + { + ret = 0; + break; + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + closedir( dir ); + + return ret; +} + +static void __remove_dir( void *data, void *user_data ) +{ + const char *dname = (const char *)data; + + if( dname && is_dir_empty( (const char *)dname ) ) + { + (void)rmdir( dname ); + } +} + +static void rollback( void ) +{ + /* Try to change CWD to the ROOT directory: */ + (void)chdir( (const char *)root ); + + if( links ) { dlist_foreach( links, __remove_link, NULL ); } + + if( files ) { dlist_foreach( files, __remove_file, NULL ); } + + if( dirs ) + { + dirs = dlist_sort( dirs, __cmp_list_items ); + dirs = dlist_reverse( dirs ); + dlist_foreach( dirs, __remove_dir, NULL ); + } + + /* Try to remove PKGLOG file */ + { + char *tmp = NULL; + + tmp = (char *)malloc( PATH_MAX ); + if( tmp ) + { + const char *fname = basename( (char *)pkglog_fname ); + + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/%s", pkgs_path, group, fname ); + else + (void)sprintf( &tmp[0], "%s/%s", pkgs_path, fname ); + + (void)unlink( (const char *)&tmp[0] ); + + if( group ) + { + const char *dir = (const char *)dirname( (char *)&tmp[0] ); + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + free( tmp ); + } + } + + /* Try to change CWD to the CURRENT directory: */ + (void)chdir( (const char *)curdir ); +} + +static void remove_package( void ) +{ + /* Try to change CWD to the ROOT directory: */ + (void)chdir( (const char *)root ); + + if( rlinks ) { dlist_foreach( rlinks, __remove_link, NULL ); } + + if( rfiles ) { dlist_foreach( rfiles, __remove_file, NULL ); } + + if( rdirs ) + { + rdirs = dlist_sort( rdirs, __cmp_list_items ); + rdirs = dlist_reverse( rdirs ); + dlist_foreach( rdirs, __remove_dir, NULL ); + } + + /* Try to change CWD to the CURRENT directory: */ + (void)chdir( (const char *)curdir ); +} + +static void finalize_removal( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *tmp = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /********************************************* + Decrement references in the Setup Database: + */ + if( installed_group ) + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s/%s > /dev/null 2>&1", + selfdir, pkgs_path, installed_group, basename( (char *)remlog_fname ) ); + else + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s > /dev/null 2>&1", + selfdir, pkgs_path, basename( (char *)remlog_fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot decrement '%s-%s' package references", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( (rc != 0) && !ignore_chrefs_errors ) + { + free( cmd ); + free( tmp ); + + exit_status = 48; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, NULL, + "\n\\Z1Cannot decrement package references in Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /***************************************************** + Backup PKGLOG file into removed-packages directory: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( installed_group ) + (void)sprintf( &tmp[0], "%s/%s/", rempkgs_path, installed_group ); + else + (void)sprintf( &tmp[0], "%s/", rempkgs_path ); + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "mv %s %s > /dev/null 2>&1", + remlog_fname, (const char *)&tmp[0] ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot backup '%s' pkglog file", basename( (char *)remlog_fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + free( tmp ); + + exit_status = 47; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, NULL, + "\n\\Z1Cannot backup PKGLOG file.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)remlog_fname ) ); +#endif + } + else + { + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)remlog_fname ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /**************************************** + Remove group directory if it is empty: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( installed_group ) + { + (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, installed_group ); + + const char *dir = (const char *)&tmp[0]; + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + + free( tmp ); +} + +/* + End of rollback/remove functions. + ********************************************************/ + +static void read_description( const char *path, int show_compressed_size ) +{ + struct stat st; + FILE *fp = NULL; + + char *buf = NULL, *tmp = NULL; + char *lp = NULL; + int n = 0; + + char *ln = NULL; + char *line = NULL; + + if( !path ) return; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.DESCRIPTION", path ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .DESCRIPTION file" ); + } + + (void)sprintf( (char *)&buf[0], "%s:", pkgname ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + lp = (char *)&tmp[0]; + bzero( (void *)tmp, PATH_MAX ); + (void)sprintf( (char *)&tmp[0], "\n" ); + ++lp; + + while( (ln = fgets( line, PATH_MAX, fp )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */ + { + int mlen = strlen( match ), plen = strlen( buf ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + + match += plen + 1; + if( match[0] != '\0' ) { (void)sprintf( lp, " %s\n", match ); lp += strlen( match ) + 2; } + else { (void)sprintf( lp, "\n" ); ++lp; } + ++n; + } + } /* End of while( ln = fgets() ) */ + + fclose( fp ); + + (void)sprintf( lp, " Uncompressed Size: %s\n", uncompressed_size ); + lp += strlen( uncompressed_size ) + 21; + if( show_compressed_size ) + { + (void)sprintf( lp, " Compressed Size: %s\n", compressed_size ); + lp += strlen( compressed_size ) + 21; + } + + if( description ) { free( description ); description = NULL; } + description = xstrdup( (const char *)&tmp[0] ); + + free( buf ); + free( line ); + free( tmp ); +} + +static int ask_for_update( void ) +{ + int ret = 0; /* continue update */ +#if defined( HAVE_DIALOG ) + /****************************************************** + Ask for update dialog shown only in MENUDIALOG mode + when priority != REQUIRED or --always-ask=yes: + */ + if( (update_mode == MENUDIALOG) && (((priority == REQUIRED) && ask) || (priority != REQUIRED)) ) + { + ret = ask_update_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + description, 18, 0, 0 ); + } + + if( ret ) + { + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\nUpdate terminated by user.\n", 5, 0, 0 ); + } +#endif + return ret; +} + +static int ask_for_reinstall( void ) +{ + int ret = 0; /* continue update */ +#if defined( HAVE_DIALOG ) + /*************************************************************** + Ask for remove dialog shown only in INFO or MENU DIALOG mode: + */ + if( update_mode == MENUDIALOG ) + { + char *msg = NULL; + + msg = (char *)malloc( (size_t)PATH_MAX ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, PATH_MAX ); + + (void)sprintf( &msg[0], "\nThe same version of requested package installed and correct.\n" + "\n\\Z1Re-install this vesion?\\Zn\n" ); + + if( (ret = ask_reinstall_box( "Update:", pkgname, installed_version, (const char *)&msg[0], 9, 0, 0 )) ) + { + info_pkg_box( "Update:", pkgname, installed_version, NULL, + "\nPackage update terminated by user.\n", 5, 0, 0 ); + } + + free( msg ); + } + else if( update_mode == INFODIALOG ) + { + if( !reinstall ) + { + info_pkg_box( "Update:", pkgname, installed_version, NULL, + "\nThe same version of requested package installed and correct.\n", 5, 0, 0 ); + ret = 1; + } + } + else + { + if( !reinstall ) + { + fprintf( stdout, "\nThe same version of '%s-%s' package is installed and correct.\n\n", pkgname, installed_version ); + ret = 1; + } + } +#else + { + if( !reinstall ) + { + fprintf( stdout, "\nThe same version of '%s-%s' package is installed and correct.\n\n", pkgname, installed_version ); + ret = 1; + } + } +#endif + return ret; +} + + +static void show_update_progress( void ) +{ + fprintf( stdout, "\033[2J" ); /* clear screen */ + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + description, 16, 0, 0 ); +#else + fprintf( stdout, "\n Update: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 )); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */ +#endif + } + else + { + fprintf( stdout, "\n Update: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 )); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */ + } +} + + +static void pre_update_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL pre_update %s %s > /dev/null 2>&1", + root, tmpdir, pkgver, installed_version ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run pre-update script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 43; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Pre-update script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPre-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPre-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static const char *fill_decompressor( char *buffer, char compressor ) +{ + switch( compressor ) + { + case 'J': + (void)sprintf( buffer, "xz --threads=%d -dc", get_nprocs() ); + break; + case 'j': + (void)sprintf( buffer, "bzip2 -dc" ); + break; + case 'z': + (void)sprintf( buffer, "gzip -dc" ); + break; + default: + (void)sprintf( buffer, "cat -" ); + break; + } + return (const char *)buffer; +} + +static void uncompress_package( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + char decompressor[64]; + + (void)fill_decompressor( (char *)&decompressor[0], uncompress ); + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cat %s | %s | tar -C %s " + "--exclude='.DESCRIPTION' " + "--exclude='.FILELIST' " + "--exclude='.INSTALL' " + "--exclude='.PKGINFO' " + "--exclude='.REQUIRES' " + "--exclude='.RESTORELINKS' " + "-xf - > /dev/null 2>&1", + pkg_fname, decompressor, root ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot uncompress '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 44; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot uncompress package.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void restore_links( void ) +{ + struct stat st; + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + (void)sprintf( &cmd[0], "%s/.RESTORELINKS", tmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&cmd[0], &st ) == -1) || (st.st_size < 8) ) + { + free( cmd ); + return; + } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && sh %s/.RESTORELINKS > /dev/null 2>&1", + root, tmpdir ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot restore links for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + rollback(); + + exit_status = 45; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Restore-links script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void post_update_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "cd %s && %s/.INSTALL post_update %s %s > /dev/null 2>&1", + root, tmpdir, pkgver, installed_version ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run post-update script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + rollback(); + + exit_status = 46; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Post-update script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPost-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPost-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void finalize_update( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *tmp = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, group ); + else + (void)sprintf( &tmp[0], "%s/", pkgs_path ); + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + + /**************************************** + Store PKGLOG file into Setup Database: + */ + len = snprintf( &cmd[0], PATH_MAX, + "cp %s %s > /dev/null 2>&1", + pkglog_fname, (const char *)&tmp[0] ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot store '%s' pkglog file", basename( (char *)pkglog_fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + rollback(); + + free( cmd ); + free( tmp ); + + exit_status = 47; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot store PKGLOG file into Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) ); +#endif + } + else + { + fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /********************************************* + Increment references in the Setup Database: + */ + if( group ) + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=inc --destination=%s %s/%s > /dev/null 2>&1", + selfdir, pkgs_path, group, basename( (char *)pkglog_fname ) ); + else + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=inc --destination=%s %s > /dev/null 2>&1", + selfdir, pkgs_path, basename( (char *)pkglog_fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot increment '%s-%s' package references", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( (rc != 0) && !ignore_chrefs_errors ) + { + free( tmp ); + + rollback(); + + exit_status = 48; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot increment package references in Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /************************************************* + Remove backup PKGLOG file from removed-packages + directory if exists: + */ + bzero( (void *)tmp, PATH_MAX ); + { + const char *fname = basename( (char *)pkglog_fname ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/%s", rempkgs_path, group, fname ); + else + (void)sprintf( &tmp[0], "%s/%s", rempkgs_path, fname ); + + (void)unlink( (const char *)&tmp[0] ); + + if( group ) + { + const char *dir = (const char *)dirname( (char *)&tmp[0] ); + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + } + + free( tmp ); +} + +#if defined( HAVE_GPG2 ) +static void verify_gpg_signature( void ) +{ + struct stat st; + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + bzero( (void *)&st, sizeof( struct stat ) ); + + /****************************************************************** + Do not try to verify signature if '.asc' file is not accessible: + */ + if( stat( (const char *)asc_fname, &st ) == -1 ) return; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "gpg2 --verify %s %s > /dev/null 2>&1", + asc_fname, pkg_fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot verify GPG2 signature of '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 51; + + if( update_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\n\\Z1Cannot verify GPG2 signature of the package.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} +#endif + + +static void dialogrc( void ) +{ + struct stat st; + char *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /* imagine that the utility is in /sbin directory: */ + (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + if( stat( (const char *)&tmp[0], &st ) == -1 ) + { + /* finaly assume that /usr/sbin is a sbindir: */ + (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME ); + } + + setenv( "DIALOGRC", (const char *)&tmp[0], 1 ); + + free( tmp ); +} + +static char *get_curdir( void ) +{ + char *cwd = NULL; + + cwd = (char *)malloc( PATH_MAX ); + if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cwd, PATH_MAX ); + + if( getcwd( cwd, (size_t)PATH_MAX ) != NULL ) + { + char *p = NULL; + remove_trailing_slash( cwd ); + p = xstrdup( (const char *)cwd ); + free( cwd ); + return p; + } + else + { + FATAL_ERROR( "Cannot get absolute path to current directory" ); + } + + return (char *)NULL; +} + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + selfdir = get_selfdir(); + curdir = get_curdir(); + dialogrc(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + + { + int status = 0; + + /********************************************************** + Fill pkginfo data and put or replace pkglog into tmpdir: + */ + status = check_installed_package(); + + read_filelist( (void *)&rdirs, (void *)&rfiles, (const char *)rtmpdir ); + read_restorelinks( (void *)&rlinks, (const char *)rtmpdir ); + read_description( (const char *)rtmpdir, 0 ); + if( status == 0 ) + { + int integrity = check_installed_pkg_integrity(); + if( integrity ) /* not depends on --always-ask option */ + { + /* same version of requested package is already installed and correct: */ + if( ask_for_reinstall() ) + { + /* Terminate update procedure with success return code: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + } + } + } + + /************************************************************ + Getting Service Files, reading pkginfo, preserving pkglog: + */ + read_service_files(); + + if( rqck ) check_requires(); +#if defined( HAVE_GPG2 ) + if( gpgck ) verify_gpg_signature(); +#endif + + read_filelist( (void *)&dirs, (void *)&files, (const char *)tmpdir ); + read_restorelinks( (void *)&links, (const char *)tmpdir ); + + read_description( (const char *)tmpdir, 1 ); + + if( ask_for_update() ) + { + /* Terminate update: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /************ + DO REMOVE: + */ + remove_package(); + finalize_removal(); + + show_update_progress(); + + /************ + DO UPDATE: + */ + pre_update_routine(); + uncompress_package(); + restore_links(); + post_update_routine(); + finalize_update(); + + fprintf( stdout, "\033[3A" ); /* move cursor up 3 lines */ + + if( (update_mode != CONSOLE) && (update_mode == MENUDIALOG) ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ), + "\nPackage has been updated.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' has been updated.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( (update_mode != INFODIALOG) ) + { + fprintf( stdout, "\nPackage '%s-%s' has been updated.\n\n", pkgname, pkgver ); + } + } + + setup_log( "Package '%s-%s' has been updated", pkgname, pkgver ); + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} diff --git a/src/wrapper.c b/src/wrapper.c new file mode 100644 index 0000000..2b311aa --- /dev/null +++ b/src/wrapper.c @@ -0,0 +1,75 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +char *xstrdup( const char *str ) +{ + char *ret = (char *)NULL; + size_t len; + + if( !str ) return ret; + + len = strlen( str ) + 1; + + ret = (char *)malloc( len ); + if( !ret ) + FATAL_ERROR( "Out of memory, strdup failed (tried to allocate %lu bytes)", (unsigned long)len ); + + ret[len-1] = '\0'; + ret = strncpy( ret, str, len-1 ); + return ret; +} + +void *xmalloc( size_t size ) +{ + void *ret = NULL; + + ret = malloc( size ); + if( !ret ) + { + FATAL_ERROR( "Out of memory, malloc failed (tried to allocate %lu bytes)", (unsigned long)size ); + } + memset( ret, 0, size ); + return ret; +} + +void *xrealloc( void *ptr, size_t size ) +{ + void *ret = NULL; + + ret = realloc( ptr, size ); + if( !ret && !size ) + ret = realloc( ptr, 1 ); + if( !ret ) + FATAL_ERROR( "Out of memory, realloc failed" ); + return ret; +} diff --git a/src/wrapper.h b/src/wrapper.h new file mode 100644 index 0000000..c02ea6c --- /dev/null +++ b/src/wrapper.h @@ -0,0 +1,36 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _WRAPPER_H_ +#define _WRAPPER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern char *xstrdup( const char *str ); +extern void *xmalloc( size_t size ); +extern void *xrealloc( void *ptr, size_t size ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _WRAPPER_H_ */ -- cgit v1.2.3