[Scummvm-git-logs] scummvm master -> f40006fddf7a2fb445fcd731f38dcae7f9ebb3de
sev-
noreply at scummvm.org
Sat Feb 7 22:20:18 UTC 2026
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
390bf17826 SAGA: Normalized keymapper names
09cff5ecc0 DEVTOOLS: Imported pycdlib
66554c6e96 DEVTOOLS: PYCDLIB: Allow user to specify encoding
162ebe483e DEVTOOLS: PYCDLIB: More fixes for Japanese ISOs
f40006fddf DEVTOOLS: COMPANION: Use in-tree pycdlib
Commit: 390bf17826aef1014f1048d349bdc55f8470d14a
https://github.com/scummvm/scummvm/commit/390bf17826aef1014f1048d349bdc55f8470d14a
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-02-07T23:19:17+01:00
Commit Message:
SAGA: Normalized keymapper names
Changed paths:
engines/saga/metaengine.cpp
diff --git a/engines/saga/metaengine.cpp b/engines/saga/metaengine.cpp
index f7afad2c9ec..19f7a29342e 100644
--- a/engines/saga/metaengine.cpp
+++ b/engines/saga/metaengine.cpp
@@ -264,13 +264,13 @@ Common::KeymapArray SagaMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Saga;
- Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, engineKeyMapId, _("Default game keymap"));
- Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, gameKeyMapId, _("Game keymapping"));
- Keymap *optionKeyMap = new Keymap(Keymap::kKeymapTypeGame, optionKeyMapId, _("Option panel keymapping"));
- Keymap *saveKeyMap = new Keymap(Keymap::kKeymapTypeGame, saveKeyMapId, _("Save panel keymapping"));
- Keymap *loadKeyMap = new Keymap(Keymap::kKeymapTypeGame, loadKeyMapId, _("Load panel keymapping"));
- Keymap *quitKeyMap = new Keymap(Keymap::kKeymapTypeGame, quitKeyMapId, _("Quit panel keymapping"));
- Keymap *converseKeyMap = new Keymap(Keymap::kKeymapTypeGame, converseKeyMapId, _("Converse panel keymapping"));
+ Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, engineKeyMapId, _("Default game keymappings"));
+ Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, gameKeyMapId, _("Game keymappings"));
+ Keymap *optionKeyMap = new Keymap(Keymap::kKeymapTypeGame, optionKeyMapId, _("Option panel keymappings"));
+ Keymap *saveKeyMap = new Keymap(Keymap::kKeymapTypeGame, saveKeyMapId, _("Save panel keymappings"));
+ Keymap *loadKeyMap = new Keymap(Keymap::kKeymapTypeGame, loadKeyMapId, _("Load panel keymappings"));
+ Keymap *quitKeyMap = new Keymap(Keymap::kKeymapTypeGame, quitKeyMapId, _("Quit panel keymappings"));
+ Keymap *converseKeyMap = new Keymap(Keymap::kKeymapTypeGame, converseKeyMapId, _("Converse panel keymappings"));
Action *act;
Commit: 09cff5ecc0c9999f0a076907895abeaafeda3b1b
https://github.com/scummvm/scummvm/commit/09cff5ecc0c9999f0a076907895abeaafeda3b1b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-02-07T23:19:38+01:00
Commit Message:
DEVTOOLS: Imported pycdlib
tag v1.15.0 67fe5ea7f68cf1185379c2c5e8acf37d483a2d4a
https://github.com/clalancette/pycdlib.git
Changed paths:
A devtools/contrib/pycdlib/.gitattributes
A devtools/contrib/pycdlib/BUGS
A devtools/contrib/pycdlib/CHANGELOG
A devtools/contrib/pycdlib/COPYING
A devtools/contrib/pycdlib/MANIFEST.in
A devtools/contrib/pycdlib/Makefile
A devtools/contrib/pycdlib/README.md
A devtools/contrib/pycdlib/custom-pydoc.py
A devtools/contrib/pycdlib/debian/changelog
A devtools/contrib/pycdlib/debian/compat
A devtools/contrib/pycdlib/debian/control
A devtools/contrib/pycdlib/debian/copyright
A devtools/contrib/pycdlib/debian/pycdlib-tools.install
A devtools/contrib/pycdlib/debian/pycdlib-tools.manpages
A devtools/contrib/pycdlib/debian/rules
A devtools/contrib/pycdlib/debian/watch
A devtools/contrib/pycdlib/docs/README.md
A devtools/contrib/pycdlib/docs/_config.yml
A devtools/contrib/pycdlib/docs/design.md
A devtools/contrib/pycdlib/docs/example-creating-bootable-iso.md
A devtools/contrib/pycdlib/docs/example-creating-hybrid-bootable-iso.md
A devtools/contrib/pycdlib/docs/example-creating-joliet-iso.md
A devtools/contrib/pycdlib/docs/example-creating-new-basic-iso.md
A devtools/contrib/pycdlib/docs/example-creating-rock-ridge-iso.md
A devtools/contrib/pycdlib/docs/example-creating-udf-iso.md
A devtools/contrib/pycdlib/docs/example-extracting-data-from-iso.md
A devtools/contrib/pycdlib/docs/example-forcing-consistency.md
A devtools/contrib/pycdlib/docs/example-managing-hard-links.md
A devtools/contrib/pycdlib/docs/example-modifying-file-in-place.md
A devtools/contrib/pycdlib/docs/example-opening-existing-iso.md
A devtools/contrib/pycdlib/docs/example-reading-file-in-chunks.md
A devtools/contrib/pycdlib/docs/example-using-facade.md
A devtools/contrib/pycdlib/docs/example-walking-iso-filesystem.md
A devtools/contrib/pycdlib/docs/examples.md
A devtools/contrib/pycdlib/docs/exceptions.md
A devtools/contrib/pycdlib/docs/pycdlib-api.html
A devtools/contrib/pycdlib/docs/pycdlib-explorer.html
A devtools/contrib/pycdlib/docs/pycdlib-extract-files.html
A devtools/contrib/pycdlib/docs/pycdlib-genisoimage.html
A devtools/contrib/pycdlib/docs/pydoc_data/_pydoc.css
A devtools/contrib/pycdlib/docs/python-compatibility.md
A devtools/contrib/pycdlib/docs/reporting-issues.md
A devtools/contrib/pycdlib/docs/standards.md
A devtools/contrib/pycdlib/docs/tools.md
A devtools/contrib/pycdlib/examples/create-bootable.py
A devtools/contrib/pycdlib/examples/create-new.py
A devtools/contrib/pycdlib/examples/extract-data.py
A devtools/contrib/pycdlib/examples/open-existing.py
A devtools/contrib/pycdlib/examples/open-raw-windows-device.py
A devtools/contrib/pycdlib/man/pycdlib-explorer.1
A devtools/contrib/pycdlib/man/pycdlib-extract-files.1
A devtools/contrib/pycdlib/man/pycdlib-genisoimage.1
A devtools/contrib/pycdlib/pycdlib.patch
A devtools/contrib/pycdlib/pycdlib/__init__.py
A devtools/contrib/pycdlib/pycdlib/dates.py
A devtools/contrib/pycdlib/pycdlib/dr.py
A devtools/contrib/pycdlib/pycdlib/eltorito.py
A devtools/contrib/pycdlib/pycdlib/facade.py
A devtools/contrib/pycdlib/pycdlib/headervd.py
A devtools/contrib/pycdlib/pycdlib/inode.py
A devtools/contrib/pycdlib/pycdlib/isohybrid.py
A devtools/contrib/pycdlib/pycdlib/path_table_record.py
A devtools/contrib/pycdlib/pycdlib/py.typed
A devtools/contrib/pycdlib/pycdlib/pycdlib.py
A devtools/contrib/pycdlib/pycdlib/pycdlib.py.orig
A devtools/contrib/pycdlib/pycdlib/pycdlibexception.py
A devtools/contrib/pycdlib/pycdlib/pycdlibio.py
A devtools/contrib/pycdlib/pycdlib/rockridge.py
A devtools/contrib/pycdlib/pycdlib/udf.py
A devtools/contrib/pycdlib/pycdlib/utils.py
A devtools/contrib/pycdlib/pylint.conf
A devtools/contrib/pycdlib/pytest.ini
A devtools/contrib/pycdlib/python-pycdlib.spec.in
A devtools/contrib/pycdlib/setup.cfg
A devtools/contrib/pycdlib/setup.py
A devtools/contrib/pycdlib/tests/conftest.py
A devtools/contrib/pycdlib/tests/integration/test_common.py
A devtools/contrib/pycdlib/tests/integration/test_facade.py
A devtools/contrib/pycdlib/tests/integration/test_hybrid.py
A devtools/contrib/pycdlib/tests/integration/test_new.py
A devtools/contrib/pycdlib/tests/integration/test_parse.py
A devtools/contrib/pycdlib/tests/tools/test_pycdlib_genisoimage.py
A devtools/contrib/pycdlib/tests/unit/test_dates.py
A devtools/contrib/pycdlib/tests/unit/test_dr.py
A devtools/contrib/pycdlib/tests/unit/test_eltorito.py
A devtools/contrib/pycdlib/tests/unit/test_headervd.py
A devtools/contrib/pycdlib/tests/unit/test_inode.py
A devtools/contrib/pycdlib/tests/unit/test_isohybrid.py
A devtools/contrib/pycdlib/tests/unit/test_ptr.py
A devtools/contrib/pycdlib/tests/unit/test_rockridge.py
A devtools/contrib/pycdlib/tests/unit/test_udf.py
A devtools/contrib/pycdlib/tests/unit/test_utils.py
A devtools/contrib/pycdlib/tools/pycdlib-explorer
A devtools/contrib/pycdlib/tools/pycdlib-extract-files
A devtools/contrib/pycdlib/tools/pycdlib-genisoimage
A devtools/contrib/pycdlib/vendor/README
A devtools/contrib/pycdlib/vendor/cdrkit-1.1.11-fedora29-patched.tar.gz
A devtools/contrib/pycdlib/vendor/cdrkit-1.1.11-fedora40-patched.tar.gz
diff --git a/devtools/contrib/pycdlib/.gitattributes b/devtools/contrib/pycdlib/.gitattributes
new file mode 100644
index 00000000000..f087b429e2f
--- /dev/null
+++ b/devtools/contrib/pycdlib/.gitattributes
@@ -0,0 +1 @@
+*.tar.gz filter=lfs diff=lfs merge=lfs -text
diff --git a/devtools/contrib/pycdlib/BUGS b/devtools/contrib/pycdlib/BUGS
new file mode 100644
index 00000000000..b1aafda4853
--- /dev/null
+++ b/devtools/contrib/pycdlib/BUGS
@@ -0,0 +1,49 @@
+1. Add tests to ensure that non-zero length symlinks get dealt with properly
+ both for parsing and for writing back out (don't write them out, they have
+ bogus data).
+2. Add tests to ensure that Rock Ridge CE records are the very last record in
+ the Rock Ridge extensions (some OSs, like FreeBSD, doesn't like them if
+ they aren't the last one).
+3. Add a test for Rockridge RR entries in 1.12 version ISOs. This isn't
+ technically allowed by the standard, but we have seen it in the wild, so
+ we should have a test to make sure we don't break it.
+4. Add a test for "bare" El Torito section entries. The El Torito standard
+ requires that section entries beyond the initial entry all start with a
+ section header, but we've seen ISOs in the wild where this isn't the case.
+ Add a test to ensure that these entries still work.
+5. Add a test for when the El Torito boot catalog record is in a Rock Ridge
+ Continuation Entry.
+6. Add a test for when the El Torito boot catalog record is in a Rock Ridge
+ relocated directory record.
+7. Break the API for progress_cb(), by making sure the user always has to
+ have a function with a 3rd opaque argument.
+8. Allow removal of El Torito entries without removing El Torito altogether.
+ To do this, we'll need to look up the bootfile when removing and only
+ remove the entry that corresponds to it, or the whole thing if it is
+ the initial entry.
+9. Show the "logical" Rock Ridge tree in pycdlib-explorer when the user
+ requests the "rr" print mode. This essentially means relocating Rock Ridge
+ deep directory trees as appropriate.
+10. Support the genisoimage options to allow relaxed ISO9660 filenames.
+11. Add parsing tests against xorriso.
+12. Remove the deprecated methods get_and_write(), get_and_write_fp(),
+ add_joliet_directory(), rm_joliet_directory(), list_dir(), and get_entry().
+13. Add the ability to hard-link from El Torito Initial and Section entries to
+ ISO9660, Joliet, and UDF.
+14. It should be possible to implement add_hard_link_in_place() and
+ rm_hard_link_in_place(), provided that the addition/removal doesn't affect
+ the number of extents for the parent DirectoryRecord/UDF File Entry.
+15. Add support for the rest of the "walk" API to make it more compatible with
+ os.walk(). In particular, support the topdown, onerror, and followlinks
+ flags.
+16. It is apparently possible to make an ISO that has *only* El Torito floppy
+ booting on it (with no PVD and hence no filesystem; see
+ http://www.menuetos.net/cdboot.htm for an example). This is pretty
+ esoteric, but might be interesting to support creating/parsing.
+17. According to this post: http://reboot.pro/topic/21664-makeiso/?p=205285, the
+ sizes specified for El Torito floppy disk sizes aren't actual sizes, but
+ are instead specified as T/H/S, where H and S are fixed and column
+ corresponds to 'size'. Thus, it should be possible to support booting from
+ so-called 'super-floppies'. This doesn't entirely jive with the El Torito
+ specification, but apparently most modern BIOSs support this. We should
+ fix PyCdlib for this.
diff --git a/devtools/contrib/pycdlib/CHANGELOG b/devtools/contrib/pycdlib/CHANGELOG
new file mode 100644
index 00000000000..a20c478592b
--- /dev/null
+++ b/devtools/contrib/pycdlib/CHANGELOG
@@ -0,0 +1,304 @@
+Changelog for PyCdlib
+
+1.15.0 (2025-03-02)
+-------------------
+* Remove Python 2 compatibility
+* Add an encoding parameter to the walk API so non-standard ISOs can be used
+* Fix ISOs that only have a single extent for Path Tables
+* Add the 'udf_path' argument to add_file so UDF ISOs can add paths
+* Fix timestamps for UDF
+
+1.14.0 (2023-01-14)
+-------------------
+* Fix Python 2 compatibility
+* Fix iteration over deep directories
+* Relax some error checking to allow non-standards-compliant ISOs to be read
+* Fix year rollover in the Australia timezone
+
+1.13.0 (2022-06-20)
+-------------------
+* Fix the use of inspect for modern Python 3
+* Add in pycdlib-explorer functionality for modify-in-place
+* Add full support for Windows raw block devices
+* Fix UDF entries for large files
+* Allow some UDF errors to print a warning instead of throwing
+* Search for UDF anchors in additional locations
+* Fix UDF partition map parsing
+
+1.12.0 (2021-08-10)
+-------------------
+* Several fixes to work with raw Windows devices
+* Fix ISOs with missing Apple partition map entries
+* Relax restrictions on dates hundredthsofseconds
+* Fix MBR boot code sector size
+* More UDF cleanups
+
+1.11.0 (2020-10-07)
+-------------------
+* Add many more unit tests to get test coverage > 95%
+* Preserve boot record contents when moving it
+* Make sure to allocate an extent for the boot_catalog in all cases
+* Fix deep directory depths with ISO level 4
+* Rewrite XA handling
+* Improve performance of filename checking
+* Add support for the AL record
+
+1.10.0 (2020-05-24)
+-------------------
+* Much improved IsoHybrid support for EFI/GTP
+* Fix large, multi-extent files
+* Fix support for very large ISOs
+* Remove the unused and unloved pycdlib-compare tool
+* Fix ISO9660 date hundredths of second
+
+1.9.0 (2019-12-20)
+------------------
+* Much improved support for UDF
+* Switch to read-only open by default for the open() call
+* Fixes to work on big-endian machines
+* APIs added:
+ * file_mode
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.8.0 (2019-08-10)
+------------------
+* Addition of "facades" for using PyCdlib in simpler ways
+* Make iso_path optional for UDF when using add_symlink
+* Make iso_path optional when using rm_file()
+* Make iso_path optional when using add_fp()/add_file()
+* Create symlinks appropriately in pycdlib-extract-files
+* Fix copy_data on macOS
+* Support more of the UDF standard
+* Cleanup documentation
+* Allow UEFI El Torito identifier
+* Add an 'auto' mode for pycdlib-extract-files
+* Add an '-extract-to' option for pycdlib-extract-files
+* APIs added:
+ * get_iso9660_facade
+ * get_joliet_facade
+ * get_rock_ridge_facade
+ * get_udf_facade
+ * has_rockridge
+ * has_joliet
+ * has_udf
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.7.0 (2019-02-27)
+------------------
+* Better UDF compatibility
+* Add a tool to extract files with pycdlib
+* Greatly improve performance of pycdlib-genisoimage duplicate detection
+* Add a design document to the documentation
+* Better support for Rock Ridge SF records
+* Add type annotation to the entire codebase
+* Fix a bug with UDF directory sorting
+* Many more unit tests added
+* APIs added:
+ * walk()
+ * open_file_from_iso()
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.6.0 (2018-07-29)
+------------------
+* More fixes for better UDF compatibility
+* Internal revamp of UDF to move some functionality into the classes
+* Share more code internally
+* More tests
+* Better UDF support in pycdlib-explorer
+* More documentation, mostly in pycdlib-explorer
+* Python 3.4 compatibility
+* APIs added:
+ None
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.5.0 (2018-06-23)
+------------------
+* Many more fixes for UDF compatibility
+* Support for arbitrarily many hard-links between El Torito, Joliet, ISO9660, and UDF
+* Increase test coverage
+* Add support for duplicate file detection in pycdlib-genisoimage
+* Add support for Rock Ridge version 1.10
+* Fix up unicode handling of filenames
+* Minor performance optimizations
+* APIs added:
+ None
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.4.0 (2018-05-04)
+------------------
+* Lots more updates to pycdlib-genisoimage
+* Support for absolute Rock Ridge symlinks
+* Initial UDF support
+* Documentation updates
+* Performance optimizations
+* Increase test coverage
+* Deprecate a bunch of methods
+* Support ISOs that report incorrect PVD sizes
+* Initial UDF support
+* APIs added:
+ * get_file_from_iso()
+ * get_file_from_iso_fp()
+ * list_children()
+ * get_record()
+ * set_relocated_name()
+* APIs removed:
+ None
+* APIs deprecated:
+ * get_and_write()
+ * get_and_write_fp()
+ * add_joliet_directory()
+ * rm_joliet_directory()
+ * list_dir()
+ * get_entry()
+
+1.3.2 (2017-11-20)
+------------------
+* Switch to python3 for the tools
+* Small man page fixes
+* APIs added:
+ None
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.3.1 (2017-11-20)
+------------------
+* Small fixes to fix up RPM packaging
+* APIs added:
+ None
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.3.0 (2017-11-19)
+------------------
+* Performance optimizations
+* Support for Joliet levels 1, 2, and 3
+* Very large file support (>4GB)
+* Increase test coverage
+* Add high-level documentation, hosted at https://clalancette.github.io/pycdlib/
+* Make API documentation available
+* APIs added:
+ * add_joliet_directory()
+ * rm_joliet_directory()
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.2.0 (2017-09-07)
+------------------
+* First version of pycdlib-genisoimage, a drop in replacement for genisoimage
+* Fixes to allow pycdlib to deal with a lot more files
+* Support for floppy and HD booting in El Torito
+* Add in an exception hierarchy: PyCdlibException -> PyCdlibInvalidInput, PyCdlibInvalidISO, PyCdlibInternalError
+* "Lazy" metadata updating makes this release much faster than previous ones
+* Support for more types of ISOs, including those that don't fully conform to standards
+* Increase test coverage
+* Add lots more functionality to pycdlib-genisoimage
+* Lots of optimizations all over the tree
+* APIs added:
+ * set_hidden()
+ * clear_hidden()
+ * force_consistency()
+* APIs removed:
+ None
+* APIs deprecated:
+ None
+
+1.1.0 (2017-01-31)
+------------------
+* Better documentation
+* Fix up isohybrid and tests
+* Start of isohybrid MAC support
+* Fix compatibility with Python3 < 3.5
+* Cleanup RPM spec file
+* Add man pages for the tools
+* Add support for very long Rock Ridge filenames
+* Lots of fixes to Rock Ridge symlink handling
+* Increase test coverage
+* APIs added:
+ * None
+* APIs removed:
+ * add_isohybrid_fp()
+* APIs deprecated:
+ None
+
+1.0.0 (2016-10-25)
+------------------
+* First stable release with API guarantees
+* Rename library to PyCdlib to avoid name clashes
+* Many fixes to El Torito handling
+* Performance fixes
+* Compatibility fixes for ISO9660
+* Add an API to allow modifying files in place
+* More testing all over the tree
+* Add support for ISO9660 interchange level 4
+* Add support for "hard-links" between files
+* Add support for Rock Ridge 1.12
+* Dual Python2/Python3 support
+* APIs added:
+ * open_fp()
+ * get_and_write_fp()
+ * write_fp()
+ * add_file()
+ * modify_file_in_place()
+ * add_hard_link()
+ * rm_hard_link()
+ * add_isohybrid_fp()
+ * full_path_from_dirrecord()
+ * duplicate_pvd()
+* APIs removed:
+ * print_tree()
+* APIs deprecated:
+ None
+
+0.5.0 (2016-01-27)
+------------------
+* First release of PyCdlib (still called PyISO at the time)
+* Initial support for ISO9660, interchange levels 1, 2, and 3
+* Initial support for Joliet
+* Initial support for Rock Ridge
+* Initial support for El Torito
+* Initial support for isohybrid
+* Created pyiso-compare tool for comparing two ISOs
+* Created pyiso-explorer tool for examining ISOs
+* APIs added:
+ * new()
+ * open()
+ * print_tree()
+ * get_and_write()
+ * write()
+ * add_fp()
+ * add_directory()
+ * rm_file()
+ * rm_directory()
+ * add_eltorito()
+ * rm_eltorito()
+ * add_symlink()
+ * list_dir()
+ * get_entry()
+ * add_isohybrid()
+ * rm_isohybrid()
+ * close()
+* APIs removed:
+ None
+* APIs deprecated:
+ None
diff --git a/devtools/contrib/pycdlib/COPYING b/devtools/contrib/pycdlib/COPYING
new file mode 100644
index 00000000000..7bfe8a1b7b0
--- /dev/null
+++ b/devtools/contrib/pycdlib/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation;
+ version 2.1 of the License.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/devtools/contrib/pycdlib/MANIFEST.in b/devtools/contrib/pycdlib/MANIFEST.in
new file mode 100644
index 00000000000..78cb4cb466a
--- /dev/null
+++ b/devtools/contrib/pycdlib/MANIFEST.in
@@ -0,0 +1,4 @@
+include COPYING README.md pytest.ini
+include examples/*
+include tests/*.py
+include tests/**/*.py
diff --git a/devtools/contrib/pycdlib/Makefile b/devtools/contrib/pycdlib/Makefile
new file mode 100644
index 00000000000..b272b72970d
--- /dev/null
+++ b/devtools/contrib/pycdlib/Makefile
@@ -0,0 +1,57 @@
+clean:
+ rm -rf htmlcov python-pycdlib.spec dist MANIFEST .coverage profile build *.lprof .mypy_cache
+ find . -iname '*~' -exec rm -f {} \;
+ find . -iname '*.pyc' -exec rm -f {} \;
+
+deb:
+ debuild -i -uc -us -b
+
+docs:
+ groff -mandoc -Thtml man/pycdlib-explorer.1 > docs/pycdlib-explorer.html
+ groff -mandoc -Thtml man/pycdlib-extract-files.1 > docs/pycdlib-extract-files.html
+ groff -mandoc -Thtml man/pycdlib-genisoimage.1 > docs/pycdlib-genisoimage.html
+ python3 custom-pydoc.py > docs/pycdlib-api.html
+
+flake8:
+ -flake8-3 --ignore=E501,E266 --max-complexity 80 pycdlib tools/*
+
+# kernprof-3 comes from the "line_profiler" package. It allows performance
+# profiling on a line-by-line basis, but needs to be told which functions to
+# profile by using an "@profile" decorator on particular functions. The easiest
+# way to use this is to profile using the built-in cProfile module (like the
+# "profile" target), then mark the hotspots with "@profile", and then run
+# the "lineprof" target.
+lineprof:
+ kernprof-3 -v -l /usr/bin/py.test-3 --verbose tests
+
+mypy:
+ mypy --ignore-missing-imports -m pycdlib
+
+profile:
+ python3 -m cProfile -o profile /usr/bin/py.test-3 --verbose tests
+ python3 -c "import pstats; p=pstats.Stats('profile');p.strip_dirs();p.sort_stats('time').print_stats(30)"
+
+pylint:
+ -pylint-3 --rcfile=pylint.conf pycdlib tools/*
+
+rpm: sdist
+ rpmbuild -ba python-pycdlib.spec --define "_sourcedir `pwd`/dist"
+
+sdist:
+ python3 setup.py sdist
+
+slowtests:
+ PYCDLIB_TRACK_WRITES=1 py.test-3 --basetemp=/var/tmp/pycdlib-tests --runslow --verbose tests
+
+srpm: sdist
+ rpmbuild -bs python-pycdlib.spec --define "_sourcedir `pwd`/dist"
+
+test-coverage:
+ PYCDLIB_TRACK_WRITES=1 coverage3 run --source pycdlib /usr/bin/py.test-3 --basetemp=/var/tmp/pycdlib-tests --runslow --verbose tests
+ coverage3 html
+ xdg-open htmlcov/index.html
+
+tests:
+ py.test-3 --verbose tests
+
+.PHONY: clean deb docs flake8 lineprof mypy profile pylint rpm sdist slowtests srpm test-coverage tests
diff --git a/devtools/contrib/pycdlib/README.md b/devtools/contrib/pycdlib/README.md
new file mode 100644
index 00000000000..70acd61ad8d
--- /dev/null
+++ b/devtools/contrib/pycdlib/README.md
@@ -0,0 +1,5 @@
+PyCdlib is a pure python library to parse, write (master), and create ISO9660 files, suitable for writing to a CD or USB.
+
+The original ISO9660 (including ISO9660-1999) specification is supported, as well the El Torito, Joliet, Rock Ridge, and UDF extensions.
+
+Please see https://clalancette.github.io/pycdlib/ for much more documentation.
diff --git a/devtools/contrib/pycdlib/custom-pydoc.py b/devtools/contrib/pycdlib/custom-pydoc.py
new file mode 100755
index 00000000000..47c14e39191
--- /dev/null
+++ b/devtools/contrib/pycdlib/custom-pydoc.py
@@ -0,0 +1,1026 @@
+# As of Python 3.11, the ability to write out API documentation with the
+# pydoc module (the equivalent of 'pydoc -w <module>') seems completely
+# broken. The code below is a lightly-modified version of the pydoc
+# module that makes this actually work. It then goes on to remove the "Modules"
+# section and the absolute links.
+
+import __future__
+import builtins
+import collections
+import importlib
+import inspect
+import os
+import platform
+import re
+import reprlib
+import sys
+import sysconfig
+import urllib.parse
+
+def _split_list(s, predicate):
+ """Split sequence s via predicate, and return pair ([true], [false]).
+
+ The return value is a 2-tuple of lists,
+ ([x for x in s if predicate(x)],
+ [x for x in s if not predicate(x)])
+ """
+
+ yes = []
+ no = []
+ for x in s:
+ if predicate(x):
+ yes.append(x)
+ else:
+ no.append(x)
+ return yes, no
+
+_future_feature_names = set(__future__.all_feature_names)
+
+def _is_bound_method(fn):
+ """
+ Returns True if fn is a bound method, regardless of whether
+ fn was implemented in Python or in C.
+ """
+ if inspect.ismethod(fn):
+ return True
+ if inspect.isbuiltin(fn):
+ self = getattr(fn, '__self__', None)
+ return not (inspect.ismodule(self) or (self is None))
+ return False
+
+def classify_class_attrs(object):
+ """Wrap inspect.classify_class_attrs, with fixup for data descriptors and bound methods."""
+ results = []
+ for (name, kind, cls, value) in inspect.classify_class_attrs(object):
+ if inspect.isdatadescriptor(value):
+ kind = 'data descriptor'
+ if isinstance(value, property) and value.fset is None:
+ kind = 'readonly property'
+ elif kind == 'method' and _is_bound_method(value):
+ kind = 'static method'
+ results.append((name, kind, cls, value))
+ return results
+
+def sort_attributes(attrs, object):
+ 'Sort the attrs list in-place by _fields and then alphabetically by name'
+ # This allows data descriptors to be ordered according
+ # to a _fields attribute if present.
+ fields = getattr(object, '_fields', [])
+ try:
+ field_order = {name : i-len(fields) for (i, name) in enumerate(fields)}
+ except TypeError:
+ field_order = {}
+ keyfunc = lambda attr: (field_order.get(attr[0], 0), attr[0])
+ attrs.sort(key=keyfunc)
+
+def _findclass(func):
+ cls = sys.modules.get(func.__module__)
+ if cls is None:
+ return None
+ for name in func.__qualname__.split('.')[:-1]:
+ cls = getattr(cls, name)
+ if not inspect.isclass(cls):
+ return None
+ return cls
+
+def _finddoc(obj):
+ if inspect.ismethod(obj):
+ name = obj.__func__.__name__
+ self = obj.__self__
+ if (inspect.isclass(self) and
+ getattr(getattr(self, name, None), '__func__') is obj.__func__):
+ # classmethod
+ cls = self
+ else:
+ cls = self.__class__
+ elif inspect.isfunction(obj):
+ name = obj.__name__
+ cls = _findclass(obj)
+ if cls is None or getattr(cls, name) is not obj:
+ return None
+ elif inspect.isbuiltin(obj):
+ name = obj.__name__
+ self = obj.__self__
+ if (inspect.isclass(self) and
+ self.__qualname__ + '.' + name == obj.__qualname__):
+ # classmethod
+ cls = self
+ else:
+ cls = self.__class__
+ # Should be tested before isdatadescriptor().
+ elif isinstance(obj, property):
+ func = obj.fget
+ name = func.__name__
+ cls = _findclass(func)
+ if cls is None or getattr(cls, name) is not obj:
+ return None
+ elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
+ name = obj.__name__
+ cls = obj.__objclass__
+ if getattr(cls, name) is not obj:
+ return None
+ if inspect.ismemberdescriptor(obj):
+ slots = getattr(cls, '__slots__', None)
+ if isinstance(slots, dict) and name in slots:
+ return slots[name]
+ else:
+ return None
+ for base in cls.__mro__:
+ try:
+ doc = _getowndoc(getattr(base, name))
+ except AttributeError:
+ continue
+ if doc is not None:
+ return doc
+ return None
+
+def _getowndoc(obj):
+ """Get the documentation string for an object if it is not
+ inherited from its class."""
+ try:
+ doc = object.__getattribute__(obj, '__doc__')
+ if doc is None:
+ return None
+ if obj is not type:
+ typedoc = type(obj).__doc__
+ if isinstance(typedoc, str) and typedoc == doc:
+ return None
+ return doc
+ except AttributeError:
+ return None
+
+def _getdoc(object):
+ """Get the documentation string for an object.
+
+ All tabs are expanded to spaces. To clean up docstrings that are
+ indented to line up with blocks of code, any whitespace than can be
+ uniformly removed from the second line onwards is removed."""
+ doc = _getowndoc(object)
+ if doc is None:
+ try:
+ doc = _finddoc(object)
+ except (AttributeError, TypeError):
+ return None
+ if not isinstance(doc, str):
+ return None
+ return inspect.cleandoc(doc)
+
+def getdoc(object):
+ """Get the doc string or comments for an object."""
+ result = _getdoc(object) or inspect.getcomments(object)
+ return result and re.sub('^ *\n', '', result.rstrip()) or ''
+
+def isdata(object):
+ """Check if an object is of a type that probably means it's data."""
+ return not (inspect.ismodule(object) or inspect.isclass(object) or
+ inspect.isroutine(object) or inspect.isframe(object) or
+ inspect.istraceback(object) or inspect.iscode(object))
+
+def visiblename(name, all=None, obj=None):
+ """Decide whether to show documentation on a variable."""
+ # Certain special names are redundant or internal.
+ # XXX Remove __initializing__?
+ if name in {'__author__', '__builtins__', '__cached__', '__credits__',
+ '__date__', '__doc__', '__file__', '__spec__',
+ '__loader__', '__module__', '__name__', '__package__',
+ '__path__', '__qualname__', '__slots__', '__version__'}:
+ return 0
+ # Private names are hidden, but special names are displayed.
+ if name.startswith('__') and name.endswith('__'): return 1
+ # Namedtuples have public fields and methods with a single leading underscore
+ if name.startswith('_') and hasattr(obj, '_fields'):
+ return True
+ # Ignore __future__ imports.
+ if obj is not __future__ and name in _future_feature_names:
+ if isinstance(getattr(obj, name, None), __future__._Feature):
+ return False
+ if all is not None:
+ # only document that which the programmer exported in __all__
+ return name in all
+ else:
+ return not name.startswith('_')
+
+def describe(thing):
+ """Produce a short description of the given thing."""
+ if inspect.ismodule(thing):
+ if thing.__name__ in sys.builtin_module_names:
+ return 'built-in module ' + thing.__name__
+ if hasattr(thing, '__path__'):
+ return 'package ' + thing.__name__
+ else:
+ return 'module ' + thing.__name__
+ if inspect.isbuiltin(thing):
+ return 'built-in function ' + thing.__name__
+ if inspect.isgetsetdescriptor(thing):
+ return 'getset descriptor %s.%s.%s' % (
+ thing.__objclass__.__module__, thing.__objclass__.__name__,
+ thing.__name__)
+ if inspect.ismemberdescriptor(thing):
+ return 'member descriptor %s.%s.%s' % (
+ thing.__objclass__.__module__, thing.__objclass__.__name__,
+ thing.__name__)
+ if inspect.isclass(thing):
+ return 'class ' + thing.__name__
+ if inspect.isfunction(thing):
+ return 'function ' + thing.__name__
+ if inspect.ismethod(thing):
+ return 'method ' + thing.__name__
+ return type(thing).__name__
+
+def safeimport(path, forceload=0, cache={}):
+ """Import a module; handle errors; return None if the module isn't found.
+
+ If the module *is* found but an exception occurs, it's wrapped in an
+ ErrorDuringImport exception and reraised. Unlike __import__, if a
+ package path is specified, the module at the end of the path is returned,
+ not the package at the beginning. If the optional 'forceload' argument
+ is 1, we reload the module from disk (unless it's a dynamic extension)."""
+ try:
+ # If forceload is 1 and the module has been previously loaded from
+ # disk, we always have to reload the module. Checking the file's
+ # mtime isn't good enough (e.g. the module could contain a class
+ # that inherits from another module that has changed).
+ if forceload and path in sys.modules:
+ if path not in sys.builtin_module_names:
+ # Remove the module from sys.modules and re-import to try
+ # and avoid problems with partially loaded modules.
+ # Also remove any submodules because they won't appear
+ # in the newly loaded module's namespace if they're already
+ # in sys.modules.
+ subs = [m for m in sys.modules if m.startswith(path + '.')]
+ for key in [path] + subs:
+ # Prevent garbage collection.
+ cache[key] = sys.modules[key]
+ del sys.modules[key]
+ module = importlib.import_module(path)
+ except BaseException as err:
+ # Did the error occur before or after the module was found?
+ if path in sys.modules:
+ # An error occurred while executing the imported module.
+ raise ErrorDuringImport(sys.modules[path].__file__, err)
+ elif type(err) is SyntaxError:
+ # A SyntaxError occurred before we could execute the module.
+ raise ErrorDuringImport(err.filename, err)
+ elif isinstance(err, ImportError) and err.name == path:
+ # No such module in the path.
+ return None
+ else:
+ # Some other error occurred during the importing process.
+ raise ErrorDuringImport(path, err)
+ return module
+
+def locate(path, forceload=0):
+ """Locate an object by name or dotted path, importing as necessary."""
+ parts = [part for part in path.split('.') if part]
+ module, n = None, 0
+ while n < len(parts):
+ nextmodule = safeimport('.'.join(parts[:n+1]), forceload)
+ if nextmodule: module, n = nextmodule, n + 1
+ else: break
+ if module:
+ object = module
+ else:
+ object = builtins
+ for part in parts[n:]:
+ try:
+ object = getattr(object, part)
+ except AttributeError:
+ return None
+ return object
+
+class Doc:
+
+ PYTHONDOCS = os.environ.get("PYTHONDOCS",
+ "https://docs.python.org/%d.%d/library"
+ % sys.version_info[:2])
+
+ def document(self, object, name=None, *args):
+ """Generate documentation for an object."""
+ args = (object, name) + args
+ # 'try' clause is to attempt to handle the possibility that inspect
+ # identifies something in a way that pydoc itself has issues handling;
+ # think 'super' and how it is a descriptor (which raises the exception
+ # by lacking a __name__ attribute) and an instance.
+ try:
+ if inspect.ismodule(object): return self.docmodule(*args)
+ if inspect.isclass(object): return self.docclass(*args)
+ if inspect.isroutine(object): return self.docroutine(*args)
+ except AttributeError:
+ pass
+ if inspect.isdatadescriptor(object): return self.docdata(*args)
+ return self.docother(*args)
+
+ def fail(self, object, name=None, *args):
+ """Raise an exception for unimplemented types."""
+ message = "don't know how to document object%s of type %s" % (
+ name and ' ' + repr(name), type(object).__name__)
+ raise TypeError(message)
+
+ docmodule = docclass = docroutine = docother = docproperty = docdata = fail
+
+ def getdocloc(self, object, basedir=sysconfig.get_path('stdlib')):
+ """Return the location of module docs or None"""
+
+ try:
+ file = inspect.getabsfile(object)
+ except TypeError:
+ file = '(built-in)'
+
+ docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS)
+
+ basedir = os.path.normcase(basedir)
+ if (isinstance(object, type(os)) and
+ (object.__name__ in ('errno', 'exceptions', 'gc',
+ 'marshal', 'posix', 'signal', 'sys',
+ '_thread', 'zipimport') or
+ (file.startswith(basedir) and
+ not file.startswith(os.path.join(basedir, 'site-packages')))) and
+ object.__name__ not in ('xml.etree', 'test.test_pydoc.pydoc_mod')):
+ if docloc.startswith(("http://", "https://")):
+ docloc = "{}/{}.html".format(docloc.rstrip("/"), object.__name__.lower())
+ else:
+ docloc = os.path.join(docloc, object.__name__.lower() + ".html")
+ else:
+ docloc = None
+ return docloc
+
+def classname(object, modname):
+ """Get a class name and qualify it with a module name if necessary."""
+ name = object.__name__
+ if object.__module__ != modname:
+ name = object.__module__ + '.' + name
+ return name
+
+def replace(text, *pairs):
+ """Do a series of global replacements on a string."""
+ while pairs:
+ text = pairs[1].join(text.split(pairs[0]))
+ pairs = pairs[2:]
+ return text
+
+class HTMLRepr(reprlib.Repr):
+ """Class for safely making an HTML representation of a Python object."""
+ def __init__(self):
+ reprlib.Repr.__init__(self)
+ self.maxlist = self.maxtuple = 20
+ self.maxdict = 10
+ self.maxstring = self.maxother = 100
+
+ def escape(self, text):
+ return replace(text, '&', '&', '<', '<', '>', '>')
+
+ def repr(self, object):
+ return reprlib.Repr.repr(self, object)
+
+ def repr1(self, x, level):
+ if hasattr(type(x), '__name__'):
+ methodname = 'repr_' + '_'.join(type(x).__name__.split())
+ if hasattr(self, methodname):
+ return getattr(self, methodname)(x, level)
+ return self.escape(cram(stripid(repr(x)), self.maxother))
+
+ def repr_string(self, x, level):
+ test = cram(x, self.maxstring)
+ testrepr = repr(test)
+ if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
+ # Backslashes are only literal in the string and are never
+ # needed to make any special characters, so show a raw string.
+ return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
+ return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
+ r'<span class="repr">\1</span>',
+ self.escape(testrepr))
+
+ repr_str = repr_string
+
+ def repr_instance(self, x, level):
+ try:
+ return self.escape(cram(stripid(repr(x)), self.maxstring))
+ except:
+ return self.escape('<%s instance>' % x.__class__.__name__)
+
+ repr_unicode = repr_string
+
+class HTMLDoc(Doc):
+ """Formatter class for HTML documentation."""
+
+ # ------------------------------------------- HTML formatting utilities
+
+ _repr_instance = HTMLRepr()
+ repr = _repr_instance.repr
+ escape = _repr_instance.escape
+
+ def page(self, title, contents):
+ """Format an HTML page."""
+ return '''\
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Python: %s</title>
+</head><body>
+%s
+</body></html>''' % (title, contents)
+
+ def heading(self, title, extras=''):
+ """Format a page heading."""
+ return '''
+<table class="heading">
+<tr class="heading-text decor">
+<td class="title"> <br>%s</td>
+<td class="extra">%s</td></tr></table>
+ ''' % (title, extras or ' ')
+
+ def section(self, title, cls, contents, width=6,
+ prelude='', marginalia=None, gap=' '):
+ """Format a section with a heading."""
+ if marginalia is None:
+ marginalia = '<span class="code">' + ' ' * width + '</span>'
+ result = '''<p>
+<table class="section">
+<tr class="decor %s-decor heading-text">
+<td class="section-title" colspan=3> <br>%s</td></tr>
+ ''' % (cls, title)
+ if prelude:
+ result = result + '''
+<tr><td class="decor %s-decor" rowspan=2>%s</td>
+<td class="decor %s-decor" colspan=2>%s</td></tr>
+<tr><td>%s</td>''' % (cls, marginalia, cls, prelude, gap)
+ else:
+ result = result + '''
+<tr><td class="decor %s-decor">%s</td><td>%s</td>''' % (cls, marginalia, gap)
+
+ return result + '\n<td class="singlecolumn">%s</td></tr></table>' % contents
+
+ def bigsection(self, title, *args):
+ """Format a section with a big heading."""
+ title = '<strong class="bigsection">%s</strong>' % title
+ return self.section(title, *args)
+
+ def preformat(self, text):
+ """Format literal preformatted text."""
+ text = self.escape(text.expandtabs())
+ return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
+ ' ', ' ', '\n', '<br>\n')
+
+ def multicolumn(self, list, format):
+ """Format a list of items into a multi-column list."""
+ result = ''
+ rows = (len(list) + 3) // 4
+ for col in range(4):
+ result = result + '<td class="multicolumn">'
+ for i in range(rows*col, rows*col+rows):
+ if i < len(list):
+ result = result + format(list[i]) + '<br>\n'
+ result = result + '</td>'
+ return '<table><tr>%s</tr></table>' % result
+
+ def grey(self, text): return '<span class="grey">%s</span>' % text
+
+ def namelink(self, name, *dicts):
+ """Make a link for an identifier, given name-to-URL mappings."""
+ for dict in dicts:
+ if name in dict:
+ return '<a href="%s">%s</a>' % (dict[name], name)
+ return name
+
+ def classlink(self, object, modname):
+ """Make a link for a class."""
+ name, module = object.__name__, sys.modules.get(object.__module__)
+ if hasattr(module, name) and getattr(module, name) is object:
+ return '<a href="%s.html#%s">%s</a>' % (
+ module.__name__, name, classname(object, modname))
+ return classname(object, modname)
+
+ def parentlink(self, object, modname):
+ """Make a link for the enclosing class or module."""
+ link = None
+ name, module = object.__name__, sys.modules.get(object.__module__)
+ if hasattr(module, name) and getattr(module, name) is object:
+ if '.' in object.__qualname__:
+ name = object.__qualname__.rpartition('.')[0]
+ if object.__module__ != modname:
+ link = '%s.html#%s' % (module.__name__, name)
+ else:
+ link = '#%s' % name
+ else:
+ if object.__module__ != modname:
+ link = '%s.html' % module.__name__
+ if link:
+ return '<a href="%s">%s</a>' % (link, parentname(object, modname))
+ else:
+ return parentname(object, modname)
+
+ def modulelink(self, object):
+ """Make a link for a module."""
+ return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
+
+ def modpkglink(self, modpkginfo):
+ """Make a link for a module or package to display in an index."""
+ name, path, ispackage, shadowed = modpkginfo
+ if shadowed:
+ return self.grey(name)
+ if path:
+ url = '%s.%s.html' % (path, name)
+ else:
+ url = '%s.html' % name
+ if ispackage:
+ text = '<strong>%s</strong> (package)' % name
+ else:
+ text = name
+ return '<a href="%s">%s</a>' % (url, text)
+
+ def filelink(self, url, path):
+ """Make a link to source file."""
+ return '<a href="file:%s">%s</a>' % (url, path)
+
+ def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
+ """Mark up some plain text, given a context of symbols to look for.
+ Each context dictionary maps object names to anchor names."""
+ escape = escape or self.escape
+ results = []
+ here = 0
+ pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|'
+ r'RFC[- ]?(\d+)|'
+ r'PEP[- ]?(\d+)|'
+ r'(self\.)?(\w+))')
+ while match := pattern.search(text, here):
+ start, end = match.span()
+ results.append(escape(text[here:start]))
+
+ all, scheme, rfc, pep, selfdot, name = match.groups()
+ if scheme:
+ url = escape(all).replace('"', '"')
+ results.append('<a href="%s">%s</a>' % (url, url))
+ elif rfc:
+ url = 'https://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
+ results.append('<a href="%s">%s</a>' % (url, escape(all)))
+ elif pep:
+ url = 'https://peps.python.org/pep-%04d/' % int(pep)
+ results.append('<a href="%s">%s</a>' % (url, escape(all)))
+ elif selfdot:
+ # Create a link for methods like 'self.method(...)'
+ # and use <strong> for attributes like 'self.attr'
+ if text[end:end+1] == '(':
+ results.append('self.' + self.namelink(name, methods))
+ else:
+ results.append('self.<strong>%s</strong>' % name)
+ elif text[end:end+1] == '(':
+ results.append(self.namelink(name, methods, funcs, classes))
+ else:
+ results.append(self.namelink(name, classes))
+ here = end
+ results.append(escape(text[here:]))
+ return ''.join(results)
+
+ # ---------------------------------------------- type-specific routines
+
+ def formattree(self, tree, modname, parent=None):
+ """Produce HTML for a class tree as given by inspect.getclasstree()."""
+ result = ''
+ for entry in tree:
+ if isinstance(entry, tuple):
+ c, bases = entry
+ result = result + '<dt class="heading-text">'
+ result = result + self.classlink(c, modname)
+ if bases and bases != (parent,):
+ parents = []
+ for base in bases:
+ parents.append(self.classlink(base, modname))
+ result = result + '(' + ', '.join(parents) + ')'
+ result = result + '\n</dt>'
+ elif isinstance(entry, list):
+ result = result + '<dd>\n%s</dd>\n' % self.formattree(
+ entry, modname, c)
+ return '<dl>\n%s</dl>\n' % result
+
+ def docmodule(self, object, name=None, mod=None, *ignored):
+ """Produce HTML documentation for a module object."""
+ name = object.__name__ # ignore the passed-in name
+ try:
+ all = object.__all__
+ except AttributeError:
+ all = None
+ parts = name.split('.')
+ links = []
+ for i in range(len(parts)-1):
+ links.append(
+ '<a href="%s.html" class="white">%s</a>' %
+ ('.'.join(parts[:i+1]), parts[i]))
+ linkedname = '.'.join(links + parts[-1:])
+ head = '<strong class="title">%s</strong>' % linkedname
+ try:
+ path = inspect.getabsfile(object)
+ url = urllib.parse.quote(path)
+ filelink = self.filelink(url, path)
+ except TypeError:
+ filelink = '(built-in)'
+ info = []
+ if hasattr(object, '__version__'):
+ version = str(object.__version__)
+ if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
+ version = version[11:-1].strip()
+ info.append('version %s' % self.escape(version))
+ if hasattr(object, '__date__'):
+ info.append(self.escape(str(object.__date__)))
+ if info:
+ head = head + ' (%s)' % ', '.join(info)
+ docloc = self.getdocloc(object)
+ if docloc is not None:
+ docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals()
+ else:
+ docloc = ''
+
+ result = self.heading(head, '')
+
+ classes, cdict = [], {}
+ for key, value in inspect.getmembers(object, inspect.isclass):
+ # if __all__ exists, believe it. Otherwise use old heuristic.
+ if (all is not None or
+ (inspect.getmodule(value) or object) is object):
+ if visiblename(key, all, object):
+ classes.append((key, value))
+ cdict[key] = cdict[value] = '#' + key
+ for key, value in classes:
+ for base in value.__bases__:
+ key, modname = base.__name__, base.__module__
+ module = sys.modules.get(modname)
+ if modname != name and module and hasattr(module, key):
+ if getattr(module, key) is base:
+ if not key in cdict:
+ cdict[key] = cdict[base] = modname + '.html#' + key
+ funcs, fdict = [], {}
+ for key, value in inspect.getmembers(object, inspect.isroutine):
+ # if __all__ exists, believe it. Otherwise use old heuristic.
+ if (all is not None or
+ inspect.isbuiltin(value) or inspect.getmodule(value) is object):
+ if visiblename(key, all, object):
+ funcs.append((key, value))
+ fdict[key] = '#-' + key
+ if inspect.isfunction(value): fdict[value] = fdict[key]
+ data = []
+ for key, value in inspect.getmembers(object, isdata):
+ if visiblename(key, all, object):
+ data.append((key, value))
+
+ doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
+ doc = doc and '<span class="code">%s</span>' % doc
+ result = result + '<p>%s</p>\n' % doc
+
+ if hasattr(object, '__path__'):
+ modpkgs = []
+ for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
+ modpkgs.append((modname, name, ispkg, 0))
+ modpkgs.sort()
+ contents = self.multicolumn(modpkgs, self.modpkglink)
+ result = result + self.bigsection(
+ 'Package Contents', 'pkg-content', contents)
+
+ if classes:
+ classlist = [value for (key, value) in classes]
+ contents = [
+ self.formattree(inspect.getclasstree(classlist, 1), name)]
+ for key, value in classes:
+ contents.append(self.document(value, key, name, fdict, cdict))
+ result = result + self.bigsection(
+ 'Classes', 'index', ' '.join(contents))
+ if funcs:
+ contents = []
+ for key, value in funcs:
+ contents.append(self.document(value, key, name, fdict, cdict))
+ result = result + self.bigsection(
+ 'Functions', 'functions', ' '.join(contents))
+ if data:
+ contents = []
+ for key, value in data:
+ contents.append(self.document(value, key))
+ result = result + self.bigsection(
+ 'Data', 'data', '<br>\n'.join(contents))
+ if hasattr(object, '__author__'):
+ contents = self.markup(str(object.__author__), self.preformat)
+ result = result + self.bigsection('Author', 'author', contents)
+ if hasattr(object, '__credits__'):
+ contents = self.markup(str(object.__credits__), self.preformat)
+ result = result + self.bigsection('Credits', 'credits', contents)
+
+ return result
+
+ def docclass(self, object, name=None, mod=None, funcs={}, classes={},
+ *ignored):
+ """Produce HTML documentation for a class object."""
+ realname = object.__name__
+ name = name or realname
+ bases = object.__bases__
+
+ contents = []
+ push = contents.append
+
+ # Cute little class to pump out a horizontal rule between sections.
+ class HorizontalRule:
+ def __init__(self):
+ self.needone = 0
+ def maybe(self):
+ if self.needone:
+ push('<hr>\n')
+ self.needone = 1
+ hr = HorizontalRule()
+
+ # List the mro, if non-trivial.
+ mro = collections.deque(inspect.getmro(object))
+ if len(mro) > 2:
+ hr.maybe()
+ push('<dl><dt>Method resolution order:</dt>\n')
+ for base in mro:
+ push('<dd>%s</dd>\n' % self.classlink(base,
+ object.__module__))
+ push('</dl>\n')
+
+ def spill(msg, attrs, predicate):
+ ok, attrs = _split_list(attrs, predicate)
+ if ok:
+ hr.maybe()
+ push(msg)
+ for name, kind, homecls, value in ok:
+ try:
+ value = getattr(object, name)
+ except Exception:
+ # Some descriptors may meet a failure in their __get__.
+ # (bug #1785)
+ push(self.docdata(value, name, mod))
+ else:
+ push(self.document(value, name, mod,
+ funcs, classes, mdict, object, homecls))
+ push('\n')
+ return attrs
+
+ def spilldescriptors(msg, attrs, predicate):
+ ok, attrs = _split_list(attrs, predicate)
+ if ok:
+ hr.maybe()
+ push(msg)
+ for name, kind, homecls, value in ok:
+ push(self.docdata(value, name, mod))
+ return attrs
+
+ def spilldata(msg, attrs, predicate):
+ ok, attrs = _split_list(attrs, predicate)
+ if ok:
+ hr.maybe()
+ push(msg)
+ for name, kind, homecls, value in ok:
+ base = self.docother(getattr(object, name), name, mod)
+ doc = getdoc(value)
+ if not doc:
+ push('<dl><dt>%s</dl>\n' % base)
+ else:
+ doc = self.markup(getdoc(value), self.preformat,
+ funcs, classes, mdict)
+ doc = '<dd><span class="code">%s</span>' % doc
+ push('<dl><dt>%s%s</dl>\n' % (base, doc))
+ push('\n')
+ return attrs
+
+ attrs = [(name, kind, cls, value)
+ for name, kind, cls, value in classify_class_attrs(object)
+ if visiblename(name, obj=object)]
+
+ mdict = {}
+ for key, kind, homecls, value in attrs:
+ mdict[key] = anchor = '#' + name + '-' + key
+ try:
+ value = getattr(object, name)
+ except Exception:
+ # Some descriptors may meet a failure in their __get__.
+ # (bug #1785)
+ pass
+ try:
+ # The value may not be hashable (e.g., a data attr with
+ # a dict or list value).
+ mdict[value] = anchor
+ except TypeError:
+ pass
+
+ while attrs:
+ if mro:
+ thisclass = mro.popleft()
+ else:
+ thisclass = attrs[0][2]
+ attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
+
+ if object is not builtins.object and thisclass is builtins.object:
+ attrs = inherited
+ continue
+ elif thisclass is object:
+ tag = 'defined here'
+ else:
+ tag = 'inherited from %s' % self.classlink(thisclass,
+ object.__module__)
+ tag += ':<br>\n'
+
+ sort_attributes(attrs, object)
+
+ # Pump out the attrs, segregated by kind.
+ attrs = spill('Methods %s' % tag, attrs,
+ lambda t: t[1] == 'method')
+ attrs = spill('Class methods %s' % tag, attrs,
+ lambda t: t[1] == 'class method')
+ attrs = spill('Static methods %s' % tag, attrs,
+ lambda t: t[1] == 'static method')
+ attrs = spilldescriptors("Readonly properties %s" % tag, attrs,
+ lambda t: t[1] == 'readonly property')
+ attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
+ lambda t: t[1] == 'data descriptor')
+ attrs = spilldata('Data and other attributes %s' % tag, attrs,
+ lambda t: t[1] == 'data')
+ assert attrs == []
+ attrs = inherited
+
+ contents = ''.join(contents)
+
+ if name == realname:
+ title = '<a name="%s">class <strong>%s</strong></a>' % (
+ name, realname)
+ else:
+ title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
+ name, name, realname)
+ if bases:
+ parents = []
+ for base in bases:
+ parents.append(self.classlink(base, object.__module__))
+ title = title + '(%s)' % ', '.join(parents)
+
+ decl = ''
+ try:
+ signature = inspect.signature(object)
+ except (ValueError, TypeError):
+ signature = None
+ if signature:
+ argspec = str(signature)
+ if argspec and argspec != '()':
+ decl = name + self.escape(argspec) + '\n\n'
+
+ doc = getdoc(object)
+ if decl:
+ doc = decl + (doc or '')
+ doc = self.markup(doc, self.preformat, funcs, classes, mdict)
+ doc = doc and '<span class="code">%s<br> </span>' % doc
+
+ return self.section(title, 'title', contents, 3, doc)
+
+ def formatvalue(self, object):
+ """Format an argument default value as text."""
+ return self.grey('=' + self.repr(object))
+
+ def docroutine(self, object, name=None, mod=None,
+ funcs={}, classes={}, methods={}, cl=None, homecls=None):
+ """Produce HTML documentation for a function or method object."""
+ realname = object.__name__
+ name = name or realname
+ if homecls is None:
+ homecls = cl
+ anchor = ('' if cl is None else cl.__name__) + '-' + name
+ note = ''
+ skipdocs = False
+ imfunc = None
+ if _is_bound_method(object):
+ imself = object.__self__
+ if imself is cl:
+ imfunc = getattr(object, '__func__', None)
+ elif inspect.isclass(imself):
+ note = ' class method of %s' % self.classlink(imself, mod)
+ else:
+ note = ' method of %s instance' % self.classlink(
+ imself.__class__, mod)
+ elif (inspect.ismethoddescriptor(object) or
+ inspect.ismethodwrapper(object)):
+ try:
+ objclass = object.__objclass__
+ except AttributeError:
+ pass
+ else:
+ if cl is None:
+ note = ' unbound %s method' % self.classlink(objclass, mod)
+ elif objclass is not homecls:
+ note = ' from ' + self.classlink(objclass, mod)
+ else:
+ imfunc = object
+ if inspect.isfunction(imfunc) and homecls is not None and (
+ imfunc.__module__ != homecls.__module__ or
+ imfunc.__qualname__ != homecls.__qualname__ + '.' + realname):
+ pname = self.parentlink(imfunc, mod)
+ if pname:
+ note = ' from %s' % pname
+
+ if (inspect.iscoroutinefunction(object) or
+ inspect.isasyncgenfunction(object)):
+ asyncqualifier = 'async '
+ else:
+ asyncqualifier = ''
+
+ if name == realname:
+ title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
+ else:
+ if (cl is not None and
+ inspect.getattr_static(cl, realname, []) is object):
+ reallink = '<a href="#%s">%s</a>' % (
+ cl.__name__ + '-' + realname, realname)
+ skipdocs = True
+ if note.startswith(' from '):
+ note = ''
+ else:
+ reallink = realname
+ title = '<a name="%s"><strong>%s</strong></a> = %s' % (
+ anchor, name, reallink)
+ argspec = None
+ if inspect.isroutine(object):
+ try:
+ signature = inspect.signature(object)
+ except (ValueError, TypeError):
+ signature = None
+ if signature:
+ argspec = str(signature)
+ if realname == '<lambda>':
+ title = '<strong>%s</strong> <em>lambda</em> ' % name
+ # XXX lambda's won't usually have func_annotations['return']
+ # since the syntax doesn't support but it is possible.
+ # So removing parentheses isn't truly safe.
+ if not object.__annotations__:
+ argspec = argspec[1:-1] # remove parentheses
+ if not argspec:
+ argspec = '(...)'
+
+ decl = asyncqualifier + title + self.escape(argspec) + (note and
+ self.grey('<span class="heading-text">%s</span>' % note))
+
+ if skipdocs:
+ return '<dl><dt>%s</dt></dl>\n' % decl
+ else:
+ doc = self.markup(
+ getdoc(object), self.preformat, funcs, classes, methods)
+ doc = doc and '<dd><span class="code">%s</span></dd>' % doc
+ return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
+
+ def docdata(self, object, name=None, mod=None, cl=None, *ignored):
+ """Produce html documentation for a data descriptor."""
+ results = []
+ push = results.append
+
+ if name:
+ push('<dl><dt><strong>%s</strong></dt>\n' % name)
+ doc = self.markup(getdoc(object), self.preformat)
+ if doc:
+ push('<dd><span class="code">%s</span></dd>\n' % doc)
+ push('</dl>\n')
+
+ return ''.join(results)
+
+ docproperty = docdata
+
+ def docother(self, object, name=None, mod=None, *ignored):
+ """Produce HTML documentation for a data object."""
+ lhs = name and '<strong>%s</strong> = ' % name or ''
+ return lhs + self.repr(object)
+
+ def index(self, dir, shadowed=None):
+ """Generate an HTML index for a directory of modules."""
+ modpkgs = []
+ if shadowed is None: shadowed = {}
+ for importer, name, ispkg in pkgutil.iter_modules([dir]):
+ if any((0xD800 <= ord(ch) <= 0xDFFF) for ch in name):
+ # ignore a module if its name contains a surrogate character
+ continue
+ modpkgs.append((name, '', ispkg, name in shadowed))
+ shadowed[name] = 1
+
+ modpkgs.sort()
+ contents = self.multicolumn(modpkgs, self.modpkglink)
+ return self.bigsection(dir, 'index', contents)
+
+class _HTMLDoc(HTMLDoc):
+
+ def page(self, title, contents):
+ """Format an HTML page."""
+ css_path = "pydoc_data/_pydoc.css"
+ css_link = (
+ '<link rel="stylesheet" type="text/css" href="%s">' %
+ css_path)
+ return '''\
+<!DOCTYPE>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Pydoc: %s</title>
+%s</head><body><div style="clear:both;padding-top:.5em;">%s</div>
+</body></html>''' % (title, css_link, contents)
+
+html = _HTMLDoc()
+
+def html_getobj(url):
+ obj = locate(url, forceload=1)
+ if obj is None and url != 'None':
+ raise ValueError('could not find object')
+ title = describe(obj)
+ content = html.document(obj, url)
+ return title, content
+
+title, content = html_getobj('pycdlib.pycdlib')
+print(html.page(title, content))
diff --git a/devtools/contrib/pycdlib/debian/changelog b/devtools/contrib/pycdlib/debian/changelog
new file mode 100644
index 00000000000..9b8b0181bf5
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/changelog
@@ -0,0 +1,113 @@
+pycdlib (1.15.0-1) UNRELEASED; urgency=low
+
+ * Release 1.15.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sun, 02 Mar 2025 21:00:00 -0500
+
+pycdlib (1.14.0-1) UNRELEASED; urgency=low
+
+ * Release 1.14.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sat, 14 Jan 2023 21:00:00 -0500
+
+pycdlib (1.13.0-1) UNRELEASED; urgency=low
+
+ * Release 1.13.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Mon, 20 Jun 2022 21:00:00 -0500
+
+pycdlib (1.12.0-1) UNRELEASED; urgency=low
+
+ * Release 1.12.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Tue, 10 Aug 2021 21:00:00 -0500
+
+pycdlib (1.11.0-1) UNRELEASED; urgency=low
+
+ * Release 1.11.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Wed, 07 Oct 2020 21:00:00 -0500
+
+pycdlib (1.10.0-1) UNRELEASED; urgency=low
+
+ * Release 1.10.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sun, 24 May 2020 21:00:00 -0500
+
+pycdlib (1.9.0-1) UNRELEASED; urgency=low
+
+ * Release 1.9.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Fri, 20 Aug 2019 21:00:00 -0500
+
+pycdlib (1.8.0-1) UNRELEASED; urgency=low
+
+ * Release 1.8.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sat, 10 Aug 2019 21:00:00 -0500
+
+pycdlib (1.7.0-1) UNRELEASED; urgency=low
+
+ * Release 1.7.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Wed, 27 Feb 2019 21:00:00 -0500
+
+pycdlib (1.6.0-1) UNRELEASED; urgency=low
+
+ * Release 1.6.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sun, 29 Jul 2018 21:00:00 -0500
+
+pycdlib (1.5.0-1) UNRELEASED; urgency=low
+
+ * Release 1.5.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sat, 23 Jun 2018 21:00:00 -0500
+
+pycdlib (1.4.0-1) UNRELEASED; urgency=low
+
+ * Release 1.4.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Fri, 04 May 2018 21:00:00 -0500
+
+pycdlib (1.3.2-1) UNRELEASED; urgency=low
+
+ * Release 1.3.2 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Mon, 20 Nov 2017 21:00:00 -0500
+
+pycdlib (1.3.1-1) UNRELEASED; urgency=low
+
+ * Release 1.3.1 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Mon, 20 Nov 2017 19:00:00 -0500
+
+pycdlib (1.3.0-1) UNRELEASED; urgency=low
+
+ * Release 1.3.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Sun, 19 Nov 2017 21:00:00 -0500
+
+pycdlib (1.2.0-1) UNRELEASED; urgency=low
+
+ * Release 1.2.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Tue, 03 Oct 2017 21:00:00 -0500
+
+pycdlib (1.1.0-1) UNRELEASED; urgency=low
+
+ * Release 1.1.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Tue, 31 Jan 2017 21:00:00 -0500
+
+pycdlib (1.0.0-1) UNRELEASED; urgency=low
+
+ * Release 1.0.0 of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Tue, 25 Oct 2016 21:00:00 -0500
+
+pycdlib (0.1.0-1) UNRELEASED; urgency=low
+
+ * Initial release of pycdlib
+
+ -- Chris Lalancette <clalancette at gmail.com> Thu, 19 Nov 2015 21:00:00 -0500
diff --git a/devtools/contrib/pycdlib/debian/compat b/devtools/contrib/pycdlib/debian/compat
new file mode 100644
index 00000000000..45a4fb75db8
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/compat
@@ -0,0 +1 @@
+8
diff --git a/devtools/contrib/pycdlib/debian/control b/devtools/contrib/pycdlib/debian/control
new file mode 100644
index 00000000000..17efe3162fd
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/control
@@ -0,0 +1,25 @@
+Source: pycdlib
+Maintainer: Chris Lalancette <clalancette at gmail.com>
+Section: python
+Priority: optional
+Build-Depends: debhelper (>= 8.0.0), python-all (>= 2.6.6-3), dh-python, python-setuptools, python3-all, python3-setuptools
+Standards-Version: 3.9.4
+Homepage: https://github.com/clalancette/pycdlib
+Vcs-Git: https://github.com/clalancette/pycdlib.git
+X-Python3-Version: >= 3.2
+
+Package: python3-pycdlib
+Architecture: all
+Depends: ${misc:Depends}, ${python3:Depends}
+Description: Pure python ISO9660 read and write library
+ Pycdlib is a pure python library for reading, writing, and otherwise
+ manipulating ISO9660 files. It is focused on speed, correctness, and
+ conformance to the various standards around ISO9660, including ISO9660 itself,
+ the Joliet extensions, the Rock Ridge extensions, the El Torito boot
+ extensions, and the UDF standard.
+
+Package: pycdlib-tools
+Depends: ${misc:Depends}, ${python:Depends}, python3-pycdlib, python3
+Architecture: all
+Description: Tools based on pycdlib
+ This package contains tools that depend on the pycdlib library to operate.
diff --git a/devtools/contrib/pycdlib/debian/copyright b/devtools/contrib/pycdlib/debian/copyright
new file mode 100644
index 00000000000..d03eea61cbc
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/copyright
@@ -0,0 +1,37 @@
+This work was packaged for Debian by:
+
+ Chris Lalancette <clalancette at gmail.com> on Thu, 19 Nov 2015 21:00:00 -0500
+
+Upstream Author:
+
+ Chris Lalancette <clalancette at gmail.com>
+
+Copyright:
+
+ Copyright (C) 2015 Chris Lalancette <clalancette at gmail.com>
+
+License:
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation;
+ version 2.1 of the License.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ USA
+
+On Debian systems, the complete text of the GNU General
+Public License version 2 can be found in "/usr/share/common-licenses/LGPL-2.1".
+
+The Debian packaging is:
+
+ Copyright (C) 2016 Chris Lalancette <clalancette at gmail.com>
+
+and is licensed under the LGPL version 2.1 or later, see above.
diff --git a/devtools/contrib/pycdlib/debian/pycdlib-tools.install b/devtools/contrib/pycdlib/debian/pycdlib-tools.install
new file mode 100644
index 00000000000..52005109f12
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/pycdlib-tools.install
@@ -0,0 +1,3 @@
+tools/pycdlib-explorer usr/bin
+tools/pycdlib-extract-files usr/bin
+tools/pycdlib-genisoimage usr/bin
diff --git a/devtools/contrib/pycdlib/debian/pycdlib-tools.manpages b/devtools/contrib/pycdlib/debian/pycdlib-tools.manpages
new file mode 100644
index 00000000000..cfc55509cb0
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/pycdlib-tools.manpages
@@ -0,0 +1,3 @@
+man/pycdlib-explorer.1
+man/pycdlib-extract-files.1
+man/pycdlib-genisoimage.1
diff --git a/devtools/contrib/pycdlib/debian/rules b/devtools/contrib/pycdlib/debian/rules
new file mode 100755
index 00000000000..d729c93ced0
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/rules
@@ -0,0 +1,6 @@
+#!/usr/bin/make -f
+
+export PYBUILD_NAME = pycdlib
+
+%:
+ dh $@ --buildsystem=pybuild --with=python2,python3
diff --git a/devtools/contrib/pycdlib/debian/watch b/devtools/contrib/pycdlib/debian/watch
new file mode 100644
index 00000000000..2ef0313787b
--- /dev/null
+++ b/devtools/contrib/pycdlib/debian/watch
@@ -0,0 +1,5 @@
+# Compulsory line, this is a version 3 file
+version=3
+
+https://github.com/clalancette/pycdlib/tags \
+ /clalancette/pycdlib/archive/v(.*).tar.gz
diff --git a/devtools/contrib/pycdlib/docs/README.md b/devtools/contrib/pycdlib/docs/README.md
new file mode 100644
index 00000000000..b9f2b34a363
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/README.md
@@ -0,0 +1,12 @@
+# PyCdlib
+PyCdlib is a pure python library to parse, write (master), create, and manipulate ISO9660 files. These files are suitable for writing to a CD or USB.
+
+The documentation for PyCdlib is split into several parts. If you are unfamiliar with the ISO and related standards, it is probably best to read the pages in order. If you already have a decent idea about ISO9660, Joliet, Rock Ridge, El Torito, and UDF, you can skip directly to the examples to find out how to use PyCdlib.
+
+* [Standards](standards.md)
+* [Python compatibility](python-compatibility.md)
+* [Examples](examples.md)
+* [Design](design.md)
+* [Tools](tools.md)
+* [Exceptions](exceptions.md)
+* [Reporting issues/bugs](reporting-issues.md)
diff --git a/devtools/contrib/pycdlib/docs/_config.yml b/devtools/contrib/pycdlib/docs/_config.yml
new file mode 100644
index 00000000000..c7418817439
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-slate
\ No newline at end of file
diff --git a/devtools/contrib/pycdlib/docs/design.md b/devtools/contrib/pycdlib/docs/design.md
new file mode 100644
index 00000000000..b8f89a1d290
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/design.md
@@ -0,0 +1,54 @@
+# Design
+
+## Overview
+The aim of PyCdlib is to be a pure Python library that can be used to easily interact with the filesystems that make up various optical disks (collectively known as "ISOs" throughout the rest of this document). This includes the original ISO9660 standard (also known as Ecma-119), the El Torito booting standard, the Joliet and Rock Ridge extensions, and the UDF filesystem.
+
+## Context
+The original motivation for writing the library was to replace the subprocess calls to genisoimage in [Oz](https://github.com/clalancette/oz) with something pure Python. During initial research, no suitably complete, Python-only ISO manipulation library was found. It was also discovered that [cdrkit](https://launchpad.net/cdrkit) (the upstream project that contains genisoimage) is dormant, embroiled in a somewhat bitter fork, contains several serious bugs, and lacks an externally visible test suite. PyCdlib was created to address the above problems and provide a pure Python library for ISO introspection and manipulation.
+
+## Goals
+* A pure Python library to interact with optical disk filesystems.
+* Support for reading, writing (mastering), and introspecting optical disk filesystems.
+* Support for all of the common optical disk filesystems and their extensions.
+* Compatibility with the large corpus of existing ISOs (this implies reading ISOs that violate the standards).
+* Relatively simple API.
+* Expansive test coverage.
+* In-place modification of existing ISOs, something that none of the other libraries support.
+* Performance approaching that of genisoimage.
+
+## Non-Goals
+* Support for non-optical disk filesystem (ext4, NTFS, FAT, etc are not supported).
+* Speed improvements via a less portable C library.
+
+## Existing solutions
+The [cdrkit](https://launchpad.net/cdrkit) project mentioned in the Context section is the canonical Linux ISO filesystem manipulation program on many Linux distributions. The upstream project that it was forked from is called [cdrtools](http://cdrtools.sourceforge.net/private/cdrecord.html). While cdrtools is *not* dormant, it does not offer a Python library, and thus does not meet the original criteria for the project.
+
+The [isoparser](https://github.com/barneygale/isoparser) project is a Python library to parse ISO9660 and Rock Ridge filesystems, but is unmaintained and now inactive.
+
+## PyCdlib solution
+PyCdlib allows the user to either open an existing ISO or create a new ISO from scratch. From that point forward, the ISO can be manipulated or introspected in various ways by calling additional APIs on the object. Once the filesystem has been modified to the users specification, it can then be written out to a file descriptor.
+
+## Testing
+PyCdlib has an extensive test suite of hundreds of tests that get run on each release. There are four types of tests that PyCdlib currently runs:
+
+- In unit tests, low-level details of the PyCdlib implementation are tested for completeness and code coverage.
+- In parsing integration tests, specific sequences of files and directories are created, and then an ISO is generated using genisoimage from [cdrkit](https://launchpad.net/cdrkit). Then the PyCdlib [open](pycdlib-api.html#PyCdlib-open) method is used to open up the resulting file and check various aspects of the file. This ensures that PyCdlib can successfully open up existing ISOs.
+- In new integration tests, a new ISO is created using the PyCdlib [new](pycdlib-api.html#PyCdlib-new) method, and the ISO is manipulated in specific ways. Various aspects of these newly created files are compared against known examples to ensure that things were created as they should be.
+- In hybrid integration tests, specific sequences of files and directories are created, and then an ISO is generated using genisoimage from [cdrkit](https://launchpad.net/cdrkit). Then the PyCdlib [open](pycdlib-api.html#PyCdlib-open) method is used to open up the resulting file, and the ISO is manipulated in specific ways. Various aspects of these newly created files are compared against known examples to ensure that things were created as they should be.
+
+PyCdlib currently has 95% code coverage from the tests, and anytime a new bug is found, a test is written to ensure that the bug canât happen again.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-reading-file-in-chunks.html"><-- Example: Reading a large file in chunks</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="tools.html">Tools --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-bootable-iso.md b/devtools/contrib/pycdlib/docs/example-creating-bootable-iso.md
new file mode 100644
index 00000000000..19bde1d7b6b
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-bootable-iso.md
@@ -0,0 +1,81 @@
+# Example: Creating a bootable ISO (El Torito)
+This example will show how to create a bootable [El Torito](standards.md#el-torito) ISO. Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+
+iso.new()
+
+bootstr = b'boot\n'
+iso.add_fp(BytesIO(bootstr), len(bootstr), '/BOOT.;1')
+
+iso.add_eltorito('/BOOT.;1')
+
+iso.write('eltorito.iso')
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As usual, import the necessary libraries, including pycdlib.
+
+```python
+iso = pycdlib.PyCdlib()
+
+iso.new()
+```
+
+Create a new PyCdlib object, and then create a new, basic ISO.
+
+```python
+bootstr = b'boot\n'
+iso.add_fp(BytesIO(bootstr), len(bootstr), '/BOOT.;1')
+```
+
+Add a file called /BOOT.;1 to the ISO. This is the file that contains the data to be used to boot the ISO when placed into a computer. The name of the file can be anything (and can even be nested in directories), but the contents have to be very specific. Getting the appropriate data into the boot file is beyond the scope of this tutorial; see [isolinux](http://www.syslinux.org/wiki/index.php?title=ISOLINUX) for one way of getting the appropriate data. Suffice it to say that the example code that we are using above will not actually boot, but is good enough to show the PyCdlib API usage.
+
+```python
+iso.add_eltorito('/BOOT.;1')
+```
+
+Add El Torito to the ISO, making the boot file "/BOOT.;1". After this call, the ISO is actually bootable. By default, the [add_eltorito](pycdlib-api.html#PyCdlib-add_eltorito) method will use so-called "no emulation" booting, which allows arbitrary data in the boot file. "Hard drive" and "floppy" emulation is also supported, though these options are more esoteric and need specifically configured boot data to work properly.
+
+```python
+iso.write('eltorito.iso')
+
+iso.close()
+```
+
+Write the ISO out to a file, and close out the PyCdlib object.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-extracting-data-from-iso.html"><-- Example: Extracting data from an existing ISO</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-rock-ridge-iso.html">Example: Creating an ISO with Rock Ridge --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-hybrid-bootable-iso.md b/devtools/contrib/pycdlib/docs/example-creating-hybrid-bootable-iso.md
new file mode 100644
index 00000000000..4c2964acb9c
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-hybrid-bootable-iso.md
@@ -0,0 +1,91 @@
+# Example: Creating a "hybrid" bootable ISO
+The first 32768 bytes of any ISO are designated as "system use". In a normal ISO (even an El Torito bootable one), these bytes are all zero, but this space can also be used to add in alternative booting mechanisms. In particular, this space can be used to embed boot code so that the file can be written to a USB stick and booted. These so called "hybrid" ISO files thus have two booting mechanisms: if the file is actually burned to a CD, then "El Torito" is used to boot, but if it is written to a USB stick, then the system use boot code is used to boot. PyCdlib supports creating hybrid bootable ISOs through the main API, and the following example will show how.
+
+Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+
+iso.new()
+
+bootstr = b'boot\n'
+iso.add_fp(BytesIO(bootstr), len(bootstr), '/BOOT.;1')
+
+iso.add_eltorito('/BOOT.;1')
+
+iso.add_isohybrid()
+
+iso.write('eltorito.iso')
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As usual, import the necessary libraries, including pycdlib.
+
+```python
+iso = pycdlib.PyCdlib()
+
+iso.new()
+```
+
+Create a new PyCdlib object, and then create a new, basic ISO.
+
+```python
+isolinuxstr = b'\x00'*0x40 + b'\xfb\xc0\x78\x70'
+iso.add_fp(BytesIO(isolinuxstr), len(isolinuxstr), '/BOOT.;1')
+```
+
+Add a file called /BOOT.;1 to the ISO. The contents of this conform to the expected boot start sequence as specified by isolinux. A complete discussion of the correct form of the file is out of scope for this tutorial; see [isolinux](http://www.syslinux.org/wiki/index.php?title=ISOLINUX) for more details. The above is the minimum code that conforms to the sequence, though it is not technically bootable.
+
+```python
+iso.add_eltorito('/BOOT.;1', boot_load_size=4)
+```
+
+Add El Torito to the ISO, making the boot file "/BOOT.;1", and setting the `boot_load_size` to 4. The `boot_load_size` is the number of 512-bytes sectors to read during initial boot. While other values may be allowed for this, all current examples (from cdrkit or isolinux) use this value. After this call, the ISO is El Torito bootable, but not yet a hybrid ISO.
+
+```python
+iso.add_isohybrid()
+```
+
+Add the boot file to the system use area, making this a hybrid ISO. There are various parameters that can be passed to control how the hybrid file is added, but the defaults are typically good enough for creating a hybrid ISO similar to those made for most Linux distributions.
+
+```python
+iso.write('eltorito.iso')
+
+iso.close()
+```
+
+Write the ISO out to a file, and close out the PyCdlib object.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-forcing-consistency.html"><-- Example: Forcing consistency</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-walking-iso-filesystem.html">Example: Walking the ISO filesystem --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-joliet-iso.md b/devtools/contrib/pycdlib/docs/example-creating-joliet-iso.md
new file mode 100644
index 00000000000..5cae6d6f30e
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-joliet-iso.md
@@ -0,0 +1,74 @@
+# Example: Creating an ISO with Joliet extensions
+This example will show how to create an ISO with the [Joliet](standards.md#joliet) extensions. Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
+iso.add_directory('/DIR1', joliet_path='/dir1')
+iso.write('new.iso')
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+```
+
+Create a new PyCdlib object, and then create a new ISO with that object. In order to make it have Joliet extensions, we pass the argument `joliet=3` to the [new](pycdlib-api.html#PyCdlib-new) method. PyCdlib supports Joliet levels 1, 2, and 3, but level 3 is by far the most common, so is recommended.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
+```
+
+As in earlier examples, create a new file on the ISO from a string. Because this is a Joliet ISO, we have to provide the `joliet_path` argument to [add_fp](pycdlib-api.html#PyCdlib-add_fp) as well. In contrast to Rock Ridge, Joliet is a completely different context from the original ISO9660 structure, and so the argument to be passed here must be an absolute path, not a name. Because of this, the Joliet file can be on a completely different part of the directory structure, or be omitted completely (in which case the file will only show up on the ISO9660 portion of the ISO). In practice the Joliet portion of the ISO almost always mirrors the ISO9660 portion of the ISO, so it is recommended to do that when creating new Joliet ISOs.
+
+```python
+iso.add_directory('/DIR1', joliet_path='/dir1')
+```
+
+Create a new directory on the ISO. Again we must pass the `joliet_path` argument to [add_directory](pycdlib-api.html#PyCdlib-add_directory), for all of the same reasons and with the same restrictions as we saw above for [add_fp](pycdlib-api.html#PyCdlib-add_fp).
+
+```python
+iso.write('new.iso')
+iso.close()
+```
+
+Write the new ISO out to a file, then close out the ISO.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-rock-ridge-iso.html"><-- Example: Creating an ISO with Rock Ridge</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-udf-iso.html">Example: Creating an ISO with UDF --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-new-basic-iso.md b/devtools/contrib/pycdlib/docs/example-creating-new-basic-iso.md
new file mode 100644
index 00000000000..a3d37192f0c
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-new-basic-iso.md
@@ -0,0 +1,87 @@
+# Example: Creating a new, basic ISO
+This example will show how to create a new, basic ISO with no extensions. Here's the complete code for this example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+
+iso.add_directory('/DIR1')
+
+iso.write('new.iso')
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+First import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+```
+
+Create a new PyCdlib object. At this point, the object can only do one of two things: open up an existing ISO, or create a new ISO.
+
+```python
+iso.new()
+```
+
+Create a new ISO using the [new](pycdlib-api.html#PyCdlib-new) method. The [new](pycdlib-api.html#PyCdlib-new) method has quite a few available arguments, but by passing no arguments, we ask for a basic interchange level 1 ISO with no extensions. At this point, we could write out a valid ISO image, but it won't have any files or directories in it, so it wouldn't be very interesting.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+```
+
+Now we add a new file to the ISO. There are a few details to notice in this code. The first detail is that there are two related APIs called [add_file](pycdlib-api.html#PyCdlib-add_file) and [add_fp](pycdlib-api.html#PyCdlib-add_fp). The [add_file](pycdlib-api.html#PyCdlib-add_file) API takes the pathname to a file on the local disk to get the contents from. The [add_fp](pycdlib-api.html#PyCdlib-add_fp) API takes a file-like object to get the contents from; this can be a normal file-object (such as that returned by standard python [open](https://docs.python.org/3.6/library/functions.html#open)), or this can be any other object that acts like a file. In this case, we use a python [BytesIO](https://docs.python.org/3/library/io.html#binary-i-o) object, which behaves like a file-object but is backed by a string. The second detail to notice is that the second argument to [add_fp](pycdlib-api.html#PyCdlib-add_fp) is the length of the content to add to the ISO. Since file-like objects don't have a standard way to get the length, this must be provided by the user. The [add_file](pycdlib-api.html#PyCdlib-add_file) API can use the length of the file itself for this purpose, so the second argument isn't required there. The third detail to notice is that the final argument to [add_fp](pycdlib-api.html#PyCdlib-add_fp) is the location of the file on the resulting ISO (also known as the `iso_path`). The `iso_path` is specified using something similar to a Unix file path. These paths differ from Unix file paths in that they *must* be absolute paths, since PyCdlib has no concept of a current working directory. All intermediate directories along the path must exist, otherwise the [add_fp](pycdlib-api.html#PyCdlib-add_fp) call will fail (the `/` root directory always exists and doesn't have to be explicitly created). Also note that ISO9660-compliant filenames have a slightly odd format owing to their history. In standard ISO interchange level 1, filenames have a maximum of 8 characters, followed by a required dot, followed by a maximum 3 character extension, followed by a semicolon and a version. The filename and the extension are both optional, but one or the other must exist. Only uppercase letters, numbers, and underscore are allowed for either the name or extension. If any of these rules are violated, PyCdlib will throw an exception.
+
+```python
+iso.add_directory('/DIR1')
+```
+
+Here we add a new directory to the ISO called `DIR1`. Like [add_fp](pycdlib-api.html#PyCdlib-add_fp), the `iso_path` argument to [add_directory](pycdlib-api.html#PyCdlib-add_directory) is an absolute, Unix like pathname. The rules for ISO directory names are similar to that of filenames, except that directory names do not have extensions and do not have versions.
+
+```python
+iso.write('new.iso')
+```
+
+Now we finally get to write out the ISO we just created. The process of writing out an ISO is sometimes called "mastering". In any case, this is the process of writing the contents of the ISO out to a file on disk. Similar to the [add_file](pycdlib-api.html#PyCdlib-add_file) and [add_fp](pycdlib-api.html#PyCdlib-add_fp) methods, there are the related [write](pycdlib-api.html#PyCdlib-write) and [write_fp](pycdlib-api.html#PyCdlib-write_fp) methods, the former of which takes a filename to write to, and the latter of which takes a file-like object.
+
+```python
+iso.close()
+```
+
+Close out the PyCdlib object, releasing all resources and invalidating the contents. After this call, the object can be reused to create a new ISO or open up an existing ISO.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="examples.html"><-- Examples</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-opening-existing-iso.html">Example: Opening an existing ISO --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-rock-ridge-iso.md b/devtools/contrib/pycdlib/docs/example-creating-rock-ridge-iso.md
new file mode 100644
index 00000000000..bdd284c234c
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-rock-ridge-iso.md
@@ -0,0 +1,74 @@
+# Example: Creating an ISO with Rock Ridge extensions
+This example will show how to create an ISO with the [Rock Ridge](standards.md#rock-ridge) extensions. Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new(rock_ridge='1.09')
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', rr_name='foo')
+iso.add_directory('/DIR1', rr_name='dir1')
+iso.write('new.iso')
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new(rock_ridge='1.09')
+```
+
+Create a new PyCdlib object, and then create a new ISO with that object. In order to make it have Rock Ridge extensions, we pass the argument `rock_ridge="1.09"` to the [new](pycdlib-api.html#PyCdlib-new) method. PyCdlib supports Rock Ridge versions 1.09, 1.10, and 1.12, though 1.09 is more common.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', rr_name='foo')
+```
+
+As in earlier examples, create a new file on the ISO from a string. Because this is a Rock Ridge ISO, we have to also supply the `rr_name` argument to the [add_fp](pycdlib-api.html#PyCdlib-add_fp) method. Forgetting the `rr_name` argument on a Rock Ridge ISO is an error and PyCdlib will throw an exception. Note that it is called `rr_name`, and that the argument given is truly a name, not an absolute path. This is because Rock Ridge is an extension to the original ISO9660, and this alternate name will be stored alongside the original ISO data.
+
+```python
+iso.add_directory('/DIR1', rr_name='dir1')
+```
+
+Create a new directory on the ISO. Again we must pass the `rr_name` argument to [add_directory](pycdlib-api.html#PyCdlib-add_directory), for all of the same reasons and with the same restrictions as we saw above for [add_fp](pycdlib-api.html#PyCdlib-add_fp).
+
+```python
+iso.write('new.iso')
+iso.close()
+```
+
+Write the new ISO out to a file, then close out the ISO.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-bootable-iso.html"><-- Example: Creating a bootable ISO (El Torito)</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-joliet-iso.html">Example: Creating an ISO with Joliet --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-creating-udf-iso.md b/devtools/contrib/pycdlib/docs/example-creating-udf-iso.md
new file mode 100644
index 00000000000..d30ee2d8b27
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-creating-udf-iso.md
@@ -0,0 +1,74 @@
+# Example: Creating an ISO with UDF
+This example will show how to create an ISO with the [UDF](standards.md#udf) bridge format. Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new(udf='2.60')
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', udf_path='/foo')
+iso.add_directory('/DIR1', udf_path='/dir1')
+iso.write('new.iso')
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new(udf='2.60')
+```
+
+Create a new PyCdlib object, and then create a new ISO with that object. In order to make it have UDF, we pass the argument `udf='2.60'` to the [new](pycdlib-api.html#PyCdlib-new) method.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', udf_path='/foo')
+```
+
+As in earlier examples, create a new file on the ISO from a string. Because this is a UDF ISO, we have to provide the `udf_path` argument to [add_fp](pycdlib-api.html#PyCdlib-add_fp) as well. Like Joliet, UDF is a completely different context from the original ISO9660 structure, and so the argument to be passed here must be an absolute path, not a name. Because of this, the UDF file can be on a completely different part of the directory structure, or be omitted completely (in which case the file will only show up on the ISO9660 portion of the ISO).
+
+```python
+iso.add_directory('/DIR1', udf_path='/dir1')
+```
+
+Create a new directory on the ISO. Again we must pass the `udf_path` argument to [add_directory](pycdlib-api.html#PyCdlib-add_directory), for all of the same reasons and with the same restrictions as we saw above for [add_fp](pycdlib-api.html#PyCdlib-add_fp).
+
+```python
+iso.write('new.iso')
+iso.close()
+```
+
+Write the new ISO out to a file, then close out the ISO.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-joliet-iso.html"><-- Example: Creating an ISO with Joliet</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-using-facade.html">Example: Using a facade --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-extracting-data-from-iso.md b/devtools/contrib/pycdlib/docs/example-extracting-data-from-iso.md
new file mode 100644
index 00000000000..39473ad3b0c
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-extracting-data-from-iso.md
@@ -0,0 +1,87 @@
+# Example: Extracting data from an existing ISO
+This example will show how to extract data from an existing ISO. Here's the complete code for this example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+out = BytesIO()
+iso.write_fp(out)
+iso.close()
+
+iso.open_fp(out)
+extracted = BytesIO()
+iso.get_file_from_iso_fp(extracted, iso_path='/FOO.;1')
+iso.close()
+
+print(extracted.getvalue().decode('utf-8'))
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As we've seen before, import pycdlib. We also import the [BytesIO](https://docs.python.org/3/library/io.html#binary-i-o) module so we can use a python string as a file-like object.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+out = BytesIO()
+iso.write_fp(out)
+iso.close()
+```
+
+This code creates a new ISO, adds a single file to it, and writes it out. This is very similar to the code in [Creating a new, basic ISO](example-creating-new-basic-iso.md), so see that example for more information. One important difference in this code is that it uses a `BytesIO` object to master the ISO into so we don't have to write any temporary data out to the filesystem; it all happens in memory.
+
+```python
+iso.open_fp(out)
+```
+
+Here we open up the ISO we created above. We can safely re-use the PyCdlib object because we called the [close](pycdlib-apihtml#PyCdlib-close) method earlier. Also note that we use [open_fp](pycdlib-api.html#PyCdlib-open_fp) to open the file-like object we wrote into using [write_fp](pycdlib-api.html#PyCdlib-write_fp) above.
+
+```python
+extracted = BytesIO()
+iso.get_file_from_iso_fp(extracted, iso_path='/FOO.;1')
+```
+
+Now we use the [get_file_from_iso_fp](pycdlib-api.html#PyCdlib-get_file_from_iso_fp) API to extract the data from a file on the ISO. In this case, we access the "/FOO.;1" file that we created above, and write out the data to the BytesIO object `extracted`.
+
+```python
+iso.close()
+
+print(extracted)
+```
+
+As is the case in other examples, we close out the PyCdlib object, and print out the data we extracted.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-opening-existing-iso.html"><-- Example: Opening an existing ISO</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-bootable-iso.html">Example: Creating a bootable ISO (El Torito) --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-forcing-consistency.md b/devtools/contrib/pycdlib/docs/example-forcing-consistency.md
new file mode 100644
index 00000000000..1134d368038
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-forcing-consistency.md
@@ -0,0 +1,97 @@
+# Example: Forcing consistency
+As discussed in the example [introduction](examples.md#pycdlib-theory-of-operation), PyCdlib takes a lazy approach to updating metadata. For performance reasons it is recommended to let PyCdlib handle when and how to update the metadata, but sometimes users need the metadata to be consistent immediately. PyCdlib offers two solutions for this:
+
+1. There is an API called [force_consistency](pycdlib-api.html#PyCdlib-force_consistency) that immediately updates all metadata to the latest.
+1. When initially creating the PyCdlib object, the user can set the `always_consistent` parameter. When this is True, PyCdlib will update the metadata after every operation, ensuring that it is always up-to-date.
+
+Of the two, using lazy metadata updating and only calling [force_consistency](pycdlib-api.html#PyCdlib-force_consistency) when absolutely needed is highly preferred. Using `always_consistent` is only needed in specialized cases (such as first modifying, then introspecting the extent number that a file exists on the ISO). The following example will use [force_consistency](pycdlib-api.html#PyCdlib-force_consistency) at a particular point to cause the metadata to be updated. To learn how to use `always_consistent`, please see the documentation for the `__init__` method for PyCdlib.
+
+Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+
+iso.force_consistency()
+
+iso.add_directory('/DIR1')
+
+iso.write('new.iso')
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new()
+```
+
+As in earlier examples, create a new PyCdlib object and then create a new ISO with no extensions.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+```
+
+As in earlier examples, add a new file to the ISO.
+
+```python
+iso.force_consistency()
+```
+
+Now force consistency on the ISO. This will cause PyCdlib to update all of the metadata on the ISO, and after this call all of the metadata for all entries on the ISO will be accurate.
+
+```python
+iso.add_directory('/DIR1')
+```
+
+As in earlier examples, add a new directory to the ISO. Note that the metadata on the ISO is now out-of-date again, so to accurately look at the metadata, [force_consistency](pycdlib-api.html#PyCdlib-force_consistency) would have to be called again after this modification.
+
+```python
+iso.write('new.iso')
+```
+
+As in earlier examples, write the ISO out to a file. The [write](pycdlib-api.html#PyCdlib-write) method implicitly does a metadata update since it needs all of the metadata to be accurate to successfully write out the ISO.
+
+```python
+iso.close()
+```
+
+As in earlier examples, close the PyCdlib object when we are done with it.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-managing-hard-links.html"><-- Example: Managing hard-links on an ISO</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-hybrid-bootable-iso.html">Example: Creating a "hybrid" bootable ISO --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-managing-hard-links.md b/devtools/contrib/pycdlib/docs/example-managing-hard-links.md
new file mode 100644
index 00000000000..df7088c81d3
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-managing-hard-links.md
@@ -0,0 +1,104 @@
+# Example: Managing hard-links on an ISO
+PyCdlib supports an advanced concept called hard-links, which is multiple names for the same piece of data (this is somewhat similar to Unix hard-links). Most users will not need to use this functionality and should stick with the standard [add_file](pycdlib-api.html#PyCdlib-add_file) and [rm_file](pycdlib-api.html#PyCdlib-rm_file) APIs. However, for those that want to do more advanced things like hiding a file from Joliet while having it remain visible in ISO9660, this functionality can be useful.
+
+On an ISO, a piece of data can be referred to (possibly several times) from four different contexts:
+
+1. From the original ISO9660 context, including the Rock Ridge extensions.
+1. From the Joliet context, since this is a separate context.
+1. From the El Torito boot record, since this is effectively a separate context.
+1. From the UDF context, since this is a separate context.
+
+The data can be referred to zero, one, or many times from each of these contexts. The most classic example of hard-links happens when an ISO has the Joliet extensions. In that case, there is implicitly a hard-link from the ISO9660 (and Rock Ridge) context to the file contents, and a hard-link from the Joliet context to the file contents. When a piece of data has zero entries in a context, it is effectively hidden from that context. For example, a file could be visible from ISO9660/Rock Ridge, but hidden from Joliet, or vice-versa. A file could be used for booting, but be hidden from both ISO9660/Rock Ridge and Joliet, etc. Management of these hard-links is done via the PyCdlib APIs [add_hard_link](pycdlib-api.html#PyCdlib-add_hard_link) and [rm_hard_link](pycdlib-api.html#PyCdlib-rm_hard_link). Adding or removing a file through the [add_file](pycdlib-api.html#PyCdlib-add_file) and [rm_file](pycdlib-api.html#PyCdlib-rm_file) APIs implicitly manipulates hard-links behind the scenes. Note that hard-links only make sense for files, since directories have no direct data (only metadata).
+
+An example should help to illustrate the concept. Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
+
+iso.add_hard_link(iso_old_path='/FOO.;1', iso_new_path='/BAR.;1')
+
+iso.rm_hard_link(joliet_path='/foo')
+
+outiso = BytesIO()
+iso.write_fp(outiso)
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+```
+
+As in earlier examples, create a PyCdlib object, and then create a new, empty ISO with the Joliet extensions.
+
+```python
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
+```
+
+As in earlier examples, add a new file to the ISO. Here we have provided both the ISO path '/FOO.;1' and the Joliet path '/foo', so the file implicitly has two links; one from the ISO context, and one from the Joliet context.
+
+```python
+iso.add_hard_link(iso_old_path='/FOO.;1', iso_new_path='/BAR.;1')
+```
+
+Add a hard-link from the original '/FOO.;1' location in the ISO context to a second location in the ISO context '/BAR.;1'. This takes up no additional space on the ISO for the data, only for the metadata.
+
+```python
+iso.rm_hard_link(joliet_path='/foo')
+```
+
+Remove the link from the Joliet context for the file. Now this file is effectively hidden from the Joliet context, while still being visible in the ISO context.
+
+```python
+outiso = BytesIO()
+iso.write_fp(outiso)
+```
+
+As in earlier examples, write the ISO out to the BytesIO object.
+
+```python
+iso.close()
+```
+
+Since we are done with the ISO object, close it out.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-modifying-file-in-place.html"><-- Example: Modifying a file in place</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-forcing-consistency.html">Example: Forcing consistency --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-modifying-file-in-place.md b/devtools/contrib/pycdlib/docs/example-modifying-file-in-place.md
new file mode 100644
index 00000000000..00e741ac08b
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-modifying-file-in-place.md
@@ -0,0 +1,97 @@
+# Example: Modifying a file in place
+This example will show how to use one of the unique features of PyCdlib, the ability to modify a file in place. While this doesn't seem like a big deal, it is actually somewhat difficult to achieve in an ISO. The reason is that modifying a file usually involves moving around a lot of metadata, and additionally may require moving around data as well. For these reasons, PyCdlib has limitations when modifying a file in place. In particular:
+
+1. Only files can be modified in place; directories cannot be changed.
+1. Only existing files can be modified; no files can be added or removed.
+1. The file cannot be extended beyond the extent that the current file lives in. In ISO9660 terms, an extent is (almost) always 2048 bytes. Thus, if the current file is 48 bytes, the modification can only increase the size of the file by an additional 2000 bytes. Shrinking a file is almost never a problem, but note that if the file contents are modified to be smaller than the original, no size will be saved on the resulting ISO.
+
+Despite these limitations, modifying a file in place is extremely fast, much faster than traditional modification and mastering. Therefore, if the use case calls for just changing a few bytes in a file, it is well worth it to consider modifying the file in place.
+
+Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+outiso = BytesIO()
+iso.write_fp(outiso)
+iso.close()
+
+iso.open_fp(outiso)
+
+bazstr = b'bazzzzzz\n'
+iso.modify_file_in_place(BytesIO(bazstr), len(bazstr), '/FOO.;1')
+
+modifiediso = BytesIO()
+iso.write_fp(modifiediso)
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+outiso = BytesIO()
+iso.write_fp(outiso)
+iso.close()
+```
+
+Create an ISO with a single file called "/FOO.;1" on it. This is similar to previous examples, with the one exception that we are using the [write_fp](pycdlib-api.html#PyCdlib-write_fp) API to write the ISO out to a string in memory (rather than on-disk). Note that at this point, the "/FOO.;1" file has the contents 'foo\n' on the ISO.
+
+```python
+iso.open_fp(outiso)
+```
+
+Open up the ISO that is in the `outiso` BytesIO object.
+
+```python
+bazstr = b'bazzzzzz\n'
+iso.modify_file_in_place(BytesIO(bazstr), len(bazstr), '/FOO.;1')
+```
+
+Here we get to the heart of the example. We use [modify_file_in_place](pycdlib-api.html#PyCdlib-modify_file_in_place) to modify the "/FOO.;1" file to have the contents 'bazzzzzz\n'. We are allowed to expand the size of the file because we are still smaller than the size of the extent (the [modify_file_in_place](pycdlib-api.html#PyCdlib-modify_file_in_place) API enforces this).
+
+```python
+modifiediso = BytesIO()
+iso.write_fp(modifiediso)
+iso.close()
+```
+
+Write the modified ISO out to the BytesIO object called "modifiediso". At this point, the "/FOO.;1" file on "modifiediso" has the contents 'bazzzzzz\n'. Once we are done with this, close out the object.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-udf-iso.html"><-- Example: Creating an ISO with UDF</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-managing-hard-links.html">Example: Managing hard-links on an ISO --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-opening-existing-iso.md b/devtools/contrib/pycdlib/docs/example-opening-existing-iso.md
new file mode 100644
index 00000000000..ff340e3637d
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-opening-existing-iso.md
@@ -0,0 +1,59 @@
+# Example: Opening an existing ISO
+This example will show how to examine an existing ISO. Here's the complete code for this example:
+
+```python
+import sys
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.open(sys.argv[1])
+
+for child in iso.list_children(iso_path='/'):
+ print(child.file_identifier())
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+import sys
+import pycdlib
+```
+
+As we've seen before, import pycdlib. We also import the sys module so we get access to the command-line arguments.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.open(sys.argv[1])
+```
+
+As we saw in the last example, create a new PyCdlib object. Once we have the object, we can then open up the file passed on the command-line. During the open, PyCdlib will parse all of the metadata on the ISO, so if the file is coming over a network, this may take a bit of time. Note that besides the [open](pycdlib-api.html#PyCdlib-open) method, there is also an [open_fp](pycdlib-api.html#PyCdlib-open_fp) method that takes an arbitrary file-like object.
+
+```python
+for child in iso.list_children(iso_path='/'):
+ print(child.file_identifier())
+```
+
+Use the [list_children](pycdlib-api.html#PyCdlib-list_children) API from PyCdlib to iterate over all of the files and directories at the root of the ISO. As discussed in the [Creating a new, basic ISO](example-creating-new-basic-iso.md) example, the paths are Unix-like absolute paths.
+
+```python
+iso.close()
+```
+
+Close out the PyCdlib object, releasing all resources and invalidating the contents. After this call, the object can be reused to create a new ISO or open up an existing ISO.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-new-basic-iso.html"><-- Example: Creating a new, basic ISO</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-extracting-data-from-iso.html">Example: Extracting data from an existing ISO --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-reading-file-in-chunks.md b/devtools/contrib/pycdlib/docs/example-reading-file-in-chunks.md
new file mode 100644
index 00000000000..e8ecf833c2c
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-reading-file-in-chunks.md
@@ -0,0 +1,97 @@
+# Example: Reading a large file in chunks
+It may be useful in some applications to be able to read a file from an ISO a bit at a time and do some processing on it. PyCdlib provides the context manager [open_file_from_iso](pycdlib-api.html#PyCdlib-open_file_from_iso) API to allow opening a file and reading in parts of it. Here's the complete code for this example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foofoo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+
+with iso.open_file_from_iso(iso_path='/FOO.;1') as infp:
+ all1 = infp.read()
+ infp.seek(0)
+ first = infp.read(3)
+ second = infp.read()
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As we've seen before, import pycdlib. We also import the [BytesIO](https://docs.python.org/3/library/io.html#binary-i-o) module so we can use a python string as a file-like object.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foofoo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+```
+
+This code creates a new ISO, adds a single file to it, and writes it out. This is very similar to the code in [Creating a new, basic ISO](example-creating-new-basic-iso.md), so see that example for more information.
+
+```python
+with iso.open_file_from_iso(iso_path='/FOO.;1') as infp:
+```
+
+Here we use the [open_file_from_iso](pycdlib-api.html#PyCdlib-open_file_from_iso) API to get a context manager to the file that we created; this will be used in the rest of the explanations below.
+
+```python
+ all = infp.read()
+```
+
+The first `read` call reads in all of the data in the file, so at the end of the call the "all" variable will contain `foofoo\n`.
+
+```python
+ infp.seek(0)
+```
+
+The 'seek' call then resets the file pointer back to the beginning of the file.
+
+```python
+ first = infp.read(3)
+```
+
+If the `read` API is passed a number, it will read and return that many bytes. In this case, the 'first' variable will end up containing `foo`.
+
+```python
+ second = infp.read()
+```
+
+And now we read the rest of the data, so the 'second' variable will end up containing `foo\n`.
+
+```python
+iso.close()
+```
+
+As is the case in other examples, we close out the PyCdlib object.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-walking-iso-filesystem.html"><-- Example: Walking the ISO filesystem</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="design.html">Design --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-using-facade.md b/devtools/contrib/pycdlib/docs/example-using-facade.md
new file mode 100644
index 00000000000..71e4eca458a
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-using-facade.md
@@ -0,0 +1,86 @@
+# Example: Using a facade
+This example will show how to access various aspects of an ISO with a [facade](examples.md#facades). Here's the complete code for the example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+
+joliet = iso.get_joliet_facade()
+
+foostr = b'foo\n'
+joliet.add_fp(BytesIO(foostr), len(foostr), joliet_path='/foo')
+
+joliet.add_directory(joliet_path='/dir1')
+
+iso.write('new.iso')
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new(joliet=3)
+```
+
+Create a new PyCdlib object, and then create a new ISO with that object. As in previous examples, make it a Joliet ISO by passing `joliet=3` to the [new](pycdlib-api.html#PyCdlib-new) method.
+
+```python
+joliet = iso.get_joliet_facade()
+```
+
+Fetch a Joliet facade for the PyCdlib object. We'll use this for manipulation of the object below.
+
+```python
+foostr = b'foo\n'
+joliet.add_fp(BytesIO(foostr), len(foostr), joliet_path='/foo')
+```
+
+As in earlier examples, create a new file on the ISO from a string. Note that we call the `add_fp` method on the facade, and that we *only* pass the joliet_path to the facade. This simplifies things considerably for us. However, this also means that the file will only show up in the Joliet context. If we want it to show up in other contexts (like Rock Ridge or UDF), we have two options. We can either use the more complicated form of the `add_fp` method on the original `PyCdlib` object, or we can call `add_hard_link` on the original `PyCdlib` object to make the file appear in other contexts.
+
+```python
+joliet.add_directory(joliet_path='/dir1')
+```
+
+Create a new directory on the ISO. Again we use the facade to create the directory, so we only have to pass the `joliet_path`.
+
+```python
+iso.write('new.iso')
+iso.close()
+```
+
+Write the new ISO out to a file, then close out the ISO. Note that we use the original `PyCdlib` object, and not the facades for this. Since there is nothing context-specific about these calls, the original object works just fine.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-udf-iso.html"><-- Example: Creating an ISO with UDF</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-modifying-file-in-place.html">Example: Modifying a file in place --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/example-walking-iso-filesystem.md b/devtools/contrib/pycdlib/docs/example-walking-iso-filesystem.md
new file mode 100644
index 00000000000..5bf7ce4f674
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/example-walking-iso-filesystem.md
@@ -0,0 +1,113 @@
+# Example: Walking the ISO filesystem
+In some circumstances it may be useful to walk all or some of the filesystem tree on the ISO. For that purpose, PyCdlib provides the [walk](pycdlib-api.html#PyCdlib-walk) API. Much like the built-in Python [os.walk](https://docs.python.org/3.6/library/os.html#os.walk) API, this method takes a PyCdlib full path, and iterates over the entire filesystem starting at that root. For each directory, the method returns a 3-tuple of `(dirpath, dirnames, filenames)` that can be used by the user. Here's the complete code for this example:
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+
+iso = pycdlib.PyCdlib()
+iso.new()
+
+iso.add_directory('/DIR1')
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/DIR1/FOO.;1')
+barstr = b'bar\n'
+iso.add_fp(BytesIO(barstr), len(barstr), '/DIR1/BAR.;1')
+iso.add_directory('/DIR2')
+bazstr = b'baz\n'
+iso.add_fp(BytesIO(bazstr), len(bazstr), '/DIR2/BAZ.;1')
+
+for dirname, dirlist, filelist in iso.walk(iso_path='/'):
+ print("Dirname:", dirname, ", Dirlist:", dirlist, ", Filelist:", filelist)
+
+for dirname, dirlist, filelist in iso.walk(iso_path='/DIR1'):
+ print("Dirname:", dirname, ", Dirlist:", dirlist, ", Filelist:", filelist)
+
+iso.close()
+```
+
+Let's take a closer look at the code.
+
+```python
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+import pycdlib
+```
+
+As in earlier examples, import the relevant libraries, including pycdlib itself.
+
+```python
+iso = pycdlib.PyCdlib()
+iso.new()
+```
+
+Create a new PyCdlib object, and then create a new ISO with that object.
+
+```python
+iso.add_directory('/DIR1')
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/DIR1/FOO.;1')
+barstr = b'bar\n'
+iso.add_fp(BytesIO(barstr), len(barstr), '/DIR1/BAR.;1')
+iso.add_directory('/DIR2')
+bazstr = b'baz\n'
+iso.add_fp(BytesIO(bazstr), len(bazstr), '/DIR2/BAZ.;1')
+```
+
+As in earlier examples, create some new directories and files on the ISO from strings.
+
+```python
+for dirname, dirlist, filelist in iso.walk(iso_path='/'):
+ print("Dirname:", dirname, ", Dirlist:", dirlist, ", Filelist:", filelist)
+```
+
+Use the [walk](pycdlib-api.html#PyCdlib-walk) API to iterate over the directories and files on the ISO, printing them out in turn. Note that [walk](pycdlib-api.html#PyCdlib-walk) takes one and only one of `iso_path`, `rr_path`, `joliet_path`, or `udf_path`, and only accepts `rr_path`, `joliet_path`, or `udf_path` if the ISO supports those extensions. Since we added a number of files and directories on the ISO, the above code will print out:
+
+```python
+Dirname: / , Dirlist: ['DIR2', 'DIR1'] , Filelist: []
+Dirname: /DIR1 , Dirlist: [] , Filelist: ['FOO.;1', 'BAR.;1']
+Dirname: /DIR2 , Dirlist: [] , Filelist: ['BAZ.;1']
+```
+
+This can be interpreted as follows. The "dirname" is the name of the directory currently being examined; for the root it is `/`, while it is `/DIR1` and `/DIR2` for the respective directories. The "dirlist" is the list of sub-directores at this level of the hierarchy; the root contains `DIR1` and `DIR2`, while each sub-directory has no sub-sub-directories and hence has an empty list. The "filelist" is the list of files at this level of the hierarchy; the root contains no files so has an empty list, while `DIR1` contains `FOO.;1` and `BAR.;1` and `DIR2` contains `BAZ.;1`.
+
+```python
+for dirname, dirlist, filelist in iso.walk(iso_path='/DIR1'):
+ print("Dirname:", dirname, ", Dirlist:", dirlist, ", Filelist:", filelist)
+```
+
+The second call to [walk](pycdlib-api.html#PyCdlib-walk) starts in the `DIR1` subdirectory, so the output from this loop looks like:
+
+```sh
+Dirname: /DIR1 , Dirlist: [] , Filelist: ['FOO.;1', 'BAR.;1']
+```
+
+Like we saw before, the "dirname" for this level is `/DIR1`, while the "dirlist" is empty since there are no subdirectories, and the filelist contains `FOO.;1` and `BAR.;1`.
+
+```python
+iso.close()
+```
+
+Close out the ISO object to release resources and allow it to be reused.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="example-creating-hybrid-bootable-iso.html"><-- Example: Creating a "hybrid" bootable ISO</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-reading-file-in-chunks.html">Example: Reading a large file in chunks --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/examples.md b/devtools/contrib/pycdlib/docs/examples.md
new file mode 100644
index 00000000000..25f6f9be3b1
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/examples.md
@@ -0,0 +1,45 @@
+# Examples
+The easiest way to learn PyCdlib is to see some examples. Before getting to the examples, we first start out with the PyCdlib theory of operation.
+
+## PyCdlib theory of operation
+PyCdlib aims to allow users to manipulate ISOs in arbitrary ways, from creating new ISOs to modifying and writing out existing ISOs. Along the way, the PyCdlib API is meant to hide the details of the above standards, letting users concentrate on the modifications they wish to make.
+
+To start using the PyCdlib API, the user must create a new PyCdlib object. A PyCdlib object cannot do very much until it is initialized, either by creating a new ISO (using the [new](pycdlib-api.html#PyCdlib-new) method), or by opening an existing ISO (using the [open](pycdlib-api.html#PyCdlib-open) method). Once a PyCdlib object is initialized, files can be added or removed, directories can be added or removed, the ISO can be made bootable, and various other manipulations of the ISO can happen. Once the user is happy with the current layout of the ISO, the [write](pycdlib-api.html#PyCdlib-write) method can be called, which will write out the current state of the ISO to a file (or file-like object).
+
+Due to some historical aspects of the ISO standards, making modifications to an existing ISO can involve shuffling around a lot of metadata. In order to maintain decent performance, PyCdlib takes a "lazy" approach to updating that metadata, and only does the update when it needs the results. This allows the user to make several modifications and effectively "batch" operations without significantly impacting speed. The minor downside to this is that the metadata stored in the PyCdlib object is not always consistent, so if the user wants to reach into the object to look at a particular field, it may not always be up-to-date. PyCdlib offers a [force\_consistency](pycdlib-api.html#PyCdlib-force_consistency) API that immediately updates all metadata for just this reason.
+
+## Facades
+The base PyCdlib object hides many of the details of the ISO and related standards, but still has some complexity to allow arbitrary modifications to be made. The "facades", on the other hand, sacrifice some of this flexibility for an even simpler interface that allows the user to operate in just one of the contexts. The benefit is that the user can just work in the context they prefer, and the facade will handle the details of compatibility with the other contexts. The downside is that making modifications via the facades only modifies the context that the facade is for, leaving it out of the other contexts. For instance, if the "PyCdlibJoliet" facade is being used, and a file is added via "add_fp", then that file will *only* show up in the Joliet context. This means that it won't be visible by default to Unix operating systems (which generally prefer to use the Rock Ridge context).
+
+## Example format
+We'll start out each example with the entire source code needed to run the example, and then break down the code to show what the individual pieces do. Note that in most cases, error handling is elided for brevity, though it probably shouldn't be in real code.
+
+* [Creating a new, basic ISO](example-creating-new-basic-iso.md)
+* [Opening an existing ISO](example-opening-existing-iso.md)
+* [Extracting data from an existing ISO](example-extracting-data-from-iso.md)
+* [Creating a bootable ISO (El Torito)](example-creating-bootable-iso.md)
+* [Creating an ISO with Rock Ridge](example-creating-rock-ridge-iso.md)
+* [Creating an ISO with Joliet](example-creating-joliet-iso.md)
+* [Creating an ISO with UDF](example-creating-udf-iso.md)
+* [Using a facade](example-using-facade.md)
+* [Modifying a file in place](example-modifying-file-in-place.md)
+* [Managing hard-links on an ISO](example-managing-hard-links.md)
+* [Forcing consistency](example-forcing-consistency.md)
+* [Creating a "hybrid" bootable ISO](example-creating-hybrid-bootable-iso.md)
+* [Walking the ISO filesystem](example-walking-iso-filesystem.md)
+* [Reading a large file in chunks](example-reading-file-in-chunks.md)
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="python-compatibility.html"><-- Python compatibility</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="example-creating-new-basic-iso.html">Example: Creating a new, basic ISO --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/exceptions.md b/devtools/contrib/pycdlib/docs/exceptions.md
new file mode 100644
index 00000000000..435626cc46a
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/exceptions.md
@@ -0,0 +1,23 @@
+# Exceptions
+When things go wrong, PyCdlib generally throws an exception. There is a base exception called `PyCdlibException`, which is never itself thrown. Instead, PyCdlib will throw one of the following exceptions, all of which are subclasses of `PyCdlibException`:
+
+* PyCdlibInvalidISO - Thrown when PyCdlib can't successfully parse an ISO with one of the [open](pycdlib-api.html#PyCdlib-open) methods. Usually this indicates that the ISO does not follow relevant standards, though it can also sometimes be a bug in PyCdlib itself.
+* PyCdlibInvalidInput - Thrown when the user provides invalid input to a PyCdlib API.
+* PyCdlibInternalError - Thrown when an unexpected situation happens within PyCdlib itself. This can happen when there is a bug within PyCdlib itself, or sometimes when an ISO that doesn't conform to standards is parsed.
+
+The arrangement of a base exception of `PyCdlibException` along with subclassed specified errors allows maximum error handling flexibility for client programs. If a client program wants to handle all PyCdlib errors, it can catch `PyCdlibException`, but if it wants to do something different based on the exception type, it can catch the various exception types.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="tools.html"><-- Tools</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="reporting-issues.html">Reporting issues/bugs --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/pycdlib-api.html b/devtools/contrib/pycdlib/docs/pycdlib-api.html
new file mode 100644
index 00000000000..2f311b5e543
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/pycdlib-api.html
@@ -0,0 +1,720 @@
+<!DOCTYPE>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Pydoc: module pycdlib.pycdlib</title>
+<link rel="stylesheet" type="text/css" href="pydoc_data/_pydoc.css"></head><body><div style="clear:both;padding-top:.5em;">
+<table class="heading">
+<tr class="heading-text decor">
+<td class="title"> <br><strong class="title"><a href="pycdlib.html" class="white">pycdlib</a>.pycdlib</strong></td>
+<td class="extra"> </td></tr></table>
+ <p><span class="code">Main <a href="#PyCdlib">PyCdlib</a> class and support classes and utilities.</span></p>
+<p>
+<table class="section">
+<tr class="decor index-decor heading-text">
+<td class="section-title" colspan=3> <br><strong class="bigsection">Classes</strong></td></tr>
+
+<tr><td class="decor index-decor"><span class="code"> </span></td><td> </td>
+<td class="singlecolumn"><dl>
+<dt class="heading-text"><a href="builtins.html#object">builtins.object</a>
+</dt><dd>
+<dl>
+<dt class="heading-text"><a href="pycdlib.pycdlib.html#PyCdlib">PyCdlib</a>
+</dt></dl>
+</dd>
+</dl>
+ <p>
+<table class="section">
+<tr class="decor title-decor heading-text">
+<td class="section-title" colspan=3> <br><a name="PyCdlib">class <strong>PyCdlib</strong></a>(<a href="builtins.html#object">builtins.object</a>)</td></tr>
+
+<tr><td class="decor title-decor" rowspan=2><span class="code"> </span></td>
+<td class="decor title-decor" colspan=2><span class="code"><a href="#PyCdlib">PyCdlib</a>(always_consistent=False)<br>
+ <br>
+The main class for manipulating ISOs.<br> </span></td></tr>
+<tr><td> </td>
+<td class="singlecolumn">Methods defined here:<br>
+<dl><dt><a name="PyCdlib-__init__"><strong>__init__</strong></a>(self, always_consistent=False)</dt><dd><span class="code">Initialize self. See help(type(self)) for accurate signature.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_directory"><strong>add_directory</strong></a>(self, iso_path=None, rr_name=None, joliet_path=None, file_mode=None, udf_path=None)</dt><dd><span class="code">Add a directory to the ISO. At least one of an iso_path, joliet_path,<br>
+or udf_path must be provided. Providing joliet_path on a non-Joliet<br>
+ISO, or udf_path on a non-UDF ISO, is an error. If the ISO contains<br>
+Rock Ridge, then a Rock Ridge name must be provided.<br>
+ <br>
+Parameters:<br>
+ iso_path - The ISO9660 absolute path to use for the directory.<br>
+ rr_name - The Rock Ridge name to use for the directory.<br>
+ joliet_path - The Joliet absolute path to use for the directory.<br>
+ file_mode - The POSIX file mode to use for the directory. This only<br>
+ applies for Rock Ridge ISOs.<br>
+ udf_path - The UDF absolute path to use for the directory.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_eltorito"><strong>add_eltorito</strong></a>(self, bootfile_path, bootcatfile=None, rr_bootcatname=None, joliet_bootcatfile=None, boot_load_size=None, platform_id=0, boot_info_table=False, efi=False, media_name='noemul', bootable=True, boot_load_seg=0, udf_bootcatfile=None)</dt><dd><span class="code">Add an El Torito Boot Record, and associated files, to the ISO. The<br>
+file that will be used as the bootfile must be passed into this function<br>
+and must already be present on the ISO.<br>
+ <br>
+Parameters:<br>
+ bootfile_path - The file to use as the boot file; it must already<br>
+ exist on this ISO.<br>
+ bootcatfile - The fake file to use as the boot catalog entry; set to<br>
+ BOOT.CAT;1 by default.<br>
+ rr_bootcatname - The Rock Ridge name for the fake file to use as the<br>
+ boot catalog entry; set to 'boot.cat' by default.<br>
+ joliet_bootcatfile - The Joliet name for the fake file to use as the<br>
+ boot catalog entry; set to 'boot.cat' by default.<br>
+ boot_load_size - The number of sectors to use for the boot entry; if<br>
+ set to None (the default), the number of sectors will<br>
+ be calculated.<br>
+ platform_id - The platform ID to set for the El Torito entry; 0 is for<br>
+ x86, 1 is for Power PC, 2 is for Mac, and 0xef is for<br>
+ UEFI. 0 is the default.<br>
+ boot_info_table - Whether to add a boot info table to the ISO. The<br>
+ default is False.<br>
+ efi - Whether this is an EFI entry for El Torito. The default is False.<br>
+ media_name - The name of the media type, one of 'noemul', 'floppy', or 'hdemul'.<br>
+ bootable - Whether the boot media is bootable. The default is True.<br>
+ boot_load_seg - The load segment address of the boot image.<br>
+ udf_bootcatfile - The name of the boot.cat file on the UDF filesystem.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_file"><strong>add_file</strong></a>(self, filename, iso_path=None, rr_name=None, joliet_path=None, file_mode=None, udf_path=None)</dt><dd><span class="code">Add a file to the ISO. If the ISO is a Rock Ridge one, then a Rock<br>
+Ridge name must also be provided. If the ISO is a Joliet one, then a<br>
+Joliet path may also be provided; while it is optional to do so, it is<br>
+highly recommended.<br>
+ <br>
+Parameters:<br>
+ filename - The filename to use for the data contents for the new file.<br>
+ iso_path - The ISO9660 absolute path to the file destination on the ISO.<br>
+ rr_name - The Rock Ridge name of the file destination on the ISO.<br>
+ joliet_path - The Joliet absolute path to the file destination on the ISO.<br>
+ file_mode - The POSIX file_mode to apply to this file. This only<br>
+ applies if this is a Rock Ridge ISO. If this is None (the<br>
+ default), the permissions from the original file are used.<br>
+ udf_path - The UDF name of the file destination on the ISO.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_fp"><strong>add_fp</strong></a>(self, fp, length, iso_path=None, rr_name=None, joliet_path=None, file_mode=None, udf_path=None)</dt><dd><span class="code">Add a file to the ISO. If the ISO is a Rock Ridge one, then a Rock<br>
+Ridge name must also be provided. If the ISO is a Joliet one, then a<br>
+Joliet path may also be provided; while it is optional to do so, it is<br>
+highly recommended. Note that the caller must ensure that 'fp' remains<br>
+open for the lifetime of the <a href="#PyCdlib">PyCdlib</a> <a href="builtins.html#object">object</a>, as the <a href="#PyCdlib">PyCdlib</a> class uses<br>
+the file descriptor internally when writing (mastering) the ISO. To<br>
+have <a href="#PyCdlib">PyCdlib</a> manage this automatically, use 'add_file' instead.<br>
+ <br>
+Parameters:<br>
+ fp - The file <a href="builtins.html#object">object</a> to use for the contents of the new file.<br>
+ length - The length of the data for the new file.<br>
+ iso_path - The ISO9660 absolute path to the file destination on the ISO.<br>
+ rr_name - The Rock Ridge name of the file destination on the ISO.<br>
+ joliet_path - The Joliet absolute path to the file destination on the ISO.<br>
+ file_mode - The POSIX file_mode to apply to this file. This only<br>
+ applies if this is a Rock Ridge ISO. If this is None (the<br>
+ default), the permissions from the original file are used.<br>
+ udf_path - The UDF name of the file destination on the ISO.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_hard_link"><strong>add_hard_link</strong></a>(self, **kwargs)</dt><dd><span class="code">Add a hard link to the ISO. Hard links are alternate names for the<br>
+same file contents that don't take up any additional space on the the<br>
+ISO. This API can be used to create hard links between two files on<br>
+the ISO9660 filesystem, between two files on the Joliet filesystem, or<br>
+between a file on the ISO9660 filesystem and the Joliet filesystem.<br>
+In all cases, exactly one old path must be specified, and exactly one<br>
+new path must be specified.<br>
+Note that this is an advanced API, so using it in combination with the<br>
+higher-level APIs (like <a href="#PyCdlib-rm_file">rm_file</a>()) may result in unexpected behavior.<br>
+Once this API has been used, this API and <a href="#PyCdlib-rm_hard_link">rm_hard_link</a>() should be<br>
+preferred over <a href="#PyCdlib-add_file">add_file</a>() and <a href="#PyCdlib-rm_file">rm_file</a>(), respectively.<br>
+ <br>
+Parameters:<br>
+ iso_old_path - The old path on the ISO9660 filesystem to link from.<br>
+ iso_new_path - The new path on the ISO9660 filesystem to link to.<br>
+ joliet_old_path - The old path on the Joliet filesystem to link from.<br>
+ joliet_new_path - The new path on the Joliet filesystem to link to.<br>
+ rr_name - The Rock Ridge name to use for the new file if this is a Rock<br>
+ Ridge ISO and the new path is on the ISO9660 filesystem.<br>
+ boot_catalog_old - Use the El Torito boot catalog as the old path.<br>
+ udf_old_path - The old path on the UDF filesystem to link from.<br>
+ udf_new_path - The new path on the UDF filesystem to link to.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_isohybrid"><strong>add_isohybrid</strong></a>(self, part_entry=1, mbr_id=None, part_offset=0, geometry_sectors=32, geometry_heads=64, part_type=None, mac=False, efi=None)</dt><dd><span class="code">Make an ISO a 'hybrid', which means that it can be booted either from a<br>
+CD or from more traditional media (like a USB stick). This requires<br>
+that the ISO already have El Torito, and will use the El Torito boot<br>
+file as a bootable image. That image must contain a certain signature<br>
+in order to work as a hybrid (if using syslinux, this generally means<br>
+the isohdpfx.bin files).<br>
+ <br>
+Parameters:<br>
+ part_entry - The partition entry to use; one by default.<br>
+ mbr_id - The mbr_id to use. If set to None (the default), a random one<br>
+ will be generated.<br>
+ part_offset - The partition offset to use; zero by default.<br>
+ geometry_sectors - The number of sectors to assign; thirty-two by<br>
+ default.<br>
+ geometry_heads - The number of heads to assign; sixty-four by default.<br>
+ part_type - The partition type to assign; twenty-three by default, but<br>
+ will automatically be set to 0 if mac or efi are True.<br>
+ mac - Add support for Mac; False by default.<br>
+ efi - Add support for EFI; False by default, but will automatically<br>
+ be set to True if mac is True.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_joliet_directory"><strong>add_joliet_directory</strong></a>(self, joliet_path)</dt><dd><span class="code">(deprecated) Add a directory to the Joliet portion of the ISO. Since<br>
+Joliet occupies a completely different context than ISO9660, this<br>
+method can be invoked to create a completely different directory<br>
+structure in the Joliet context, though that is generally not advised.<br>
+It is recommended to use the 'joliet_path' argument of the<br>
+'add_directory' instead of this method.<br>
+ <br>
+Parameters:<br>
+ joliet_path - The Joliet directory to create.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-add_symlink"><strong>add_symlink</strong></a>(self, symlink_path=None, rr_symlink_name=None, rr_path=None, joliet_path=None, udf_symlink_path=None, udf_target=None)</dt><dd><span class="code">Add a symlink from rr_symlink_name to the rr_path. The ISO must have<br>
+either Rock Ridge or UDF support (or both).<br>
+ <br>
+Parameters:<br>
+ symlink_path - The ISO9660 path of the symlink itself on the ISO.<br>
+ rr_symlink_name - The Rock Ridge name of the symlink itself on the ISO.<br>
+ rr_path - The path that the symlink points to on the Rock Ridge part<br>
+ of the ISO.<br>
+ joliet_path - The Joliet path of the symlink (if this ISO has Joliet).<br>
+ udf_symlink_path - The UDF path of the symlink itself on the ISO.<br>
+ udf_target - The UDF name of the entry on the ISO that the symlink<br>
+ points to.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-clear_hidden"><strong>clear_hidden</strong></a>(self, iso_path=None, rr_path=None, joliet_path=None)</dt><dd><span class="code">Clear the ISO9660 hidden attribute on a file or directory. This will<br>
+cause the file or directory to show up when listing entries on the ISO.<br>
+Exactly one of iso_path, rr_path, or joliet_path must be specified.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path on the ISO to clear the hidden bit from.<br>
+ rr_path - The Rock Ridge path on the ISO to clear the hidden bit from.<br>
+ joliet_path - The Joliet path on the ISO to clear the hidden bit from.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-close"><strong>close</strong></a>(self)</dt><dd><span class="code">Close the <a href="#PyCdlib">PyCdlib</a> <a href="builtins.html#object">object</a>, and re-initialize the <a href="builtins.html#object">object</a> to the defaults.<br>
+The <a href="builtins.html#object">object</a> can then be re-used for manipulation of another ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-duplicate_pvd"><strong>duplicate_pvd</strong></a>(self)</dt><dd><span class="code">Add a duplicate PVD to the ISO. This is a mostly useless feature<br>
+allowed by Ecma-119 to have duplicate PVDs to avoid possible corruption.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-file_mode"><strong>file_mode</strong></a>(self, **kwargs)</dt><dd><span class="code">Get the POSIX file mode of the file if is a Rock Ridge file.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute ISO path to the file on the ISO.<br>
+ rr_path - The absolute Rock Ridge path to the file on the ISO.<br>
+ joliet_path - The absolute Joliet path to the file on the ISO.<br>
+ udf_path - The absolute UDF path to the file on the ISO.<br>
+Returns:<br>
+ An integer representing the POSIX file mode of the file if it is Rock<br>
+ Ridge, or None otherwise.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-force_consistency"><strong>force_consistency</strong></a>(self)</dt><dd><span class="code">Make sure the ISO <a href="builtins.html#object">object</a> is fully consistent. <a href="#PyCdlib">PyCdlib</a> typically delays<br>
+doing work until it is necessary, and this detail is usually hidden<br>
+from users. However, there are times that a user may want a fully<br>
+consistent view of the ISO without calling one of the methods that<br>
+forces consistency. This method allows the user to force a consistent<br>
+view of this <a href="builtins.html#object">object</a>.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-full_path_from_dirrecord"><strong>full_path_from_dirrecord</strong></a>(self, rec, rockridge=False)</dt><dd><span class="code">Get the absolute path of a directory record.<br>
+ <br>
+Parameters:<br>
+ rec - The directory record to get the full path for.<br>
+ rockridge - Whether to get the rock ridge full path.<br>
+Returns:<br>
+ A string representing the absolute path to the file on the ISO.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_and_write"><strong>get_and_write</strong></a>(self, iso_path, local_path, blocksize=8192)</dt><dd><span class="code">(deprecated) Fetch a single file from the ISO and write it out to the<br>
+specified file. Note that this will overwrite the contents of the local<br>
+file if it already exists. Also note that 'iso_path' must be an<br>
+absolute path to the file. Finally, the 'iso_path' can be an ISO9660<br>
+path, a Rock Ridge path, or a Joliet path. In the case of ambiguity,<br>
+the Joliet path is tried first, followed by the ISO9660 path, followed<br>
+by the Rock Ridge path. It is recommended to use the get_file_from_iso<br>
+API instead to resolve this ambiguity.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute path to the file to get data from.<br>
+ local_path - The local filename to write the contents to.<br>
+ blocksize - The blocksize to use when copying data; the default is 8192.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_and_write_fp"><strong>get_and_write_fp</strong></a>(self, iso_path, outfp, blocksize=8192)</dt><dd><span class="code">(deprecated) Fetch a single file from the ISO and write it out to the<br>
+file <a href="builtins.html#object">object</a>. Note that 'iso_path' must be an absolute path to the file.<br>
+Also note that the 'iso_path' can be an ISO9660 path, a Rock Ridge path,<br>
+or a Joliet path. In the case of ambiguity, the Joliet path is tried<br>
+first, followed by the ISO9660 path, followed by the Rock Ridge path.<br>
+It is recommend to use the get_file_from_iso_fp API instead to resolve<br>
+this ambiguity.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute path to the file to get data from.<br>
+ outfp - The file <a href="builtins.html#object">object</a> to write data to.<br>
+ blocksize - The blocksize to use when copying data; the default is 8192.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_entry"><strong>get_entry</strong></a>(self, iso_path, joliet=False)</dt><dd><span class="code">(deprecated) Get the directory record for a particular path. It is<br>
+recommended to use the 'get_record' API instead.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path on the ISO to look up information for.<br>
+ joliet - Whether to look for the path in the Joliet portion of the ISO.<br>
+Returns:<br>
+ A dr.DirectoryRecord <a href="builtins.html#object">object</a> representing the path.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_file_from_iso"><strong>get_file_from_iso</strong></a>(self, local_path, **kwargs)</dt><dd><span class="code">Fetch a single file from the ISO and write it out to a local file.<br>
+ <br>
+Parameters:<br>
+ local_path - The local file to write to.<br>
+ blocksize - The number of bytes in each transfer.<br>
+ iso_path - The absolute ISO9660 path to lookup on the ISO (exclusive<br>
+ with rr_path, joliet_path, and udf_path).<br>
+ rr_path - The absolute Rock Ridge path to lookup on the ISO (exclusive<br>
+ with iso_path, joliet_path, and udf_path).<br>
+ joliet_path - The absolute Joliet path to lookup on the ISO (exclusive<br>
+ with iso_path, rr_path, and udf_path).<br>
+ udf_path - The absolute UDF path to lookup on the ISO (exclusive with<br>
+ iso_path, rr_path, and joliet_path).<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_file_from_iso_fp"><strong>get_file_from_iso_fp</strong></a>(self, outfp, **kwargs)</dt><dd><span class="code">Fetch a single file from the ISO and write it out to the file <a href="builtins.html#object">object</a>.<br>
+ <br>
+Parameters:<br>
+ outfp - The file <a href="builtins.html#object">object</a> to write data to.<br>
+ blocksize - The number of bytes in each transfer.<br>
+ iso_path - The absolute ISO9660 path to lookup on the ISO (exclusive<br>
+ with rr_path, joliet_path, and udf_path).<br>
+ rr_path - The absolute Rock Ridge path to lookup on the ISO (exclusive<br>
+ with iso_path, joliet_path, and udf_path).<br>
+ joliet_path - The absolute Joliet path to lookup on the ISO (exclusive<br>
+ with iso_path, rr_path, and udf_path).<br>
+ udf_path - The absolute UDF path to lookup on the ISO (exclusive with<br>
+ iso_path, rr_path, and joliet_path).<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_iso9660_facade"><strong>get_iso9660_facade</strong></a>(self)</dt><dd><span class="code">Return a 'facade' that simplifies some of the complexities of the<br>
+<a href="#PyCdlib">PyCdlib</a> class, while giving up some of the full power. This facade<br>
+only allows manipulation of the ISO9660 portions of the ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ A PyCdlibISO9660 <a href="builtins.html#object">object</a> that can be used to interact with the ISO.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_joliet_facade"><strong>get_joliet_facade</strong></a>(self)</dt><dd><span class="code">Return a 'facade' that simplifies some of the complexities of the<br>
+<a href="#PyCdlib">PyCdlib</a> class, while giving up some of the full power. This facade<br>
+only allows manipulation of the Joliet portions of the ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ A PyCdlibJoliet <a href="builtins.html#object">object</a> that can be used to interact with the ISO.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_record"><strong>get_record</strong></a>(self, **kwargs)</dt><dd><span class="code">Get the directory record for a particular path.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute path on the ISO9660 filesystem to get the<br>
+ record for.<br>
+ rr_path - The absolute path on the Rock Ridge filesystem to get the<br>
+ record for.<br>
+ joliet_path - The absolute path on the Joliet filesystem to get the<br>
+ record for.<br>
+ udf_path - The absolute path on the UDF filesystem to get the record<br>
+ for.<br>
+Returns:<br>
+ An <a href="builtins.html#object">object</a> that represents the path. This may be a dr.DirectoryRecord<br>
+ <a href="builtins.html#object">object</a> (in the cases of iso_path, rr_path, or joliet_path), or a<br>
+ udf.UDFFileEntry <a href="builtins.html#object">object</a> (in the case of udf_path).</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_rock_ridge_facade"><strong>get_rock_ridge_facade</strong></a>(self)</dt><dd><span class="code">Return a 'facade' that simplifies some of the complexities of the<br>
+<a href="#PyCdlib">PyCdlib</a> class, while giving up some of the full power. This facade<br>
+only allows manipulation of the Rock Ridge portions of the ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ A PyCdlibRockRidge <a href="builtins.html#object">object</a> that can be used to interact with the ISO.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-get_udf_facade"><strong>get_udf_facade</strong></a>(self)</dt><dd><span class="code">Return a 'facade' that simplifies some of the complexities of the<br>
+<a href="#PyCdlib">PyCdlib</a> class, while giving up some of the full power. This facade<br>
+only allows manipulation of the UDF portions of the ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ A PyCdlibUDF <a href="builtins.html#object">object</a> that can be used to interact with the ISO.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-has_joliet"><strong>has_joliet</strong></a>(self)</dt><dd><span class="code">Returns whether this ISO has Joliet extensions.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ True if this ISO has Joliet, False otherwise.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-has_rock_ridge"><strong>has_rock_ridge</strong></a>(self)</dt><dd><span class="code">Returns whether this ISO has Rock Ridge extensions.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ True if this ISO has Rock Ridge extensions, False otherwise.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-has_udf"><strong>has_udf</strong></a>(self)</dt><dd><span class="code">Returns whether this ISO has UDF extensions.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ True if this ISO has UDF, False otherwise.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-list_children"><strong>list_children</strong></a>(self, **kwargs)</dt><dd><span class="code">Generate a list of all of the file/directory objects in the<br>
+specified location on the ISO.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute path on the ISO to list the children for.<br>
+ rr_path - The absolute Rock Ridge path on the ISO to list the children for.<br>
+ joliet_path - The absolute Joliet path on the ISO to list the children for.<br>
+ udf_path - The absolute UDF path on the ISO to list the children for.<br>
+Yields:<br>
+ Children of this path.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-list_dir"><strong>list_dir</strong></a>(self, iso_path, joliet=False)</dt><dd><span class="code">(deprecated) Generate a list of all of the file/directory objects in the<br>
+specified location on the ISO. It is recommended to use the<br>
+'list_children' API instead.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path on the ISO to look up information for.<br>
+ joliet - Whether to look for the path in the Joliet portion of the ISO.<br>
+Yields:<br>
+ Children of this path.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-modify_file_in_place"><strong>modify_file_in_place</strong></a>(self, fp, length, iso_path, rr_name=None, joliet_path=None, udf_path=None)</dt><dd><span class="code">An API to modify a file in place on the ISO. This can be extremely fast<br>
+(much faster than calling the write method), but has many restrictions.<br>
+ <br>
+1. The original ISO file pointer must have been opened for reading<br>
+ and writing.<br>
+2. Only an existing *file* can be modified; directories cannot be<br>
+ changed.<br>
+3. Only an existing file can be *modified*; no new files can be added<br>
+ or removed.<br>
+4. The new file contents must use the same number of extents (typically<br>
+ 2048 bytes) as the old file contents. If using this API to shrink<br>
+ a file, this is usually easy since the new contents can be padded<br>
+ out with zeros or newlines to meet the requirement. If using this<br>
+ API to grow a file, the new contents can only grow up to the next<br>
+ extent boundary.<br>
+ <br>
+Unlike all other APIs in <a href="#PyCdlib">PyCdlib</a>, this API actually modifies the<br>
+originally opened on-disk file, so use it with caution.<br>
+ <br>
+Parameters:<br>
+ fp - The file <a href="builtins.html#object">object</a> to use for the contents of the new file.<br>
+ length - The length of the new data for the file.<br>
+ iso_path - The ISO9660 absolute path to the file destination on the ISO.<br>
+ rr_name - The Rock Ridge name of the file destination on the ISO.<br>
+ joliet_path - The Joliet absolute path to the file destination on the ISO.<br>
+ udf_path - The UDF absolute path to the file destination on the ISO.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-new"><strong>new</strong></a>(self, interchange_level=1, sys_ident='', vol_ident='', set_size=1, seqnum=1, log_block_size=2048, vol_set_ident=' ', pub_ident_str='', preparer_ident_str='', app_ident_str='', copyright_file='', abstract_file='', bibli_file='', vol_expire_date=None, app_use='', joliet=None, rock_ridge=None, xa=False, udf=None)</dt><dd><span class="code">Create a new ISO from scratch.<br>
+ <br>
+Parameters:<br>
+ interchange_level - The ISO9660 interchange level to use; this dictates<br>
+ the rules on the names of files. Levels 1, 2, 3,<br>
+ and 4 are supported. Level 1 is the most<br>
+ conservative, and is the default, but level 3 is<br>
+ recommended.<br>
+ sys_ident - The system identification string to use on the new ISO.<br>
+ vol_ident - The volume identification string to use on the new ISO.<br>
+ set_size - The size of the set of ISOs this ISO is a part of.<br>
+ seqnum - The sequence number of the set of this ISO.<br>
+ log_block_size - The logical block size to use for the ISO. While ISO9660<br>
+ technically supports sizes other than 2048 (the default),<br>
+ this almost certainly doesn't work.<br>
+ vol_set_ident - The volume set identification string to use on the new ISO.<br>
+ pub_ident_str - The publisher identification string to use on the new ISO.<br>
+ preparer_ident_str - The preparer identification string to use on the new ISO.<br>
+ app_ident_str - The application identification string to use on the new ISO.<br>
+ copyright_file - The name of a file at the root of the ISO to use as the<br>
+ copyright file.<br>
+ abstract_file - The name of a file at the root of the ISO to use as the<br>
+ abstract file.<br>
+ bibli_file - The name of a file at the root of the ISO to use as the<br>
+ bibliographic file.<br>
+ vol_expire_date - The date that this ISO will expire at.<br>
+ app_use - Arbitrary data that the application can stuff into the primary<br>
+ volume descriptor of this ISO.<br>
+ joliet - A integer that can have the value 1, 2, or 3 for Joliet<br>
+ levels 1, 2, or 3 (3 is by far the most common), or None for<br>
+ no Joliet support (the default). For legacy reasons, this<br>
+ parameter also accepts a boolean, where the value of 'False'<br>
+ means no Joliet and a value of 'True' means level 3.<br>
+ rock_ridge - Whether to make this ISO have the Rock Ridge extensions or<br>
+ not. The default value of None does not add Rock Ridge<br>
+ extensions. A string value of '1.09', '1.10', or '1.12'<br>
+ adds the specified Rock Ridge version to the ISO. If<br>
+ unsure, pass '1.09' to ensure maximum compatibility.<br>
+ xa - Whether to add the ISO9660 Extended Attribute extensions to this<br>
+ ISO. The default is False.<br>
+ udf - Whether to add UDF support to this ISO. If it is None (the<br>
+ default), no UDF support is added. If it is "2.60", version 2.60<br>
+ of the UDF spec is used. All other values are disallowed.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-open"><strong>open</strong></a>(self, filename, mode='rb')</dt><dd><span class="code">Open up an existing ISO for inspection and modification.<br>
+ <br>
+Parameters:<br>
+ filename - The filename containing the ISO to open up.<br>
+ mode - The mode to use when opening the ISO file; the default is 'rb'.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-open_file_from_iso"><strong>open_file_from_iso</strong></a>(self, **kwargs)</dt><dd><span class="code">Open a file for reading in a context manager. This allows the user to<br>
+operate on the file in user-defined chunks (utilizing the read() method<br>
+of the returned context manager).<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute ISO path to the file on the ISO.<br>
+ rr_path - The absolute Rock Ridge path to the file on the ISO.<br>
+ joliet_path - The absolute Joliet path to the file on the ISO.<br>
+ udf_path - The absolute UDF path to the file on the ISO.<br>
+Returns:<br>
+ A PyCdlibIO <a href="builtins.html#object">object</a> allowing access to the file.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-open_fp"><strong>open_fp</strong></a>(self, fp)</dt><dd><span class="code">Open up an existing ISO for inspection and modification. Note that the<br>
+file <a href="builtins.html#object">object</a> passed in here must stay open for the lifetime of this<br>
+<a href="builtins.html#object">object</a>, as the <a href="#PyCdlib">PyCdlib</a> class uses it internally to do writing and reading<br>
+operations. To have <a href="#PyCdlib">PyCdlib</a> manage this automatically, use 'open'<br>
+instead.<br>
+ <br>
+Parameters:<br>
+ fp - The file <a href="builtins.html#object">object</a> containing the ISO to open up.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_directory"><strong>rm_directory</strong></a>(self, iso_path=None, rr_name=None, joliet_path=None, udf_path=None)</dt><dd><span class="code">Remove a directory from the ISO. The directory must be empty.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path to the directory to remove.<br>
+ rr_name - The Rock Ridge name of the directory to remove.<br>
+ joliet_path - The Joliet path to the directory to remove.<br>
+ udf_path - The UDF absolute path to the directory to remove.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_eltorito"><strong>rm_eltorito</strong></a>(self)</dt><dd><span class="code">Remove the El Torito boot record (and Boot Catalog) from the ISO.<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_file"><strong>rm_file</strong></a>(self, iso_path=None, rr_name=None, joliet_path=None, udf_path=None)</dt><dd><span class="code">Remove a file from the ISO. This removes the data and the listing of<br>
+the file from all contexts, even when only one path is given (to only<br>
+remove it from a single context, use <a href="#PyCdlib-rm_hard_link">rm_hard_link</a>() instead). Due to<br>
+some complexities of the ISO format, removal of zero-byte files from all<br>
+contexts does not automatically happen, so this method may need to be<br>
+called more than once for zero-byte files.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path to the file to remove.<br>
+ rr_name - The Rock Ridge name of the file to remove.<br>
+ joliet_path - The Joliet path to the file to remove.<br>
+ udf_path - The UDF path to the file to remove.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_hard_link"><strong>rm_hard_link</strong></a>(self, iso_path=None, joliet_path=None, udf_path=None)</dt><dd><span class="code">Remove a hard link from the ISO. If the number of links to a piece of<br>
+data drops to zero, then the contents will be removed from the ISO.<br>
+This can be thought of as a lower-level interface to <a href="#PyCdlib-rm_file">rm_file</a>(). Either<br>
+an ISO9660 path or a Joliet path must be passed to this API, but not<br>
+both. Thus, this interface can be used to hide files from either the<br>
+ISO9660 filesystem, the Joliet filesystem, or both (if there is another<br>
+reference to the data on the ISO, such as in El Torito). Note that this<br>
+is an advanced API, so using it in combination with the higher-level APIs<br>
+(like <a href="#PyCdlib-rm_file">rm_file</a>()) may result in unexpected behavior. Once this API has<br>
+been used, this API and <a href="#PyCdlib-add_hard_link">add_hard_link</a>() should be preferred over<br>
+<a href="#PyCdlib-rm_file">rm_file</a>() and <a href="#PyCdlib-add_file">add_file</a>().<br>
+ <br>
+Parameters:<br>
+ iso_path - The ISO link path to remove.<br>
+ joliet_path - The Joliet link path to remove.<br>
+ udf_path - The UDF link path to remove.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_isohybrid"><strong>rm_isohybrid</strong></a>(self)</dt><dd><span class="code">Remove the 'hybridization' of an ISO, making it a traditional ISO again.<br>
+This means the ISO will no longer be able to be copied and booted off<br>
+of traditional media (like USB sticks).<br>
+ <br>
+Parameters:<br>
+ None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-rm_joliet_directory"><strong>rm_joliet_directory</strong></a>(self, joliet_path)</dt><dd><span class="code">(deprecated) Remove a Joliet directory from the ISO. It is recommended<br>
+to use the 'joliet_path' parameter to 'rm_directory' instead.<br>
+ <br>
+Parameters:<br>
+ joliet_path - The Joliet path to the directory to remove.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-set_hidden"><strong>set_hidden</strong></a>(self, iso_path=None, rr_path=None, joliet_path=None)</dt><dd><span class="code">Set the ISO9660 hidden attribute on a file or directory. This will<br>
+cause the file or directory not to show up when listing entries on the<br>
+ISO. Exactly one of iso_path, rr_path, or joliet_path must be specified.<br>
+ <br>
+Parameters:<br>
+ iso_path - The path on the ISO to set the hidden bit on.<br>
+ rr_path - The Rock Ridge path on the ISO to set the hidden bit on.<br>
+ joliet_path - The Joliet path on the ISO to set the hidden bit on.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-set_relocated_name"><strong>set_relocated_name</strong></a>(self, name, rr_name)</dt><dd><span class="code">Set the name of the relocated directory on a Rock Ridge ISO. The ISO<br>
+must be a Rock Ridge one, and must not have previously had the relocated<br>
+name set.<br>
+ <br>
+Parameters:<br>
+ name - The name for a relocated directory.<br>
+ rr_name - The Rock Ridge name for a relocated directory.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-walk"><strong>walk</strong></a>(self, **kwargs)</dt><dd><span class="code">Walk the entries on the ISO, starting at the given path. One, and only<br>
+one, of iso_path, rr_path, joliet_path, and udf_path is allowed.<br>
+Similar to os.<a href="#PyCdlib-walk">walk</a>(), yield a 3-tuple of (path-to-here, dirlist, filelist)<br>
+for each directory level.<br>
+ <br>
+Parameters:<br>
+ iso_path - The absolute ISO path to the starting entry on the ISO.<br>
+ rr_path - The absolute Rock Ridge path to the starting entry on the ISO.<br>
+ joliet_path - The absolute Joliet path to the starting entry on the ISO.<br>
+ udf_path - The absolute UDF path to the starting entry on the ISO.<br>
+ encoding - The encoding to use for returned strings.<br>
+Yields:<br>
+ 3-tuples of (path-to-here, dirlist, filelist)<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-write"><strong>write</strong></a>(self, filename, blocksize=32768, progress_cb=None, progress_opaque=None)</dt><dd><span class="code">Write a properly formatted ISO out to the filename passed in. This<br>
+also goes by the name of 'mastering'.<br>
+ <br>
+Parameters:<br>
+ filename - The filename to write the data to.<br>
+ blocksize - The blocksize to use when copying data; the default is 32768.<br>
+ progress_cb - If not None, a function to call as the write call does its<br>
+ work. The callback function must have a signature of:<br>
+ def func(done, total, opaque).<br>
+ progress_opaque - User data to be passed to the progress callback; the<br>
+ default is None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<dl><dt><a name="PyCdlib-write_fp"><strong>write_fp</strong></a>(self, outfp, blocksize=32768, progress_cb=None, progress_opaque=None)</dt><dd><span class="code">Write a properly formatted ISO out to the file <a href="builtins.html#object">object</a> passed in. This<br>
+also goes by the name of 'mastering'.<br>
+ <br>
+Parameters:<br>
+ outfp - The file <a href="builtins.html#object">object</a> to write the data to.<br>
+ blocksize - The blocksize to use when copying data; the default is 32768.<br>
+ progress_cb - If not None, a function to call as the write call does its<br>
+ work. The callback function must have a signature of:<br>
+ def func(done, total, opaque).<br>
+ progress_opaque - User data to be passed to the progress callback; the<br>
+ default is None.<br>
+Returns:<br>
+ Nothing.</span></dd></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>brs</strong></dt>
+</dl>
+<dl><dt><strong>eltorito_boot_catalog</strong></dt>
+</dl>
+<dl><dt><strong>enhanced_vd</strong></dt>
+</dl>
+<dl><dt><strong>inodes</strong></dt>
+</dl>
+<dl><dt><strong>interchange_level</strong></dt>
+</dl>
+<dl><dt><strong>isohybrid_mbr</strong></dt>
+</dl>
+<dl><dt><strong>joliet_vd</strong></dt>
+</dl>
+<dl><dt><strong>logical_block_size</strong></dt>
+</dl>
+<dl><dt><strong>pvd</strong></dt>
+</dl>
+<dl><dt><strong>pvds</strong></dt>
+</dl>
+<dl><dt><strong>rock_ridge</strong></dt>
+</dl>
+<dl><dt><strong>svds</strong></dt>
+</dl>
+<dl><dt><strong>udf_anchors</strong></dt>
+</dl>
+<dl><dt><strong>udf_beas</strong></dt>
+</dl>
+<dl><dt><strong>udf_boots</strong></dt>
+</dl>
+<dl><dt><strong>udf_file_set</strong></dt>
+</dl>
+<dl><dt><strong>udf_file_set_terminator</strong></dt>
+</dl>
+<dl><dt><strong>udf_logical_volume_integrity</strong></dt>
+</dl>
+<dl><dt><strong>udf_logical_volume_integrity_terminator</strong></dt>
+</dl>
+<dl><dt><strong>udf_main_descs</strong></dt>
+</dl>
+<dl><dt><strong>udf_nsr</strong></dt>
+</dl>
+<dl><dt><strong>udf_reserve_descs</strong></dt>
+</dl>
+<dl><dt><strong>udf_root</strong></dt>
+</dl>
+<dl><dt><strong>udf_teas</strong></dt>
+</dl>
+<dl><dt><strong>vdsts</strong></dt>
+</dl>
+<dl><dt><strong>version_vd</strong></dt>
+</dl>
+<dl><dt><strong>xa</strong></dt>
+</dl>
+</td></tr></table></td></tr></table></div>
+</body></html>
diff --git a/devtools/contrib/pycdlib/docs/pycdlib-explorer.html b/devtools/contrib/pycdlib/docs/pycdlib-explorer.html
new file mode 100644
index 00000000000..1c65212bb33
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/pycdlib-explorer.html
@@ -0,0 +1,288 @@
+<!-- Creator : groff version 1.23.0 -->
+<!-- CreationDate: Sun Mar 2 21:34:04 2025 -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
+<meta name="Content-Style" content="text/css">
+<style type="text/css">
+ p { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ pre { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ table { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ h1 { text-align: center }
+</style>
+<title>PYCDLIB-EXPLORER</title>
+
+</head>
+<body>
+
+<h1 align="center">PYCDLIB-EXPLORER</h1>
+
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#COMMANDS">COMMANDS</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+
+<hr>
+
+
+<h2>NAME
+<a name="NAME"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-explorer
+- tool to examine and modify ISOs using pycdlib</p>
+
+<h2>SYNOPSIS
+<a name="SYNOPSIS"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>pycdlib-explorer
+<iso-file></b></p>
+
+<h2>DESCRIPTION
+<a name="DESCRIPTION"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">This is a tool
+to examine and modify existing ISO files on disk. Using this
+tool, the files, directories, and metadata on an ISO can be
+examined, new files can be added, and old files can be
+deleted. Note that due to the nature of the ISO standard,
+files or directories on the ISO cannot be modified in place
+in a general way. To accomplish this, remove the file and
+then re-add it with new contents.</p>
+
+<p style="margin-left:9%; margin-top: 1em">The commands
+that change the contents of the ISO only modify the
+in-memory copy. Changes are written out to a new ISO file
+when the <b>write</b> command is issued.</p>
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-explorer
+has no command-line options; instead, it is controlled
+entirely at runtime through commands. The following section
+describes the available commands in pycdlib-explorer.</p>
+
+<h2>COMMANDS
+<a name="COMMANDS"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>add_file
+<iso_path> <src_filename>
+[rr_name=<rr_name>] <br>
+[joliet_path=<joliet_path>]</b></p>
+
+<p style="margin-left:18%;">Add the contents of
+<b>src_filename</b> to the ISO at the location specified in
+<b>iso_path</b>. If the ISO is a Rock Ridge ISO,
+<b>rr_name</b> must be specified; otherwise, it must not be.
+If the ISO is not a Joliet ISO, <b>joliet_path</b> must not
+be specified. If the ISO is a Joliet ISO, <b>joliet_path</b>
+is optional, but highly recommended to supply.</p>
+
+<p style="margin-left:9%;"><b>cd <iso_dir></b></p>
+
+<p style="margin-left:18%;">Change the current working
+directory to relative or absolute ISO path
+<b>iso_dir</b>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>cwd</b></p></td>
+<td width="4%"></td>
+<td width="45%">
+
+
+<p>Show the current working directory.</p></td>
+<td width="37%">
+</td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>exit</b></p></td>
+<td width="4%"></td>
+<td width="45%">
+
+
+<p>Exit out of pycdlib-explorer.</p></td>
+<td width="37%">
+</td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>get <iso_file>
+<out_file></b></p>
+
+<p style="margin-left:18%;">Copy the contents of the
+relative or absolute ISO path <b>iso_file</b> into
+<b>out_file</b>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>help</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>Print the available commands. Use "help
+<cmd>" for a more detailed description of the
+commands, including the command-line arguments they
+require.</p> </td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>ls</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>Show the contents of the current working directory. The
+format of the output is: TYPE(F=file, D=directory) NAME.</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>modify_file_in_place
+<iso_path> <src_filename></b></p>
+
+<p style="margin-left:18%;">Replace the contents of the
+file referred to in <b>iso_path</b> with the contents from
+<b>src_filename</b>. Warning: This command modifies the
+opened ISO file in-place. Also, the <b>src_filename</b> must
+occupy the same number of extents (2048-byte blocks) as the
+original file.</p>
+
+<p style="margin-left:9%;"><b>mkdir <iso_path>
+[rr_name=<rr_name>]
+[joliet_path=<joliet_path>]</b></p>
+
+<p style="margin-left:18%;">Make a new directory called
+<b>iso_path</b>. If the ISO is a Rock Ridge ISO,
+<b>rr_name</b> must be specified; otherwise, it must not be.
+If the ISO is not a Joliet ISO, <b>joliet_path</b> must not
+be specified. If the ISO is a Joliet ISO, <b>joliet_path</b>
+is optional, but highly recommended to supply.</p>
+
+<p style="margin-left:9%;"><b>print_mode
+[iso9660|rr|joliet|udf]</b></p>
+
+<p style="margin-left:18%;">Change which ’mode’
+of filenames are printed out. There are four main modes:
+ISO9660 (iso9660, the default), Rock Ridge (rr), Joliet
+(joliet), and UDF (udf). The original iso9660 mode only
+allows filenames of 8 characters, plus 3 for the extension.
+The Rock Ridge extensions allow much longer filenames and
+much deeper directory structures. The Joliet extensions also
+allow longer filenames and deeper directory structures, but
+in an entirely different context (though in most
+circumstances, the Joliet context will mirror the
+ISO9660/Rock Ridge context). The UDF Bridge extensions add
+an entirely parallel UDF context to the ISO as well. Any
+given ISO will always have ISO9660 mode, but may have any
+combination of Rock Ridge, Joliet, and UDF (including none
+of them). Running this command with no arguments prints out
+the current mode. Passing ’iso9660’ as an
+argument sets it to the original ISO9660 mode. Passing
+’rr’ as an argument sets it to Rock Ridge mode.
+Passing ’joliet’ as an argument sets it to
+Joliet mode. Passing ’udf’ as an argument sets
+it to UDF mode.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>quit</b></p></td>
+<td width="4%"></td>
+<td width="37%">
+
+
+<p>Exit out of pycdlib-explorer.</p></td>
+<td width="45%">
+</td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>rm_file
+<iso_path></b></p>
+
+<p style="margin-left:18%;">Remove the file named
+<b>iso_path</b> from the ISO. Note that this must be a file;
+to remove a directory, use <b>rmdir</b>.</p>
+
+<p style="margin-left:9%;"><b>rmdir
+<iso_path></b></p>
+
+<p style="margin-left:18%;">Remove the directory named
+<b>iso_path</b> from the ISO. Note that this must be a
+directory; to remove a file, use <b>rm_file</b>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>tree</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>List the contents of the ISO in a tree-like format,
+similar to the bash <b>tree</b> command.</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>write
+<out_file></b></p>
+
+<p style="margin-left:18%;">Write a valid ISO9660 file out
+to <b>out_file</b>, taking into account any changes made
+while running the program. This is also sometimes referred
+to as "mastering" the ISO. Note that the
+<b>out_file</b> must NOT be the same file as the input file,
+or the resulting ISO will not work properly.</p>
+
+<h2>SEE ALSO
+<a name="SEE ALSO"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-extract-files(1),
+pycdlib-genisoimage(1)</p>
+
+<h2>AUTHOR
+<a name="AUTHOR"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">Chris Lalancette
+<clalancette at gmail.com></p>
+<hr>
+</body>
+</html>
diff --git a/devtools/contrib/pycdlib/docs/pycdlib-extract-files.html b/devtools/contrib/pycdlib/docs/pycdlib-extract-files.html
new file mode 100644
index 00000000000..c9b40045bef
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/pycdlib-extract-files.html
@@ -0,0 +1,120 @@
+<!-- Creator : groff version 1.23.0 -->
+<!-- CreationDate: Sun Mar 2 21:34:04 2025 -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
+<meta name="Content-Style" content="text/css">
+<style type="text/css">
+ p { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ pre { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ table { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ h1 { text-align: center }
+</style>
+<title>PYCDLIB-EXTRACT-FILES</title>
+
+</head>
+<body>
+
+<h1 align="center">PYCDLIB-EXTRACT-FILES</h1>
+
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#OPTIONS">OPTIONS</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+
+<hr>
+
+
+<h2>NAME
+<a name="NAME"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-extract-files
+- tool to extract files from an ISO using pycdlib</p>
+
+<h2>SYNOPSIS
+<a name="SYNOPSIS"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>pycdlib-extract-files
+[OPTIONS] <iso-file></b></p>
+
+<h2>DESCRIPTION
+<a name="DESCRIPTION"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">This is a tool
+to extract files from an existing ISO file. Using this tool,
+the files and directories on an ISO can be extracted to the
+local filesystem.</p>
+
+<h2>OPTIONS
+<a name="OPTIONS"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−path−type</b>
+<i>[auto,iso9660,rockridge,joliet,udf]</i></p>
+
+<p style="margin-left:18%;">Specifies the path name
+convention to use while extracting the files. If the ISO to
+be extracted doesn’t contain the path type specified,
+an error is thrown. If not specified, defaults to auto,
+which first tries to extract files via the UDF path, then
+the Rock Ridge path, then the Joliet path, and finally falls
+back to the ISO9660 path if all else fails. Once one path
+type succeeds, all files will be extracted as that path
+type.</p>
+
+<p style="margin-left:9%;"><b>−start−path</b>
+<i><pathname></i></p>
+
+<p style="margin-left:18%;">The ISO path to start
+extracting from, specified in a Unix like path (something
+like "/foo/bar"). If not specified, extraction
+starts from the root of the ISO (equivalent to specifying
+"/"). The starting path must be a directory on the
+ISO. If −path−type is not specified, the path is
+an ISO9660 style path. If −path−type is
+specified, the start path specified here must be of that
+particular type.</p>
+
+<p style="margin-left:9%;"><b>−extract−to</b>
+<i><destpath></i></p>
+
+<p style="margin-left:18%;">The local directory to extract
+the data to. The "destpath" must exist and must be
+a directory, or an error will be thrown. If not specified,
+pycdlib−extract−files will extract the files to
+the current directory.</p>
+
+<h2>SEE ALSO
+<a name="SEE ALSO"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-explorer(1),
+pycdlib-genisoimage(1)</p>
+
+<h2>AUTHOR
+<a name="AUTHOR"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">Chris Lalancette
+<clalancette at gmail.com></p>
+<hr>
+</body>
+</html>
diff --git a/devtools/contrib/pycdlib/docs/pycdlib-genisoimage.html b/devtools/contrib/pycdlib/docs/pycdlib-genisoimage.html
new file mode 100644
index 00000000000..cec5c80839d
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/pycdlib-genisoimage.html
@@ -0,0 +1,2632 @@
+<!-- Creator : groff version 1.23.0 -->
+<!-- CreationDate: Sun Mar 2 21:34:04 2025 -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
+<meta name="Content-Style" content="text/css">
+<style type="text/css">
+ p { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ pre { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ table { margin-top: 0; margin-bottom: 0; vertical-align: top }
+ h1 { text-align: center }
+</style>
+<title>PYCDLIB-GENISOIMAGE</title>
+
+</head>
+<body>
+
+<h1 align="center">PYCDLIB-GENISOIMAGE</h1>
+
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#OPTIONS">OPTIONS</a><br>
+<a href="#HFS OPTIONS">HFS OPTIONS</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+
+<hr>
+
+
+<h2>NAME
+<a name="NAME"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em">pycdlib-genisoimage
+- tool to master ISOs using pycdlib</p>
+
+<h2>SYNOPSIS
+<a name="SYNOPSIS"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>pycdlib-genisoimage
+[options] [-o filename] pathspec [pathspec ...]</b></p>
+
+<h2>DESCRIPTION
+<a name="DESCRIPTION"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>pycdlib-genisoimage</b>
+is a pre-mastering program to generate ISO9660/Joliet/HFS
+hybrid filesystems. It is meant to be 100% flag-compatible
+with the original <b>genisoimage</b> program so that it can
+be dropped into existing scripts with no changes. Please see
+the man page for <b>genisoimage</b> for more detailed
+explanation of the options to this program. There are a few
+differences to note between this program and the original
+<b>genisoimage.</b> First, not all of the options are
+implemented in this program. This means that
+<b>pycdlib-genisoimage</b> will silently ignore some flags;
+for the most common usage of this program, this will not
+matter. However, if you are trying to do something odd and
+specific, it may not work. The flags that this applies to
+are noted in the OPTIONS below. In some cases these flags
+can be implemented with a bit of work, and in some cases the
+flags can never be implemented due to the design of pycdlib.
+If in doubt, please ask on
+https://github.com/clalancette/pycdlib/issues. Second,
+<b>pycdlib-genisoimage</b> does not output all of the same
+messages to standard out/standard error that
+<b>genisoimage</b> does. Any program that relies on parsing
+the output of <b>genisoimage</b> will probably not work.
+Third, <b>pycdlib-genisoimage</b> will not always generate
+ISOs that are 100% the same as the <b>genisoimage</b>
+counterparts. This is for a variety of reasons, ranging from
+bug fixing to simple differences in implementations. In
+almost all cases this does not matter, but please keep it in
+mind when using this program instead of
+<b>genisoimage.</b></p>
+
+<h2>OPTIONS
+<a name="OPTIONS"></a>
+</h2>
+
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−abstract</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">Specifies the abstract
+filename. There is space for 37 characters.</p>
+
+<p style="margin-left:9%;"><b>−A</b>
+<i>application_id</i> <b><br>
+−appid</b> <i>application_id</i></p>
+
+<p style="margin-left:18%;">Specifies a text string that
+will be written into the volume header. This should describe
+the application that will be on the disc. There is space for
+128 characters.</p>
+
+
+<p style="margin-left:9%;"><b>−allow−limited−size</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) When processing files larger than 2GiB
+which cannot be easily represented in ISO9660, add them with
+a shrunk visible file size to ISO9660 and with the correct
+visible file size to the UDF system. The result is an
+inconsistent filesystem and users need to make sure that
+they really use UDF rather than ISO9660 driver to read a
+such disk. Implies enabling <b>−udf.</b></p>
+
+
+<p style="margin-left:9%;"><b>−allow−leading−dots</b></p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−ldots</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Allow ISO9660
+filenames to begin with a period. Usually, a leading dot is
+replaced with an underscore in order to maintain MS-DOS
+compatibility.</p> </td></tr>
+</table>
+
+<p style="margin-left:18%;">This violates the ISO9660
+standard, but it happens to work on many systems. Use with
+caution.</p>
+
+
+<p style="margin-left:9%;"><b>−allow−lowercase</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) This options allows lowercase
+characters to appear in ISO9660 filenames. <br>
+This violates the ISO9660 standard, but it happens to work
+on some systems. Use with caution.</p>
+
+
+<p style="margin-left:9%;"><b>−allow−multidot</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) This options allows more than one dot
+to appear in ISO9660 filenames. A leading dot is not
+affected by this option, it may be allowed separately using
+<b>−allow−leading−dots</b>. <br>
+This violates the ISO9660 standard, but it happens to work
+on many systems. Use with caution.</p>
+
+<p style="margin-left:9%;"><b>−biblio</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">Specifies the bibliographic
+filename. There is space for 37 characters.</p>
+
+<p style="margin-left:9%;"><b>−cache−inodes
+<br>
+−no−cache−inodes</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Enable or disable caching inode and
+device numbers to find hard links to files. If
+<b>pycdlib-genisoimage</b> finds a hard link (a file with
+multiple names), the file will also be hard-linked on the
+CD, so the file contents only appear once. This helps to
+save space. <b>−cache−inodes</b> is default on
+Unix-like operating systems, but
+<b>−no−cache−inodes</b> is default on some
+other systems such as Cygwin, because it is not safe to
+assume that inode numbers are unique on those systems. (Some
+versions of Cygwin create fake inode numbers using a weak
+hashing algorithm, which may produce duplicates.) If two
+files have the same inode number but are not hard links to
+the same file, <b>pycdlib-genisoimage
+−cache−inodes</b> will not behave correctly.
+<b>−no−cache−inodes</b> is safe in all
+situations, but in that case <b>pycdlib-genisoimage</b>
+cannot detect hard links, so the resulting CD image may be
+larger than necessary.</p>
+
+<p style="margin-left:9%;"><b>−alpha−boot</b>
+<i>alpha_boot_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+boot image to be used when making an Alpha/SRM bootable CD.
+The pathname must be relative to the source path specified
+to <b>pycdlib-genisoimage</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−hppa−bootloader</b>
+<i>hppa_bootloader_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+boot image to be used when making an HPPA bootable CD. The
+pathname must be relative to the source path specified to
+<b>pycdlib-genisoimage</b>. Other options are required, at
+the very least a kernel filename and a boot command
+line.</p>
+
+
+<p style="margin-left:9%;"><b>−hppa−cmdline</b>
+<i>hppa_boot_command_line</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the command line to be passed
+to the HPPA boot loader when making a bootable CD. Separate
+the parameters with spaces or commas. More options must be
+passed to <b>pycdlib-genisoimage,</b> at the very least a
+kernel filename and the boot loader filename.</p>
+
+
+<p style="margin-left:9%;"><b>−hppa−kernel−32</b>
+<i>hppa_kernel_32</i> <b><br>
+−hppa−kernel−64</b>
+<i>hppa_kernel_64</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+32-bit and/or 64-bit kernel images to be used when making an
+HPPA bootable CD. The pathnames must be relative to the
+source path specified to <b>pycdlib-genisoimage</b>. Other
+options are required, at the very least the boot loader
+filename and the boot command line.</p>
+
+
+<p style="margin-left:9%;"><b>−hppa−ramdisk</b>
+<i>hppa_ramdisk_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+ramdisk image to be used when making an HPPA bootable CD.
+The pathname must be relative to the source path specified
+to <b>pycdlib-genisoimage</b>. This parameter is optional.
+Other options are required, at the very least a kernel
+filename and the boot command line.</p>
+
+<p style="margin-left:9%;"><b>−mips−boot</b>
+<i>mips_boot_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+boot image to be used when making an SGI/big-endian MIPS
+bootable CD. The pathname must be relative to the source
+path specified to <b>pycdlib-genisoimage</b>. This option
+may be specified several times, to store up to 15 boot
+images.</p>
+
+<p style="margin-left:9%;"><b>−mipsel−boot</b>
+<i>mipsel_boot_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+boot image to be used when making an DEC/little-endian MIPS
+bootable CD. The pathname must be relative to the source
+path specified to <b>pycdlib-genisoimage</b>.</p>
+
+<p style="margin-left:9%;"><b>−B</b>
+<i>img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e</i>
+<b><br>
+−sparc−boot</b>
+<i>img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies a comma-separated list of
+boot images that are needed to make a bootable CD for SPARC
+systems. Partition 0 is used for the ISO9660 image, the
+first image file is mapped to partition 1. The
+comma-separated list may have up to 7 fields, including
+empty fields. This option is required to make a bootable CD
+for Sun SPARC systems. If <b>−B</b> or
+<b>−sparc−boot</b> has been specified, the first
+sector of the resulting image will contain a Sun disk label.
+This disk label specifies slice 0 for the ISO9660 image and
+slices 1 to 7 for the boot images that have been specified
+with this option. Byte offsets 512 to 8191 within each of
+the additional boot images must contain a primary boot that
+works for the appropriate SPARC architecture. The rest of
+each of the images usually contains a UFS filesystem used
+for the primary kernel boot stage.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The implemented
+boot method is the one found with SunOS 4.x and SunOS 5.x.
+However, it does not depend on SunOS internals but only on
+properties of the Open Boot prom, so it should be usable for
+any OS for SPARC systems. For more information also see the
+<b>NOTES</b> section below.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If the special
+filename <b>...</b> is used, the actual and all following
+boot partitions are mapped to the previous partition. If
+<b>pycdlib-genisoimage</b> is called with <b>−G</b>
+<i>image</i> <b>−B</b> <i>...</i> all boot partitions
+are mapped to the partition that contains the ISO9660
+filesystem image and the generic boot image that is located
+in the first 16 sectors of the disc is used for all
+architectures.</p>
+
+<p style="margin-left:9%;"><b>−G</b>
+<i>generic_boot_image</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies the path and filename of the
+generic boot image to be used when making a generic bootable
+CD. The boot image will be placed on the first 16 sectors of
+the CD, before the ISO9660 primary volume descriptor. If
+this option is used together with
+<b>−sparc−boot</b>, the Sun disk label will
+overlay the first 512 bytes of the generic boot image.</p>
+
+<p style="margin-left:9%;"><b>−b</b>
+<i>eltorito_boot_image</i> <b><br>
+−eltorito−boot</b>
+<i>eltorito_boot_image</i></p>
+
+<p style="margin-left:18%;">Specifies the path and filename
+of the boot image to be used when making an El Torito
+bootable CD for x86 PCs. The pathname must be relative to
+the source path specified to <b>pycdlib-genisoimage</b>.
+This option is required to make an El Torito bootable CD.
+The boot image must be exactly 1200 kB, 1440 kB or 2880 kB,
+and <b>pycdlib-genisoimage</b> will use this size when
+creating the output ISO9660 filesystem. The PC BIOS will use
+the image to emulate a floppy disk, so the first 512-byte
+sector should contain PC boot code. This will work, for
+example, if the boot image is a LILO-based boot floppy.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If the boot
+image is not an image of a floppy, you need to add either
+<b>−hard−disk−boot</b> or
+<b>−no−emul−boot</b>. If the system should
+not boot off the emulated disk, use
+<b>−no−boot</b>.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If
+<b>−sort</b> has not been specified, the boot images
+are sorted with low priority (+2) to the beginning of the
+medium. If you don’t like this, you need to specify a
+sort weight of 0 for the boot images.</p>
+
+
+<p style="margin-left:9%;"><b>−eltorito−alt−boot</b></p>
+
+<p style="margin-left:18%;">Start with a new set of El
+Torito boot parameters. Up to 63 El Torito boot entries may
+be stored on a single CD.</p>
+
+
+<p style="margin-left:9%;"><b>−hard−disk−boot</b></p>
+
+<p style="margin-left:18%;">Specifies that the boot image
+used to create El Torito bootable CDs is a hard disk image.
+The image must begin with a master boot record that contains
+a single partition.</p>
+
+
+<p style="margin-left:9%;"><b>−eltorito−platform</b>
+<i>id</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the "El Torito" platform
+id for a boot record or a section of boot records. The
+<i>id</i> parameter may be either:</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="4%">
+
+
+<p><b>x86</b></p></td>
+<td width="5%"></td>
+<td width="73%">
+
+
+<p>This is the default <i>platform id</i> value and
+specifies entries for the PC platform. If no
+<b>−eltorito−platform</b> option appears before
+the first <b>−eltorito−boot</b> option, the
+default boot entry becomes an entry for the x86 PC
+platform.</p> </td></tr>
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="4%">
+
+
+<p><b>PPC</b></p></td>
+<td width="5%"></td>
+<td width="73%">
+
+
+<p>Boot entries for the Power PC platform.</p></td></tr>
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="4%">
+
+
+<p><b>Mac</b></p></td>
+<td width="5%"></td>
+<td width="73%">
+
+
+<p>Boot entries for the Apple Mac platform.</p></td></tr>
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="4%">
+
+
+<p><b>efi</b></p></td>
+<td width="5%"></td>
+<td width="73%">
+
+
+<p>Boot entries for EFI based PCs.</p></td></tr>
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="4%">
+
+
+<p><b>#</b></p></td>
+<td width="5%"></td>
+<td width="73%">
+
+
+<p>A numeric value specifying any platform id.</p></td></tr>
+</table>
+
+<p style="margin-left:18%; margin-top: 1em">If the option
+<b>−eltorito−platform</b> appears before the
+first <b>−eltorito−boot</b> option, it sets the
+<i>platform id</i> for the default boot entry.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If the option
+<b>−eltorito−platform</b> appears after an
+<b>−eltorito−boot</b> option and sets the
+<i>platform id</i> to a value different from the previous
+value, it starts a new set of boot entries.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The second boot
+entry and any new <i>platform id</i> creates a new section
+header and reduces the number of boot entries per CD by
+one.</p>
+
+
+<p style="margin-left:9%;"><b>−ignore−error</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Ignore errors.
+<b>pycdlib-genisoimage</b> by default aborts on several
+errors, such as read errors. With this option in effect,
+<b>pycdlib-genisoimage</b> tries to continue. Use with
+care.</p>
+
+
+<p style="margin-left:9%;"><b>−no−emul−boot</b></p>
+
+<p style="margin-left:18%;">Specifies that the boot image
+used to create El Torito bootable CDs is a "no
+emulation" image. The system will load and execute this
+image without performing any disk emulation.</p>
+
+<p style="margin-left:9%;"><b>−no−boot</b></p>
+
+<p style="margin-left:18%;">Specifies that the created El
+Torito CD should be marked as not bootable. The system will
+provide an emulated drive for the image, but will boot off a
+standard boot device.</p>
+
+
+<p style="margin-left:9%;"><b>−boot−load−seg</b>
+<i>segment_address</i></p>
+
+<p style="margin-left:18%;">Specifies the load segment
+address of the boot image for no-emulation El Torito
+CDs.</p>
+
+
+<p style="margin-left:9%;"><b>−boot−load−size</b>
+<i>load_sectors</i></p>
+
+<p style="margin-left:18%;">Specifies the number of
+"virtual" (512-byte) sectors to load in
+no-emulation mode. The default is to load the entire boot
+file. Some BIOSes may have problems if this is not a
+multiple of 4.</p>
+
+
+<p style="margin-left:9%;"><b>−boot−info−table</b></p>
+
+<p style="margin-left:18%;">Specifies that a 56-byte table
+with information of the CD-ROM layout will be patched in at
+offset 8 in the boot file.</p>
+
+<p style="margin-left:9%;"><b>−C</b>
+<i>last_sess_start,next_sess_start</i> <b><br>
+−cdrecord−params</b>
+<i>last_sess_start,next_sess_start</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) This option is needed to create a CD
+Extra or the image of a second session or a higher-level
+session for a multisession disc. <b>−C</b> takes two
+numbers separated by a comma. The first is the first sector
+in the last session of the disc that should be appended to.
+The second number is the starting sector number of the new
+session. The correct numbers may be retrieved by calling
+<b>wodim −msinfo ...</b> If <b>−C</b> is used in
+conjunction with <b>−M</b>, <b>pycdlib-genisoimage</b>
+will create a filesystem image that is intended to be a
+continuation of the previous session. If <b>−C</b> is
+used without <b>−M</b>, <b>pycdlib-genisoimage</b>
+will create a filesystem image that is intended to be used
+for a second session on a CD Extra. This is a multisession
+CD that holds audio data in the first session and an ISO9660
+filesystem in the second session.</p>
+
+<p style="margin-left:9%;"><b>−c</b>
+<i>boot_catalog</i> <b><br>
+−eltorito−catalog</b> <i>boot_catalog</i></p>
+
+<p style="margin-left:18%;">Specifies the path and filename
+of the boot catalog, which is required for an El Torito
+bootable CD. The pathname must be relative to the source
+path specified to <b>pycdlib-genisoimage</b>. This file will
+be inserted into the output tree and not created in the
+source filesystem, so be sure the specified filename does
+not conflict with an existing file, or it will be excluded.
+Usually a name like <i>boot.catalog</i> is chosen.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If
+<b>−sort</b> has not been specified, the boot catalog
+sorted with low priority (+1) to the beginning of the
+medium. If you don’t like this, you need to specify a
+sort weight of 0 for the boot catalog.</p>
+
+
+<p style="margin-left:9%;"><b>−check−oldnames</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Check all filenames imported from the
+old session for compliance with the ISO9660 file naming
+rules. Without this option, only names longer than 31
+characters are checked, as these files are a serious
+violation of the ISO9660 standard.</p>
+
+
+<p style="margin-left:9%;"><b>−check−session</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Check all old sessions for compliance
+with actual <b>pycdlib-genisoimage</b> ISO9660 file naming
+rules. This is a high-level option that combines
+<b>−M</b> <i>file</i> <b>−C 0,0
+−check−oldnames</b>. For the parameter
+<i>file</i>, see the description of <b>−M</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−checksum_algorithm_iso</b>
+<i>alg1,alg2,...</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify the checksum types desired for
+the output image.</p>
+
+
+<p style="margin-left:9%;"><b>−checksum_algorithm_template</b>
+<i>alg1,alg2,...</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify the checksum types desired for
+the output jigdo template.</p>
+
+<p style="margin-left:9%;"><b>−copyright</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">Specifies copyright
+information, typically a filename on the disc. There is
+space for 37 characters.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−d</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−omit−period</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not append a period to files that do
+not have one. <br>
+This violates the ISO9660 standard, but it happens to work
+on many systems. Use with caution.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−D</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−disable−deep−relocation</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not use deep directory relocation,
+and instead just pack them in the way we see them. <br>
+If ISO9660:1999 has not been selected, this violates the
+ISO9660 standard, but it happens to work on many systems.
+Use with caution.</p>
+
+
+<p style="margin-left:9%;"><b>−data−change−warn</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) If the size of a file changes while the
+file is being archived, treat this condition as a warning
+only that does not cause <b>pycdlib-genisoimage</b> to
+abort.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−debug</b></p></td>
+<td width="1%"></td>
+<td width="69%">
+
+
+<p>(not supported by pycdlib-genisoimage) Set debug
+flag.</p> </td>
+<td width="13%">
+</td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−dir−mode</b>
+<i>mode</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Overrides the mode of directories used
+to create the image to <i>mode</i>, specified as 4 digits of
+permission bits as in <b>chmod</b>(1). This option
+automatically enables Rock Ridge extensions.</p>
+
+
+<p style="margin-left:9%;"><b>−dvd−video</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Generate a DVD-Video compliant UDF
+filesystem. This is done by sorting the order of the content
+of the appropriate files and by adding padding between the
+files if needed. Note that the sorting only works if the
+DVD-Video filenames include uppercase characters only.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Note that in
+order to get a DVD-Video compliant filesystem image, you
+need to prepare a DVD-Video compliant directory tree. This
+requires a directory <b>VIDEO_TS</b> (all caps) in the root
+directory of the resulting DVD, and usually another
+directory <b>AUDIO_TS</b>. <b>VIDEO_TS</b> needs to include
+all needed files (filenames must be all caps) for a
+compliant DVD-Video filesystem.</p>
+
+<p style="margin-left:9%;"><b>−e</b>
+<i>efi_boot_file</i> <b><br>
+−efi−boot</b> <i>efi_boot_file</i></p>
+
+<p style="margin-left:18%;">Set EFI boot image name.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−f</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−follow−links</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Follow symbolic links when generating
+the filesystem. When this option is not in use, symbolic
+links will be entered using Rock Ridge if enabled, otherwise
+they will be ignored.</p>
+
+<p style="margin-left:9%;"><b>−file−mode</b>
+<i>mode</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Overrides the mode of regular files
+used to create the image to <i>mode</i>, specified as 4
+digits of permission bits as in <b>chmod</b>(1). This option
+automatically enables Rock Ridge extensions.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="6%">
+
+
+<p><b>−find</b></p></td>
+<td width="3%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) This option acts
+a separator. If it is used, all <b>pycdlib-genisoimage</b>
+options must be to the left of the <b>−find</b>
+option. To the right of the <b>−find</b> option,
+<b>pycdlib-genisoimage</b> accepts the <b>find</b> command
+line syntax only.</p></td></tr>
+</table>
+
+<p style="margin-left:18%; margin-top: 1em">The <b>find</b>
+expression acts as a filter between the source of file names
+and the consumer, which is archiving engine. If the
+<b>find</b> expression evaluated as TRUE, then the related
+file is selected for processing, otherwise it is omited.</p>
+
+<p style="margin-left:18%; margin-top: 1em">In order to
+make the evaluation of the <b>find</b> expression more
+convenient, <b>pycdlib-genisoimage</b> implements additional
+<b>find primaries</b> that have side effects on the file
+meta data. <b>pycdlib-genisoimage</b> implements the
+following additional <b>find</b> primaries:</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="6%">
+
+
+<p style="margin-top: 1em"><b>−help</b></p></td>
+<td width="3%"></td>
+<td width="45%">
+
+
+<p style="margin-top: 1em">Lists the available
+<b>find</b>(1) syntax.</p></td>
+<td width="28%">
+</td></tr>
+</table>
+
+<p style="margin-left:18%;"><b>−chgrp</b>
+<i>gname</i></p>
+
+<p style="margin-left:27%;">The primary always evaluates as
+true; it sets the group of the file to <i>gname</i>.</p>
+
+<p style="margin-left:18%;"><b>−chmod</b>
+<i>mode</i></p>
+
+<p style="margin-left:27%;">The primary always evaluates as
+true; it sets the permissions of the file to <i>mode</i>.
+Octal and symbolic permissions are accepted for <i>mode</i>
+as with <b>chmod</b>(1).</p>
+
+<p style="margin-left:18%;"><b>−chown</b>
+<i>uname</i></p>
+
+<p style="margin-left:27%;">The primary always evaluates as
+true; it sets the owner of the file to <i>uname</i>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="8%">
+
+
+<p><b>−false</b></p></td>
+<td width="1%"></td>
+<td width="73%">
+
+
+<p>The primary always evaluates as false; it allows to make
+the result of the full expression different from the result
+of a part of the expression.</p></td></tr>
+<tr valign="top" align="left">
+<td width="18%"></td>
+<td width="8%">
+
+
+<p><b>−true</b></p></td>
+<td width="1%"></td>
+<td width="73%">
+
+
+<p>The primary always evaluates as true; it allows to make
+the result of the full expression different from the result
+of a part of the expression.</p></td></tr>
+</table>
+
+<p style="margin-left:18%; margin-top: 1em">The command
+line:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em"><b>pycdlib-genisoimage
+-o o.iso -find . ( -type d -ls -o false ) -o ! -type
+d</b></p>
+
+<p style="margin-left:18%; margin-top: 1em">lists all
+directories and puts all non-directories to the image
+<b>o.iso</b>.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The command
+line:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em"><b>pycdlib-genisoimage
+-o o.iso -find . ( -type d -chown root -o true )</b></p>
+
+<p style="margin-left:18%; margin-top: 1em">archives all
+directories so they appear to be owned by root in the
+archive, all non-directories are archived as they are in the
+file system.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Note that the
+<b>−ls</b>, <b>−exec</b> and the
+<b>−ok</b> primary cannot be used if <b>stdin</b> or
+stdout has not been redirected.</p>
+
+<p style="margin-left:9%;"><b>−gid</b> <i>gid</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Overrides the group ID read from the
+source files to the value of <i>gid</i>. Specifying this
+option automatically enables Rock Ridge extensions.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>−gui</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Switch the
+behaviour for a GUI. This currently makes the output more
+verbose but may have other effects in the future.</p></td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−graft−points</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allow use of graft points for
+filenames. If this option is used, all filenames are checked
+for graft points. The filename is divided at the first
+unescaped equal sign. All occurrences of ‘\’ and
+‘=’ characters must be escaped with
+‘\’ if <b>−graft−points</b> has been
+specified.</p>
+
+<p style="margin-left:9%;"><b>−hide</b>
+<i>glob</i></p>
+
+<p style="margin-left:18%;">Hide any files matching
+<i>glob</i>, a shell wildcard pattern, from being seen in
+the ISO9660 or Rock Ridge directory. <i>glob</i> may match
+any part of the filename or path. If <i>glob</i> matches a
+directory, the contents of that directory will be hidden. In
+order to match a directory name, make sure the pathname does
+not include a trailing ‘/’ character. All the
+hidden files will still be written to the output CD image
+file. See also <b>−hide−joliet</b>, and
+<i>README.hide</i>. This option may be used multiple
+times.</p>
+
+<p style="margin-left:9%;"><b>−hide−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+shell wildcards to be hidden. See <b>−hide</b>.</p>
+
+<p style="margin-left:9%;"><b>−hidden</b>
+<i>glob</i></p>
+
+<p style="margin-left:18%;">Add the hidden (existence)
+ISO9660 directory attribute for files and directories
+matching <i>glob</i>, a shell wildcard pattern. This
+attribute will prevent the files from being shown by some
+MS-DOS and Windows commands. <i>glob</i> may match any part
+of the filename or path. In order to match a directory name,
+make sure the pathname does not include a trailing
+‘/’ character. This option may be used multiple
+times.</p>
+
+<p style="margin-left:9%;"><b>−hidden−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+shell wildcards to get the hidden attribute. See
+<b>−hidden</b>.</p>
+
+<p style="margin-left:9%;"><b>−hide−joliet</b>
+<i>glob</i></p>
+
+<p style="margin-left:18%;">Hide files and directories
+matching <i>glob</i>, a shell wildcard pattern, from being
+seen in the Joliet directory. <i>glob</i> may match any part
+of the filename or path. If <i>glob</i> matches a directory,
+the contents of that directory will be hidden. In order to
+match a directory name, make sure the pathname does not
+include a trailing ‘/’ character. All the hidden
+files will still be written to the output CD image file.
+This option is usually used with <b>−hide</b>. See
+also <i>README.hide</i>. This option may be used multiple
+times.</p>
+
+
+<p style="margin-left:9%;"><b>−hide−joliet−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+shell wildcards to be hidden from the Joliet tree. See
+<b>−hide−joliet</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−hide−joliet−trans−tbl</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Hide the <i>TRANS.TBL</i> files from
+the Joliet tree. These files usually don’t make sense
+in the Joliet world as they list the real name and the
+ISO9660 name which may both be different from the Joliet
+name.</p>
+
+
+<p style="margin-left:9%;"><b>−hide−rr−moved</b></p>
+
+<p style="margin-left:18%;">Rename the directory
+<i>RR_MOVED</i> to <i>.rr_moved</i> in the Rock Ridge tree.
+It seems to be impossible to completely hide the
+<i>RR_MOVED</i> directory from the Rock Ridge tree. This
+option only makes the visible tree less confusing for people
+who don’t know what this directory is for. If you need
+to have no <i>RR_MOVED</i> directory at all, you should use
+<b>−D</b>. Note that if <b>−D</b> has been
+specified, the resulting filesystem is not ISO9660 level-1
+compliant and will not be readable on MS-DOS. See also the
+<b>NOTES</b> section.</p>
+
+<p style="margin-left:9%;"><b>−hide−udf</b>
+<i>glob</i></p>
+
+<p style="margin-left:18%;">Hide <i>glob</i> from being
+seen on the UDF directory. <i>glob</i> is a shell
+wild-card-style pattern that must match any part of the
+filename or path. Multiple globs may be hidden. If
+<i>glob</i> matches a directory, then the contents of that
+directory will be hidden. In order to match a directory
+name, make sure the pathname does not include a trailing
+’/’ character. All the hidden files will still
+be written to the output CD image file. Should be used with
+the <b>−hide</b> option.</p>
+
+
+<p style="margin-left:9%;"><b>−hide−udf−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+<i>globs</i> to be hidden as above.</p>
+
+
+<p style="margin-left:9%;"><b>−input−charset</b>
+<i>charset</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Input charset that defines the
+characters used in local filenames. To get a list of valid
+charset names, call <b>pycdlib-genisoimage
+−input−charset help</b>. To get a 1:1 mapping,
+you may use <b>default</b> as charset name. The default
+initial values are <i>cp437</i> on DOS-based systems and
+<i>iso8859-1</i> on all other systems.</p>
+
+
+<p style="margin-left:9%;"><b>−output−charset</b>
+<i>charset</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Output charset that defines the
+characters that will be used in Rock Ridge filenames.
+Defaults to the input charset. See <b>CHARACTER SETS</b>
+section below for more details.</p>
+
+<p style="margin-left:9%;"><b>−iso−level</b>
+<i>level</i></p>
+
+<p style="margin-left:18%;">Set the ISO9660 conformance
+level. Valid numbers are 1 to 4.</p>
+
+<p style="margin-left:18%; margin-top: 1em">With level 1,
+files may only consist of one section and filenames are
+restricted to 8.3 characters.</p>
+
+<p style="margin-left:18%; margin-top: 1em">With level 2,
+files may only consist of one section.</p>
+
+<p style="margin-left:18%; margin-top: 1em">With level 3,
+no restrictions (other than ISO-9660:1988) do apply.</p>
+
+<p style="margin-left:18%; margin-top: 1em">With all
+ISO9660 levels from 1 to 3, all filenames are restricted to
+uppercase letters, numbers and underscores (_). Filenames
+are limited to 31 characters, directory nesting is limited
+to 8 levels, and pathnames are limited to 255
+characters.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Level 4
+officially does not exist but <b>pycdlib-genisoimage</b>
+maps it to ISO-9660:1999, which is ISO9660 version 2.</p>
+
+<p style="margin-left:18%; margin-top: 1em">With level 4,
+an enhanced volume descriptor with version number and file
+structure version number set to 2 is emitted. Directory
+nesting is not limited to 8 levels, there is no need for a
+file to contain a dot and the dot has no special meaning,
+filenames do not have version numbers, and filenames can be
+up to 207 characters long, or 197 characters if Rock Ridge
+is used.</p>
+
+<p style="margin-left:18%; margin-top: 1em">When creating
+Version 2 images, <b>pycdlib-genisoimage</b> emits an
+enhanced volume descriptor, similar but not identical to a
+primary volume descriptor. Be careful not to use broken
+software to make ISO9660 images bootable by assuming a
+second PVD copy and patching this putative PVD copy into an
+El Torito VD.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−J</b></p></td>
+<td width="6%"></td>
+<td width="82%">
+
+
+<p>Generate Joliet directory records in addition to regular
+ISO9660 filenames. This is primarily useful when the discs
+are to be used on Windows machines. Joliet filenames are
+specified in Unicode and each path component can be up to 64
+Unicode characters long. Note that Joliet is not a standard
+— only Microsoft Windows and Linux systems can read
+Joliet extensions. For greater portability, consider using
+both Joliet and Rock Ridge extensions.</p></td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−joliet−long</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allow Joliet filenames to be up to 103
+Unicode characters, instead of 64. This breaks the Joliet
+specification, but appears to work. Use with caution.</p>
+
+<p style="margin-left:9%;"><b>−jcharset</b>
+<i>charset</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) A combination of <b>−J
+−input−charset</b> <i>charset</i>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−l</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−full−iso9660−filenames</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allow full 31-character filenames.
+Normally the ISO9660 filename will be in an 8.3 format which
+is compatible with MS-DOS, even though the ISO9660 standard
+allows filenames of up to 31 characters. If you use this
+option, the disc may be difficult to use on a MS-DOS system,
+but will work on most other systems. Use with caution.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−L</b></p></td>
+<td width="6%"></td>
+<td width="63%">
+
+
+<p>Outdated option; use
+<b>−allow−leading−dots</b> instead.</p></td>
+<td width="19%">
+</td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−jigdo−jigdo</b>
+<i>jigdo_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Produce a <b>jigdo</b> <i>.jigdo</i>
+metadata file as well as the filesystem image.</p>
+
+
+<p style="margin-left:9%;"><b>−jigdo−template</b>
+<i>template_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Produce a <b>jigdo</b> <i>.template</i>
+file as well as the filesystem image.</p>
+
+
+<p style="margin-left:9%;"><b>−jigdo−min−file−size</b>
+<i>size</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify the minimum size for a file to
+be listed in the <i>.jigdo</i> file. Default (and minimum
+allowed) is 1KB.</p>
+
+
+<p style="margin-left:9%;"><b>−jigdo−force−md5</b>
+<i>path</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a file pattern where files
+<i>must</i> be contained in the externally-supplied MD5 list
+as supplied by <b>−md5−list</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−jigdo−exclude</b>
+<i>path</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a file pattern where files will
+not be listed in the <i>.jigdo</i> file.</p>
+
+<p style="margin-left:9%;"><b>−jigdo−map</b>
+<i>path</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a pattern mapping for the jigdo
+file (e.g. <i>Debian=/mirror/debian</i>).</p>
+
+<p style="margin-left:9%;"><b>−md5−list</b>
+<i>md5_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a file containing the MD5sums,
+sizes and pathnames of the files to be included in the
+<i>.jigdo</i> file.</p>
+
+
+<p style="margin-left:9%;"><b>−jigdo−template−compress</b>
+<i>algorithm</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a compression algorithm to use
+for template date. gzip and bzip2 are currently supported,
+and gzip is the default.</p>
+
+<p style="margin-left:9%;"><b>−log−file</b>
+<i>log_file</i></p>
+
+<p style="margin-left:18%;">Redirect all error, warning and
+informational messages to <i>log_file</i> instead of the
+standard error.</p>
+
+
+<p style="margin-left:9%;"><b>−long−rr−time</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Use the long ISO-9660 time format for
+the file time stamps used in Rock Ridge. This time format
+allows to represent year 0 .. year 9999 with a granularity
+of 10ms.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The short
+ISO-9660 time format only allows to represent year 1900 ..
+year 2155 with a granularity of 1s.</p>
+
+<p style="margin-left:9%;"><b>−m</b> <i>glob</i></p>
+
+<p style="margin-left:18%;">Exclude files matching
+<i>glob</i>, a shell wildcard pattern, from being written to
+CD-ROM. <i>glob</i> may match either the filename component
+or the full pathname. This option may be used multiple
+times. For example:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em">pycdlib-genisoimage
+−o rom −m '*.o' −m core −m
+foobar</p>
+
+<p style="margin-left:18%; margin-top: 1em">would exclude
+all files ending in ‘.o’, or called <i>core</i>
+or <i>foobar</i> from the image. Note that if you had a
+directory called <i>foobar</i>, it too (and of course all
+its descendants) would be excluded.</p>
+
+
+<p style="margin-left:9%;"><b>−exclude−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+shell wildcards to be excluded. See <b>−m</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−max−iso9660−filenames</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allow ISO9660 filenames to be up to 37
+characters long. This option enables <b>−N</b> as the
+extra name space is taken from the space reserved for file
+version numbers. <br>
+This violates the ISO9660 standard, but it happens to work
+on many systems. Although a conforming application needs to
+provide a buffer space of at least 37 characters, discs
+created with this option may cause a buffer overflow in the
+reading operating system. Use with extreme care.</p>
+
+<p style="margin-left:9%;"><b>−M</b> <i>path</i>
+<b><br>
+−M</b> <i>device</i> <b><br>
+−dev</b> <i>device</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies path to existing ISO9660
+image to be merged. The alternate form takes a SCSI device
+specifier that uses the same syntax as the <b>dev=</b>
+parameter of <b>wodim</b>. The output of
+<b>pycdlib-genisoimage</b> will be a new session which
+should get written to the end of the image specified in
+<b>−M</b>. Typically this requires multisession
+capability for the CD recorder used to write the image. This
+option may only be used in conjunction with
+<b>−C</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−modification−date</b>
+<i>date-spec</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the <b>modification date</b> in the
+primary volume descriptor (PVD) to a value different from
+the current time. This allows e.g. to set up an intentional
+UUID for <b>grub</b>.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The format of
+<i>date-spec</i> is:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em"><i>yyyy</i>[<i>mm</i>[<i>dd</i>[<i>hh</i>[<i>mm</i>[<i>ss</i>]]]]][.<i>hh</i>][+-<i>ghgm</i>]</p>
+
+<p style="margin-left:18%; margin-top: 1em">The fields are
+<b>year</b>, <b>month</b>, <b>day of month</b>, <b>hour</b>,
+<b>minute</b>, <b>second</b>, <b>hundreds of a second</b>,
+<b>GMT offset in hours and minutes</b>. The time is
+interpreted as local time.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Year and the
+GMT offset are four digit fields, all other fields take two
+digits. The GMT offset may be between -12 and +13 hours in
+15 minute steps. Locations east to Greenwich have positive
+values. The value is the sum of the time zone offset and the
+effects from daylight saving time. Omited values are
+replaced by the minimal possible values. If the GMT offset
+is omited, it is computed from the local time value that has
+been supplied.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Between year
+and month as well as between month and day of month, a
+separator chosen from ’/’ and ’-’
+may appear. In this case, the year may be a two digit number
+with values 69..99 representing 1969..1999 and values 00..68
+representing 2000..2068. Between date and time spec, an
+optional space is permitted. Between hours and minutes as
+well as between minutes and seconds, an optional
+’:’ separator is permitted. This allows
+<b>pycdlib-genisoimage</b> to parse the popular POSIX date
+format created by:</p>
+
+<p style="margin-left:18%; margin-top: 1em"><b>date
+"+%Y-%m-%d %H:%M:%S %z"</b></p>
+
+<p style="margin-left:18%; margin-top: 1em">Note that the
+possible range for <i>date-spec</i> for 32 bit programs is
+limited to values up to 2038 Jan 19 04:14:07 GMT.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−N</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−omit−version−number</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Omit version numbers from ISO9660
+filenames. <br>
+This violates the ISO9660 standard, but no one really uses
+the version numbers anyway. Use with caution.</p>
+
+
+<p style="margin-left:9%;"><b>−new−dir−mode</b>
+<i>mode</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify the mode, a 4-digit number as
+used in <b>chmod</b>(1), to use when creating new
+directories in the filesystem image. The default is
+0555.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−nobak</b></p></td>
+<td width="83%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−no−bak</b></p>
+
+<p style="margin-left:18%;">Exclude backup files files on
+the ISO9660 filesystem; that is, filenames that contain the
+characters ‘˜’ or ‘#’ or end in
+<i>.bak</i>. These are typically backup files for Unix text
+editors.</p>
+
+
+<p style="margin-left:9%;"><b>−no−limit−pathtables</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) A ISO-9660 filesystem contains path
+tables that contain a list of directories. This list may
+contain many directories but only 65535 of them may be
+parent directories. When
+<b>−no−limit−pathtables</b> is in use,
+further parent directories will be folded to the root
+directory and the resulting filesystem will no longer be
+usable on <b>DOS</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−no−long−rr−time</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Use the short ISO-9660 time format for
+the file time stamps used in Rock Ridge. This time format
+allows to represent year 1990 .. year 2155 with a
+granularity of one second.</p>
+
+
+<p style="margin-left:9%;"><b>−force−rr</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not use the automatic Rock Ridge
+attributes recognition for previous sessions. This can work
+around problems with images created by, e.g., NERO Burning
+ROM.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−no−rr</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Do not use the
+Rock Ridge attributes from previous sessions. This may help
+to avoid problems when <b>pycdlib-genisoimage</b> finds
+illegal Rock Ridge signatures on an old session.</p></td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−no−split−symlink−components</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Don’t split the symlink
+components, but begin a new Continuation Area (CE) instead.
+This may waste some space, but the SunOS 4.1.4 cdrom driver
+has a bug in reading split symlink components.</p>
+
+<p style="margin-left:18%; margin-top: 1em">It is
+questionable whether this option is useful nowadays.</p>
+
+
+<p style="margin-left:9%;"><b>−no−split−symlink−fields</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Don’t split the symlink fields,
+but begin a new Continuation Area (CE) instead. This may
+waste some space, but the SunOS 4.1.4 and Solaris 2.5.1
+cdrom driver have a bug in reading split symlink fields (a
+‘/’ can be dropped).</p>
+
+<p style="margin-left:18%; margin-top: 1em">It is
+questionable whether this option is useful nowadays.</p>
+
+<p style="margin-left:9%;"><b>−o</b>
+<i>filename</i></p>
+
+<p style="margin-left:18%;">Specify the output file for the
+the ISO9660 filesystem image. This can be a disk file, a
+tape drive, or it can correspond directly to the device name
+of the optical disc writer. If not specified, stdout is
+used. Note that the output can also be a block device for a
+regular disk partition, in which case the ISO9660 filesystem
+can be mounted normally to verify that it was generated
+correctly.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>−pad</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Pad the end of
+the whole image by 150 sectors (300 kB). This option is
+enabled by default. If used in combination with
+<b>−B</b>, padding is inserted between the ISO9660
+partition and the boot partitions, such that the first boot
+partition starts on a sector number that is a multiple of
+16.</p> </td></tr>
+</table>
+
+<p style="margin-left:18%; margin-top: 1em">The padding is
+needed as many operating systems (e.g. Linux) implement
+read-ahead bugs in their filesystem I/O. These bugs result
+in read errors on files that are located near the end of a
+track, particularly if the disc is written in Track At Once
+mode, or where a CD audio track follows the data track.</p>
+
+<p style="margin-left:9%;"><b>−no−pad</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not pad the end by 150 sectors (300
+kB) and do not make the the boot partitions start on a
+multiple of 16 sectors.</p>
+
+<p style="margin-left:9%;"><b>−path−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">A file containing a list of
+<i>pathspec</i> directories and filenames to be added to the
+ISO9660 filesystem. This list of pathspecs are processed
+after any that appear on the command line. If the argument
+is <i>−</i>, the list is read from the standard
+input.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−P</b></p></td>
+<td width="6%"></td>
+<td width="51%">
+
+
+<p>Outdated option; use <b>−publisher</b>
+instead.</p> </td>
+<td width="31%">
+</td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−publisher</b>
+<i>publisher_id</i></p>
+
+<p style="margin-left:18%;">Specifies a text string that
+will be written into the volume header. This should describe
+the publisher of the CD-ROM, usually with a mailing address
+and phone number. There is space for 128 characters.</p>
+
+<p style="margin-left:9%;"><b>−p</b>
+<i>preparer_id</i> <b><br>
+−preparer</b> <i>preparer_id</i></p>
+
+<p style="margin-left:18%;">Specifies a text string that
+will be written into the volume header. This should describe
+the preparer of the CD-ROM, usually with a mailing address
+and phone number. There is space on the disc for 128
+characters of information. The related Joliet entry is
+limited to 64 characters.</p>
+
+<p style="margin-left:9%;"><b>−posix−H</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Follow all symbolic links encountered
+on command line when generating the filesystem.</p>
+
+<p style="margin-left:9%;"><b>−posix−L</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Follow all symbolic links when
+generating the filesystem. When this option is not in use,
+symbolic links will be entered using Rock Ridge if enabled,
+otherwise the file will be ignored.</p>
+
+<p style="margin-left:9%;"><b>−posix−P</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not follow symbolic links when
+generating the filesystem (this is the default). If
+<b>−posix−P</b> is specified after
+<b>−posix−H</b> or <b>−posix−L</b>,
+the effect of these options will be reset.</p>
+
+
+<p style="margin-left:9%;"><b>−print−size</b></p>
+
+<p style="margin-left:18%;">Print estimated filesystem size
+in multiples of the sector size (2048 bytes) and exit. This
+option is needed for Disk At Once mode and with some CD-R
+drives when piping directly into <b>wodim</b>, cases where
+<b>wodim</b> needs to know the size of the filesystem image
+in advance. Old versions of <b>mkisofs</b> wrote this
+information (among other information) to <i>stderr</i>. As
+this turns out to be hard to parse, the number without any
+other information is now printed on <i>stdout</i> too. If
+you like to write a simple shell script, redirect
+<i>stderr</i> and catch the number from <i>stdout</i>. This
+may be done with:</p>
+
+<p style="margin-left:18%; margin-top: 1em">cdblocks=`
+pycdlib-genisoimage −print−size −quiet ...
+` <br>
+pycdlib-genisoimage ... | wodim ... tsize=${cdblocks}s
+−</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−quiet</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>This makes <b>pycdlib-genisoimage</b> even less verbose.
+No progress output will be provided.</p></td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−R</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+</td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−rock</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>Generate SUSP and RR records using the Rock Ridge
+protocol to further describe the files on the ISO9660
+filesystem.</p> </td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−r</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−rational−rock</b></p>
+
+<p style="margin-left:18%;">This is like the −R
+option, but file ownership and modes are set to more useful
+values. The uid and gid are set to zero, because they are
+usually only useful on the author’s system, and not
+useful to the client. All the file read bits are set true,
+so that files and directories are globally readable on the
+client. If any execute bit is set for a file, set all of the
+execute bits, so that executables are globally executable on
+the client. If any search bit is set for a directory, set
+all of the search bits, so that directories are globally
+searchable on the client. All write bits are cleared,
+because the filesystem will be mounted read-only in any
+case. If any of the special mode bits are set, clear them,
+because file locks are not useful on a read-only filesystem,
+and set-id bits are not desirable for uid 0 or gid 0. When
+used on Win32, the execute bit is set on <i>all</i> files.
+This is a result of the lack of file permissions on Win32
+and the Cygwin POSIX emulation layer. See also
+<b>−uid</b>, <b>−gid</b>,
+<b>−dir−mode</b>, <b>−file−mode</b>
+and <b>−new−dir−mode</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−relaxed−filenames</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allows ISO9660 filenames to include all
+7-bit ASCII characters except lowercase letters. <br>
+This violates the ISO9660 standard, but it happens to work
+on many systems. Use with caution.</p>
+
+<p style="margin-left:9%;"><b>−root</b>
+<i>dir</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Moves all files and directories into
+<i>dir</i> in the image. This is essentially the same as
+using <b>−graft−points</b> and adding <i>dir</i>
+in front of every pathspec, but is easier to use. <i>dir</i>
+may actually be several levels deep. It is created with the
+same permissions as other graft points.</p>
+
+<p style="margin-left:9%;"><b>−rrip110</b></p>
+
+<p style="margin-left:18%;">Create ISO-9660 file system
+images that follow the old Rrip Version-1.10 standard from
+1993. This option may be needed if you know of systems that
+do not implement the Rrip protocol correctly and like the
+file system to be read by such a system. Currently no such
+system is known.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If a file
+system has been created with <b>−rrip110</b>, the Rock
+Ridge attributes do not include inode number
+information.</p>
+
+<p style="margin-left:9%;"><b>−rrip112</b></p>
+
+<p style="margin-left:18%;">Create ISO-9660 file system
+images that follow the new Rrip Version-1.12 standard from
+1994.</p>
+
+<p style="margin-left:9%;"><b>−old-root</b>
+<i>dir</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) This option is necessary when writing a
+multisession image and the previous (or even older) session
+was written with <b>-root</b> <i>dir</i>. Using a directory
+name not found in the previous session causes
+<b>pycdlib-genisoimage</b> to abort with an error. Without
+this option, <b>pycdlib-genisoimage</b> would not be able to
+find unmodified files and would be forced to write their
+data into the image once more. <b>−root</b> and
+<b>−old-root</b> are meant to be used together to do
+incremental backups. The initial session would e.g. use:
+<b>pycdlib-genisoimage −root backup_1</b> <i>dirs</i>.
+The next incremental backup with <b>pycdlib-genisoimage
+−root backup_2 −old-root backup_1</b>
+<i>dirs</i> would take another snapshot of these
+directories. The first snapshot would be found in
+<b>backup_1</b>, the second one in <b>backup_2</b>, but only
+modified or new files need to be written into the second
+session. Without these options, new files would be added and
+old ones would be preserved. But old ones would be
+overwritten if the file was modified. Recovering the files
+by copying the whole directory back from CD would also
+restore files that were deleted intentionally. Accessing
+several older versions of a file requires support by the
+operating system to choose which sessions are to be
+mounted.</p>
+
+<p style="margin-left:9%;"><b>−s</b> <i>sector
+type</i> <b><br>
+−sectype</b> <i>sector type</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set output sector type to e.g.
+data/xa1/raw.</p>
+
+<p style="margin-left:9%;"><b>−sort</b>
+<i>sort_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Sort file locations on the media.
+Sorting is controlled by a file that contains pairs of
+filenames and sorting offset weighting. If the weighting is
+higher, the file will be located closer to the beginning of
+the media, if the weighting is lower, the file will be
+located closer to the end of the media. There must be only
+one space or tabs character between the filename and the
+weight and the weight must be the last characters on a line.
+The filename is taken to include all the characters up to,
+but not including the last space or tab character on a line.
+This is to allow for space characters to be in, or at the
+end of a filename. This option does <b>not</b> sort the
+order of the filenames that appear in the ISO9660 directory.
+It sorts the order in which the file data is written to the
+CD image, which is useful in order to optimize the data
+layout on a CD. See <b>README.sort</b> for more details.</p>
+
+<p style="margin-left:9%;"><b>−sparc−boot</b>
+<i>img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) See <b>−B</b> above.</p>
+
+<p style="margin-left:9%;"><b>−sparc−label</b>
+<i>label</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the Sun disk label name for the Sun
+disk label that is created with
+<b>−sparc-boot</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−split−output</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Split the output image into several
+files of approximately 1 GB each. This helps to create
+DVD-sized ISO9660 images on operating systems without large
+file support. <b>wodim</b> will concatenate more than one
+file into a single track if writing to a DVD. To make
+<b>−split−output</b> work, <b>−o</b>
+<i>filename</i> must be specified. The resulting output
+images will be named: <i>filename_00</i>,
+<i>filename_01</i>, <i>filename_02</i>....</p>
+
+
+<p style="margin-left:9%;"><b>−stream−media−size</b>
+<i>#</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Select streaming operation and set the
+media size to # sectors. This allows you to pipe the output
+of the <b>tar</b>(1) program into <b>pycdlib-genisoimage</b>
+and to create an ISO9660 filesystem without the need of an
+intermediate tar archive file. If this option has been
+specified, <b>pycdlib-genisoimage</b> reads from
+<i>stdin</i> and creates a file with the name
+<i>STREAM.IMG</i>. The maximum size of the file (with
+padding) is 200 sectors less than the specified media size.
+If <b>−no−pad</b> has been specified, the file
+size is 50 sectors less than the specified media size. If
+the file is smaller, <b>pycdlib-genisoimage</b> will write
+padding. This may take awhile.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The option
+<b>−stream−media−size</b> creates simple
+ISO9660 filesystems only and may not used together with
+multisession or hybrid filesystem options.</p>
+
+
+<p style="margin-left:9%;"><b>−stream−file−name</b>
+<i>name</i></p>
+
+<p style="margin-left:18%;">Reserved for future use.</p>
+
+<p style="margin-left:9%;"><b>−sunx86−boot</b>
+<i>UFS_img,,,AUX1_img</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specifies a comma-separated list of
+filesystem images that are needed to make a bootable CD for
+Solaris x86 systems.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Note that
+partition 1 is used for the ISO9660 image and that partition
+2 is the whole disk, so partition 1 and 2 may not be used by
+external partition data. The first image file is mapped to
+partition 0. There may be empty fields in the
+comma-separated list, and list entries for partition 1 and 2
+must be empty. The maximum number of supported partitions is
+8 (although the Solaris x86 partition table could support up
+to 16 partitions), so it is impossible to specify more than
+6 partition images. This option is required to make a
+bootable CD for Solaris x86 systems.</p>
+
+<p style="margin-left:18%; margin-top: 1em">If
+<b>−sunx86−boot</b> has been specified, the
+first sector of the resulting image will contain a PC fdisk
+label with a Solaris type 0x82 fdisk partition that starts
+at offset 512 and spans the whole CD. In addition, for the
+Solaris type 0x82 fdisk partition, there is a SVr4 disk
+label at offset 1024 in the first sector of the CD. This
+disk label specifies slice 0 for the first (usually UFS
+type) filesystem image that is used to boot the PC and slice
+1 for the ISO9660 image. Slice 2 spans the whole CD slice 3
+... slice 7 may be used for additional filesystem images
+that have been specified with this option.</p>
+
+<p style="margin-left:18%; margin-top: 1em">A Solaris x86
+boot CD uses a 1024 byte sized primary boot that uses the
+<b>El-Torito no-emulation</b> boot mode and a secondary
+generic boot that is in CD sectors 1..15. For this reason,
+both <b>-b</b> <i>bootimage</i>
+<b>−no−emul−boot</b> and <b>−G</b>
+<i>genboot</i> must be specified.</p>
+
+
+<p style="margin-left:9%;"><b>−sunx86−label</b>
+<i>label</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the SVr4 disk label name for the
+SVr4 disk label that is created with
+<b>−sunx86-boot</b>.</p>
+
+<p style="margin-left:9%;"><b>−sysid</b>
+<i>ID</i></p>
+
+<p style="margin-left:18%;">Specifies the system ID. There
+is space for 32 characters.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−T</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−translation−table</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Generate a file <i>TRANS.TBL</i> in
+each directory on the CD-ROM, which can be used on
+non-Rock Ridge-capable systems to help establish the
+correct filenames. There is also information present in the
+file that indicates the major and minor numbers for block
+and character devices, and each symlink has the name of the
+link file given.</p>
+
+<p style="margin-left:9%;"><b>−table−name</b>
+<i>table_name</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Alternative translation table filename
+(see above). Implies <b>−T</b>. If you are creating a
+multisession image you must use the same name as in the
+previous session.</p>
+
+<p style="margin-left:9%;"><b>−ucs−level</b>
+<i>level</i></p>
+
+<p style="margin-left:18%;">Set Unicode conformance level
+in the Joliet SVD. The default level is 3. It may be set to
+1..3 using this option.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>−UDF</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>Include a <b>UDF</b> hybrid in the generated filesystem
+image. As <b>pycdlib-genisoimage</b> always creates a
+ISO-9660 filesystem, it is not possible to create UDF only
+images. Note that <b>UDF</b> wastes the space from sector
+˜20 to sector 256 at the beginning of the disk in
+addition to the space needed for real <b>UDF</b> data
+structures.</p> </td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p><b>−udf</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p>Rationalized UDF with user and group set to 0 and with
+simplified permissions. See <b>−r</b> option for more
+information.</p> </td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−udf−symlinks</b></p>
+
+<p style="margin-left:18%;">Support symlinks in <b>UDF</b>
+filesystems. This is the default.</p>
+
+
+<p style="margin-left:9%;"><b>−no−udf−symlinks</b></p>
+
+<p style="margin-left:18%;">Do not support symlinks in
+<b>UDF</b> filesystems.</p>
+
+<p style="margin-left:9%;"><b>−uid</b> <i>uid</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Overrides the uid read from the source
+files to the value of <i>uid</i>. Specifying this option
+automatically enables Rock Ridge extensions.</p>
+
+
+<p style="margin-left:9%;"><b>−use−fileversion</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) The option
+<b>−use−fileversion</b> allows
+<b>pycdlib-genisoimage</b> to use file version numbers from
+the filesystem. If the option is not specified,
+<b>pycdlib-genisoimage</b> creates a version number of 1 for
+all files. File versions are strings in the range <i>;1</i>
+to <i>;32767</i> This option is the default on VMS.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−U</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−untranslated−filenames</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Allows "untranslated"
+filenames, completely violating the ISO9660 standards
+described above. Enables the following flags: <b>−d
+−l −N −allow−leading−dots
+−relaxed−filenames −allow−lowercase
+−allow−multidot
+−no−iso−translate</b>. Allows more than
+one ‘.’ character in the filename, as well as
+mixed-case filenames. This is useful on HP-UX, where the
+built-in <i>cdfs</i> filesystem does not recognize any
+extensions. Use with extreme caution.</p>
+
+
+<p style="margin-left:9%;"><b>−no−iso−translate</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not translate the characters
+‘#’ and ‘˜’ which are invalid
+for ISO9660 filenames. Although invalid, these characters
+are often used by Microsoft systems. <br>
+This violates the ISO9660 standard, but it happens to work
+on many systems. Use with caution.</p>
+
+<p style="margin-left:9%;"><b>−V</b> <i>volid</i></p>
+
+<p style="margin-left:18%;">Specifies the volume ID (volume
+name or label) to be written into the master block. There is
+space for 32 characters. The volume ID is used as the mount
+point by the Solaris volume manager and as a label assigned
+to a disc on various other platforms such as Windows and
+Apple Mac OS.</p>
+
+<p style="margin-left:9%;"><b>−volset</b>
+<i>ID</i></p>
+
+<p style="margin-left:18%;">Specifies the volume set ID.
+There is space for 128 characters.</p>
+
+<p style="margin-left:9%;"><b>−volset−size</b>
+<i>#</i></p>
+
+<p style="margin-left:18%;">Sets the volume set size to #.
+The volume set size is the number of CDs that are in a CD
+volume set. A volume set is a collection of one or more
+volumes, on which a set of files is recorded.</p>
+
+<p style="margin-left:18%; margin-top: 1em">Volume Sets are
+not intended to be used to create a set numbered CDs that
+are part of e.g. a Operation System installation set of CDs.
+Volume Sets are rather used to record a big directory tree
+that would not fit on a single volume. Each volume of a
+Volume Set contains a description of all the directories and
+files that are recorded on the volumes where the sequence
+numbers are less than, or equal to, the assigned Volume Set
+Size of the current volume.</p>
+
+
+<p style="margin-left:18%; margin-top: 1em"><b>pycdlib-genisoimage</b>
+currently does not support a <b>−volset−size</b>
+that is larger than 1.</p>
+
+<p style="margin-left:18%; margin-top: 1em">The option
+<b>−volset−size</b> must be specified before
+<b>−volset−seqno</b> on each command line.</p>
+
+
+<p style="margin-left:9%;"><b>−volset−seqno</b>
+<i>#</i></p>
+
+<p style="margin-left:18%;">Sets the volume set sequence
+number to #. The volume set sequence number is the index
+number of the current CD in a CD set. The option
+<b>−volset−size</b> must be specified before
+<b>−volset−seqno</b> on each command line.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="3%">
+
+
+<p><b>−v</b></p></td>
+<td width="88%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−verbose</b></p>
+
+<p style="margin-left:18%;">Verbose execution. If given
+twice on the command line, extra debug information will be
+printed.</p>
+
+<p style="margin-left:9%;"><b>−x</b> <i>glob</i></p>
+
+<p style="margin-left:18%;">Identical to <b>−m</b>
+<i>glob</i>.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="4%">
+
+
+<p><b>−XA</b></p></td>
+<td width="5%"></td>
+<td width="60%">
+
+
+<p>Generate XA directory attruibutes.</p></td>
+<td width="22%">
+</td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="4%">
+
+
+<p><b>−xa</b></p></td>
+<td width="5%"></td>
+<td width="60%">
+
+
+<p>Generate rationalized XA directory attruibutes.</p></td>
+<td width="22%">
+</td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="4%">
+
+
+<p><b>−z</b></p></td>
+<td width="5%"></td>
+<td width="60%"></td>
+<td width="22%">
+</td></tr>
+</table>
+
+
+<p style="margin-left:9%; margin-top: 1em"><b>−transparent−compression</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Generate special <i>RRIP</i> records
+for transparently compressed files. This is only of use and
+interest for hosts that support transparent decompression,
+such as Linux 2.4.14 or later. You must specify
+<b>−R</b> or <b>−r</b> to enable Rock Ridge, and
+generate compressed files using the <b>mkzftree</b> utility
+before running <b>pycdlib-genisoimage</b>. Note that
+transparent compression is a nonstandard Rock Ridge
+extension. The resulting disks are only transparently
+readable if used on Linux. On other operating systems you
+will need to call <b>mkzftree</b> by hand to decompress the
+files.</p>
+
+
+<p style="margin-left:9%;"><b>−scan−for−duplicates</b></p>
+
+<p style="margin-left:18%;">Keep a running list of file
+hashes, attempting to link as many files together as
+possible. This results in the smallest possible ISO image,
+but may be very slow, particular with large files.</p>
+
+<h2>HFS OPTIONS
+<a name="HFS OPTIONS"></a>
+</h2>
+
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="5%">
+
+
+<p style="margin-top: 1em"><b>−hfs</b></p></td>
+<td width="4%"></td>
+<td width="82%">
+
+
+<p style="margin-top: 1em">(not supported by
+pycdlib-genisoimage) Create an ISO9660/HFS hybrid CD. This
+option should be used in conjunction with the
+<b>−map</b>, <b>−magic</b> and/or the various
+<i>double dash</i> options given below.</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−no−hfs</b></p>
+
+<p style="margin-left:18%;">Do not create an ISO-9660/HFS
+hybrid CD even though other options may imply to do so.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−apple</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Create an ISO9660
+CD with Apple’s extensions. Similar to
+<b>−hfs</b>, except that the Apple Extensions to
+ISO9660 are added instead of creating an HFS hybrid volume.
+Former <b>pycdlib-genisoimage</b> versions did include Rock
+Ridge attributes by default if <b>−apple</b> was
+specified. This versions of <b>pycdlib-genisoimage</b> does
+not do this anymore. If you like to have Rock Ridge
+attributes, you need to specify this separately.</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−map</b>
+<i>mapping_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Use the <i>mapping_file</i> to set the
+CREATOR and TYPE information for a file based on the
+filename’s extension. A filename is mapped only if it
+is not one of the know Apple/Unix file formats.</p>
+
+<p style="margin-left:9%;"><b>−magic</b>
+<i>magic_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) The CREATOR and TYPE information is set
+by using a file’s <i>magic number</i> (usually the
+first few bytes of a file). The <i>magic_file</i> is only
+used if a file is not one of the known Apple/Unix file
+formats, or the filename extension has not been mapped using
+<b>−map</b>.</p>
+
+<p style="margin-left:9%;"><b>−hfs−creator</b>
+<i>creator</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the default CREATOR for all files.
+Must be exactly 4 characters.</p>
+
+<p style="margin-left:9%;"><b>−hfs−type</b>
+<i>type</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the default TYPE for all files.
+Must be exactly 4 characters.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−probe</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Search the
+contents of files for all the known Apple/Unix file formats.
+However, the only way to check for <i>MacBinary</i> and
+<i>AppleSingle</i> files is to open and read them, so this
+option may increase processing time. It is better to use one
+or more <i>double dash</i> options given below if the
+Apple/Unix formats in use are known.</p></td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−no−desktop</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Do not create (empty) Desktop files.
+New HFS Desktop files will be created when the CD is used on
+a Macintosh (and stored in the System Folder). By default,
+empty Desktop files are added to the HFS volume.</p>
+
+
+<p style="margin-left:9%;"><b>−mac−name</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Use the HFS filename as the starting
+point for the ISO9660, Joliet and Rock Ridge filenames.</p>
+
+
+<p style="margin-left:9%;"><b>−boot−hfs−file</b>
+<i>driver_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Installs the <i>driver_file</i> that
+<i>may</i> make the CD bootable on a Macintosh.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="6%">
+
+
+<p><b>−part</b></p></td>
+<td width="3%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Generate an HFS
+partition table. By default, no partition table is
+generated, but some older Macintosh CD-ROM drivers need an
+HFS partition table on the CD-ROM to be able to recognize a
+hybrid CD-ROM.</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−auto</b>
+<i>AutoStart_file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Make the HFS CD use the QuickTime 2.0
+Autostart feature to launch an application or document. The
+given filename must be the name of a document or application
+located at the top level of the CD. The filename must be
+less than 12 characters. (Alpha).</p>
+
+
+<p style="margin-left:9%;"><b>−cluster−size</b>
+<i>size</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the size in bytes of the cluster or
+allocation units of PC Exchange files. Implies
+<b>−−exchange</b>.</p>
+
+<p style="margin-left:9%;"><b>−hide−hfs</b>
+<i>glob</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Hide <i>glob</i>, a shell wildcard
+pattern, from the HFS volume. The file or directory will
+still exist in the ISO9660 and/or Joliet directory.
+<i>glob</i> may match any part of the filename. Multiple
+globs may be excluded. Example:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em">pycdlib-genisoimage
+−o rom −hfs −hide−hfs '*.o'
+−hide−hfs foobar</p>
+
+<p style="margin-left:18%; margin-top: 1em">would exclude
+all files ending in ‘.o’ or called <i>foobar</i>
+from the HFS volume. Note that if you had a directory called
+<i>foobar</i>, it too (and of course all its descendants)
+would be excluded. The <i>glob</i> can also be a path name
+relative to the source directories given on the command
+line. Example:</p>
+
+
+<p style="margin-left:18%; margin-top: 1em">pycdlib-genisoimage
+−o rom −hfs −hide−hfs src/html
+src</p>
+
+<p style="margin-left:18%; margin-top: 1em">would exclude
+just the file or directory called <i>html</i> from the
+<i>src</i> directory. Any other file or directory called
+<i>html</i> in the tree will not be excluded. Should be used
+with <b>−hide</b> and/or
+<b>−hide−joliet</b>. In order to match a
+directory name, make sure the pattern does not include a
+trailing ‘/’ character. See <i>README.hide</i>
+for more details.</p>
+
+
+<p style="margin-left:9%;"><b>−hide−hfs−list</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Specify a file containing a list of
+wildcard patterns to be hidden as in
+<b>−hide−hfs</b>.</p>
+
+<p style="margin-left:9%;"><b>−hfs−volid</b>
+<i>hfs_volid</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Volume name for the HFS partition. This
+is the name that is assigned to the disc on a Macintosh and
+replaces the <i>volid</i> used with <b>−V</b>.</p>
+
+
+<p style="margin-left:9%;"><b>−icon−position</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Use the icon position information, if
+it exists, from the Apple/Unix file. The icons will appear
+in the same position as they would on a Macintosh desktop.
+Folder location and size on screen, its scroll positions,
+folder View (view as Icons, Small Icons, etc.) are also
+preserved. (Alpha).</p>
+
+<p style="margin-left:9%;"><b>−root−info</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Set the location, size on screen,
+scroll positions, folder View etc. for the root folder of an
+HFS volume. See <i>README.rootinfo</i> for more information.
+(Alpha)</p>
+
+<p style="margin-left:9%;"><b>−prep−boot</b>
+<i>file</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) PReP boot image file. Up to 4 are
+allowed. See <i>README.prep_boot</i> for more information.
+(Alpha)</p>
+
+
+<p style="margin-left:9%;"><b>−chrp−boot</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Add CHRP boot header.</p>
+
+
+<p style="margin-left:9%;"><b>−input−hfs−charset</b>
+<i>charset</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Input charset that defines the
+characters used in HFS filenames when used with
+<b>−mac−name</b>. The default charset is
+<i>cp10000</i> (Mac Roman).</p>
+
+
+<p style="margin-left:9%;"><b>−output−hfs−charset</b>
+<i>charset</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Output charset that defines the
+characters that will be used in the HFS filenames. Defaults
+to the input charset.</p>
+
+
+<p style="margin-left:9%;"><b>−hfs−unlock</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) By default, <b>pycdlib-genisoimage</b>
+will create an HFS volume that is locked. This option leaves
+the volume unlocked so that other applications (e.g.
+<b>hfsutils</b>) can modify the volume.</p>
+
+<p style="margin-left:9%;"><b>−hfs−bless</b>
+<i>folder_name</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) "Bless" the given directory
+(folder). This is usually the <i>System Folder</i> and is
+used in creating HFS bootable CDs. The name of the directory
+must be the whole path name as <b>pycdlib-genisoimage</b>
+sees it. E.g., if the given pathspec is <i>./cddata</i> and
+the required folder is called <i>System Folder</i>, the
+whole path name is <i>"/cddata/System Folder"</i>
+(remember to use quotes if the name contains spaces).</p>
+
+<p style="margin-left:9%;"><b>−hfs−parms</b>
+<i>parameters</i></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Override certain parameters used to
+create the HFS filesystem. Unlikely to be used in normal
+circumstances.</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="6%">
+
+
+<p><b>−−cap</b></p></td>
+<td width="3%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Look for AUFS CAP
+Macintosh files. Search for CAP Apple/Unix file formats
+only. Searching for the other possible Apple/Unix file
+formats is disabled, unless other <i>double dash</i> options
+are given.</p></td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−−netatalk</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for NETATALK Macintosh files</p>
+
+<p style="margin-left:9%;"><b>−−double</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for AppleDouble Macintosh
+files</p>
+
+
+<p style="margin-left:9%;"><b>−−ethershare</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for Helios EtherShare Macintosh
+files</p>
+
+<p style="margin-left:9%;"><b>−−ushare</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for IPT UShare Macintosh files</p>
+
+
+<p style="margin-left:9%;"><b>−−exchange</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for PC Exchange Macintosh
+files</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="6%">
+
+
+<p><b>−−sgi</b></p></td>
+<td width="3%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Look for SGI
+Macintosh files</p></td></tr>
+</table>
+
+<p style="margin-left:9%;"><b>−−xinet</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for XINET Macintosh files</p>
+
+<p style="margin-left:9%;"><b>−−macbin</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for MacBinary Macintosh files</p>
+
+<p style="margin-left:9%;"><b>−−single</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for AppleSingle Macintosh
+files</p>
+
+<table width="100%" border="0" rules="none" frame="void"
+ cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−−dave</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Look for Thursby
+Software Systems DAVE Macintosh files</p></td></tr>
+<tr valign="top" align="left">
+<td width="9%"></td>
+<td width="8%">
+
+
+<p><b>−−sfm</b></p></td>
+<td width="1%"></td>
+<td width="82%">
+
+
+<p>(not supported by pycdlib-genisoimage) Look for
+Microsoft’s Services for Macintosh files (NT only)
+(Alpha)</p> </td></tr>
+</table>
+
+
+<p style="margin-left:9%;"><b>−−osx−double</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for Mac OS X AppleDouble Macintosh
+files</p>
+
+
+<p style="margin-left:9%;"><b>−−osx−hfs</b></p>
+
+<p style="margin-left:18%;">(not supported by
+pycdlib-genisoimage) Look for Mac OS X HFS Macintosh
+files</p>
+
+<h2>SEE ALSO
+<a name="SEE ALSO"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">genisoimage(1),
+pycdlib-explorer(1), pycdlib-extract-files(1)</p>
+
+<h2>AUTHOR
+<a name="AUTHOR"></a>
+</h2>
+
+
+<p style="margin-left:9%; margin-top: 1em">Chris Lalancette
+<clalancette at gmail.com></p>
+<hr>
+</body>
+</html>
diff --git a/devtools/contrib/pycdlib/docs/pydoc_data/_pydoc.css b/devtools/contrib/pycdlib/docs/pydoc_data/_pydoc.css
new file mode 100644
index 00000000000..a6aa2e4c1a0
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/pydoc_data/_pydoc.css
@@ -0,0 +1,112 @@
+/*
+ CSS file for pydoc.
+
+ Contents of this file are subject to change without notice.
+
+*/
+
+body {
+ background-color: #f0f0f8;
+}
+
+table.heading tr {
+ background-color: #7799ee;
+}
+
+.decor {
+ color: #ffffff;
+}
+
+.title-decor {
+ background-color: #ffc8d8;
+ color: #000000;
+}
+
+.pkg-content-decor {
+ background-color: #aa55cc;
+}
+
+.index-decor {
+ background-color: #ee77aa;
+}
+
+.functions-decor {
+ background-color: #eeaa77;
+}
+
+.data-decor {
+ background-color: #55aa55;
+}
+
+.author-decor {
+ background-color: #7799ee;
+}
+
+.credits-decor {
+ background-color: #7799ee;
+}
+
+.error-decor {
+ background-color: #bb0000;
+}
+
+.grey {
+ color: #909090;
+}
+
+.white {
+ color: #ffffff;
+}
+
+.repr {
+ color: #c040c0;
+}
+
+table.heading tr td.title {
+ vertical-align: bottom;
+}
+
+table.heading tr td.extra {
+ vertical-align: bottom;
+ text-align: right;
+}
+
+.heading-text {
+ font-family: helvetica, arial;
+}
+
+.bigsection {
+ font-size: larger;
+}
+
+.title {
+ font-size: x-large;
+}
+
+.code {
+ font-family: monospace;
+}
+
+table {
+ width: 100%;
+ border-spacing : 0;
+ border-collapse : collapse;
+ border: 0;
+}
+
+td {
+ padding: 2;
+}
+
+td.section-title {
+ vertical-align: bottom;
+}
+
+td.multicolumn {
+ width: 25%;
+ vertical-align: bottom;
+}
+
+td.singlecolumn {
+ width: 100%;
+}
diff --git a/devtools/contrib/pycdlib/docs/python-compatibility.md b/devtools/contrib/pycdlib/docs/python-compatibility.md
new file mode 100644
index 00000000000..f5f1ffdb045
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/python-compatibility.md
@@ -0,0 +1,17 @@
+# Python Compatibility
+The [test suite](design.md#testing) ensures that the core PyCdlib code works with all versions of Python greater than or equal to Python 3.7.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="standards.html"><-- Standards</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="examples.html">Examples --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/reporting-issues.md b/devtools/contrib/pycdlib/docs/reporting-issues.md
new file mode 100644
index 00000000000..1387df35ccd
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/reporting-issues.md
@@ -0,0 +1,16 @@
+# What to do when things go wrong
+The PyCdlib library can fail for a number of reasons. The most common reason for an unexpected failure is when an ISO file doesn't follow the relevant standards. In these cases, PyCdlib will usually throw a `PyCdlibInvalidISO` exception (though it can also throw a `PyCdlibInternalError` in some circumstances). If this happens, the absolute best thing to do is to open up a new [issue](https://github.com/clalancette/pycdlib/issues), putting a pointer to the problematic ISO in the issue. If the ISO file cannot be shared, then a new issue should be created anyway, with the relevant backtrace from PyCdlib in it. From there, additional information can sometimes help to identify the root cause of the issue.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="exceptions.html"><-- Exceptions</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/standards.md b/devtools/contrib/pycdlib/docs/standards.md
new file mode 100644
index 00000000000..0fc75e4c4a6
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/standards.md
@@ -0,0 +1,55 @@
+# Standards
+The original ISO9660 standard is fairly old, having first been ratified in 1988. This standard has many limitations (such as a maximum of 8 directory levels, a maximum of 31 characters for filenames, etc.), and thus a number of extensions have made the original standard a lot more palatable on modern systems. The most relevant standards that are used today include:
+
+- [ISO 9660:1988](https://en.wikipedia.org/wiki/ISO_9660). The original ISO9660 standard. Also known as [ECMA-119](https://www.ecma-international.org/publications/standards/Ecma-119.htm).
+- [ISO/IEC 9660:1999](http://pismotec.com/cfs/iso9660-1999.html). The extension to the original ISO9660 standard done in 1999.
+- [El Torito](http://wiki.osdev.org/El-Torito). The standard for making ISO9660 compatible disks bootable.
+- [Joliet](https://en.wikipedia.org/wiki/Joliet_(file_system)). The Microsoft extension to ISO9660 to allow deeper directories, longer filenames, etc. The complete standard is [here](http://pismotec.com/cfs/jolspec.html).
+- [System Use Sharing Protocol (SUSP)](http://fileformats.archiveteam.org/wiki/System_Use_Sharing_Protocol#Specifications), Versions 1.09, 1.10, and 1.12. The standard for extending the amount of metadata each ISO9660 filename or directory can have (necessary for Rock Ridge, below).
+- [Rock Ridge Interchange Protocol (RRIP)](http://fileformats.archiveteam.org/wiki/Rock_Ridge#Specifications), Versions 1.09, 1.10, and 1.12. The standard for adding Unix-like metadata and semantics to ISO9660 filenames and directories.
+- [ISO/IEC 13346-1:1995](https://www.iso.org/standard/26783.html). The basis for the more modern UDF standard below. Also known as [ECMA-167](https://www.ecma-international.org/publications/standards/Ecma-167.htm).
+- [UDF](http://www.osta.org/specs/), Version 1.02 through 2.60. The standard used on DVDs and more modern ISOs.
+- [ECMA TR-071](https://www.ecma-international.org/publications/techreports/E-TR-071.htm). A technical report on how to implement UDF for Read-Only DVD media.
+- [Arbitrary Attribute](https://dev.lovelyhq.com/libburnia/libisofs/src/commit/d297ce3aed5935e469bb108a36b7d6e31763a075/doc/susp_aaip_2_0.txt) standard from libburnia.
+
+Unfortunately, accessing most of these standards requires a license, so the links above are not primary sources (with the exception of the ECMA standards, which are available free of charge from [https://www.ecma-international.org](https://www.ecma-international.org)). Nevertheless, they give a good overview of the state of the ISO ecosystem as it exists today.
+
+While PyCdlib aims to be compliant with these standards, there are a number of complicating factors. One such factor is that there are places in the standards that are ambiguous, and different implementations have taken different approaches to solving the same problem. Another complicating factor is the fact that there are several "standard" parts of ISOs that have no relevant standard backing them up; they are just generally agreed to by the various implementations. PyCdlib takes a middle road here, and tries to be pretty forgiving with the type of ISOs that it can open, but fairly strict with what it can produce. When there are ambiguities in the standards, PyCdlib generally takes the approach of being compliant with whatever [cdrkit](https://launchpad.net/cdrkit) does. However, there are several bugs in the cdrkit implementation, so in those cases, PyCdlib falls back to being standards compliant.
+
+## Interchange levels
+The original ISO9660 standard defines "interchange" levels 1, 2, and 3. The differences between the three interchange levels is practically irrelevant now, as most modern ISOs use interchange level 3 (and supplement it with one of the extensions). A newer version of the ISO9660 standard was put out in 1999 that lifts some of the restrictions of the original ISO9660 standard. PyCdlib follows the lead of genisoimage here and defines this as interchange level 4, although that is not an "official" designation. For almost all use cases, interchange level 3 should be used with the [Rock Ridge](#rock-ridge) and [Joliet](#joliet) extensions.
+
+## Rock Ridge and Joliet
+The two most common extensions to the original ISO9660 standard are Rock Ridge and Joliet, both of which allow ISOs to contain deeper directory structures, longer filenames, and other features usually used by modern filesystems. While both standards aim to accomplish the same goal, they do it in entirely different ways, and some of those details leak through into the PyCdlib API. Thus, a brief discussion of each of them is in order.
+
+### Rock Ridge
+The standard commonly referred to as "Rock Ridge" is actually two standards, SUSP and Rock Ridge proper. SUSP stands for "System Use and Sharing Protocol", and defines a few generic, operating system-independent fields to be placed at the end of file and directory metadata on the ISO. Rock Ridge proper then defines a number of Unix-specific fields to be placed after the SUSP fields. The combination of the two allows ISOs to contain Unix-like semantics for each file and directory, including permission bits, longer filenames, timestamps, symlinks, character and block devices, and a few other minor features. One important thing to realize about Rock Ridge is that it is an extension to the original ISO9660, and thus shares the file/directory structure with the original ISO. This structure can actually be virtually extended for deeper directory structures, but that is an implementation detail and will be glossed over here. For more information, read the Rock Ridge standard.
+
+### Joliet
+The Joliet standard came out of Microsoft, and is primarily intended to provide extensions to ISO for Windows compatibility. However, the data stored in Joliet is mostly generic, so can easily be used by all operating systems. In large contrast to Rock Ridge, Joliet uses an entirely different context to store the file and directory structure of the extended names. The file *data* is shared between ISO9660 and Joliet, but the essential metadata is not. The consequence of this is that there can be files on the ISO that are only visible to ISO9660/Rock Ridge, files that are only visible to Joliet, or files that are visible to both. That being said, the most common arrangement is for the file and directory structure to be replicated between ISO9660/Rock Ridge and Joliet.
+
+## El Torito
+El Torito is the name of the standard used to make an ISO bootable. Without going into the details too much, El Torito works by defining a "boot catalog" that has a list of one or more entries for booting. Each entry consists of a pointer to a file on the ISO, and a booting method. The available booting methods are:
+
+1. Floppy emulation booting - Emulate the boot that would have been done by a floppy disk. The file to be used must be one of the sizes of a floppy, and must have a particular structure. This method was developed to support very old BIOS's that didn't know how to boot from a CD, but is rarely used anymore.
+1. HD emulation - Emulate the boot that would have happened from a hard drive. The file to be used must be 512 bytes long, and be a valid MBR. Again, this method was developed to support old BIOS's, but is rarely used in modern ISOs.
+1. No emulation - Don't do any emulation for booting. This is the method that is used for BIOS's that know how to boot from ISO, and is the one most commonly used today. There are few restrictions on what the contents of the file must be, other than it should be valid code for the machine it will be used to boot.
+
+## UDF
+The "Universal Disk Format" was proposed in the late 1990's to supplant the aging ISO9660 as the filesystem of choice on optical media. It was adopted as the standard DVD filesystem format, and thus started gaining widespread popularity and use. UDF can either be used as the only filesystem on an ISO, or can be used in combination with the original ISO9660 filesystem. The latter is known as the "bridge" format.
+
+The UDF filesystem format consists of two or three separate standards, depending on how you count them. ECMA-167 is the base standard that defines the filesystem structure, types, etc. The UDF standard starts with ECMA-167 and adds specific rules and restrictions that, when followed, make a filesystem layout UDF compliant. Further, ECMA TR-071 starts with the UDF standard and adds even more rules and restrictions that, when followed, make a filesystem layout DVD Read-only media compliant.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="python-compatibility.html">Python Compatibility --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/docs/tools.md b/devtools/contrib/pycdlib/docs/tools.md
new file mode 100644
index 00000000000..c005461f7c2
--- /dev/null
+++ b/devtools/contrib/pycdlib/docs/tools.md
@@ -0,0 +1,26 @@
+# Tools
+PyCdlib comes with several tools built on the main library API. These tools are useful in themselves, but also give (relatively) simple examples on how to use the PyCdlib API in a real application.
+
+## pycdlib-genisoimage
+The `pycdlib-genisoimage` tool aims to be a flag-compatible drop-in replacement for the venerable [genisoimage](https://linux.die.net/man/1/genisoimage) tool, with the big exception that it uses PyCdlib under the hood. Please see the man page [pycdlib-genisoimage](pycdlib-genisoimage.html) for more information.
+
+## pycdlib-explorer
+The `pycdlib-explorer` tool gives the user a convenient, shell-like interface for opening, exploring, and manipulating existing ISO files. Please see the man page [pycdlib-explorer](pycdlib-explorer.html) for more information.
+
+## pycdlib-extract-files
+The `pycdlib-extract-files` tool is a convenient way to extract particular files or directories from an ISO (including the entire ISO). Please see the man page [pycdlib-extract-files](pycdlib-extract-files.html) for more information.
+
+---
+
+<div style="width: 100%; display: table;">
+ <div style="display: table-row;">
+ <div style="width: 33%; display: table-cell; text-align: left;">
+ <a href="design.html"><-- Design</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: center;">
+ <a href="https://clalancette.github.io/pycdlib/">Top</a>
+ </div>
+ <div style="width: 33%; display: table-cell; text-align: right;">
+ <a href="exceptions.html">Exceptions --></a>
+ </div>
+</div>
diff --git a/devtools/contrib/pycdlib/examples/create-bootable.py b/devtools/contrib/pycdlib/examples/create-bootable.py
new file mode 100644
index 00000000000..468c02ad8ce
--- /dev/null
+++ b/devtools/contrib/pycdlib/examples/create-bootable.py
@@ -0,0 +1,40 @@
+# This is program to show how to use PyCdlib to create a new ISO that is
+# bootable.
+
+# Import standard python modules.
+import sys
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+# Import pycdlib itself.
+import pycdlib
+
+# Check that there are enough command-line arguments.
+if len(sys.argv) != 1:
+ print('Usage: %s' % (sys.argv[0]))
+ sys.exit(1)
+
+# Create a new PyCdlib object.
+iso = pycdlib.PyCdlib()
+
+# Create a new ISO, accepting all of the defaults.
+iso.new()
+
+# Add a new file to the ISO, with the contents coming from the file object.
+# This file will be used as the boot file on the bootable ISO.
+bootstr = b'boot\n'
+iso.add_fp(BytesIO(bootstr), len(bootstr), '/BOOT.;1')
+
+# Once the bootable file is on the ISO, we need to link it to the boot catalog
+# by calling add_eltorito.
+iso.add_eltorito('/BOOT.;1', bootcatfile='/BOOT.CAT;1')
+
+# Write out the ISO to the file called 'eltorito.iso'. This will fully master
+# the ISO, making it bootable.
+iso.write('eltorito.iso')
+
+# Close the ISO object. After this call, the PyCdlib object has forgotten
+# everything about the previous ISO, and can be re-used.
+iso.close()
diff --git a/devtools/contrib/pycdlib/examples/create-new.py b/devtools/contrib/pycdlib/examples/create-new.py
new file mode 100644
index 00000000000..f0960a5dce9
--- /dev/null
+++ b/devtools/contrib/pycdlib/examples/create-new.py
@@ -0,0 +1,46 @@
+# This is a simple program to show how to use PyCdlib to create a new
+# ISO, with one file and one directory on it.
+
+# Import standard python modules.
+import sys
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+# Import pycdlib itself.
+import pycdlib
+
+# Check that there are enough command-line arguments.
+if len(sys.argv) != 1:
+ print('Usage: %s' % (sys.argv[0]))
+ sys.exit(1)
+
+# Create a new PyCdlib object.
+iso = pycdlib.PyCdlib()
+
+# Create a new ISO, accepting all of the defaults.
+iso.new()
+
+# Add a new file to the ISO, with the contents coming from the file object.
+# Note that the file object must remain open for the lifetime of the PyCdlib
+# object, as the PyCdlib object uses it for internal operations. Also note that
+# the filename passed here is the filename the data will get assigned on the
+# final ISO; it must begin with a forward slash, and according to ISO9660 must
+# have a '.', and a semicolon followed by a number. PyCdlib will raise a
+# PyCdlibException if any of the rules for an ISO9660 filename are violated.
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+
+# Add a new directory to the ISO. Like the filename above, ISO9660 directory
+# names must conform to certain standards, and PyCdlib will raise a
+# PyCdlibException if those standards are not met.
+iso.add_directory('/DIR1')
+
+# Write out the ISO to the file called 'new.iso'. This will fully master the
+# ISO, creating a file that can be burned onto a CD.
+iso.write('new.iso')
+
+# Close the ISO object. After this call, the PyCdlib object has forgotten
+# everything about the previous ISO, and can be re-used.
+iso.close()
diff --git a/devtools/contrib/pycdlib/examples/extract-data.py b/devtools/contrib/pycdlib/examples/extract-data.py
new file mode 100644
index 00000000000..881b87f4a77
--- /dev/null
+++ b/devtools/contrib/pycdlib/examples/extract-data.py
@@ -0,0 +1,38 @@
+# This is a simple program to show how to use PyCdlib to extract data from the
+# ISO.
+
+# Import standard python modules.
+import sys
+try:
+ from cStringIO import StringIO as BytesIO
+except ImportError:
+ from io import BytesIO
+
+# Import pycdlib itself.
+import pycdlib
+
+# Check that there are enough command-line arguments.
+if len(sys.argv) != 1:
+ print('Usage: %s' % (sys.argv[0]))
+ sys.exit(1)
+
+
+# First we'll create a new ISO and write it out (see create-new.py for more
+# information about these steps).
+iso = pycdlib.PyCdlib()
+iso.new()
+foostr = b'foo\n'
+iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1')
+out = BytesIO()
+iso.write_fp(out)
+iso.close()
+
+# Now, let's open up the ISO and extract the contents of the FOO.;1 file.
+iso.open_fp(out)
+extracted = BytesIO()
+# Use the get_file_from_iso_fp() API to extract the named filename into the file
+# descriptor.
+iso.get_file_from_iso_fp(extracted, iso_path='/FOO.;1')
+iso.close()
+
+print(extracted.getvalue().decode('utf-8'))
diff --git a/devtools/contrib/pycdlib/examples/open-existing.py b/devtools/contrib/pycdlib/examples/open-existing.py
new file mode 100644
index 00000000000..e3206280e20
--- /dev/null
+++ b/devtools/contrib/pycdlib/examples/open-existing.py
@@ -0,0 +1,30 @@
+# This is a simple example program to show how to use PyCdlib to open up an
+# existing ISO passed on the command-line, and print out all of the file names
+# at the root of the ISO.
+
+# Import standard python modules.
+import sys
+
+# Import pycdlib itself.
+import pycdlib
+
+# Check that there are enough command-line arguments.
+if len(sys.argv) != 2:
+ print('Usage: %s <iso>' % (sys.argv[0]))
+ sys.exit(1)
+
+# Create a new PyCdlib object.
+iso = pycdlib.PyCdlib()
+
+# Open up a file object. This causes PyCdlib to parse all of the metadata on the
+# ISO, which is used for later manipulation.
+iso.open(sys.argv[1])
+
+# Now iterate through each of the files on the root of the ISO, printing out
+# their names.
+for child in iso.list_children(iso_path='/'):
+ print(child.file_identifier())
+
+# Close the ISO object. After this call, the PyCdlib object has forgotten
+# everything about the previous ISO, and can be re-used.
+iso.close()
diff --git a/devtools/contrib/pycdlib/examples/open-raw-windows-device.py b/devtools/contrib/pycdlib/examples/open-raw-windows-device.py
new file mode 100644
index 00000000000..65e39ccfb90
--- /dev/null
+++ b/devtools/contrib/pycdlib/examples/open-raw-windows-device.py
@@ -0,0 +1,22 @@
+# This is a example program to show how to use PyCdlib to open up a 'raw'
+# Windows device. That is, assuming that the real CD drive on a computer is
+# 'D:', this example will open up a disc inserted into that drive and print
+# all of the file names at the root of the ISO.
+
+# Import pycdlib itself.
+import pycdlib
+
+# Create a new PyCdlib object.
+iso = pycdlib.PyCdlib()
+
+# Open up the raw Windows CD drive.
+iso.open(r'\\.\D:')
+
+# Now iterate through each of the files on the root of the ISO, printing out
+# their names.
+for child in iso.list_children(iso_path='/'):
+ print(child.file_identifier())
+
+# Close the ISO object. After this call, the PyCdlib object has forgotten
+# everything about the previous ISO, and can be re-used.
+iso.close()
diff --git a/devtools/contrib/pycdlib/man/pycdlib-explorer.1 b/devtools/contrib/pycdlib/man/pycdlib-explorer.1
new file mode 100644
index 00000000000..5fc1aa2ee34
--- /dev/null
+++ b/devtools/contrib/pycdlib/man/pycdlib-explorer.1
@@ -0,0 +1,108 @@
+.TH PYCDLIB-EXPLORER 1 "Jan 2018" "pycdlib-explorer"
+
+.SH NAME
+pycdlib-explorer - tool to examine and modify ISOs using pycdlib
+
+.SH SYNOPSIS
+.B pycdlib-explorer <iso-file>
+
+.SH DESCRIPTION
+This is a tool to examine and modify existing ISO files on disk. Using this
+tool, the files, directories, and metadata on an ISO can be examined, new
+files can be added, and old files can be deleted. Note that due to the nature
+of the ISO standard, files or directories on the ISO cannot be modified in
+place in a general way. To accomplish this, remove the file and then re-add
+it with new contents.
+
+The commands that change the contents of the ISO only modify the in-memory copy.
+Changes are written out to a new ISO file when the \fBwrite\fR command is issued.
+
+pycdlib-explorer has no command-line options; instead, it is controlled
+entirely at runtime through commands. The following section describes the
+available commands in pycdlib-explorer.
+
+.SH COMMANDS
+.TP
+.B "add_file <iso_path> <src_filename> [rr_name=<rr_name>] [joliet_path=<joliet_path>]"
+Add the contents of \fBsrc_filename\fR to the ISO at the location specified in \fBiso_path\fR.
+If the ISO is a Rock Ridge ISO, \fBrr_name\fR must be specified; otherwise, it must not be.
+If the ISO is not a Joliet ISO, \fBjoliet_path\fR must not be specified. If the ISO is a
+Joliet ISO, \fBjoliet_path\fR is optional, but highly recommended to supply.
+.TP
+.B "cd <iso_dir>"
+Change the current working directory to relative or absolute ISO path \fBiso_dir\fR.
+.TP
+.B "cwd"
+Show the current working directory.
+.TP
+.B "exit"
+Exit out of pycdlib-explorer.
+.TP
+.B "get <iso_file> <out_file>"
+Copy the contents of the relative or absolute ISO path \fBiso_file\fR into \fBout_file\fR.
+.TP
+.B "help"
+Print the available commands. Use "help <cmd>" for a more detailed
+description of the commands, including the command-line arguments they
+require.
+.TP
+.B "ls"
+Show the contents of the current working directory. The format of the output is:
+TYPE(F=file, D=directory) NAME.
+.TP
+.B "modify_file_in_place <iso_path> <src_filename>"
+Replace the contents of the file referred to in \fBiso_path\fR with the contents
+from \fBsrc_filename\fR.
+Warning: This command modifies the opened ISO file in-place.
+Also, the \fBsrc_filename\fR must occupy the same number of extents (2048-byte blocks) as the original file.
+.TP
+.B "mkdir <iso_path> [rr_name=<rr_name>] [joliet_path=<joliet_path>]"
+Make a new directory called \fBiso_path\fR.
+If the ISO is a Rock Ridge ISO, \fBrr_name\fR must be specified; otherwise, it must not be.
+If the ISO is not a Joliet ISO, \fBjoliet_path\fR must not be specified. If the ISO is a
+Joliet ISO, \fBjoliet_path\fR is optional, but highly recommended to supply.
+.TP
+.B "print_mode [iso9660|rr|joliet|udf]"
+Change which 'mode' of filenames are printed out. There are four main
+modes: ISO9660 (iso9660, the default), Rock Ridge (rr), Joliet (joliet), and
+UDF (udf). The original iso9660 mode only allows filenames of 8 characters,
+plus 3 for the extension. The Rock Ridge extensions allow much longer
+filenames and much deeper directory structures. The Joliet extensions also
+allow longer filenames and deeper directory structures, but in an entirely
+different context (though in most circumstances, the Joliet context will
+mirror the ISO9660/Rock Ridge context). The UDF Bridge extensions add an
+entirely parallel UDF context to the ISO as well. Any given ISO will always
+have ISO9660 mode, but may have any combination of Rock Ridge, Joliet, and UDF
+(including none of them). Running this command with no arguments prints out
+the current mode. Passing 'iso9660' as an argument sets it to the original
+ISO9660 mode. Passing 'rr' as an argument sets it to Rock Ridge mode.
+Passing 'joliet' as an argument sets it to Joliet mode. Passing 'udf' as an
+argument sets it to UDF mode.
+.TP
+.B "quit"
+Exit out of pycdlib-explorer.
+.TP
+.B "rm_file <iso_path>"
+Remove the file named \fBiso_path\fR from the ISO. Note that this
+must be a file; to remove a directory, use \fBrmdir\fR.
+.TP
+.B "rmdir <iso_path>"
+Remove the directory named \fBiso_path\fR from the ISO. Note that
+this must be a directory; to remove a file, use \fBrm_file\fR.
+.TP
+.B "tree"
+List the contents of the ISO in a tree-like format, similar to the
+bash \fBtree\fR command.
+.TP
+.B "write <out_file>"
+Write a valid ISO9660 file out to \fBout_file\fR, taking into
+account any changes made while running the program. This is
+also sometimes referred to as "mastering" the ISO. Note that
+the \fBout_file\fR must NOT be the same file as the input
+file, or the resulting ISO will not work properly.
+
+.SH SEE ALSO
+pycdlib-extract-files(1), pycdlib-genisoimage(1)
+
+.SH AUTHOR
+Chris Lalancette <clalancette at gmail.com>
diff --git a/devtools/contrib/pycdlib/man/pycdlib-extract-files.1 b/devtools/contrib/pycdlib/man/pycdlib-extract-files.1
new file mode 100644
index 00000000000..9a5a7fdb223
--- /dev/null
+++ b/devtools/contrib/pycdlib/man/pycdlib-extract-files.1
@@ -0,0 +1,40 @@
+.TH PYCDLIB-EXTRACT-FILES 1 "Sep 2018" "pycdlib-extract-files"
+
+.SH NAME
+pycdlib-extract-files - tool to extract files from an ISO using pycdlib
+
+.SH SYNOPSIS
+.B pycdlib-extract-files [OPTIONS] <iso-file>
+
+.SH DESCRIPTION
+This is a tool to extract files from an existing ISO file. Using this tool,
+the files and directories on an ISO can be extracted to the local filesystem.
+
+.SH OPTIONS
+.TP
+.BI \-path\-type " [auto,iso9660,rockridge,joliet,udf]"
+Specifies the path name convention to use while extracting the files. If the
+ISO to be extracted doesn't contain the path type specified, an error is thrown.
+If not specified, defaults to auto, which first tries to extract files via the
+UDF path, then the Rock Ridge path, then the Joliet path, and finally falls back
+to the ISO9660 path if all else fails. Once one path type succeeds, all files
+will be extracted as that path type.
+.TP
+.BI \-start\-path " <pathname>"
+The ISO path to start extracting from, specified in a Unix like path (something
+like "/foo/bar"). If not specified, extraction starts from the root of the ISO
+(equivalent to specifying "/"). The starting path must be a directory on the
+ISO. If \-path\-type is not specified, the path is an ISO9660 style path.
+If \-path\-type is specified, the start path specified here must be of that
+particular type.
+.TP
+.BI \-extract\-to " <destpath>"
+The local directory to extract the data to. The "destpath" must exist and
+must be a directory, or an error will be thrown. If not specified,
+pycdlib\-extract\-files will extract the files to the current directory.
+
+.SH SEE ALSO
+pycdlib-explorer(1), pycdlib-genisoimage(1)
+
+.SH AUTHOR
+Chris Lalancette <clalancette at gmail.com>
diff --git a/devtools/contrib/pycdlib/man/pycdlib-genisoimage.1 b/devtools/contrib/pycdlib/man/pycdlib-genisoimage.1
new file mode 100644
index 00000000000..86398d7e878
--- /dev/null
+++ b/devtools/contrib/pycdlib/man/pycdlib-genisoimage.1
@@ -0,0 +1,1778 @@
+.TH PYCDLIB-GENISOIMAGE 1 "Sep 2017" "pycdlib-genisoimage"
+
+.SH NAME
+pycdlib-genisoimage - tool to master ISOs using pycdlib
+
+.SH SYNOPSIS
+.B pycdlib-genisoimage [options] [-o filename] pathspec [pathspec ...]
+
+.SH DESCRIPTION
+.B pycdlib-genisoimage
+is a pre-mastering program to generate ISO9660/Joliet/HFS hybrid filesystems.
+It is meant to be 100% flag-compatible with the original
+.B genisoimage
+program so that it can be dropped into existing scripts with no changes. Please
+see the man page for
+.B genisoimage
+for more detailed explanation of the options to this program. There are a few
+differences to note between this program and the original
+.B genisoimage.
+First, not all of the options are implemented in this program. This means that
+.B pycdlib-genisoimage
+will silently ignore some flags; for the most common usage of this program,
+this will not matter. However, if you are trying to do something odd and
+specific, it may not work. The flags that this applies to are noted in the
+OPTIONS below. In some cases these flags can be implemented with a bit of
+work, and in some cases the flags can never be implemented due to the design
+of pycdlib. If in doubt, please ask on https://github.com/clalancette/pycdlib/issues.
+Second,
+.B pycdlib-genisoimage
+does not output all of the same messages to standard out/standard error that
+.B genisoimage
+does. Any program that relies on parsing the output of
+.B genisoimage
+will probably not work. Third,
+.B pycdlib-genisoimage
+will not always generate ISOs that are 100% the same as the
+.B genisoimage
+counterparts. This is for a variety of reasons, ranging from bug fixing to simple differences in implementations. In almost all cases this does not matter, but please keep it in mind when using this program instead of
+.B genisoimage.
+
+.SH OPTIONS
+.TP
+.BI \-abstract " file"
+Specifies the abstract filename. There is space for 37 characters.
+.TP
+.BI \-A " application_id"
+.TP
+.BI \-appid " application_id"
+Specifies a text string that will be written into the volume header.
+This should describe the application that will be on the disc. There
+is space for 128 characters.
+.TP
+.B \-allow\-limited\-size
+(not supported by pycdlib-genisoimage) When processing files larger than 2GiB which cannot be easily represented in
+ISO9660, add them with a shrunk visible file size to ISO9660 and with the
+correct visible file size to the UDF system. The result is an inconsistent
+filesystem and users need to make sure that they really use UDF rather than
+ISO9660 driver to read a such disk. Implies enabling
+.BR \-udf.
+.TP
+.B \-allow\-leading\-dots
+.TP
+.B \-ldots
+(not supported by pycdlib-genisoimage) Allow ISO9660 filenames to begin with a period. Usually, a leading dot is
+replaced with an underscore in order to maintain MS-DOS compatibility.
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.B \-allow\-lowercase
+(not supported by pycdlib-genisoimage) This options allows lowercase characters to appear in ISO9660 filenames.
+.br
+This violates the ISO9660 standard, but it happens to work on some systems.
+Use with caution.
+.TP
+.B \-allow\-multidot
+(not supported by pycdlib-genisoimage) This options allows more than one dot to appear in ISO9660 filenames.
+A leading dot is not affected by this option, it
+may be allowed separately using
+.BR \-allow\-leading\-dots .
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.BI \-biblio " file"
+Specifies the bibliographic filename. There is space for 37 characters.
+.TP
+.B \-cache\-inodes
+.TP
+.B \-no\-cache\-inodes
+(not supported by pycdlib-genisoimage) Enable or disable caching inode and device numbers to find hard links
+to files. If
+.B pycdlib-genisoimage
+finds a hard link (a file with multiple names), the file will also be
+hard-linked on the CD, so the file contents only appear once. This
+helps to save space.
+.B \-cache\-inodes
+is default on Unix-like operating systems, but
+.B \-no\-cache\-inodes
+is default on some other systems such as Cygwin, because it is not safe
+to assume that inode numbers are unique on those systems. (Some
+versions of Cygwin create fake inode numbers using a weak hashing
+algorithm, which may produce duplicates.) If two files have the same
+inode number but are not hard links to the same file,
+.B pycdlib-genisoimage \-cache\-inodes
+will not behave correctly.
+.B \-no\-cache\-inodes
+is safe in all situations, but in that case
+.B pycdlib-genisoimage
+cannot detect hard links, so the resulting CD image may be larger
+than necessary.
+.TP
+.BI \-alpha\-boot " alpha_boot_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the boot image to be used when
+making an Alpha/SRM bootable CD. The pathname must be relative to the
+source path specified to
+.BR pycdlib-genisoimage .
+.TP
+.BI \-hppa\-bootloader " hppa_bootloader_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the boot image to be used when
+making an HPPA bootable CD. The pathname must be relative to the
+source path specified to
+.BR pycdlib-genisoimage .
+Other options are required, at the very least a kernel filename and
+a boot command line.
+.TP
+.BI \-hppa\-cmdline " hppa_boot_command_line"
+(not supported by pycdlib-genisoimage) Specifies the command line to be passed to the HPPA boot loader when
+making a bootable CD. Separate the parameters with spaces or
+commas. More options must be passed to
+.B pycdlib-genisoimage,
+at the very least a kernel filename and the boot loader filename.
+.TP
+.BI \-hppa\-kernel\-32 " hppa_kernel_32"
+.TP
+.BI \-hppa\-kernel\-64 " hppa_kernel_64"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the 32-bit and/or 64-bit kernel images
+to be used when making an HPPA bootable CD. The pathnames must be
+relative to the source path specified to
+.BR pycdlib-genisoimage .
+Other options are required, at the very least the boot loader filename
+and the boot command line.
+.TP
+.BI \-hppa\-ramdisk " hppa_ramdisk_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the ramdisk image to be used when
+making an HPPA bootable CD. The pathname must be relative to the
+source path specified to
+.BR pycdlib-genisoimage .
+This parameter is optional. Other options are required, at the very
+least a kernel filename and the boot command line.
+.TP
+.BI \-mips\-boot " mips_boot_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the boot image to be used when
+making an SGI/big-endian MIPS bootable CD. The pathname must be
+relative to the source path specified to
+.BR pycdlib-genisoimage .
+This option may be specified several times, to store up to 15 boot
+images.
+.TP
+.BI \-mipsel\-boot " mipsel_boot_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the boot image to be used when
+making an DEC/little-endian MIPS bootable CD. The pathname must be
+relative to the source path specified to
+.BR pycdlib-genisoimage .
+.TP
+.BI \-B " img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e"
+.TP
+.BI \-sparc\-boot " img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e"
+(not supported by pycdlib-genisoimage) Specifies a comma-separated list of boot images that are needed to make
+a bootable CD for SPARC systems.
+Partition 0 is used for the ISO9660 image, the first image file is mapped
+to partition 1.
+The comma-separated list may have up to 7 fields, including empty fields.
+This option is required to make a bootable CD for Sun SPARC systems.
+If
+.B \-B
+or
+.B \-sparc\-boot
+has been specified, the first sector of the resulting image will
+contain a Sun disk label. This disk label specifies slice 0 for the
+ISO9660 image and slices 1 to 7 for the boot images that
+have been specified with this option. Byte offsets 512 to 8191
+within each of the additional boot images must contain a primary boot
+that works for the appropriate SPARC architecture. The rest of each
+of the images usually contains a UFS filesystem used for the primary
+kernel boot stage.
+.IP
+The implemented boot method is the one found with SunOS 4.x and SunOS 5.x.
+However, it does not depend on SunOS internals but only on properties of
+the Open Boot prom, so it should be usable for any OS for SPARC systems.
+For more information also see the
+.B NOTES
+section below.
+.IP
+If the special filename
+.B ...
+is used, the actual and all following boot partitions are mapped to the
+previous partition. If
+.B pycdlib-genisoimage
+is called with
+.BI \-G " image " \-B " ..."
+all boot partitions are mapped to the partition that contains the ISO9660
+filesystem image and the generic boot image that is located in the first
+16 sectors of the disc is used for all architectures.
+.TP
+.BI \-G " generic_boot_image"
+(not supported by pycdlib-genisoimage) Specifies the path and filename of the generic boot image to be used when making
+a generic bootable CD. The boot image will be placed on the first 16
+sectors of the CD, before the ISO9660 primary volume descriptor.
+If this option is used together with
+.BR \-sparc\-boot ,
+the Sun disk label will overlay the first 512 bytes of the generic
+boot image.
+.TP
+.BI \-b " eltorito_boot_image"
+.TP
+.BI \-eltorito\-boot " eltorito_boot_image"
+Specifies the path and filename of the boot image to be used when making
+an El Torito bootable CD for x86 PCs. The pathname must be relative to
+the source path specified to
+.BR pycdlib-genisoimage .
+This option is required to make an El Torito bootable CD.
+The boot image must be exactly 1200 kB, 1440 kB or 2880 kB, and
+.B pycdlib-genisoimage
+will use this size when creating the output ISO9660 filesystem. The PC
+BIOS will use the image to emulate a floppy disk, so the first 512-byte
+sector should contain PC boot code. This will work, for example, if
+the boot image is a LILO-based boot floppy.
+.IP
+If the boot image is not an image of a floppy, you need to add either
+.BR \-hard\-disk\-boot " or " \-no\-emul\-boot .
+If the system should not boot off the emulated disk, use
+.BR \-no\-boot .
+.IP
+If
+.B \-sort
+has not been specified, the boot images are sorted
+with low priority (+2) to the beginning of the medium.
+If you don't like this, you need to specify a sort weight of 0 for the boot images.
+.TP
+.B \-eltorito\-alt\-boot
+Start with a new set of El Torito boot parameters. Up to 63 El Torito
+boot entries may be stored on a single CD.
+.TP
+.BI \-hard\-disk\-boot
+Specifies that the boot image used to create El Torito bootable CDs is
+a hard disk image. The image must begin with a master boot
+record that contains a single partition.
+.TP
+.BI \-eltorito\-platform " id"
+(not supported by pycdlib-genisoimage) Set the "El Torito" platform id for a boot record or a section of boot records.
+The
+.I id
+parameter may be either:
+.RS
+.TP
+.B x86
+This is the default
+.I platform id
+value and specifies entries for the PC platform.
+If no
+.B \-eltorito\-platform
+option appears before the first
+.B \-eltorito\-boot
+option, the default boot entry becomes an entry for the x86 PC platform.
+.TP
+.B PPC
+Boot entries for the Power PC platform.
+.TP
+.B Mac
+Boot entries for the Apple Mac platform.
+.TP
+.B efi
+Boot entries for EFI based PCs.
+.TP
+.B #
+A numeric value specifying any platform id.
+.LP
+If the option
+.B \-eltorito\-platform
+appears before the first
+.B \-eltorito\-boot
+option, it sets the
+.I platform id
+for the default boot entry.
+.LP
+If the option
+.B \-eltorito\-platform
+appears after an
+.B \-eltorito\-boot
+option and sets the
+.I platform id
+to a value different from the previous value,
+it starts a new set of boot entries.
+.LP
+The second boot entry and any new
+.I platform id
+creates a new section header and reduces the number of boot
+entries per CD by one.
+.RE
+.TP
+.BI \-ignore\-error
+(not supported by pycdlib-genisoimage) Ignore errors.
+.B pycdlib-genisoimage
+by default aborts on several errors, such as read errors. With this option in effect,
+.B pycdlib-genisoimage
+tries to continue.
+Use with care.
+.TP
+.BI \-no\-emul\-boot
+Specifies that the boot image used to create El Torito bootable CDs is
+a "no emulation" image. The system will load and execute this image without
+performing any disk emulation.
+.TP
+.BI \-no\-boot
+Specifies that the created El Torito CD should be marked as not bootable. The
+system will provide an emulated drive for the image, but will boot off
+a standard boot device.
+.TP
+.BI \-boot\-load\-seg " segment_address"
+Specifies the load segment address of the boot image for no-emulation
+El Torito CDs.
+.TP
+.BI \-boot\-load\-size " load_sectors"
+Specifies the number of "virtual" (512-byte) sectors to load in
+no-emulation mode. The default is to load the entire boot file. Some
+BIOSes may have problems if this is not a multiple of 4.
+.TP
+.B \-boot\-info\-table
+Specifies that a 56-byte table with information of the CD-ROM layout
+will be patched in at offset 8 in the boot file.
+.TP
+.BI \-C " last_sess_start,next_sess_start"
+.TP
+.BI \-cdrecord\-params " last_sess_start,next_sess_start"
+(not supported by pycdlib-genisoimage) This option is needed to create a CD Extra or the image of a second
+session or a higher-level session for a multisession disc.
+.B \-C
+takes two numbers separated by a comma. The first is the first sector
+in the last session of the disc that should be appended to.
+The second number is the starting sector number of the new session.
+The correct numbers may be retrieved by calling
+.B wodim \-msinfo ...
+If
+.B \-C
+is used in conjunction with
+.BR \-M ,
+.B pycdlib-genisoimage
+will create a filesystem image that is intended to be a continuation
+of the previous session.
+If
+.B \-C
+is used without
+.BR \-M ,
+.B pycdlib-genisoimage
+will create a filesystem image that is intended to be used for a second
+session on a CD Extra. This is a multisession CD that holds audio data
+in the first session and an ISO9660 filesystem in the second session.
+.TP
+.BI \-c " boot_catalog"
+.TP
+.BI \-eltorito\-catalog " boot_catalog"
+Specifies the path and filename of the boot catalog, which is required
+for an El Torito bootable CD. The pathname must be relative to the source
+path specified to
+.BR pycdlib-genisoimage .
+This file will be inserted into the output tree and not created
+in the source filesystem, so be
+sure the specified filename does not conflict with an existing file, or
+it will be excluded. Usually a name like
+.I boot.catalog
+is chosen.
+.IP
+If
+.B \-sort
+has not been specified, the boot catalog sorted
+with low priority (+1) to the beginning of the medium.
+If you don't like this, you need to specify a sort weight of 0 for the boot catalog.
+.TP
+.B \-check\-oldnames
+(not supported by pycdlib-genisoimage) Check all filenames imported from the old session for compliance with
+the ISO9660 file naming rules.
+Without this option, only names longer than 31 characters are checked,
+as these files are a serious violation of the ISO9660 standard.
+.TP
+.BI \-check\-session " file"
+(not supported by pycdlib-genisoimage) Check all old sessions for compliance with actual
+.B pycdlib-genisoimage
+ISO9660 file naming rules.
+This is a high-level option that combines
+.B \-M
+.I file
+.BR "\-C 0,0 \-check\-oldnames" .
+For the parameter
+.IR file ,
+see the description of
+.BR \-M .
+.TP
+.BI \-checksum_algorithm_iso " alg1,alg2,..."
+(not supported by pycdlib-genisoimage) Specify the checksum types desired for the output image.
+.TP
+.BI \-checksum_algorithm_template " alg1,alg2,..."
+(not supported by pycdlib-genisoimage) Specify the checksum types desired for the output jigdo template.
+.TP
+.BI \-copyright " file"
+Specifies copyright information, typically a filename on the disc.
+There is space for 37 characters.
+.TP
+.B \-d
+.TP
+.B \-omit\-period
+(not supported by pycdlib-genisoimage) Do not append a period to files that do not have one.
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.B \-D
+.TP
+.B \-disable\-deep\-relocation
+(not supported by pycdlib-genisoimage) Do not use deep directory relocation, and instead just pack them in the
+way we see them.
+.br
+If ISO9660:1999 has not been selected,
+this violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.B \-data\-change\-warn
+(not supported by pycdlib-genisoimage) If the size of a file changes while the file is being archived, treat this
+condition as a warning only that does not cause
+.B pycdlib-genisoimage
+to abort.
+.TP
+.B \-debug
+(not supported by pycdlib-genisoimage) Set debug flag.
+.TP
+.BI \-dir\-mode " mode"
+(not supported by pycdlib-genisoimage) Overrides the mode of directories used to create the image to
+.IR mode ,
+specified as 4 digits of permission bits as in
+.BR chmod (1).
+This option automatically enables Rock Ridge extensions.
+.TP
+.B \-dvd\-video
+(not supported by pycdlib-genisoimage) Generate a DVD-Video compliant UDF filesystem. This is done by sorting the
+order of the content of the appropriate files and by adding padding
+between the files if needed.
+Note that the sorting only works if the DVD-Video filenames include uppercase
+characters only.
+.IP
+Note that in order to get a DVD-Video compliant filesystem image, you
+need to prepare a DVD-Video compliant directory tree. This requires a
+directory
+.B VIDEO_TS
+(all caps) in the root directory of the resulting DVD, and usually
+another directory
+.BR AUDIO_TS .
+.B VIDEO_TS
+needs to include all needed files (filenames must be all caps) for a
+compliant DVD-Video filesystem.
+.TP
+.BI \-e " efi_boot_file"
+.TP
+.BI \-efi\-boot " efi_boot_file"
+Set EFI boot image name.
+.TP
+.B \-f
+.TP
+.B \-follow\-links
+(not supported by pycdlib-genisoimage) Follow symbolic links when generating the filesystem. When this option is not
+in use, symbolic links will be entered using Rock Ridge if enabled, otherwise
+they will be ignored.
+.TP
+.BI \-file\-mode " mode"
+(not supported by pycdlib-genisoimage) Overrides the mode of regular files used to create the image to
+.IR mode ,
+specified as 4 digits of permission bits as in
+.BR chmod (1).
+This option automatically enables Rock Ridge extensions.
+.TP
+.B \-find
+(not supported by pycdlib-genisoimage) This option acts a separator.
+If it is used, all
+.B pycdlib-genisoimage
+options must be to the left of the
+.B \-find
+option. To the right of the
+.B \-find
+option,
+.B pycdlib-genisoimage
+accepts the
+.B find
+command line syntax only.
+.sp
+The
+.B find
+expression acts as a filter between the source of file names and the
+consumer, which is archiving engine.
+If the
+.B find
+expression evaluated as TRUE, then the related file is selected for
+processing, otherwise it is omited.
+.sp
+In order to make the evaluation of the
+.B find
+expression more convenient,
+.B pycdlib-genisoimage
+implements additional
+.B find primaries
+that have side effects on the file meta data.
+.B pycdlib-genisoimage
+implements the following additional
+.B find
+primaries:
+.RS
+.TP
+.B \-help
+Lists the available
+.BR find (1)
+syntax.
+.TP
+.BI \-chgrp " gname"
+The primary always evaluates as true;
+it sets the group of the file to
+.IR gname .
+.TP
+.BI \-chmod " mode"
+The primary always evaluates as true;
+it sets the permissions of the file to
+.IR mode .
+Octal and symbolic permissions are accepted for
+.I mode
+as with
+.BR chmod (1).
+.TP
+.BI \-chown " uname"
+The primary always evaluates as true;
+it sets the owner of the file to
+.IR uname .
+.TP
+.B \-false
+The primary always evaluates as false;
+it allows to make the result of the full expression different from
+the result of a part of the expression.
+.TP
+.B \-true
+The primary always evaluates as true;
+it allows to make the result of the full expression different from
+the result of a part of the expression.
+.PP
+The command line:
+.PP
+.B pycdlib-genisoimage -o o.iso -find . ( -type d -ls -o false ) -o ! -type d
+.PP
+lists all directories and puts all non-directories to the image
+.BR o.iso .
+.PP
+The command line:
+.PP
+.B pycdlib-genisoimage -o o.iso -find . ( -type d -chown root -o true )
+.PP
+archives all directories so they appear to be owned by root in the archive,
+all non-directories are archived as they are in the file system.
+.PP
+Note that the
+.BR \-ls ,
+.B \-exec
+and the
+.B \-ok
+primary cannot be used if
+.B stdin
+or
+stdout
+has not been redirected.
+.RE
+.TP
+.BI \-gid " gid"
+(not supported by pycdlib-genisoimage) Overrides the group ID read from the source files to the value of
+.IR gid .
+Specifying this option automatically enables Rock Ridge extensions.
+.TP
+.B \-gui
+(not supported by pycdlib-genisoimage) Switch the behaviour for a GUI. This currently makes the output more verbose
+but may have other effects in the future.
+.TP
+.B \-graft\-points
+(not supported by pycdlib-genisoimage) Allow use of graft points for filenames. If this option is used, all
+filenames are checked for graft points. The filename is divided at the
+first unescaped equal sign. All occurrences of `\(rs' and `=' characters
+must be escaped with `\(rs' if
+.B \-graft\-points
+has been specified.
+.TP
+.BI \-hide " glob"
+Hide any files matching
+.IR glob ,
+a shell wildcard pattern, from being seen in the ISO9660 or Rock Ridge
+directory.
+.I glob
+may match any part of the filename or path. If
+.I glob
+matches a directory, the contents of that directory will be hidden.
+In order to match a directory name, make sure the pathname does not include
+a trailing `/' character.
+All the hidden files will still be written to the output CD image file.
+See also
+.BR \-hide\-joliet ,
+and
+.IR README.hide .
+This option may be used multiple times.
+.TP
+.BI \-hide\-list " file"
+A file containing a list of shell wildcards to be hidden. See
+.BR \-hide .
+.TP
+.BI \-hidden " glob"
+Add the hidden (existence) ISO9660 directory attribute for files and
+directories matching
+.IR glob ,
+a shell wildcard pattern. This attribute will prevent the files from
+being shown by some MS-DOS and Windows commands.
+.I glob
+may match any part of the filename or path.
+In order to match a directory name, make sure the pathname does not include
+a trailing `/' character.
+This option may be used multiple times.
+.TP
+.BI \-hidden\-list " file"
+A file containing a list of shell wildcards to get the hidden
+attribute. See
+.BR \-hidden .
+.TP
+.BI \-hide\-joliet " glob"
+Hide files and directories matching
+.IR glob ,
+a shell wildcard pattern, from being seen in the Joliet directory.
+.I glob
+may match any part of the filename or path. If
+.I glob
+matches a directory, the contents of that directory will be hidden.
+In order to match a directory name, make sure the pathname does not include
+a trailing `/' character.
+All the hidden files will still be written to the output CD image file.
+This option is usually used with
+.BR \-hide .
+See also
+.IR README.hide .
+This option may be used multiple times.
+.TP
+.BI \-hide\-joliet\-list " file"
+A file containing a list of shell wildcards to be hidden from the
+Joliet tree. See
+.BR \-hide\-joliet .
+.TP
+.B \-hide\-joliet\-trans\-tbl
+(not supported by pycdlib-genisoimage) Hide the
+.I TRANS.TBL
+files from the Joliet tree.
+These files usually don't make sense in the Joliet world as they list
+the real name and the ISO9660 name which may both be different from the
+Joliet name.
+.TP
+.B \-hide\-rr\-moved
+Rename the directory
+.I RR_MOVED
+to
+.I .rr_moved
+in the Rock Ridge tree.
+It seems to be impossible to completely hide the
+.I RR_MOVED
+directory from the Rock Ridge tree.
+This option only makes the visible tree less confusing for
+people who don't know what this directory is for.
+If you need to have no
+.I RR_MOVED
+directory at all, you should use
+.BR \-D .
+Note that if
+.B \-D
+has been specified, the resulting filesystem is not ISO9660
+level-1 compliant and will not be readable on MS-DOS.
+See also the
+.B NOTES
+section.
+.TP
+.BI \-hide\-udf " glob"
+Hide
+.I glob
+from being seen on the UDF directory.
+.I glob
+is a shell wild-card-style pattern that must match any part of the filename
+or path.
+Multiple globs may be hidden.
+If
+.I glob
+matches a directory, then the contents of that directory will be hidden.
+In order to match a directory name, make sure the pathname does not include
+a trailing '/' character.
+All the hidden files will still be written to the output CD image file.
+Should be used with the
+.B \-hide
+option.
+.TP
+.BI \-hide\-udf\-list " file"
+A file containing a list of
+.I globs
+to be hidden as above.
+.TP
+.BI \-input\-charset " charset"
+(not supported by pycdlib-genisoimage) Input charset that defines the characters used in local filenames.
+To get a list of valid charset names, call
+.BR "pycdlib-genisoimage \-input\-charset help" .
+To get a 1:1 mapping, you may use
+.B default
+as charset name. The default initial values are
+.I cp437
+on DOS-based systems and
+.I iso8859-1
+on all other systems.
+.TP
+.BI \-output\-charset " charset"
+(not supported by pycdlib-genisoimage) Output charset that defines the characters that will be used in Rock Ridge
+filenames. Defaults to the input charset. See
+.B CHARACTER SETS
+section below for more details.
+.TP
+.BI \-iso\-level " level"
+Set the ISO9660 conformance level. Valid numbers are 1 to 4.
+.IP
+With level 1, files may only consist of one section and filenames are
+restricted to 8.3 characters.
+.IP
+With level 2, files may only consist of one section.
+.IP
+With level 3, no restrictions (other than ISO-9660:1988) do apply.
+.IP
+With all ISO9660 levels from 1 to 3, all filenames are restricted to
+uppercase letters, numbers and underscores (_). Filenames are
+limited to 31 characters, directory nesting is limited to 8
+levels, and pathnames are limited to 255 characters.
+.IP
+Level 4 officially does not exist but
+.B pycdlib-genisoimage
+maps it to ISO-9660:1999, which is ISO9660 version 2.
+.IP
+With level 4, an enhanced volume descriptor with version number
+and file structure version number set to 2 is emitted.
+Directory nesting is not limited to 8 levels,
+there is no need for a file to contain a dot and the dot has no
+special meaning, filenames do not have version numbers,
+.\" (f XXX ??? The character used for filling byte positions which are
+.\" specified to be characters is subject to agreement between the
+.\" originator and the recipient of the volume),
+and filenames can be up to 207 characters long, or 197 characters if
+Rock Ridge is used.
+.IP
+When creating Version 2 images,
+.B pycdlib-genisoimage
+emits an enhanced volume descriptor, similar but not identical to a
+primary volume descriptor. Be careful not to use broken software
+to make ISO9660 images bootable by assuming a second PVD copy and patching
+this putative PVD copy into an El Torito VD.
+.TP
+.B \-J
+Generate Joliet directory records in addition to regular ISO9660
+filenames. This is primarily useful when the discs are to be used on
+Windows machines. Joliet filenames are specified in Unicode and each
+path component can be up to 64 Unicode characters long.
+Note that Joliet is not a standard \(em only Microsoft Windows and Linux
+systems can read Joliet extensions. For greater portability, consider
+using both Joliet and Rock Ridge extensions.
+.TP
+.B \-joliet\-long
+(not supported by pycdlib-genisoimage) Allow Joliet filenames to be up to 103 Unicode characters, instead of
+64. This breaks the Joliet specification, but appears to work. Use
+with caution.
+.\" The number 103 is derived from: the maximum Directory Record Length
+.\" (254), minus the length of Directory Record (33), minus CD-ROM XA
+.\" System Use Extension Information (14), divided by the UTF-16
+.\" character size (2).
+.TP
+.BI \-jcharset " charset"
+(not supported by pycdlib-genisoimage) A combination of
+.B \-J \-input\-charset
+.IR charset .
+.TP
+.B \-l
+.TP
+.B \-full\-iso9660\-filenames
+(not supported by pycdlib-genisoimage) Allow full 31-character filenames. Normally the ISO9660 filename will be in an
+8.3 format which is compatible with MS-DOS, even though the ISO9660 standard
+allows filenames of up to 31 characters. If you use this option, the disc may
+be difficult to use on a MS-DOS system, but will work on most other systems.
+Use with caution.
+.TP
+.B \-L
+Outdated option; use
+.B \-allow\-leading\-dots
+instead.
+.TP
+.BI \-jigdo\-jigdo " jigdo_file"
+(not supported by pycdlib-genisoimage) Produce a
+.B jigdo
+.I .jigdo
+metadata file as well as the filesystem image.
+.TP
+.BI \-jigdo\-template " template_file"
+(not supported by pycdlib-genisoimage) Produce a
+.B jigdo
+.I .template
+file as well as the filesystem image.
+.TP
+.BI \-jigdo\-min\-file\-size " size"
+(not supported by pycdlib-genisoimage) Specify the minimum size for a file to be listed in the
+.I .jigdo
+file. Default (and minimum allowed) is 1KB.
+.TP
+.BI \-jigdo\-force\-md5 " path"
+(not supported by pycdlib-genisoimage) Specify a file pattern where files
+.I must
+be contained in the externally-supplied MD5 list as supplied by
+.BR \-md5\-list .
+.TP
+.BI \-jigdo\-exclude " path"
+(not supported by pycdlib-genisoimage) Specify a file pattern where files will not be listed in the
+.I .jigdo
+file.
+.TP
+.BI \-jigdo\-map " path"
+(not supported by pycdlib-genisoimage) Specify a pattern mapping for the jigdo file
+(e.g.
+.IR Debian=/mirror/debian ).
+.TP
+.BI \-md5\-list " md5_file"
+(not supported by pycdlib-genisoimage) Specify a file containing the MD5sums, sizes and pathnames of the
+files to be included in the
+.I .jigdo
+file.
+.TP
+.BI \-jigdo\-template\-compress " algorithm"
+(not supported by pycdlib-genisoimage) Specify a compression algorithm to use for template date. gzip and
+bzip2 are currently supported, and gzip is the default.
+.TP
+.BI \-log\-file " log_file"
+Redirect all error, warning and informational messages to
+.I log_file
+instead of the standard error.
+.TP
+.B \-long\-rr\-time
+(not supported by pycdlib-genisoimage) Use the long ISO-9660 time format for the file time stamps used in Rock Ridge.
+This time format allows to represent year 0 .. year 9999 with a granularity of 10ms.
+.sp
+The short ISO-9660 time format only allows to represent year 1900 .. year 2155
+with a granularity of 1s.
+.TP
+.BI \-m " glob"
+Exclude files matching
+.IR glob ,
+a shell wildcard pattern, from being written to CD-ROM.
+.I glob
+may match either the filename component or the full pathname.
+This option may be used multiple times. For example:
+.sp
+ pycdlib-genisoimage \-o rom \-m \(aq*.o\(aq \-m core \-m foobar
+.sp
+would exclude all files ending in `.o', or called
+.IR core " or " foobar
+from the image. Note that if you had a directory called
+.IR foobar ,
+it too (and of course all its descendants) would be excluded.
+.TP
+.BI \-exclude\-list " file"
+A file containing a list of shell wildcards to be excluded. See
+.BR \-m .
+.TP
+.B \-max\-iso9660\-filenames
+(not supported by pycdlib-genisoimage) Allow ISO9660 filenames to be up to 37 characters long.
+This option enables
+.B \-N
+as the extra name space is taken from the space reserved for
+file version numbers.
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Although a conforming application needs to provide a buffer space of at
+least 37 characters, discs created with this option may cause a buffer
+overflow in the reading operating system. Use with extreme care.
+.TP
+.BI \-M " path"
+.TP
+.BI \-M " device"
+.TP
+.BI \-dev " device"
+(not supported by pycdlib-genisoimage) Specifies path to existing ISO9660 image to be merged. The alternate form
+takes a SCSI device specifier that uses the same syntax as the
+.B dev=
+parameter of
+.BR wodim .
+The output of
+.B pycdlib-genisoimage
+will be a new session which should get written to the end of the
+image specified in
+.BR \-M .
+Typically this requires multisession capability for the CD recorder
+used to write the image. This option may only be used in conjunction
+with
+.BR \-C .
+.TP
+.BI \-modification\-date " date-spec"
+(not supported by pycdlib-genisoimage) Set the
+.B modification date
+in the primary volume descriptor (PVD) to a value different from the current
+time.
+This allows e.g. to set up an intentional UUID for
+.BR grub .
+.sp
+.ne 3
+The format of
+.I date-spec
+is:
+.sp
+.nf
+ \fIyyyy\fR[\fImm\fR[\fIdd\fR[\fIhh\fR[\fImm\fR[\fIss\fR]\|]\|]\|]\|][.\fIhh\fR][+-\fIghgm\fR]
+.fi
+.sp
+The fields are
+.BR year ,
+.BR month ,
+.BR "day of month" ,
+.BR hour ,
+.BR minute ,
+.BR second ,
+.BR "hundreds of a second" ,
+.BR "GMT offset in hours and minutes" .
+The time is interpreted as local time.
+.sp
+Year and the GMT offset are four digit fields, all other fields take two digits.
+The GMT offset may be between -12 and +13 hours in 15 minute steps. Locations
+east to Greenwich have positive values. The value is the sum of the time zone offset
+and the effects from daylight saving time.
+Omited values are replaced by the minimal possible values.
+If the GMT offset is omited, it is computed from the local time value that has been
+supplied.
+.sp
+Between year and month as well as between month and day of month, a separator chosen
+from '/' and '-' may appear. In this case, the year may be a two digit number with
+values 69..99 representing 1969..1999 and values 00..68 representing 2000..2068.
+Between date and time spec, an optional space is permitted. Between hours and minutes
+as well as between minutes and seconds, an optional ':' separator is permitted.
+This allows
+.B pycdlib-genisoimage
+to parse the popular POSIX date format created by:
+.sp
+.nf
+ \fBdate "+%Y-%m-%d %H:%M:%S %z"\fR
+.fi
+.sp
+Note that the possible range for
+.I date-spec
+for 32 bit programs is limited to values up to 2038 Jan 19 04:14:07 GMT.
+.TP
+.B \-N
+.TP
+.B \-omit\-version\-number
+(not supported by pycdlib-genisoimage) Omit version numbers from ISO9660 filenames.
+.br
+This violates the ISO9660 standard, but no one really uses the
+version numbers anyway. Use with caution.
+.TP
+.BI \-new\-dir\-mode " mode"
+(not supported by pycdlib-genisoimage) Specify the mode, a 4-digit number as used in
+.BR chmod (1),
+to use when creating new directories in the filesystem image. The
+default is 0555.
+.TP
+.B \-nobak
+.TP
+.B \-no\-bak
+Exclude backup files files on the ISO9660 filesystem; that is,
+filenames that contain the characters `~' or `#' or end in
+.IR .bak .
+These are typically backup files for Unix text editors.
+.TP
+.B \-no\-limit\-pathtables
+(not supported by pycdlib-genisoimage) A ISO-9660 filesystem contains path tables that contain a list of directories.
+This list may contain many directories but only 65535 of them may be parent
+directories.
+When
+.B \-no\-limit\-pathtables
+is in use, further parent directories will be folded to the root directory
+and the resulting filesystem will no longer be usable on
+.BR DOS .
+.TP
+.B \-no\-long\-rr\-time
+(not supported by pycdlib-genisoimage) Use the short ISO-9660 time format for the file time stamps used in Rock Ridge.
+This time format allows to represent year 1990 .. year 2155 with a granularity of one second.
+.TP
+.B \-force\-rr
+(not supported by pycdlib-genisoimage) Do not use the automatic Rock Ridge attributes recognition for previous sessions.
+This can work around problems with images created by, e.g., NERO Burning ROM.
+.TP
+.B \-no\-rr
+(not supported by pycdlib-genisoimage) Do not use the Rock Ridge attributes from previous sessions.
+This may help to avoid problems when
+.B pycdlib-genisoimage
+finds illegal Rock Ridge signatures on an old session.
+.TP
+.B \-no\-split\-symlink\-components
+(not supported by pycdlib-genisoimage) Don't split the symlink components, but begin a new Continuation Area (CE)
+instead. This may waste some space, but the SunOS 4.1.4 cdrom driver
+has a bug in reading split symlink components.
+.IP
+It is questionable whether this option is useful nowadays.
+.TP
+.B \-no\-split\-symlink\-fields
+(not supported by pycdlib-genisoimage) Don't split the symlink fields, but begin a new Continuation Area (CE)
+instead. This may waste some space, but the SunOS 4.1.4 and
+Solaris 2.5.1 cdrom driver have a bug in reading split symlink fields
+(a `/' can be dropped).
+.IP
+It is questionable whether this option is useful nowadays.
+.TP
+.BI \-o " filename"
+Specify the output file for the the ISO9660 filesystem image.
+This can be a disk file, a tape drive, or it can correspond directly
+to the device name of the optical disc writer. If not specified, stdout is
+used. Note that the output can also be a block device for a regular
+disk partition, in which case the ISO9660 filesystem can be mounted
+normally to verify that it was generated correctly.
+.TP
+.B \-pad
+(not supported by pycdlib-genisoimage) Pad the end of the whole image by 150 sectors (300 kB). This option is
+enabled by default. If used in combination with
+.BR \-B ,
+padding is inserted between the ISO9660 partition and the boot
+partitions, such that the first boot partition starts
+on a sector number that is a multiple of 16.
+.IP
+The padding is needed as many operating systems (e.g. Linux)
+implement read-ahead bugs in their filesystem I/O. These bugs result in read
+errors on files that are located near the end of a track, particularly
+if the disc is written in Track At Once mode, or where a CD audio track
+follows the data track.
+.\" XXX: Someone should check to see if the Linux readahead bug is
+.\" XXX: still present, and update this comment accordingly.
+.TP
+.B \-no\-pad
+(not supported by pycdlib-genisoimage) Do not pad the end by 150 sectors (300 kB) and do not make the the boot partitions
+start on a multiple of 16 sectors.
+.TP
+.BI \-path\-list " file"
+A file containing a list of
+.I pathspec
+directories and filenames to be added to the ISO9660 filesystem. This list
+of pathspecs are processed after any that appear on the command line. If the
+argument is
+.IR \- ,
+the list is read from the standard input.
+.TP
+.B \-P
+Outdated option; use
+.B \-publisher
+instead.
+.TP
+.BI \-publisher " publisher_id"
+Specifies a text string that will be written into the volume header.
+This should describe the publisher of the CD-ROM, usually with a
+mailing address and phone number. There is space for 128 characters.
+.TP
+.BI \-p " preparer_id"
+.TP
+.BI \-preparer " preparer_id"
+Specifies a text string that will be written into the volume header.
+This should describe the preparer of the CD-ROM, usually with a mailing
+address and phone number. There is space on the disc for 128
+characters of information.
+The related Joliet entry is limited to 64 characters.
+.TP
+.B \-posix\-H
+(not supported by pycdlib-genisoimage) Follow all symbolic links encountered on command line when generating the filesystem.
+.TP
+.B \-posix\-L
+(not supported by pycdlib-genisoimage) Follow all symbolic links when generating the filesystem.
+When this option is not in use, symbolic links will be entered using
+Rock Ridge if enabled, otherwise the file will be ignored.
+.TP
+.B \-posix\-P
+(not supported by pycdlib-genisoimage) Do not follow symbolic links when generating the filesystem (this is the default).
+If
+.B \-posix\-P
+is specified after
+.B \-posix\-H
+or
+.BR \-posix\-L ,
+the effect of these options will be reset.
+.TP
+.B \-print\-size
+Print estimated filesystem size in multiples of the sector size (2048 bytes)
+and exit. This option is needed for
+Disk At Once mode and with some CD-R drives when piping directly into
+.BR wodim ,
+cases where
+.B wodim
+needs to know the size of the filesystem image in advance.
+Old versions of
+.B mkisofs
+wrote this information (among other information) to
+.IR stderr .
+As this turns out to be hard to parse, the number without any other information
+is now printed on
+.I stdout
+too.
+If you like to write a simple shell script, redirect
+.I stderr
+and catch the number from
+.IR stdout .
+This may be done with:
+.sp
+ cdblocks=\` pycdlib-genisoimage \-print\-size \-quiet .\|.\|. \`
+.br
+ pycdlib-genisoimage .\|.\|. | wodim .\|.\|. tsize=${cdblocks}s \-
+.TP
+.B \-quiet
+This makes
+.B pycdlib-genisoimage
+even less verbose. No progress output will be provided.
+.TP
+.B \-R
+.TP
+.B \-rock
+Generate SUSP and RR records using the Rock Ridge protocol to further describe
+the files on the ISO9660 filesystem.
+.TP
+.B \-r
+.TP
+.B \-rational\-rock
+This is like the \-R option, but file ownership and modes are set to
+more useful values. The uid and gid are set to zero, because they are
+usually only useful on the author's system, and not useful to the
+client. All the file read bits are set true, so that files and
+directories are globally readable on the client. If any execute bit is
+set for a file, set all of the execute bits, so that executables are
+globally executable on the client. If any search bit is set for a
+directory, set all of the search bits, so that directories are globally
+searchable on the client. All write bits are cleared, because the
+filesystem will be mounted read-only in any case. If any of the special
+mode bits are set, clear them, because file locks are not useful on a
+read-only filesystem, and set-id bits are not desirable for uid 0 or
+gid 0.
+When used on Win32, the execute bit is set on
+.I all
+files. This is a result of the lack of file permissions on Win32 and the
+Cygwin POSIX emulation layer. See also
+.BR \-uid ", " \-gid ,
+.BR \-dir\-mode ", " \-file\-mode
+and
+.BR \-new\-dir\-mode .
+.TP
+.B \-relaxed\-filenames
+(not supported by pycdlib-genisoimage) Allows ISO9660 filenames to include all 7-bit ASCII characters except
+lowercase letters.
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.BI \-root " dir"
+(not supported by pycdlib-genisoimage) Moves all files and directories into
+.I dir
+in the image. This is essentially the
+same as using
+.B \-graft\-points
+and adding
+.I dir
+in front of every pathspec, but is easier to use.
+.I dir
+may actually be several levels deep. It is
+created with the same permissions as other graft points.
+.TP
+.B \-rrip110
+Create ISO-9660 file system images that follow the old Rrip Version-1.10 standard
+from 1993. This option may be needed if you know of systems that do not implement
+the Rrip protocol correctly and like the file system to be read by such a system.
+Currently no such system is known.
+.sp
+If a file system has been created with
+.BR \-rrip110 ,
+the Rock Ridge attributes do not include inode number information.
+.TP
+.B \-rrip112
+Create ISO-9660 file system images that follow the new Rrip Version-1.12 standard
+from 1994.
+.TP
+.BI \-old-root " dir"
+(not supported by pycdlib-genisoimage) This option is necessary when writing a multisession
+image and the previous (or even older) session was written with
+.B -root
+.IR dir .
+Using a directory name not found in the previous session
+causes
+.B pycdlib-genisoimage
+to abort with an error.
+Without this option,
+.B pycdlib-genisoimage
+would not be able to find unmodified files and would
+be forced to write their data into the image once more.
+.B \-root
+and
+.B \-old-root
+are meant to be used together to do incremental backups.
+The initial session would e.g. use:
+.B pycdlib-genisoimage \-root backup_1
+.IR dirs .
+The next incremental backup with
+.B pycdlib-genisoimage \-root backup_2 \-old-root backup_1
+.I dirs
+would take another snapshot of these directories. The first
+snapshot would be found in
+.BR backup_1 ,
+the second one in
+.BR backup_2 ,
+but only modified or new files need to be written
+into the second session.
+Without these options, new files would be added and old ones would be
+preserved. But old ones would be overwritten if the file was
+modified. Recovering the files by copying the whole directory back
+from CD would also restore files that were deleted
+intentionally. Accessing several older versions of a file requires
+support by the operating system to choose which sessions are to be
+mounted.
+.TP
+.BI \-s " sector type"
+.TP
+.BI \-sectype " sector type"
+(not supported by pycdlib-genisoimage) Set output sector type to e.g. data/xa1/raw.
+.TP
+.BI \-sort " sort_file"
+(not supported by pycdlib-genisoimage) Sort file locations on the media. Sorting is controlled by a file that
+contains pairs of filenames and sorting offset weighting.
+If the weighting is higher, the file will be located closer to the
+beginning of the media, if the weighting is lower, the file will be located
+closer to the end of the media. There must be only one space or tabs
+character between the filename and the
+weight and the weight must be the last characters on a line. The filename
+is taken to include all the characters up to, but not including the last
+space or tab character on a line. This is to allow for space characters to
+be in, or at the end of a filename.
+This option does
+.B not
+sort the order of the filenames that appear
+in the ISO9660 directory. It sorts the order in which the file data is
+written to the CD image, which is useful in order to optimize the
+data layout on a CD. See
+.B README.sort
+for more details.
+.TP
+.BI \-sparc\-boot " img_sun4,img_sun4c,img_sun4m,img_sun4d,img_sun4e"
+(not supported by pycdlib-genisoimage) See
+.B \-B
+above.
+.TP
+.BI \-sparc\-label " label"
+(not supported by pycdlib-genisoimage) Set the Sun disk label name for the Sun disk label that is created with
+.BR \-sparc-boot .
+.TP
+.B \-split\-output
+(not supported by pycdlib-genisoimage) Split the output image into several files of approximately 1 GB each.
+This helps to create DVD-sized ISO9660 images on operating systems without
+large file support.
+.B wodim
+will concatenate more than one file into a single track if writing to a DVD.
+To make
+.B \-split\-output
+work,
+.BI \-o " filename"
+must be specified. The resulting output images will be named:
+.IR filename_00 ", " filename_01 ", " filename_02 ....
+.TP
+.BI \-stream\-media\-size " #"
+(not supported by pycdlib-genisoimage) Select streaming operation and set the media size to # sectors.
+This allows you to pipe the output of the
+.BR tar (1)
+program into
+.B pycdlib-genisoimage
+and to create an ISO9660 filesystem without the need of an intermediate
+tar archive file.
+If this option has been specified,
+.B pycdlib-genisoimage
+reads from
+.I stdin
+and creates a file with the name
+.IR STREAM.IMG .
+The maximum size of the file (with padding) is 200 sectors less than the
+specified media size. If
+.B \-no\-pad
+has been specified, the file size is 50 sectors less than the specified media size.
+If the file is smaller,
+.B pycdlib-genisoimage
+will write padding. This may take awhile.
+.IP
+The option
+.B \-stream\-media\-size
+creates simple ISO9660 filesystems only and may not used together with multisession
+or hybrid filesystem options.
+.TP
+.BI \-stream\-file\-name " name"
+Reserved for future use.
+.TP
+.BI \-sunx86\-boot " UFS_img,,,AUX1_img"
+(not supported by pycdlib-genisoimage) Specifies a comma-separated list of filesystem images that are needed to make
+a bootable CD for Solaris x86 systems.
+.IP
+Note that partition 1 is used for the ISO9660 image and that partition 2 is
+the whole disk, so partition 1 and 2 may not be used by external partition data.
+The first image file is mapped to partition 0.
+There may be empty fields in the comma-separated list,
+and list entries for partition 1 and 2 must be empty.
+The maximum number of supported partitions is 8 (although the Solaris x86
+partition table could support up to 16 partitions), so it is impossible
+to specify more than 6 partition images.
+This option is required to make a bootable CD for Solaris x86 systems.
+.IP
+If
+.B \-sunx86\-boot
+has been specified, the first sector of the resulting image will
+contain a PC fdisk label with a Solaris type 0x82 fdisk partition that
+starts at offset 512 and spans the whole CD.
+In addition, for the Solaris type 0x82 fdisk partition, there is a
+SVr4 disk label at offset 1024 in the first sector of the CD.
+This disk label specifies slice 0 for the first (usually UFS type)
+filesystem image that is used to boot the PC and slice 1 for
+the ISO9660 image.
+Slice 2 spans the whole CD slice 3 .\|.\|. slice 7 may be used for additional
+filesystem images that have been specified with this option.
+.IP
+A Solaris x86 boot CD uses a 1024 byte sized primary boot that uses the
+.B El-Torito no-emulation
+boot mode and a secondary generic boot that is in CD sectors 1\|.\|.15.
+For this reason, both
+.BI "-b " bootimage " \-no\-emul\-boot"
+and
+.BI \-G " genboot"
+must be specified.
+.TP
+.BI \-sunx86\-label " label"
+(not supported by pycdlib-genisoimage) Set the SVr4 disk label name for the SVr4 disk label that is created with
+.BR \-sunx86-boot .
+.TP
+.BI \-sysid " ID"
+Specifies the system ID. There is space for 32 characters.
+.TP
+.B \-T
+.TP
+.B \-translation\-table
+(not supported by pycdlib-genisoimage) Generate a file
+.I TRANS.TBL
+in each directory on the CD-ROM, which can be used
+on non-Rock\ Ridge-capable systems to help establish the correct filenames.
+There is also information present in the file that indicates the major and
+minor numbers for block and character devices, and each symlink has the name of
+the link file given.
+.TP
+.BI \-table\-name " table_name"
+(not supported by pycdlib-genisoimage) Alternative translation table filename (see above). Implies
+.BR \-T .
+If you are creating a multisession image you must use the same name
+as in the previous session.
+.TP
+.BI \-ucs\-level " level"
+Set Unicode conformance level in the Joliet SVD. The default level is 3.
+It may be set to 1..3 using this option.
+.TP
+.B \-UDF
+Include a
+.B UDF
+hybrid in the generated filesystem image.
+As
+.B pycdlib-genisoimage
+always creates a ISO-9660 filesystem,
+it is not possible to create UDF only images.
+Note that
+.B UDF
+wastes the space from sector ~20 to sector 256 at the beginning of the disk
+in addition to the space needed for real
+.B UDF
+data structures.
+.TP
+.B \-udf
+Rationalized UDF with user and group set to 0 and with simplified permissions.
+See
+.B \-r
+option for more information.
+.TP
+.B \-udf\-symlinks
+Support symlinks in
+.B UDF
+filesystems. This is the default.
+.TP
+.B \-no\-udf\-symlinks
+Do not support symlinks in
+.B UDF
+filesystems.
+.TP
+.BI \-uid " uid"
+(not supported by pycdlib-genisoimage) Overrides the uid read from the source files to the value of
+.IR uid .
+Specifying this option automatically enables Rock Ridge extensions.
+.TP
+.B \-use\-fileversion
+(not supported by pycdlib-genisoimage) The option
+.B \-use\-fileversion
+allows
+.B pycdlib-genisoimage
+to use file version numbers from the filesystem.
+If the option is not specified,
+.B pycdlib-genisoimage
+creates a version number of 1 for all files.
+File versions are strings in the range
+.I ;1
+to
+.I ;32767
+This option is the default on VMS.
+.TP
+.B \-U
+.TP
+.B \-untranslated\-filenames
+(not supported by pycdlib-genisoimage) Allows "untranslated" filenames, completely violating the ISO9660 standards
+described above. Enables the following flags:
+.B \-d \-l \-N \-allow\-leading\-dots \-relaxed\-filenames
+.BR "\-allow\-lowercase \-allow\-multidot \-no\-iso\-translate" .
+Allows more than one `.' character in the filename, as well as
+mixed-case filenames. This is useful on HP-UX, where the built-in
+.I cdfs
+filesystem does not recognize any extensions. Use with extreme caution.
+.TP
+.B \-no\-iso\-translate
+(not supported by pycdlib-genisoimage) Do not translate the characters `#' and `~' which are invalid for ISO9660 filenames.
+Although invalid, these characters are often used by Microsoft systems.
+.br
+This violates the ISO9660 standard, but it happens to work on many systems.
+Use with caution.
+.TP
+.BI \-V " volid"
+Specifies the volume ID (volume name or label) to be written into the
+master block. There is space for 32 characters.
+The volume ID is used as the mount point by the Solaris volume
+manager and as a label assigned to a disc on various other platforms
+such as Windows and Apple Mac OS.
+.TP
+.BI \-volset " ID"
+Specifies the volume set ID. There is space for 128 characters.
+.TP
+.BI \-volset\-size " #"
+Sets the volume set size to #.
+The volume set size is the number of CDs that are in a CD volume set.
+A volume set is a collection of one or more volumes, on which a set of
+files is recorded.
+.IP
+Volume Sets are not intended to be used to create a set numbered CDs
+that are part of e.g. a Operation System installation set of CDs.
+Volume Sets are rather used to record a big directory tree that would not
+fit on a single volume.
+Each volume of a Volume Set contains a description of all the directories
+and files that are recorded on the volumes where the sequence numbers
+are less than, or equal to, the assigned Volume Set Size of the current
+volume.
+.IP
+.B pycdlib-genisoimage
+currently does not support a
+.B \-volset\-size
+that is larger than 1.
+.IP
+The option
+.B \-volset\-size
+must be specified before
+.B \-volset\-seqno
+on each command line.
+.TP
+.BI \-volset\-seqno " #"
+Sets the volume set sequence number to #.
+The volume set sequence number is the index number of the current
+CD in a CD set.
+The option
+.B \-volset\-size
+must be specified before
+.B \-volset\-seqno
+on each command line.
+.TP
+.B \-v
+.TP
+.B \-verbose
+Verbose execution. If given twice on the command line, extra debug information
+will be printed.
+.TP
+.BI \-x " glob"
+Identical to
+.B \-m
+.IR glob .
+.TP
+.B \-XA
+Generate XA directory attruibutes.
+.TP
+.B \-xa
+Generate rationalized XA directory attruibutes.
+.TP
+.B \-z
+.TP
+.B \-transparent\-compression
+(not supported by pycdlib-genisoimage) Generate special
+.I RRIP
+records for transparently compressed files.
+This is only of use and interest for hosts that support transparent
+decompression, such as Linux 2.4.14 or later. You must specify
+.BR \-R " or " \-r
+to enable Rock Ridge, and generate compressed files using the
+.B mkzftree
+utility before running
+.BR pycdlib-genisoimage .
+Note that transparent compression is a nonstandard Rock Ridge extension.
+The resulting disks are only transparently readable if used on Linux.
+On other operating systems you will need to call
+.B mkzftree
+by hand to decompress the files.
+.TP
+.B \-scan\-for\-duplicates
+Keep a running list of file hashes, attempting to link as many files together
+as possible. This results in the smallest possible ISO image, but may be
+very slow, particular with large files.
+.\" ----------------------------------------
+.SH "HFS OPTIONS"
+.TP
+.B \-hfs
+(not supported by pycdlib-genisoimage) Create an ISO9660/HFS hybrid CD. This option should be used in conjunction
+with the
+.BR \-map ,
+.B \-magic
+and/or the various
+.I double dash
+options given below.
+.TP
+.B \-no\-hfs
+Do not create an ISO-9660/HFS hybrid CD even though other options may imply to do so.
+.TP
+.B \-apple
+(not supported by pycdlib-genisoimage) Create an ISO9660 CD with Apple's extensions. Similar to
+.BR \-hfs ,
+except that the Apple Extensions to ISO9660 are added instead of
+creating an HFS hybrid volume.
+Former
+.B pycdlib-genisoimage
+versions did include Rock Ridge attributes by default if
+.B \-apple
+was specified. This versions of
+.B pycdlib-genisoimage
+does not do this anymore. If you like to have Rock Ridge attributes,
+you need to specify this separately.
+.TP
+.BI \-map " mapping_file"
+(not supported by pycdlib-genisoimage) Use the
+.I mapping_file
+to set the CREATOR and TYPE information for a file based on the
+filename's extension. A filename is
+mapped only if it is not one of the know Apple/Unix file formats.
+.TP
+.BI \-magic " magic_file"
+(not supported by pycdlib-genisoimage) The CREATOR and TYPE information is set by using a file's
+.I magic number
+(usually the first few bytes of a file). The
+.I magic_file
+is only used if a file is not one of the known Apple/Unix file formats, or
+the filename extension has not been mapped using
+.BR \-map .
+.TP
+.BI \-hfs\-creator " creator"
+(not supported by pycdlib-genisoimage) Set the default CREATOR for all files. Must be exactly 4 characters.
+.TP
+.BI \-hfs\-type " type"
+(not supported by pycdlib-genisoimage) Set the default TYPE for all files. Must be exactly 4 characters.
+.TP
+.B \-probe
+(not supported by pycdlib-genisoimage) Search the contents of files for all the known Apple/Unix file formats.
+However, the only way to check for
+.I MacBinary
+and
+.I AppleSingle
+files is to open and read them, so this option may
+increase processing time. It is better to use one or more
+.I double dash
+options given below if the Apple/Unix formats in use are known.
+.TP
+.B \-no\-desktop
+(not supported by pycdlib-genisoimage) Do not create (empty) Desktop files. New HFS Desktop files will be created
+when the CD is used on a Macintosh (and stored in the System Folder).
+By default, empty Desktop files are added to the HFS volume.
+.TP
+.B \-mac\-name
+(not supported by pycdlib-genisoimage) Use the HFS filename as the starting point for the ISO9660, Joliet and
+Rock Ridge filenames.
+.TP
+.BI \-boot\-hfs\-file " driver_file"
+(not supported by pycdlib-genisoimage) Installs the
+.I driver_file
+that
+.I may
+make the CD bootable on a Macintosh.
+.TP
+.B \-part
+(not supported by pycdlib-genisoimage) Generate an HFS partition table. By default, no partition table is generated,
+but some older Macintosh CD-ROM drivers need an HFS partition table on the
+CD-ROM to be able to recognize a hybrid CD-ROM.
+.TP
+.BI \-auto " AutoStart_file"
+(not supported by pycdlib-genisoimage) Make the HFS CD use the QuickTime 2.0 Autostart feature to launch an
+application or document. The given filename must be the name of a document or
+application located at the top level of the CD. The filename must be less
+than 12 characters. (Alpha).
+.TP
+.BI \-cluster\-size " size"
+(not supported by pycdlib-genisoimage) Set the size in bytes of the cluster or allocation units of PC Exchange
+files. Implies
+.BR \-\-exchange .
+.TP
+.BI \-hide\-hfs " glob"
+(not supported by pycdlib-genisoimage) Hide
+.IR glob ,
+a shell wildcard pattern, from the HFS volume. The file or directory
+will still exist in the ISO9660 and/or Joliet directory.
+.I glob
+may match any part of the filename. Multiple globs may be excluded.
+Example:
+.sp
+ pycdlib-genisoimage \-o rom \-hfs \-hide\-hfs \(aq*.o\(aq \-hide\-hfs foobar
+.sp
+would exclude all files ending in `.o' or called
+.I foobar
+from the HFS volume. Note that if you had a directory called
+.IR foobar ,
+it too (and of course all its descendants) would be excluded. The
+.I glob
+can also be a path name relative to the source directories given on the
+command line. Example:
+.sp
+ pycdlib-genisoimage \-o rom \-hfs \-hide\-hfs src/html src
+.sp
+would exclude just the file or directory called
+.I html
+from the
+.I src
+directory. Any other file or directory called
+.I html
+in the tree will not be excluded. Should be used with
+.B \-hide
+and/or
+.BR \-hide\-joliet .
+In order to match a directory name, make sure the pattern does not
+include a trailing `/' character. See
+.I README.hide
+for more details.
+.TP
+.BI \-hide\-hfs\-list " file"
+(not supported by pycdlib-genisoimage) Specify a file containing a list of wildcard patterns to be hidden as in
+.BR \-hide\-hfs .
+.TP
+.BI \-hfs\-volid " hfs_volid"
+(not supported by pycdlib-genisoimage) Volume name for the HFS partition. This is the name that is
+assigned to the disc on a Macintosh and replaces the
+.I volid
+used with
+.BR \-V .
+.TP
+.B \-icon\-position
+(not supported by pycdlib-genisoimage) Use the icon position information, if it exists, from the Apple/Unix file.
+The icons will appear in the same position as they would on a Macintosh
+desktop. Folder location and size on screen, its scroll positions, folder
+View (view as Icons, Small Icons, etc.) are also preserved.
+.\" This option may become set by default in the future.
+(Alpha).
+.TP
+.BI \-root\-info " file"
+(not supported by pycdlib-genisoimage) Set the location, size on screen, scroll positions, folder View etc. for the
+root folder of an HFS volume. See
+.I README.rootinfo
+for more information. (Alpha)
+.TP
+.BI \-prep\-boot " file"
+(not supported by pycdlib-genisoimage) PReP boot image file. Up to 4 are allowed. See
+.I README.prep_boot
+for more information. (Alpha)
+.TP
+.BI \-chrp\-boot
+(not supported by pycdlib-genisoimage) Add CHRP boot header.
+.TP
+.BI \-input\-hfs\-charset " charset"
+(not supported by pycdlib-genisoimage) Input charset that defines the characters used in HFS filenames when
+used with
+.BR \-mac\-name .
+The default charset is
+.I cp10000
+(Mac Roman).
+.TP
+.BI \-output\-hfs\-charset " charset"
+(not supported by pycdlib-genisoimage) Output charset that defines the characters that will be used in the HFS
+filenames. Defaults to the input charset.
+.TP
+.B \-hfs\-unlock
+(not supported by pycdlib-genisoimage) By default,
+.B pycdlib-genisoimage
+will create an HFS volume that is locked.
+This option leaves the volume unlocked so that other applications (e.g.
+.BR hfsutils )
+can modify the volume.
+.TP
+.BI \-hfs\-bless " folder_name"
+(not supported by pycdlib-genisoimage) "Bless" the given directory (folder). This is usually the
+.I System Folder
+and is used in creating HFS bootable CDs. The name of the directory must
+be the whole path name as
+.B pycdlib-genisoimage
+sees it. E.g., if the given pathspec is
+.I ./cddata
+and the required folder is called
+.IR "System Folder" ,
+the whole path name is
+.I \(dq/cddata/System Folder\(dq
+(remember to use quotes if the name contains spaces).
+.TP
+.BI \-hfs\-parms " parameters"
+(not supported by pycdlib-genisoimage) Override certain parameters used to create the HFS filesystem. Unlikely to
+be used in normal circumstances.
+.TP
+.B \-\-cap
+(not supported by pycdlib-genisoimage) Look for AUFS CAP Macintosh files. Search for CAP Apple/Unix file formats
+only. Searching for the other possible Apple/Unix file formats is disabled,
+unless other
+.I double dash
+options are given.
+.TP
+.B \-\-netatalk
+(not supported by pycdlib-genisoimage) Look for NETATALK Macintosh files
+.TP
+.B \-\-double
+(not supported by pycdlib-genisoimage) Look for AppleDouble Macintosh files
+.TP
+.B \-\-ethershare
+(not supported by pycdlib-genisoimage) Look for Helios EtherShare Macintosh files
+.TP
+.B \-\-ushare
+(not supported by pycdlib-genisoimage) Look for IPT UShare Macintosh files
+.TP
+.B \-\-exchange
+(not supported by pycdlib-genisoimage) Look for PC Exchange Macintosh files
+.TP
+.B \-\-sgi
+(not supported by pycdlib-genisoimage) Look for SGI Macintosh files
+.TP
+.B \-\-xinet
+(not supported by pycdlib-genisoimage) Look for XINET Macintosh files
+.TP
+.B \-\-macbin
+(not supported by pycdlib-genisoimage) Look for MacBinary Macintosh files
+.TP
+.B \-\-single
+(not supported by pycdlib-genisoimage) Look for AppleSingle Macintosh files
+.TP
+.B \-\-dave
+(not supported by pycdlib-genisoimage) Look for Thursby Software Systems DAVE Macintosh files
+.TP
+.B \-\-sfm
+(not supported by pycdlib-genisoimage) Look for Microsoft's Services for Macintosh files (NT only) (Alpha)
+.TP
+.B \-\-osx\-double
+(not supported by pycdlib-genisoimage) Look for Mac OS X AppleDouble Macintosh files
+.TP
+.B \-\-osx\-hfs
+(not supported by pycdlib-genisoimage) Look for Mac OS X HFS Macintosh files
+
+.SH SEE ALSO
+genisoimage(1), pycdlib-explorer(1), pycdlib-extract-files(1)
+
+.SH AUTHOR
+Chris Lalancette <clalancette at gmail.com>
diff --git a/devtools/contrib/pycdlib/pycdlib.patch b/devtools/contrib/pycdlib/pycdlib.patch
new file mode 100644
index 00000000000..cbc3f9b4c5b
--- /dev/null
+++ b/devtools/contrib/pycdlib/pycdlib.patch
@@ -0,0 +1,41 @@
+diff --git a/pycdlib/pycdlib.py b/pycdlib/pycdlib.py
+index 65b608c..214741a 100644
+--- a/pycdlib/pycdlib.py
++++ b/pycdlib/pycdlib.py
+@@ -5544,19 +5544,22 @@ class PyCdlib:
+ if key in ('joliet_path', 'rr_path', 'iso_path', 'udf_path'):
+ if value is not None:
+ num_paths += 1
++ elif key == "encoding":
++ pass
+ else:
+- raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
++ raise pycdlibexception.PyCdlibInvalidInput(f"Invalid keyword {key}, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+
++ encoding = {"encoding": kwargs["encoding"]} if "encoding" in kwargs else {}
+ if num_paths != 1:
+ raise pycdlibexception.PyCdlibInvalidInput("Must specify one, and only one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+
+ if 'joliet_path' in kwargs:
+- return self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']))
++ return self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']), **encoding)
+ if 'rr_path' in kwargs:
+- return self._get_rr_entry(utils.normpath(kwargs['rr_path']))
++ return self._get_rr_entry(utils.normpath(kwargs['rr_path']), **encoding)
+ if 'udf_path' in kwargs:
+- return self._get_udf_entry(kwargs['udf_path'])
+- return self._get_iso_entry(utils.normpath(kwargs['iso_path']))
++ return self._get_udf_entry(kwargs['udf_path'], **encoding)
++ return self._get_iso_entry(utils.normpath(kwargs['iso_path']), **encoding)
+
+ def add_isohybrid(self, part_entry=1, mbr_id=None, part_offset=0,
+ geometry_sectors=32, geometry_heads=64, part_type=None,
+@@ -5875,7 +5878,7 @@ class PyCdlib:
+ elif key == 'encoding':
+ user_encoding = value
+ else:
+- raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
++ raise pycdlibexception.PyCdlibInvalidInput(f"Invalid keyword {key}, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+
+ if num_paths != 1:
+ raise pycdlibexception.PyCdlibInvalidInput("Must specify one, and only one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
diff --git a/devtools/contrib/pycdlib/pycdlib/__init__.py b/devtools/contrib/pycdlib/pycdlib/__init__.py
new file mode 100644
index 00000000000..fa3bbcbacca
--- /dev/null
+++ b/devtools/contrib/pycdlib/pycdlib/__init__.py
@@ -0,0 +1,5 @@
+"""
+Pycdlib is a pure python library to parse, write (master), and create ISO9660
+files. These files are suitable for writing to a CD or USB.
+"""
+from .pycdlib import PyCdlib as PyCdlib # NOQA, pylint: disable=useless-import-alias
diff --git a/devtools/contrib/pycdlib/pycdlib/dates.py b/devtools/contrib/pycdlib/pycdlib/dates.py
new file mode 100644
index 00000000000..c87284d0f07
--- /dev/null
+++ b/devtools/contrib/pycdlib/pycdlib/dates.py
@@ -0,0 +1,270 @@
+# Copyright (C) 2015-2022 Chris Lalancette <clalancette at gmail.com>
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation;
+# version 2.1 of the License.
+
+# This library 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
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+Classes and utilities for ISO date support.
+"""
+
+import functools
+import struct
+import time
+
+from pycdlib import pycdlibexception
+from pycdlib import utils
+
+
+ at functools.lru_cache(maxsize=256)
+def string_to_timestruct(input_string):
+ # type: (bytes) -> time.struct_time
+ """
+ A cacheable function to take an input string and decode it into a
+ time.struct_time from the time module. If the string cannot be decoded
+ because of an illegal value, then the all-zero time.struct_time will be
+ returned instead.
+
+ Parameters:
Commit: 66554c6e96a123e2b954fd41d1be8f9a0251af59
https://github.com/scummvm/scummvm/commit/66554c6e96a123e2b954fd41d1be8f9a0251af59
Author: eientei95 (einstein95 at users.noreply.github.com)
Date: 2026-02-07T23:20:04+01:00
Commit Message:
DEVTOOLS: PYCDLIB: Allow user to specify encoding
Changed paths:
devtools/contrib/pycdlib/pycdlib/pycdlib.py
diff --git a/devtools/contrib/pycdlib/pycdlib/pycdlib.py b/devtools/contrib/pycdlib/pycdlib/pycdlib.py
index 2a9ef720d10..dbe68a1518c 100644
--- a/devtools/contrib/pycdlib/pycdlib/pycdlib.py
+++ b/devtools/contrib/pycdlib/pycdlib/pycdlib.py
@@ -705,8 +705,8 @@ class PyCdlib:
self._cdfp.seek(extent * self.logical_block_size)
@functools.lru_cache(maxsize=256)
- def _find_iso_record(self, iso_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _find_iso_record(self, iso_path, encoding='utf-8'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
An internal method to find a directory record on the ISO given an ISO
path. If the entry is found, it returns the directory record object
@@ -715,14 +715,15 @@ class PyCdlib:
Parameters:
iso_path - The ISO9660 path to lookup.
+ encoding - The string encoding used for the path.
Returns:
The directory record entry representing the entry on the ISO.
"""
- return _find_dr_record_by_name(self.pvd, iso_path, 'utf-8')
+ return _find_dr_record_by_name(self.pvd, iso_path, encoding)
@functools.lru_cache(maxsize=256)
- def _find_rr_record(self, rr_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _find_rr_record(self, rr_path, encoding='utf-8'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
An internal method to find a directory record on the ISO given a Rock
Ridge path. If the entry is found, it returns the directory record
@@ -731,6 +732,7 @@ class PyCdlib:
Parameters:
rr_path - The Rock Ridge path to lookup.
+ encoding - The string encoding used for the path.
Returns:
The directory record entry representing the entry on the ISO.
"""
@@ -742,7 +744,7 @@ class PyCdlib:
splitpath = utils.split_path(rr_path)
- currpath = splitpath.pop(0).decode('utf-8').encode('utf-8')
+ currpath = splitpath.pop(0).decode('utf-8').encode(encoding)
entry = root_dir_record
@@ -793,13 +795,13 @@ class PyCdlib:
if not child.is_dir():
break
entry = child
- currpath = splitpath.pop(0).decode('utf-8').encode('utf-8')
+ currpath = splitpath.pop(0).decode('utf-8').encode(encoding)
raise pycdlibexception.PyCdlibInvalidInput('Could not find path')
@functools.lru_cache(maxsize=256)
- def _find_joliet_record(self, joliet_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _find_joliet_record(self, joliet_path, encoding='utf-16_be'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
An internal method to find a directory record on the ISO given a Joliet
path. If the entry is found, it returns the directory record object
@@ -808,12 +810,13 @@ class PyCdlib:
Parameters:
joliet_path - The Joliet path to lookup.
+ encoding - The string encoding used for the path.
Returns:
The directory record entry representing the entry on the ISO.
"""
if self.joliet_vd is None:
raise pycdlibexception.PyCdlibInternalError('Joliet path requested on non-Joliet ISO')
- return _find_dr_record_by_name(self.joliet_vd, joliet_path, 'utf-16_be')
+ return _find_dr_record_by_name(self.joliet_vd, joliet_path, encoding)
@functools.lru_cache(maxsize=256)
def _find_udf_record(self, udf_path):
@@ -2412,8 +2415,8 @@ class PyCdlib:
utils.copy_data(data_len, blocksize, data_fp, outfp)
def _get_file_from_iso_fp(self, outfp, blocksize, iso_path, rr_path,
- joliet_path):
- # type: (BinaryIO, int, Optional[bytes], Optional[bytes], Optional[bytes]) -> None
+ joliet_path, encoding=''):
+ # type: (BinaryIO, int, Optional[bytes], Optional[bytes], Optional[bytes], str) -> None
"""
An internal method to fetch a single file from the ISO and write it out
to the file object.
@@ -2427,19 +2430,23 @@ class PyCdlib:
with iso_path and joliet_path).
joliet_path - The absolute Joliet path to lookup on the ISO (exclusive
with iso_path and rr_path).
+ encoding - The string encoding used for the path.
Returns:
Nothing.
"""
if joliet_path is not None:
if self.joliet_vd is None:
raise pycdlibexception.PyCdlibInvalidInput('Cannot fetch a joliet_path from a non-Joliet ISO')
- found_record = self._find_joliet_record(joliet_path)
+ encoding = encoding or 'utf-16_be'
+ found_record = self._find_joliet_record(joliet_path, encoding)
elif rr_path is not None:
if not self.rock_ridge:
raise pycdlibexception.PyCdlibInvalidInput('Cannot fetch a rr_path from a non-Rock Ridge ISO')
- found_record = self._find_rr_record(rr_path)
+ encoding = encoding or 'utf-8'
+ found_record = self._find_rr_record(rr_path, encoding)
elif iso_path is not None:
- found_record = self._find_iso_record(iso_path)
+ encoding = encoding or 'utf-8'
+ found_record = self._find_iso_record(iso_path, encoding)
else:
raise pycdlibexception.PyCdlibInternalError('Invalid path passed to get_file_from_iso_fp')
@@ -3472,52 +3479,55 @@ class PyCdlib:
return num_bytes_to_remove
- def _get_iso_entry(self, iso_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _get_iso_entry(self, iso_path, encoding='utf-8'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
Internal method to get the directory record for an ISO path.
Parameters:
iso_path - The path on the ISO filesystem to look up the record for.
+ encoding - The string encoding used for the path.
Returns:
A dr.DirectoryRecord object representing the path.
"""
if self._needs_reshuffle:
self._reshuffle_extents()
- return self._find_iso_record(iso_path)
+ return self._find_iso_record(iso_path, encoding)
- def _get_rr_entry(self, rr_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _get_rr_entry(self, rr_path, encoding='utf-8'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
Internal method to get the directory record for a Rock Ridge path.
Parameters:
rr_path - The Rock Ridge path on the ISO filesystem to look up the
record for.
+ encoding - The string encoding used for the path.
Returns:
A dr.DirectoryRecord object representing the path.
"""
if self._needs_reshuffle:
self._reshuffle_extents()
- return self._find_rr_record(rr_path)
+ return self._find_rr_record(rr_path, encoding)
- def _get_joliet_entry(self, joliet_path):
- # type: (bytes) -> dr.DirectoryRecord
+ def _get_joliet_entry(self, joliet_path, encoding='utf-16_be'):
+ # type: (bytes, str) -> dr.DirectoryRecord
"""
Internal method to get the directory record for a Joliet path.
Parameters:
joliet_path - The path on the Joliet filesystem to look up the record
for.
+ encoding - The string encoding used for the path.
Returns:
A dr.DirectoryRecord object representing the path.
"""
if self._needs_reshuffle:
self._reshuffle_extents()
- return self._find_joliet_record(joliet_path)
+ return self._find_joliet_record(joliet_path, encoding)
def _get_udf_entry(self, udf_path):
# type: (str) -> udfmod.UDFFileEntry
@@ -4103,6 +4113,7 @@ class PyCdlib:
with iso_path, rr_path, and udf_path).
udf_path - The absolute UDF path to lookup on the ISO (exclusive with
iso_path, rr_path, and joliet_path).
+ encoding - The encoding to use for parsing the filenames.
Returns:
Nothing.
"""
@@ -4114,6 +4125,7 @@ class PyCdlib:
iso_path = None
rr_path = None
udf_path = None
+ encoding = ''
num_paths = 0
for key, value in kwargs.items():
if key == 'blocksize':
@@ -4144,6 +4156,10 @@ class PyCdlib:
num_paths += 1
elif value is not None:
raise pycdlibexception.PyCdlibInvalidInput('iso_path must be a string')
+ elif key == 'encoding':
+ if not isinstance(value, str):
+ raise pycdlibexception.PyCdlibInvalidInput('encoding must be a string')
+ encoding = value
else:
raise pycdlibexception.PyCdlibInvalidInput('Unknown keyword %s' % (key))
@@ -4155,7 +4171,7 @@ class PyCdlib:
self._udf_get_file_from_iso_fp(fp, blocksize, udf_path)
else:
self._get_file_from_iso_fp(fp, blocksize, iso_path, rr_path,
- joliet_path)
+ joliet_path, encoding)
def get_file_from_iso_fp(self, outfp, **kwargs):
# type: (BinaryIO, Union[str, int]) -> None
@@ -4173,6 +4189,7 @@ class PyCdlib:
with iso_path, rr_path, and udf_path).
udf_path - The absolute UDF path to lookup on the ISO (exclusive with
iso_path, rr_path, and joliet_path).
+ encoding - The encoding to use for parsing the filenames.
Returns:
Nothing.
"""
@@ -4184,6 +4201,7 @@ class PyCdlib:
iso_path = None
rr_path = None
udf_path = None
+ encoding = None
num_paths = 0
for key, value in kwargs.items():
if key == 'blocksize':
@@ -4214,6 +4232,10 @@ class PyCdlib:
num_paths += 1
elif value is not None:
raise pycdlibexception.PyCdlibInvalidInput('udf_path must be a string')
+ elif key == 'encoding':
+ if not isinstance(value, str):
+ raise pycdlibexception.PyCdlibInvalidInput('encoding must be a string')
+ encoding = value
else:
raise pycdlibexception.PyCdlibInvalidInput('Unknown keyword %s' % (key))
@@ -4224,7 +4246,7 @@ class PyCdlib:
self._udf_get_file_from_iso_fp(outfp, blocksize, udf_path)
else:
self._get_file_from_iso_fp(outfp, blocksize, iso_path, rr_path,
- joliet_path)
+ joliet_path, encoding)
def get_and_write(self, iso_path, local_path, blocksize=8192):
# type: (str, str, int) -> None
@@ -5450,6 +5472,7 @@ class PyCdlib:
rr_path - The absolute Rock Ridge path on the ISO to list the children for.
joliet_path - The absolute Joliet path on the ISO to list the children for.
udf_path - The absolute UDF path on the ISO to list the children for.
+ encoding - The string encoding used for the path; defaults to 'utf-8' or 'utf-16_be'
Yields:
Children of this path.
Returns:
@@ -5463,6 +5486,8 @@ class PyCdlib:
if key in ('joliet_path', 'rr_path', 'iso_path', 'udf_path'):
if value is not None:
num_paths += 1
+ elif key in ('encoding'):
+ continue
else:
raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
@@ -5480,12 +5505,15 @@ class PyCdlib:
else:
use_rr = False
if 'joliet_path' in kwargs:
- rec = self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']))
+ kwargs['encoding'] = kwargs.get('encoding') or 'utf-16_be'
+ rec = self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']), kwargs['encoding'])
elif 'rr_path' in kwargs:
- rec = self._get_rr_entry(utils.normpath(kwargs['rr_path']))
+ kwargs['encoding'] = kwargs.get('encoding') or 'utf-8'
+ rec = self._get_rr_entry(utils.normpath(kwargs['rr_path']), kwargs['encoding'])
use_rr = True
else:
- rec = self._get_iso_entry(utils.normpath(kwargs['iso_path']))
+ kwargs['encoding'] = kwargs.get('encoding') or 'utf-8'
+ rec = self._get_iso_entry(utils.normpath(kwargs['iso_path']), kwargs['encoding'])
for c in _yield_children(rec, use_rr): # pylint: disable=use-yield-from
yield c
@@ -5630,14 +5658,15 @@ class PyCdlib:
self.isohybrid_mbr = None
- def full_path_from_dirrecord(self, rec, rockridge=False):
- # type: (Union[dr.DirectoryRecord, udfmod.UDFFileEntry], bool) -> str
+ def full_path_from_dirrecord(self, rec, rockridge=False, user_encoding=''):
+ # type: (Union[dr.DirectoryRecord, udfmod.UDFFileEntry], bool, str) -> str
"""
Get the absolute path of a directory record.
Parameters:
rec - The directory record to get the full path for.
rockridge - Whether to get the rock ridge full path.
+ user_encoding - The string encoding used for the path as determined by the user.
Returns:
A string representing the absolute path to the file on the ISO.
"""
@@ -5650,6 +5679,9 @@ class PyCdlib:
if self.joliet_vd is not None and id(rec.vd) == id(self.joliet_vd):
encoding = 'utf-16_be'
+ if user_encoding:
+ encoding = user_encoding
+
# A root entry has no Rock Ridge entry, even on a Rock Ridge ISO.
# Always return / here.
if rec.is_root:
@@ -5689,6 +5721,8 @@ class PyCdlib:
encoding = rec.file_ident.encoding
else:
encoding = 'utf-8'
+ if user_encoding:
+ encoding = user_encoding
udf_rec = rec # type: Optional[udfmod.UDFFileEntry]
while udf_rec is not None:
ident = udf_rec.file_identifier()
@@ -5859,12 +5893,11 @@ class PyCdlib:
raise pycdlibexception.PyCdlibInvalidInput('This object is not initialized; call either open() or new() to create an ISO')
num_paths = 0
- user_encoding = None
+ user_encoding = ''
for key, value in kwargs.items():
- if key in ('joliet_path', 'rr_path', 'iso_path', 'udf_path'):
- if value is not None:
- num_paths += 1
- elif key == 'encoding':
+ if key in ('joliet_path', 'rr_path', 'iso_path', 'udf_path') and value is not None:
+ num_paths += 1
+ elif key == 'encoding' and value:
user_encoding = value
else:
raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
@@ -5901,23 +5934,22 @@ class PyCdlib:
while dirs:
dir_record = dirs.popleft()
- relpath = self.full_path_from_dirrecord(dir_record,
- rockridge=path_type == 'rr_path')
+ relpath = self.full_path_from_dirrecord(dir_record, rockridge=path_type == 'rr_path',
+ user_encoding=user_encoding)
dirlist = []
filelist = []
dirdict = {}
- for child in reversed(list(self.list_children(**{path_type: relpath}))):
+ for child in reversed(list(self.list_children(**{path_type: relpath, 'encoding': user_encoding or default_encoding}))):
if child is None or child.is_dot() or child.is_dotdot():
continue
- if user_encoding is not None:
+ if user_encoding != '':
encoding = user_encoding
+ elif isinstance(child, udfmod.UDFFileEntry) and child.file_ident is not None:
+ encoding = child.file_ident.encoding
else:
- if isinstance(child, udfmod.UDFFileEntry) and child.file_ident is not None:
- encoding = child.file_ident.encoding
- else:
- encoding = default_encoding
+ encoding = default_encoding or 'utf-8'
if path_type == 'rr_path':
name = child.rock_ridge.name()
Commit: 162ebe483ef3c3b71b35184284ea79be40fe2b84
https://github.com/scummvm/scummvm/commit/162ebe483ef3c3b71b35184284ea79be40fe2b84
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-02-07T23:20:04+01:00
Commit Message:
DEVTOOLS: PYCDLIB: More fixes for Japanese ISOs
Changed paths:
A idevtools/contrib/pycdlib/pycdlib/pycdlib.py
R devtools/contrib/pycdlib/pycdlib/pycdlib.py
diff --git a/devtools/contrib/pycdlib/pycdlib/pycdlib.py b/idevtools/contrib/pycdlib/pycdlib/pycdlib.py
similarity index 99%
rename from devtools/contrib/pycdlib/pycdlib/pycdlib.py
rename to idevtools/contrib/pycdlib/pycdlib/pycdlib.py
index dbe68a1518c..fdc2f0289de 100644
--- a/devtools/contrib/pycdlib/pycdlib/pycdlib.py
+++ b/idevtools/contrib/pycdlib/pycdlib/pycdlib.py
@@ -5564,19 +5564,22 @@ class PyCdlib:
if key in ('joliet_path', 'rr_path', 'iso_path', 'udf_path'):
if value is not None:
num_paths += 1
+ elif key == "encoding":
+ pass
else:
- raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+ raise pycdlibexception.PyCdlibInvalidInput(f"Invalid keyword {key}, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+ encoding = {"encoding": kwargs["encoding"]} if "encoding" in kwargs else {}
if num_paths != 1:
raise pycdlibexception.PyCdlibInvalidInput("Must specify one, and only one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
if 'joliet_path' in kwargs:
- return self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']))
+ return self._get_joliet_entry(self._normalize_joliet_path(kwargs['joliet_path']), **encoding)
if 'rr_path' in kwargs:
- return self._get_rr_entry(utils.normpath(kwargs['rr_path']))
+ return self._get_rr_entry(utils.normpath(kwargs['rr_path']), **encoding)
if 'udf_path' in kwargs:
- return self._get_udf_entry(kwargs['udf_path'])
- return self._get_iso_entry(utils.normpath(kwargs['iso_path']))
+ return self._get_udf_entry(kwargs['udf_path'], **encoding)
+ return self._get_iso_entry(utils.normpath(kwargs['iso_path']), **encoding)
def add_isohybrid(self, part_entry=1, mbr_id=None, part_offset=0,
geometry_sectors=32, geometry_heads=64, part_type=None,
@@ -5900,7 +5903,7 @@ class PyCdlib:
elif key == 'encoding' and value:
user_encoding = value
else:
- raise pycdlibexception.PyCdlibInvalidInput("Invalid keyword, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
+ raise pycdlibexception.PyCdlibInvalidInput(f"Invalid keyword {key}, must be one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
if num_paths != 1:
raise pycdlibexception.PyCdlibInvalidInput("Must specify one, and only one of 'iso_path', 'rr_path', 'joliet_path', or 'udf_path'")
Commit: f40006fddf7a2fb445fcd731f38dcae7f9ebb3de
https://github.com/scummvm/scummvm/commit/f40006fddf7a2fb445fcd731f38dcae7f9ebb3de
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-02-07T23:20:05+01:00
Commit Message:
DEVTOOLS: COMPANION: Use in-tree pycdlib
Changed paths:
devtools/dumper-companion.py
diff --git a/devtools/dumper-companion.py b/devtools/dumper-companion.py
index 454bfc9b85b..7dfc133791e 100755
--- a/devtools/dumper-companion.py
+++ b/devtools/dumper-companion.py
@@ -22,6 +22,9 @@ import logging
import os
import re
import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "contrib/pycdlib"))
+
import unicodedata
import urllib.request
import zipfile
More information about the Scummvm-git-logs
mailing list