summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--EXPERIMENTAL0
-rw-r--r--README.md88
-rw-r--r--REFLECTION335
-rw-r--r--config.m425
-rw-r--r--examples/librpm.php68
-rw-r--r--package.xml277
-rw-r--r--php_rpminfo.h22
-rw-r--r--rpminfo.c757
-rw-r--r--rpminfo.stub.php17
-rw-r--r--rpminfo_arginfo.h60
-rw-r--r--tests/002-rpmvercmp.phpt95
-rw-r--r--tests/003-rpminfo.phpt2
-rw-r--r--tests/005-rpminfo-full.phpt6
-rw-r--r--tests/006-rpminfo-errors.phpt8
-rw-r--r--tests/007-rpmdbinfo.phpt30
-rw-r--r--tests/008-rpmdbsearch.phpt67
-rw-r--r--tests/009-rpmdbinfo2.phpt22
-rw-r--r--tests/011-rpmvercmp_error8.phpt37
-rw-r--r--tests/012-rpmaddtag.phpt16
-rw-r--r--tests/013-rpmdbsearch-error.phpt16
-rw-r--r--tests/014-stream.phpt97
-rw-r--r--tests/bidon.rpmbin6972 -> 7599 bytes
-rw-r--r--tests/bidon.spec42
24 files changed, 1903 insertions, 187 deletions
diff --git a/.gitignore b/.gitignore
index 23246db..54b9e13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# Object files
*.o
*.lo
+*.dep
# Libraries
*.lib
@@ -26,12 +27,14 @@ config.cache
config.guess
config.h
config.h.in
+config.h.in~
config.log
config.nice
config.status
config.sub
configure
configure.in
+configure.ac
conftest
conftest.c
Makefile
diff --git a/EXPERIMENTAL b/EXPERIMENTAL
deleted file mode 100644
index e69de29..0000000
--- a/EXPERIMENTAL
+++ /dev/null
diff --git a/README.md b/README.md
index 9461c22..421d5a1 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,11 @@
Retrieve RPM information from PHP code using librpm.
-**Notice**: this is a experimental extension, a work in progress, so don't expect a stable API yet.
+This extension can be considered as stable, and be used on production environement.
+
+But be aware that if its API will probably stay stable,
+some changes may occur before version 1.0.0.
+
----
@@ -97,8 +101,86 @@ The return value is an array of hash tables, or false if it fails.
[0] => Array
(
[Name] => php
- [Version] => 7.2.2
- [Release] => 1.fc27.remi
+ [Version] => 7.3.5
+ [Release] => 1.fc31.remi
+ [Summary] => PHP scripting language for creating dynamic web sites
+ [Arch] => x86_64
+ )
+ )
+
+Retrieve information from rpm database about installed packages using glob or regex.
+The return value is an array of hash tables, or false if it fails.
+
+ $ php -a
+ php > print_r(rpmdbsearch("php-pecl-r*", RPMTAG_NAME, RPMMIRE_GLOB));
+ Array
+ (
+ [0] => Array
+ (
+ [Name] => php-pecl-radius
+ [Version] => 1.4.0
+ [Release] => 0.10.b1.fc31
+ [Summary] => Radius client library
+ [Arch] => x86_64
+ )
+ [1] => Array
+ (
+ [Name] => php-pecl-redis5
+ [Version] => 5.2.0
+ [Release] => 1.fc31.remi.7.3
+ [Summary] => Extension for communicating with the Redis key-value store
+ [Arch] => x86_64
+ )
+ [2] => Array
+ (
+ [Name] => php-pecl-rpminfo
+ [Version] => 0.2.3
+ [Release] => 1.fc31.remi.7.3
+ [Summary] => RPM information
+ [Arch] => x86_64
+ )
+ )
+
+ $ php -a
+ php > print_r(rpmdbsearch("^php-pecl-r", RPMTAG_NAME, RPMMIRE_REGEX));
+ Array
+ (
+ [0] => Array
+ (
+ [Name] => php-pecl-radius
+ [Version] => 1.4.0
+ [Release] => 0.10.b1.fc31
+ [Summary] => Radius client library
+ [Arch] => x86_64
+ )
+ [1] => Array
+ (
+ [Name] => php-pecl-redis5
+ [Version] => 5.2.0
+ [Release] => 1.fc31.remi.7.3
+ [Summary] => Extension for communicating with the Redis key-value store
+ [Arch] => x86_64
+ )
+ [2] => Array
+ (
+ [Name] => php-pecl-rpminfo
+ [Version] => 0.2.3
+ [Release] => 1.fc31.remi.7.3
+ [Summary] => RPM information
+ [Arch] => x86_64
+ )
+ )
+
+ $ php -a
+ php > print_r(rpmdbsearch(PHP_BINARY, RPMTAG_INSTFILENAMES));
+ Array
+ (
+ [0] => Array
+ (
+ [Name] => php-cli
+ [Version] => 7.3.15
+ [Release] => 1.fc31.remi
+ [Summary] => Command-line interface for PHP
[Arch] => x86_64
)
)
diff --git a/REFLECTION b/REFLECTION
index e67b4a3..273ac29 100644
--- a/REFLECTION
+++ b/REFLECTION
@@ -1,54 +1,321 @@
-Extension [ <persistent> extension #129 rpminfo version 0.2.1 ] {
-
- - Constants [24] {
- Constant [ string RPMVERSION ] { 4.14.1 }
- Constant [ integer RPMSENSE_ANY ] { 0 }
- Constant [ integer RPMSENSE_LESS ] { 2 }
- Constant [ integer RPMSENSE_GREATER ] { 4 }
- Constant [ integer RPMSENSE_EQUAL ] { 8 }
- Constant [ integer RPMSENSE_POSTTRANS ] { 32 }
- Constant [ integer RPMSENSE_PREREQ ] { 64 }
- Constant [ integer RPMSENSE_PRETRANS ] { 128 }
- Constant [ integer RPMSENSE_INTERP ] { 256 }
- Constant [ integer RPMSENSE_SCRIPT_PRE ] { 512 }
- Constant [ integer RPMSENSE_SCRIPT_POST ] { 1024 }
- Constant [ integer RPMSENSE_SCRIPT_PREUN ] { 2048 }
- Constant [ integer RPMSENSE_SCRIPT_POSTUN ] { 4096 }
- Constant [ integer RPMSENSE_SCRIPT_VERIFY ] { 8192 }
- Constant [ integer RPMSENSE_FIND_REQUIRES ] { 16384 }
- Constant [ integer RPMSENSE_FIND_PROVIDES ] { 32768 }
- Constant [ integer RPMSENSE_TRIGGERIN ] { 65536 }
- Constant [ integer RPMSENSE_TRIGGERUN ] { 131072 }
- Constant [ integer RPMSENSE_TRIGGERPOSTUN ] { 262144 }
- Constant [ integer RPMSENSE_MISSINGOK ] { 524288 }
- Constant [ integer RPMSENSE_RPMLIB ] { 16777216 }
- Constant [ integer RPMSENSE_TRIGGERPREIN ] { 33554432 }
- Constant [ integer RPMSENSE_KEYRING ] { 67108864 }
- Constant [ integer RPMSENSE_CONFIG ] { 268435456 }
+Extension [ <persistent> extension #15 rpminfo version 0.5.0 ] {
+
+ - Constants [271] {
+ Constant [ string RPMVERSION ] { 4.15.1 }
+ Constant [ int RPMSENSE_ANY ] { 0 }
+ Constant [ int RPMSENSE_LESS ] { 2 }
+ Constant [ int RPMSENSE_GREATER ] { 4 }
+ Constant [ int RPMSENSE_EQUAL ] { 8 }
+ Constant [ int RPMSENSE_POSTTRANS ] { 32 }
+ Constant [ int RPMSENSE_PREREQ ] { 64 }
+ Constant [ int RPMSENSE_PRETRANS ] { 128 }
+ Constant [ int RPMSENSE_INTERP ] { 256 }
+ Constant [ int RPMSENSE_SCRIPT_PRE ] { 512 }
+ Constant [ int RPMSENSE_SCRIPT_POST ] { 1024 }
+ Constant [ int RPMSENSE_SCRIPT_PREUN ] { 2048 }
+ Constant [ int RPMSENSE_SCRIPT_POSTUN ] { 4096 }
+ Constant [ int RPMSENSE_SCRIPT_VERIFY ] { 8192 }
+ Constant [ int RPMSENSE_FIND_REQUIRES ] { 16384 }
+ Constant [ int RPMSENSE_FIND_PROVIDES ] { 32768 }
+ Constant [ int RPMSENSE_TRIGGERIN ] { 65536 }
+ Constant [ int RPMSENSE_TRIGGERUN ] { 131072 }
+ Constant [ int RPMSENSE_TRIGGERPOSTUN ] { 262144 }
+ Constant [ int RPMSENSE_MISSINGOK ] { 524288 }
+ Constant [ int RPMSENSE_RPMLIB ] { 16777216 }
+ Constant [ int RPMSENSE_TRIGGERPREIN ] { 33554432 }
+ Constant [ int RPMSENSE_KEYRING ] { 67108864 }
+ Constant [ int RPMSENSE_CONFIG ] { 268435456 }
+ Constant [ int RPMMIRE_DEFAULT ] { 0 }
+ Constant [ int RPMMIRE_STRCMP ] { 1 }
+ Constant [ int RPMMIRE_REGEX ] { 2 }
+ Constant [ int RPMMIRE_GLOB ] { 3 }
+ Constant [ int RPMTAG_ARCH ] { 1022 }
+ Constant [ int RPMTAG_ARCHIVESIZE ] { 1046 }
+ Constant [ int RPMTAG_BASENAMES ] { 1117 }
+ Constant [ int RPMTAG_BUGURL ] { 5012 }
+ Constant [ int RPMTAG_BUILDARCHS ] { 1089 }
+ Constant [ int RPMTAG_BUILDHOST ] { 1007 }
+ Constant [ int RPMTAG_BUILDTIME ] { 1006 }
+ Constant [ int RPMTAG_C ] { 1054 }
+ Constant [ int RPMTAG_CHANGELOGNAME ] { 1081 }
+ Constant [ int RPMTAG_CHANGELOGTEXT ] { 1082 }
+ Constant [ int RPMTAG_CHANGELOGTIME ] { 1080 }
+ Constant [ int RPMTAG_CLASSDICT ] { 1142 }
+ Constant [ int RPMTAG_CONFLICTFLAGS ] { 1053 }
+ Constant [ int RPMTAG_CONFLICTNAME ] { 1054 }
+ Constant [ int RPMTAG_CONFLICTNEVRS ] { 5044 }
+ Constant [ int RPMTAG_CONFLICTS ] { 1054 }
+ Constant [ int RPMTAG_CONFLICTVERSION ] { 1055 }
+ Constant [ int RPMTAG_COOKIE ] { 1094 }
+ Constant [ int RPMTAG_DBINSTANCE ] { 1195 }
+ Constant [ int RPMTAG_DEPENDSDICT ] { 1145 }
+ Constant [ int RPMTAG_DESCRIPTION ] { 1005 }
+ Constant [ int RPMTAG_DIRINDEXES ] { 1116 }
+ Constant [ int RPMTAG_DIRNAMES ] { 1118 }
+ Constant [ int RPMTAG_DISTRIBUTION ] { 1010 }
+ Constant [ int RPMTAG_DISTTAG ] { 1155 }
+ Constant [ int RPMTAG_DISTURL ] { 1123 }
+ Constant [ int RPMTAG_DSAHEADER ] { 267 }
+ Constant [ int RPMTAG_E ] { 1003 }
+ Constant [ int RPMTAG_ENCODING ] { 5062 }
+ Constant [ int RPMTAG_ENHANCEFLAGS ] { 5057 }
+ Constant [ int RPMTAG_ENHANCENAME ] { 5055 }
+ Constant [ int RPMTAG_ENHANCENEVRS ] { 5061 }
+ Constant [ int RPMTAG_ENHANCES ] { 5055 }
+ Constant [ int RPMTAG_ENHANCEVERSION ] { 5056 }
+ Constant [ int RPMTAG_EPOCH ] { 1003 }
+ Constant [ int RPMTAG_EPOCHNUM ] { 5019 }
+ Constant [ int RPMTAG_EVR ] { 5013 }
+ Constant [ int RPMTAG_EXCLUDEARCH ] { 1059 }
+ Constant [ int RPMTAG_EXCLUDEOS ] { 1060 }
+ Constant [ int RPMTAG_EXCLUSIVEARCH ] { 1061 }
+ Constant [ int RPMTAG_EXCLUSIVEOS ] { 1062 }
+ Constant [ int RPMTAG_FILECAPS ] { 5010 }
+ Constant [ int RPMTAG_FILECLASS ] { 1141 }
+ Constant [ int RPMTAG_FILECOLORS ] { 1140 }
+ Constant [ int RPMTAG_FILECONTEXTS ] { 1147 }
+ Constant [ int RPMTAG_FILEDEPENDSN ] { 1144 }
+ Constant [ int RPMTAG_FILEDEPENDSX ] { 1143 }
+ Constant [ int RPMTAG_FILEDEVICES ] { 1095 }
+ Constant [ int RPMTAG_FILEDIGESTALGO ] { 5011 }
+ Constant [ int RPMTAG_FILEDIGESTS ] { 1035 }
+ Constant [ int RPMTAG_FILEFLAGS ] { 1037 }
+ Constant [ int RPMTAG_FILEGROUPNAME ] { 1040 }
+ Constant [ int RPMTAG_FILEINODES ] { 1096 }
+ Constant [ int RPMTAG_FILELANGS ] { 1097 }
+ Constant [ int RPMTAG_FILELINKTOS ] { 1036 }
+ Constant [ int RPMTAG_FILEMD5S ] { 1035 }
+ Constant [ int RPMTAG_FILEMODES ] { 1030 }
+ Constant [ int RPMTAG_FILEMTIMES ] { 1034 }
+ Constant [ int RPMTAG_FILENAMES ] { 5000 }
+ Constant [ int RPMTAG_FILENLINKS ] { 5045 }
+ Constant [ int RPMTAG_FILEPROVIDE ] { 5001 }
+ Constant [ int RPMTAG_FILERDEVS ] { 1033 }
+ Constant [ int RPMTAG_FILEREQUIRE ] { 5002 }
+ Constant [ int RPMTAG_FILESIGNATURELENGTH ] { 5091 }
+ Constant [ int RPMTAG_FILESIGNATURES ] { 5090 }
+ Constant [ int RPMTAG_FILESIZES ] { 1028 }
+ Constant [ int RPMTAG_FILESTATES ] { 1029 }
+ Constant [ int RPMTAG_FILETRIGGERCONDS ] { 5086 }
+ Constant [ int RPMTAG_FILETRIGGERFLAGS ] { 5072 }
+ Constant [ int RPMTAG_FILETRIGGERINDEX ] { 5070 }
+ Constant [ int RPMTAG_FILETRIGGERNAME ] { 5069 }
+ Constant [ int RPMTAG_FILETRIGGERPRIORITIES ] { 5084 }
+ Constant [ int RPMTAG_FILETRIGGERSCRIPTFLAGS ] { 5068 }
+ Constant [ int RPMTAG_FILETRIGGERSCRIPTPROG ] { 5067 }
+ Constant [ int RPMTAG_FILETRIGGERSCRIPTS ] { 5066 }
+ Constant [ int RPMTAG_FILETRIGGERTYPE ] { 5087 }
+ Constant [ int RPMTAG_FILETRIGGERVERSION ] { 5071 }
+ Constant [ int RPMTAG_FILEUSERNAME ] { 1039 }
+ Constant [ int RPMTAG_FILEVERIFYFLAGS ] { 1045 }
+ Constant [ int RPMTAG_FSCONTEXTS ] { 1148 }
+ Constant [ int RPMTAG_GIF ] { 1012 }
+ Constant [ int RPMTAG_GROUP ] { 1016 }
+ Constant [ int RPMTAG_HDRID ] { 269 }
+ Constant [ int RPMTAG_HEADERCOLOR ] { 5017 }
+ Constant [ int RPMTAG_HEADERI18NTABLE ] { 100 }
+ Constant [ int RPMTAG_HEADERIMAGE ] { 61 }
+ Constant [ int RPMTAG_HEADERIMMUTABLE ] { 63 }
+ Constant [ int RPMTAG_HEADERREGIONS ] { 64 }
+ Constant [ int RPMTAG_HEADERSIGNATURES ] { 62 }
+ Constant [ int RPMTAG_ICON ] { 1043 }
+ Constant [ int RPMTAG_INSTALLCOLOR ] { 1127 }
+ Constant [ int RPMTAG_INSTALLTID ] { 1128 }
+ Constant [ int RPMTAG_INSTALLTIME ] { 1008 }
+ Constant [ int RPMTAG_INSTFILENAMES ] { 5040 }
+ Constant [ int RPMTAG_INSTPREFIXES ] { 1099 }
+ Constant [ int RPMTAG_LICENSE ] { 1014 }
+ Constant [ int RPMTAG_LONGARCHIVESIZE ] { 271 }
+ Constant [ int RPMTAG_LONGFILESIZES ] { 5008 }
+ Constant [ int RPMTAG_LONGSIGSIZE ] { 270 }
+ Constant [ int RPMTAG_LONGSIZE ] { 5009 }
+ Constant [ int RPMTAG_MODULARITYLABEL ] { 5096 }
+ Constant [ int RPMTAG_N ] { 1000 }
+ Constant [ int RPMTAG_NAME ] { 1000 }
+ Constant [ int RPMTAG_NEVR ] { 5015 }
+ Constant [ int RPMTAG_NEVRA ] { 5016 }
+ Constant [ int RPMTAG_NOPATCH ] { 1052 }
+ Constant [ int RPMTAG_NOSOURCE ] { 1051 }
+ Constant [ int RPMTAG_NVR ] { 5014 }
+ Constant [ int RPMTAG_NVRA ] { 1196 }
+ Constant [ int RPMTAG_O ] { 1090 }
+ Constant [ int RPMTAG_OBSOLETEFLAGS ] { 1114 }
+ Constant [ int RPMTAG_OBSOLETENAME ] { 1090 }
+ Constant [ int RPMTAG_OBSOLETENEVRS ] { 5043 }
+ Constant [ int RPMTAG_OBSOLETES ] { 1090 }
+ Constant [ int RPMTAG_OBSOLETEVERSION ] { 1115 }
+ Constant [ int RPMTAG_OLDENHANCES ] { 1159 }
+ Constant [ int RPMTAG_OLDENHANCESFLAGS ] { 1161 }
+ Constant [ int RPMTAG_OLDENHANCESNAME ] { 1159 }
+ Constant [ int RPMTAG_OLDENHANCESVERSION ] { 1160 }
+ Constant [ int RPMTAG_OLDFILENAMES ] { 1027 }
+ Constant [ int RPMTAG_OLDSUGGESTS ] { 1156 }
+ Constant [ int RPMTAG_OLDSUGGESTSFLAGS ] { 1158 }
+ Constant [ int RPMTAG_OLDSUGGESTSNAME ] { 1156 }
+ Constant [ int RPMTAG_OLDSUGGESTSVERSION ] { 1157 }
+ Constant [ int RPMTAG_OPTFLAGS ] { 1122 }
+ Constant [ int RPMTAG_ORDERFLAGS ] { 5037 }
+ Constant [ int RPMTAG_ORDERNAME ] { 5035 }
+ Constant [ int RPMTAG_ORDERVERSION ] { 5036 }
+ Constant [ int RPMTAG_ORIGBASENAMES ] { 1120 }
+ Constant [ int RPMTAG_ORIGDIRINDEXES ] { 1119 }
+ Constant [ int RPMTAG_ORIGDIRNAMES ] { 1121 }
+ Constant [ int RPMTAG_ORIGFILENAMES ] { 5007 }
+ Constant [ int RPMTAG_OS ] { 1021 }
+ Constant [ int RPMTAG_P ] { 1047 }
+ Constant [ int RPMTAG_PACKAGER ] { 1015 }
+ Constant [ int RPMTAG_PATCH ] { 1019 }
+ Constant [ int RPMTAG_PATCHESFLAGS ] { 1134 }
+ Constant [ int RPMTAG_PATCHESNAME ] { 1133 }
+ Constant [ int RPMTAG_PATCHESVERSION ] { 1135 }
+ Constant [ int RPMTAG_PAYLOADCOMPRESSOR ] { 1125 }
+ Constant [ int RPMTAG_PAYLOADDIGEST ] { 5092 }
+ Constant [ int RPMTAG_PAYLOADDIGESTALGO ] { 5093 }
+ Constant [ int RPMTAG_PAYLOADFLAGS ] { 1126 }
+ Constant [ int RPMTAG_PAYLOADFORMAT ] { 1124 }
+ Constant [ int RPMTAG_PKGID ] { 261 }
+ Constant [ int RPMTAG_PLATFORM ] { 1132 }
+ Constant [ int RPMTAG_POLICIES ] { 1150 }
+ Constant [ int RPMTAG_POLICYFLAGS ] { 5033 }
+ Constant [ int RPMTAG_POLICYNAMES ] { 5030 }
+ Constant [ int RPMTAG_POLICYTYPES ] { 5031 }
+ Constant [ int RPMTAG_POLICYTYPESINDEXES ] { 5032 }
+ Constant [ int RPMTAG_POSTIN ] { 1024 }
+ Constant [ int RPMTAG_POSTINFLAGS ] { 5021 }
+ Constant [ int RPMTAG_POSTINPROG ] { 1086 }
+ Constant [ int RPMTAG_POSTTRANS ] { 1152 }
+ Constant [ int RPMTAG_POSTTRANSFLAGS ] { 5025 }
+ Constant [ int RPMTAG_POSTTRANSPROG ] { 1154 }
+ Constant [ int RPMTAG_POSTUN ] { 1026 }
+ Constant [ int RPMTAG_POSTUNFLAGS ] { 5023 }
+ Constant [ int RPMTAG_POSTUNPROG ] { 1088 }
+ Constant [ int RPMTAG_PREFIXES ] { 1098 }
+ Constant [ int RPMTAG_PREIN ] { 1023 }
+ Constant [ int RPMTAG_PREINFLAGS ] { 5020 }
+ Constant [ int RPMTAG_PREINPROG ] { 1085 }
+ Constant [ int RPMTAG_PRETRANS ] { 1151 }
+ Constant [ int RPMTAG_PRETRANSFLAGS ] { 5024 }
+ Constant [ int RPMTAG_PRETRANSPROG ] { 1153 }
+ Constant [ int RPMTAG_PREUN ] { 1025 }
+ Constant [ int RPMTAG_PREUNFLAGS ] { 5022 }
+ Constant [ int RPMTAG_PREUNPROG ] { 1087 }
+ Constant [ int RPMTAG_PROVIDEFLAGS ] { 1112 }
+ Constant [ int RPMTAG_PROVIDENAME ] { 1047 }
+ Constant [ int RPMTAG_PROVIDENEVRS ] { 5042 }
+ Constant [ int RPMTAG_PROVIDES ] { 1047 }
+ Constant [ int RPMTAG_PROVIDEVERSION ] { 1113 }
+ Constant [ int RPMTAG_PUBKEYS ] { 266 }
+ Constant [ int RPMTAG_R ] { 1002 }
+ Constant [ int RPMTAG_RECOMMENDFLAGS ] { 5048 }
+ Constant [ int RPMTAG_RECOMMENDNAME ] { 5046 }
+ Constant [ int RPMTAG_RECOMMENDNEVRS ] { 5058 }
+ Constant [ int RPMTAG_RECOMMENDS ] { 5046 }
+ Constant [ int RPMTAG_RECOMMENDVERSION ] { 5047 }
+ Constant [ int RPMTAG_RECONTEXTS ] { 1149 }
+ Constant [ int RPMTAG_RELEASE ] { 1002 }
+ Constant [ int RPMTAG_REMOVETID ] { 1129 }
+ Constant [ int RPMTAG_REQUIREFLAGS ] { 1048 }
+ Constant [ int RPMTAG_REQUIRENAME ] { 1049 }
+ Constant [ int RPMTAG_REQUIRENEVRS ] { 5041 }
+ Constant [ int RPMTAG_REQUIRES ] { 1049 }
+ Constant [ int RPMTAG_REQUIREVERSION ] { 1050 }
+ Constant [ int RPMTAG_RPMVERSION ] { 1064 }
+ Constant [ int RPMTAG_RSAHEADER ] { 268 }
+ Constant [ int RPMTAG_SHA1HEADER ] { 269 }
+ Constant [ int RPMTAG_SHA256HEADER ] { 273 }
+ Constant [ int RPMTAG_SIGGPG ] { 262 }
+ Constant [ int RPMTAG_SIGMD5 ] { 261 }
+ Constant [ int RPMTAG_SIGPGP ] { 259 }
+ Constant [ int RPMTAG_SIGSIZE ] { 257 }
+ Constant [ int RPMTAG_SIZE ] { 1009 }
+ Constant [ int RPMTAG_SOURCE ] { 1018 }
+ Constant [ int RPMTAG_SOURCEPACKAGE ] { 1106 }
+ Constant [ int RPMTAG_SOURCEPKGID ] { 1146 }
+ Constant [ int RPMTAG_SOURCERPM ] { 1044 }
+ Constant [ int RPMTAG_SUGGESTFLAGS ] { 5051 }
+ Constant [ int RPMTAG_SUGGESTNAME ] { 5049 }
+ Constant [ int RPMTAG_SUGGESTNEVRS ] { 5059 }
+ Constant [ int RPMTAG_SUGGESTS ] { 5049 }
+ Constant [ int RPMTAG_SUGGESTVERSION ] { 5050 }
+ Constant [ int RPMTAG_SUMMARY ] { 1004 }
+ Constant [ int RPMTAG_SUPPLEMENTFLAGS ] { 5054 }
+ Constant [ int RPMTAG_SUPPLEMENTNAME ] { 5052 }
+ Constant [ int RPMTAG_SUPPLEMENTNEVRS ] { 5060 }
+ Constant [ int RPMTAG_SUPPLEMENTS ] { 5052 }
+ Constant [ int RPMTAG_SUPPLEMENTVERSION ] { 5053 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERCONDS ] { 5088 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERFLAGS ] { 5082 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERINDEX ] { 5080 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERNAME ] { 5079 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERPRIORITIES ] { 5085 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS ] { 5078 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERSCRIPTPROG ] { 5077 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERSCRIPTS ] { 5076 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERTYPE ] { 5089 }
+ Constant [ int RPMTAG_TRANSFILETRIGGERVERSION ] { 5081 }
+ Constant [ int RPMTAG_TRIGGERCONDS ] { 5005 }
+ Constant [ int RPMTAG_TRIGGERFLAGS ] { 1068 }
+ Constant [ int RPMTAG_TRIGGERINDEX ] { 1069 }
+ Constant [ int RPMTAG_TRIGGERNAME ] { 1066 }
+ Constant [ int RPMTAG_TRIGGERSCRIPTFLAGS ] { 5027 }
+ Constant [ int RPMTAG_TRIGGERSCRIPTPROG ] { 1092 }
+ Constant [ int RPMTAG_TRIGGERSCRIPTS ] { 1065 }
+ Constant [ int RPMTAG_TRIGGERTYPE ] { 5006 }
+ Constant [ int RPMTAG_TRIGGERVERSION ] { 1067 }
+ Constant [ int RPMTAG_URL ] { 1020 }
+ Constant [ int RPMTAG_V ] { 1001 }
+ Constant [ int RPMTAG_VCS ] { 5034 }
+ Constant [ int RPMTAG_VENDOR ] { 1011 }
+ Constant [ int RPMTAG_VERBOSE ] { 5018 }
+ Constant [ int RPMTAG_VERIFYSCRIPT ] { 1079 }
+ Constant [ int RPMTAG_VERIFYSCRIPTFLAGS ] { 5026 }
+ Constant [ int RPMTAG_VERIFYSCRIPTPROG ] { 1091 }
+ Constant [ int RPMTAG_VERSION ] { 1001 }
+ Constant [ int RPMTAG_XPM ] { 1013 }
}
- Functions {
+ Function [ <internal:rpminfo> function rpmaddtag ] {
+
+ - Parameters [1] {
+ Parameter #0 [ <required> int $rpmtag ]
+ }
+ - Return [ bool ]
+ }
Function [ <internal:rpminfo> function rpmdbinfo ] {
- Parameters [2] {
- Parameter #0 [ <required> $name ]
- Parameter #1 [ <optional> $full ]
+ Parameter #0 [ <required> string $nevr ]
+ Parameter #1 [ <optional> bool $full ]
+ }
+ - Return [ array or NULL ]
+ }
+ Function [ <internal:rpminfo> function rpmdbsearch ] {
+
+ - Parameters [4] {
+ Parameter #0 [ <required> string $pattern ]
+ Parameter #1 [ <optional> int $rpmtag ]
+ Parameter #2 [ <optional> int $rpmmire ]
+ Parameter #3 [ <optional> bool $full ]
}
+ - Return [ array or NULL ]
}
Function [ <internal:rpminfo> function rpminfo ] {
- Parameters [3] {
- Parameter #0 [ <required> $path ]
- Parameter #1 [ <optional> $full ]
- Parameter #2 [ <optional> &$error ]
+ Parameter #0 [ <required> string $path ]
+ Parameter #1 [ <optional> bool $full ]
+ Parameter #2 [ <optional> string or NULL &$error ]
}
+ - Return [ array or NULL ]
}
Function [ <internal:rpminfo> function rpmvercmp ] {
- Parameters [2] {
- Parameter #0 [ <required> $evr1 ]
- Parameter #1 [ <required> $evr2 ]
+ Parameter #0 [ <required> string $evr1 ]
+ Parameter #1 [ <required> string $evr2 ]
}
+ - Return [ int ]
}
}
}
diff --git a/config.m4 b/config.m4
index ef96f22..d0e2565 100644
--- a/config.m4
+++ b/config.m4
@@ -4,26 +4,19 @@ PHP_ARG_ENABLE(rpminfo, whether to enable rpminfo support,
[ --enable-rpminfo Enable rpminfo support])
if test "$PHP_RPMINFO" != "no"; then
- dnl Write more examples of tests here...
- AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ PKG_CHECK_MODULES([LIBRPM], [rpm >= 4.11.3])
- AC_MSG_CHECKING(for librpm)
- if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists rpm; then
- if $PKG_CONFIG rpm --atleast-version 4.11.3; then
- LIBRPM_CFLAGS=`$PKG_CONFIG rpm --cflags`
- LIBRPM_LIBDIR=`$PKG_CONFIG rpm --libs`
- LIBRPM_VERSON=`$PKG_CONFIG rpm --modversion`
- AC_MSG_RESULT(from pkgconfig: version $LIBRPM_VERSON)
- else
- AC_MSG_ERROR(system librpm is too old: version 4.11.3 required)
- fi
- else
- AC_MSG_ERROR(pkg-config not found)
- fi
- PHP_EVAL_LIBLINE($LIBRPM_LIBDIR, RPMINFO_SHARED_LIBADD)
+ PHP_EVAL_LIBLINE($LIBRPM_LIBS, RPMINFO_SHARED_LIBADD)
PHP_EVAL_INCLINE($LIBRPM_CFLAGS)
+ AC_MSG_CHECKING(for rpm >= 4.13)
+ PKG_CHECK_EXISTS([rpm >= 4.13],
+ AC_DEFINE(HAVE_ARCHIVE, 1, [ Archive reader since RPM 4.13 ])
+ AC_DEFINE(HAVE_WEAKDEP, 1, [ Indexes on weak dependency field since RPM 4.13 ])
+ AC_MSG_RESULT([yes]), AC_MSG_RESULT([no])
+ )
+
PHP_SUBST(RPMINFO_SHARED_LIBADD)
PHP_NEW_EXTENSION(rpminfo, rpminfo.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
diff --git a/examples/librpm.php b/examples/librpm.php
index 7d36f2d..6ae8b81 100644
--- a/examples/librpm.php
+++ b/examples/librpm.php
@@ -59,22 +59,47 @@ abstract class Common {
if (isset($this->info['Requirename'])) {
return $this->_dep($this->info['Requirename'], $this->info['Requireflags'], $this->info['Requireversion']);
}
+ return NULL;
case 'Conflicts':
if (isset($this->info['Conflictname'])) {
return $this->_dep($this->info['Conflictname'], $this->info['Conflictflags'], $this->info['Conflictversion']);
}
+ return NULL;
case 'Obsoletes':
if (isset($this->info['Obsoletename'])) {
return $this->_dep($this->info['Obsoletename'], $this->info['Obsoleteflags'], $this->info['Obsoleteversion']);
}
+ return NULL;
case 'Provides':
if (isset($this->info['Providename'])) {
return $this->_dep($this->info['Providename'], $this->info['Provideflags'], $this->info['Provideversion']);
}
+ return NULL;
+ case 'Recommends':
+ if (isset($this->info['Recommendname'])) {
+ return $this->_dep($this->info['Recommendname'], $this->info['Recommendflags'], $this->info['Recommendversion']);
+ }
+ return NULL;
+ case 'Suggests':
+ if (isset($this->info['Suggestname'])) {
+ return $this->_dep($this->info['Suggestname'], $this->info['Suggestflags'], $this->info['Suggestversion']);
+ }
+ return NULL;
+ case 'Supplements':
+ if (isset($this->info['Supplementname'])) {
+ return $this->_dep($this->info['Supplementname'], $this->info['Supplementflags'], $this->info['Supplementversion']);
+ }
+ return NULL;
+ case 'Enhances':
+ if (isset($this->info['Enhancename'])) {
+ return $this->_dep($this->info['Enhancename'], $this->info['Enhanceflags'], $this->info['Enhanceversion']);
+ }
+ return NULL;
case 'Files':
if (isset($this->info['Basenames'])) {
return $this->_files();
}
+ return NULL;
default:
if (isset($this->info[$name])) {
return $this->info[$name];
@@ -94,7 +119,7 @@ abstract class Common {
class File extends Common {
public function __construct($path) {
- $info = rpminfo($path, true, $error);
+ $info = \rpminfo($path, true, $error);
if ($error) {
throw new \RuntimeException($error);
@@ -114,7 +139,7 @@ class File extends Common {
class Package extends Common {
public function __construct($name, $index=0) {
- $info = rpmdbinfo($name, true);
+ $info = \rpmdbinfo($name, true);
if (!$info) {
throw new \RuntimeException("$name not found");
@@ -126,8 +151,47 @@ class Package extends Common {
}
}
+/**
+ * Find packages which provides something or own a file
+ *
+ * $a = \Remi\RPM\WhatProvides("php-redis");
+ * print_r($a[0]->NEVR);
+ *
+ * $a = \Remi\RPM\WhatProvides("/usr/bin/php");
+ * print_r($a[0]->NEVR);
+ **/
+function WhatProvides($crit) {
+ if (file_exists($crit)) {
+ $a = \rpmdbsearch($crit, RPMTAG_INSTFILENAMES);
+ } else {
+ $a = \rpmdbsearch($crit, RPMTAG_PROVIDES);
+ }
+ $r = [];
+ if (is_array($a)) {
+ foreach($a as $i) {
+ $r[] = new Package($i['Name']);
+ }
+ }
+ return $r;
+}
+/**
+ * Find packages which requires something
+ *
+ * $a = \Remi\RPM\WhatRequires("php-common");
+ * print_r($a[0]->NEVRA);
+ **/
+function WhatRequires($crit) {
+ $a = \rpmdbsearch($crit, RPMTAG_REQUIRES);
+ $r = [];
+ if (is_array($a)) {
+ foreach($a as $i) {
+ $r[] = new Package($i['Name']);
+ }
+ }
+ return $r;
+}
/*
$a = new File(dirname(__DIR__).'/tests/bidon.rpm');
$a = new Package('phpunit7');
diff --git a/package.xml b/package.xml
index 3d0ddec..ff6a59e 100644
--- a/package.xml
+++ b/package.xml
@@ -3,11 +3,9 @@
<name>rpminfo</name>
<channel>pecl.php.net</channel>
<summary>RPM information</summary>
- <description>Retrieve RPM information using librpm.
-Available functions:
-- rpmvercmp to compare 2 EVRs
-- rpminfo to retrieve information from a RPM file
-- rpmdbinfo to rerieve information from an installed RPM
+ <description>Retrieve RPM information using librpm, from local RPM file or from installed packages database.
+
+Documentation: https://www.php.net/rpminfo
</description>
<lead>
<name>Remi Collet</name>
@@ -15,20 +13,19 @@ Available functions:
<email>remi@php.net</email>
<active>yes</active>
</lead>
- <date>2018-02-12</date>
+ <date>2023-11-10</date>
<version>
- <release>0.2.1</release>
- <api>0.2.0</api>
+ <release>1.1.1dev</release>
+ <api>1.1.0</api>
</version>
<stability>
- <release>beta</release>
- <api>beta</api>
+ <release>stable</release>
+ <api>stable</api>
</stability>
- <license>PHP 3.01</license>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
<notes>
-- add summary in minimal information set
-- retrieve array from metadata
-- add RPMSENSE_* macros
+- check open_basedir restriction
+- new function: rpmgetsymlink(string $path, string $name): ?string
</notes>
<contents>
<dir name="/">
@@ -36,9 +33,10 @@ Available functions:
<file name="config.m4" role="src"/>
<file name="php_rpminfo.h" role="src" />
<file name="rpminfo.c" role="src"/>
+ <file name="rpminfo.stub.php" role="src"/>
+ <file name="rpminfo_arginfo.h" role="src"/>
<!-- documentation -->
<file name="CREDITS" role="doc"/>
- <file name="EXPERIMENTAL" role="doc"/>
<file name="LICENSE" role="doc"/>
<file name="README.md" role="doc"/>
<dir name ="examples">
@@ -54,6 +52,12 @@ Available functions:
<file name="005-rpminfo-full.phpt" role="test"/>
<file name="006-rpminfo-errors.phpt" role="test"/>
<file name="007-rpmdbinfo.phpt" role="test"/>
+ <file name="008-rpmdbsearch.phpt" role="test"/>
+ <file name="009-rpmdbinfo2.phpt" role="test"/>
+ <file name="011-rpmvercmp_error8.phpt" role="test"/>
+ <file name="012-rpmaddtag.phpt" role="test"/>
+ <file name="013-rpmdbsearch-error.phpt" role="test"/>
+ <file name="014-stream.phpt" role="test"/>
<file name="bidon.rpm" role="test"/>
<file name="bidon-src.rpm" role="test"/>
</dir>
@@ -62,7 +66,7 @@ Available functions:
<dependencies>
<required>
<php>
- <min>7.0.0</min>
+ <min>8.0.0</min>
</php>
<pearinstaller>
<min>1.10.0</min>
@@ -73,6 +77,247 @@ Available functions:
<extsrcrelease/>
<changelog>
<release>
+ <date>2023-11-10</date>
+ <version>
+ <release>1.1.0</release>
+ <api>1.1.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
+ <notes>
+- check open_basedir restriction
+- new function: rpmgetsymlink(string $path, string $name): ?string
+ </notes>
+ </release>
+ <release>
+ <date>2023-10-13</date>
+ <version>
+ <release>1.0.1</release>
+ <api>1.0.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
+ <notes>
+- fix stack smashing on 32-bit
+- allow retrieval of hardlink content
+ </notes>
+ </release>
+ <release>
+ <date>2023-10-12</date>
+ <version>
+ <release>1.0.0</release>
+ <api>1.0.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
+ <notes>
+- implement rpm stream wrapper with librpm >= 4.13
+ </notes>
+ </release>
+ <release>
+ <date>2023-09-26</date>
+ <version>
+ <release>0.7.0</release>
+ <api>0.7.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
+ <notes>
+- add optional operator to rpmcmpver for consistency with version_compare
+- drop support for PHP 7
+ </notes>
+ </release>
+ <release>
+ <date>2021-06-18</date>
+ <version>
+ <release>0.6.0</release>
+ <api>0.5.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license>
+ <notes>
+- generate arginfo from stub and add missing default values
+- raise dependency on PHP 7.2
+- raise exception on bad parameter value with PHP 8
+ </notes>
+ </release>
+ <release>
+ <date>2020-09-23</date>
+ <version>
+ <release>0.5.1</release>
+ <api>0.5.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- split tests for PHP 7/8
+- improve librpm example
+ </notes>
+ </release>
+ <release>
+ <date>2020-04-07</date>
+ <version>
+ <release>0.5.0</release>
+ <api>0.5.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- add rpmaddtag() function
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-25</date>
+ <version>
+ <release>0.4.2</release>
+ <api>0.4.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- improve reflection with better parameter names
+- speed optimization: open DB only once per request
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-18</date>
+ <version>
+ <release>0.4.1</release>
+ <api>0.4.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- fix build with RPM 4.12 (Fedora 21-22 only)
+- add type hinting in reflection
+- return NULL instead of FALSE on failure
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-13</date>
+ <version>
+ <release>0.4.0</release>
+ <api>0.4.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- improve search logic, use index when exists and no search mode
+- add 'full' parameter to rpmdbsearch
+- allow 'rpmdbinfo' to search on NEVR (instead of name only)
+- first "stable" release
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-12</date>
+ <version>
+ <release>0.3.1</release>
+ <api>0.3.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- allow search by Pkgid, Hdrid, Installtid with specific input
+- fix search with various other tags (Version, ...)
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-12</date>
+ <version>
+ <release>0.3.0</release>
+ <api>0.3.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- add rpmdbsearch function to search packages using
+ name, installed files, requires, provides...
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-11</date>
+ <version>
+ <release>0.2.3</release>
+ <api>0.2.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- fix gh#2 free allocated iterators to avoid "DB2053 Freeing read locks..." messages
+ </notes>
+ </release>
+ <release>
+ <date>2020-03-11</date>
+ <version>
+ <release>0.2.2</release>
+ <api>0.2.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- Fix gh#1 rpmvercmp() incorrect comparison result
+ </notes>
+ </release>
+ <release>
+ <date>2018-02-12</date>
+ <version>
+ <release>0.2.1</release>
+ <api>0.2.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP 3.01</license>
+ <notes>
+- add summary in minimal information set
+- retrieve array from metadata
+- add RPMSENSE_* macros
+ </notes>
+ </release>
+ <release>
<date>2018-02-08</date>
<version>
<release>0.2.0</release>
diff --git a/php_rpminfo.h b/php_rpminfo.h
index 6516139..ddc912f 100644
--- a/php_rpminfo.h
+++ b/php_rpminfo.h
@@ -1,8 +1,8 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 7 |
+ | rpminfo extension for PHP |
+----------------------------------------------------------------------+
- | Copyright (c) 2018 The PHP Group |
+ | Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -22,22 +22,20 @@
extern zend_module_entry rpminfo_module_entry;
#define phpext_rpminfo_ptr &rpminfo_module_entry
-#define PHP_RPMINFO_VERSION "0.2.1"
-
-#ifdef PHP_WIN32
-# define PHP_RPMINFO_API __declspec(dllexport)
-#elif defined(__GNUC__) && __GNUC__ >= 4
-# define PHP_RPMINFO_API __attribute__ ((visibility("default")))
-#else
-# define PHP_RPMINFO_API
-#endif
+#define PHP_RPMINFO_VERSION "1.1.1-dev"
+#define PHP_RPMINFO_AUTHOR "Remi Collet"
+#define PHP_RPMINFO_LICENSE "PHP-3.01"
#ifdef ZTS
#include "TSRM.h"
#endif
ZEND_BEGIN_MODULE_GLOBALS(rpminfo)
- rpmts ts;
+ rpmts ts; /* transaction set */
+ rpmdb db; /* database */
+ int nb_tags; /* stored tags */
+ int max_tags; /* allocated tags */
+ rpmTagVal *tags; /* tags storage */
ZEND_END_MODULE_GLOBALS(rpminfo)
#define RPMINFO_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(rpminfo, v)
diff --git a/rpminfo.c b/rpminfo.c
index 263270a..e3fc4f5 100644
--- a/rpminfo.c
+++ b/rpminfo.c
@@ -1,8 +1,8 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 7 |
+ | rpminfo extension for PHP |
+----------------------------------------------------------------------+
- | Copyright (c) 2018 The PHP Group |
+ | Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -23,6 +23,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
+#include "ext/standard/php_string.h"
#include <fcntl.h>
#include <rpm/rpmdb.h>
@@ -32,44 +33,78 @@
#include "php_rpminfo.h"
+#include "rpminfo_arginfo.h"
+
+#ifdef HAVE_ARCHIVE
+struct php_rpm_stream_data_t {
+ FD_t gzdi;
+ Header h;
+ rpmfiles files;
+ rpmfi fi;
+ php_stream *stream;
+};
+
+#define STREAM_DATA_FROM_STREAM() \
+ struct php_rpm_stream_data_t *self = (struct php_rpm_stream_data_t *) stream->abstract;
+#endif
+
ZEND_DECLARE_MODULE_GLOBALS(rpminfo)
-static rpmts rpminfo_getts(rpmVSFlags flags) {
+static rpmts rpminfo_getts(void) {
if (!RPMINFO_G(ts)) {
rpmReadConfigFiles(NULL, NULL);
RPMINFO_G(ts) = rpmtsCreate();
}
if (RPMINFO_G(ts)) {
- (void)rpmtsSetVSFlags(RPMINFO_G(ts), flags);
+ (void)rpmtsSetVSFlags(RPMINFO_G(ts), _RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | RPMVSF_NOHDRCHK);
}
return RPMINFO_G(ts);
}
+static rpmdb rpminfo_getdb(void) {
+ if (!RPMINFO_G(db)) {
+ rpmts ts = rpminfo_getts();
+
+ rpmtsOpenDB(ts, O_RDONLY);
+ RPMINFO_G(db) = rpmtsGetRdb(ts);
+ }
+ return RPMINFO_G(db);
+}
+
static void rpm_header_to_zval(zval *return_value, Header h, zend_bool full)
{
HeaderIterator hi;
rpmTagVal tag;
rpmTagType type;
const char *val;
+ int i;
array_init(return_value);
hi = headerInitIterator(h);
while ((tag=headerNextTag(hi)) != RPMTAG_NOT_FOUND) {
- switch (tag) {
- case RPMTAG_NAME:
- case RPMTAG_VERSION:
- case RPMTAG_RELEASE:
- case RPMTAG_EPOCH:
- case RPMTAG_ARCH:
- case RPMTAG_SUMMARY:
- break;
- default:
- if (!full) {
- continue;
- }
+ if (!full) {
+ switch (tag) {
+ case RPMTAG_NAME:
+ case RPMTAG_VERSION:
+ case RPMTAG_RELEASE:
+ case RPMTAG_EPOCH:
+ case RPMTAG_ARCH:
+ case RPMTAG_SUMMARY:
+ /* Always present tags */
+ break;
+ default:
+ /* Additional tags */
+ for (i=0 ; i<RPMINFO_G(nb_tags) ; i++) {
+ if (tag == RPMINFO_G(tags)[i]) {
+ break;
+ }
+ }
+ if (i==RPMINFO_G(nb_tags)) {
+ continue;
+ }
+ }
}
- //printf("Tag: %-30s Type: %8lx - %8lx - %8lx\n", rpmTagGetName(tag), (long)rpmTagGetTagType(tag), (long)rpmTagGetType(tag), (long)rpmTagGetReturnType(tag));
type = rpmTagGetTagType(tag);
switch (type) {
case RPM_STRING_TYPE:
@@ -174,17 +209,12 @@ static void rpm_header_to_zval(zval *return_value, Header h, zend_bool full)
}
}
}
+ headerFreeIterator(hi);
if (full) {
add_assoc_bool(return_value, "IsSource", headerIsSource(h));
}
}
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rpminfo, 0, 0, 1)
- ZEND_ARG_INFO(0, path)
- ZEND_ARG_INFO(0, full)
- ZEND_ARG_INFO(1, error)
-ZEND_END_ARG_INFO()
-
/* {{{ proto array rpminfo(string path [, bool full [, string &$error])
Retrieve information from a RPM file */
PHP_FUNCTION(rpminfo)
@@ -195,16 +225,19 @@ PHP_FUNCTION(rpminfo)
zval *error = NULL;
FD_t f;
Header h;
- rpmts ts = rpminfo_getts(_RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | RPMVSF_NOHDRCHK);
+ rpmts ts = rpminfo_getts();
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|bz", &path, &len, &full, &error) == FAILURE) {
- return;
+ RETURN_THROWS();
}
if (error) {
ZVAL_DEREF(error);
zval_dtor(error);
ZVAL_NULL(error);
}
+ if (php_check_open_basedir(path)) {
+ RETURN_NULL();
+ }
f = Fopen(path, "r");
if (f) {
@@ -239,17 +272,12 @@ PHP_FUNCTION(rpminfo)
}
efree(e_msg);
}
- RETURN_FALSE;
+ RETURN_NULL();
}
/* }}} */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rpmdbinfo, 0, 0, 1)
- ZEND_ARG_INFO(0, name)
- ZEND_ARG_INFO(0, full)
-ZEND_END_ARG_INFO()
-
-/* {{{ proto array rpmdbinfo(string name [, bool full [, string &$error])
- Retrieve information from a RPM file */
+/* {{{ proto array rpmdbinfo(string nevr [, bool full])
+ Retrieve information from an installed RPM */
PHP_FUNCTION(rpmdbinfo)
{
char *name;
@@ -258,17 +286,16 @@ PHP_FUNCTION(rpmdbinfo)
Header h;
rpmdb db;
rpmdbMatchIterator di;
- rpmts ts = rpminfo_getts(_RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | RPMVSF_NOHDRCHK);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|b", &name, &len, &full) == FAILURE) {
- return;
+ RETURN_THROWS();
}
- rpmtsOpenDB(ts, O_RDONLY);
- db = rpmtsGetRdb(ts);
- di = rpmdbInitIterator(db, RPMTAG_NAME, name, len);
+ db = rpminfo_getdb();
+ di = rpmdbInitIterator(db, RPMDBI_LABEL, name, len);
if (!di) {
- RETURN_FALSE;
+ /* Not found */
+ RETURN_NULL();
}
array_init(return_value);
@@ -277,33 +304,583 @@ PHP_FUNCTION(rpmdbinfo)
rpm_header_to_zval(&tmp, h, full);
add_next_index_zval(return_value, &tmp);
}
+
+ rpmdbFreeIterator(di);
}
/* }}} */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rpmvercmp, 0, 0, 2)
- ZEND_ARG_INFO(0, evr1)
- ZEND_ARG_INFO(0, evr2)
-ZEND_END_ARG_INFO()
+static unsigned char nibble(char c) {
+ if (c >= '0' && c <= '9') {
+ return (c - '0');
+ }
+ if (c >= 'a' && c <= 'f') {
+ return (c - 'a') + 10;
+ }
+ if (c >= 'A' && c <= 'F') {
+ return (c - 'A') + 10;
+ }
+ return 0;
+}
+
+static int hex2bin(const char *hex, char *bin, int len) {
+ int i;
+
+ for (i=0 ; (i+1)<len ; i+=2, hex+=2, bin++) {
+ *bin = nibble(hex[0]) << 4 | nibble(hex[1]);
+ }
+
+ return i/2;
+}
+
+static int haveIndex(zend_long tag) {
+ /*
+ All DB indexes
+ excepted RPMDBI_PACKAGES and RPMDBI_LABEL doesn't match any tag
+ */
+ if (tag == RPMDBI_NAME ||
+ tag == RPMDBI_BASENAMES ||
+ tag == RPMDBI_GROUP ||
+ tag == RPMDBI_REQUIRENAME ||
+ tag == RPMDBI_PROVIDENAME ||
+ tag == RPMDBI_CONFLICTNAME ||
+ tag == RPMDBI_OBSOLETENAME ||
+ tag == RPMDBI_TRIGGERNAME ||
+ tag == RPMDBI_DIRNAMES ||
+ tag == RPMDBI_INSTALLTID ||
+ tag == RPMDBI_SIGMD5 ||
+ tag == RPMDBI_SHA1HEADER ||
+#ifdef HAVE_WEAKDEP
+ tag == RPMDBI_FILETRIGGERNAME ||
+ tag == RPMDBI_TRANSFILETRIGGERNAME ||
+ tag == RPMDBI_RECOMMENDNAME ||
+ tag == RPMDBI_SUGGESTNAME ||
+ tag == RPMDBI_SUPPLEMENTNAME ||
+ tag == RPMDBI_ENHANCENAME ||
+#endif
+ tag == RPMDBI_INSTFILENAMES) {
+ return 1;
+ }
+ return 0;
+}
+
+/* {{{ proto array rpmdbsearch(string pattern [, integer tag_name = RPMTAG_NAME [, integer mode = -1 [, bool full = 0]]])
+ Search information from installed RPMs */
+PHP_FUNCTION(rpmdbsearch)
+{
+ char MD5[16];
+ rpm_tid_t tid;
+ char *name;
+ size_t len;
+ zend_long crit = RPMTAG_NAME;
+ zend_long mode = -1;
+ zend_bool full = 0;
+ Header h;
+ rpmdb db;
+ rpmdbMatchIterator di;
+ int useIndex = 1;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llb", &name, &len, &crit, &mode, &full) == FAILURE) {
+ RETURN_THROWS();
+ }
+ if (rpmTagGetType(crit) == RPM_NULL_TYPE) {
+ zend_argument_value_error(2, "Unkown rpmtag");
+ RETURN_THROWS();
+ }
+ if (mode != RPMMIRE_DEFAULT &&
+ mode != RPMMIRE_STRCMP &&
+ mode != RPMMIRE_REGEX &&
+ mode != RPMMIRE_GLOB &&
+ mode != -1) {
+ zend_argument_value_error(3, "Unkown rpmmire");
+ RETURN_THROWS();
+ }
+
+ if (crit == RPMTAG_PKGID) {
+ if (len != 32) {
+ zend_argument_value_error(1, "Bad length for PKGID, 32 expected");
+ RETURN_THROWS();
+ }
+ len = hex2bin(name, MD5, len);
+ name = MD5;
+ } else if (crit == RPMTAG_HDRID) {
+ if (len != 40) {
+ zend_argument_value_error(1, "Bad length for HDRID, 40 expected");
+ RETURN_THROWS();
+ }
+ } else if (crit == RPMTAG_INSTALLTID) {
+ tid = atol(name);
+ name = (char *)&tid;
+ len = sizeof(tid);
+ } else if (crit == RPMTAG_INSTFILENAMES) {
+ /* use input parameters */
+ } else {
+ /* Faster mode if index exists and name is not a pattern */
+ useIndex = haveIndex(crit) && mode < 0;
+ }
+
+ db = rpminfo_getdb();
+ if (useIndex) {
+ /* Simple criterion using index */
+ di = rpmdbInitIterator(db, crit, name, len);
+ } else {
+ /* query all packages */
+ di = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0);
+ /* add criterion */
+ if (di) {
+ if (rpmdbSetIteratorRE(di, crit, (mode<0 ? RPMMIRE_DEFAULT : mode), name)) {
+ php_error_docref(NULL, E_WARNING, "Can't set filter");
+ RETURN_NULL();
+ }
+ }
+ }
+ if (!di) {
+ /* Not found */
+ RETURN_NULL();
+ }
+
+ array_init_size(return_value, rpmdbGetIteratorCount(di));
+ while ((h = rpmdbNextIterator(di)) != NULL) {
+ zval tmp;
+ rpm_header_to_zval(&tmp, h, full);
+ add_next_index_zval(return_value, &tmp);
+ }
+
+ rpmdbFreeIterator(di);
+}
+/* }}} */
/* {{{ proto int rpmcmpver(string evr1, string evr2)
- Compare 2 RPM evr (epoch:version-release) strings */
+ Compare 2 RPM EVRs (epoch:version-release) strings */
PHP_FUNCTION(rpmvercmp)
{
- char *evr1, *evr2;
- size_t len1, len2;
+ char *in_evr1, *evr1, *v1, *r1;
+ char *in_evr2, *evr2, *v2, *r2;
+ char *p, empty[] = "";
+ char *op = NULL;
+ long e1, e2, r;
+ size_t len1, len2, oplen;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|s", &in_evr1, &len1, &in_evr2, &len2, &op, &oplen) == FAILURE) {
+ RETURN_THROWS();
+ }
+ evr1 = estrdup(in_evr1);
+ evr2 = estrdup(in_evr2);
+
+ /* Epoch */
+ p = strchr(evr1, ':');
+ if (p) {
+ v1 = p+1;
+ *p=0;
+ e1 = atol(evr1);
+ } else {
+ v1 = evr1;
+ e1 = 0;
+ }
+ p = strchr(evr2, ':');
+ if (p) {
+ v2 = p+1;
+ *p=0;
+ e2 = atol(evr2);
+ } else {
+ v2 = evr2;
+ e2 = 0;
+ }
+ if (e1 < e2) {
+ r = -1;
+ } else if (e1 > e2) {
+ r = 1;
+ } else {
+ /* Version */
+ p = strchr(v1, '-');
+ if (p) {
+ r1 = p+1;
+ *p = 0;
+ } else {
+ r1 = empty;
+ }
+ p = strchr(v2, '-');
+ if (p) {
+ r2 = p+1;
+ *p = 0;
+ } else {
+ r2 = empty;
+ }
+ r = rpmvercmp(v1, v2);
+ if (!r) {
+ /* Release*/
+ r = rpmvercmp(r1, r2);
+ }
+ }
+ efree(evr1);
+ efree(evr2);
+
+ if (!op) {
+ RETURN_LONG(r);
+ }
+
+ if (!strcmp(op, "<") || !strcmp(op, "lt")) {
+ RETURN_BOOL(r < 0);
+ }
+ if (!strcmp(op, "<=") || !strcmp(op, "le")) {
+ RETURN_BOOL(r <= 0);
+ }
+ if (!strcmp(op, ">") || !strcmp(op, "gt")) {
+ RETURN_BOOL(r > 0);
+ }
+ if (!strcmp(op, ">=") || !strcmp(op, "ge")) {
+ RETURN_BOOL(r >= 0);
+ }
+ if (!strcmp(op, "==") || !strcmp(op, "=") || !strcmp(op, "eq")) {
+ RETURN_BOOL(r == 0);
+ }
+ if (!strcmp(op, "!=") || !strcmp(op, "<>") || !strcmp(op, "ne")) {
+ RETURN_BOOL(r != 0);
+ }
+
+ zend_argument_value_error(3, "must be a valid comparison operator");
+ RETURN_THROWS();
+}
+/* }}} */
+
+/* {{{ proto int rpmaddtag(int tag)
+ add a tag in the default set */
+PHP_FUNCTION(rpmaddtag)
+{
+ int i;
+ zend_long tag;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &tag) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ if (rpmTagGetType(tag) == RPM_NULL_TYPE) {
+ zend_argument_value_error(1, "Unkown rpmtag");
+ RETURN_THROWS();
+ }
+
+ if (RPMINFO_G(tags)) {
+ for (i=0 ; i<RPMINFO_G(nb_tags) ; i++) {
+ if (RPMINFO_G(tags)[i] == tag) {
+ RETURN_BOOL(0);
+ }
+ }
+ if (RPMINFO_G(nb_tags) == RPMINFO_G(max_tags)) {
+ RPMINFO_G(max_tags) += 16;
+ RPMINFO_G(tags) = erealloc(RPMINFO_G(tags), RPMINFO_G(max_tags) * sizeof(rpmTagVal));
+ }
+ } else {
+ RPMINFO_G(max_tags) = 16;
+ RPMINFO_G(tags) = emalloc(RPMINFO_G(max_tags) * sizeof(rpmTagVal));
+ }
+ RPMINFO_G(tags)[RPMINFO_G(nb_tags)++] = tag;
+
+ RETURN_BOOL(1);
+}
+/* }}} */
+
+#ifdef HAVE_ARCHIVE
+static ssize_t php_rpm_ops_read(php_stream *stream, char *buf, size_t count)
+{
+ ssize_t n = -1;
+ STREAM_DATA_FROM_STREAM();
+
+ if (self) {
+ n = rpmfiArchiveRead(self->fi, buf, count);
+ if (n == 0 || n < (ssize_t)count) {
+ stream->eof = 1;
+ }
+ }
+ return n;
+}
+
+static void php_rpm_ops_free(struct php_rpm_stream_data_t *self, int close_handle)
+{
+ if (self) {
+ if (close_handle) {
+ Fclose(self->gzdi);
+ rpmfilesFree(self->files);
+ rpmfiFree(self->fi);
+ headerFree(self->h);
+ }
+ efree(self);
+ }
+}
+
+static int php_rpm_ops_close(php_stream *stream, int close_handle)
+{
+ STREAM_DATA_FROM_STREAM();
+
+ php_rpm_ops_free(self, close_handle);
+ stream->abstract = NULL;
+
+ return EOF;
+}
+
+static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb)
+{
+ STREAM_DATA_FROM_STREAM();
+
+ if (self) {
+ struct stat s[2]; /* librpm may use different size (32-bit) */
+ int rc;
+ rc = rpmfiStat(self->fi, 0, s);
+ memcpy(&ssb->sb, s, sizeof(ssb->sb));
+ return rc;
+ }
+ return -1;
+}
+
+const php_stream_ops php_stream_rpmio_ops = {
+ NULL, /* write */
+ php_rpm_ops_read,
+ php_rpm_ops_close,
+ NULL, /* flush */
+ "rpm",
+ NULL, /* seek */
+ NULL, /* cast */
+ php_zip_ops_stat,
+ NULL /* set_option */
+};
+
+static struct php_rpm_stream_data_t *php_stream_rpm_finder(const char *path, int want_content)
+{
+ size_t path_len;
+ zend_string *file_basename;
+ char file_dirname[MAXPATHLEN];
+ char *fragment;
+ size_t fragment_len;
+ struct php_rpm_stream_data_t *self = NULL;
+ FD_t fdi;
+ FD_t gzdi;
+ int rc;
+ Header h;
+ char rpmio_flags[80];
+ const char *compr;
+ rpmfiles files;
+ rpmfi fi;
+ rpmts ts = rpminfo_getts();
+
+ fragment = strchr(path, '#');
+ if (!fragment) {
+ return NULL;
+ }
+ if (strncasecmp("rpm://", path, 6) == 0) {
+ path += 6;
+ }
+ fragment_len = strlen(fragment);
+ if (fragment_len < 1) {
+ return NULL;
+ }
+ path_len = strlen(path);
+ if (path_len >= MAXPATHLEN) {
+ return NULL;
+ }
+ memcpy(file_dirname, path, path_len - fragment_len);
+ file_dirname[path_len - fragment_len] = '\0';
+ file_basename = php_basename(path, path_len - fragment_len, NULL, 0);
+ fragment++;
+ if (php_check_open_basedir(file_dirname)) {
+ zend_string_release_ex(file_basename, 0);
+ return NULL;
+ }
+ fdi = Fopen(file_dirname, "r.ufdio");
+ if (Ferror(fdi)) {
+ zend_string_release_ex(file_basename, 0);
+ return NULL;
+ }
+ rc = rpmReadPackageFile(ts, fdi, "rpm2cpio", &h);
+ if (rc != RPMRC_OK && rc != RPMRC_NOKEY && rc != RPMRC_NOTTRUSTED) {
+ zend_string_release_ex(file_basename, 0);
+ Fclose(fdi);
+ return NULL;
+ }
+
+ compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
+ snprintf(rpmio_flags, sizeof(rpmio_flags), "r.%s", compr ? compr : "gzip");
+ gzdi = Fdopen(fdi, rpmio_flags);
+ if (gzdi == NULL) {
+ headerFree(h);
+ Fclose(fdi);
+ zend_string_release_ex(file_basename, 0);
+ return NULL;
+ }
+
+ files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
+ fi = rpmfiNewArchiveReader(gzdi, files, RPMFI_ITER_READ_ARCHIVE);
+
+ while((rc = rpmfiNext(fi)) >=0) {
+ const char *fn = rpmfiFN(fi);
+ /*
+ printf("Name=%s, Size=%d, N=%d, mode=%d, reg=%d, content=%d, rdev=%d, inode=%d link=%s\n", fn,
+ (int)rpmfiFSize(fi), (int)rpmfiFNlink(fi), (int)rpmfiFMode(fi),
+ (int)S_ISREG(rpmfiFMode(fi)), (int)rpmfiArchiveHasContent(fi),
+ (int)rpmfiFRdev(fi), (int)rpmfiFInode(fi), rpmfiFLink(fi));
+ */
+ if (!strcmp(fn, fragment)) {
+ if (want_content && S_ISREG(rpmfiFMode(fi)) && !rpmfiArchiveHasContent(fi)) {
+ rpm_rdev_t rdev = rpmfiFRdev(fi);
+ rpm_ino_t inode = rpmfiFInode(fi);
+ while((rc = rpmfiNext(fi)) >=0) {
+ if (rdev == rpmfiFRdev(fi) && inode == rpmfiFInode(fi) && rpmfiArchiveHasContent(fi)) {
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (rc == RPMERR_ITER_END) {
+ Fclose(gzdi);
+ rpmfilesFree(files);
+ rpmfiFree(fi);
+ headerFree(h);
+ } else {
+ self = emalloc(sizeof(*self));
+ self->gzdi = gzdi;
+ self->files = files;
+ self->fi = fi;
+ self->h = h;
+ }
+ zend_string_release_ex(file_basename, 0);
+
+ return self;
+}
+
+php_stream *php_stream_rpm_opener(php_stream_wrapper *wrapper,
+ const char *path,
+ const char *mode,
+ int options,
+ zend_string **opened_path,
+ php_stream_context *context STREAMS_DC)
+{
+ struct php_rpm_stream_data_t *self;
+
+ if (mode[0] != 'r') {
+ return NULL;
+ }
+ self = php_stream_rpm_finder(path, 1);
+ if (self) {
+ if (opened_path) {
+ *opened_path = zend_string_init(path, strlen(path), 0);
+ }
+ if (!S_ISREG(rpmfiFMode(self->fi)) || !rpmfiArchiveHasContent(self->fi)) {
+ php_rpm_ops_free(self, 1);
+ } else {
+ return php_stream_alloc(&php_stream_rpmio_ops, self, NULL, mode);
+ }
+ }
+
+ return NULL;
+}
+
+static int php_stream_rpm_stat(php_stream_wrapper *wrapper, const char *url, int flags,
+ php_stream_statbuf *ssb, php_stream_context *context)
+{
+ struct php_rpm_stream_data_t *self;
+ int rc = -1;
+
+ self = php_stream_rpm_finder(url, 0);
+ if (self) {
+ struct stat s[2]; /* librpm may use different size (32-bit) */
+ rc = rpmfiStat(self->fi, 0, s);
+ memcpy(&ssb->sb, s, sizeof(ssb->sb));
+ php_rpm_ops_free(self, 1);
+ }
+
+ return rc;
+}
+
+static const php_stream_wrapper_ops rpm_stream_wops = {
+ php_stream_rpm_opener,
+ NULL, /* close */
+ NULL, /* fstat */
+ php_stream_rpm_stat,
+ NULL, /* opendir */
+ "RPM wrapper",
+ NULL, /* unlink */
+ NULL, /* rename */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL /* metadata */
+};
+
+const php_stream_wrapper php_stream_rpm_wrapper = {
+ &rpm_stream_wops,
+ NULL,
+ 0 /* is_url */
+};
+
+/* {{{ proto array rpmgetsymlink(string path , string name)
+ Retrieve soft link target of en entry */
+PHP_FUNCTION(rpmgetsymlink)
+{
+ char *path, *name;
+ const char *link;
+ size_t plen, nlen;
+ FD_t fdi;
+ FD_t gzdi;
+ int rc;
+ Header h;
+ char rpmio_flags[80];
+ const char *compr;
+ rpmfiles files;
+ rpmfi fi;
+ rpmts ts = rpminfo_getts();
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &path, &plen, &name, &nlen) == FAILURE) {
+ RETURN_THROWS();
+ }
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &evr1, &len1, &evr2, &len2) == FAILURE) {
- return;
+ if (php_check_open_basedir(path)) {
+ RETURN_NULL();
+ }
+ fdi = Fopen(path, "r.ufdio");
+ if (Ferror(fdi)) {
+ RETURN_NULL();
+ }
+ rc = rpmReadPackageFile(ts, fdi, "rpm2cpio", &h);
+ if (rc != RPMRC_OK && rc != RPMRC_NOKEY && rc != RPMRC_NOTTRUSTED) {
+ Fclose(fdi);
+ RETURN_NULL();
}
- RETURN_LONG(rpmvercmp(evr1, evr2));
+ compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
+ snprintf(rpmio_flags, sizeof(rpmio_flags), "r.%s", compr ? compr : "gzip");
+ gzdi = Fdopen(fdi, rpmio_flags);
+ if (gzdi == NULL) {
+ headerFree(h);
+ Fclose(fdi);
+ RETURN_NULL();
+ }
+
+ files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
+ fi = rpmfiNewArchiveReader(gzdi, files, RPMFI_ITER_READ_ARCHIVE);
+
+ rc = rpmfiFindFN(fi, name);
+ rpmfiSetFX(fi, rc); /* return value have change in 4.18 (from previous to new) */
+ if (rc < 0
+ || rpmfiFX(fi) != rc
+ || (link = rpmfiFLink(fi)) == NULL) {
+ RETVAL_NULL();
+ } else {
+ RETVAL_STRING(link);
+ }
+ rpmfiFree(fi);
+ rpmfilesFree(files);
+ headerFree(h);
+ Fclose(gzdi);
}
/* }}} */
+#endif /* HAVE_ARCHIVE */
+
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(rpminfo)
{
+ const char *tagname;
+ rpmtd names;
+
REGISTER_STRING_CONSTANT("RPMVERSION", (char *)RPMVERSION, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RPMSENSE_ANY", RPMSENSE_ANY, CONST_CS | CONST_PERSISTENT);
@@ -330,6 +907,22 @@ PHP_MINIT_FUNCTION(rpminfo)
REGISTER_LONG_CONSTANT("RPMSENSE_KEYRING", RPMSENSE_KEYRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RPMSENSE_CONFIG", RPMSENSE_CONFIG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("RPMMIRE_DEFAULT", RPMMIRE_DEFAULT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("RPMMIRE_STRCMP", RPMMIRE_STRCMP, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("RPMMIRE_REGEX", RPMMIRE_REGEX, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("RPMMIRE_GLOB", RPMMIRE_GLOB, CONST_CS | CONST_PERSISTENT);
+
+ names = rpmtdNew();
+ rpmTagGetNames(names, 1);
+ while ((tagname = rpmtdNextString(names))) {
+ zend_register_long_constant(tagname, strlen(tagname), rpmTagGetValue(tagname+7), CONST_CS | CONST_PERSISTENT, module_number);
+ }
+ rpmtdFree(names);
+
+#ifdef HAVE_ARCHIVE
+ php_register_url_stream_wrapper("rpm", &php_stream_rpm_wrapper);
+#endif
+
return SUCCESS;
}
/* }}} */
@@ -341,6 +934,35 @@ PHP_RINIT_FUNCTION(rpminfo)
#if defined(COMPILE_DL_RPMINFO) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
+
+ RPMINFO_G(nb_tags) = 0;
+ RPMINFO_G(max_tags) = 0;
+ RPMINFO_G(tags) = NULL;
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION(rpminfo)
+{
+ if (RPMINFO_G(ts)) {
+ if (RPMINFO_G(db)) {
+ rpmtsCloseDB(RPMINFO_G(ts));
+ RPMINFO_G(db) = NULL;
+ }
+ rpmtsFree(RPMINFO_G(ts));
+ RPMINFO_G(ts) = NULL;
+ }
+
+ if (RPMINFO_G(tags)) {
+ efree(RPMINFO_G(tags));
+ RPMINFO_G(nb_tags) = 0;
+ RPMINFO_G(max_tags) = 0;
+ RPMINFO_G(tags) = NULL;
+ }
+
return SUCCESS;
}
/* }}} */
@@ -352,7 +974,14 @@ PHP_MINFO_FUNCTION(rpminfo)
php_info_print_table_start();
php_info_print_table_header(2, "rpminfo support", "enabled");
php_info_print_table_row(2, "Extension version", PHP_RPMINFO_VERSION);
+ php_info_print_table_row(2, "Author", PHP_RPMINFO_AUTHOR);
+ php_info_print_table_row(2, "License", PHP_RPMINFO_LICENSE);
php_info_print_table_row(2, "RPM library version", RPMVERSION);
+#ifdef HAVE_ARCHIVE
+ php_info_print_table_row(2, "RPM stream wrapper", "yes");
+#else
+ php_info_print_table_row(2, "RPM stream wrapper", "no");
+#endif
php_info_print_table_end();
/* Remove comments if you have entries in php.ini
@@ -369,32 +998,10 @@ static PHP_GINIT_FUNCTION(rpminfo) /* {{{ */
ZEND_TSRMLS_CACHE_UPDATE();
#endif
rpminfo_globals->ts = NULL;
+ rpminfo_globals->db = NULL;
}
/* }}} */
-/* {{{ PHP_GSHUTDOWN_FUNCTION
-*/
-PHP_GSHUTDOWN_FUNCTION(rpminfo)
-{
- if (rpminfo_globals->ts) {
- rpmtsFree(rpminfo_globals->ts);
- rpminfo_globals->ts = NULL;
- }
-}
-/* }}} */
-
-/* {{{ rpminfo_functions[]
- *
- * Every user visible function must have an entry in rpminfo_functions[].
- */
-const zend_function_entry rpminfo_functions[] = {
- PHP_FE(rpmdbinfo, arginfo_rpmdbinfo)
- PHP_FE(rpminfo, arginfo_rpminfo)
- PHP_FE(rpmvercmp, arginfo_rpmvercmp)
- PHP_FE_END
-};
-/* }}} */
-
/* {{{ rpminfo_module_entry
*/
zend_module_entry rpminfo_module_entry = {
@@ -402,16 +1009,16 @@ zend_module_entry rpminfo_module_entry = {
NULL,
NULL,
"rpminfo",
- rpminfo_functions,
+ ext_functions,
PHP_MINIT(rpminfo),
NULL,
PHP_RINIT(rpminfo),
- NULL,
+ PHP_RSHUTDOWN(rpminfo),
PHP_MINFO(rpminfo),
PHP_RPMINFO_VERSION,
PHP_MODULE_GLOBALS(rpminfo),
PHP_GINIT(rpminfo),
- PHP_GSHUTDOWN(rpminfo),
+ NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
diff --git a/rpminfo.stub.php b/rpminfo.stub.php
new file mode 100644
index 0000000..7e6e32e
--- /dev/null
+++ b/rpminfo.stub.php
@@ -0,0 +1,17 @@
+<?php
+
+/** @generate-function-entries */
+
+function rpmaddtag(int $rpmtag): bool {}
+
+function rpmdbinfo(string $nevr, bool $full = false): Array|null {}
+
+function rpmdbsearch(string $pattern, int $rpmtag = RPMTAG_NAME, int $rpmmire = -1, bool $full = false): Array|null {}
+
+function rpminfo(string $path, bool $full = false, ?string &$error = null): Array|null {}
+
+function rpmvercmp(string $evr1, string $evr2, ?string $operator = null): int|bool {}
+
+#ifdef HAVE_ARCHIVE
+function rpmgetsymlink(string $path, string $name): string|null {}
+#endif
diff --git a/rpminfo_arginfo.h b/rpminfo_arginfo.h
new file mode 100644
index 0000000..aa0e1d3
--- /dev/null
+++ b/rpminfo_arginfo.h
@@ -0,0 +1,60 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: e65e33b4f6ebedcc7ef3030714ebf7e4c06f2778 */
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rpmaddtag, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, rpmtag, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rpmdbinfo, 0, 1, IS_ARRAY, 1)
+ ZEND_ARG_TYPE_INFO(0, nevr, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, full, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rpmdbsearch, 0, 1, IS_ARRAY, 1)
+ ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, rpmtag, IS_LONG, 0, "RPMTAG_NAME")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, rpmmire, IS_LONG, 0, "-1")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, full, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rpminfo, 0, 1, IS_ARRAY, 1)
+ ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, full, _IS_BOOL, 0, "false")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(1, error, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_rpmvercmp, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
+ ZEND_ARG_TYPE_INFO(0, evr1, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, evr2, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, operator, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+#if defined(HAVE_ARCHIVE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rpmgetsymlink, 0, 2, IS_STRING, 1)
+ ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+#endif
+
+
+ZEND_FUNCTION(rpmaddtag);
+ZEND_FUNCTION(rpmdbinfo);
+ZEND_FUNCTION(rpmdbsearch);
+ZEND_FUNCTION(rpminfo);
+ZEND_FUNCTION(rpmvercmp);
+#if defined(HAVE_ARCHIVE)
+ZEND_FUNCTION(rpmgetsymlink);
+#endif
+
+
+static const zend_function_entry ext_functions[] = {
+ ZEND_FE(rpmaddtag, arginfo_rpmaddtag)
+ ZEND_FE(rpmdbinfo, arginfo_rpmdbinfo)
+ ZEND_FE(rpmdbsearch, arginfo_rpmdbsearch)
+ ZEND_FE(rpminfo, arginfo_rpminfo)
+ ZEND_FE(rpmvercmp, arginfo_rpmvercmp)
+#if defined(HAVE_ARCHIVE)
+ ZEND_FE(rpmgetsymlink, arginfo_rpmgetsymlink)
+#endif
+ ZEND_FE_END
+};
diff --git a/tests/002-rpmvercmp.phpt b/tests/002-rpmvercmp.phpt
index 6cffef0..c53a183 100644
--- a/tests/002-rpmvercmp.phpt
+++ b/tests/002-rpmvercmp.phpt
@@ -3,27 +3,82 @@ Check for rpmvercmp function
--SKIPIF--
<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
--FILE--
-<?php
-var_dump(rpmvercmp("1.0", "1.1"));
-var_dump(rpmvercmp("1.1", "1.0"));
-var_dump(rpmvercmp("1.0", "1.0"));
-// Errors
-var_dump(rpmvercmp());
-var_dump(rpmvercmp("a"));
-var_dump(rpmvercmp("a", "b", "c"));
-?>
-Done
---EXPECTF--
-int(-1)
-int(1)
-int(0)
+<?php
+$cases = [
+ ['1.0', '1.1', -1],
+ ['1.1', '1.0', 1],
+ ['1.0', '1.0', 0],
+ ['1.0', '1', 1],
+ ['1', '1.1', -1],
+ ['2.0.14-22.el7_0', '2.0.14.1-35.el7_6', -1],
+ ['', '', 0],
+ ['0:1', '1', 0],
+ ['0:1', '1:1', -1],
+ ['1:1', '2', 1],
+ ['1~RC1', '1', -1],
+ ['1~RC1', '1', -1],
+ ['1~RC1-1', '1-0', -1],
+ ['1~beta', '1~RC', 1],
+ ['1-1', '1-2', -1],
+ ['1.1-1', '1-1.1', 1],
+ ['1.1-1~a', '1.1-1', -1],
+ ['1.2.3-4', '1.2.3p1-2', -1],
+ ['1.2.3-4', '1.2.3+a-2', -1],
+];
+$ok = true;
+foreach ($cases as $case) {
+ list($a,$b,$expected) = $case;
+ $result = rpmvercmp($a,$b);
+ if ($result !== $expected) {
+ $ok = false;
+ printf("rpmvercmp(%s, %s) = %d when %d expected\n", $a, $b, $result, $expected);
+ }
+}
+
+$cases = [
+ ['1', '2', '>', false],
+ ['1', '2', 'gt', false],
+ ['1', '2', '>=', false],
+ ['1', '2', 'ge', false],
+ ['1', '1', '>=', true],
+ ['1', '1', 'ge', true],
+
+ ['1', '2', '<', true],
+ ['1', '2', 'lt', true],
+ ['1', '2', '<=', true],
+ ['1', '2', 'le', true],
+ ['1', '1', '<=', true],
+ ['1', '1', 'le', true],
-Warning: rpmvercmp() expects exactly 2 parameters, 0 given in %s/002-rpmvercmp.php on line 6
-NULL
+ ['1', '1', '=', true],
+ ['1', '1', '==', true],
+ ['1', '1', 'eq', true],
-Warning: rpmvercmp() expects exactly 2 parameters, 1 given in %s/002-rpmvercmp.php on line 7
-NULL
+ ['1', '2', '=', false],
+ ['1', '2', '==', false],
+ ['1', '2', 'eq', false],
-Warning: rpmvercmp() expects exactly 2 parameters, 3 given in %s/002-rpmvercmp.php on line 8
-NULL
+ ['1', '1', '!=', false],
+ ['1', '1', '<>', false],
+ ['1', '1', 'ne', false],
+
+ ['1', '2', '!=', true],
+ ['1', '2', '<>', true],
+ ['1', '2', 'ne', true],
+];
+foreach ($cases as $case) {
+ list($a,$b,$op,$expected) = $case;
+ $result = rpmvercmp($a,$b,$op);
+ if ($result !== $expected) {
+ $ok = false;
+ printf("rpmvercmp(%s, %s, %s) = %s when %s expected\n",
+ $a, $b, $op, $result ? "true" : "false", $expected ? "true" : "false");
+ }
+}
+
+if ($ok) echo "OK\n";
+?>
+Done
+--EXPECTF--
+OK
Done
diff --git a/tests/003-rpminfo.phpt b/tests/003-rpminfo.phpt
index beb7b47..919bcc9 100644
--- a/tests/003-rpminfo.phpt
+++ b/tests/003-rpminfo.phpt
@@ -14,7 +14,7 @@ array(5) {
["Version"]=>
string(1) "1"
["Release"]=>
- string(11) "1.fc27.remi"
+ string(1) "3"
["Summary"]=>
string(5) "Bidon"
["Arch"]=>
diff --git a/tests/005-rpminfo-full.phpt b/tests/005-rpminfo-full.phpt
index 38f5bab..15c158d 100644
--- a/tests/005-rpminfo-full.phpt
+++ b/tests/005-rpminfo-full.phpt
@@ -24,9 +24,11 @@ Done
--- bidon.rpm ---
string(5) "bidon"
string(15) "A dummy package"
-array(1) {
+array(2) {
[0]=>
- string(8) "- create"
+ string(14) "- add symlinks"
+ [1]=>
+ string(20) "- add some hardlinks"
}
bool(false)
array(1) {
diff --git a/tests/006-rpminfo-errors.phpt b/tests/006-rpminfo-errors.phpt
index edacbdb..5d205d7 100644
--- a/tests/006-rpminfo-errors.phpt
+++ b/tests/006-rpminfo-errors.phpt
@@ -21,15 +21,15 @@ Done
+ PHP Warnings
Warning: rpminfo(): Can't open '%s/tests/missing.rpm': No such file or directory in %s on line %d
-bool(false)
+NULL
Warning: rpminfo(): Can't read '%s/tests/006-rpminfo-errors.php': Argument is not a RPM file in %s on line %d
-bool(false)
+NULL
+ PHP Warnings
-bool(false)
+NULL
string(%d) "Can't open '%s/tests/missing.rpm': No such file or directory"
-bool(false)
+NULL
string(%d) "Can't read '%s/tests/006-rpminfo-errors.php': Argument is not a RPM file"
bool(true)
NULL
diff --git a/tests/007-rpmdbinfo.phpt b/tests/007-rpmdbinfo.phpt
index 6fe3116..4a977d1 100644
--- a/tests/007-rpmdbinfo.phpt
+++ b/tests/007-rpmdbinfo.phpt
@@ -1,15 +1,19 @@
--TEST--
-Check for rpmdbinfo function
+Check for rpmdbinfo function on Name
--SKIPIF--
<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
--FILE--
<?php
var_dump(rpmdbinfo('doesntexistsinrpmdb'));
var_dump(rpmdbinfo('bash'));
+var_dump(rpmaddtag(RPMTAG_INSTALLTIME));
+var_dump(rpmaddtag(RPMTAG_BUILDTIME));
+var_dump(rpmaddtag(RPMTAG_INSTALLTIME));
+var_dump(rpmdbinfo('bash'));
?>
Done
--EXPECTF--
-bool(false)
+NULL
array(1) {
[0]=>
array(5) {
@@ -25,4 +29,26 @@ array(1) {
string(%d) "%s"
}
}
+bool(true)
+bool(true)
+bool(false)
+array(1) {
+ [0]=>
+ array(7) {
+ ["Name"]=>
+ string(4) "bash"
+ ["Version"]=>
+ string(%d) "%s"
+ ["Release"]=>
+ string(%d) "%s"
+ ["Summary"]=>
+ string(26) "The GNU Bourne Again shell"
+ ["Buildtime"]=>
+ int(%d)
+ ["Installtime"]=>
+ int(%d)
+ ["Arch"]=>
+ string(%d) "%s"
+ }
+}
Done
diff --git a/tests/008-rpmdbsearch.phpt b/tests/008-rpmdbsearch.phpt
new file mode 100644
index 0000000..f348b92
--- /dev/null
+++ b/tests/008-rpmdbsearch.phpt
@@ -0,0 +1,67 @@
+--TEST--
+Check for rpmdbinfo function
+--SKIPIF--
+<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
+--FILE--
+<?php
+echo "Name / glob\n";
+$a = rpmdbsearch('php*', RPMTAG_NAME , RPMMIRE_GLOB);
+var_dump(count($a) > 1);
+
+echo "Name / regex\n";
+$a = rpmdbsearch('^php', RPMTAG_NAME, RPMMIRE_REGEX);
+var_dump(count($a) > 1);
+
+echo "Installed file\n";
+$a = rpmdbsearch(PHP_BINARY, RPMTAG_INSTFILENAMES);
+var_dump(count($a) == 1);
+
+$phprpm = $a[0]['Name'];
+$p = rpmdbinfo($phprpm, 1);
+
+echo "Pkgid\n";
+$a = rpmdbsearch($p[0]['Sigmd5'], RPMTAG_PKGID);
+var_dump($a[0]['Name'] == $phprpm);
+
+echo "Hdrid\n";
+$a = rpmdbsearch($p[0]['Sha1header'], RPMTAG_HDRID);
+var_dump($a[0]['Name'] == $phprpm);
+var_dump(count($a[0]) < 10);
+
+echo "Hdrid (full)\n";
+$a = rpmdbsearch($p[0]['Sha1header'], RPMTAG_HDRID, -1, true);
+var_dump($a[0]['Name'] == $phprpm);
+var_dump($a[0]['Sha1header'] == $p[0]['Sha1header']);
+var_dump(count($a[0]) > 20);
+
+echo "Installtid\n";
+$a = rpmdbsearch($p[0]['Installtid'], RPMTAG_INSTALLTID);
+var_dump(count($a) >= 1);
+
+echo "Version\n";
+$a = rpmdbsearch($p[0]['Version'], RPMTAG_VERSION);
+var_dump(count($a) > 1);
+
+?>
+Done
+--EXPECTF--
+Name / glob
+bool(true)
+Name / regex
+bool(true)
+Installed file
+bool(true)
+Pkgid
+bool(true)
+Hdrid
+bool(true)
+bool(true)
+Hdrid (full)
+bool(true)
+bool(true)
+bool(true)
+Installtid
+bool(true)
+Version
+bool(true)
+Done
diff --git a/tests/009-rpmdbinfo2.phpt b/tests/009-rpmdbinfo2.phpt
new file mode 100644
index 0000000..23f95c5
--- /dev/null
+++ b/tests/009-rpmdbinfo2.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Check for rpmdbinfo function on NEVR
+--SKIPIF--
+<?php
+if (!extension_loaded("rpminfo")) print "skip";
+if (!rpmdbinfo('kernel')) print "skip kernel not installed";
+?>
+--FILE--
+<?php
+$k = rpmdbinfo('kernel');
+var_dump(count($k) > 0); // multiple kernels
+$n = $k[0]['Name'] . '-' . $k[0]['Version'] . '-' . $k[0]['Release'];
+
+$k = rpmdbinfo($n); // single kernel with NEVR
+var_dump(count($k) ==1);
+
+?>
+Done
+--EXPECTF--
+bool(true)
+bool(true)
+Done
diff --git a/tests/011-rpmvercmp_error8.phpt b/tests/011-rpmvercmp_error8.phpt
new file mode 100644
index 0000000..2fe6aa1
--- /dev/null
+++ b/tests/011-rpmvercmp_error8.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Check for rpmvercmp function
+--SKIPIF--
+<?php
+if (!extension_loaded("rpminfo")) print "skip";
+if (PHP_VERSION_ID < 80000) print "skip only for PHP 8";
+?>
+--FILE--
+<?php
+try {
+ var_dump(rpmvercmp());
+} catch (ArgumentCountError $e) {
+ echo $e->getMessage(), "\n";
+}
+try {
+ var_dump(rpmvercmp("a"));
+} catch (ArgumentCountError $e) {
+ echo $e->getMessage(), "\n";
+}
+try {
+ var_dump(rpmvercmp("a", "b", "c"));
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+try {
+ var_dump(rpmvercmp("a", "b", "c", "d"));
+} catch (ArgumentCountError $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+Done
+--EXPECTF--
+rpmvercmp() expects at least 2 %s, 0 given
+rpmvercmp() expects at least 2 %s, 1 given
+rpmvercmp(): Argument #3 ($operator) must be a valid comparison operator
+rpmvercmp() expects at most 3 %s, 4 given
+Done
diff --git a/tests/012-rpmaddtag.phpt b/tests/012-rpmaddtag.phpt
new file mode 100644
index 0000000..bf371a4
--- /dev/null
+++ b/tests/012-rpmaddtag.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Check for rpmaddtag parameter check
+--SKIPIF--
+<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
+--FILE--
+<?php
+var_dump(rpmaddtag(RPMTAG_INSTALLTIME));
+try {
+ var_dump(rpmaddtag(-1));
+} catch (ValueError $e) {
+ echo $e->getMessage();
+}
+?>
+--EXPECTF--
+bool(true)
+%A Unkown rpmtag%A
diff --git a/tests/013-rpmdbsearch-error.phpt b/tests/013-rpmdbsearch-error.phpt
new file mode 100644
index 0000000..4a61227
--- /dev/null
+++ b/tests/013-rpmdbsearch-error.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Check for rpmdbinfo function
+--SKIPIF--
+<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
+--FILE--
+<?php
+var_dump(rpmdbsearch('notexists', RPMTAG_NAME));
+try {
+var_dump(rpmdbsearch('notexists', RPMTAG_NAME, 99));
+} catch (ValueError $e) {
+ echo $e->getMessage();
+}
+?>
+--EXPECTF--
+NULL
+%A Unkown rpmmire%A
diff --git a/tests/014-stream.phpt b/tests/014-stream.phpt
new file mode 100644
index 0000000..69b881e
--- /dev/null
+++ b/tests/014-stream.phpt
@@ -0,0 +1,97 @@
+--TEST--
+Check for stream
+--SKIPIF--
+<?php
+if (!extension_loaded("rpminfo")) print "skip";
+if (version_compare(RPMVERSION, '4.13', 'lt')) print("skip librpm is older than 4.13");
+?>
+--FILE--
+<?php
+$d = "rpm://" . __DIR__ . "/bidon.rpm#/usr/share/doc/bidon";
+$n = "rpm://" . __DIR__ . "/bidon.rpm#/usr/share/doc/bidon/README";
+$x = "rpm://" . __DIR__ . "/bidon.rpm#/usr/share/doc/bidon/MISSING";
+$foo = "rpm://" . __DIR__ . "/bidon.rpm#/etc/foo.conf";
+$bar = "rpm://" . __DIR__ . "/bidon.rpm#/etc/bar.conf";
+$toto = "rpm://" . __DIR__ . "/bidon.rpm#/etc/toto.conf";
+
+echo "+ wrapper\n";
+var_dump(in_array('rpm', stream_get_wrappers()));
+
+echo "+ stat\n";
+$s = stat($d); // S_ISDIR
+var_dump($s['size'], $s['mode'] , ($s['mode'] & 0170000) == 0040000 ? "OK" : "KO");
+var_dump(file_exists($d), is_dir($d), is_file($d), is_link($n));
+$s = stat($n); // S_ISREG
+var_dump($s['size'], $s['mode'] , ($s['mode'] & 0170000) == 0100000 ? "OK" : "KO");
+var_dump(file_exists($n), is_dir($n), is_file($n), is_link($n));
+$s = stat($toto); // S_ISLNK
+var_dump($s['size'], $s['mode'] , ($s['mode'] & 0170000) == 0120000 ? "OK" : "KO");
+var_dump(file_exists($toto), is_dir($toto), is_file($toto), is_link($toto));
+
+echo "+ file\n";
+var_dump($f = fopen($n, "r"));
+$s = fstat($f);
+var_dump($s['size'], $s['mode']);
+var_dump(trim(fread($f, 10)));
+var_dump(feof($f));
+var_dump(trim(fread($f, 100)));
+var_dump(feof($f));
+fclose($f);
+
+echo "+ stream\n";
+var_dump(trim(file_get_contents($n))); // Existing file
+var_dump(trim(file_get_contents($foo))); // Hardlink with content
+var_dump(trim(file_get_contents($bar))); // hardlink without content
+var_dump(file_get_contents($x)); // Missing file
+
+echo "+ symlink\n";
+var_dump(rpmgetsymlink(__DIR__ . "/bidon.rpm", "missing"));
+var_dump(rpmgetsymlink(__DIR__ . "/bidon.rpm", "/etc/foo.conf")); // not a symlink
+var_dump(rpmgetsymlink(__DIR__ . "/bidon.rpm", "/etc/toto.conf")); // symlink
+?>
+Done
+--EXPECTF--
++ wrapper
+bool(true)
++ stat
+int(0)
+int(16877)
+string(2) "OK"
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+int(30)
+int(33188)
+string(2) "OK"
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+int(8)
+int(41471)
+string(2) "OK"
+bool(true)
+bool(false)
+bool(false)
+bool(true)
++ file
+resource(%d) of type (stream)
+int(30)
+int(33188)
+string(10) "Thu Oct 19"
+bool(false)
+string(18) "12:01:02 CEST 2023"
+bool(true)
++ stream
+string(29) "Thu Oct 19 12:01:02 CEST 2023"
+string(7) "content"
+string(7) "content"
+
+Warning: file_get_contents(%s/bidon.rpm#/usr/share/doc/bidon/MISSING): Failed to open stream: operation failed in %s on line %d
+bool(false)
++ symlink
+NULL
+string(0) ""
+string(8) "foo.conf"
+Done
diff --git a/tests/bidon.rpm b/tests/bidon.rpm
index 6cbfa04..d8efb06 100644
--- a/tests/bidon.rpm
+++ b/tests/bidon.rpm
Binary files differ
diff --git a/tests/bidon.spec b/tests/bidon.spec
new file mode 100644
index 0000000..b3ad790
--- /dev/null
+++ b/tests/bidon.spec
@@ -0,0 +1,42 @@
+%{!?ver: %global ver 1}
+
+Name: bidon
+Version: %{ver}
+Release: 3
+Summary: Bidon
+License: Public Domain
+URL: https://rpms.remirepo.net/
+
+Obsoletes: fooobs < 2
+
+
+%description
+A dummy package
+
+%prep
+date >README
+echo "content" >conf
+
+%build
+: nothing to build
+
+%install
+install -Dpm644 conf %{buildroot}%{_sysconfdir}/foo.conf
+cd %{buildroot}%{_sysconfdir}
+ln foo.conf bar.conf
+ln -s foo.conf toto.conf
+
+%files
+%doc README
+%config(noreplace) %{_sysconfdir}/*.conf
+
+
+%changelog
+* Thu Oct 19 2023 Remi Collet <remi@fedoraproject.org> - 1-3
+- add symlinks
+
+* Fri Oct 13 2023 Remi Collet <remi@fedoraproject.org> - 1-2
+- add some hardlinks
+
+* Wed Dec 24 2014 Remi Collet <remi@fedoraproject.org> - 1-1
+- create