diff --git a/ChangeLog b/ChangeLog index 31d0f35..fd28b6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ ChangeLog for pkg-get +0.5.0 - Fix doscript() to accommodate rootfs other than "/", + and ports with dashes in their names + - Use native Perl routines to reduce the number of external processes + - Consolidate code + - Do more tasks in parallel when generating the metadata + - Update the html index too, when individual packages are passed to + pkg-repgen + - Replace wget by curl, to avoid depending on packages outside 'core' + - Update man-pages and README + +0.4.7 - Fix man-page location + 0.4.6 - Fixed warnings on output of diff command - Use compression-mode defined in pkgmk.conf - pkg-repgen.pl: Improved prt-get commands and added --prtdir switch diff --git a/Makefile b/Makefile index 9966b71..512cdd0 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ NAME=pkg-get -VERSION="0.4.6" +VERSION="0.5.0" PREFIX=/usr CFGDIR=/etc @@ -8,27 +8,18 @@ MANDIR=/usr/share/man all: @echo "Use 'make install' to install pkg-get" -man: - #makeman doc/pkg-get.8.txt - #makeman doc/pkg-repgen.8.txt - #man2ps doc/pkg-get.8 | ps2pdf14 - > doc/pkg-get.pdf - -dist: man +dist: rm -rf ${NAME}-${VERSION} - mkdir ${NAME}-${VERSION} - cp -r doc scripts Makefile ChangeLog COPYING README TODO ${NAME}-${VERSION} - rm -f ${NAME}-${VERSION}/doc/*.txt - rm -f ${NAME}-${VERSION}/doc/*.pdf + mkdir -p ${NAME}-${VERSION}/doc + cp -r scripts Makefile ChangeLog COPYING README TODO ${NAME}-${VERSION} + cp doc/*.* ${NAME}-${VERSION}/doc tar cvzf ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION} rm -rf ${NAME}-${VERSION} install: - - install -D -m 755 scripts/pkg-get.pl ${PREFIX}/bin/pkg-get install -D -m 755 scripts/pkg-repgen.pl ${PREFIX}/bin/pkg-repgen install -D -m 755 scripts/pkg-get.pl ${PREFIX}/bin/pkg-get - install -D -m 644 doc/pkg-get.8 ${MANDIR}/man8/pkg-get.8 - install -D -m 644 doc/pkg-get.conf ${CFGDIR}/pkg-get.conf install -D -m 644 doc/pkg-repgen.8 ${MANDIR}/man8/pkg-repgen.8 install -D -m 644 doc/pkg-get.8 ${MANDIR}/man8/pkg-get.8 - + install -D -m 644 doc/pkg-get.conf ${CFGDIR}/pkg-get.conf + install -D -m 644 doc/style.html ${PREFIX}/share/pkg-get/style.html diff --git a/README b/README index 1e0eb33..46e365a 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ INTRODUCTION ---------------------------------------------------------------------------- -pkg-get is a package / repository management tool for CRUX Linux. -Syntax and features are very close (often a carbon copy) -to the ones found in the port management tool 'prt-get' +pkg-get is a package / repository management tool for CRUX. +Syntax and features are very close to (often a carbon copy of) +the ones found in the port management tool 'prt-get' by Johannes Winkelmann. In fact pkg-get was developed as a prt-get/ports drop-in replacement for systems in which it is preferable to handle binary packages instead @@ -11,10 +11,10 @@ of compiling ports. ARCHITECTURE ---------------------------------------------------------------------------- -The local machines sync metadata files (available packages, -readme files, dependencies, etc) from a remote (http or ftp) +The client machines sync metadata files (available packages, +readme files, dependencies, etc) from a remote server (http or ftp) OR a local path. -Once the metadata is present on the local machine, the usual +Once the metadata files are on the client machine, the usual operations of installing, removing, getting info on packages are available. @@ -22,17 +22,19 @@ are available. QUICK START ---------------------------------------------------------------------------- Server: - A repository can be generated using 'pkg-repgen' in a - dir containing packages. It will take a while since md5sums - have to be calculated. + A repository can be generated using 'pkg-repgen' in a directory + containing packages. It will take a while since md5sums have to be + calculated. Alternatively, you can pass arguments to 'pkg-repgen', + specifying a repository other than $PWD, optionally followed by the + individual packages for which metadata will be created. Client: Adjust settings in /etc/pkg-get.conf, then use the 'pkg-get sync' command to gather metadata from the server (if remote). You can now - use the commands as described in the manual, i.e.: + use the commands as described in the manual, e.g.: pkg-get info apache - pkg-get depinst kdebase + pkg-get depinst qt6-base pkg-get listinst See the manual page for a detailed list of commands and options. @@ -40,5 +42,68 @@ See the manual page for a detailed list of commands and options. REQUIREMENTS ---------------------------------------------------------------------------- -For the client nothing outside the CRUX 'core' collection +For the client, nothing outside the CRUX 'core' collection For the server, prt-get + + +LIMITATIONS +---------------------------------------------------------------------------- +The pkg-get configuration file does not offer as many settings as the one +for prt-get. In particular, you cannot change "addcommand", "rmcommand", or +"runscriptscommand"; these are hard-coded as /usr/bin/pkgadd, /usr/bin/pkgrm, +and /bin/bash, respectively. + +There is also no intelligent version comparator as in prt-get; the +repository and html index are sorted lexographically according to the +current setting for $LANG. When multiple versions of a package are found +within the active collections, pkg-get will install the latest version in +the first collection that contains any such package. This behaviour is akin +to how prt-get handles dups, but with additional logic to account for +different versions of the built package within the same collection. + +'pkg-get dependent' does not support the --recursive option. Other +useful prt-get commands (grpinst, fsearch, deptree, listorphans, ls, +cat, edit, cache) have no counterpart in pkg-get. Of these omissions, +only the 'grpinst' command is of possible interest for binary package +management; the unimplemented commands and options are better handled +by prt-get itself. + +pkg-get only makes use of the hard dependencies listed by the port +maintainer, not any of the eager linking that might have occurred on the +build machine. As a result, 'pkg-get depinst $foo' might omit some of the +packages needed by $foo. User ppetrov^ has contributed some helper scripts +to facilitate the fixing of these broken binaries; visit the site [1] to +download them. + +Further omissions related to dependencies are the absence of any mechanism +for declaring aliases (e.g., package openjdk16-bin can serve as a drop-in +replacement for the listed dependency openjdk16), and the lack of an --ignore +switch (to exclude certain packages from being installed in a 'depinst' +operation). You can work around these omissions by avoiding 'depinst' +entirely, and manually performing the desired 'install' transactions (once +you have a clear sense of what the actual runtime dependencies are). + +These gaps in pkg-get's design highlight an awkward fact about trying to +erect an infrastructure for binary package management upon a foundation +designed for compiling source code (the ports tree). Inheriting the +Pkgfile's lack of separation between build-time and runtime dependencies, +pkg-get will unwittingly recurse through all the dependencies (in a 'depinst' +transaction) and install packages that you might not really need. Hence the +suggestion to consider avoiding 'depinst'. But pairing 'install' with the +helper script written by ppetrov^ [1] might not be enough to ensure zero +breakage, since revdep does not detect every runtime dependency. In the +end, you might have to manually interpolate between the footprint +recommended by 'pkg-get depends' and the (smaller) footprint +recommended by 'revlibpkg'. + +In handling any new hard dependencies added by the maintainer since +the previous version of a package, pkg-get performs a sysup in the same +manner as the original prt-get (i.e., new dependencies are not injected +by default). With binary packages there's no need to carry out the +installation in any particular order, so the lack of dependency injection is +actually less of a problem for pkg-get than it was for prt-get. Combining +'pkg-get depends $foo | grep "\[ \]"' with the output of 'revlibpkg $foo' +should help identify the packages you will need to install to fix any +breakage in $foo. + +[1] https://github.com/slackalaxy/depsck diff --git a/TODO b/TODO index fb2bc3d..ba75d37 100644 --- a/TODO +++ b/TODO @@ -4,8 +4,15 @@ TODO file for pkg-get - add more commands: - deptree (?) - - lock/unlock/listlocked (?) + - grpinst (?) -- optimize the pkg-repgen script +- let the user control whether pkg-repgen prints the metadata only for the + latest built package, or for all the versions in the directory -- improve pkg-get help information +- allow 'sysup' to inject new dependencies (?) + +- add a --test switch (?) + +- add an --ignore switch (?) + +- switch from MD5 to a different hash function (?) diff --git a/doc/pkg-get.8 b/doc/pkg-get.8 index 1f8cd58..46236c7 100644 --- a/doc/pkg-get.8 +++ b/doc/pkg-get.8 @@ -1,75 +1,79 @@ -." Text automatically generated by txt2man-1.4.7 -.TH pkg-get 8 "July 13, 2006" "" "" +.TH pkg\-get 8 "January 13, 2025" "" "" .SH NAME -\fBpkg-get \fP- a package management tool for CRUX Linux +\fBpkg\-get\fP \- a package management tool for CRUX \fB .SH SYNOPSIS .nf .fam C -\fBpkg-get\fP \fIcommand\fP [\fIoptions\fP] +\fBpkg\-get\fP \fIcommand\fP [\fIoptions\fP] .fam T .fi .SH DESCRIPTION -\fBpkg-get\fP is a simple package management tool for CRUX Linux. +\fBpkg\-get\fP is a simple package management tool for CRUX. It tries to replicate some of the most useful features of the -port management tool \fBprt-get\fP(8) to be used with binary packages. -\fBpkg-get\fP requires a remote or local package repository that -can be generated by repository maintainers with the -\fBpkg-repgen\fP(8) script. +port management tool \fBprt\-get\fP(8) to be used with binary packages. +\fBpkg\-get\fP requires a remote or local package repository that +can be generated by the \fBpkg\-repgen\fP(8) script. .SH COMMANDS .TP .B install [..] -Install given packages, -download if necessary. +Install given packages, download if necessary. .TP .B -update [..] -Update given packages, -download if necessary. +update [\fB\-fr\fP] [..] +Update given packages, download if necessary. Pass the flag \fB\-fr\fP +to force a fresh download, even if a package of the expected name +already exists in the local directory. .TP .B -diff [\fB--all\fP] -Show a list of outdated packages. The \fB--all\fP option also displays locked packages. +diff [\fB\-\-all\fP] +Show a list of outdated packages. +The \fB\-\-all\fP option also displays locked packages. .TP .B quickdiff Show a compact list of outdated packages. .TP .B -sysup -Update all outdated packages. Download if necessary. +sysup [\fB\-\-all\fP] +Update all outdated packages, download if necessary. +Locked packages are excluded from the operation +unless \fB\-\-all\fP is passed. .TP .B depinst [..] -Install given packages -and relative dependencies. +Install given packages and their dependencies. .TP .B depends -Show a recursive list of dependencies for package +Show a recursive list of dependencies for and their installation status. .TP .B -dependent [\fB--all\fP] -Show installed (or all with the \fB--all\fP option) packages -that depend from package. +quickdep +Show a brief list of dependencies for . .TP .B -quickdep -Show a brief list of dependencies for package. +dependent [\fB\-\-all\fP] +Show installed (or all with the \fB\-\-all\fP option) packages +that depend on . +.TP +.B +path +Show local path of . .TP .B info -Show information about package. +Show information about . .TP .B current Show currently installed version of package. .TP .B -path -Show local path of package. +isinst +Display whether a package is installed. .TP .B readme @@ -77,33 +81,32 @@ Print README information (if available) for the package. .TP .B list -List all packages in the repository. +List all packages in the active repositories. .TP .B listinst List all installed packages. .TP .B -isinst -Display whether a package is installed. -.TP -.B dsearch -Search for packages which name or description contain . +Search for packages whose name or description contains . .TP .B search -Search for packages which name contains . +Search for packages whose name contains . .TP .B dup -List all duplicates ports (present in more than one repository). +List all duplicate ports (present in more than one repository). +Multiple versions of a port appearing in the same repository +are NOT considered dups; only the most recently-built package gets +an entry in the listing for that repository. .TP .B -printf [\fB--filter\fP=] -Description blatantly -stolen from prt-get man file. Print formatted port list. Format string can -contain variables, which are replaced like this: +printf [\fB\-\-filter\fP=] +Description blatantly stolen from \fBprt\-get\fP(8). +Print formatted port list. Format string can contain variables, +which are replaced like this: .RS .IP \(bu 3 %n -> name @@ -122,13 +125,13 @@ contain variables, which are replaced like this: .IP \(bu 3 %R -> Readme ("yes"/"no") .IP \(bu 3 -%E -> pre-install script ("yes"/"no") +%E -> pre\-install script ("yes"/"no") .IP \(bu 3 -%O -> post-install script ("yes"/"no") +%O -> post\-install script ("yes"/"no") .IP \(bu 3 -%M -> "Nobody". for compatibility with prt-get +%M -> "None". for compatibility with prt\-get .IP \(bu 3 -%P -> "Nobody". for compatibility with prt-get +%P -> "None". for compatibility with prt\-get .IP \(bu 3 %l -> is locked ("yes"/"no") .IP \(bu 3 @@ -138,97 +141,118 @@ repository. .RE .PP Use "\\n" and "\\t" to format your output (no additional format -specified suported). The optional format string2 can contain the -same variables as format string1 and is used to sort the output. -You can specify a wildcard filter to filter by package name. +codes suported). You can specify a wildcard to filter +by package name. .TP .B lock [..] -Lock a package (ignore updates). +Lock a package (exclude it from being updated in a \fBsysup\fP). +\fBpkg\-get\fP and \fBprt\-get\fP read their locked packages +from \fIdifferent\fP locations, allowing you to exclude certain +packages from \fBprt\-get sysup\fP but not from \fBpkg\-get sysup\fP, +or vice\-versa. .TP .B unlock [..] -Unlock a package. +Unlock a package. Only affects the \fBpkg\-get\fP database. .TP .B listlocked -Display a list of locked packages. +Display a list of locked packages. Only the \fBpkg\-get\fP database +is considered. .TP .B sync Syncronize local packages with the ones from the remote repository. -If the repository is local, this \fIcommand\fP does nothing. -.TP -.B -sysup -Update all outdated packages. +If the repository is local, this command does nothing. .TP .B help -Display brief help screen. +Display brief help screen. .TP .B version -Show \fBpkg-get\fP version. +Show \fBpkg\-get\fP version. .SH OPTIONS .TP .B -\fB-r\fP -Use directory when wrapping pkgadd. Note that -this only works with update / install ! +\fB\-do\fP +Download only. Applicable to: sysup, install, depinst, update. .TP .B -\fB-do\fP -Download only. Applicable to: sysup, depinst, install, update. -.TP -.B -\fB-f\fP +\fB\-f\fP Force installing / upgrading. This is passed to \fBpkgadd\fP(8). .TP .B -\fB-im\fP +\fB\-im\fP Ignore md5sum mismatches. .TP .B -\fB--aargs\fP="arguments" -pass the specified arguments to \fBpkgadd\fP(8). +\fB\-\-aargs\fP="arguments" +Pass the specified arguments to \fBpkgadd\fP(8). .TP .B -\fB--config\fP=/path/to/file +\fB\-\-config\fP=/path/to/file Use the specified configuration file. .TP .B -\fB--pre-install\fP -executes pre-install script if available. +\fB\-r\fP +Use directory for operations involving the package database +(install, depinst, update, isinst, current, listinst, diff, quickdiff, sysup). +This option does not affect the directories from which \fBpkg\-get\fP loads the +metadata or tarballs; those settings are still governed by the entries in +the configuration file. + +When using \fB\-r\fP , the relevant pkgadd.conf is the one on the +target filesystem, not the one where \fBpkg\-get\fP is running. Any custom +directives in your pkgadd.conf should be copied to the corresponding +location under the new root, or else you should pass the \fB\-\-aargs\fP +option to tell \fBpkgadd\fP to read its configuration from a non\-standard +location. + +When combined with \fB\-\-pre\-install\fP or \fB\-\-post\-install\fP or +\fB\-\-install\-scripts\fP, setting a root directory other than '/' has the +side effect of copying the PKGINST shell script into the corresponding +location under the new root. This action (and the subsequent chroot command) +is the most direct way to accommodate the user's intention. Unfortunately it +clutters the mounted volume with a small fragment of shell script. If such +clutter is not desired, the option \fB\-r\fP should always be paired +with the configuration setting 'runscripts no', leaving it up to the +administrator of the mounted volume to perform any pre\- or post\-install +tasks separately. .TP .B -\fB--post-install\fP -executes post-install script if available. +\fB\-\-pre\-install\fP +Execute pre-install script if available. .TP .B -\fB--install-scripts\fP -executes pre-install and post-install +\fB\-\-post\-install\fP +Execute post-install script if available. +.TP +.B +\fB\-\-install\-scripts\fP +Execute pre\-install and post\-install scripts if available. .RE .PP .SH CONFIGURATION -Configuration is handled by the /etc/pkg-get.conf file, +Configuration is handled by the /etc/pkg\-get.conf file, \fIoptions\fP are explained in the file itself. .SH EXAMPLES .TP .B -\fBpkg-get\fP install sqlite pysqlite -Install sqlite and pysqlite. +\fBpkg\-get\fP install sqlite3 php\-sqlite3 +Install sqlite3 and php\-sqlite3. .TP .B -\fBpkg-get\fP depinst kdebase \fB-f\fP -Install kdebase and all its dependencies, forcing upgrade. +\fBpkg\-get\fP depinst qt6\-base \fB\-f\fP +Install qt6\-base and all its dependencies, forcing any pkgadd operation if conflicting files are found. .TP .B -\fBpkg-get\fP sysup \fB-do\fP +\fBpkg\-get\fP sysup \fB\-do\fP Download new releases of all the outdated packages. .SH AUTHORS -Simone Rota +Simone Rota , John McQuah .SH SEE ALSO -\fBpkgadd\fP(8), \fBprt-get\fP(8) +\fBpkgadd\fP(8), \fBprt\-get\fP(8), \fBpkg\-repgen\fP(8) diff --git a/doc/pkg-get.8.txt b/doc/pkg-get.8.txt deleted file mode 100644 index 99a9d36..0000000 --- a/doc/pkg-get.8.txt +++ /dev/null @@ -1,101 +0,0 @@ -NAME - pkg-get - a package management tool for CRUX Linux - -SYNOPSIS - pkg-get command [options] - -DESCRIPTION - pkg-get is a simple package management tool for CRUX Linux. - It tries to replicate some of the most useful features of the - port management tool prt-get(8) to be used with binary packages. - pkg-get requires a remote or local package repository that - can be generated by repository maintainers with the - pkg-repgen(8) script. - -COMMANDS - install [..] Install given packages, - download if necessary. - update [..] Update given packages, - download if necessary. - diff [--all] Show a list of outdated packages. The --all option also displays locked packages. - quickdiff Show a compact list of outdated packages. - sysup Update all outdated packages. Download if necessary. - depinst [..] Install given packages - and relative dependencies. - depends Show a recursive list of dependencies for package - and their installation status. - dependent [--all] Show installed (or all with the --all option) packages - that depend from package. - quickdep Show a brief list of dependencies for package. - info Show information about package. - current Show currently installed version of package. - path Show local path of package. - readme Print README information (if available) for the package. - list List all packages in the repository. - listinst List all installed packages. - isinst Display whether a package is installed. - dsearch Search for packages which name or description contain . - search Search for packages which name contains . - dup List all duplicates ports (present in more than one repository). - printf [--filter=] Description blatantly - stolen from prt-get man file. Print formatted port list. Format string can - contain variables, which are replaced like this: - - %n -> name - - %p -> path - - %v -> version - - %r -> release - - %d -> description - - %e -> dependencies - - %u -> url - - %R -> Readme ("yes"/"no") - - %E -> pre-install script ("yes"/"no") - - %O -> post-install script ("yes"/"no") - - %M -> "Nobody". for compatibility with prt-get - - %P -> "Nobody". for compatibility with prt-get - - %l -> is locked ("yes"/"no") - - %i -> "no" if not installed, "yes" if it's installed and up to - date and "diff" if it's installed and a new version is in the - repository. - - Use "\\n" and "\\t" to format your output (no additional format - specified suported). The optional format string2 can contain the - same variables as format string1 and is used to sort the output. - You can specify a wildcard filter to filter by package name. - - lock [..] Lock a package (ignore updates). - unlock [..] Unlock a package. - listlocked Display a list of locked packages. - sync Syncronize local packages with the ones from the remote repository. - If the repository is local, this command does nothing. - sysup Update all outdated packages. - help Display brief help screen. - version Show pkg-get version. - -OPTIONS - -r Use directory when wrapping pkgadd. Note that - this only works with update / install ! - -do Download only. Applicable to: sysup, depinst, install, update. - -f Force installing / upgrading. This is passed to pkgadd(8). - -im Ignore md5sum mismatches. - --aargs="arguments" pass the specified arguments to pkgadd(8). - --config=/path/to/file Use the specified configuration file. - --pre-install executes pre-install script if available. - --post-install executes post-install script if available. - --install-scripts executes pre-install and post-install - scripts if available. - - -CONFIGURATION - Configuration is handled by the /etc/pkg-get.conf file, - options are explained in the file itself. - -EXAMPLES - pkg-get install sqlite pysqlite Install sqlite and pysqlite. - pkg-get depinst kdebase -f Install kdebase and all its dependencies, forcing upgrade. - pkg-get sysup -do Download new releases of all the outdated packages. - -AUTHORS - Simone Rota - -SEE ALSO - pkgadd(8), prt-get(8) diff --git a/doc/pkg-get.conf b/doc/pkg-get.conf index 9c42044..951c40b 100644 --- a/doc/pkg-get.conf +++ b/doc/pkg-get.conf @@ -3,7 +3,7 @@ # pkg-get configuration file # package repositories (remote) -# The first two are remote repoistories, the last is a local one +# The first two are remote repositories, the last is a local one pkgdir /usr/packages/server|http://www.somesite.com/packages pkgdir /usr/packages/java|http://www.foobar.com/java pkgdir /usr/packages/games diff --git a/doc/pkg-repgen.8 b/doc/pkg-repgen.8 index bf9b03f..9e0931e 100644 --- a/doc/pkg-repgen.8 +++ b/doc/pkg-repgen.8 @@ -1,47 +1,72 @@ -." Text automatically generated by txt2man-1.4.7 -.TH pkg-repgen 8 "July 13, 2006" "" "" +.TH pkg\-repgen 8 "January 13, 2025" "" "" .SH NAME -\fBpkg-repgen \fP- generate a package repository for pkg-get +\fBpkg\-repgen\fP \- generate a package repository for pkg\-get \fB .SH SYNOPSIS .nf .fam C -\fBpkg-repgen\fP [options][package1\.\.\.packageN] +\fBpkg\-repgen\fP [options] [directory [package1\.\.\.packageN]] .fam T .fi .SH DESCRIPTION -\fBpkg-repgen\fP generates files needed by a \fBpkg-get\fP(8) package -repository. It also generates a html index of the packages. +\fBpkg\-repgen\fP generates files needed by a \fBpkg\-get\fP(8) package +repository. It also generates a package index suitable for web browsing. .SH USAGE -run \fBpkg-repgen\fP from the directory containing packages to -generate the whole repository. -Specify a list of package names to update only those packages -(this is expecially useful with large repositories). -Note that the html index is not updated when single -packages are specified. +Run \(cqpkg\-repgen \(cq to generate the whole repository of packages +found in \fB\fP. Or to accomplish the same, \(cqcd && +pkg\-repgen\(cq. Any additional arguments after \fB\fP will be interpreted +as the specific package names you want updated (this is especially useful with +large repositories). Even when individual packages are specified, the entire +directory is still processed to collect all the readmes and {pre,post}\-install +scripts. .SH OPTIONS .TP .B -\fB--header\fP=FILE +\fB\-\-header\fP=FILE insert FILE at the beginning of the html index .TP .B -\fB--header\fP=FILE -insert FILE at the beginning of the html index -.TP -.B -\fB--title\fP=title +\fB\-\-title\fP=title use the specified title for the index page .TP .B -\fB--prtdir\fP=DIR -use alternative prtdir for prt-get commands +\fB\-\-prtdir\fP=DIR +use alternative prtdir for the prt\-get metadata lookup (dependencies, +description, install scripts, readme). This option tells prt\-get to behave +as if its config file contained the single line \fIprtdir DIR\fP; all other +directives in /etc/prt\-get.conf are ignored. +.IP +If multiple ports collections all dump their built packages into the common +directory where you are running \fBpkg\-repgen\fP, then this option will omit +metadata, readmes, and install scripts for any ports not found in \fIDIR\fP. +(Every package will still be indexed, but placeholder metadata \(dqNone\(dq +and \(dqN.A.\(dq will appear for Maintainer, Description, and Dependencies +when the corresponding port is outside \fIDIR\fP.) These omissions lead to +bogus output on the client machine running \(cqpkg\-get depends\(cq or +\(cqpkg\-get printf\(cq. Use \fBpkg_installed(1)\fP in conjunction with +\-\-prtdir=/usr/ports/installed to avoid an incomplete repository index. +.TP +.B +\fB\-\-no\-pkgmk\-conf\fP +look for packages matching any supported compression mode, not just the +current setting in pkgmk.conf .SH EXAMPLES -\fBpkg-repgen\fP +cd /home/sip/packages && \fBpkg\-repgen\fP .PP -\fBpkg-repgen\fP kdebase kdelibs +\fBpkg\-repgen\fP /home/sip/packages +.PP +\fBpkg\-repgen\fP . qt6\-base qt6\-tools +.SH FILES +\fB/usr/share/pkg\-get/style.html\fP +.PP +To use a common style for both \fBportspage\fP and \fBpkg\-get\fP, you can +create under /usr/share/pkg\-get a symbolic link to ../portspage/style.html, +or vice\-versa. Keeping independent directories allows you to make the two +types of repositories more visually distinguishable when viewed in a web browser, +even though \fBprt\-utils\fP and \fBpkg\-get\fP install initially identical style sheets. .SH AUTHORS -Simone Rota -index generation code adapted from Jukka Heino's portspage +Simone Rota , John McQuah +.TP +html index code adapted from Jukka Heino's portspage .SH SEE ALSO -\fBpkg-get\fP(8), \fBprt-get\fP(8) +\fBhttpup\-repgen\fP(8), \fBpkg\-get\fP(8), \fBprt\-get\fP(8) diff --git a/doc/pkg-repgen.8.txt b/doc/pkg-repgen.8.txt deleted file mode 100644 index 5a2c07d..0000000 --- a/doc/pkg-repgen.8.txt +++ /dev/null @@ -1,35 +0,0 @@ -NAME - pkg-repgen - generate a package repository for pkg-get - -SYNOPSIS - pkg-repgen [options][package1...packageN] - -DESCRIPTION - pkg-repgen generates files needed by a pkg-get(8) package - repository. It also generates a html index of the packages. - -USAGE - run pkg-repgen from the directory containing packages to - generate the whole repository. - Specify a list of package names to update only those packages - (this is expecially useful with large repositories). - Note that the html index is not updated when single - packages are specified. - -OPTIONS - --header=FILE insert FILE at the beginning of the html index - --header=FILE insert FILE at the beginning of the html index - --title=title use the specified title for the index page - --prtdir=DIR use alternative prtdir for prt-get commands - -EXAMPLES - pkg-repgen - - pkg-repgen kdebase kdelibs - -AUTHORS - Simone Rota - index generation code adapted from Jukka Heino's portspage - -SEE ALSO - pkg-get(8), prt-get(8) diff --git a/doc/style.html b/doc/style.html new file mode 100644 index 0000000..0f141bd --- /dev/null +++ b/doc/style.html @@ -0,0 +1,26 @@ + + + + + + + + + +

diff --git a/scripts/pkg-get.pl b/scripts/pkg-get.pl index 090f117..8ef481e 100755 --- a/scripts/pkg-get.pl +++ b/scripts/pkg-get.pl @@ -2,7 +2,7 @@ # # pkg-get - A binary package management utility for CRUX Linux # Copyright (C) 2004-2006 Simone Rota -# Copyright (c) 2006-2013 by CRUX team (http://crux.nu) +# Copyright (C) 2006-2025 by CRUX team (http://crux.nu) # # 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 @@ -12,568 +12,415 @@ use warnings; use strict; use Getopt::Long; +use Digest::file qw(digest_file_hex); +use File::Path; -my $VERSION = "0.4.6"; +my $VERSION = "0.5.0"; my $CFGFILE = "/etc/pkg-get.conf"; my $LOCKFILE = "/var/lib/pkg/pkg-get.locker"; my $PKGDB = "/var/lib/pkg/db" ; -$SIG{HUP} = \&trap; $SIG{INT} = \&trap; $SIG{QUIT} = \&trap; $SIG{TERM} = \&trap; +$SIG{HUP} = \&trap; $SIG{INT} = \&trap; +$SIG{QUIT} = \&trap; $SIG{TERM} = \&trap; # Global vars my @repos = (); # package repositories my @donetasks; my @failtasks; my @prevtasks; my %pptasks; my %readmetasks; -my $curraction = ""; my %installed; my %deps; my @dependencies; my %missingdeps; +my $curraction = ""; my %depmap; my %locked; my %checksumsize; +my %fullpath; my %remote; my %prepostread; my %description; +my %instver; my %repver; my %shortstatus; # CL Options -my $download_only; my $pre_install; my $post_install; -my $install_scripts; my $filter = ""; my $all; my $unused; -my $aargs=""; my $ignore_md5sum; my $force; my $root; +my $download_only; my $pre_install; my $post_install; my $root=""; +my $install_scripts; my $all; my $filter = ""; my $unused; +my $aargs=""; my $ignore_md5sum; my $force; my $force_reinst; GetOptions("do"=>\$download_only, - "pre-install"=>\$pre_install, "post-install"=>\$post_install, - "install-scripts"=>\$install_scripts, "all"=>\$all, - "filter=s"=>\$filter, "config=s"=>\$CFGFILE, "aargs=s"=>\$aargs, - "f"=>\$force, "im"=>\$ignore_md5sum, "margs=s"=>\$unused, - "rargs=s"=>\$unused, "r=s"=>\$root); - -# use compression-mode defined in pkgmk.conf -our $compress = "gz"; -open CONFIG, "/etc/pkgmk.conf" or die "Could not open /etc/pkgmk.conf"; -while () { - $compress = $1 if m/^PKGMK_COMPRESSION_MODE="(.*)"\n/; -} -close CONFIG; + "pre-install"=>\$pre_install, "post-install"=>\$post_install, + "install-scripts"=>\$install_scripts, "all"=>\$all, + "filter=s"=>\$filter, "config=s"=>\$CFGFILE, "aargs=s"=>\$aargs, + "f"=>\$force, "im"=>\$ignore_md5sum, "margs=s"=>\$unused, + "fr"=>\$force_reinst, "rargs=s"=>\$unused, "r=s"=>\$root); if ($root) { - $LOCKFILE = $root.$LOCKFILE ; - $PKGDB = $root.$PKGDB; + $LOCKFILE = $root.$LOCKFILE ; + $PKGDB = $root.$PKGDB; } # Get command, verify it's valid my $command = getcommand(@ARGV); if (index($command,"Error: ") eq 0 ) { - $command =~ s/Error\: //; - exiterr($command); + $command =~ s/Error\: //; + exiterr($command); } + readconfig(); +get_locked() unless ($command =~ + /^(info|help|readme|search|dsearch|list|path|depend|current|isinst)$/); SWITCH: { - if ($command eq "version") { version(); last SWITCH; } - if ($command eq "sync") { sync(); last SWITCH; } - if ($command eq "info") { info(); last SWITCH; } - if ($command eq "help") { help(); last SWITCH; } - if ($command eq "readme") { readme(); last SWITCH; } - if ($command eq "search") { search(); last SWITCH; } - if ($command eq "dsearch") { dsearch(); last SWITCH; } - if ($command eq "list") { list(); last SWITCH; } - if ($command eq "path") { path(); last SWITCH; } - if ($command eq "remove") { remove(); last SWITCH; } - if ($command eq "listinst") { listinst(); last SWITCH; } - if ($command eq "lock") { dolock(); last SWITCH; } - if ($command eq "unlock") { unlock(); last SWITCH; } - if ($command eq "listlocked") { listlocked(); last SWITCH; } - if ($command eq "printf") { doprintf(); last SWITCH; } - if ($command eq "isinst") { isinst(); last SWITCH; } - if ($command eq "diff") { diff(); last SWITCH; } - if ($command eq "quickdiff") { quickdiff(); last SWITCH; } - if ($command eq "dup") { dup(); last SWITCH; } - if ($command eq "depends") { depends(); last SWITCH; } - if ($command eq "quickdep") { quickdep(); last SWITCH; } - if ($command eq "install") { install(@ARGV); last SWITCH; } - if ($command eq "update") { update(@ARGV); last SWITCH; } - if ($command eq "sysup") { sysup(); last SWITCH; } - if ($command eq "dependent") { dependent(); last SWITCH; } - if ($command eq "depinst") { depinst(); last SWITCH; } - if ($command eq "current") { current(); last SWITCH; } + if ($command eq "version") { version(); last SWITCH; } + if ($command eq "sync") { sync(); last SWITCH; } + if ($command =~ /^(info|path|readme)$/) { info($1); last SWITCH; } + if ($command eq "help") { help(); last SWITCH; } + if ($command =~ /^(d|)search$/) { search(); last SWITCH; } + if ($command eq "list") { list(); last SWITCH; } + if ($command eq "remove") { remove(); last SWITCH; } + if ($command eq "listinst") { listinst(); last SWITCH; } + if ($command eq "lock") { dolock(); last SWITCH; } + if ($command eq "unlock") { unlock(); last SWITCH; } + if ($command eq "listlocked") { listlocked(); last SWITCH; } + if ($command eq "printf") { doprintf(); last SWITCH; } + if ($command =~ /^(isinst|current)$/) { current(); last SWITCH; } + if ($command =~ /^(diff|quickdiff|sysup)$/) { diff($1); last SWITCH; } + if ($command eq "dup") { dup(); last SWITCH; } + if ($command =~ /^(install|update)$/) { upinst(@ARGV); last SWITCH; } + if ($command eq "dependent") { dependent(); last SWITCH; } + if ($command =~ /^(depends|quickdep)$/) { load_depmap(); depends($1); last SWITCH; } + if ($command eq "depinst") { load_depmap(); depinst(); last SWITCH; } } - ############################################################################ # Support functions ############################################################################ # Exit with error sub exiterr { - my ($msg) = @_; - print "pkg-get: $msg\n"; - exit 1; + my ($msg) = @_; + print "pkg-get: $msg\n"; + exit 1; } sub trap { - printresults(1); - die("\npkg-get: interrupted\n"); + printresults(1); + die("\npkg-get: interrupted\n"); } # Get command, return an error if not in the list of allowed commands sub getcommand { - my @args = @_; - my $givencmd = $args[0]; - my $givenarg = $args[1]; - if (not $givenarg){$givenarg = ""}; - if (not $givencmd){$givencmd = ""}; - my @allowed = ("depinst:", "install:", "sysup", "diff", "update:", "depends:", "info:", "sync", - "version", "help", "quickdep:", "dependent:", "list", "listinst", "isinst:", - "search:", "dsearch:", "lock:", "unlock:", "listlocked", "quickdiff", "printf:", - "remove:", "readme:", "dup", "path:", "current:"); + my ($givencmd, $givenarg) = @_; + if (not $givenarg){$givenarg = ""}; + if (not $givencmd){ + return "Error: no command given. try pkg-get help for more information"; + } - foreach my $valid(@allowed) { - if ($givencmd eq ""){ - return "Error: no command given. try pkg-get help for more information"; - } - if ($givencmd eq $valid) { - return $givencmd; - } elsif ($givencmd.":" eq $valid) { - if ($givenarg ne "") { - return $givencmd; - } else { - return "Error: '$givencmd' requires an argument"; - } - } - } - return "Error: unknown command '$givencmd'. try pkg-get help for more information"; + my @allowed = ("depinst:", "install:", "sysup", "diff", "update:", + "depends:", "info:", "sync", "version", "help", + "quickdep:", "dependent:", "list", "listinst", "isinst:", + "search:", "dsearch:", "lock:", "unlock:", "listlocked", + "quickdiff", "printf:", "remove:", "readme:", "dup", + "path:", "current:"); + + (grep { ($_ eq $givencmd) } @allowed) + or ((grep { ($_ eq "${givencmd}:") } @allowed) and ($givenarg ne "")) + or return "Error: improper command '$givencmd $givenarg'. Try pkg-get help for more information"; + + return $givencmd; } -# Parses the configuration file +# Parse the configuration file sub readconfig { - open(CFG, $CFGFILE) - or exiterr("could not open $CFGFILE"); - while () { - chomp; - if ( /^pkgdir\s+/ ) { - my $repo = $_; - $repo =~ s/^pkgdir\s+//; - $repo =~ s/#(.*)$//; - $repo =~ s/\s+$//; - push @repos, $repo; - } elsif (/^runscripts\s+/) { - my $rs = $_; - $rs =~ s/^runscripts\s+//; - $rs =~ s/#(.*)$//; - $rs =~ s/\s+$//; - if ($rs eq "yes") {$install_scripts = 1}; - } - } - close(CFG); + open(CFG, $CFGFILE) + or exiterr("could not open $CFGFILE"); + while () { + chomp; + if ( /^pkgdir\s+/ ) { + my $repo = $_; + $repo =~ s/^pkgdir\s+//; + $repo =~ s/#.*$//; + $repo =~ s/\s+$//; + push @repos, $repo; + } elsif (/^runscripts\s+/) { + my $rs = $_; + $rs =~ s/^runscripts\s+//; + $rs =~ s/#.*$//; + $rs =~ s/\s+$//; + if ($rs eq "yes") {$install_scripts = 1}; + } + } + close(CFG); +} + +# Populate a hash of installed packages +sub getinstalled { + local $/ = ""; # read files paragraph-wise; see ``perldoc perlvar'' + open(DB, $PKGDB) or exiterr("could not open ".$PKGDB); + while () { + my ($name, $version, @files) = split /\n/, $_; + $instver{$name} = $version; + } + close(DB); +} + +# Populate a hash of locked packages +sub get_locked { + open (my $fL, $LOCKFILE) or return; + while (<$fL>) { chomp; $locked{$_} = 1; } + close ($fL); +} + +# Populate hashes for the available packages +# (requires a previous run of getinstalled) +sub load_repos { + (%instver) or ($command eq "list") or getinstalled(); + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); + while () { + chomp; + my ($filename,$du,$checksum,$desc,$pre,$post,$read)=split(/:/,$_); + my $pkgname = $filename; + $pkgname =~ s/#.*//; + next if ($repver{$pkgname}); # only consider the first matching entry + $repver{$pkgname} = $1 if ($filename =~ m/.*#(.*)\.pkg\.tar\./); + + # no need to populate the remaining hashes if the user only asked for a list + next if ($command eq "list"); + $remote{$pkgname} = ($url) ? $url : ""; + $fullpath{$pkgname} = "$dir/$filename"; + $checksumsize{$pkgname} = "$du:$checksum"; + ($command ne "info") or $description{$pkgname} = $desc; + $prepostread{$pkgname} = "$pre:$post:$read"; + if (! $instver{$pkgname}) { + $shortstatus{$pkgname} = "[ ]"; + } elsif ($instver{$pkgname} eq $repver{$pkgname}) { + $shortstatus{$pkgname} = "[i]"; + } else { + $shortstatus{$pkgname} = "[u]"; + } + } + close (REPO); + } } # Parse a line describing a package sub parsepackage { - my @p = split(/\:/, $_[0]); - if ($#p != 6) {exiterr("$_[1]/PKGREPO appears to be in wrong format!\nAborting.")}; - my %pkg; - my $name = $p[0]; - $name =~ s/\#.*$//; - my $version = $p[0]; - $version =~ s/^.*\#//; - $version =~ s/-\w*\.pkg\.tar\.[gbx]z*//; - my $release = $p[0]; - $release =~ s/^.*-//; - $release =~ s/\.pkg\.tar\.[gbx]z*//; - if (not $_[2]) {$_[2] = $_[1]}; - $pkg{'name'} = $name; - $pkg{'version'} = $version; - $pkg{'release'} = $release; - $pkg{'path'} = $_[1]; - $pkg{'url'} = $_[2] . "/$p[0]"; - $pkg{'size'} = $p[1]; - $pkg{'md5sum'} = $p[2]; - $pkg{'description'} = $p[3]; - $pkg{'pre_install'} = $p[4]; - $pkg{'post_install'} = $p[5]; - $pkg{'readme'} = $p[6]; - if ($_[3] == 1) { - getinstalled(); - $pkg{'instversion'} = $installed{$name}; - } - return %pkg; -} - -# Parse a line describing a package (just the name) -sub parsepackagelight { - my @p = split(/\:/, $_[0]); - if ($#p != 6) {exiterr("$_[1]/PKGREPO appears to be in wrong format!\nAborting.")}; - my %pkg; - my $name = $p[0]; - $name =~ s/\#.*$//; - $pkg{'name'} = $name; - return %pkg; + my $type=shift; my @p = split(/\:/, $_[0]); + if ($#p < 6) {exiterr("$_[1]/PKGREPO appears to be in wrong format!\nAborting.")}; + my ($N, $V, $C) = ($p[0] =~ m/(.*)\#(.*)\.pkg\.tar\.(bz2|gz|lz|xz|zst)$/) ? ($1, $2, $3) : ("","",""); + ($type ne "light") or return ('name' => $N); + my $R = ($V =~ m/^.*-(\w)$/) ? $1 : 0; + $V =~ s/-\w$//; + my %pkg = ( 'name' => $N, 'version' => $V, 'release' => $R, 'compression' => $C ); + if (not $_[2]) {$_[2] = $_[1]}; + $pkg{'path'} = $_[1]; + $pkg{'url'} = $_[2]; + $pkg{'url'} =~ s/\/$//; + $pkg{'url'} .= "/$p[0]"; + $pkg{'size'} = $p[1]; + $pkg{'md5sum'} = $p[2]; + $pkg{'description'} = $p[3]; + $pkg{'pre_install'} = $p[4]; + $pkg{'post_install'} = $p[5]; + $pkg{'readme'} = $p[6]; + return %pkg; } # Print info about the given package sub printinfo { - my %pkg = @_; - print "Name : " . $pkg{'name'} . "\n"; - print "Version : " . $pkg{'version'} . "\n"; - print "Release : " . $pkg{'release'} . "\n"; - print "Description : " . $pkg{'description'} . "\n"; - print "URL : " . $pkg{'url'} . "\n"; - print "Md5sum : " . $pkg{'md5sum'} . "\n"; - print "Size : " . $pkg{'size'} . "\n"; - my $deps = getdirectdeps($pkg{'name'}, $pkg{'path'}); - if ($deps ne "") { print "Depends on : $deps\n";}; - my $files = ""; - if ($pkg{'readme'} eq "yes") {$files .= "README,"}; - if ($pkg{'pre_install'} eq "yes") {$files .= "pre-install,"}; - if ($pkg{'post_install'} eq "yes") {$files .= "post-install,"}; - $files =~ s/\,$//; - if ($files ne "") { print "Files : $files\n";}; -} - -# Get direct dependencies for package -sub getdirectdeps { - my $pkgname = $_[0]; - my $dir = $_[1]; - open(DEPS, "$dir/PKGDEPS") - or exiterr("could not open $dir/PKGDEPS"); - while () { - chomp; - if ( /^\Q$pkgname\E\s+/ ) { - my $dep = $_; - $dep =~ s/^.*\: //; - close(DEPS); - return $dep; - } - } - close(DEPS); - return ""; + my $pkgname = shift; + my ($path, $version, $release) = ($1,$2,$3) + if ($fullpath{$pkgname} =~ m/(.*)\/$pkgname#(.*)-([0-9]*)\.pkg\.tar/); + my ($du, $md5sum) = ($1,$2) if ($checksumsize{$pkgname} =~ m/([0-9]*):(.*)/); + my $url = ($remote{$pkgname}) ? $remote{$pkgname} : $path; + print "Name : " . $pkgname . "\n"; + print "Version : " . $version . "\n"; + print "Release : " . $release . "\n"; + print "Description : " . $description{$pkgname} . "\n"; + print "URL : " . $url . "\n"; + print "Md5sum : " . $md5sum . "\n"; + print "Size : " . $du . "\n"; + my $deps = ($depmap{$pkgname}) ? $depmap{$pkgname} : ""; + ($deps eq "") or print "Depends on : $deps\n"; + my $files = ""; + if ($prepostread{$pkgname} =~ /:yes$/) {$files .= "README,"}; + if ($prepostread{$pkgname} =~ /^yes:/) {$files .= "pre-install,"}; + if ($prepostread{$pkgname} =~ /:yes:/) {$files .= "post-install,"}; + $files =~ s/\,$//; + ($files eq "") or print "Files : $files\n"; } # Prints the README file to stdout sub printreadme { - my %pkg = @_; - my $dir = $pkg{'path'}; - my $pkgname = $pkg{'name'}; - my $found = 0; - my $finished = 0; - open(READ, "$dir/PKGREAD") - or exiterr("could not open $dir/PKGREAD"); - while () { - if ($finished eq 1) {return;}; - chomp; - if ($found eq 1) { - if ( /PKGREADME\:/ ) { - $finished = 1; - close(READ); - return; - } else { - print "$_\n"; - } - } - if ($finished eq 0) { - if ( /PKGREADME: $pkgname$/ ) { - $found = 1; - } - } - } - close(READ); + my $pkgname = shift; + my ($found, $finished) = (0, 0); + my $path = $fullpath{$pkgname}; + $path =~ s/[^\/]*$//; + open(READ, "$path/PKGREAD") + or exiterr("could not open $path/PKGREAD"); + while () { + chomp; + if ( ($found == 1) and (/PKGREADME\:/) ) { + $finished = 1; + } elsif ($found == 1) { + print "$_\n"; + } elsif ( /PKGREADME\: $pkgname$/ ) { + $found = 1; + } + last if ($finished == 1); + } + close(READ); } # Print results for multiple package operations sub printresults { - my $okaction = $curraction; - my $curr = ""; - my $action; - my $pkg; - my @t; - my @readme; - my $goterror = 0; - if (@donetasks) { - print "\n-- Packages $okaction\n"; - foreach my $task(@donetasks) { - if ($readmetasks{$task}) {push(@readme, $task)} - print "$task" . $pptasks{$task}."\n"; - } - } - if (@prevtasks) { - if ($okaction eq "installed") { - print "\n-- Packages installed before this run (ignored)\n"; - } else { - print "\n-- Packages not previously installed (ignored)\n"; - } - foreach my $task(@prevtasks) { - print "$task\n"; - } - } - if (@failtasks) { - @failtasks = sort(@failtasks); - foreach my $task(@failtasks) { - @t = split(/,/,$task); - $action = $t[0]; - $pkg = $t[1]; - if ($curr ne $action) { - print "\n-- Packages $action\n"; - $curr = $action; - } - print "$pkg\n"; - } - } - if (@readme) { - print "\n-- $okaction packgages with README file\n"; - foreach my $task(@readme) { - print "$task" . $pptasks{$task}."\n"; - } - } - if(@donetasks and not @failtasks and not $_[0]) { - print "\npkg-get: $okaction successfully\n" - } + my $okaction = $curraction; + my $curr = ""; + my $action; + my $pkg; + my @readme; + if (@donetasks) { + @readme = grep { ($readmetasks{$_}) } @donetasks; + print "\n-- Packages $okaction\n"; + foreach my $task(@donetasks) { + print "$task" . $pptasks{$task}."\n"; + } + } + if (@prevtasks) { + if ($okaction eq "installed") { + print "\n-- Packages installed before this run (ignored)\n"; + } else { + print "\n-- Packages not previously installed (ignored)\n"; + } + foreach my $task(@prevtasks) { print "$task\n"; } + } + if (@failtasks) { + foreach my $task(sort @failtasks) { + ($action, $pkg) = split(/,/,$task); + if ($curr ne $action) { + print "\n-- Packages $action\n"; + $curr = $action; + } + print "$pkg\n"; + } + } + if (@readme) { + print "\n-- $okaction packages with README file\n"; + print join(", ",@readme); + print "\n"; + } + (! @donetasks) or (@failtasks) or ($_[0]) or + print "\npkg-get: $okaction successfully\n"; } -# Get the list of installed packages -sub getinstalled { - if (%installed) { - return; - } - my $name; - my $version; - my $sec = 0; - open(DB, $PKGDB) - or exiterr("could not open ".$PKGDB); - while () { + + +# Initialize a hash of dependencies +sub load_depmap { + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(DEPS, "$dir/PKGDEPS") or exiterr("could not open $dir/PKGDEPS"); + while () { chomp; - if ($_ ne "") { - if ($sec == 0) { - $name = $_; - $sec = 1; - } elsif ($sec == 1) { - $version = $_; - $sec = 3; - } - } - if ($sec == 3) { - if ($_ eq "") { - $sec = 0; - $installed{$name} = $version; - } - } + my ($pkgname,$pkgdep) = ($1,$2) if ( m/([^\s]*)\s*:\s*(.*)$/ ); + next if ($depmap{$pkgname}); + $depmap{$pkgname} = $pkgdep; } - close(DB); + } } -# Lock the given pkgname -sub lockadd { - my $pkg = $_[0]; - my @locked = (); - if (not -e $LOCKFILE) { - open(LCK, "+>> $LOCKFILE") or exiterr("could not write to lock file"); - close(LCK); - } - open(LCK, $LOCKFILE); - while () { - chomp; - if ($_ eq $pkg) { - print "Already locked: $pkg\n"; - close(LCK); - return; - } else { - push (@locked, $_); - } - } - close(LCK); - push(@locked, $pkg); - @locked = sort(@locked); - open(LCK, "> $LOCKFILE") or exiterr("could not write to lock file"); - foreach my $lock(@locked) { - print LCK "$lock\n"; - } - close(LCK); -} +# Return a sorted list of packages required to satisfy all dependencies. +sub deporder { + my $type=shift; my @seeds=@_; our @treewalk=(); our @result; our %missing; + our %given = map { $_ => 1 } @seeds; our %imark=(); our %fmark=(); -# Rrint formatted info for given package -sub formattedprint { - my %pkg = @_; - my $fmt = $ARGV[1]; - $fmt =~ s|%n|$pkg{'name'}|; - $fmt =~ s|%p|$pkg{'path'}|; - $fmt =~ s|%v|$pkg{'version'}|; - $fmt =~ s|%r|$pkg{'release'}|; - $fmt =~ s|%d|$pkg{'description'}|; - $fmt =~ s|%u|$pkg{'url'}|; - $fmt =~ s|%R|$pkg{'readme'}|; - $fmt =~ s|%E|$pkg{'pre_install'}|; - $fmt =~ s|%O|$pkg{'post_install'}|; - $fmt =~ s|%M|Nobody|; # for prt-get compatibility - $fmt =~ s|%P|Nobody|; # for prt-get compatibility - $fmt =~ s|\\n|\n|; - $fmt =~ s|\\t|\t|; - if (index($fmt,"%e") >=0) { - my $deps = getdirectdeps($pkg{'name'}, $pkg{'path'}); - $fmt =~ s|%e|$deps|; - } - if (index($fmt,"%l") >=0) { - my $locked = islocked($pkg{'name'}); - $fmt =~ s|%l|$locked|; - } - if (index($fmt,"%i") >=0) { - my $inst = "no"; - if ($pkg{'instversion'}) { - if ($pkg{'instversion'} eq $pkg{'version'}."-".$pkg{'release'}) { - $inst = "yes"; - } else { - $inst = "diff"; - } - } - $fmt =~ s|%i|$inst|; - } - print "$fmt"; -} + foreach my $t (@seeds) { recurse_deptree($t,""); } -# See if package is currently locked -sub islocked { - my $pkg = $_[0]; - open(LCK, $LOCKFILE) or return "no"; - while () { - chomp; - if ($_ eq $pkg) { - close(LCK); - return "yes"; - } - } - close(LCK); - return "no"; -} + sub recurse_deptree { + my $s=shift; my $pkgparent=shift; my %curdeps=(); -# Get package from the repo(s) -sub getpackage { - my $pkgname = $_[0]; - my $checkver = $_[1]; - my %found; - my %res; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, $checkver); - if ($pkg{'name'} eq $pkgname) { - close (REPO); - return %pkg; - } - } - close (REPO); - } - return %res; -} + # early return if this node has been visited already + if ($fmark{$s}) { return; } -# Get short status for package, ie [i] -sub getshortstatus { - my %pkg = @_; - if ($pkg{'instversion'}) { - if ($pkg{'instversion'} eq $pkg{'version'}."-".$pkg{'release'}) { - return "[i]"; - } else { - return "[u]"; - } - } - return "[ ]"; -} + # detect targets that are not present among the repositories + if (! $repver{$s}) { + $missing{$pkgparent} .= $s; + (! $pkgparent) or $missing{$pkgparent} .= " from $pkgparent;"; + $fmark{$s}=1; + return; + } -# Get (recursive) dependencies for pkgname -sub getdependencies { - my $pkgname = $_[0]; - my $checkver = $_[1]; - my $pkgparent = $_[2]; - my $depstring = ""; - if ($pkgparent eq "") { - #%deps = (); - }; - if (not $deps{$pkgname}) { - my %pkg = getpackage($pkgname, 1); - if (%pkg) { - my $ddeps = getdirectdeps($pkg{'name'}, $pkg{'path'}); - my @d = split(/,/, $ddeps); - foreach my $dep(@d) { - getdependencies($dep, $checkver, $pkgname); - } - if ($checkver) { - $depstring = getshortstatus(%pkg) . " $pkgname"; - } else { - $depstring = $pkgname; - } - $deps{$pkgname} = $depstring; - push(@dependencies, $depstring); - } else { - if ($pkgparent eq "") { - return 0; - } else { - $missingdeps{$pkgname} = $pkgparent; - } - } - } + # dependency cycle detection + if ($imark{$s}) { + print "Dependency cycle found: "; + foreach (@treewalk) { print "$_ => "; } + print "$s\n"; + return; + } + + push(@treewalk, $s); $imark{$s}=1; + + # assemble the list of dependencies that must be visited next + (! $depmap{$s}) or %curdeps = map { $_ => 1 } split / /, $depmap{$s}; + + foreach my $sd (keys %curdeps) { + recurse_deptree($sd,$s); + } + delete $imark{$s}; pop(@treewalk); + $fmark{$s} = 1; + push(@result, $s); + } + + if ((%missing) and ($type ne "quick")) { + push (@result, "--- missing deps:"); + foreach my $mdep (sort(keys %missing)) { + push (@result, split /;/, $missing{$mdep}); + } + } + return @result; } # Download given package (if needed), check md5sum sub downloadpkg { - my %pkg = @_; - my $fullpath = $pkg{'path'}."/".$pkg{'name'}."#".$pkg{'version'}."-".$pkg{'release'}.".pkg.tar.$compress"; - if (-f $fullpath) { - my $md5 = `md5sum $fullpath`; chomp; - $md5 =~ s/\s+.*$//; - $md5 =~ chop($md5); - if ($md5 ne $pkg{'md5sum'} and not $ignore_md5sum) { - print STDERR "=======> ERROR: md5sum mismatch for $pkg{'name'}:\n"; - print STDERR "required : $pkg{'md5sum'}\n"; - print STDERR "found : $md5\n"; - return 0; - } - return 1; - } else { - if ($pkg{'url'} eq "") {return 1}; # repo is local and pkg does not exist. - my $url = $pkg{'url'}; - $url =~ s/\#/\%23/; - system ("wget --no-directories --tries=3 --waitretry=3 --directory-prefix=$pkg{'path'} $url") == 0 or return 0; - my $md5 = `md5sum $fullpath`; chomp; - $md5 =~ s/\s+.*$//; - $md5 =~ chop($md5); - if ($md5 ne $pkg{'md5sum'} and not $ignore_md5sum) { - print STDERR "=======> ERROR: md5sum mismatch for $pkg{'name'}:\n"; - print STDERR "required : $pkg{'md5sum'}\n"; - print STDERR "found : $md5\n"; - return 0; - } - } - return 1; + my $pkgname = shift; + my $url = "$remote{$pkgname}"; + if ($url) { + $url =~ s/\#/\%23/; + } else { + my $saved = (-f $fullpath{$pkgname}) ? 1 : 0; + return $saved; + } + my $downloadcmd = "curl --retry 3 --retry-delay 3 -o $fullpath{$pkgname} $url"; + (! $force_reinst) or system ($downloadcmd) == 0 or return 0; + (-f $fullpath{$pkgname}) or system ($downloadcmd) == 0 or return 0; + # by now there should be a file in the expected location + my $md5found = digest_file_hex($fullpath{$pkgname},"MD5"); + my $md5expected = $1 if ($checksumsize{$pkgname} =~ m/:(.*)/); + if ($md5found ne $md5expected and not $ignore_md5sum) { + print STDERR "=======> ERROR: md5sum mismatch for $pkgname:\n"; + print STDERR "required : $md5expected\n"; + print STDERR "found : $md5found\n"; + return 0; + } + return 1; } # Install given package sub installpkg { - my $upgrade = shift(@_); - my %pkg = @_; - my $aa = $aargs." "; - if ($pkg{'readme'} eq "yes") {$readmetasks{$pkg{'name'}} = 1}; - $pptasks{$pkg{'name'}} = ""; - if ($download_only) {return 1}; - if ($force){$aa = $aa."-f "}; - if ($root) {$aa = $aa."-r ".$root." "}; - if ($pkg{'pre_install'} eq "yes" and ($install_scripts or $pre_install)) {dopre(%pkg)}; - my $fullpath = $pkg{'path'}."/".$pkg{'name'}."#".$pkg{'version'}."-".$pkg{'release'}.".pkg.tar.$compress"; - print "pkg-get: /usr/bin/pkgadd $upgrade$aa$fullpath\n"; - system ("/usr/bin/pkgadd $upgrade$aa$fullpath") == 0 or return 0; - if ($pkg{'post_install'} eq "yes" and ($install_scripts or $post_install)) {dopost(%pkg)}; - return 1; + my ($upgrade, $pkgname) = @_; + my $aa = $aargs." "; + if ($prepostread{$pkgname} =~ m/:yes$/) {$readmetasks{$pkgname} = 1}; + $pptasks{$pkgname} = ""; + if ($force){$aa = $aa."-f ";} + if ($root ne "") {$aa = $aa."-r ".$root." ";} + if ($install_scripts and $prepostread{$pkgname} =~ m/^yes/) {doscript("pre",$pkgname);} + print "pkg-get: /usr/bin/pkgadd $upgrade $aa$fullpath{$pkgname}\n"; + system ("/usr/bin/pkgadd $upgrade $aa$fullpath{$pkgname}") == 0 or return 0; + if ($install_scripts and $prepostread{$pkgname} =~ m/:yes:/) {doscript("post",$pkgname);} + return 1; } -# Execute pre-install script -sub dopre { - my %pkg = @_; - my $cmd = "/bin/bash $pkg{'path'}/PKGINST $pkg{name}_pre_install"; - if (system($cmd) == 0){ - $pptasks{$pkg{'name'}} .= " [pre: ok]"; - } else { - $pptasks{$pkg{'name'}} .= " [pre: failed]"; - } -} - -# Execute post-install script -sub dopost { - my %pkg = @_; - my $cmd = "/bin/bash $pkg{'path'}/PKGINST $pkg{name}_post_install"; - if (system($cmd) == 0){ - $pptasks{$pkg{'name'}} .= " [post: ok]"; - } else { - $pptasks{$pkg{'name'}} .= " [post: failed]"; - } +# Execute pre- or post-install script +sub doscript { + my ($when, $pkgname) = @_; + my $path = $fullpath{$pkgname}; + $path =~ s/[^\/]*$//; + my $cmd = ($root ne "") ? "chroot $root " : ""; + $cmd .= "/bin/bash $path/PKGINST $pkgname $when"; + if ((-e "$root$path/PKGINST") and (system($cmd) == 0)) { + $pptasks{$pkgname} .= " [$when: ok]"; + } else { + $pptasks{$pkgname} .= " [$when: failed]"; + } } ############################################################################ @@ -582,606 +429,393 @@ sub dopost { # No pun intended ########################################################## sub version { - print "pkg-get $VERSION "; - print "by Simone Rota \n"; + print "pkg-get $VERSION by Simone Rota \n"; } # Show brief help ########################################################## sub help { - print "Usage: pkg-get command [package2 ... packageN] [options] + print "Usage: pkg-get command [package2 ... packageN] [options] -Some command: +Some commands: sync synchronize with the repository - depinst install package and its dependencies; + depinst install package and its dependencies info info about package - sysup updates all outdated packages + sysup update all outdated packages diff list all outdated packages -Some option: +Some options: -do download only + -im ignore MD5 mismatches --install-scripts use install scripts -r use for pkgadd Example: - pkg-get install sqlite pysqlite + pkg-get install sqlite3 php-sqlite3 For other commands and samples, see the pkg-get(8) man page\n"; } # Sync with the remote server(s) ########################################### sub sync { - my @r; - my $dir; - my $url; - my $dlerror = 0; - foreach my $repo(@repos) { - @r = split(/\|/, $repo); - $dir = $r[0]; - $url = $r[1]; - if (not $url){$url = ""}; - print "Updating collection $dir\n"; - if (not -d $dir) { - mkdir($dir) or exiterr("cannot create $dir"); - } - if ($url ne "") { - for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { - if (-f "$dir/$f") {rename("$dir/$f", "$dir/$f.old") or exiterr("cannot write to $dir")}; - if (system("wget -q -P $dir $url/$f") != 0) { - print " cannot retrieve $f\n"; - $dlerror=1; - } - } - if ($dlerror) { # restore backup repo - for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { - if (-f "$dir/$f.old") {rename("$dir/$f.old", "$dir/$f") or exiterr("cannot write to $dir")};}; - } else { # remove backup repo - for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { - if (-f "$dir/$f.old") {unlink("$dir/$f.old") or exiterr("cannot write to $dir")};}; - } - } - } + my $dlerror = 0; + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + next if (not $url); + print "Updating collection $dir\n"; + (-d $dir) or mkdir($dir) or exiterr("cannot create $dir"); + for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { + (! -f "$dir/$f") or rename("$dir/$f", "$dir/$f.old") or exiterr("cannot write to $dir"); + if (system("curl -s --output-dir $dir -o $f $url/$f") != 0) { + print " cannot retrieve $f\n"; + $dlerror=1; + } + } + if ($dlerror) { # restore backup repo + for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { + (! -f "$dir/$f.old") or rename("$dir/$f.old", "$dir/$f") or exiterr("cannot write to $dir"); + } + } else { # remove backup repo + for my $f ("PKGREPO", "PKGDEPS", "PKGREAD", "PKGINST") { + (! -f "$dir/$f.old") or unlink("$dir/$f.old") or exiterr("cannot write to $dir"); + } + } + } } -# Show info about the package ############################################## +# Show info/readme/path for specific packages #################################### sub info { - my $arg = $ARGV[1]; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - if ($pkg{'name'} eq $arg) { - printinfo(%pkg); - close(REPO); - return; - } - } - close(REPO); - } - print "Package '$arg' not found\n"; + my ($type, @args) = @ARGV; + (%repver) or load_repos(); + ($type ne "info") or load_depmap(); + foreach my $arg (@args) { + ($fullpath{$arg}) or next; + if ($type eq "info") { + printinfo($arg); + } elsif ($type eq "readme") { + printreadme($arg); + } else { + my $path = $fullpath{$arg}; + $path =~ s/[^\/]*$//; + print $path . "\n"; + } + } } -# List packages containing given string in their name ###################### +# List packages containing given string (name/description) ################# sub search { - my $arg = $ARGV[1]; - my %found; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - if (index($pkg{'name'}, $arg) > -1 and not $found{$pkg{'name'}}) { - $found{$pkg{'name'}} = 1; - } - } - close(REPO); - } - foreach my $key (sort keys %found) { - print "$key\n"; - } - if (not %found) {print "No matching packages found\n"}; -} - -# List packages containing given string (name / description) ############### -sub dsearch { - my $arg = $ARGV[1]; - my %found; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - if ((index($pkg{'name'}, $arg) > -1 or index($pkg{'description'}, $arg) > -1) - and not $found{$pkg{'name'}}) { - $found{$pkg{'name'}} = 1; - } - } - close(REPO); - } - foreach my $key (sort keys %found) { - print "$key\n"; - } - if (not %found) {print "No matching packages found\n";}; + my $arg = $ARGV[1]; + my $parsetype = ($ARGV[0] =~ /^d/) ? "full" : "light"; + my %found; + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); + while () { + chomp; + my %pkg = parsepackage($parsetype,$_, $dir, $url); + next if ($found{$pkg{'name'}}); + (index($pkg{'name'}, $arg) < 0) or ($parsetype eq "full") or $found{$pkg{'name'}} = 1; + ($parsetype ne "full") or (index($pkg{'description'}, $arg) < 0) or $found{$pkg{'name'}} = 1; + } + close(REPO); + } + foreach my $key (sort keys %found) { print "$key\n"; } + if (not %found) { print "No matching packages found\n"; } } # List all available packages ############################################## +# (requires a previous run of load_repos) sub list { - my $arg = $ARGV[1]; - my %found; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - $found{$pkg{'name'}} = 1; - } - close(REPO); - } - foreach my $key (sort keys %found) { - print "$key\n"; - } -} - -# Show path for a package ################################################## -sub path { - my $arg = $ARGV[1]; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - if ($pkg{'name'} eq $arg) { - print $pkg{'path'} . "\n"; - close(REPO); - return; - } - } - close(REPO); - } - print "Package '$arg' not found\n"; -} - -# Show current installed version of a package ############################## -sub current { - my $pkgname = $ARGV[1]; - getinstalled(); - if ($installed{$pkgname}) { - print "$installed{$pkgname}\n"; - } else { - print "Package $pkgname not installed\n"; - } -} - -# Print the README file for a package ###################################### -sub readme { - my $arg = $ARGV[1]; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackage($_, $dir, $url, 0); - if ($pkg{'name'} eq $arg) { - printreadme(%pkg); - close(REPO); - return; - } - } - close(REPO); - } - print "Package '$arg' not found\n"; + (%repver) or load_repos(); + foreach my $key(sort keys %repver) { print "$key\n"; } } # Remove given packages #################################################### sub remove { - $curraction = "removed"; - shift(@ARGV); - foreach my $pkg(@ARGV) { - $pptasks{$pkg} = ""; - if (system("/usr/bin/pkgrm $pkg") ==0) { - push(@donetasks, $pkg); - } else { - push(@failtasks, "where removal failed,$pkg"); - } - } - printresults(); + $curraction = "removed"; + shift(@ARGV); + my $cmd = "/usr/bin/pkgrm"; + $cmd .= " -r $root" if ($root ne ""); + foreach my $pkg(@ARGV) { + $pptasks{$pkg} = ""; + if (system("$cmd $pkg")==0) { + push(@donetasks, $pkg); + } else { + push(@failtasks, "where removal failed,$pkg"); + } + } + printresults(); } # List installed packages ################################################## sub listinst { - getinstalled(); - foreach my $key (sort keys %installed) { - print "$key\n"; - } + getinstalled() if (! %instver); + foreach my $key (sort keys %instver) { print "$key\n"; } } -# Print if package is installed ########################################### -sub isinst { - getinstalled(); - shift(@ARGV); - foreach my $pkg(@ARGV) { - if ($installed{$pkg}) { - print "package $pkg is installed\n"; - } else { - print "package $pkg is not installed\n"; - } - } +# Print package version, or install status ################################# +sub current { + getinstalled() if (! %instver); + my $type = shift(@ARGV); my $result; + foreach my $pkg(@ARGV) { + if ($instver{$pkg}) { + $result = ($type eq "current") ? ": version $instver{$pkg}\n" + : " is installed\n"; + } else { + $result = " not installed\n"; + } + print "Package " . $pkg . $result; + } } - # Lock given packages ###################################################### sub dolock { - shift(@ARGV); - foreach my $arg(@ARGV) { - my $found = 0; - foreach my $repo(@repos) { - if ($found == 0) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg = parsepackagelight($_); - if ($pkg{'name'} eq $arg) { - $found = 1; - lockadd($arg); - } - } - close(REPO); - } - } - if ($found == 0) {print "Package '$arg' not found\n"}; - } + shift(@ARGV); + open(LCK, ">> $LOCKFILE") or exiterr("could not write to lock file"); + foreach my $arg(@ARGV) { + if ($locked{$arg}) { + print "Already locked: $arg\n"; next; + } + print LCK "$arg\n"; + } + close(LCK); } # Unlock given packages #################################################### sub unlock { - shift(@ARGV); - my @locked; - my $found; - foreach my $arg(@ARGV) { - $found = 0; - @locked = (); - open(LCK, $LOCKFILE) or exiterr("could not open lock file"); - while () { - chomp; - if ($_ eq $arg) { - push (@locked, "-"); - $found = 1; - } else { - push (@locked, $_); - } - } - close(LCK); - if ($found == 1) { - @locked = sort(@locked); - open(LCK, ">$LOCKFILE") or exiterr("could not write to lock file"); - foreach my $lock(@locked) { - if ($lock ne "-") {print LCK "$lock\n"}; - } - close(LCK); - } else { - print "Not locked previously: $arg\n"; - } - } + shift(@ARGV); + foreach my $arg(@ARGV) { + if (! $locked{$arg}) { + print "Not locked previously: $arg\n"; next; + } else { + delete $locked{$arg}; + } + } + open(my $fL, ">$LOCKFILE") or exiterr("could not write to lock file"); + foreach (sort keys %locked) { print $fL "$_\n"; } + close($fL); } # List locked packages ##################################################### sub listlocked { - open(LCK, $LOCKFILE) - or exit; - while () { - chomp; - print "$_\n"; - } - close(LCK); + foreach (sort keys %locked) { print "$_\n"; } } # Print formatted info ##################################################### sub doprintf { - my %found; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg; - if (index($ARGV[1], "%i") >=0 ) { - %pkg = parsepackage($_, $dir, $url, 1); - } else { - %pkg = parsepackage($_, $dir, $url, 0); - } - if (not $found{$pkg{'name'}}) { - if ($filter ne "") { - my $match = $pkg{'name'}; - my $cfilter = $filter; - $cfilter =~ s/\*/\.\*/; - if ($match =~ /^$cfilter$/) { - formattedprint(%pkg); - $found{$pkg{'name'}} = 1; - } - } else { - formattedprint(%pkg); - $found{$pkg{'name'}} = 1; - } - } - } - close(REPO); - } + my %printed; my $linewanted; + my $fmt = $ARGV[1]; + if ($filter) { + $filter =~ s/\*/.*/g; + $linewanted = qr/^$filter$/is; + } + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); + while () { + chomp; + my %pkg = parsepackage("full",$_, $dir, $url); + next if ($printed{$pkg{'name'}}); + if (($filter) and ($pkg{'name'} !~ $linewanted)) { + $printed{$pkg{'name'}} = 1; next; + } + my $printstring = $fmt; + $printstring =~ s|%n|$pkg{'name'}|; + $printstring =~ s|%p|$pkg{'path'}|; + $printstring =~ s|%v|$pkg{'version'}|; + $printstring =~ s|%r|$pkg{'release'}|; + $printstring =~ s|%d|$pkg{'description'}|; + $printstring =~ s|%u|$pkg{'url'}|; + $printstring =~ s|%R|$pkg{'readme'}|; + $printstring =~ s|%E|$pkg{'pre_install'}|; + $printstring =~ s|%O|$pkg{'post_install'}|; + $printstring =~ s|%M|None|; # for prt-get compatibility + $printstring =~ s|%P|None|; # for prt-get compatibility + $printstring =~ s|\\n|\n|; + $printstring =~ s|\\t|\t|; + if (index($printstring,"%e") >=0) { + my $deps = $depmap{$pkg{'name'}}; + $printstring =~ s|%e|$deps|; + } + if (index($printstring,"%l") >=0) { + my $locked = ($locked{$pkg{'name'}}) ? "yes" : "no"; + $printstring =~ s|%l|$locked|; + } + if (index($printstring,"%i") >=0) { + (%instver) or getinstalled(); + my $inst = ($instver{$pkg{'name'}}) ? "yes" : "no"; + ($inst eq "no") or + ($instver{$pkg{'name'}} eq "$repver{$pkg{'name'}}") + or $inst = "diff"; + $printstring =~ s|%i|$inst|; + } + print $printstring; + $printed{$pkg{'name'}} = 1; + } + close(REPO); + } } -# Show differences between installed and available packages ################ +# Show or resolve differences between installed and available packages ##### sub diff { - my %found; - my $gotdiff = 0; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg; - %pkg = parsepackage($_, $dir, $url, 1); - if (not $found{$pkg{'name'}}) { - if ($pkg{'instversion'}) { - if ($pkg{'instversion'} ne $pkg{'version'}."-".$pkg{'release'}) { - if (islocked($pkg{'name'}) eq "no") { - if ($gotdiff == 0){ - print "Differences between installed packages and packages repo:\n\n"; - printf("%-19s %-19s %-19s\n\n","Package","Installed","Available in the repositories"); - $gotdiff = 1; - } - printf("%-19s %-19s %-19s\n", $pkg{'name'}, $pkg{'instversion'}, $pkg{'version'}."-".$pkg{'release'}); - } elsif ($all) { # yeah, it blows, at least avoid to read the locked state twice. - if ($gotdiff == 0){ - print "Differences between installed packages and packages repo:\n\n"; - printf("%-19s %-19s %-19s\n\n","Package","Installed","Available in the repositories"); - $gotdiff = 1; - } - printf("%-19s %-19s %-19s %-19s\n", $pkg{'name'}, $pkg{'instversion'}, $pkg{'version'}."-".$pkg{'release'}, "locked"); - } - } - } - $found{$pkg{'name'}} = 1; - } - } - close(REPO); - } - if ($gotdiff ==0) {print "No differences found\n"}; -} + my $format = shift; my %found; my @diff; + my $strf= ($format =~ /^quick/) ? "%s " : "%-19s %-19s %-19s\n"; + (%instver) or getinstalled(); -# Show differences between installed and available packages ################ -sub quickdiff { - my %found; - my $gotdiff = 0; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg; - %pkg = parsepackage($_, $dir, $url, 1); - if (not $found{$pkg{'name'}}) { - if ($pkg{'instversion'}) { - if ($pkg{'instversion'} ne $pkg{'version'}."-".$pkg{'release'} and islocked($pkg{'name'}) eq "no") { - if ($gotdiff == 0){ - print $pkg{'name'}; - $gotdiff = 1; - } else { - print " " . $pkg{'name'}; - } - } - } - $found{$pkg{'name'}} = 1; - } - } - close(REPO); - } - if ($gotdiff != 0) {print "\n"}; + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); + my @multip=(); + while () { + chomp; + my %pkg = parsepackage("full",$_, $dir, $url); + next if ( ($found{$pkg{'name'}}) or (! $instver{$pkg{'name'}}) ); + next if ( ($locked{$pkg{'name'}}) and (! $all) ); + my $lastcol = ($locked{$pkg{'name'}}) ? "locked" : ""; + ($lastcol eq "locked") or $lastcol = ($instver{$pkg{'name'}} + eq $pkg{'version'}."-".$pkg{'release'}) ? "uptodate" : + $pkg{'version'}."-".$pkg{'release'}; + push @multip, "$pkg{'name'}^$instver{$pkg{'name'}}^$lastcol"; + } + close(REPO); + while (my $mp = shift @multip) { + my ($mpname, $vinst, $mpinfo) = split /\^/, $mp; + next if ( (@multip) and ($multip[0] =~ m/^\Q$mpname\E\^/) ); + $found{$mpname} = 1; + next if ($mpinfo eq "uptodate"); + ($format !~ /^(quick|sysup)/) or push @diff, $mpname; + ($format =~ /^(quick|sysup)/) or push @diff, join("^", + $mpname, $vinst, $mpinfo); + } + } + ($#diff < 0) or ($format =~ /^(quick|sysup)/) or + printf $strf, "Package","Installed", "Available in the repositories"; + if ($format ne "sysup") { + print "\n"; + foreach my $dl (@diff) { printf $strf, split /\^/, $dl; } + } + ($format !~ /^quick/) or ($#diff < 0) or print "\n"; + ($#diff >= 0) or print "No differences found\n"; + + # proceed with updates if sysup was requested + ($#diff < 0) or ($format ne "sysup") or upinst("update", @diff); } # Display duplicate packages (found in more than one repo) ################# sub dup { - my %found; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg; - %pkg = parsepackage($_, $dir, $url, 0); - $found{$pkg{'name'}} .= "###" . $pkg{'path'}."/". $pkg{'name'}.$pkg{'version'}."-".$pkg{'release'}; - } - close(REPO); - } - my $curr = ""; - foreach my $key (sort keys %found) { - my $value = $found{$key}; - $value =~ s/^\#\#\#//; - if (rindex($value, "###") >=0){ - print "* $key\n"; - my @d = split(/\#\#\#/, $value); - foreach my $dup(@d){ - print " $dup\n"; - } - } - } + my %found; + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); + while () { + chomp; + my %pkg = parsepackage("full",$_, $dir, $url); + $found{$pkg{'name'}} .= "###$pkg{'path'}/$pkg{'name'}#" + . "$pkg{'version'}-$pkg{'release'}" + . ".pkg.tar.$pkg{'compression'}"; + } + close(REPO); + } + foreach my $key (sort keys %found) { + my $value = $found{$key}; + $value =~ s/^\#\#\#//; + (index($value, "###") > 0) or next; + print "* $key\n"; + foreach my $dup(split(/\#\#\#/, $value)) { print " $dup\n"; } + } } # Show list of dependencies for package #################################### sub depends { - getdependencies($ARGV[1], 1, "") or exiterr("package '$ARGV[1]' not found"); - if (@dependencies) {print "-- dependencies ([i] = installed, [u] = updatable)\n"} - foreach my $dep(@dependencies) { - print "$dep\n"; - } - if (%missingdeps) { - print "\n-- missing packages\n"; - foreach my $dep(sort keys %missingdeps) { - print "$dep from $missingdeps{$dep}\n"; - } - } -} - -# Show compact list of dependencies for package ############################ -sub quickdep { - getdependencies($ARGV[1], 0, "") or exiterr("package '$ARGV[1]' not found"); - foreach my $dep(@dependencies) { - print "$dep "; - } - print "\n"; + my ($j, $type) = ($ARGV[0] =~ /^quick/) ? (" ","quick") : ("\n","full"); + ($type eq "quick") or (%instver) or getinstalled(); + (%repver) or load_repos(); + my @args = @ARGV; shift @args; + my @result=deporder($type,@args) or exiterr("package '$ARGV[1]' not found"); + (! @result) or ($type = "quick") or print "-- dependencies ([i] = installed, [u] = updatable)\n"; + foreach my $res (@result) { print $res . $j; } + ($type ne "quick") or print "\n"; } # Show packages directly depending from given package ###################### sub dependent { - my $arg = $ARGV[1]; - my %dp; - if (not $all) { getinstalled(); } - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(DEPS, "$dir/PKGDEPS") - or exiterr("could not open $dir/PKGDEPS"); - while () { - chomp; - my $dep = $_; - $dep =~ s/\s+\:.*$//; - s/^.*\: /\,/; - s/$/\,\$/; - if ( /\,\Q$arg\E\,/ ) { - if (not $all) { - if ($installed{$dep}) { - $dp{$dep} = 1; - } - } else { - $dp{$dep} = 1; - } - } - } - close(DEPS); - } - foreach my $res(keys %dp) { - print "$res\n"; - } + my $arg = $ARGV[1]; my %dp; + getinstalled() unless (($all) or (%instver)); + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + open(DEPS, "$dir/PKGDEPS") + or exiterr("could not open $dir/PKGDEPS"); + while () { + chomp; + my ($maybe, $deplist) = split /\:/; + # collapse trailing or leading whitespace + $maybe =~ s/\s+$//; $deplist =~ s/^\s+//; + # ensure that arg is surrounded by commas, even if appearing at + # the beginning or the end of the list + $deplist =~ s/^/\,/; $deplist =~ s/$/\,/; + $dp{$maybe} = 1 if (index($deplist,",$arg,") >= 0); + } + close(DEPS); + } + foreach my $res(keys %dp) { + print "$res\n" unless ((not $all) and (! $instver{$res})); + } } -# Install given package #################################################### -sub install { - $curraction = "installed"; - my @args = @_; shift(@args); - getinstalled(); - foreach my $pkgname(@args) { - my %pkg = getpackage($pkgname, 1); - if (not %pkg) { - push(@failtasks, "not found,$pkgname"); - } elsif (getshortstatus(%pkg) ne "[ ]") { - push(@prevtasks, "$pkgname"); - } elsif (downloadpkg(%pkg) and installpkg("", %pkg)) { - push(@donetasks, $pkgname); - } else { - push(@failtasks, "where install failed,$pkgname"); - } - } - printresults(); -} - -# Update given package ##################################################### -sub update { - $curraction = "updated"; - my @args = @_; shift(@args); - getinstalled(); - foreach my $pkgname(@args) { - my %pkg = getpackage($pkgname, 1); - if (not %pkg) { - push(@failtasks, "not found,$pkgname"); - } elsif (getshortstatus(%pkg) eq "[ ]") { - push(@prevtasks, "$pkgname"); - } elsif (downloadpkg(%pkg) and installpkg("-u ", %pkg)) { - push(@donetasks, $pkgname); - } else { - push(@failtasks, "where update failed,$pkgname"); - } - } - printresults(); -} - -# Update out of date packages ############################################## -sub sysup { - my %found; - my @diff; - foreach my $repo(@repos) { - my @r = split(/\|/, $repo); - my $dir = $r[0]; - my $url = $r[1]; - open(REPO, "$dir/PKGREPO") or exiterr("could not open $dir/PKGREPO"); - while () { - chomp; - my %pkg; - %pkg = parsepackage($_, $dir, $url, 1); - if (not $found{$pkg{'name'}}) { - if ($pkg{'instversion'}) { - if ($pkg{'instversion'} ne $pkg{'version'}."-".$pkg{'release'} and islocked($pkg{'name'}) eq "no") { - push(@diff, $pkg{'name'}); - } - } - $found{$pkg{'name'}} = 1; - } - } - close(REPO); - } - if (@diff) { - unshift(@diff, "dummy"); # is shifted later in update sub; - update(@diff); - } +# Install or update given package ########################################## +sub upinst { + my ($cmd, @args) = @_; my $aa; + ($curraction, $aa) = ($cmd =~ /^up/) ? ("updated","-u") : ("installed",""); + + if ($root ne "") { + foreach my $repo(@repos) { + my ($dir, $url) = split(/\|/, $repo); + ( -e "$root$dir" ) or make_path("$root$dir"); + ( -d "$root$dir" ) or next; + open(my $host, "$dir/PKGINST") or next; + open(my $mount, ">$root$dir/PKGINST"); + while (<$host>) { print $mount $_; } + close($mount); + close($host); + } + } + + getinstalled() if (! %instver); + load_repos() if (! %repver); + + foreach my $pkgname(@args) { + my $failed=0; + if (not $repver{$pkgname}) { + push(@failtasks, "not found,$pkgname"); + } elsif ( ($cmd . $shortstatus{$pkgname}) + =~ /^(update. |install.u|install.i)/ ) { + push(@prevtasks, "$pkgname"); + } elsif ( (($cmd . $shortstatus{$pkgname}) =~ /^update.i/) and (! $force_reinst) ) { + push(@prevtasks, "$pkgname"); + } elsif (downloadpkg($pkgname)) { + ($download_only) or installpkg($aa, $pkgname) or $failed=1; + ($failed == 1) ? push(@failtasks, "where $cmd failed,$pkgname") + : push(@donetasks, $pkgname); + } else { + push(@failtasks, "where download failed,$pkgname"); + } + } + printresults(); } +# Install given package, along with dependencies ########################### sub depinst { - my @toinst; my %seen; - $curraction = "installed"; - my @args = @ARGV; shift(@args); - getinstalled(); - foreach my $pkgname(@args) { - getdependencies($pkgname, 0, ""); - foreach my $dep(@dependencies) { - if (not $seen{$dep}) { - my %pkg = getpackage($dep, 1); - if (%pkg) { - if (getshortstatus(%pkg) eq "[ ]"){ - push(@toinst, $pkg{'name'}); - } - } - $seen{$dep} = 1; - } - } - } - if (@toinst) { - unshift(@toinst, "dummy"); # is shifted later in install sub; - install(@toinst); - } + my @toinst; + $curraction = "installed"; + my @args = @ARGV; shift(@args); + getinstalled() if (! %instver); + (%repver) or load_repos(); + + my @maybeinst = deporder("quick",@args); + foreach my $pkg (@maybeinst) { + next if (($locked{$pkg}) or (! $repver{$pkg})); + if (! $instver{$pkg}) { + push(@toinst, $pkg); + } + } + upinst("install",@toinst) if (@toinst); } diff --git a/scripts/pkg-get_completion.sh b/scripts/pkg-get_completion.sh index d65366c..7c7e60e 100755 --- a/scripts/pkg-get_completion.sh +++ b/scripts/pkg-get_completion.sh @@ -2,17 +2,16 @@ # derived form Johannes Winkelmann's prt-get completion # problems: options ending on = should not add a space afterwards - _pkg-get() { - local cur prev + local cur prev - COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - prev=${COMP_WORDS[COMP_CWORD-1]} + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} - if [ $COMP_CWORD -eq 1 ]; then - COMPREPLY=( $( compgen -W ' \ + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W ' \ install depinst update help \ version readme list info path \ search dsearch printf sync \ @@ -24,14 +23,13 @@ _pkg-get() if [ $COMP_CWORD '>' 1 ]; then if [[ "$cur" != -* ]]; then - case ${COMP_WORDS[1]} in + case ${COMP_WORDS[1]} in "install" | "depinst" | "path" | "dependent" | \ - "depends" | "quickdep" | "info" | "readme" | \ - "isinst" ) - plist=`pkg-get list` - if [ ! "$plist" == "" ]; then - COMPREPLY=( $( compgen -W '$plist' $cur ) ) - fi + "depends" | "quickdep" | "info" | "readme" | "isinst" ) + plist=`pkg-get list` + if [ -n "$plist" ]; then + COMPREPLY=( $( compgen -W '$plist' $cur ) ) + fi ;; "current"|"lock"|"remove") plist=`pkg-get listinst` @@ -48,12 +46,23 @@ _pkg-get() esac else case ${COMP_WORDS[1]} in - "install" | "update" | "sysup") + "install" | "sysup") COMPREPLY=( $( compgen -W '-do \ + -im \ --pre-install \ --post-install \ --install-scripts' \ - -- $cur ) ) + -- $cur ) ) + ;; + "update") + COMPREPLY=( $( compgen -W '-do \ + -im \ + -fr \ + --pre-install \ + --pre-install \ + --post-install \ + --install-scripts' \ + -- $cur ) ) ;; "dependent" ) COMPREPLY=( $( compgen -W '--all' -- $cur ) ) @@ -65,6 +74,6 @@ _pkg-get() fi fi - return 0 + return 0 } complete -F _pkg-get -o default pkg-get diff --git a/scripts/pkg-repgen.pl b/scripts/pkg-repgen.pl index a2d461e..d6712d3 100755 --- a/scripts/pkg-repgen.pl +++ b/scripts/pkg-repgen.pl @@ -6,340 +6,477 @@ # # html index generation code adapted from Jukka Heino's portspage # -# usage: pkg-repgen [..] +# usage: pkg-repgen [options] [directory [pkgname1..pkgnameN]] # use warnings; use strict; use Getopt::Long; +use Digest::file qw(digest_file_hex); -our $prtget = "/usr/bin/prt-get"; our $prtdir; -our $title = "CRUX Packages"; our $header; our $footer; -GetOptions("prtdir=s"=>\$prtdir, "title=s"=>\$title, "header=s"=>\$header, "footer=s"=>\$footer); +our $prtget = "/usr/bin/prt-get"; our $prtdir; our $compress; +our $title = "CRUX Packages"; our $header; our $footer; our $help=0; our $noconf; +GetOptions("prtdir=s"=>\$prtdir, "title=s"=>\$title, "header=s"=>\$header, "footer=s"=>\$footer, + "h"=>\$help, "help"=>\$help, "no-pkgmk-conf"=>\$noconf); -# Use compression-mode defined in pkgmk-conf -our $compress = "gz"; -open CONFIG, "/etc/pkgmk.conf" or die "Could not open /etc/pkgmk.conf"; -while () { - $compress = $1 if m/^PKGMK_COMPRESSION_MODE="(.*)"\n/; -} -close CONFIG; - -if ($#ARGV >= 0) { # single packages - pkgrepo_single(); - pkgdeps_single(); - pkgread(); - pkginst(); -} else { - if ($prtdir) { - $prtget = "$prtget --no-std-config --config-set=\"prtdir $prtdir\""; +if ($noconf) { + $compress = "{gz,lz,xz,bz2,zst}"; +} else { # Use compression-mode defined in pkgmk-conf + $compress = "gz"; + open CONFIG, "/etc/pkgmk.conf" or die "Could not open /etc/pkgmk.conf"; + while () { + $compress = $1 if m/^PKGMK_COMPRESSION_MODE=(.*)(\#|$)/; } - pkgrepo(); - pkgdeps(); - pkgread(); - pkginst(); + close CONFIG; + $compress =~ s/["' ]//g; } -######################## single packages ######################## +$prtget .= " --no-std-config --config-set=\"prtdir $prtdir\"" if ($prtdir); -# generate dependencies -sub pkgdeps_single { - print "+ Generating dependencies\n"; - my $hasnew = 0; - foreach my $p (@ARGV) { - my @packages = glob("$p#*.pkg.tar.$compress"); - if ($#packages == 0) { - my $found = 0; - my $package = $packages[0]; - $package =~ s/#.*//; - my $deps = `$prtget printf "%e" --filter="$package"`; - if ($deps ne "") { - my $isnew = `grep "$p .*:" PKGDEPS`; - if ($isnew eq ""){ # package is new, put deps at the end. - open (my $fh, '>>PKGDEPS'); - printf $fh "%-30s : %-s\n", $package, $deps; - close $fh; - $hasnew = 1; - } else { - system("sed -i \"/^$p /s/: .*\$/: $deps/\" PKGDEPS"); - } - } - - } else { - print "Package '$p' not found or duplicate\n" - } - } - if ($hasnew == 1){system("sort -o PKGDEPS PKGDEPS")}; +my @packages; my %isDup; + +sub pkg_mtime { + my $aName = $a; my $bName = $b; + my $aTime; my $bTime; + $aName =~ s/#.*//; + $bName =~ s/#.*//; + if ($aName lt $bName) { return -1; } + elsif ($aName gt $bName) { return 1; } + else { + $aTime = (stat $a)[9]; + $bTime = (stat $b)[9]; + } + if ($aTime le $bTime) { return -1; } + else { return 1; } } -# generate the main repository file -sub pkgrepo_single { - print "+ Generating repository\n"; - my $hasnew = 0; - foreach my $p (@ARGV) { - my @packages = glob("$p#*.pkg.tar.$compress"); - if ($#packages == 0) { - my $found = 0; - my $package = $packages[0]; - my $name = $package; - $name =~ s/#.*//; - my $du = (-s $package); - my $md5 = `md5sum $package`; - $md5 =~ s/ .*$|\n//g; - my $des=`$prtget printf %d --filter="$name"`; - $des =~ s/:/ /g; - if ($des eq ""){$des = "N.A."}; - my $flags=`$prtget printf %E:%O:%R --filter="$name"`; - if ($flags eq "") {$flags = "no:no:no"} - my $isnew = `grep "$p#" PKGREPO`; - if ($isnew eq ""){ # package is new, put it at the end - open (my $fh, '>>PKGREPO'); - printf $fh "%-s:%-s:%-s:%-s:%-s\n", $package,$du,$md5,$des,$flags; - close $fh; - $hasnew = 1; - } else { - my $newp = "$package:$du:$md5:$des:$flags"; - system("sed -i \"s/^$p#.*\$/$newp/\" PKGREPO"); - } - #printf $fh "%-s:%-s:%-s:%-s\n", $du,$md5,$des,$flags; - } else { - print "Package '$p' not found or duplicate\n" +sub print_usage { + print "usage: pkg-repgen [options] [directory [pkgname1..pkgnameN]]\n"; + print " toggle option: --no-pkgmk-conf\n"; + print " string option: --title=\n"; + print " file/path options: --header=, --footer=, --prtdir=\n"; + exit(1-$help); +} + +($help==0) or print_usage(); +my $pkgdir = shift @ARGV; my $quickMode=0; +(! $pkgdir) or (-d $pkgdir) or print_usage(); + +$pkgdir = "." if (! $pkgdir); +my @dirlist = glob("$pkgdir/*.pkg.tar.$compress"); +@dirlist = sort pkg_mtime @dirlist; +%isDup = map { $_ => 0 } @dirlist; + +if (@ARGV) { # individual packages + $quickMode=1; + my @updates = sort @ARGV; + while (my $name = shift @updates) { + push @packages, grep { m/$name#.*\.pkg\.tar\./ } @dirlist; + } +} else { # the entire directory + @packages = @dirlist; +} + +# hashes to determine the package name ... +our %pname = map { $_ => (split /\//, $_)[-1] } @dirlist; +foreach my $p (@dirlist) { $pname{$p} =~ s/\#.*//; } + +# ... or to look up the successor when merging old metadata files +my %followR; my %followH; my %followD; my @queue = @dirlist; +while (my $q = shift @queue) { + ($#queue < 0) or ($pname{$q} ne $pname{$queue[0]}) or $isDup{$q} = 1; +} + +# Populate some other hashes using a single run of prt-get +our %path; our %depends; our %descrip; our %flags; +my @validkeys = map { (split /\//, $_)[-1] } @dirlist; +map { s/\#.*// } @validkeys; +my %printme = map { $_ => 1 } @validkeys; +open (my $ppf, "$prtget printf '%n^%p^%e^%d^%E:%O:%R\n' |"); +while (<$ppf>) { + chomp; + my ($name,$repo,$deps,$desc,$prepostread) = split /\^/; + next if (! $printme{$name}); + $path{$name} = $repo . "/" . $name; + $depends{$name} = $deps; + $depends{$name} =~ s/,/ /g; + $descrip{$name} = $desc; + $desc =~ s/:/ /g; + $flags{$name} = $prepostread; +} +close ($ppf); + +# Needed for alternating colors in the html index +my %parity = ( 0 => "even", 1 => "odd" ); + +# Generate the metadata files +($quickMode) ? pkg_single() : pkg_dir(); + +# Generate README and PKGINST +pkgreadscripts(); + +###################### individual packages ########################## +sub pkg_single { + my ($oR, $oD, $oH, $nR, $nD, $nH, $oline, $oname); + my $count = 0; # needed for the html index + my @dep_packages = @packages; + my @idx_packages = @packages; + + my %firstrun = map { $_ => 0 } ("PKGREPO", "PKGDEPS", "index.html"); + open ($oR, "$pkgdir/PKGREPO") or $firstrun{"PKGREPO"} = 1; + open ($oD, "$pkgdir/PKGDEPS") or $firstrun{"PKGDEPS"} = 1; + open ($oH, "$pkgdir/index.html") or $firstrun{"index.html"} = 1; + + open ($nR, ">$pkgdir/PKGREPO.new"); + print "+ Updating specified entries in repository\n"; + RPKG: while (my $p =shift @packages) { + my ($basename, $du, $md5, $ppr) = repodata($p); + my $desc = (! $descrip{$pname{$p}}) ? "N.A." : $descrip{$pname{$p}}; + if ($firstrun{"PKGREPO"}==1) { + printf $nR "%-s:%-s:%-s:%-s:%-s\n",$basename, $du, $md5, $desc, $ppr; + next RPKG; + } + + # Shift entries from the old repository until we find + # a successor to the current package. + while ( (! $followR{$pname{$p}}) and $oline = <$oR> ) { + chomp($oline); $oname = $oline; + $oname =~ s/\#.*//; + print $nR "$oline\n" if ($oname lt $pname{$p}); + + # before breaking out of the loop, append all the packages + # from the globbed queue that are lexographically earlier + # than the current entry in the old repository. + while ($pname{$p} le $oname) { + printf $nR "%-s:%-s:%-s:%-s:%-s\n", $basename, $du, $md5, $desc, $ppr; + next RPKG if (! $isDup{$p}); + $p = shift @packages; + ($basename, $du, $md5, $ppr) = repodata($p); + $desc = (! $descrip{$pname{$p}}) ? "N.A." : $descrip{$pname{$p}}; + + # save what got shifted from the repository if we're not going to + # print it now, but don't save packages that match the same glob. + $followR{$pname{$p}} = "$oline\n" if ($pname{$p} lt $oname); + } + } + + # if the current package comes after everything in the old repository, + # just append its metadata + ($followR{$pname{$p}}) or printf $nR "%-s:%-s:%-s:%-s:%-s\n", $basename, $du, $md5, $desc, $ppr; + + next RPKG if (($isDup{$p}) or (! $followR{$pname{$p}})); + + # Arriving here means the current package is not a dup, and + # definitely has a successor in the old repository. But the + # next globbed package might be a more immediate successor. + # Decide which of the two possible successors comes first. + # By defining a successor for the next package in the queue, + # we delay shifting entries off the old repo. + if ((@packages) and ($pname{$packages[0]} le $followR{$pname{$p}})) { + $followR{$pname{$packages[0]}} = $followR{$pname{$p}}; + next RPKG; + } else { + print $nR $followR{$pname{$p}}; + } + + # Shift another package from the queue + } + + # Likewise for the html index + printheader(1); + open ($nH, ">>$pkgdir/index.html.new"); + + print "+ Updating specified entries in the html index\n"; + HPKG: while (my $p =shift @idx_packages) { + my ($pver, $desc, $date) = htmldata($p); + my $url = $p; + $url =~ s/#/%23/; + + if ($firstrun{"index.html"} == 1) { + $count++; + htmlrow($nH,$count,$pname{$p},$url,$pver,$desc,$date); + next HPKG; + } + # Shift entries from the old html index until we find + # a successor to the current package. + while ( (! $followH{$pname{$p}}) and $oline=<$oH> ) { + chomp($oline); + # no need to copy the header, it should already be there + next if ($oline !~ m/^$pkgdir/PKGDEPS.new"); + + print "+ Updating specified entries in the depmap\n"; + DPKG: while (my $p =shift @dep_packages) { + if ($firstrun{"PKGDEPS"}==1) { + (! $depends{$pname{$p}}) or ($isDup{$p}) + or printf $nD "%-30s : %-s\n", $pname{$p}, $depends{$pname{$p}}; + next DPKG; + } + # Shift entries from the old depmap until we find a successor + # to the current package + while ( (! $followD{$pname{$p}}) and $oline = <$oD> ) { + chomp($oline); $oname = $oline; + $oname =~ s/\s*\:.*//; + print $nD "$oline\n" if ($oname lt $pname{$p}); + while ($pname{$p} le $oname) { + if (! $isDup{$p}) { + printf $nD "%-30s : %-s\n", $pname{$p}, $depends{$pname{$p}}; + next DPKG; + } else { + $p = shift @dep_packages; + } + # save what got shifted from the depmap if we're not going to print + # it now, but ignore packages that match the same glob. + $followD{$pname{$p}} = $oline if ($pname{$p} lt $oname); + } + } + + # if the current package comes after everything in the old depmap + # and is not a dup, just append its metadata + ($followD{$pname{$p}}) or ($isDup{$p}) or (! $depends{$pname{$p}}) + or printf $nD "%-30s : %-s\n", $pname{$p}, $depends{$pname{$p}}; + + next DPKG if (($isDup{$p}) or (! $followD{$pname{$p}})); + + # Arriving here means the current package is not a dup, and + # definitely has a successor entry in the old depmap. + # But the next globbed package might be a more immediate successor. + # Decide which of the two possible successors comes first. If it's the + # globbed package that comes next, save the old depmap entry. + if ((@packages) and ($pname{$packages[0]} le $followD{$pname{$p}})) { + $followD{$pname{$packages[0]}} = $followD{$pname{$p}}; + next DPKG; + } else { + printf $nD $followD{$pname{$p}}; + } + + # Shift another package from the queue + } + + # Done with all the packages that match command-line arguments. + # Now append the tails of the old metadata files to their new counterparts. + # Remember to update the count, in case new packages were inserted. + while ($firstrun{"index.html"}==0 and $oline = <$oH>) { + if ($oline =~ m/class="(even|odd)"/) { + $count++; + $oline =~ s/class="(even|odd)"/class="$parity{($count % 2)}"/; + print $nH $oline; + } + } + while ($firstrun{"PKGDEPS"}==0 and $oline = <$oD>) { print $nD $oline; } + while ($firstrun{"PKGREPO"}==0 and $oline = <$oR>) { print $nR $oline; } + + close($nH); + close($nD); + close($nR); + ($firstrun{"PKGREPO"}==1) or close($oR); + ($firstrun{"PKGDEPS"}==1) or close($oD); + ($firstrun{"index.html"}==1) or close($oH); + + foreach my $db (keys %firstrun) { rename("$pkgdir/$db.new", "$pkgdir/$db"); } + printfooter($count); +} ######################## full repository ######################## - -# generate dependencies -sub pkgdeps { +sub pkg_dir { print "+ Generating dependencies\n"; - my @packages = glob("*#*.pkg.tar.$compress"); - open (my $fh, '>PKGDEPS'); - foreach my $package (@packages) { - $package =~ s/#.*//; - my $deps = `$prtget printf "%e" --filter="$package"`; - if ($deps ne "") { - printf $fh "%-30s : %-s\n", $package, $deps; - } - } - close $fh; -} - -# generate the main repository file and index page -sub pkgrepo { + open (my $iD, ">$pkgdir/PKGDEPS"); print "+ Generating repository\n"; - my @packages = glob("*#*.pkg.tar.$compress"); - our $odd = "odd"; - my $count = 0; - open (my $fh, '>PKGREPO'); - printheader(); - open (my $ih, '>>index.html'); - foreach my $package (@packages) { - my $date = (stat($package))[9]; - $count++; - $package =~ s/\n//g; - my $name = $package; - $name =~ s/#.*//; - my $du = (-s $package); - my $md5 = `md5sum $package`; - $md5 =~ s/ .*$|\n//g; - my $des=`$prtget printf %d --filter="$name"`; - $des =~ s/:/ /g; - if ($des eq ""){$des = "N.A."}; - my $flags=`$prtget printf %E:%O:%R --filter="$name"`; - if ($flags eq "") {$flags = "no:no:no"} - printf $fh "%-s:%-s:%-s:%-s:%-s\n", $package,$du,$md5,$des,$flags; - my $version = $package; - $version =~ s/^.*\#//; - $version =~ s/\.pkg\.tar\.[gbx]z*//; - print $ih ""; - print $ih "$name"; - my $url = $package; - $url =~ s/\#/\%23/; - print $ih "$version"; - print $ih "$des"; - print $ih "" . isotime($date, 1) . ""; - print $ih "\n"; - - if ($odd eq "odd") { $odd = "even"; } - else { $odd = "odd"; } + open (my $iR, ">$pkgdir/PKGREPO"); + printheader(0); + my $count = 0; + open (my $ih, ">>$pkgdir/index.html"); + foreach my $p (@packages) { + my ($basename, $du, $md5, $ppr) = repodata($p); + my ($pver, $desc, $date) = htmldata($p); + my $url = $basename; + $url =~ s/#/%23/; + (! $depends{$pname{$p}}) or ($isDup{$p}) + or printf $iD "%-30s : %-s\n", $pname{$p}, $depends{$pname{$p}}; + printf $iR "%-s:%-s:%-s:%-s:%-s\n", $basename,$du,$md5,$desc,$ppr; + $count++; + htmlrow($ih,$count,$pname{$p},$url,$pver,$desc,$date); } - close $fh; - close $ih; - printfooter($count); + close($ih); + printfooter($count); + close($iR); + close($iD); } -# generate README file -sub pkgread { +# consolidate all the README and install scripts for the available packages +sub pkgreadscripts { print "+ Generating README\n"; - my @packages = glob("*#*.pkg.tar.$compress"); - open (my $fh, '>PKGREAD'); - print $fh "# README files for repository. Do NOT remove this line.\n"; - foreach my $package (@packages) { - $package =~ s/#.*//; - my $path = `$prtget path $package`; - $path =~ s/\n//g; - if (-f "$path/README"){ - print $fh "##### PKGREADME: $package\n"; - open(my $readme, "$path/README"); - while (<$readme>){ - my $line = $_; - print $fh $line; - } + open (my $fR, ">$pkgdir/PKGREAD"); + print $fR "# README files for repository. Do NOT remove this line.\n"; + + print "+ Generating scripts\n"; + open (my $fS, ">$pkgdir/PKGINST"); + print $fS '#!/usr/bin/env bash +# +# PKGINST: pre- and post-install scripts for CRUX packages +# +run_script() { + case "$1" in +'; + + my %seen; + foreach my $name (@dirlist) { + $name =~ s/\#.*//; $name = (split /\//, $name)[-1]; + next if ($seen{$name}); + $seen{$name} = 1; + next if (! $path{$name}); + if (-f "$path{$name}/README"){ + print $fR "##### PKGREADME: $name\n"; + open(my $readme, "$path{$name}/README"); + while (<$readme>){ print $fR $_; } close($readme); } - } - close $fh; -} - -# generate pre-post install scripts file -sub pkginst { - print "+ Generating scripts\n"; - open (my $fh, '>PKGINST'); - print $fh -" -#!/bin/bash -# -# PKGINST: pre-post install scripts for CRUX packages -"; - my @packages = glob("*#*.pkg.tar.$compress"); - foreach my $package (@packages) { - $package =~ s/#.*//; - my $path = `$prtget path $package`; - $path =~ s/\n//g; - my $normal= $package; - $normal =~ s/[^[:alnum:]]/_/g; - if (-f "$path/pre-install"){ - print $fh "${normal}_pre_install() {\n"; - open(my $pre, "$path/pre-install"); - while (<$pre>){ - my $line = $_; - print $fh $line; + foreach my $when ("pre", "post") { + if (-f "$path{$name}/${when}-install"){ + print $fS " $name.$when)\n"; + open(my $rs, "$path{$name}/${when}-install"); + while (<$rs>){ + chomp; + (m/^\#(!.*sh|\s*EOF|\s*End)/) or print $fS " $_\n"; + } + close($rs); + print $fS " ;;\n"; } - close($pre); - print $fh "}\n\n"; - } - if (-f "$path/post-install"){ - print $fh "${normal}_post_install() {\n"; - open(my $post, "$path/post-install"); - while (<$post>){ - my $line = $_; - print $fh $line; - } - close($post); - print $fh "}\n\n"; } } - print $fh "\n\n"; - print $fh 'if [ ! -z "$1" ]; then $1; fi'; - print $fh "\n"; - close $fh; + print $fS " esac\n}\n\n"; + print $fS '[ "$1" ] && [[ "$2" == @(pre|post) ]] && run_script "$1.$2"'; + print $fS "\n"; + close $fS; + close $fR; } - ######################## html index subs ######################## sub printheader { - open (my $ih, '>index.html'); - print $ih < - - -EOH + my $isTemp = shift; my $ih; my $fS; + my $stylePage = "/usr/share/pkg-get/style.html"; + ($isTemp == 0) ? open ($ih, ">$pkgdir/index.html") : open ($ih, ">$pkgdir/index.html.new"); + open ($fS, $stylePage) or die "cannot find html template. Please install $stylePage and try again."; + while (my $sline = <$fS>) { + if ($sline =~ m/<(title|h[1-3])>/) { + $sline =~ s/(title|h[1-3])>[^<]*$title$title\n"; + if ($header) { + open(FILE, $header) or die "Couldn't open header file"; + while () { print $ih " " . $_; } + close(FILE); + } - print $ih < - body - { - font-family: Verdana, sans-serif; - font-size: 85%; - padding: 2em; - } - a - { - color: #67550d; - } - table - { - border: solid #e5dccf 1px; - font-size: 85%; - } - td - { - padding: 6px; - } - tr.header - { - background-color: #e5dccf; - } - tr.odd - { - background-color: #f7f3ed; - } - tr.even - { - background-color: #fcf9f8; - } - - - - -EOH + print $ih " \n"; + print $ih " "; + print $ih ""; + print $ih "\n"; + close($ih); +} - print $ih "

$title

\n"; - if ($header) { - open(FILE, $header) or die "Couldn't open header file"; - while () { - print $ih " " . $_; - } - close(FILE); - } - - print $ih "
PortVersionDescriptionLast modified
\n"; - print $ih " "; - print $ih ""; - print $ih "\n"; - close($ih); +sub htmlrow { + my ($ih, $count, $name, $url, $version, $desc, $date) = @_; + print $ih ""; + print $ih ""; + print $ih "\n"; } sub printfooter { - my $count = $_[0]; - open (my $ih, '>>index.html'); - print $ih "
PortVersionDescriptionLast modified
$name$version$desc$date
\n"; - print $ih "

$count packages

\n"; - if ($footer) { - open(FILE, $footer) or die "Couldn't open footer file"; - while () { - print $ih " " . $_; - } - close(FILE); - } - print $ih "

Generated by pkg-repgen on " . isotime() . ".

\n"; - print $ih <>$pkgdir/index.html"); + print $ih " \n"; + print $ih "

$count packages

\n"; + if ($footer) { + open(FILE, $footer) or die "Couldn't open footer file"; + while () { print $ih " " . $_; } + close(FILE); + } + print $ih "

Generated by pkg-repgen on " . isotime() . ".

\n"; + print $ih < EOH - close($ih); + close($ih); +} +sub htmldata { + my $p = shift; + my $pver = $1 if ($p =~ m/.*\#(.*)\.pkg\.tar\.*/); + my $date = isotime( (stat($p))[9] ); + my $desc = (! $descrip{$pname{$p}}) ? "N.A." : $descrip{$pname{$p}}; + return $pver, $desc, $date; +} + +sub repodata { + my $p = shift; + my $basename = (split /\//, $p)[-1]; + my $du = (-s $p); + my $md5 = digest_file_hex($p,"MD5"); + my $ppr = (! $flags{$pname{$p}}) ? "no:no:no" : $flags{$pname{$p}}; + return $basename, $du, $md5, $ppr; } sub isotime { - my $time = (shift or time); - my $accuracy = (shift or 2); - my @t = gmtime ($time); - my $year = $t[5] + 1900; - my $month = sprintf("%02d", $t[4] + 1); - my $day = sprintf("%02d", $t[3]); + my $time = (shift or time); + my @t = gmtime ($time); + my $year = $t[5] + 1900; + my $month = sprintf("%02d", $t[4] + 1); + my $day = sprintf("%02d", $t[3]); - if ($accuracy == 1) { - return "$year-$month-$day"; - } - - return "$year-$month-$day " . sprintf("%02d:%02d:%02d UTC", $t[2], $t[1], $t[0]); + return "$year-$month-$day"; } -