diff --git a/doc/changelog.txt b/doc/changelog.txt index 6b92765..d157b13 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -8,6 +8,7 @@ Closes: SF bug #2977749 * Added support for the lbzip2 program handling BZIP2 archives. * Added support for the plzip program handling LZIP archives. +* Prevent overwriting the same file with repack. 0.15 "Contraband" (released 8.4.2012) diff --git a/doc/development.txt b/doc/development.txt new file mode 100644 index 0000000..3f3cd38 --- /dev/null +++ b/doc/development.txt @@ -0,0 +1,65 @@ +Development with the patoolib library +====================================== + +The patool funtionality can also be used in other Python programs. +To do this, install the patool program, import the library and +use one or more of the convenience functions. + +import patoolib +if patoolib.extract("myarchive.zip", verbose=True) == 0: + print "Success." +else: + print "Error." + +General rules for all convenience functions: + +* All convenience function return zero when no error occurred, else + an integer unequal zero. + +* Error messages are printed on stderr, informative messages + are printed on stdout. + +* All file arguments are filenames. File objects are not accepted + as input. + +* Filenames can be relative or absolute. + +The convenience functions are: + +* ``def extract(archive, verbose=False, outdir=None)`` + + This function extracts the given archive filename to the current + working directory or if specified to the given directory name in outdir. + If verbose operation is set to True, additional output of the archive + program is shown. + +* ``def list(archive, verbose=False)`` + + This function lists the contents of the given archive filename + on stdout. + If verbose operation is set to True, additional output of the archive + program is shown. + +* ``def test(archive, verbose=False)`` + + This function tests the given archive filename. + If verbose operation is set to True, additional output of the archive + program is shown. + +* ``def create(archive, *filenames, **kwargs)`` + + This function create a new archive. The type of archive is determined + by the archive filename extension. The archive must not already + exist. + The list of filenames to add to the archive must not be empty. + If verbose operation is set to True, additional output of the archive + program is shown. + +* ``def diff(archive1, archive2, verbose=False)`` + + This function + +* ``def repack(archive1, archive2, verbose=False)`` + + This function + diff --git a/patoolib/__init__.py b/patoolib/__init__.py index cb680cd..424072b 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -398,9 +398,12 @@ def cleanup_outdir (outdir): def _handle_archive (archive, command, *args, **kwargs): """Handle archive command; raising PatoolError on errors.""" - if command != 'create': - # check that archive is a regular file - util.check_filename(archive) + # check arguments + if command == 'create': + util.check_archive_filelist(args) + util.check_new_filename(archive) + else: + util.check_existing_filename(archive) format, encoding = kwargs.get("format"), kwargs.get("encoding") if format is None: format, encoding = get_archive_format(archive) @@ -470,9 +473,14 @@ def rmtree_log_error (func, path, exc): def _diff_archives (archive1, archive2, **kwargs): """Show differences between two archives.""" + if util.is_same_file(archive1, archive2): + msg = "no differences found: archive `%s' and `%s' are the same files" + print msg % (archive1, archive2) + return 0 diff = util.find_program("diff") if not diff: - raise util.PatoolError("The diff(1) program is required for showing archive differences, please install it") + msg = "The diff(1) program is required for showing archive differences, please install it." + raise util.PatoolError(msg) tmpdir1 = util.tmpdir() tmpdir2 = util.tmpdir() try: @@ -486,6 +494,9 @@ def _diff_archives (archive1, archive2, **kwargs): def _repack_archive (archive1, archive2, **kwargs): """Repackage an archive to a different format.""" + if util.is_same_file(archive1, archive2): + msg = "cannot repack identical archives `%s' and `%s'" + raise util.PatoolError(msg % (archive1, archive2)) tmpdir = util.tmpdir() try: _handle_archive(archive1, 'extract', outdir=tmpdir, **kwargs) @@ -539,7 +550,6 @@ def test (archive, verbose=False): def create (archive, *filenames, **kwargs): """Create given archive with given files.""" - assert len(filenames) > 0 return handle_archive(archive, 'create', *filenames, **kwargs) diff --git a/patoolib/util.py b/patoolib/util.py index 95bb3c8..6776747 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -189,7 +189,7 @@ def guess_mime_file_mime (file_prog, filename): cmd = [file_prog, "--brief", "--mime", "--uncompress", filename] try: outparts = backtick(cmd).strip().split(";") - except OSError, msg: + except OSError: # ignore errors, as file(1) is only a fallback return mime, encoding mime2 = outparts[0].split(" ", 1)[0] @@ -250,7 +250,7 @@ def guess_mime_file_text (file_prog, filename): cmd = [file_prog, "--brief", filename] try: output = backtick(cmd).strip() - except OSError, msg: + except OSError: # ignore errors, as file(1) is only a fallback return None # match output against known strings @@ -260,14 +260,28 @@ def guess_mime_file_text (file_prog, filename): return None -def check_filename (filename): +def check_existing_filename (filename): """Ensure that given filename is a valid, existing file.""" if not os.path.isfile(filename): - raise PatoolError("`%s' is not a file." % filename) + raise PatoolError("`%s' is not a file" % filename) if not os.path.exists(filename): - raise PatoolError("File `%s' not found." % filename) + raise PatoolError("file `%s' was not found" % filename) if not os.access(filename, os.R_OK): - raise PatoolError("File `%s' not readable." % filename) + raise PatoolError("file `%s' is not readable" % filename) + + +def check_new_filename (filename): + """Check that filename does not already exist.""" + if os.path.exists(filename): + raise PatoolError("cannot overwrite existing file `%s'" % filename) + + +def check_archive_filelist (filenames): + """Check that file list is not empty and contains only existing files.""" + if not filenames: + raise PatoolError("cannot create archive with empty filelist") + for filename in filenames: + check_existing_filename(filename) def set_mode (filename, flags): @@ -387,3 +401,10 @@ def strlist_with_or (alist): if len(alist) > 1: return "%s or %s" % (", ".join(alist[:-1]), alist[-1]) return ", ".join(alist) + + +def is_same_file (filename1, filename2): + """Check if filename1 and filename2 point to the same file object.""" + if os.name == 'posix': + return os.path.samefile(filename1, filename2) + return os.path.realpath(filename1) == os.path.realpath(filename2)