1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612 |
- #! /bin/sh -
- # Copyright (C) 2016-2020 Matias Fonzo <selk@dragora.org>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- # EXIT STATUS
- # 0 = Successful completion
- # 1 = Minor common errors (e.g: help usage, support not available)
- # 2 = Command execution error
- # 3 = Integrity check error for compressed files
- # 4 = File empty, not regular, or expected
- # 5 = Empty or not defined variable
- # 6 = Package already installed
- # 10 = Network manager error
- PROGRAM="${0##*/}"
- LC_ALL=C; # Override locale settings.
- umask 022; # Remove write permission for group and other.
- export LC_ALL
- ### Functions
- usage()
- {
- printf '%s' \
- "Usage: $PROGRAM COMMAND [OPTIONS] [FILE]...
- "'A simple but well-integrated package manager.
- List of commands:
- warn Warn about files that will be installed
- install Install packages
- remove Remove packages
- upgrade Upgrade packages
- extract Extract packages for debugging purposes
- create Create a .tlz package from directory
- build Build packages using recipe names
- order Resolve build order through .order files
- Options when installing, removing, or upgrading software packages:
- -f, --force Force upgrade of pre-existing packages
- -k, --keep Keep package directory when remove/upgrade
- -p, --prune Prune conflicts
- -P, --packagedir=<dir> Set directory for package installations
- -t, --targetdir=<dir> Set target directory for symbolic links
- -r, --rootdir=<dir> Use the fully qualified named directory as
- the root directory for all qi operations
- Note: the target directory and the package
- directory will be relative to the specified
- directory, excepting the graft log file
- Options when building software packages using recipes:
- -a, --architecture Set architecture name for the package
- -j, --jobs Parallel jobs for the compiler
- -k, --keep Keep ${srcdir} or ${destdir} when build
- -S, --skip-questions Skip questions on completed recipes
- -1, --increment Increment release number (${release} + 1)
- -n, --no-package Do not create a .tlz package
- -i, --install Install package after the build
- -u, --upgrade Upgrade package after the build
- -o, --outdir=<dir> Where the packages produced will be written
- -w, --worktree=<dir> Where archives, patches, recipes are expected
- -s, --sourcedir=<dir> Where compressed sources will be found
- Other options:
- -N, --no-rc Do not read the configuration file
- -v, --verbose Be verbose (an extra -v gives more)
- -L, --show-location Print default directory locations and exit
- -h, --help Display this help and exit
- -V, --version Output version information and exit
- Some influential environment variables:
- TMPDIR Temporary directory for sources
- QICFLAGS C compiler flags
- QICXXFLAGS C++ compiler flags
- QILDFLAGS Linker flags
- SOURCE_DATE_EPOCH Last modification time for created packages
- When FILE is -, read standard input.
- Exit status: 0 for a normal exit, 1 for minor common errors (help usage,
- support not available, etc), 2 to indicate a command execution error;
- 3 for integrity check error on compressed files, 4 for empty, not
- regular, or expected files, 5 for empty or not defined variables,
- 6 when a package already exist, 10 for network manager errors.
- Qi home page: https://www.dragora.org
- '
- }
- warn()
- {
- printf '%s\n' "$@" 1>&2
- }
- is_readable()
- {
- if test -e "$1"
- then
- if ! test -r "$1"
- then
- echo "${PROGRAM}: cannot read ${1}: Permission denied" 1>&2
- return 1
- fi
- else
- echo "${PROGRAM}: cannot access ${1}: No such file or directory" 1>&2
- return 1
- fi
- }
- # Portable alternative to the file operator -nt (among shells)
- is_newer()
- {
- if test -n "$(find "$1" -prune -newer "$2" -print)"
- then
- return 0
- fi
- return 1
- }
- # Determine whether $2 matches pattern $1
- fnmatch()
- {
- case $2 in
- $1)
- return 0
- ;;
- *)
- return 1
- ;;
- esac
- }
- chkstatus_or_exit()
- {
- status=$?
- if test $status -ne 0
- then
- echo "^ Return status = $status" 1>&2
- exit "${1-2}"; # If not given, defaults to 2
- fi
- unset -v status
- }
- readconfig()
- {
- if test "${_readconfig:-readconfig}" = readconfig
- then
- is_readable "$HOME/.qirc" 2> /dev/null && _rcfile="$HOME/.qirc";
- echo "Importing configuration file from \`${_rcfile}' ..."
- . "$_rcfile" || chkstatus_or_exit 5
- fi
- unset -v _readconfig
- }
- ### Mode functions
- mode_build()
- {
- recipe=$1
- echo ""
- echo "{@} Building \"${recipe}\" ..."
- # A recipe is any valid regular file. Qi sets priorities for reading
- # a recipe, the order in which qi looks for a recipe is:
- #
- # 1. Current working directory.
- #
- # 2. If the specified path name does not contain "recipe" as the last
- # component. Qi will complete it by adding "recipe" to the path
- # name.
- #
- # 3. If the recipe is not in the current working directory, it will be
- # searched under '${worktree}/recipes'. The last component will be
- # completed adding "recipe" to the specified path name.
- if test ! -f "$recipe"
- then
- if test -f "${recipe}/recipe"
- then
- recipe="${recipe}/recipe"
- elif test -f "${worktree}/recipes/${recipe}/recipe"
- then
- recipe="${worktree}/recipes/${recipe}/recipe"
- fi
- fi
- test -f "$recipe" || {
- warn "\`${recipe}' is not a regular file."
- return 4
- }
- # Complain if the file name is not "recipe"
- if test "${recipe##*/}" != recipe
- then
- warn "\`${recipe}' is not a valid recipe name."
- return 4
- fi
- # Start preparations to import the recipe
- # Get working directory and base name of the recipe
- CWD="$(dirname -- "$recipe")"
- recipe="$(basename -- "$recipe")"
- # Check readability for load the recipe on success
- is_readable "${CWD}/$recipe" || exit 4
- # Re-create external directories
- mkdir -p -- "${worktree}/archive" \
- "${worktree}/patches" \
- "${worktree}/recipes" \
- "$tardir"
- # Variables treatment for the current and the next recipe.
- #
- # Unset special variables that can only be predefined in
- # the recipe and does not come from '${sysconfdir}/qirc'
- unset -v \
- srcdir destdir pkgname pkgversion pkgcategory program version release \
- fetch description homepage license replace full_pkgname \
- CFLAGS CXXFLAGS LDFLAGS
- # The following variables must be restored, later
- save_arch="${save_arch:=$arch}"
- save_jobs="${save_jobs:=$jobs}"
- save_outdir="${save_outdir:=$outdir}"
- save_opt_nopkg="${save_opt_nopkg:=$opt_nopkg}"
- # Reset variable values in case of return
- arch="$save_arch"
- jobs="$save_jobs"
- outdir="$save_outdir"
- opt_nopkg="$save_opt_nopkg"
- # The following variables cannot be redefined on the recipe
- readonly worktree netget rsync
- # Import the recipe
- . "${CWD}/$recipe"
- chkstatus_or_exit
- # Check for required variables
- if test -z "$program"
- then
- warn "${recipe}: The variable 'program' is not defined."
- exit 5
- fi
- if test -z "$version"
- then
- warn "${recipe}: The variable 'version' is not defined."
- exit 5
- fi
- if test -z "$release"
- then
- warn "${recipe}: The variable 'release' is not defined."
- exit 5
- fi
- # Pre-settings before to start building
- # Increment the release number if the option was given
- if test "$opt_incr_release" = opt_incr_release
- then
- release=$(( release + 1 ))
- fi
- # Allow the dot as definition for 'tardir'
- if test "$tardir" = .
- then
- tardir="$CWD"
- fi
- # Set default values for the following special variables
- pkgname="${pkgname:=$program}"
- pkgversion="${pkgversion:=$version}"
- srcdir="${srcdir:=${program}-$version}"
- destdir="${destdir:=${TMPDIR}/package-$pkgname}"
- # If 'pkgcategory' has been defined, prefix it using the "at" symbol
- if test -n "$pkgcategory"
- then
- pkgcategory="@${pkgcategory}"
- fi
- # Compose the full package name
- full_pkgname="${full_pkgname:=${pkgname}_${pkgversion}_${arch}-${release}${pkgcategory}}"
- # Use 'arch' as suffix for 'outdir' to have a well-organized package output
- outdir="${outdir}/${arch}"
- # If a package is going to be created the existence of a
- # previous build will be detected and reported. Under normal
- # conditions the recipe is built as long as it is newer than
- # the produced package, if not, we warn to the user about it.
- # Rebuilding the package is possible (through the force ;-)
- if test "$opt_nopkg" != opt_nopkg && \
- { test "$opt_force" != opt_force && \
- test -e "${outdir}/${full_pkgname}.tlz" ; }
- then
- if is_newer "${CWD}/$recipe" "${outdir}/${full_pkgname}.tlz"
- then
- warn \
- "" \
- "The recipe is more RECENT than the detected package:" \
- "" \
- "$( stat -c "%y %n" "${CWD}/$recipe" )" \
- "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
- "" \
- " This recipe will be processed ..." \
- ""
- elif test -e "${CWD}/post-install" && \
- is_newer "${CWD}/post-install" "${CWD}/$recipe"
- then
- warn \
- "" \
- "The post-install script is more RECENT than the recipe:" \
- "" \
- "$( stat -c "%y %n" "${CWD}/post-install" )" \
- "$( stat -c "%y %n" "${CWD}/$recipe" )" \
- "" \
- " The recipe will be re-processed ..." \
- ""
- touch "${CWD}/$recipe"
- elif test "$opt_skipqsts" = opt_skipqsts
- then
- warn "Recipe for '${full_pkgname}.tlz': Ignored." ""
- return 0
- else
- warn \
- "" \
- "This recipe ALREADY produced a package:" \
- "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
- "" \
- "The recipe is still OLDER than the produced package:" \
- "$( stat -c "%y %n" "${CWD}/$recipe" )" \
- "" \
- " Probably nothing has changed." \
- ""
- # In non-interactive mode, the user is asked about
- # rebuilding the package. In interactive mode,
- # the user need to pass the option explicitly
- if test ! -t 0
- then
- printf '%s\n' \
- "Do you want to rebuild this package?" \
- "1) Yes, built it" \
- "2) No, skip it [default]" \
- "3) Resume, skipping completed recipes" \
- "Enter an option number:" > /dev/tty
- IFS= read -r ANSWER < /dev/tty || exit 2;
- case $ANSWER in
- 1*)
- echo "$ANSWER" > /dev/tty
- unset -v ANSWER
- ;;
- 3*)
- unset -v ANSWER
- echo "=== Building UNPROCESSED/MODIFIED recipes ..." > /dev/tty
- echo ""
- opt_skipqsts=opt_skipqsts
- readonly opt_skipqsts
- return 0
- ;;
- *)
- unset -v ANSWER
- echo "Recipe for '${full_pkgname}.tlz': Cancelled." > /dev/tty
- echo ""
- return 0
- ;;
- esac
- else
- warn "Use the --force option to reprocess ${CWD}/$recipe." ""
- return 6;
- fi
- fi
- fi
- # Fetch remote sources
- echo "=== Fetching remote sources if needed ..."
- if test -n "$fetch"
- then
- for origin in $fetch
- do
- _source="${origin##*/}"
- echo "=== Looking for \"$_source\" ..."
- echo "=== Verifying checksum file \`${tardir}/${_source}.sha256'"
- if test -e "${tardir}/${_source}.sha256"
- then
- ( cd -- "$tardir" && sha256sum - ) < "${tardir}/${_source}.sha256"
- chkstatus_or_exit
- continue;
- else
- warn "${_source}.sha256: Checksum file does not exist, yet."
- fi
- # Download source or resume if allowed
- if test ! -e "${tardir}/$_source"
- then
- warn " Can't find \"${_source}\" at \"${tardir}\";" \
- "attempting to get it from ${origin%/*} ..."
- fi
- case $origin in
- rsync://*)
- (
- cd -- "$tardir" && $rsync "$origin" || exit $?
- sha256sum "$_source" > "${_source}.sha256"
- ); chkstatus_or_exit 10
- ;;
- *://*)
- (
- cd -- "$tardir" && $netget "$origin" || exit $?
- sha256sum "$_source" > "${_source}.sha256"
- ); chkstatus_or_exit 10
- ;;
- *)
- warn "${PROGRAM}: Unrecognized protocol for ${origin}."
- exit 4
- esac
- done
- unset -v origin _source
- else
- warn "${recipe}: The variable 'fetch' is empty."
- fi
- # Prepare special directories for build the source,
- # the destination and the output of the package
- echo "=== Preparing directories ..."
- if test -z "$keep_srcdir"
- then
- if test -e "${TMPDIR}/$srcdir"
- then
- rm -r -- "${TMPDIR:?}/$srcdir" || chkstatus_or_exit
- echo "removed directory: '${TMPDIR}/$srcdir'"
- fi
- else
- warn \
- "WARNING: The variable 'keep_srcdir' has been set (${keep_srcdir})."
- fi
- if test -z "$keep_destdir"
- then
- if test -e "$destdir"
- then
- rm -r -- "$destdir" || chkstatus_or_exit
- echo "removed directory: '$destdir'"
- fi
- mkdir -p -- "$destdir" || chkstatus_or_exit
- echo "mkdir: created directory '$destdir'"
- fi
- if test ! -e "$outdir"
- then
- mkdir -p -- "$outdir" || chkstatus_or_exit
- echo "mkdir: created directory '$outdir'"
- fi
- echo "=== Changing to '${TMPDIR}' ..."
- cd -- "$TMPDIR" || chkstatus_or_exit
- # Set trap before to run the build() function in order
- # to catch the return status, exit code 2 if fails
- trap 'chkstatus_or_exit 2' EXIT HUP INT QUIT ABRT TERM
- # Determine if the debugging indicators of the shell should be
- # retained, assuming that it has been previously passed
- case $- in *x*)
- _xtrace_flag=_xtrace_flag_is_set ;;
- esac
- echo "=== Running the 'build' function ..."
- build
- unset -f build
- # Turn off possible shell flags coming from the recipe
- set +e
- if test "${_xtrace_flag:+$_xtrace_flag}" != _xtrace_flag_is_set
- then
- set +x
- fi
- # Reset given signals
- trap - EXIT HUP INT QUIT ABRT TERM
- # If 'destdir' is empty, the package won't be created
- if rmdir -- "$destdir" 2> /dev/null
- then
- warn "The package \"${full_pkgname}.tlz\" won't be created. 'destdir' is empty."
- opt_nopkg=opt_nopkg
- fi
- # Create (make) the package
- if test "$opt_nopkg" != opt_nopkg
- then
- # Edit the recipe when 'release' is incremented
- if test "$opt_incr_release" = opt_incr_release
- then
- echo ",s/^\\(release\\)=.*/\\1=${release}/"$'\nw' | \
- ed "${CWD}/$recipe" || chkstatus_or_exit
- fi
- mkdir -p -- "${destdir}/var/lib/qi" || chkstatus_or_exit
- # Include a copy of the recipe into the package
- cp -p "${CWD}/$recipe" \
- "${destdir}/var/lib/qi/${full_pkgname}.recipe" && \
- chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.recipe"
- chkstatus_or_exit
- # Detect post-install script for inclusion
- if test -f "${CWD}/post-install"
- then
- echo "${CWD}/post-install: Detected."
- cp -p "${CWD}/post-install" \
- "${destdir}/var/lib/qi/${full_pkgname}.sh" && \
- chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.sh"
- chkstatus_or_exit
- fi
- # Detect declared package name(s) for later replacement
- if test -n "$replace"
- then
- warn \
- "=== The following package names has been declared for replacement:" \
- "$replace"
- : > "${destdir}/var/lib/qi/${full_pkgname}.replace"
- for item in $replace
- do
- printf '%s\n' "$replace" >> \
- "${destdir}/var/lib/qi/${full_pkgname}.replace"
- done
- unset -v item
- fi
- # Create (external) meta file for package information,
- # make a copy of it for the package database
- echo "=== Creating meta file ${full_pkgname}.tlz.txt ..."
- do_meta > "${outdir}/${full_pkgname}.tlz.txt" || chkstatus_or_exit
- cp -p "${outdir}/${full_pkgname}.tlz.txt" \
- "${destdir}/var/lib/qi/${full_pkgname}.txt" || chkstatus_or_exit
- # Produce the package
- cd -- "$destdir" && mode_create "${outdir}/${full_pkgname}.tlz"
- fi
- # Back to the current working directory
- cd -- "$CWD" || chkstatus_or_exit
- echo "=== Deleting 'srcdir', 'destdir' ..."
- if test "$opt_keep" != opt_keep
- then
- if test -z "$keep_srcdir"
- then
- srcdir="${srcdir%%/*}"; # Directory name without parents.
- if test -e "${TMPDIR}/$srcdir"
- then
- rm -r -- "${TMPDIR:?}/$srcdir" || chkstatus_or_exit
- echo "removed directory: '${TMPDIR}/$srcdir'"
- fi
- fi
- if test -z "$keep_destdir"
- then
- if test -e "$destdir"
- then
- rm -r -- "$destdir" || chkstatus_or_exit
- echo "removed directory: '$destdir'"
- fi
- fi
- else
- warn \
- " Request via --keep to preserve 'srcdir' or 'destdir' has been made:" \
- "${TMPDIR}/$srcdir" \
- "$destdir"
- fi
- # Install or upgrade the package after build
- if test "$opt_nopkg" != opt_nopkg
- then
- if test "$opt_install" = opt_install
- then
- mode_install "${outdir}/${full_pkgname}.tlz"
- elif test "$opt_upgrade" = opt_upgrade
- then
- mode_upgrade "${outdir}/${full_pkgname}.tlz"
- fi
- fi
- echo "{@} All done for \"${CWD}/${recipe}\"."
- echo ""
- }
- mode_create()
- {
- directory="$(dirname -- "$1")"
- # Perform sanity checks
- if ! fnmatch '/?*' "$directory"
- then
- warn "${PROGRAM}: Output directory \`${directory}' is not fully qualified"
- exit 4
- fi
- is_readable "$directory" || exit 4
- name="$(basename -- "$1")"
- echo ""
- echo "{#} Creating package name \`${name}' ..."
- if test "$name" = "${name%.tlz}"
- then
- warn "Package format '$name' not supported." \
- "It should be \"name_version_architecture-release[@pkgcategory].tlz\""
- exit 4
- fi
- # Pass extra options for tarlz(1)
- if test -n "$SOURCE_DATE_EPOCH"
- then
- tarlz_opts="--mtime=@${SOURCE_DATE_EPOCH}"
- fi
- ( umask 022 ; tarlz --solid -9 $tarlz_opts -cvf - -- * ) \
- > "${directory}/$name"
- chkstatus_or_exit 3
- unset -v tarlz_opts
- ( cd -- "$directory" && sha256sum "$name" > "${name}.sha256" )
- chkstatus_or_exit 4
- echo "{#} Package \"${name}\" created on ${directory}."
- # Remove used variables
- unset -v directory name
- echo ""
- }
- mode_remove()
- {
- expunge="${packagedir}/$(basename -- "$1" .tlz)"
- echo ""
- echo "<<< Removing package \`$rootdir${expunge}' ..."
- # Complain if the package directory does not exist
- test -e "$rootdir${expunge}" || {
- warn "Package directory '$rootdir${expunge}' does not exist."
- return 4
- }
- # Complain if the package directory cannot be well-read
- is_readable "$rootdir${expunge}" || exit 4
- # Remove package from Graft control
- # Scan for possible conflicts, stop if arise
- if test "$opt_prune" != opt_prune
- then
- echo "=== Checking for possible conflicts ..."
- if graft -d -n $graft_r -t "$targetdir" "$expunge" 2>&1 | \
- grep "^CONFLICT"
- then
- warn "" \
- " A conflict occurred during uninstallation;" \
- "Unless the --prune option is given, this package will be PRESERVED."
- return 6;
- fi
- fi
- # Remove objects (files, links or directories) from the target
- # directory that are in conflict with the package directory
- echo "=== Pruning any conflict ..."
- graft -p -D -u $graft_r -t "$targetdir" "$expunge"
- chkstatus_or_exit 2
- echo "=== Disabling links ..."
- graft -d -D -u $graft_v $graft_r -t "$targetdir" "$expunge"
- chkstatus_or_exit 2
- # Delete package directory
- if test "$opt_keep" != opt_keep
- then
- echo "=== Deleting package directory, if exists as such ..."
- if test -e "${rootdir}$expunge"
- then
- rm -r -- "${rootdir}$expunge" || chkstatus_or_exit
- echo "removed directory: '${rootdir}$expunge'"
- fi
- fi
- # Remove used variables
- unset -v expunge
- echo ""
- }
- mode_install()
- {
- # Complain if the package cannot be well-read
- is_readable "$1" || exit 4
- # Complain if the package does not end in .tlz
- if ! fnmatch '*.tlz' "$1"
- then
- warn "\`${1}' does not end in .tlz"
- return 4
- fi
- # Make preparations to install the package
- # Get the filename
- name="$(basename -- "$1" .tlz)"
- echo ""
- echo ">>> Installing package \`${name}' ..."
- echo "=== Checking tarball integrity ..."
- tarlz --missing-crc -tf "$1" > /dev/null
- chkstatus_or_exit 3
- # To accept random directory from the upgrade mode
- _packagedir="$2"
- _packagedir="${_packagedir:=$packagedir}"
- # Create package directory using 'name'
- if ! test -d "$rootdir${_packagedir}/$name"
- then
- mkdir -p -- "$rootdir${_packagedir}/$name" || chkstatus_or_exit
- echo "mkdir: created directory '$rootdir${_packagedir}/$name'"
- fi
- # Scan for possible conflicts, stop if arise
- if test "$opt_prune" != opt_prune
- then
- echo "=== Checking for possible conflicts ..."
- if graft -i -n $graft_r -t "$targetdir" "${_packagedir}/$name" 2>&1 | \
- grep "^CONFLICT"
- then
- warn "" \
- " A conflict occurred during installation;" \
- "Unless the --prune option is given, this package won't be LINKED."
- return 6;
- fi
- fi
- echo "=== Decompressing package ..."
- ( cd -- "$rootdir${_packagedir}/$name" && tarlz -xpf - ) < "$1"
- chkstatus_or_exit 3
- # Transite package to Graft control
- # Remove objects (files, links or directories) from the target
- # directory that are in conflict with the package directory
- echo "=== Pruning any conflict ..."
- graft -p -D -u $graft_r -t "$targetdir" "${_packagedir}/$name"
- chkstatus_or_exit 2
- echo "=== Enabling symbolic links ..."
- graft -i -P $graft_v $graft_r -t "$targetdir" "${_packagedir}/$name"
- chkstatus_or_exit 2
- # Avoid unnecessary runs coming from the upgrade mode,
- # this is when the incoming package is **pre-installed**
- if test "$_isUpgrade" != _isUpgrade.on
- then
- # Show package description
- if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.txt"
- then
- awk '/^#/' "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.txt"
- elif test -r "${1}.txt"
- then
- # From external meta file (current directory)
- awk '/^#/' "${1}.txt"
- else
- warn "Description file not found for '$name'."
- fi
- # Check and run the post-install script if exist
- if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.sh"
- then
- echo "=== Running post-install script for \`${name}' ..."
- (
- # Rely on 'targetdir' if 'rootdir' is empty
- cd -- "${rootdir:=$targetdir}"/ && \
- . "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.sh"
- )
- fi
- # Check if there are declared packages for replacement
- if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.replace"
- then
- while read -r line
- do
- for replace in "$rootdir${_packagedir}/${line%%_*}"_*
- do
- if ! test -e "$replace"
- then
- warn "<^> Declared package \`${replace}' does not exist. (ignoring)"
- continue;
- fi
- replace="${replace##*/}"
- # The search for the package to be replaced cannot
- # be the same to the incoming package, even to the
- # temporary location coming from the upgrade mode
- if test "$replace" = "$name" || \
- test "_x_${replace}" = "_x_${PRVLOC##*/}"
- then
- continue;
- fi
- warn "WARNING: Replacing package \`${replace}' ..."
- # Since the links belongs to the new package, only
- # those which are not in conflict can be deleted.
- # To complete, we will remove the package directory
- graft -d -D -u $graft_r \
- -t "$targetdir" "$replace" > /dev/null 2>&1
- rm -rf -- "$rootdir${_packagedir}/$replace"
- done
- done < "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.replace"
- fi
- fi
- # Remove used variables
- unset -v name _packagedir
- echo ""
- }
- mode_order()
- {
- # Complain if the file cannot be well-read
- is_readable "$1" || exit 4
- # Complain if the file does not end in .order
- if ! fnmatch '*.order' "$1"
- then
- warn "\`${1}' does not end in .order"
- return 4
- fi
- # Get a clean list of the file while printing its contents in reverse
- # order. The last `awk 'in the pipeline eliminates the non-consecutive
- # lines, the duplicates. Blank lines, colons and parentheses are
- # simply ignored, comment lines beginning with '#' are allowed
- awk \
- '{ gsub( /:|^#(.*)$|\([^)]*)|^$/,"" ); for( i=NF; i > 0; i-- ) print $i }' \
- "$1" | awk '!s[$0]++'
- }
- mode_upgrade()
- {
- # Complain if the package does not end in .tlz
- if ! fnmatch '*.tlz' "$1"
- then
- warn "\`${1}' does not end in .tlz"
- return 4
- fi
- # Get the filename
- incoming="$(basename -- "$1" .tlz)"
- echo ""
- echo "{%} Upgrading package \`${incoming}' ..."
- # Check package pre-existence
- if test "$opt_force" != opt_force && \
- test -e "$rootdir${packagedir}/$incoming"
- then
- warn \
- "" \
- " The package to be upgraded already exist;" \
- "Unless the --force option is given, this package won't be UPGRADED."
- return 6;
- fi
- # Ignore some signals until the upgrade process is complete
- trap "" HUP INT QUIT ABRT TERM
- # Check blacklisted packages
- echo "=== Checking blacklist ..."
- for item in $blacklist
- do
- case $item in
- ${incoming}*)
- warn \
- "Package name declared on the blacklist \"${incoming}\"." \
- "" \
- " This package will be INSTALLED instead of being upgraded."
- opt_prune=opt_prune mode_install "$1"
- return 0
- ;;
- esac
- done
- unset -v item
- # Prepare the package to install it in a temporary but custom location
- # Set random directory under 'rootdir/packagedir' using 'incoming' as name
- PRVLOC=$(mktemp -dp "$rootdir${packagedir}" ${incoming}.XXXXXXXXXXXX) || exit 2
- echo "=== Pre-installing package in temporary location ..."
- opt_prune=opt_prune # Turn on prune operation.
- _isUpgrade=_isUpgrade.on mode_install "$1" "$PRVLOC" > /dev/null
- _isUpgrade=_isUpgrade.off
- echo "=== Looking for installations under the same name ..."
- for long_name in "$rootdir${packagedir}/${incoming%%_*}"_*
- do
- # Check if it is a valid directory and if the incoming
- # package is not equivalent to a previous installation;
- # also check if the package to be deleted is equivalent
- # to the temporary, private location.
- if ! test -d "$long_name"
- then
- continue;
- fi
- if test "${long_name##*/}" = "$incoming"
- then
- continue;
- fi
- if test "$long_name" = "$PRVLOC"
- then
- continue;
- fi
- warn "Proceeding to the removal of ${long_name} ..."
- # A package directory will be preserved if --keep is given
- mode_remove "${long_name##*/}" > /dev/null
- done
- unset -v long_name
- # Re-install the package removing the temporary location
- mode_install "$1"
- opt_prune=opt_prune.off # Turn off prune operation.
- echo "=== Deleting temporary location ..."
- rm -rf -- "$PRVLOC" || chkstatus_or_exit
- echo "removed directory: '$PRVLOC'"
- echo "{%} Upgraded from \"${1}\"."
- # Remove remaining variables
- unset -v incoming PRVLOC
- echo ""
- # Reset given signals
- trap - HUP INT QUIT ABRT TERM
- }
- mode_warn()
- {
- # Complain if the package cannot be well-read
- is_readable "$1" || exit 4
- # Complain if the package does not end in .tlz
- if ! fnmatch '*.tlz' "$1"
- then
- warn "\`${1}' does not end in .tlz"
- return 4
- fi
- # List content of files excluding directories
- while test -f "$1"
- do
- tarlz -tvvf "$1" | awk '!/^drwx/'
- chkstatus_or_exit 3
- shift;
- done
- return 0
- }
- mode_extract()
- {
- # Perform sanity checks before package extraction
- is_readable "$1" || exit 4
- test -f "$1" || {
- warn "\`${1}' is not a regular file."
- return 4
- }
- # Preparations to extract the package
- name="$(basename -- "$1" .tlz)"
- # Set random directory under 'TMPDIR' using 'name'
- PRVDIR=$(mktemp -dp "$TMPDIR" ${name}.XXXXXXXXXXXX) || exit 2
- # Trap to remove 'PRVDIR' on disruptions
- trap 'rm -rf -- "$PRVDIR"' HUP INT ABRT TERM
- # Create 'PRVDIR' removing access for all but user
- ( umask 077 ; mkdir -- "$PRVDIR" )
- mkdir -p -- "$PRVDIR"
- chmod 700 -- "$PRVDIR"
- echo ""
- echo "--- Extracting package \`${name}' ..."
- ( umask 000 ; cd -- "$PRVDIR" && tarlz -xvf - ) < "$1"
- if test $? -ne 0
- then
- # Try to remove (empty) 'PRVDIR' on failure
- rmdir -- "$PRVDIR"
- exit 3;
- fi
- echo ""
- echo "\"${name}\" has been extracted on \`${PRVDIR}'"
- # Reset given signals
- trap - HUP INT ABRT TERM
- # Remove used variables
- unset -v name PRVDIR
- }
- ### Extra functions to be used during the modes
- unpack()
- {
- for file in "$@"
- do
- case $file in
- *.tar)
- tar -tf "$file" > /dev/null && \
- tar -xpf "$file"
- chkstatus_or_exit 3
- ;;
- *.tar.gz | *.tgz | *.tar.Z )
- gzip -cd "$file" | tar -tf - > /dev/null && \
- gzip -cd "$file" | tar -xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.bz2 | *.tbz2 | *.tbz )
- bzip2 -cd "$file" | tar -tf - > /dev/null && \
- bzip2 -cd "$file" | tar -xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.lz | *.tlz )
- lzip -cd "$file" | tar -tf - > /dev/null && \
- lzip -cd "$file" | tar -xpf -
- chkstatus_or_exit 3
- ;;
- *.tar.xz | *.txz )
- xz -cd "$file" | tar -tf - > /dev/null && \
- xz -cd "$file" | tar -xpf -
- chkstatus_or_exit 3
- ;;
- *.zip | *.ZIP )
- unzip -t "$file" > /dev/null && \
- unzip "$file" > /dev/null
- chkstatus_or_exit 3
- ;;
- *.gz)
- gzip -t "$file" && \
- gzip -cd "$file" > "$(basename -- "$file" .gz)"
- chkstatus_or_exit 3
- ;;
- *.Z)
- gzip -t "$file" && \
- gzip -cd "$file" > "$(basename -- "$file" .Z)"
- chkstatus_or_exit 3
- ;;
- *.bz2)
- bzip2 -t "$file" && \
- bzip2 -cd "$file" > "$(basename -- "$file" .bz2)"
- chkstatus_or_exit 3
- ;;
- *.lz)
- lzip -t "$file" && \
- lzip -cd "$file" > "$(basename -- "$file" .lz)"
- chkstatus_or_exit 3
- ;;
- *.xz)
- xz -t "$file" && \
- xz -cd "$file" > "$(basename -- "$file" .xz)"
- chkstatus_or_exit 3
- ;;
- *)
- warn "${PROGRAM}: cannot unpack ${file}: Unsupported extension"
- exit 1
- esac
- done
- unset -v file
- }
- do_meta()
- {
- # Extract information from the recipe to create the meta file.
- #
- # The package description is pre-formatted in 78 columns,
- # the '#' character and a space is added as prefix to conform
- # the 80 columns in total
- printf '%s\n' "$description" | fold -w 78 | awk '$0="# " $0'
- printf '%s' \
- "
- QICFLAGS=\"$QICFLAGS\"
- QICXXFLAGS=\"$QICXXFLAGS\"
- QILDFLAGS=\"$QILDFLAGS\"
- pkgname=$pkgname
- pkgversion=$pkgversion
- arch=$arch
- release=$release
- pkgcategory=\"${pkgcategory#@*}\"
- full_pkgname=$full_pkgname
- blurb=\"$(printf '%s\n' "$description" | sed -e '/^$/d;2q')\"
- homepage=\"$homepage\"
- license=\"$license\"
- fetch=\"$fetch\"
- replace=\"$replace\"
- "
- }
- ### Default values
- packagedir=@PACKAGEDIR@
- targetdir=@TARGETDIR@
- blacklist="perl5 graft tarlz plzip musl glibc coreutils bash mksh"
- _rcfile=@SYSCONFDIR@/qirc
- opt_install=opt_install.off
- opt_upgrade=opt_upgrade.off
- opt_force=opt_force.off
- opt_keep=opt_keep.off
- opt_incr_release=opt_incr_release.off
- opt_skipqsts=opt_skipqsts.off
- opt_nopkg=opt_nopkg.off
- opt_prune=opt_prune.off
- verbose=0
- rootdir=""
- arch=@ARCH@
- jobs=1
- mode=""
- _readstdin=""
- graft_v=""
- graft_r=""
- _isUpgrade=_isUpgrade.off
- keep_srcdir=""
- keep_destdir=""
- TMPDIR="${TMPDIR:-/usr/src/qi/build}"
- QICFLAGS="${QICFLAGS:--g0 -O2}"
- QICXXFLAGS="${QICXXFLAGS:--g0 -O2}"
- QILDFLAGS="${QILDFLAGS:--s}"
- worktree=/usr/src/qi
- tardir=${worktree}/sources
- outdir=/var/cache/qi/packages
- netget="wget -c -w1 -t3 --no-check-certificate"
- rsync="rsync -v -a -L -z -i --progress"
- configure_args="--prefix=@PREFIX@ --libexecdir=@LIBEXECDIR@ --bindir=@BINDIR@ --sbindir=@SBINDIR@ --sysconfdir=@SYSCONFDIR@ --localstatedir=@LOCALSTATEDIR@"
- infodir=@INFODIR@
- mandir=@MANDIR@
- docdir=@DOCDIR@
- # Store (default) directory locations
- QI_TARGETDIR=$targetdir
- QI_PACKAGEDIR=$packagedir
- QI_WORKTREE=$worktree
- QI_TARDIR=$tardir
- QI_OUTDIR=$outdir
- ### Parse commands and options
- validate_mode()
- {
- if test -n "$mode"
- then
- warn "${PROGRAM}: First defined command: ${mode#*_}" \
- "Switching to another command is not allowed (${1})."
- exit 1
- fi
- }
- validate_option()
- {
- if test -z "$2"
- then
- warn "${PROGRAM}: The '${1}' option requires an argument" \
- "Try '${PROGRAM} --help' for more information."
- exit 1
- fi
- }
- validate_directory()
- {
- if test ! -d "$2"
- then
- warn "${PROGRAM}: ${1} \"${2}\" must be a valid directory name"
- exit 1
- fi
- }
- validate_digit()
- {
- name="$1"
- shift
- # Taken from https://mywiki.wooledge.org/BashFAQ/054
- case ${1#[-+]} in
- '')
- warn "${PROGRAM}: The '${name}' option has no defined value"
- exit 1
- ;;
- *.*.*)
- warn "${PROGRAM}: The '${name}' option has more than one decimal point on it \"${1}\""
- exit 1
- ;;
- *[!0-9]*)
- warn "${PROGRAM}: The '${name}' option contains a non-valid digit on it \"${1}\""
- exit 1
- ;;
- esac
- unset -v name
- }
- while test $# -gt 0
- do
- case $1 in
- warn)
- validate_mode warn
- mode=mode_warn
- ;;
- install)
- validate_mode install
- readconfig
- mode=mode_install
- ;;
- remove)
- validate_mode remove
- readconfig
- mode=mode_remove
- ;;
- upgrade)
- validate_mode upgrade
- readconfig
- mode=mode_upgrade
- ;;
- extract)
- validate_mode extract
- readconfig
- mode=mode_extract
- ;;
- create)
- validate_mode create
- readconfig
- mode=mode_create
- ;;
- order)
- validate_mode order
- readconfig
- mode=mode_order
- ;;
- build)
- validate_mode build
- readconfig
- mode=mode_build
- ;;
- --no-rc | -N )
- _readconfig=readconfig.off
- ;;
- --install | -i )
- opt_install=opt_install
- ;;
- --upgrade | -u )
- opt_upgrade=opt_upgrade
- ;;
- --force | -f )
- opt_force=opt_force
- ;;
- --keep | -k )
- opt_keep=opt_keep
- ;;
- --prune | -p )
- opt_prune=opt_prune
- ;;
- --packagedir | -P )
- validate_option "$1" "$2"
- packagedir="$2"
- validate_directory "$1" "$packagedir"
- shift
- ;;
- --packagedir=*)
- validate_option "$1" "$2"
- packagedir="${1#*=}"
- validate_directory "$1" "$packagedir"
- ;;
- --targetdir | -t )
- validate_option "$1" "$2"
- targetdir="$2"
- validate_directory "$1" "$targetdir"
- shift
- ;;
- --targetdir=*)
- validate_option "$1" "$2"
- targetdir="${1#*=}"
- validate_directory "$1" "$targetdir"
- ;;
- --rootdir | -r )
- validate_option "$1" "$2"
- rootdir="$2"
- validate_directory "$1" "$rootdir"
- shift
- ;;
- --rootdir=*)
- validate_option "$1" "$2"
- rootdir="${1#*=}"
- validate_directory "$1" "$rootdir"
- ;;
- --outdir | -o )
- validate_option "$1" "$2"
- outdir="$2"
- validate_directory "$1" "$outdir"
- shift
- ;;
- --outdir=*)
- validate_option "$1" "$2"
- outdir="${1#*=}"
- validate_directory "$1" "$outdir"
- ;;
- --worktree | -w )
- validate_option "$1" "$2"
- worktree="$2"
- validate_directory "$1" "$worktree"
- shift
- ;;
- --worktree=*)
- validate_option "$1" "$2"
- worktree="${1#*=}"
- validate_directory "$1" "$worktree"
- ;;
- --sourcedir | -s )
- validate_option "$1" "$2"
- tardir="$2"
- validate_directory "$1" "$tardir"
- shift
- ;;
- --sourcedir=*)
- validate_option "$1" "$2"
- tardir="${1#*=}"
- validate_directory "$1" "$tardir"
- ;;
- --architecture | -a )
- validate_option "$1" "$2"
- arch="$2"
- shift
- ;;
- --arch=*)
- validate_option "$1" "$2"
- arch="${1#*=}"
- ;;
- --jobs | -j )
- jobs="$2"
- validate_digit "$1" "$jobs"
- shift
- ;;
- -j[0-9]*)
- jobs="${1#-j*}"
- validate_digit '-j' "$jobs"
- ;;
- --jobs=*)
- jobs="${1#*=}"
- validate_digit '--jobs=' "$jobs"
- ;;
- --no-package | -n )
- opt_nopkg=opt_nopkg
- ;;
- --increment | -1 )
- opt_incr_release=opt_incr_release
- ;;
- --skip-questions | -S )
- opt_skipqsts=opt_skipqsts
- ;;
- --verbose | -v )
- verbose=$(( verbose + 1 ))
- ;;
- -vv)
- # ^ Trick for a second -v.
- verbose=2
- ;;
- --show-location | -L )
- printf '%s\n' \
- "QI_TARGETDIR=$QI_TARGETDIR" \
- "QI_PACKAGEDIR=$QI_PACKAGEDIR" \
- "QI_WORKTREE=$QI_WORKTREE" \
- "QI_TARDIR=$QI_TARDIR" \
- "QI_OUTDIR=$QI_OUTDIR"
- exit 0
- ;;
- --help | --hel | --he | --h | '--?' | -help | -hel | -he | -h | '-?' | \
- help )
- usage
- exit 0
- ;;
- --version | --versio | --versi | --vers | \
- -version | -versio | -versi | -vers | -V | version )
- echo "$PROGRAM version @VERSION@"
- exit 0
- ;;
- '-')
- _readstdin=readstdin
- break
- ;;
- --)
- shift
- break; # End of options.
- ;;
- -*)
- warn "qi: Unrecognized option: $1" \
- "Try '${PROGRAM} --help' for more information."
- exit 1
- ;;
- *)
- break; # No more options.
- ;;
- esac
- shift
- done
- unset -f \
- readconfig validate_mode validate_option validate_directory validate_digit
- # When there are no arguments, show the help
- if test $# -eq 0
- then
- usage
- exit 1
- fi
- unset -f usage
- # Program sanity check
- for need in awk basename chmod cp dirname find fold graft grep \
- mkdir mktemp rm rmdir sed sha256sum stat tarlz ; \
- do
- if ! \command -v $need > /dev/null
- then
- warn "${PROGRAM}: Prerequisite \`${need}' not found in PATH"
- exit 2
- fi
- done
- unset -v need
- # Set verbosity level/flag
- if test "$verbose" -gt 0
- then
- if test "$verbose" -eq 1
- then
- graft_v=-v
- else
- graft_v=-V
- fi
- fi
- # Read standard input if FILE is -, or when
- # FILE is not connected to a terminal
- if test "$_readstdin" = readstdin
- then
- if test -t 0
- then
- warn "qi: I won't read from a connected terminal." \
- "Try '${PROGRAM} --help' for more information."
- exit 1
- fi
- # Unset positional parameters setting $# to zero
- set --
- # Assign remaining arguments to the positional parameters
- while read -r input
- do
- set -- "$@" "$input"
- done
- fi
- unset -v _readstdin
- if test -z "$mode"
- then
- warn "qi: We need at least one (valid) command." \
- "Try '${PROGRAM} --help' for more information."
- exit 4
- fi
- # Validate 'packagedir' and 'targetdir' as canonical directories
- # The single slash '/' does not qualify here
- if ! fnmatch '/?*' "$packagedir"
- then
- warn "${PROGRAM}: Package directory \`${packagedir}' is not fully qualified"
- exit 4
- fi
- if test ! -d "$packagedir"
- then
- warn "${PROGRAM}: Package directory \`${packagedir}' does not exist"
- exit 4
- fi
- # The single slash '/' is valid here
- if ! fnmatch '/*' "$targetdir"
- then
- warn "${PROGRAM}: Target directory \`${targetdir}' is not fully qualified"
- exit 4
- fi
- if test ! -d "$targetdir"
- then
- warn "${PROGRAM}: Target directory \`${targetdir}' does not exist"
- exit 4
- fi
- # Validate 'rootdir' directory
- if test -n "$rootdir"
- then
- if test -d "$rootdir" && test "$rootdir" != /
- then
- rootdir="${rootdir%/}" # Remove slash from the end.
- # A workaround for graft-2.13+. The specified directory is
- # relative to the log file, we prepend it inside 'rootdir'
- eval "$(graft -L)" ; GRAFT_LOGFILE="${GRAFT_LOGFILE:=/var/log/graft}"
- mkdir -p -- "$rootdir$(dirname -- "$GRAFT_LOGFILE")" || chkstatus_or_exit
- # Compose 'rootdir' and log file option to be used with graft(1)
- graft_r="-r $rootdir -l $GRAFT_LOGFILE"
- # Unset variables coming from eval
- unset -v GRAFT_PERL GRAFT_LOGFILE GRAFT_TARGETDIR GRAFT_PACKAGEDIR
- else
- warn "${PROGRAM}: Root directory \`${rootdir}' is not fully qualified"
- exit 4
- fi
- readonly rootdir
- export rootdir
- fi
- # Ensure 'TMPDIR' creation to prefix temporary files
- if test ! -d "$TMPDIR"
- then
- mkdir -p -- "$TMPDIR" || chkstatus_or_exit
- fi
- readonly TMPDIR packagedir targetdir
- # Process each package or recipe provided on the command-line
- for package in "$@"
- do
- $mode "$package"
- done
|