diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 000000000..45c82d32e --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,47 @@ +# Makefile for rpm library. + +AUTOMAKE_OPTIONS = 1.4 foreign + +PYVER= @WITH_PYTHON_VERSION@ + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/rpmdb \ + -I$(top_srcdir)/rpmio \ + -I$(top_srcdir)/beecrypt \ + -I$(top_srcdir)/popt \ + -I/usr/include/python${PYVER} \ + @INCPATH@ + +EXTRA_DIST = db-py.h hash.h header-py.h upgrade.h + +mylibs= \ + $(top_builddir)/lib/librpm.la \ + $(top_builddir)/rpmdb/librpmdb.la \ + $(top_builddir)/rpmio/librpmio.la \ + $(top_builddir)/popt/libpopt.la + +LDADD = + +pythondir = $(prefix)/lib/python${PYVER}/site-packages +python_PROGRAMS = rpmmodule.so poptmodule.so + +rpmmodule_so_SOURCES = +rpmmodule_so_LDFLAGS = $(mylibs) $(LIBS) -shared -Wl,-soname,rpmmodule.so + +poptmodule_so_SOURCES = poptmodule.c +poptmodule_so_LDFLAGS = $(mylibs) $(LIBS) -shared -Wl,-soname,poptmodule.so + +noinst_LTLIBRARIES = librpmmodule.la +librpmmodule_la_SOURCES = rpmmodule.c hash.c upgrade.c header-py.c db-py.c + +rpmmodule.so$(EXEEXT): $(librpmmodule_la_OBJECTS) + $(LINK) -o $@ $(librpmmodule_la_OBJECTS) $(rpmmodule_so_LDFLAGS) + +poptmodule.so$(EXEEXT): $(poptmodule_so_OBJECTS) + $(LINK) -o $@ $(poptmodule_so_OBJECTS) $(poptmodule_so_LDFLAGS) + +.PHONY: lclint +lclint: + lclint $(DEFS) $(INCLUDES) $(librpmmodule_la_SOURCES) diff --git a/python/Makefile.in b/python/Makefile.in new file mode 100644 index 000000000..e7620161b --- /dev/null +++ b/python/Makefile.in @@ -0,0 +1,509 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Makefile for rpm library. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_alias = @build_alias@ +build_triplet = @build@ +host_alias = @host_alias@ +host_triplet = @host@ +target_alias = @target_alias@ +target_triplet = @target@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AWK = @AWK@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_RPMNLSTOOLS = @BUILD_RPMNLSTOOLS@ +BZIP2BIN = @BZIP2BIN@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CPP = @CPP@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +DATADIRNAME = @DATADIRNAME@ +DBLIBOBJS = @DBLIBOBJS@ +DBLIBSRCS = @DBLIBSRCS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +EXEEXT = @EXEEXT@ +FINDPROVIDES = @FINDPROVIDES@ +FINDREQUIRES = @FINDREQUIRES@ +FIXPERMS = @FIXPERMS@ +GENCAT = @GENCAT@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +GZIPBIN = @GZIPBIN@ +HAVE_LIB = @HAVE_LIB@ +INCPATH = @INCPATH@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLBISON = @INTLBISON@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@ +LDFLAGS_STATIC = @LDFLAGS_STATIC@ +LIB = @LIB@ +LIBDIR = @LIBDIR@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMISC = @LIBMISC@ +LIBOBJS = @LIBOBJS@ +LIBRPMALIAS_FILENAME = @LIBRPMALIAS_FILENAME@ +LIBRPMRC_FILENAME = @LIBRPMRC_FILENAME@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIB = @LTLIB@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +MACROFILES = @MACROFILES@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PGPBIN = @PGPBIN@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +ROOT_GROUP = @ROOT_GROUP@ +RPM = @RPM@ +RPMCANONARCH = @RPMCANONARCH@ +RPMCANONOS = @RPMCANONOS@ +RPMCANONVENDOR = @RPMCANONVENDOR@ +RPMCONFIGDIR = @RPMCONFIGDIR@ +RPMGID = @RPMGID@ +RPMGROUP = @RPMGROUP@ +RPMUID = @RPMUID@ +RPMUSER = @RPMUSER@ +STRIP = @STRIP@ +SYSCONFIGDIR = @SYSCONFIGDIR@ +U = @U@ +UNZIPBIN = @UNZIPBIN@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WITH_APIDOCS = @WITH_APIDOCS@ +WITH_APIDOCS_TARGET = @WITH_APIDOCS_TARGET@ +WITH_BZIP2 = @WITH_BZIP2@ +WITH_DB_SUBDIR = @WITH_DB_SUBDIR@ +WITH_INTERNAL_DB = @WITH_INTERNAL_DB@ +WITH_PYTHON_SUBDIR = @WITH_PYTHON_SUBDIR@ +WITH_PYTHON_SUBPACKAGE = @WITH_PYTHON_SUBPACKAGE@ +WITH_PYTHON_VERSION = @WITH_PYTHON_VERSION@ +WITH_ZLIB_INCLUDE = @WITH_ZLIB_INCLUDE@ +WITH_ZLIB_LIB = @WITH_ZLIB_LIB@ +WITH_ZLIB_SUBDIR = @WITH_ZLIB_SUBDIR@ +__CAT = @__CAT@ +__CHGRP = @__CHGRP@ +__CHGRP_RHF = @__CHGRP_RHF@ +__CHMOD = @__CHMOD@ +__CHOWN = @__CHOWN@ +__CHOWN_RHF = @__CHOWN_RHF@ +__CP = @__CP@ +__CPIO = @__CPIO@ +__DOXYGEN = @__DOXYGEN@ +__FILE = @__FILE@ +__GPG = @__GPG@ +__GREP = @__GREP@ +__ID = @__ID@ +__ID_U = @__ID_U@ +__INSTALL = @__INSTALL@ +__LD = @__LD@ +__MAKE = @__MAKE@ +__MKDIR = @__MKDIR@ +__MV = @__MV@ +__NM = @__NM@ +__OBJCOPY = @__OBJCOPY@ +__OBJDUMP = @__OBJDUMP@ +__PATCH = @__PATCH@ +__PERL = @__PERL@ +__PYTHON = @__PYTHON@ +__RM = @__RM@ +__RSH = @__RSH@ +__SED = @__SED@ +__SSH = @__SSH@ +__STRIP = @__STRIP@ +__TAR = @__TAR@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +libdb3 = @libdb3@ +libdb3a = @libdb3a@ +testdir = @testdir@ +tmpdir = @tmpdir@ +varprefix = @varprefix@ + +AUTOMAKE_OPTIONS = 1.4 foreign + +PYVER = @WITH_PYTHON_VERSION@ + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/rpmdb \ + -I$(top_srcdir)/rpmio \ + -I$(top_srcdir)/beecrypt \ + -I$(top_srcdir)/popt \ + -I/usr/include/python${PYVER} \ + @INCPATH@ + + +EXTRA_DIST = db-py.h hash.h header-py.h upgrade.h + +mylibs = \ + $(top_builddir)/lib/librpm.la \ + $(top_builddir)/rpmdb/librpmdb.la \ + $(top_builddir)/rpmio/librpmio.la \ + $(top_builddir)/popt/libpopt.la + + +LDADD = + +pythondir = $(prefix)/lib/python${PYVER}/site-packages +python_PROGRAMS = rpmmodule.so poptmodule.so + +rpmmodule_so_SOURCES = +rpmmodule_so_LDFLAGS = $(mylibs) $(LIBS) -shared -Wl,-soname,rpmmodule.so + +poptmodule_so_SOURCES = poptmodule.c +poptmodule_so_LDFLAGS = $(mylibs) $(LIBS) -shared -Wl,-soname,poptmodule.so + +noinst_LTLIBRARIES = librpmmodule.la +librpmmodule_la_SOURCES = rpmmodule.c hash.c upgrade.c header-py.c db-py.c +subdir = python +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) + +librpmmodule_la_LDFLAGS = +librpmmodule_la_LIBADD = +am_librpmmodule_la_OBJECTS = rpmmodule.lo hash.lo upgrade.lo \ + header-py.lo db-py.lo +librpmmodule_la_OBJECTS = $(am_librpmmodule_la_OBJECTS) +python_PROGRAMS = rpmmodule.so$(EXEEXT) poptmodule.so$(EXEEXT) +PROGRAMS = $(python_PROGRAMS) + +am_poptmodule_so_OBJECTS = poptmodule.$(OBJEXT) +poptmodule_so_OBJECTS = $(am_poptmodule_so_OBJECTS) +poptmodule_so_LDADD = $(LDADD) +poptmodule_so_DEPENDENCIES = +am_rpmmodule_so_OBJECTS = +rpmmodule_so_OBJECTS = $(am_rpmmodule_so_OBJECTS) +rpmmodule_so_LDADD = $(LDADD) +rpmmodule_so_DEPENDENCIES = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/db-py.Plo $(DEPDIR)/hash.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/header-py.Plo $(DEPDIR)/poptmodule.Po \ +@AMDEP_TRUE@ $(DEPDIR)/rpmmodule.Plo $(DEPDIR)/upgrade.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(librpmmodule_la_SOURCES) $(poptmodule_so_SOURCES) \ + $(rpmmodule_so_SOURCES) +DIST_COMMON = ChangeLog Makefile.am Makefile.in +SOURCES = $(librpmmodule_la_SOURCES) $(poptmodule_so_SOURCES) $(rpmmodule_so_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign python/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) +librpmmodule.la: $(librpmmodule_la_OBJECTS) $(librpmmodule_la_DEPENDENCIES) + $(LINK) $(librpmmodule_la_LDFLAGS) $(librpmmodule_la_OBJECTS) $(librpmmodule_la_LIBADD) $(LIBS) +install-pythonPROGRAMS: $(python_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pythondir) + @list='$(python_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pythondir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pythondir)/$$f; \ + else :; fi; \ + done + +uninstall-pythonPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(python_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f $(DESTDIR)$(pythondir)/$$f"; \ + rm -f $(DESTDIR)$(pythondir)/$$f; \ + done + +clean-pythonPROGRAMS: + -test -z "$(python_PROGRAMS)" || rm -f $(python_PROGRAMS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/db-py.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/header-py.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/poptmodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rpmmodule.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/upgrade.Plo@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CCDEPMODE = @CCDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(pythondir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-pythonPROGRAMS mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-pythonPROGRAMS + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am uninstall-pythonPROGRAMS + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES clean-pythonPROGRAMS \ + distclean distclean-compile distclean-depend distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-data install-data-am \ + install-exec install-exec-am install-info install-info-am \ + install-man install-pythonPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool tags uninstall \ + uninstall-am uninstall-info-am uninstall-pythonPROGRAMS + + +rpmmodule.so$(EXEEXT): $(librpmmodule_la_OBJECTS) + $(LINK) -o $@ $(librpmmodule_la_OBJECTS) $(rpmmodule_so_LDFLAGS) + +poptmodule.so$(EXEEXT): $(poptmodule_so_OBJECTS) + $(LINK) -o $@ $(poptmodule_so_OBJECTS) $(poptmodule_so_LDFLAGS) + +.PHONY: lclint +lclint: + lclint $(DEFS) $(INCLUDES) $(librpmmodule_la_SOURCES) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/python/db-py.c b/python/db-py.c new file mode 100644 index 000000000..74aeb9b58 --- /dev/null +++ b/python/db-py.c @@ -0,0 +1,499 @@ +/** \ingroup python + * \file python/db-py.c + */ + +#include +#include + +#include "Python.h" +#include "rpmio_internal.h" +#include "rpmcli.h" /* XXX for rpmCheckSig */ +#include "misc.h" +#include "header_internal.h" +#include "upgrade.h" + +#include "db-py.h" +#include "header-py.h" + +/** \ingroup python + */ +typedef struct rpmdbMIObject_s rpmdbMIObject; + +/** \ingroup python + * \class rpmdbMatchIterator + * \brief A python rpmdbMatchIterator object represents the result of an RPM + * database query. + */ + +/** \ingroup python + * \name Class: rpmdbMatchIterator + */ +/*@{*/ + +/** \ingroup python + */ +struct rpmdbMIObject_s { + PyObject_HEAD; + rpmdbObject *db; + rpmdbMatchIterator mi; +} ; + +/** \ingroup python + */ +static PyObject * +rpmdbMINext(rpmdbMIObject * s, PyObject * args) { + /* XXX assume header? */ + Header h; + hdrObject * ho; + + if (!PyArg_ParseTuple (args, "")) return NULL; + + h = rpmdbNextIterator(s->mi); + if (!h) { + Py_INCREF(Py_None); + return Py_None; + } + + ho = createHeaderObject(h); + + return (PyObject *) ho; +} + +/** \ingroup python + */ +static PyObject * +rpmdbMIpattern(rpmdbMIObject * s, PyObject * args) { + PyObject *index = NULL; + int type; + char * pattern; + int tag; + + if (!PyArg_ParseTuple(args, "Ois", &index, &type, &pattern)) + return NULL; + + if (index == NULL) + tag = 0; + else if ((tag = tagNumFromPyObject (index)) == -1) { + PyErr_SetString(PyExc_TypeError, "unknown tag type"); + return NULL; + } + + rpmdbSetIteratorRE(s->mi, tag, type, pattern); + + Py_INCREF (Py_None); + return Py_None; + +} + +/** \ingroup python + */ +static struct PyMethodDef rpmdbMIMethods[] = { + {"next", (PyCFunction) rpmdbMINext, 1 }, + {"pattern", (PyCFunction) rpmdbMIpattern, 1 }, + {NULL, NULL} /* sentinel */ +}; + +/** \ingroup python + */ +static PyObject * rpmdbMIGetAttr (rpmdbObject *s, char *name) { + return Py_FindMethod (rpmdbMIMethods, (PyObject *) s, name); +} + +/** \ingroup python + */ +static void rpmdbMIDealloc(rpmdbMIObject * s) { + if (s && s->mi) { + rpmdbFreeIterator(s->mi); + } + Py_DECREF (s->db); + PyMem_DEL(s); +} + +/** \ingroup python + */ +PyTypeObject rpmdbMIType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "rpmdbMatchIterator", /* tp_name */ + sizeof(rpmdbMIObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) rpmdbMIDealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) rpmdbMIGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ +}; + +/*@}*/ + +/** \ingroup python + * \class rpmdb + * \brief A python rpmdb object represents an RPM database. + * + * Instances of the rpmdb object provide access to the records of a + * RPM database. The records are accessed by index number. To + * retrieve the header data in the RPM database, the rpmdb object is + * subscripted as you would access members of a list. + * + * The rpmdb class contains the following methods: + * + * - firstkey() Returns the index of the first record in the database. + * @deprecated Legacy, use rpmdbMatchIterator instead. + * + * - nextkey(index) Returns the index of the next record after "index" in the + * database. + * @param index current rpmdb location + * @deprecated Legacy, use rpmdbMatchIterator instead. + * + * - findbyfile(file) Returns a list of the indexes to records that own file + * "file". + * @param file absolute path to file + * + * - findbyname(name) Returns a list of the indexes to records for packages + * named "name". + * @param name package name + * + * - findbyprovides(dep) Returns a list of the indexes to records for packages + * that provide "dep". + * @param dep provided dependency string + * + * To obtain a rpmdb object, the opendb function in the rpm module + * must be called. The opendb function takes two optional arguments. + * The first optional argument is a boolean flag that specifies if the + * database is to be opened for read/write access or read-only access. + * The second argument specifies an alternate root directory for RPM + * to use. + * + * An example of opening a database and retrieving the first header in + * the database, then printing the name of the package that the header + * represents: + * \code + * import rpm + * rpmdb = rpm.opendb() + * index = rpmdb.firstkey() + * header = rpmdb[index] + * print header[rpm.RPMTAG_NAME] + * \endcode + * To print all of the packages in the database that match a package + * name, the code will look like this: + * \code + * import rpm + * rpmdb = rpm.opendb() + * indexes = rpmdb.findbyname("foo") + * for index in indexes: + * header = rpmdb[index] + * print "%s-%s-%s" % (header[rpm.RPMTAG_NAME], + * header[rpm.RPMTAG_VERSION], + * header[rpm.RPMTAG_RELEASE]) + * \endcode + */ + +/** \ingroup python + * \name Class: rpmdb + */ +/*@{*/ + +/** + */ +static PyObject * rpmdbFirst(rpmdbObject * s, PyObject * args) { + int first; + + if (!PyArg_ParseTuple (args, "")) return NULL; + + /* Acquire all offsets in one fell swoop. */ + if (s->offsets == NULL || s->noffs <= 0) { + rpmdbMatchIterator mi; + Header h; + + if (s->offsets) + free(s->offsets); + s->offsets = NULL; + s->noffs = 0; + mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, NULL, 0); + while ((h = rpmdbNextIterator(mi)) != NULL) { + s->noffs++; + s->offsets = realloc(s->offsets, s->noffs * sizeof(s->offsets[0])); + s->offsets[s->noffs-1] = rpmdbGetIteratorOffset(mi); + } + rpmdbFreeIterator(mi); + } + + s->offx = 0; + if (s->offsets != NULL && s->offx < s->noffs) + first = s->offsets[s->offx++]; + else + first = 0; + + if (!first) { + PyErr_SetString(pyrpmError, "cannot find first entry in database\n"); + return NULL; + } + + return Py_BuildValue("i", first); +} + +/** + */ +static PyObject * rpmdbNext(rpmdbObject * s, PyObject * args) { + int where; + + if (!PyArg_ParseTuple (args, "i", &where)) return NULL; + + if (s->offsets == NULL || s->offx >= s->noffs) { + Py_INCREF(Py_None); + return Py_None; + } + + where = s->offsets[s->offx++]; + + if (!where) { + Py_INCREF(Py_None); + return Py_None; + } + + return Py_BuildValue("i", where); +} + +/** + */ +static PyObject * handleDbResult(rpmdbMatchIterator mi) { + PyObject * list, *o; + + list = PyList_New(0); + + /* XXX FIXME: unnecessary header mallocs are side effect here */ + if (mi != NULL) { + while (rpmdbNextIterator(mi)) { + PyList_Append(list, o=PyInt_FromLong(rpmdbGetIteratorOffset(mi))); + Py_DECREF(o); + } + rpmdbFreeIterator(mi); + } + + return list; +} + +/** + */ +static PyObject * rpmdbByFile(rpmdbObject * s, PyObject * args) { + char * str; + + if (!PyArg_ParseTuple(args, "s", &str)) return NULL; + + return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_BASENAMES, str, 0)); +} + +/** + */ +static PyObject * rpmdbByName(rpmdbObject * s, PyObject * args) { + char * str; + + if (!PyArg_ParseTuple(args, "s", &str)) return NULL; + + return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_NAME, str, 0)); +} + +/** + */ +static PyObject * rpmdbByProvides(rpmdbObject * s, PyObject * args) { + char * str; + + if (!PyArg_ParseTuple(args, "s", &str)) return NULL; + + return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_PROVIDENAME, str, 0)); +} + +/** + */ +static rpmdbMIObject * +py_rpmdbInitIterator (rpmdbObject * s, PyObject * args) { + PyObject *index = NULL; + char *key = NULL; + int len = 0, tag = -1; + rpmdbMIObject * mio; + + if (!PyArg_ParseTuple(args, "|Ozi", &index, &key, &len)) + return NULL; + + if (index == NULL) + tag = 0; + else if ((tag = tagNumFromPyObject (index)) == -1) { + PyErr_SetString(PyExc_TypeError, "unknown tag type"); + return NULL; + } + + mio = (rpmdbMIObject *) PyObject_NEW(rpmdbMIObject, &rpmdbMIType); + if (mio == NULL) { + PyErr_SetString(pyrpmError, "out of memory creating rpmdbMIObject"); + return NULL; + } + + mio->mi = rpmdbInitIterator(s->db, tag, key, len); + mio->db = s; + Py_INCREF (mio->db); + + return mio; +} + +/** + */ +static struct PyMethodDef rpmdbMethods[] = { + {"firstkey", (PyCFunction) rpmdbFirst, 1 }, + {"nextkey", (PyCFunction) rpmdbNext, 1 }, + {"findbyfile", (PyCFunction) rpmdbByFile, 1 }, + {"findbyname", (PyCFunction) rpmdbByName, 1 }, + {"findbyprovides", (PyCFunction) rpmdbByProvides, 1 }, + {"match", (PyCFunction) py_rpmdbInitIterator, 1 }, + {NULL, NULL} /* sentinel */ +}; + +/** + */ +static PyObject * rpmdbGetAttr(rpmdbObject * s, char * name) { + return Py_FindMethod(rpmdbMethods, (PyObject * ) s, name); +} + +/** + */ +static void rpmdbDealloc(rpmdbObject * s) { + if (s->offsets) { + free(s->offsets); + } + if (s->db) { + rpmdbClose(s->db); + } + PyMem_DEL(s); +} + +#ifndef DYINGSOON /* XXX OK, when? */ +/** + */ +static int +rpmdbLength(rpmdbObject * s) { + int count = 0; + + { rpmdbMatchIterator mi; + + /* RPMDBI_PACKAGES */ + mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, NULL, 0); + while (rpmdbNextIterator(mi) != NULL) + count++; + rpmdbFreeIterator(mi); + } + + return count; +} + +/** + */ +static hdrObject * +rpmdbSubscript(rpmdbObject * s, PyObject * key) { + int offset; + hdrObject * ho; + Header h; + rpmdbMatchIterator mi; + + if (!PyInt_Check(key)) { + PyErr_SetString(PyExc_TypeError, "integer expected"); + return NULL; + } + + offset = (int) PyInt_AsLong(key); + + mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, &offset, sizeof(offset)); + if (!(h = rpmdbNextIterator(mi))) { + rpmdbFreeIterator(mi); + PyErr_SetString(pyrpmError, "cannot read rpmdb entry"); + return NULL; + } + + ho = createHeaderObject(h); + headerFree(h, NULL); + + return ho; +} + +/** + */ +static PyMappingMethods rpmdbAsMapping = { + (inquiry) rpmdbLength, /* mp_length */ + (binaryfunc) rpmdbSubscript, /* mp_subscript */ + (objobjargproc)0, /* mp_ass_subscript */ +}; +#endif + +/** + */ +PyTypeObject rpmdbType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "rpmdb", /* tp_name */ + sizeof(rpmdbObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) rpmdbDealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) rpmdbGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ +#ifndef DYINGSOON + &rpmdbAsMapping, /* tp_as_mapping */ +#else + 0, +#endif +}; + +rpmdb dbFromDb(rpmdbObject * db) { + return db->db; +} + +/** + */ +rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args) { + rpmdbObject * o; + char * root = ""; + int forWrite = 0; + + if (!PyArg_ParseTuple(args, "|is", &forWrite, &root)) return NULL; + + o = PyObject_NEW(rpmdbObject, &rpmdbType); + o->db = NULL; + o->offx = 0; + o->noffs = 0; + o->offsets = NULL; + + if (rpmdbOpen(root, &o->db, forWrite ? O_RDWR | O_CREAT: O_RDONLY, 0644)) { + char * errmsg = "cannot open database in %s"; + char * errstr = NULL; + int errsize; + + Py_DECREF(o); + /* PyErr_SetString should take varargs... */ + errsize = strlen(errmsg) + *root == '\0' ? 15 /* "/var/lib/rpm" */ : strlen(root); + errstr = alloca(errsize); + snprintf(errstr, errsize, errmsg, *root == '\0' ? "/var/lib/rpm" : root); + PyErr_SetString(pyrpmError, errstr); + return NULL; + } + + return o; +} + +/** + */ +PyObject * rebuildDB (PyObject * self, PyObject * args) { + char * root = ""; + + if (!PyArg_ParseTuple(args, "s", &root)) return NULL; + + return Py_BuildValue("i", rpmdbRebuild(root)); +} + +/*@}*/ + diff --git a/python/db-py.h b/python/db-py.h new file mode 100644 index 000000000..c2265ffc5 --- /dev/null +++ b/python/db-py.h @@ -0,0 +1,29 @@ +#ifndef RPMPYTHON_DB +#define RPMPYTHON_DB + +/** \ingroup python + * \file python/db-py.h + */ + +/** \ingroup python + */ +struct rpmdbObject_s { + PyObject_HEAD; + rpmdb db; + int offx; + int noffs; + int *offsets; +} ; + +/** \ingroup python + */ +typedef struct rpmdbObject_s rpmdbObject; + +extern PyTypeObject rpmdbType; +PyTypeObject rpmdbMIType; + +rpmdb dbFromDb(rpmdbObject * db); +rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args); +PyObject * rebuildDB (PyObject * self, PyObject * args); + +#endif diff --git a/python/header-py.c b/python/header-py.c new file mode 100644 index 000000000..49144b71e --- /dev/null +++ b/python/header-py.c @@ -0,0 +1,1116 @@ +/** \ingroup python + * \file python/header-py.c + */ + +#include "Python.h" +#include "rpmio_internal.h" +#include "rpmcli.h" /* XXX for rpmCheckSig */ +#include "depends.h" /* XXX for ts->rpmdb */ +#include "legacy.h" +#include "misc.h" +#include "header_internal.h" + +#include "header-py.h" + +/** \ingroup python + * \class header + * \brief A python header object represents an RPM package header. + * + * All RPM packages have headers that provide metadata for the package. + * Header objects can be returned by database queries or loaded from a + * binary package on disk. + * + * The headerFromPackage function loads the package header from a + * package on disk. It returns a tuple of a "isSource" flag and the + * header object. The "isSource" flag is set to 1 if the package + * header was read from a source rpm or to 0 if the package header was + * read from a binary rpm. + * + * For example: + * \code + * import os, rpm + * + * fd = os.open("/tmp/foo-1.0-1.i386.rpm", os.O_RDONLY) + * (header, isSource) = rpm.headerFromPackage(fd) + * fd.close() + * \endcode + * The Python interface to the header data is quite elegant. It + * presents the data in a dictionary form. We'll take the header we + * just loaded and access the data within it: + * \code + * print header[rpm.RPMTAG_NAME] + * print header[rpm.RPMTAG_VERSION] + * print header[rpm.RPMTAG_RELEASE] + * \endcode + * in the case of our "foor-1.0-1.i386.rpm" package, this code would + * output: +\verbatim + foo + 1.0 + 1 +\endverbatim + * You make also access the header data by string name: + * \code + * print header['name'] + * \endcode + * This method of access is a bit slower because the name must be + * translated into the tag number dynamically. You also must make sure + * the strings in header lookups don't get translated, or the lookups + * will fail. + */ + +/** \ingroup python + * \name Class: header + */ +/*@{*/ + +/** \ingroup python + */ +struct hdrObject_s { + PyObject_HEAD; + Header h; + char ** md5list; + char ** fileList; + char ** linkList; + int_32 * fileSizes; + int_32 * mtimes; + int_32 * uids, * gids; /* XXX these tags are not used anymore */ + unsigned short * rdevs; + unsigned short * modes; +} ; + +/*@unused@*/ static inline Header headerAllocated(Header h) { + h->flags |= HEADERFLAG_ALLOCATED; + return 0; +} + +/** \ingroup python + */ +static PyObject * hdrKeyList(hdrObject * s, PyObject * args) { + PyObject * list, *o; + HeaderIterator iter; + int tag, type; + + if (!PyArg_ParseTuple(args, "")) return NULL; + + list = PyList_New(0); + + iter = headerInitIterator(s->h); + while (headerNextIterator(iter, &tag, &type, NULL, NULL)) { + if (tag == HEADER_I18NTABLE) continue; + + switch (type) { + case RPM_BIN_TYPE: + case RPM_INT32_TYPE: + case RPM_CHAR_TYPE: + case RPM_INT8_TYPE: + case RPM_INT16_TYPE: + case RPM_STRING_ARRAY_TYPE: + case RPM_STRING_TYPE: + PyList_Append(list, o=PyInt_FromLong(tag)); + Py_DECREF(o); + } + } + + headerFreeIterator(iter); + + return list; +} + +/** \ingroup python + */ +static PyObject * hdrUnload(hdrObject * s, PyObject * args, PyObject *keywords) { + char * buf; + PyObject * rc; + int len, legacy = 0; + Header h; + static char *kwlist[] = { "legacyHeader", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywords, "|i", kwlist, &legacy)) + return NULL; + + h = headerLink(s->h, "hdrUnload h"); + /* XXX this legacy switch is a hack, needs to be removed. */ + if (legacy) { + h = headerCopy(s->h); /* XXX strip region tags, etc */ + headerFree(s->h, "hdrUnload s->h"); + } + len = headerSizeof(h, 0); + buf = headerUnload(h); + h = headerFree(h, "hdrUnload h"); + + if (buf == NULL || len == 0) { + PyErr_SetString(pyrpmError, "can't unload bad header\n"); + return NULL; + } + + rc = PyString_FromStringAndSize(buf, len); + free(buf); + + return rc; +} + +/** \ingroup python + * Returns a list of these tuples for each item that failed: + * (attr_name, correctValue, currentValue) + * It should be passed the file number to verify. + */ +static PyObject * hdrVerifyFile(hdrObject * s, PyObject * args) { + int fileNumber; + rpmVerifyAttrs verifyResult = 0; + PyObject * list, * tuple, * attrName; + int type, count; + struct stat sb; + char buf[2048]; + int i; + time_t timeInt; + struct tm * timeStruct; + + if (!PyInt_Check(args)) { + PyErr_SetString(PyExc_TypeError, "integer expected"); + return NULL; + } + + fileNumber = (int) PyInt_AsLong(args); + + { rpmTransactionSet ts; + int scareMem = 1; + TFI_t fi; + int rc; + + ts = rpmtransCreateSet(NULL, NULL); + fi = fiNew(ts, NULL, s->h, RPMTAG_BASENAMES, scareMem); + fi = tfiInit(fi, fileNumber); + if (fi != NULL && tfiNext(fi) >= 0) { + /* XXX this routine might use callbacks intelligently. */ + rc = rpmVerifyFile(ts, fi, &verifyResult, RPMVERIFY_NONE); + } else + rc = 1; + + fi = fiFree(fi, 1); + rpmtransFree(ts); + + if (rc) { + Py_INCREF(Py_None); + return Py_None; + } + } + + list = PyList_New(0); + + if (!verifyResult) return list; + + /* XXX Legacy tag needs to go away. */ + if (!s->fileList) { + headerGetEntry(s->h, RPMTAG_OLDFILENAMES, &type, (void **) &s->fileList, + &count); + } + + lstat(s->fileList[fileNumber], &sb); + + if (verifyResult & RPMVERIFY_MD5) { + if (!s->md5list) { + headerGetEntry(s->h, RPMTAG_FILEMD5S, &type, (void **) &s->md5list, + &count); + } + + if (domd5(s->fileList[fileNumber], buf, 1)) { + strcpy(buf, "00000000000000000000000000000000"); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("checksum"); + PyTuple_SetItem(tuple, 0, attrName); + PyTuple_SetItem(tuple, 1, PyString_FromString(s->md5list[fileNumber])); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + if (verifyResult & RPMVERIFY_FILESIZE) { + if (!s->fileSizes) { + headerGetEntry(s->h, RPMTAG_FILESIZES, &type, (void **) &s->fileSizes, + &count); + + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("size"); + PyTuple_SetItem(tuple, 0, attrName); + + sprintf(buf, "%d", 100); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + sprintf(buf, "%ld", (long)sb.st_size); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + if (verifyResult & RPMVERIFY_LINKTO) { + if (!s->linkList) { + headerGetEntry(s->h, RPMTAG_FILELINKTOS, &type, (void **) &s->linkList, + &count); + } + + i = readlink(s->fileList[fileNumber], buf, sizeof(buf)); + if (i <= 0) + strcpy(buf, "(unknown)"); + else + buf[i] = '\0'; + + tuple = PyTuple_New(3); + attrName = PyString_FromString("link"); + PyTuple_SetItem(tuple, 0, attrName); + PyTuple_SetItem(tuple, 1, PyString_FromString(s->linkList[fileNumber])); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + if (verifyResult & RPMVERIFY_MTIME) { + if (!s->mtimes) { + headerGetEntry(s->h, RPMTAG_FILEMTIMES, &type, (void **) &s->mtimes, + &count); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("time"); + PyTuple_SetItem(tuple, 0, attrName); + + timeInt = sb.st_mtime; + timeStruct = localtime(&timeInt); + strftime(buf, sizeof(buf) - 1, "%c", timeStruct); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + + timeInt = s->mtimes[fileNumber]; + timeStruct = localtime(&timeInt); + strftime(buf, sizeof(buf) - 1, "%c", timeStruct); + + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + if (verifyResult & RPMVERIFY_RDEV) { + if (!s->rdevs) { + headerGetEntry(s->h, RPMTAG_FILERDEVS, &type, (void **) &s->rdevs, + &count); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("device"); + + PyTuple_SetItem(tuple, 0, attrName); + sprintf(buf, "0x%-4x", s->rdevs[fileNumber]); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + sprintf(buf, "0x%-4x", (unsigned int) sb.st_rdev); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + /* + * RPMVERIFY_USER and RPM_VERIFY_GROUP are handled wrong here, but rpmlib.a + * doesn't do these correctly either. At least this is consistent. + * + * XXX Consistent? rpmlib.a verifies user/group quite well, thank you. + * XXX The code below does nothing useful. FILEUSERNAME needs to be + * XXX retrieved and looked up. + */ + if (verifyResult & RPMVERIFY_USER) { + if (!s->uids) { + headerGetEntry(s->h, RPMTAG_FILEUIDS, &type, (void **) &s->uids, + &count); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("uid"); + PyTuple_SetItem(tuple, 0, attrName); + sprintf(buf, "%d", s->uids[fileNumber]); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + sprintf(buf, "%d", sb.st_uid); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + /* + * XXX The code below does nothing useful. FILEGROUPNAME needs to be + * XXX retrieved and looked up. + */ + if (verifyResult & RPMVERIFY_GROUP) { + if (!s->gids) { + headerGetEntry(s->h, RPMTAG_FILEGIDS, &type, (void **) &s->gids, + &count); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("gid"); + PyTuple_SetItem(tuple, 0, attrName); + sprintf(buf, "%d", s->gids[fileNumber]); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + sprintf(buf, "%d", sb.st_gid); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + if (verifyResult & RPMVERIFY_MODE) { + if (!s->modes) { + headerGetEntry(s->h, RPMTAG_FILEMODES, &type, (void **) &s->modes, + &count); + } + + tuple = PyTuple_New(3); + attrName = PyString_FromString("permissions"); + PyTuple_SetItem(tuple, 0, attrName); + sprintf(buf, "0%-4o", s->modes[fileNumber]); + PyTuple_SetItem(tuple, 1, PyString_FromString(buf)); + sprintf(buf, "0%-4o", sb.st_mode); + PyTuple_SetItem(tuple, 2, PyString_FromString(buf)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + return list; +} + +/** \ingroup python + */ +static PyObject * hdrExpandFilelist(hdrObject * s, PyObject * args) { + expandFilelist (s->h); + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * hdrCompressFilelist(hdrObject * s, PyObject * args) { + compressFilelist (s->h); + + Py_INCREF(Py_None); + return Py_None; +} + +/* make a header with _all_ the tags we need */ +/** \ingroup python + */ +static void mungeFilelist(Header h) +{ + const char ** fileNames = NULL; + int count = 0; + + if (!headerIsEntry (h, RPMTAG_BASENAMES) + || !headerIsEntry (h, RPMTAG_DIRNAMES) + || !headerIsEntry (h, RPMTAG_DIRINDEXES)) + compressFilelist(h); + + rpmBuildFileList(h, &fileNames, &count); + + if (fileNames == NULL || count <= 0) + return; + + /* XXX Legacy tag needs to go away. */ + headerAddEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE, + fileNames, count); + + free((void *)fileNames); +} + +/** + */ +static PyObject * rhnUnload(hdrObject * s, PyObject * args) { + int len; + char * uh; + PyObject * rc; + Header h; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + h = headerLink(s->h, "rhnUnload h"); + + /* Retrofit a RHNPlatform: tag. */ + if (!headerIsEntry(h, RPMTAG_RHNPLATFORM)) { + const char * arch; + int_32 at; + if (headerGetEntry(h, RPMTAG_ARCH, &at, (void **)&arch, NULL)) + headerAddEntry(h, RPMTAG_RHNPLATFORM, at, arch, 1); + } + + /* Legacy headers are forced into immutable region. */ + if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) { + Header nh = headerReload(h, RPMTAG_HEADERIMMUTABLE); + /* XXX Another unload/load cycle to "seal" the immutable region. */ + uh = headerUnload(nh); + headerFree(nh, "rhnUnload nh"); + h = headerLoad(uh); + headerAllocated(h); + } + + /* All headers have SHA1 digest, compute and add if necessary. */ + if (!headerIsEntry(h, RPMTAG_SHA1HEADER)) { + int_32 uht, uhc; + const char * digest; + size_t digestlen; + DIGEST_CTX ctx; + + headerGetEntry(h, RPMTAG_HEADERIMMUTABLE, &uht, (void **)&uh, &uhc); + + ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE); + rpmDigestUpdate(ctx, uh, uhc); + rpmDigestFinal(ctx, (void **)&digest, &digestlen, 1); + + headerAddEntry(h, RPMTAG_SHA1RHN, RPM_STRING_TYPE, digest, 1); + + uh = headerFreeData(uh, uht); + digest = _free(digest); + } + + len = headerSizeof(h, 0); + uh = headerUnload(h); + headerFree(h, "rhnUnload h"); + + rc = PyString_FromStringAndSize(uh, len); + free(uh); + + return rc; +} + +/** \ingroup python + */ +static PyObject * hdrFullFilelist(hdrObject * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "")) + return NULL; + + mungeFilelist (s->h); + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * hdrSprintf(hdrObject * s, PyObject * args) { + char * fmt; + char * r; + errmsg_t err; + PyObject * result; + + if (!PyArg_ParseTuple(args, "s", &fmt)) + return NULL; + + r = headerSprintf(s->h, fmt, rpmTagTable, rpmHeaderFormats, &err); + if (!r) { + PyErr_SetString(pyrpmError, err); + return NULL; + } + + result = Py_BuildValue("s", r); + free(r); + + return result; +} + + +/** \ingroup python + */ +static struct PyMethodDef hdrMethods[] = { + {"keys", (PyCFunction) hdrKeyList, 1 }, + {"unload", (PyCFunction) hdrUnload, METH_VARARGS|METH_KEYWORDS }, + {"verifyFile", (PyCFunction) hdrVerifyFile, 1 }, + {"expandFilelist", (PyCFunction) hdrExpandFilelist, 1 }, + {"compressFilelist", (PyCFunction) hdrCompressFilelist, 1 }, + {"fullFilelist", (PyCFunction) hdrFullFilelist, 1 }, + {"rhnUnload", (PyCFunction) rhnUnload, METH_VARARGS }, + {"sprintf", (PyCFunction) hdrSprintf, METH_VARARGS }, + {NULL, NULL} /* sentinel */ +}; + +/** \ingroup python + */ +static PyObject * hdrGetAttr(hdrObject * s, char * name) { + return Py_FindMethod(hdrMethods, (PyObject * ) s, name); +} + +/** \ingroup python + */ +static void hdrDealloc(hdrObject * s) { + if (s->h) headerFree(s->h, "hdrDealloc s->h"); + if (s->md5list) free(s->md5list); + if (s->fileList) free(s->fileList); + if (s->linkList) free(s->linkList); + PyMem_DEL(s); +} + +/** \ingroup python + */ +long tagNumFromPyObject (PyObject *item) +{ + char * str; + int i; + + if (PyInt_Check(item)) { + return PyInt_AsLong(item); + } else if (PyString_Check(item)) { + str = PyString_AsString(item); + for (i = 0; i < rpmTagTableSize; i++) + if (!xstrcasecmp(rpmTagTable[i].name + 7, str)) break; + if (i < rpmTagTableSize) return rpmTagTable[i].val; + } + return -1; +} + +/** \ingroup python + */ +static PyObject * hdrSubscript(hdrObject * s, PyObject * item) { + int type, count, i, tag = -1; + void * data; + PyObject * o, * metao; + char ** stringArray; + int forceArray = 0; + int freeData = 0; + char * str; + struct headerSprintfExtension_s * ext = NULL; + const struct headerSprintfExtension_s * extensions = rpmHeaderFormats; + + if (PyCObject_Check (item)) + ext = PyCObject_AsVoidPtr(item); + else + tag = tagNumFromPyObject (item); + if (tag == -1 && PyString_Check(item)) { + /* if we still don't have the tag, go looking for the header + extensions */ + str = PyString_AsString(item); + while (extensions->name) { + if (extensions->type == HEADER_EXT_TAG + && !xstrcasecmp(extensions->name + 7, str)) { + (const struct headerSprintfExtension *) ext = extensions; + } + extensions++; + } + } + + if (ext) { + ext->u.tagFunction(s->h, &type, (const void **) &data, &count, &freeData); + } else { + if (tag == -1) { + PyErr_SetString(PyExc_KeyError, "unknown header tag"); + return NULL; + } + + if (!rpmHeaderGetEntry(s->h, tag, &type, &data, &count)) { + Py_INCREF(Py_None); + return Py_None; + } + } + + switch (tag) { + case RPMTAG_OLDFILENAMES: + case RPMTAG_FILESIZES: + case RPMTAG_FILESTATES: + case RPMTAG_FILEMODES: + case RPMTAG_FILEUIDS: + case RPMTAG_FILEGIDS: + case RPMTAG_FILERDEVS: + case RPMTAG_FILEMTIMES: + case RPMTAG_FILEMD5S: + case RPMTAG_FILELINKTOS: + case RPMTAG_FILEFLAGS: + case RPMTAG_ROOT: + case RPMTAG_FILEUSERNAME: + case RPMTAG_FILEGROUPNAME: + forceArray = 1; + break; + case RPMTAG_SUMMARY: + case RPMTAG_GROUP: + case RPMTAG_DESCRIPTION: + freeData = 1; + break; + default: + break; + } + + switch (type) { + case RPM_BIN_TYPE: + o = PyString_FromStringAndSize(data, count); + break; + + case RPM_INT32_TYPE: + if (count != 1 || forceArray) { + metao = PyList_New(0); + for (i = 0; i < count; i++) { + o = PyInt_FromLong(((int *) data)[i]); + PyList_Append(metao, o); + Py_DECREF(o); + } + o = metao; + } else { + o = PyInt_FromLong(*((int *) data)); + } + break; + + case RPM_CHAR_TYPE: + case RPM_INT8_TYPE: + if (count != 1 || forceArray) { + metao = PyList_New(0); + for (i = 0; i < count; i++) { + o = PyInt_FromLong(((char *) data)[i]); + PyList_Append(metao, o); + Py_DECREF(o); + } + o = metao; + } else { + o = PyInt_FromLong(*((char *) data)); + } + break; + + case RPM_INT16_TYPE: + if (count != 1 || forceArray) { + metao = PyList_New(0); + for (i = 0; i < count; i++) { + o = PyInt_FromLong(((short *) data)[i]); + PyList_Append(metao, o); + Py_DECREF(o); + } + o = metao; + } else { + o = PyInt_FromLong(*((short *) data)); + } + break; + + case RPM_STRING_ARRAY_TYPE: + stringArray = data; + + metao = PyList_New(0); + for (i = 0; i < count; i++) { + o = PyString_FromString(stringArray[i]); + PyList_Append(metao, o); + Py_DECREF(o); + } + free (stringArray); + o = metao; + break; + + case RPM_STRING_TYPE: + if (count != 1 || forceArray) { + stringArray = data; + + metao = PyList_New(0); + for (i=0; i < count; i++) { + o = PyString_FromString(stringArray[i]); + PyList_Append(metao, o); + Py_DECREF(o); + } + o = metao; + } else { + o = PyString_FromString(data); + if (freeData) + free (data); + } + break; + + default: + PyErr_SetString(PyExc_TypeError, "unsupported type in header"); + return NULL; + } + + return o; +} + +/** \ingroup python + */ +static PyMappingMethods hdrAsMapping = { + (inquiry) 0, /* mp_length */ + (binaryfunc) hdrSubscript, /* mp_subscript */ + (objobjargproc)0, /* mp_ass_subscript */ +}; + +/** \ingroup python + */ +PyTypeObject hdrType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "header", /* tp_name */ + sizeof(hdrObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) hdrDealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) hdrGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + &hdrAsMapping, /* tp_as_mapping */ +}; + +hdrObject * createHeaderObject(Header h) { + hdrObject * ho; + + ho = PyObject_NEW(hdrObject, &hdrType); + ho->h = headerLink(h, NULL); + ho->fileList = ho->linkList = ho->md5list = NULL; + ho->uids = ho->gids = ho->mtimes = ho->fileSizes = NULL; + ho->modes = ho->rdevs = NULL; + + return ho; +} + +Header hdrGetHeader(hdrObject * h) { + return h->h; +} + +/** + */ +PyObject * rpmHeaderFromPackage(PyObject * self, PyObject * args) { + hdrObject * h; + Header header; + FD_t fd; + int rawFd; + int isSource = 0; + rpmRC rc; + + if (!PyArg_ParseTuple(args, "i", &rawFd)) return NULL; + + fd = fdDup(rawFd); + { rpmTransactionSet ts; + ts = rpmtransCreateSet(NULL, NULL); + rc = rpmReadPackageFile(ts, fd, "rpmHeaderFromPackage", &header); + rpmtransFree(ts); + } + Fclose(fd); + + switch (rc) { + case RPMRC_BADSIZE: + case RPMRC_OK: + h = (hdrObject *) PyObject_NEW(PyObject, &hdrType); + h->h = header; + h->fileList = h->linkList = h->md5list = NULL; + h->uids = h->gids = h->mtimes = h->fileSizes = NULL; + h->modes = h->rdevs = NULL; + isSource = headerIsEntry(header, RPMTAG_SOURCEPACKAGE); + break; + + case RPMRC_BADMAGIC: + Py_INCREF(Py_None); + h = (hdrObject *) Py_None; + break; + + case RPMRC_FAIL: + case RPMRC_SHORTREAD: + default: + PyErr_SetString(pyrpmError, "error reading package"); + return NULL; + } + + return Py_BuildValue("(Ni)", h, isSource); +} + +/** + */ +PyObject * hdrLoad(PyObject * self, PyObject * args) { + char * obj, * copy=NULL; + Header hdr; + hdrObject * h; + int len; + + if (!PyArg_ParseTuple(args, "s#", &obj, &len)) return NULL; + + /* malloc is needed to avoid surprises from data swab in headerLoad(). */ + copy = malloc(len); + if (copy == NULL) { + PyErr_SetString(pyrpmError, "out of memory"); + return NULL; + } + memcpy (copy, obj, len); + + hdr = headerLoad(copy); + if (!hdr) { + PyErr_SetString(pyrpmError, "bad header"); + return NULL; + } + headerAllocated(hdr); + compressFilelist (hdr); + providePackageNVR (hdr); + + h = (hdrObject *) PyObject_NEW(PyObject, &hdrType); + h->h = hdr; + h->fileList = h->linkList = h->md5list = NULL; + h->uids = h->gids = h->mtimes = h->fileSizes = NULL; + h->modes = h->rdevs = NULL; + + return (PyObject *) h; +} + +/** + */ +PyObject * rhnLoad(PyObject * self, PyObject * args) { + char * obj, * copy=NULL; + Header hdr; + hdrObject * h; + int len; + + if (!PyArg_ParseTuple(args, "s#", &obj, &len)) return NULL; + + /* malloc is needed to avoid surprises from data swab in headerLoad(). */ + copy = malloc(len); + if (copy == NULL) { + PyErr_SetString(pyrpmError, "out of memory"); + return NULL; + } + memcpy (copy, obj, len); + + hdr = headerLoad(copy); + if (!hdr) { + PyErr_SetString(pyrpmError, "bad header"); + return NULL; + } + headerAllocated(hdr); + + /* XXX avoid the false OK's from rpmverifyDigest() with missing tags. */ + if (!headerIsEntry(hdr, RPMTAG_HEADERIMMUTABLE)) { + PyErr_SetString(pyrpmError, "bad header, not immutable"); + headerFree(hdr, "rhnLoad hdr #1"); + return NULL; + } + + /* XXX avoid the false OK's from rpmverifyDigest() with missing tags. */ + if (!headerIsEntry(hdr, RPMTAG_SHA1HEADER) + && !headerIsEntry(hdr, RPMTAG_SHA1RHN)) { + PyErr_SetString(pyrpmError, "bad header, no digest"); + headerFree(hdr, "rhnLoad hdr #2"); + return NULL; + } + + if (rpmVerifyDigest(hdr)) { + PyErr_SetString(pyrpmError, "bad header, digest check failed"); + headerFree(hdr, "rhnLoad hdr #3"); + return NULL; + } + + /* Retrofit a RHNPlatform: tag. */ + if (!headerIsEntry(hdr, RPMTAG_RHNPLATFORM)) { + const char * arch; + int_32 at; + if (headerGetEntry(hdr, RPMTAG_ARCH, &at, (void **)&arch, NULL)) + headerAddEntry(hdr, RPMTAG_RHNPLATFORM, at, arch, 1); + } + + h = createHeaderObject(hdr); + + return (PyObject *) h; +} + +/** + */ +PyObject * rpmReadHeaders (FD_t fd) { + PyObject * list; + Header header; + hdrObject * h; + + if (!fd) { + PyErr_SetFromErrno(pyrpmError); + return NULL; + } + + list = PyList_New(0); + Py_BEGIN_ALLOW_THREADS + header = headerRead(fd, HEADER_MAGIC_YES); + + Py_END_ALLOW_THREADS + while (header) { + compressFilelist (header); + providePackageNVR (header); + h = (hdrObject *) PyObject_NEW(PyObject, &hdrType); + h->h = header; + h->fileList = h->linkList = h->md5list = NULL; + h->uids = h->gids = h->mtimes = h->fileSizes = NULL; + h->modes = h->rdevs = NULL; + if (PyList_Append(list, (PyObject *) h)) { + Py_DECREF(list); + Py_DECREF(h); + return NULL; + } + + Py_DECREF(h); + + Py_BEGIN_ALLOW_THREADS + header = headerRead(fd, HEADER_MAGIC_YES); + Py_END_ALLOW_THREADS + } + + return list; +} + +/** + */ +PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args) { + FD_t fd; + int fileno; + PyObject * list; + + if (!PyArg_ParseTuple(args, "i", &fileno)) return NULL; + fd = fdDup(fileno); + + list = rpmReadHeaders (fd); + Fclose(fd); + + return list; +} + +/** + */ +PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args) { + char * filespec; + FD_t fd; + PyObject * list; + + if (!PyArg_ParseTuple(args, "s", &filespec)) return NULL; + fd = Fopen(filespec, "r.fdio"); + + if (!fd) { + PyErr_SetFromErrno(pyrpmError); + return NULL; + } + + list = rpmReadHeaders (fd); + Fclose(fd); + + return list; +} + +/** + * This assumes the order of list matches the order of the new headers, and + * throws an exception if that isn't true. + */ +int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag) { + Header newH; + HeaderIterator iter; + int_32 * newMatch, * oldMatch; + hdrObject * ho; + int count = 0; + int type, c, tag; + void * p; + + Py_BEGIN_ALLOW_THREADS + newH = headerRead(fd, HEADER_MAGIC_YES); + + Py_END_ALLOW_THREADS + while (newH) { + if (!headerGetEntry(newH, matchTag, NULL, (void **) &newMatch, NULL)) { + PyErr_SetString(pyrpmError, "match tag missing in new header"); + return 1; + } + + ho = (hdrObject *) PyList_GetItem(list, count++); + if (!ho) return 1; + + if (!headerGetEntry(ho->h, matchTag, NULL, (void **) &oldMatch, NULL)) { + PyErr_SetString(pyrpmError, "match tag missing in new header"); + return 1; + } + + if (*newMatch != *oldMatch) { + PyErr_SetString(pyrpmError, "match tag mismatch"); + return 1; + } + + if (ho->md5list) free(ho->md5list); + if (ho->fileList) free(ho->fileList); + if (ho->linkList) free(ho->linkList); + + ho->md5list = NULL; + ho->fileList = NULL; + ho->linkList = NULL; + + iter = headerInitIterator(newH); + + while (headerNextIterator(iter, &tag, &type, (void *) &p, &c)) { + /* could be dupes */ + headerRemoveEntry(ho->h, tag); + headerAddEntry(ho->h, tag, type, p, c); + headerFreeData(p, type); + } + + headerFreeIterator(iter); + + Py_BEGIN_ALLOW_THREADS + newH = headerRead(fd, HEADER_MAGIC_YES); + Py_END_ALLOW_THREADS + } + + return 0; +} + +PyObject * rpmMergeHeadersFromFD(PyObject * self, PyObject * args) { + FD_t fd; + int fileno; + PyObject * list; + int rc; + int matchTag; + + if (!PyArg_ParseTuple(args, "Oii", &list, &fileno, &matchTag)) return NULL; + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, "first parameter must be a list"); + return NULL; + } + + fd = fdDup(fileno); + + rc = rpmMergeHeaders (list, fd, matchTag); + Fclose(fd); + + if (rc) { + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +/** + */ +PyObject * versionCompare (PyObject * self, PyObject * args) { + hdrObject * h1, * h2; + + if (!PyArg_ParseTuple(args, "O!O!", &hdrType, &h1, &hdrType, &h2)) return NULL; + + return Py_BuildValue("i", rpmVersionCompare(h1->h, h2->h)); +} + +/** + */ +PyObject * labelCompare (PyObject * self, PyObject * args) { + char *v1, *r1, *e1, *v2, *r2, *e2; + int rc; + + if (!PyArg_ParseTuple(args, "(zzz)(zzz)", + &e1, &v1, &r1, + &e2, &v2, &r2)) return NULL; + + if (e1 && !e2) + return Py_BuildValue("i", 1); + else if (!e1 && e2) + return Py_BuildValue("i", -1); + else if (e1 && e2) { + int ep1, ep2; + ep1 = atoi (e1); + ep2 = atoi (e2); + if (ep1 < ep2) + return Py_BuildValue("i", -1); + else if (ep1 > ep2) + return Py_BuildValue("i", 1); + } + + rc = rpmvercmp(v1, v2); + if (rc) + return Py_BuildValue("i", rc); + + return Py_BuildValue("i", rpmvercmp(r1, r2)); +} + +/*@}*/ diff --git a/python/header-py.h b/python/header-py.h new file mode 100644 index 000000000..c5d3d4ab7 --- /dev/null +++ b/python/header-py.h @@ -0,0 +1,31 @@ +#ifndef RPMPYTHON_HEADER +#define RPMPYTHON_HEADER + +/** \ingroup python + * \file python/header-py.h + */ + +/** \ingroup python + */ +typedef struct hdrObject_s hdrObject; +extern PyTypeObject hdrType; + +/** \ingroup python + */ +PyObject * pyrpmError; + +hdrObject * createHeaderObject(Header h); +Header hdrGetHeader(hdrObject * h); +PyObject * labelCompare (PyObject * self, PyObject * args); +PyObject * versionCompare (PyObject * self, PyObject * args); +PyObject * rpmMergeHeadersFromFD(PyObject * self, PyObject * args); +int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag); +PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args); +PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args); +PyObject * rpmReadHeaders (FD_t fd); +PyObject * rhnLoad(PyObject * self, PyObject * args); +PyObject * hdrLoad(PyObject * self, PyObject * args); +PyObject * rpmHeaderFromPackage(PyObject * self, PyObject * args); +long tagNumFromPyObject (PyObject *item); + +#endif diff --git a/python/rpmmodule.c b/python/rpmmodule.c new file mode 100644 index 000000000..c0c1f5fec --- /dev/null +++ b/python/rpmmodule.c @@ -0,0 +1,1170 @@ +/** \ingroup python + * \file python/rpmmodule.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* XXX rpmio.h */ +#include /* XXX rpmio.h */ +#include +#include + +#include "Python.h" +#include "rpmio_internal.h" +#include "rpmcli.h" /* XXX for rpmCheckSig */ +#include "depends.h" /* XXX for ts->rpmdb */ +#include "legacy.h" +#include "misc.h" +#include "header_internal.h" +#include "upgrade.h" + +#include "db-py.h" +#include "header-py.h" + +extern int _rpmio_debug; + +/* XXX These names/constants have been removed from the rpmlib API. */ +enum { + RPMDEP_SENSE_REQUIRES, /*!< requirement not satisfied. */ + RPMDEP_SENSE_CONFLICTS /*!< conflict was found. */ +}; + +#ifdef __LCLINT__ +#undef PyObject_HEAD +#define PyObject_HEAD int _PyObjectHead +#endif + +void initrpm(void); + +/* from lib/misc.c */ +int rpmvercmp(const char * one, const char * two); + +/** \ingroup python + */ +typedef struct rpmtransObject_s rpmtransObject; + +/** \ingroup python + * \name Class: rpmtrans + * \class rpmtrans + * \brief A python rpmtrans object represents an RPM transaction set. + * + * The transaction set is the workhorse of RPM. It performs the + * installation and upgrade of packages. The rpmtrans object is + * instantiated by the TransactionSet function in the rpm module. + * + * The TransactionSet function takes two optional arguments. The first + * argument is the root path, the second is an open database to perform + * the transaction set upon. + * + * A rpmtrans object has the following methods: + * + * - add(header,data,mode) Add a binary package to a transaction set. + * @param header the header to be added + * @param data user data that will be passed to the transaction callback + * during transaction execution + * @param mode optional argument that specifies if this package should + * be installed ('i'), upgraded ('u'), or if it is just + * available to the transaction when computing + * dependencies but no action should be performed with it + * ('a'). + * + * - remove + * + * - depcheck() Perform a dependency and conflict check on the + * transaction set. After headers have been added to a + * transaction set, a dependency check can be performed + * to make sure that all package dependencies are + * satisfied. + * @return None If there are no unresolved dependencies + * Otherwise a list of complex tuples is returned, one tuple per + * unresolved dependency, with + * The format of the dependency tuple is: + * ((packageName, packageVersion, packageRelease), + * (reqName, reqVersion), + * needsFlags, + * suggestedPackage, + * sense) + * packageName, packageVersion, packageRelease are the name, + * version, and release of the package that has the unresolved + * dependency or conflict. + * The reqName and reqVersion are the name and version of the + * requirement or conflict. + * The needsFlags is a bitfield that describes the versioned + * nature of a requirement or conflict. The constants + * rpm.RPMSENSE_LESS, rpm.RPMSENSE_GREATER, and + * rpm.RPMSENSE_EQUAL can be logical ANDed with the needsFlags + * to get versioned dependency information. + * suggestedPackage is a tuple if the dependency check was aware + * of a package that solves this dependency problem when the + * dependency check was run. Packages that are added to the + * transaction set as "available" are examined during the + * dependency check as possible dependency solvers. The tuple + * contains two values, (header, suggestedName). These are set to + * the header of the suggested package and its name, respectively. + * If there is no known package to solve the dependency problem, + * suggestedPackage is None. + * The constants rpm.RPMDEP_SENSE_CONFLICTS and + * rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a + * requirement or a conflict. + * + * - run(flags,problemSetFilter,callback,data) Attempt to execute a + * transaction set. After the transaction set has been populated + * with install and upgrade actions, it can be executed by invoking + * the run() method. + * @param flags - modifies the behavior of the transaction set as it is + * processed. The following values can be locical ORed + * together: + * - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM + * database, change any files, or run any package scripts + * - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of + * problems encountered when attempting to run this transaction + * set + * - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts + * - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm + * database, do not modify files. + * - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts + * - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc + * - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a + * file is marked %config(missingok) and an upgrade is + * being performed. + * - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted + * packages. + * @param problemSetFilter - control bit(s) to ignore classes of problems, + * any of + * - rpm.RPMPROB_FILTER_IGNOREOS - + * - rpm.RPMPROB_FILTER_IGNOREARCH - + * - rpm.RPMPROB_FILTER_REPLACEPKG - + * - rpm.RPMPROB_FILTER_FORCERELOCATE - + * - rpm.RPMPROB_FILTER_REPLACENEWFILES - + * - rpm.RPMPROB_FILTER_REPLACEOLDFILES - + * - rpm.RPMPROB_FILTER_OLDPACKAGE - + * - rpm.RPMPROB_FILTER_DISKSPACE - + */ + +/** \ingroup python + * \name Class: rpmtrans + */ +/*@{*/ + +/** \ingroup python + */ +struct rpmtransObject_s { + PyObject_HEAD; + rpmdbObject * dbo; + rpmTransactionSet ts; + PyObject * keyList; /* keeps reference counts correct */ + FD_t scriptFd; +} ; + +/** \ingroup python + */ +static PyObject * rpmtransAdd(rpmtransObject * s, PyObject * args) { + hdrObject * h; + PyObject * key; + char * how = NULL; + int isUpgrade = 0; + PyObject * hObj; + + if (!PyArg_ParseTuple(args, "OO|s", &h, &key, &how)) return NULL; + + hObj = (PyObject *) h; + if (hObj->ob_type != &hdrType) { + PyErr_SetString(PyExc_TypeError, "bad type for header argument"); + return NULL; + } + + if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) { + PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\""); + return NULL; + } else if (how && !strcmp(how, "u")) + isUpgrade = 1; + + if (how && !strcmp(how, "a")) + rpmtransAvailablePackage(s->ts, hdrGetHeader(h), key); + else + rpmtransAddPackage(s->ts, hdrGetHeader(h), key, isUpgrade, NULL); + + /* This should increment the usage count for me */ + if (key) { + PyList_Append(s->keyList, key); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * rpmtransRemove(rpmtransObject * s, PyObject * args) { + char * name; + int count; + rpmdbMatchIterator mi; + + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + + /* XXX: Copied hack from ../lib/rpminstall.c, rpmErase() */ + mi = rpmdbInitIterator(dbFromDb(s->dbo), RPMDBI_LABEL, name, 0); + count = rpmdbGetIteratorCount(mi); + if (count <= 0) { + PyErr_SetString(pyrpmError, "package not installed"); + return NULL; + } else { /* XXX: Note that we automatically choose to remove all matches */ + Header h; + while ((h = rpmdbNextIterator(mi)) != NULL) { + unsigned int recOffset = rpmdbGetIteratorOffset(mi); + if (recOffset) { + rpmtransRemovePackage(s->ts, h, recOffset); + } + } + } + rpmdbFreeIterator(mi); + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * rpmtransDepCheck(rpmtransObject * s, PyObject * args) { + rpmProblem conflicts, c; + int numConflicts; + PyObject * list, * cf; + int i; + int allSuggestions = 0; + + if (!PyArg_ParseTuple(args, "|i", &allSuggestions)) return NULL; + + rpmdepCheck(s->ts, &conflicts, &numConflicts); + if (numConflicts) { + list = PyList_New(0); + + /* XXX TODO: rpmlib >= 4.0.3 can return multiple suggested keys. */ + for (i = 0; i < numConflicts; i++) { +#ifdef DYING + cf = Py_BuildValue("((sss)(ss)iOi)", conflicts[i].byName, + conflicts[i].byVersion, conflicts[i].byRelease, + + conflicts[i].needsName, + conflicts[i].needsVersion, + + conflicts[i].needsFlags, + conflicts[i].suggestedPkgs ? + conflicts[i].suggestedPkgs[0] : Py_None, + conflicts[i].sense); +#else + char * byName, * byVersion, * byRelease; + char * needsName, * needsOP, * needsVersion; + int needsFlags, sense; + fnpyKey key; + + c = conflicts + i; + + byName = c->pkgNEVR; + if ((byRelease = strrchr(byName, '-')) != NULL) + *byRelease++ = '\0'; + if ((byVersion = strrchr(byName, '-')) != NULL) + *byVersion++ = '\0'; + + key = c->key; + + needsName = c->altNEVR; + if (needsName[1] == ' ') { + sense = (needsName[0] == 'C') + ? RPMDEP_SENSE_CONFLICTS : RPMDEP_SENSE_REQUIRES; + needsName += 2; + } else + sense = RPMDEP_SENSE_REQUIRES; + if ((needsVersion = strrchr(needsName, ' ')) != NULL) + *needsVersion++ = '\0'; + + needsFlags = 0; + if ((needsOP = strrchr(needsName, ' ')) != NULL) { + for (*needsOP++ = '\0'; *needsOP != '\0'; needsOP++) { + if (*needsOP == '<') needsFlags |= RPMSENSE_LESS; + else if (*needsOP == '>') needsFlags |= RPMSENSE_GREATER; + else if (*needsOP == '=') needsFlags |= RPMSENSE_EQUAL; + } + } + + cf = Py_BuildValue("((sss)(ss)iOi)", byName, byVersion, byRelease, + needsName, needsVersion, needsFlags, + (key != NULL ? key : Py_None), + sense); +#endif + PyList_Append(list, (PyObject *) cf); + Py_DECREF(cf); + } + + conflicts = rpmdepFreeConflicts(conflicts, numConflicts); + + return list; + } + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * rpmtransOrder(rpmtransObject * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "")) return NULL; + + rpmdepOrder(s->ts); + + Py_INCREF(Py_None); + return Py_None; +} + +/** \ingroup python + */ +static PyObject * py_rpmtransGetKeys(rpmtransObject * s, PyObject * args) { + const void **data = NULL; + int num, i; + PyObject *tuple; + + rpmtransGetKeys(s->ts, &data, &num); + if (data == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + tuple = PyTuple_New(num); + + for (i = 0; i < num; i++) { + PyObject *obj = (PyObject *) data[i]; + Py_INCREF(obj); + PyTuple_SetItem(tuple, i, obj); + } + + free (data); + + return tuple; +} + +/** \ingroup python + */ +struct tsCallbackType { + PyObject * cb; + PyObject * data; + int pythonError; +}; + +/** \ingroup python + * @todo Remove, there's no headerLink refcount on the pointer. + */ +static Header transactionSetHeader = NULL; + +/** \ingroup python + */ +static void * tsCallback(const void * hd, const rpmCallbackType what, + const unsigned long amount, const unsigned long total, + const void * pkgKey, rpmCallbackData data) { + struct tsCallbackType * cbInfo = data; + PyObject * args, * result; + int fd; + static FD_t fdt; + const Header h = (Header) hd; + + if (cbInfo->pythonError) return NULL; + if (cbInfo->cb == Py_None) return NULL; + + if (!pkgKey) pkgKey = Py_None; + transactionSetHeader = h; + + args = Py_BuildValue("(illOO)", what, amount, total, pkgKey, cbInfo->data); + result = PyEval_CallObject(cbInfo->cb, args); + Py_DECREF(args); + + if (!result) { + cbInfo->pythonError = 1; + return NULL; + } + + if (what == RPMCALLBACK_INST_OPEN_FILE) { + if (!PyArg_Parse(result, "i", &fd)) { + cbInfo->pythonError = 1; + return NULL; + } + fdt = fdDup(fd); + + Py_DECREF(result); + return fdt; + } + + if (what == RPMCALLBACK_INST_CLOSE_FILE) { + Fclose (fdt); + } + + Py_DECREF(result); + + return NULL; +} + +/** \ingroup python + */ +static PyObject * rpmtransRun(rpmtransObject * s, PyObject * args) { + int flags, ignoreSet; + int rc, i; + PyObject * list, * prob; + rpmProblemSet probs; + struct tsCallbackType cbInfo; + + if (!PyArg_ParseTuple(args, "iiOO", &flags, &ignoreSet, &cbInfo.cb, + &cbInfo.data)) + return NULL; + + cbInfo.pythonError = 0; + + (void) rpmtsSetNotifyCallback(s->ts, tsCallback, (void *) &cbInfo); + (void) rpmtsSetFlags(s->ts, flags); + + rc = rpmRunTransactions(s->ts, NULL, &probs, ignoreSet); + + if (cbInfo.pythonError) { + if (rc > 0) + rpmProblemSetFree(probs); + return NULL; + } + + if (rc < 0) { + list = PyList_New(0); + return list; + } else if (!rc) { + Py_INCREF(Py_None); + return Py_None; + } + + list = PyList_New(0); + for (i = 0; i < probs->numProblems; i++) { + rpmProblem myprob = probs->probs + i; + prob = Py_BuildValue("s(isN)", rpmProblemString(myprob), + myprob->type, + myprob->str1, + PyLong_FromLongLong(myprob->ulong1)); + PyList_Append(list, prob); + Py_DECREF(prob); + } + + rpmProblemSetFree(probs); + + return list; +} + +/** \ingroup python + */ +static struct PyMethodDef rpmtransMethods[] = { + {"add", (PyCFunction) rpmtransAdd, 1 }, + {"remove", (PyCFunction) rpmtransRemove, 1 }, + {"depcheck", (PyCFunction) rpmtransDepCheck, 1 }, + {"order", (PyCFunction) rpmtransOrder, 1 }, + {"getKeys", (PyCFunction) py_rpmtransGetKeys, 1 }, + {"run", (PyCFunction) rpmtransRun, 1 }, + {NULL, NULL} /* sentinel */ +}; + +/** \ingroup python + */ +static PyObject * rpmtransGetAttr(rpmtransObject * o, char * name) { + return Py_FindMethod(rpmtransMethods, (PyObject *) o, name); +} + +/** \ingroup python + */ +static void rpmtransDealloc(PyObject * o) { + rpmtransObject * trans = (void *) o; + + trans->ts->rpmdb = NULL; /* XXX HACK: avoid rpmdb close/free */ + rpmtransFree(trans->ts); + if (trans->dbo) { + Py_DECREF(trans->dbo); + } + if (trans->scriptFd) Fclose(trans->scriptFd); + /* this will free the keyList, and decrement the ref count of all + the items on the list as well :-) */ + Py_DECREF(trans->keyList); + PyMem_DEL(o); +} + +/** \ingroup python + */ +static int rpmtransSetAttr(rpmtransObject * o, char * name, + PyObject * val) { + int i; + + if (!strcmp(name, "scriptFd")) { + if (!PyArg_Parse(val, "i", &i)) return 0; + if (i < 0) { + PyErr_SetString(PyExc_TypeError, "bad file descriptor"); + return -1; + } else { + o->scriptFd = fdDup(i); + rpmtransSetScriptFd(o->ts, o->scriptFd); + } + } else { + PyErr_SetString(PyExc_AttributeError, name); + return -1; + } + + return 0; +} + +/** \ingroup python + */ +static PyTypeObject rpmtransType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "rpmtrans", /* tp_name */ + sizeof(rpmtransObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) rpmtransDealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) rpmtransGetAttr, /* tp_getattr */ + (setattrfunc) rpmtransSetAttr, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ +}; + +/*@}*/ + +/** \ingroup python + * \name Module: rpm + */ +/*@{*/ + +/** + */ +static PyObject * rpmtransCreate(PyObject * self, PyObject * args) { + rpmtransObject * o; + rpmdbObject * db = NULL; + char * rootPath = "/"; + + if (!PyArg_ParseTuple(args, "|sO", &rootPath, &db)) return NULL; + if (db && ((PyObject *) db)->ob_type != &rpmdbType) { + PyErr_SetString(PyExc_TypeError, "bad type for database argument"); + return NULL; + } + + o = (void *) PyObject_NEW(rpmtransObject, &rpmtransType); + + Py_XINCREF(db); + o->dbo = db; + o->scriptFd = NULL; + o->ts = rpmtransCreateSet(NULL, rootPath); + o->ts->rpmdb = (db ? dbFromDb(db) : NULL); + o->keyList = PyList_New(0); + + return (void *) o; +} + +/** + */ +static PyObject * doAddMacro(PyObject * self, PyObject * args) { + char * name, * val; + + if (!PyArg_ParseTuple(args, "ss", &name, &val)) + return NULL; + + addMacro(NULL, name, NULL, val, RMIL_DEFAULT); + + Py_INCREF(Py_None); + return Py_None; +} + +/** + */ +static PyObject * doDelMacro(PyObject * self, PyObject * args) { + char * name; + + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + + delMacro(NULL, name); + + Py_INCREF(Py_None); + return Py_None; +} + +/** + */ +static PyObject * archScore(PyObject * self, PyObject * args) { + char * arch; + int score; + + if (!PyArg_ParseTuple(args, "s", &arch)) + return NULL; + + score = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch); + + return Py_BuildValue("i", score); +} + +/** + */ +static int psGetArchScore(Header h) { + void * pkgArch; + int type, count; + + if (!headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count) || + type == RPM_INT8_TYPE) + return 150; + else + return rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch); +} + +/** + */ +static int pkgCompareVer(void * first, void * second) { + struct packageInfo ** a = first; + struct packageInfo ** b = second; + int ret, score1, score2; + + /* put packages w/o names at the end */ + if (!(*a)->name) return 1; + if (!(*b)->name) return -1; + + ret = xstrcasecmp((*a)->name, (*b)->name); + if (ret) return ret; + score1 = psGetArchScore((*a)->h); + if (!score1) return 1; + score2 = psGetArchScore((*b)->h); + if (!score2) return -1; + if (score1 < score2) return -1; + if (score1 > score2) return 1; + return rpmVersionCompare((*b)->h, (*a)->h); +} + +/** + */ +static void pkgSort(struct pkgSet * psp) { + int i; + char *name; + + if (psp->numPackages <= 0) + return; + + qsort(psp->packages, psp->numPackages, sizeof(*psp->packages), + (void *) pkgCompareVer); + + name = psp->packages[0]->name; + if (!name) { + psp->numPackages = 0; + return; + } + for (i = 1; i < psp->numPackages; i++) { + if (!psp->packages[i]->name) break; + if (!strcmp(psp->packages[i]->name, name)) + psp->packages[i]->name = NULL; + else + name = psp->packages[i]->name; + } + + qsort(psp->packages, psp->numPackages, sizeof(*psp->packages), + (void *) pkgCompareVer); + + for (i = 0; i < psp->numPackages; i++) + if (!psp->packages[i]->name) break; + psp->numPackages = i; +} + +/** + */ +static PyObject * findUpgradeSet(PyObject * self, PyObject * args) { + PyObject * hdrList, * result; + char * root = "/"; + int i; + struct pkgSet list; + hdrObject * hdr; + + if (!PyArg_ParseTuple(args, "O|s", &hdrList, &root)) return NULL; + + if (!PyList_Check(hdrList)) { + PyErr_SetString(PyExc_TypeError, "list of headers expected"); + return NULL; + } + + list.numPackages = PyList_Size(hdrList); + list.packages = alloca(sizeof(list.packages) * list.numPackages); + for (i = 0; i < list.numPackages; i++) { + hdr = (hdrObject *) PyList_GetItem(hdrList, i); + if (((PyObject *) hdr)->ob_type != &hdrType) { + PyErr_SetString(PyExc_TypeError, "list of headers expected"); + return NULL; + } + list.packages[i] = alloca(sizeof(struct packageInfo)); + list.packages[i]->h = hdrGetHeader(hdr); + list.packages[i]->selected = 0; + list.packages[i]->data = hdr; + + headerGetEntry(list.packages[i]->h, RPMTAG_NAME, NULL, + (void **) &list.packages[i]->name, NULL); + } + + pkgSort (&list); + + if (ugFindUpgradePackages(&list, root)) { + PyErr_SetString(pyrpmError, "error during upgrade check"); + return NULL; + } + + result = PyList_New(0); + for (i = 0; i < list.numPackages; i++) { + if (list.packages[i]->selected) { + PyList_Append(result, list.packages[i]->data); +/* Py_DECREF(list.packages[i]->data); */ + } + } + + return result; +} + +/** + */ +static PyObject * rpmInitDB(PyObject * self, PyObject * args) { + char *root; + int forWrite = 0; + + if (!PyArg_ParseTuple(args, "i|s", &forWrite, &root)) return NULL; + + if (rpmdbInit(root, forWrite ? O_RDWR | O_CREAT: O_RDONLY)) { + char * errmsg = "cannot initialize database in %s"; + char * errstr = NULL; + int errsize; + + errsize = strlen(errmsg) + strlen(root); + errstr = alloca(errsize); + snprintf(errstr, errsize, errmsg, root); + PyErr_SetString(pyrpmError, errstr); + return NULL; + } + + Py_INCREF(Py_None); + return(Py_None); +} + +/** + */ +static PyObject * errorCB = NULL, * errorData = NULL; + +/** + */ +static void errorcb (void) +{ + PyObject * result, * args = NULL; + + if (errorData) + args = Py_BuildValue("(O)", errorData); + + result = PyEval_CallObject(errorCB, args); + Py_XDECREF(args); + + if (result == NULL) { + PyErr_Print(); + PyErr_Clear(); + } + Py_DECREF (result); +} + +/** + */ +static PyObject * errorSetCallback (PyObject * self, PyObject * args) { + PyObject *newCB = NULL, *newData = NULL; + + if (!PyArg_ParseTuple(args, "O|O", &newCB, &newData)) return NULL; + + /* if we're getting a void*, set the error callback to this. */ + /* also, we can possibly decref any python callbacks we had */ + /* and set them to NULL. */ + if (PyCObject_Check (newCB)) { + rpmErrorSetCallback (PyCObject_AsVoidPtr(newCB)); + + Py_XDECREF (errorCB); + Py_XDECREF (errorData); + + errorCB = NULL; + errorData = NULL; + + Py_INCREF(Py_None); + return Py_None; + } + + if (!PyCallable_Check (newCB)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + + Py_XDECREF(errorCB); + Py_XDECREF(errorData); + + errorCB = newCB; + errorData = newData; + + Py_INCREF (errorCB); + Py_XINCREF (errorData); + + return PyCObject_FromVoidPtr(rpmErrorSetCallback (errorcb), NULL); +} + +/** + */ +static PyObject * errorString (PyObject * self, PyObject * args) { + return PyString_FromString(rpmErrorString ()); +} + +/** + */ +static PyObject * checkSig (PyObject * self, PyObject * args) { + char * filename; + int flags; + int rc = 255; + + if (PyArg_ParseTuple(args, "si", &filename, &flags)) { + rpmTransactionSet ts; + const char * av[2]; + QVA_t ka = memset(alloca(sizeof(*ka)), 0, sizeof(*ka)); + + av[0] = filename; + av[1] = NULL; + ka->qva_mode = 'K'; + ka->qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE); + ka->sign = 0; + ka->passPhrase = NULL; + ts = rpmtransCreateSet(NULL, NULL); + rc = rpmcliSign(ts, ka, av); + rpmtransFree(ts); + } + return Py_BuildValue("i", rc); +} + +/* hack to get the current header that's in the transaction set */ +/** + */ +static PyObject * getTsHeader (PyObject * self, PyObject * args) { + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + if (transactionSetHeader) { + return (PyObject *) createHeaderObject(transactionSetHeader); + } + Py_INCREF(Py_None); + return (PyObject *) Py_None; +} + +static PyObject * setVerbosity (PyObject * self, PyObject * args) { + int level; + + if (!PyArg_ParseTuple(args, "i", &level)) + return NULL; + + rpmSetVerbosity(level); + + Py_INCREF(Py_None); + return (PyObject *) Py_None; +} + +/** + */ +typedef struct FDlist_t FDlist; + +/** + */ +struct FDlist_t { + FILE *f; + FD_t fd; + char *note; + FDlist *next; +} ; + +/** + */ +static FDlist *fdhead = NULL; + +/** + */ +static FDlist *fdtail = NULL; + +/** + */ +static int closeCallback(FILE * f) { + FDlist *node, *last; + + printf ("close callback on %p\n", f); + + node = fdhead; + last = NULL; + while (node) { + if (node->f == f) + break; + last = node; + node = node->next; + } + if (node) { + if (last) + last->next = node->next; + else + fdhead = node->next; + printf ("closing %s %p\n", node->note, node->fd); + free (node->note); + node->fd = fdLink(node->fd, "closeCallback"); + Fclose (node->fd); + while (node->fd) + node->fd = fdFree(node->fd, "closeCallback"); + free (node); + } + return 0; +} + +/** + */ +static PyObject * doFopen(PyObject * self, PyObject * args) { + char * path, * mode; + FDlist *node; + + if (!PyArg_ParseTuple(args, "ss", &path, &mode)) + return NULL; + + node = malloc (sizeof(FDlist)); + + node->fd = Fopen(path, mode); + node->fd = fdLink(node->fd, "doFopen"); + node->note = strdup (path); + + if (!node->fd) { + PyErr_SetFromErrno(pyrpmError); + free (node); + return NULL; + } + + if (Ferror(node->fd)) { + const char *err = Fstrerror(node->fd); + free(node); + if (err) { + PyErr_SetString(pyrpmError, err); + return NULL; + } + } + node->f = fdGetFp(node->fd); + printf ("opening %s fd = %p f = %p\n", node->note, node->fd, node->f); + if (!node->f) { + PyErr_SetString(pyrpmError, "FD_t has no FILE*"); + free(node); + return NULL; + } + + node->next = NULL; + if (!fdhead) { + fdhead = fdtail = node; + } else if (fdtail) { + fdtail->next = node; + } else { + fdhead = node; + } + fdtail = node; + + return PyFile_FromFile (node->f, path, mode, closeCallback); +} + +/** + */ +static PyMethodDef rpmModuleMethods[] = { + { "TransactionSet", (PyCFunction) rpmtransCreate, METH_VARARGS, NULL }, + { "addMacro", (PyCFunction) doAddMacro, METH_VARARGS, NULL }, + { "delMacro", (PyCFunction) doDelMacro, METH_VARARGS, NULL }, + { "archscore", (PyCFunction) archScore, METH_VARARGS, NULL }, + { "findUpgradeSet", (PyCFunction) findUpgradeSet, METH_VARARGS, NULL }, + { "headerFromPackage", (PyCFunction) rpmHeaderFromPackage, METH_VARARGS, NULL }, + { "headerLoad", (PyCFunction) hdrLoad, METH_VARARGS, NULL }, + { "rhnLoad", (PyCFunction) rhnLoad, METH_VARARGS, NULL }, + { "initdb", (PyCFunction) rpmInitDB, METH_VARARGS, NULL }, + { "opendb", (PyCFunction) rpmOpenDB, METH_VARARGS, NULL }, + { "rebuilddb", (PyCFunction) rebuildDB, METH_VARARGS, NULL }, + { "mergeHeaderListFromFD", (PyCFunction) rpmMergeHeadersFromFD, METH_VARARGS, NULL }, + { "readHeaderListFromFD", (PyCFunction) rpmHeaderFromFD, METH_VARARGS, NULL }, + { "readHeaderListFromFile", (PyCFunction) rpmHeaderFromFile, METH_VARARGS, NULL }, + { "errorSetCallback", (PyCFunction) errorSetCallback, METH_VARARGS, NULL }, + { "errorString", (PyCFunction) errorString, METH_VARARGS, NULL }, + { "versionCompare", (PyCFunction) versionCompare, METH_VARARGS, NULL }, + { "labelCompare", (PyCFunction) labelCompare, METH_VARARGS, NULL }, + { "checksig", (PyCFunction) checkSig, METH_VARARGS, NULL }, + { "getTransactionCallbackHeader", (PyCFunction) getTsHeader, METH_VARARGS, NULL }, + { "Fopen", (PyCFunction) doFopen, METH_VARARGS, NULL }, + { "setVerbosity", (PyCFunction) setVerbosity, METH_VARARGS, NULL }, + { NULL } +} ; + +/** + */ +void initrpm(void) { + PyObject * m, * d, *o, * tag = NULL, * dict; + int i; + const struct headerSprintfExtension_s * extensions = rpmHeaderFormats; + struct headerSprintfExtension_s * ext; + m = Py_InitModule("rpm", rpmModuleMethods); + + hdrType.ob_type = &PyType_Type; + rpmdbMIType.ob_type = &PyType_Type; + rpmdbType.ob_type = &PyType_Type; + rpmtransType.ob_type = &PyType_Type; + + if(!m) + return; + +/* _rpmio_debug = -1; */ + rpmReadConfigFiles(NULL, NULL); + + d = PyModule_GetDict(m); + + pyrpmError = PyString_FromString("rpm.error"); + PyDict_SetItemString(d, "error", pyrpmError); + Py_DECREF(pyrpmError); + + dict = PyDict_New(); + + for (i = 0; i < rpmTagTableSize; i++) { + tag = PyInt_FromLong(rpmTagTable[i].val); + PyDict_SetItemString(d, (char *) rpmTagTable[i].name, tag); + Py_DECREF(tag); + PyDict_SetItem(dict, tag, o=PyString_FromString(rpmTagTable[i].name + 7)); + Py_DECREF(o); + } + + while (extensions->name) { + if (extensions->type == HEADER_EXT_TAG) { + (const struct headerSprintfExtension *) ext = extensions; + PyDict_SetItemString(d, (char *) extensions->name, o=PyCObject_FromVoidPtr(ext, NULL)); + Py_DECREF(o); + PyDict_SetItem(dict, tag, o=PyString_FromString(ext->name + 7)); + Py_DECREF(o); + } + extensions++; + } + + PyDict_SetItemString(d, "tagnames", dict); + Py_DECREF(dict); + + +#define REGISTER_ENUM(val) \ + PyDict_SetItemString(d, #val, o=PyInt_FromLong( val )); \ + Py_DECREF(o); + + REGISTER_ENUM(RPMFILE_STATE_NORMAL); + REGISTER_ENUM(RPMFILE_STATE_REPLACED); + REGISTER_ENUM(RPMFILE_STATE_NOTINSTALLED); + REGISTER_ENUM(RPMFILE_STATE_NETSHARED); + + REGISTER_ENUM(RPMFILE_CONFIG); + REGISTER_ENUM(RPMFILE_DOC); + REGISTER_ENUM(RPMFILE_MISSINGOK); + REGISTER_ENUM(RPMFILE_NOREPLACE); + REGISTER_ENUM(RPMFILE_GHOST); + REGISTER_ENUM(RPMFILE_LICENSE); + REGISTER_ENUM(RPMFILE_README); + + REGISTER_ENUM(RPMDEP_SENSE_REQUIRES); + REGISTER_ENUM(RPMDEP_SENSE_CONFLICTS); + + REGISTER_ENUM(RPMSENSE_SERIAL); + REGISTER_ENUM(RPMSENSE_LESS); + REGISTER_ENUM(RPMSENSE_GREATER); + REGISTER_ENUM(RPMSENSE_EQUAL); + REGISTER_ENUM(RPMSENSE_PREREQ); + REGISTER_ENUM(RPMSENSE_INTERP); + REGISTER_ENUM(RPMSENSE_SCRIPT_PRE); + REGISTER_ENUM(RPMSENSE_SCRIPT_POST); + REGISTER_ENUM(RPMSENSE_SCRIPT_PREUN); + REGISTER_ENUM(RPMSENSE_SCRIPT_POSTUN); + REGISTER_ENUM(RPMSENSE_SCRIPT_VERIFY); + REGISTER_ENUM(RPMSENSE_FIND_REQUIRES); + REGISTER_ENUM(RPMSENSE_FIND_PROVIDES); + REGISTER_ENUM(RPMSENSE_TRIGGERIN); + REGISTER_ENUM(RPMSENSE_TRIGGERUN); + REGISTER_ENUM(RPMSENSE_TRIGGERPOSTUN); + REGISTER_ENUM(RPMSENSE_MULTILIB); + REGISTER_ENUM(RPMSENSE_SCRIPT_PREP); + REGISTER_ENUM(RPMSENSE_SCRIPT_BUILD); + REGISTER_ENUM(RPMSENSE_SCRIPT_INSTALL); + REGISTER_ENUM(RPMSENSE_SCRIPT_CLEAN); + REGISTER_ENUM(RPMSENSE_RPMLIB); + REGISTER_ENUM(RPMSENSE_TRIGGERPREIN); + + REGISTER_ENUM(RPMTRANS_FLAG_TEST); + REGISTER_ENUM(RPMTRANS_FLAG_BUILD_PROBS); + REGISTER_ENUM(RPMTRANS_FLAG_NOSCRIPTS); + REGISTER_ENUM(RPMTRANS_FLAG_JUSTDB); + REGISTER_ENUM(RPMTRANS_FLAG_NOTRIGGERS); + REGISTER_ENUM(RPMTRANS_FLAG_NODOCS); + REGISTER_ENUM(RPMTRANS_FLAG_ALLFILES); + REGISTER_ENUM(RPMTRANS_FLAG_KEEPOBSOLETE); + REGISTER_ENUM(RPMTRANS_FLAG_MULTILIB); + + REGISTER_ENUM(RPMPROB_FILTER_IGNOREOS); + REGISTER_ENUM(RPMPROB_FILTER_IGNOREARCH); + REGISTER_ENUM(RPMPROB_FILTER_REPLACEPKG); + REGISTER_ENUM(RPMPROB_FILTER_FORCERELOCATE); + REGISTER_ENUM(RPMPROB_FILTER_REPLACENEWFILES); + REGISTER_ENUM(RPMPROB_FILTER_REPLACEOLDFILES); + REGISTER_ENUM(RPMPROB_FILTER_OLDPACKAGE); + REGISTER_ENUM(RPMPROB_FILTER_DISKSPACE); + REGISTER_ENUM(RPMPROB_FILTER_DISKNODES); + + REGISTER_ENUM(RPMCALLBACK_INST_PROGRESS); + REGISTER_ENUM(RPMCALLBACK_INST_START); + REGISTER_ENUM(RPMCALLBACK_INST_OPEN_FILE); + REGISTER_ENUM(RPMCALLBACK_INST_CLOSE_FILE); + REGISTER_ENUM(RPMCALLBACK_TRANS_PROGRESS); + REGISTER_ENUM(RPMCALLBACK_TRANS_START); + REGISTER_ENUM(RPMCALLBACK_TRANS_STOP); + REGISTER_ENUM(RPMCALLBACK_UNINST_PROGRESS); + REGISTER_ENUM(RPMCALLBACK_UNINST_START); + REGISTER_ENUM(RPMCALLBACK_UNINST_STOP); + REGISTER_ENUM(RPMCALLBACK_UNPACK_ERROR); + REGISTER_ENUM(RPMCALLBACK_CPIO_ERROR); + + REGISTER_ENUM(RPMPROB_BADARCH); + REGISTER_ENUM(RPMPROB_BADOS); + REGISTER_ENUM(RPMPROB_PKG_INSTALLED); + REGISTER_ENUM(RPMPROB_BADRELOCATE); + REGISTER_ENUM(RPMPROB_REQUIRES); + REGISTER_ENUM(RPMPROB_CONFLICT); + REGISTER_ENUM(RPMPROB_NEW_FILE_CONFLICT); + REGISTER_ENUM(RPMPROB_FILE_CONFLICT); + REGISTER_ENUM(RPMPROB_OLDPACKAGE); + REGISTER_ENUM(RPMPROB_DISKSPACE); + REGISTER_ENUM(RPMPROB_DISKNODES); + REGISTER_ENUM(RPMPROB_BADPRETRANS); + +#ifdef DEAD + REGISTER_ENUM(CHECKSIG_PGP); /* XXX use VERIFY_SIGNATURE */ + REGISTER_ENUM(CHECKSIG_GPG); /* XXX use VERIFY_SIGNATURE */ + REGISTER_ENUM(CHECKSIG_MD5); /* XXX use VERIFY_DIGEST */ +#else + REGISTER_ENUM(VERIFY_DIGEST); + REGISTER_ENUM(VERIFY_SIGNATURE); +#endif + + REGISTER_ENUM(RPMLOG_EMERG); + REGISTER_ENUM(RPMLOG_ALERT); + REGISTER_ENUM(RPMLOG_CRIT); + REGISTER_ENUM(RPMLOG_ERR); + REGISTER_ENUM(RPMLOG_WARNING); + REGISTER_ENUM(RPMLOG_NOTICE); + REGISTER_ENUM(RPMLOG_INFO); + REGISTER_ENUM(RPMLOG_DEBUG); + + REGISTER_ENUM(RPMMIRE_DEFAULT); + REGISTER_ENUM(RPMMIRE_STRCMP); + REGISTER_ENUM(RPMMIRE_REGEX); + REGISTER_ENUM(RPMMIRE_GLOB); + +} + +/*@}*/