summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2009-08-08 18:57:34 +0200
committerJulian Andres Klode <jak@debian.org>2009-08-08 18:57:34 +0200
commit31d95030344c77d97d3245515a861a73732d0c5c (patch)
tree6cfe66c1e7f4843290b389882744a35f7d604e85
parente526904d0440e720cbeebf895faf884717f3fbb3 (diff)
downloadpython-apt-31d95030344c77d97d3245515a861a73732d0c5c.tar.gz
python/tarfile.cc: Introduce wrapper around ExtractTar.
-rw-r--r--python/apt_instmodule.cc2
-rw-r--r--python/apt_instmodule.h9
-rw-r--r--python/tarfile.cc412
-rw-r--r--setup.py2
4 files changed, 424 insertions, 1 deletions
diff --git a/python/apt_instmodule.cc b/python/apt_instmodule.cc
index aedccb47..3baaf985 100644
--- a/python/apt_instmodule.cc
+++ b/python/apt_instmodule.cc
@@ -207,5 +207,7 @@ extern "C" void initapt_inst()
ADDTYPE(module,"ArMember",&PyArMember_Type);
ADDTYPE(module,"ArArchive",&PyArArchive_Type);
+ ADDTYPE(module,"TarFile",&PyTarFile_Type);
+ ADDTYPE(module,"TarMember",&PyTarMember_Type);
RETURN(module);
}
diff --git a/python/apt_instmodule.h b/python/apt_instmodule.h
index e06e344d..94ada0f8 100644
--- a/python/apt_instmodule.h
+++ b/python/apt_instmodule.h
@@ -11,6 +11,8 @@
#define APT_INSTMODULE_H
#include <Python.h>
+#include "generic.h"
+#include <apt-pkg/extracttar.h>
PyObject *debExtract(PyObject *Self,PyObject *Args);
extern char *doc_debExtract;
@@ -20,5 +22,12 @@ extern char *doc_tarExtract;
extern PyTypeObject PyArMember_Type;
extern PyTypeObject PyArArchive_Type;
+extern PyTypeObject PyTarFile_Type;
+extern PyTypeObject PyTarMember_Type;
+
+struct PyTarFileObject : public CppOwnedPyObject<ExtractTar*> {
+ int min;
+ FileFd Fd;
+};
#endif
diff --git a/python/tarfile.cc b/python/tarfile.cc
new file mode 100644
index 00000000..f3e11d2b
--- /dev/null
+++ b/python/tarfile.cc
@@ -0,0 +1,412 @@
+/*
+ * arfile.cc - Wrapper around ExtractTar which behaves like Python's tarfile.
+ *
+ * Copyright 2009 Julian Andres Klode <jak@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include "generic.h"
+#include "apt_instmodule.h"
+#include <apt-pkg/extracttar.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/dirstream.h>
+
+/**
+ * A subclass of pkgDirStream which calls a Python callback.
+ *
+ * This calls a Python callback in FinishedFile() with the Item as the first
+ * argument and the data as the second argument.
+ */
+class PyDirStream : public pkgDirStream
+{
+
+public:
+ PyObject *callback;
+ // Set to true if an error occured in the Python callback.
+ bool error;
+ // Place where the copy of the data is stored.
+ char *copy;
+
+ virtual bool DoItem(Item &Itm,int &Fd);
+ virtual bool FinishedFile(Item &Itm,int Fd);
+ virtual bool Process(Item &Itm,const unsigned char *Data,
+ unsigned long Size,unsigned long Pos);
+
+ PyDirStream(PyObject *callback) : callback(callback), error(false), copy(0) {
+ Py_INCREF(callback);
+ }
+
+ virtual ~PyDirStream() {
+ Py_DECREF(callback);
+ delete[] copy;
+ }
+};
+
+bool PyDirStream::DoItem(Item &Itm, int &Fd)
+{
+ delete[] copy;
+ copy = new char[Itm.Size];
+ Fd = -2;
+ return true;
+}
+
+bool PyDirStream::Process(Item &Itm,const unsigned char *Data,
+ unsigned long Size,unsigned long Pos)
+{
+ memcpy(copy + Pos, Data,Size);
+ return true;
+}
+
+bool PyDirStream::FinishedFile(Item &Itm,int Fd)
+{
+ PyObject *py_member = CppOwnedPyObject_NEW<Item*>(0,&PyTarMember_Type,&Itm);
+
+ ((CppOwnedPyObject<Item*>*)py_member)->NoDelete = true;
+ PyObject *py_data = PyBytes_FromStringAndSize(copy, Itm.Size);
+ PyObject *result = PyObject_CallFunctionObjArgs(callback, py_member,
+ py_data, 0);
+ if (result == 0)
+ error = true;
+ return (result != 0);
+}
+
+// The tarfile.TarInfo interface for our TarMember class.
+static PyObject *tarmember_isblk(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::BlockDevice);
+}
+static PyObject *tarmember_ischr(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::CharDevice);
+}
+static PyObject *tarmember_isdev(PyObject *self, PyObject *args)
+{
+ pkgDirStream::Item::Type_t type = GetCpp<pkgDirStream::Item*>(self)->Type;
+ return PyBool_FromLong(type == pkgDirStream::Item::CharDevice ||
+ type == pkgDirStream::Item::BlockDevice ||
+ type == pkgDirStream::Item::FIFO);
+}
+
+static PyObject *tarmember_isdir(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::Directory);
+}
+
+static PyObject *tarmember_isfifo(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::FIFO);
+}
+
+static PyObject *tarmember_isfile(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::File);
+}
+static PyObject *tarmember_islnk(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::HardLink);
+}
+static PyObject *tarmember_isreg(PyObject *self, PyObject *args)
+{
+ return tarmember_isfile(self, NULL);
+}
+static PyObject *tarmember_issym(PyObject *self, PyObject *args)
+{
+ return PyBool_FromLong(GetCpp<pkgDirStream::Item*>(self)->Type ==
+ pkgDirStream::Item::SymbolicLink);
+}
+
+static PyObject *tarmember_get_name(PyObject *self, void *closure)
+{
+ return PyString_FromString(GetCpp<pkgDirStream::Item*>(self)->Name);
+}
+
+static PyObject *tarmember_get_linkname(PyObject *self, void *closure)
+{
+ return Safe_FromString(GetCpp<pkgDirStream::Item*>(self)->LinkTarget);
+}
+
+static PyObject *tarmember_get_mode(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->Mode);
+}
+
+static PyObject *tarmember_get_uid(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->UID);
+}
+static PyObject *tarmember_get_gid(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->GID);
+}
+static PyObject *tarmember_get_size(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->Size);
+}
+
+static PyObject *tarmember_get_mtime(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->MTime);
+}
+
+static PyObject *tarmember_get_major(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->Major);
+}
+
+static PyObject *tarmember_get_minor(PyObject *self, void *closure)
+{
+ return Py_BuildValue("k", GetCpp<pkgDirStream::Item*>(self)->Minor);
+}
+
+static PyObject *tarmember_repr(PyObject *self)
+{
+ return PyString_FromFormat("<%s object: name:'%s'>",
+ self->ob_type->tp_name,
+ GetCpp<pkgDirStream::Item*>(self)->Name);
+
+}
+
+
+static PyMethodDef tarmember_methods[] = {
+ {"isblk",tarmember_isblk,METH_NOARGS,
+ "Determine whether the member is a block device."},
+ {"ischr",tarmember_ischr,METH_NOARGS,
+ "Determine whether the member is a character device."},
+ {"isdev",tarmember_isdev,METH_NOARGS,
+ "Determine whether the member is a device (block,character or FIFO)."},
+ {"isdir",tarmember_isdir,METH_NOARGS,
+ "Determine whether the member is a directory."},
+ {"isfifo",tarmember_isfifo,METH_NOARGS,
+ "Determine whether the member is a FIFO."},
+ {"isfile",tarmember_isfile,METH_NOARGS,
+ "Determine whether the member is a regular file."},
+ {"islnk",tarmember_islnk,METH_NOARGS,
+ "Determine whether the member is a hardlink."},
+ {"isreg",tarmember_isreg,METH_NOARGS,
+ "Determine whether the member is a regular file, same as isfile()."},
+ {"issym",tarmember_issym,METH_NOARGS,
+ "Determine whether the member is a symbolic link."},
+ {NULL}
+};
+
+static PyGetSetDef tarmember_getset[] = {
+ {"gid",tarmember_get_gid,0,"The owner's group id"},
+ {"linkname",tarmember_get_linkname,0,"The target of the link."},
+ {"major",tarmember_get_major,0,"The major ID of the device."},
+ {"minor",tarmember_get_minor,0,"The minor ID of the device."},
+ {"mode",tarmember_get_mode,0,"The mode (permissions)."},
+ {"mtime",tarmember_get_mtime,0,"Last time of modification."},
+ {"name",tarmember_get_name,0,"The name of the file."},
+ {"size",tarmember_get_size,0,"The size of the file."},
+ {"uid",tarmember_get_uid,0,"The owner's user id."},
+ {NULL}
+};
+
+static const char *tarmember_doc =
+ "Represent a single member of a 'tar' archive.\n\n"
+ "This class, which has been modelled after 'tarfile.TarInfo', represents\n"
+ "information about a given member in an archive.";
+PyTypeObject PyTarMember_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "apt_inst.TarMember", // tp_name
+ sizeof(CppOwnedPyObject<pkgDirStream::Item*>), // tp_basicsize
+ 0, // tp_itemsize
+ // Methods
+ CppOwnedDealloc<pkgDirStream::Item*>, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ tarmember_repr, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT | // tp_flags
+ Py_TPFLAGS_HAVE_GC,
+ tarmember_doc, // tp_doc
+ CppOwnedTraverse<pkgDirStream::Item*>, // tp_traverse
+ CppOwnedClear<pkgDirStream::Item*>, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ tarmember_methods, // tp_methods
+ 0, // tp_members
+ tarmember_getset // tp_getset
+};
+
+
+
+static PyObject *tarfile_new(PyTypeObject *type,PyObject *args,PyObject *kwds)
+{
+ PyObject *file;
+ PyTarFileObject *self;
+ char *filename;
+ int fileno;
+ int min = 0;
+ int max = 0xFFFFFFFF;
+ char *comp = "gzip";
+
+ static char *kwlist[] = {"file","min","max","comp",NULL};
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|iis", kwlist, &file, &min,
+ &max,&comp) == 0)
+ return 0;
+
+ self = (PyTarFileObject*)CppOwnedPyObject_NEW<ExtractTar*>(file,type);
+
+ // We receive a filename.
+ if ((filename = (char*)PyObject_AsString(file)))
+ new (&self->Fd) FileFd(filename,FileFd::ReadOnly);
+ else if ((fileno = PyObject_AsFileDescriptor(file)) != -1) {
+ // clear the error set by PyObject_AsString().
+ PyErr_Clear();
+ new (&self->Fd) FileFd(fileno,false);
+ }
+ else {
+ Py_DECREF(self);
+ return 0;
+ }
+
+ self->min = min;
+ self->Object = new ExtractTar(self->Fd,max,comp);
+ if (_error->PendingError() == true)
+ return HandleErrors(self);
+ return self;
+}
+
+static const char *tarfile_extractall_doc =
+ "extractall([rootdir: str]) -> True\n\n"
+ "Extract the archive in the current directory. The argument 'rootdir'\n"
+ "can be used to change the target directory.";
+static PyObject *tarfile_extractall(PyObject *self, PyObject *args)
+{
+ string cwd = SafeGetCWD();
+ char *rootdir = 0;
+ if (PyArg_ParseTuple(args,"|s:extractall",&rootdir) == 0)
+ return 0;
+
+ if (rootdir) {
+ if (chdir(rootdir) == -1)
+ return PyErr_SetFromErrnoWithFilename(PyExc_OSError, rootdir);
+ }
+
+ pkgDirStream Extract;
+
+ ((PyTarFileObject*)self)->Fd.Seek(((PyTarFileObject*)self)->min);
+ bool res = GetCpp<ExtractTar*>(self)->Go(Extract);
+
+
+
+ if (rootdir) {
+ if (chdir(cwd.c_str()) == -1)
+ return PyErr_SetFromErrnoWithFilename(PyExc_OSError,
+ (char*)cwd.c_str());
+ }
+ return HandleErrors(PyBool_FromLong(res));
+}
+
+static const char *tarfile_go_doc =
+ "go(callback: callable) -> True\n\n"
+ "Go through the archive and call the callable callback for each\n"
+ "member with 2 arguments. The first argument is the TarMember and\n"
+ "the second one is the data, as bytes.";
+static PyObject *tarfile_go(PyObject *self, PyObject *arg)
+{
+ pkgDirStream Extract;
+ PyDirStream stream(arg);
+ ((PyTarFileObject*)self)->Fd.Seek(((PyTarFileObject*)self)->min);
+ bool res = GetCpp<ExtractTar*>(self)->Go(stream);
+ if (stream.error)
+ return 0;
+ return HandleErrors(PyBool_FromLong(res));
+}
+
+static PyMethodDef tarfile_methods[] = {
+ {"extractall",tarfile_extractall,METH_VARARGS,tarfile_extractall_doc},
+ {"go",tarfile_go,METH_O,tarfile_go_doc},
+ {NULL}
+};
+
+static PyObject *tarfile_repr(PyObject *self)
+{
+ return PyString_FromFormat("<%s object: %s>", self->ob_type->tp_name,
+ PyString_AsString(PyObject_Repr(GetOwner<ExtractTar*>(self))));
+}
+
+static const char *tarfile_doc =
+ "TarFile(file: str/int/file[, min: int, max: int, comp: str])\n\n"
+ "The parameter 'file' may be a string specifying the path of a file, or\n"
+ "a file-like object providing the fileno() method. It may also be an int\n"
+ "specifying a file descriptor (returned by e.g. os.open()).\n\n"
+ "The parameter 'min' describes the offset in the file where the archive\n"
+ "begins and the parameter 'max' is the size of the archive.\n\n"
+ "The compression of the archive is set by the parameter 'comp'. It can\n"
+ "be set to any program supporting the -d switch, the default being gzip.";
+PyTypeObject PyTarFile_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "apt_inst.TarFile", // tp_name
+ sizeof(PyTarFileObject), // tp_basicsize
+ 0, // tp_itemsize
+ // Methods
+ CppOwnedDealloc<ExtractTar*>, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ tarfile_repr, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT | // tp_flags
+ Py_TPFLAGS_HAVE_GC,
+ tarfile_doc, // tp_doc
+ CppOwnedTraverse<ExtractTar*>, // tp_traverse
+ CppOwnedClear<ExtractTar*>, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ tarfile_methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ tarfile_new // tp_new
+};
diff --git a/setup.py b/setup.py
index 4eb6987e..635fee9b 100644
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,7 @@ apt_pkg = Extension("apt_pkg", files, libraries=["apt-pkg"])
# The apt_inst module
files = ["python/apt_instmodule.cc", "python/generic.cc", "python/tar.cc",
- "python/arfile.cc"]
+ "python/arfile.cc", "python/tarfile.cc"]
apt_inst = Extension("apt_inst", files, libraries=["apt-pkg", "apt-inst"])
# Replace the leading _ that is used in the templates for translation