commit 934d53057d1399d5760e005b12beebfd86bb84a7 Author: R. Bernstein Date: Sun Nov 23 23:17:02 2008 -0500 First semblance of distutils setuptools. Not complete yet though. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bf5d622b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/pycdio.pyc +/pyiso9660_wrap.c diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..e437a99a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,9 @@ +include data/copying.iso +include data/isofs-m1.bin +include data/isofs-m1.cue +include example/README +include example/*.py +include swig/*.swg +include test/cdda.bin +include test/cdda.cue +include test/cdda.toc diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..881d2682 --- /dev/null +++ b/README.txt @@ -0,0 +1,52 @@ +pycdio is a Python interface to the CD Input and Control library +(libcdio). You can get the source at the same place as libcdio: + ftp://ftp.gnu.org:/pub/gnu/libcdio/pycdio-* + +The pycdio (and libcdio) libraries encapsulate CD-ROM reading and +control. Python programs wishing to be oblivious of the OS- and +device-dependent properties of a CD-ROM can use this library. + +libcdio is rather large and yet may still grow a bit. (UDF support in +libcdio may be on the horizon.) + +What is in pycdio is incomplete; over time it may grow to completion +depending on various factors: e.g. interest, whether others help +out. + +Sections of libcdio that are currently missing are the (SCSI) MMC +commands, the cdparanoia library, CD-Text handling. Of the audio +controls, I put in those things that didn't require any thought. The +ISO 9660 library is pretty complete, except file "stat" information +which is at present is pretty minimal. + +That said, what's in there is very usable (It contains probably more +access capabilities than what most media players that don't use +libcdio have.) + +The encapsulation by SWIG is done in two parts. The lower-level python +interface is called pycdio and is generated by SWIG. + +The more object-oriented module is cdio; it is a Python class that +uses pycdio. Although pycdio is perfectly usable on its own, it is +expected that cdio is what most people will use. As pycdio more +closely models the C interface, it is conceivable (if unlikely) that +diehard libcdio C users who are very familiar with that interface +could prefer that. + +It is probably possible to change the SWIG in such a way to combine +these pieces. However there are the problems. First, I'm not that much +of a SWIG expert. Second it looks as though the resulting SWIG code +would be more complex. Third the separation makes translation very +straight forward to understand and maintain: first get what's in C +into Python as a one-to-one translation. Then we implement some nice +abstraction off of that. The abstraction can be modified without +having to redo the underlying translation. (But the reverse is +generally not true: usually changes to the C-to-python translation, +pycdio, do result in small, but obvious and straightforward changes to +the abstraction layer cdio.) + +There is much to be done - you want to help out, please do so! + +Standalone documentation is missing although many of the methods, +classes and functions have some document strings. See also the +programs in the example directory. diff --git a/cdio.py b/cdio.py new file mode 100644 index 00000000..c4238f3c --- /dev/null +++ b/cdio.py @@ -0,0 +1,887 @@ +#!/usr/bin/python +# $Id: cdio.py,v 1.6 2008/05/01 16:55:03 karl Exp $ +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""The CD Input and Control library (pycdio) encapsulates CD-ROM +reading and control. Applications wishing to be oblivious of the OS- +and device-dependent properties of a CD-ROM can use this library.""" + +import pycdio +import types + +class DeviceException(Exception): + """General device or driver exceptions""" + +class DriverError(DeviceException): pass +class DriverUnsupportedError(DeviceException): pass +class DriverUninitError(DeviceException): pass +class DriverNotPermittedError(DeviceException): pass +class DriverBadParameterError(DeviceException): pass +class DriverBadPointerError(DeviceException): pass +class NoDriverError(DeviceException): pass + +class TrackError(DeviceException): pass + + +# Note: the keys below match those the names returned by +# cdio_get_driver_name() + +drivers = { + 'Unknown' : pycdio.DRIVER_UNKNOWN, + 'AIX' : pycdio.DRIVER_AIX, + 'aix' : pycdio.DRIVER_AIX, + 'BSDI' : pycdio.DRIVER_BSDI, + 'bsdi' : pycdio.DRIVER_BSDI, + 'FreeBSD' : pycdio.DRIVER_FREEBSD, + 'freebsd' : pycdio.DRIVER_FREEBSD, + 'GNU/Linux': pycdio.DRIVER_LINUX, + 'Solaris' : pycdio.DRIVER_SOLARIS, + 'solaris' : pycdio.DRIVER_SOLARIS, + 'OS X' : pycdio.DRIVER_OSX, + 'WIN32' : pycdio.DRIVER_WIN32, + 'CDRDAO' : pycdio.DRIVER_CDRDAO, + 'cdrdao' : pycdio.DRIVER_CDRDAO, + 'BIN/CUE' : pycdio.DRIVER_BINCUE, + 'NRG' : pycdio.DRIVER_NRG, + 'Nero' : pycdio.DRIVER_NRG, + 'device' : pycdio.DRIVER_DEVICE + } + +read_mode2blocksize = { + pycdio.READ_MODE_AUDIO: pycdio.CD_FRAMESIZE_RAW, + pycdio.READ_MODE_M1F1: pycdio.M2RAW_SECTOR_SIZE, + pycdio.READ_MODE_M1F2: pycdio.CD_FRAMESIZE, + pycdio.READ_MODE_M2F1: pycdio.M2RAW_SECTOR_SIZE, + pycdio.READ_MODE_M2F2: pycdio.CD_FRAMESIZE + } + +def __possibly_raise_exception__(drc, msg=None): + """Raise a Driver Error exception on error as determined by drc""" + if drc==pycdio.DRIVER_OP_SUCCESS: + return + if drc==pycdio.DRIVER_OP_ERROR: + raise DriverError + if drc==pycdio.DRIVER_OP_UNINIT: + raise DriverUninitError + if drc==pycdio.DRIVER_OP_UNSUPPORTED: + raise DriverUnsupportedError + if drc==pycdio.DRIVER_OP_NOT_PERMITTED: + raise DriverUnsupportedError + if drc==pycdio.DRIVER_OP_BAD_PARAMETER: + raise DriverBadParameterError + if drc==pycdio.DRIVER_OP_BAD_POINTER: + raise DriverBadPointerError + if drc==pycdio.DRIVER_OP_NO_DRIVER: + raise NoDriverError + raise DeviceException('unknown exception %d' % drc) + +def close_tray(drive=None, driver_id=pycdio.DRIVER_UNKNOWN): + """close_tray(drive=None, driver_id=DRIVER_UNKNOWN) -> driver_id + + close media tray in CD drive if there is a routine to do so. + The driver id is returned. A DeviceException is thrown on error.""" + drc, found_driver_id = pycdio.close_tray(drive, driver_id) + __possibly_raise_exception__(drc) + return found_driver_id + +def get_default_device_driver(driver_id=pycdio.DRIVER_DEVICE): + """get_default_device_driver(self, driver_id=pycdio.DRIVER_DEVICE) + ->[device, driver] + + Return a string containing the default CD device if none is + specified. if driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE + then one set the default device for that. + + None is returned as the device if we couldn't get a default + device.""" + result = pycdio.get_default_device_driver(driver_id) + if type(result) == type([1,2]): + return result + return None + +def get_devices(driver_id=pycdio.DRIVER_UNKNOWN): + """ + get_devices(driver_id)->[device1, device2, ...] + + Get an list of device names. + """ + result = pycdio.get_devices(driver_id) + if type(result) == types.StringType: + return [result] + else: + return result + +def get_devices_ret(driver_id=pycdio.DRIVER_UNKNOWN): + """ + get_devices_ret(driver_id)->[device1, device2, ... driver_id] + + Like get_devices, but return the p_driver_id which may be different + from the passed-in driver_id if it was pycdio.DRIVER_DEVICE or + pycdio.DRIVER_UNKNOWN. The return driver_id may be useful because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds things + up for libcdio as well. + """ + # FIXME: SWIG code is not removing a parameter properly, hence the [1:] + # at the end + return pycdio.get_devices_ret(driver_id)[1:] + +def get_devices_with_cap(capabilities, any=False): + """ + get_devices_with_cap(capabilities, any=False)->[device1, device2...] + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. + + If any is False then every capability listed in the + extended portion of capabilities (i.e. not the basic filesystem) + must be satisified. If any is True, then if any of the + capabilities matches, we call that a success. + + To find a CD-drive of any type, use the mask pycdio.CDIO_FS_MATCH_ALL. + + The array of device names is returned or NULL if we couldn't get a + default device. It is also possible to return a non NULL but after + dereferencing the the value is NULL. This also means nothing was + found. + """ + # FIXME: SWIG code is not removing a parameter properly, hence the [1:] + # at the end + return pycdio.get_devices_with_cap(capabilities, any)[1:] + +def get_devices_with_cap_ret(capabilities, any=False): + """ + get_devices_with_cap(capabilities, any=False) + [device1, device2..., driver_id] + + Like cdio_get_devices_with_cap but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. + """ + # FIXME: SWIG code is not removing a parameter properly, hence the [1:] + # at the end + return pycdio.get_devices_with_cap_ret(capabilities, any)[1:] + +def have_driver(driver_id): + """ + have_driver(driver_id) -> bool + + Return True if we have driver driver_id. + """ + if type(driver_id)==types.IntType: + return pycdio.have_driver(driver_id) + elif type(driver_id)==types.StringType and driver_id in drivers: + ret = pycdio.have_driver(drivers[driver_id]) + if ret == 0: return False + if ret == 1: return True + raise ValueError('internal error: driver id came back %d' % ret) + else: + raise ValueError('need either a number or string driver id') + +def is_binfile(binfile_name): + """ + is_binfile(binfile_name)->cue_name + + Determine if binfile_name is the BIN file part of a CDRWIN CD + disk image. + + Return the corresponding CUE file if bin_name is a BIN file or + None if not a BIN file. + """ + return pycdio.is_binfile(binfile_name) + +def is_cuefile(cuefile_name): + """ + is_cuefile(cuefile_name)->bin_name + + Determine if cuefile_name is the CUE file part of a CDRWIN CD + disk image. + + Return the corresponding BIN file if bin_name is a CUE file or + None if not a CUE file. + """ + return pycdio.is_cuefile(cuefile_name) + +def is_device(source, driver_id=pycdio.DRIVER_UNKNOWN): + """ + is_device(source, driver_id=pycdio.DRIVER_UNKNOWN)->bool + Return True if source refers to a real hardware CD-ROM. + """ + if driver_id is None: driver_id=pycdio.DRIVER_UNKNOWN + return pycdio.is_device(source, driver_id) + +def is_nrg(nrgfile_name): + """ + is_nrg(nrgfile_name)->bool + + Determine if nrgfile_name is a Nero CD disc image + """ + return pycdio.is_nrg(nrgfile_name) + +def is_tocfile(tocfile_name): + """ + is_tocfile(tocfile_name)->bool + + Determine if tocfile_name is a cdrdao CD disc image + """ + return pycdio.is_tocfile(tocfile_name) + +def convert_drive_cap_misc(bitmask): + """Convert bit mask for miscellaneous drive properties + into a dictionary of drive capabilities""" + result={} + if bitmask & pycdio.DRIVE_CAP_ERROR: + result['DRIVE_CAP_ERROR'] = True + if bitmask & pycdio.DRIVE_CAP_UNKNOWN: + result['DRIVE_CAP_UNKNOWN'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_CLOSE_TRAY: + result['DRIVE_CAP_MISC_CLOSE_TRAY'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_EJECT: + result['DRIVE_CAP_MISC_EJECT'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_LOCK: + result['DRIVE_CAP_MISC_LOCK'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_SELECT_SPEED: + result['DRIVE_CAP_MISC_SELECT_SPEED'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_SELECT_DISC: + result['DRIVE_CAP_MISC_SELECT_DISC'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_MULTI_SESSION: + result['DRIVE_CAP_MISC_MULTI_SESSION'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_MEDIA_CHANGED: + result['DRIVE_CAP_MISC_MEDIA_CHANGED'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_RESET: + result['DRIVE_CAP_MISC_RESET'] = True + if bitmask & pycdio.DRIVE_CAP_MISC_FILE: + result['DRIVE_CAP_MISC_FILE'] = True + return result + +def convert_drive_cap_read(bitmask): + """Convert bit mask for drive read properties + into a dictionary of drive capabilities""" + result={} + if bitmask & pycdio.DRIVE_CAP_READ_AUDIO: + result['DRIVE_CAP_READ_AUDIO'] = True + if bitmask & pycdio.DRIVE_CAP_READ_CD_DA: + result['DRIVE_CAP_READ_CD_DA'] = True + if bitmask & pycdio.DRIVE_CAP_READ_CD_G: + result['DRIVE_CAP_READ_CD_G'] = True + if bitmask & pycdio.DRIVE_CAP_READ_CD_R: + result['DRIVE_CAP_READ_CD_R'] = True + if bitmask & pycdio.DRIVE_CAP_READ_CD_RW: + result['DRIVE_CAP_READ_CD_RW'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_R: + result['DRIVE_CAP_READ_DVD_R'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_PR: + result['DRIVE_CAP_READ_DVD_PR'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_RAM: + result['DRIVE_CAP_READ_DVD_RAM'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_ROM: + result['DRIVE_CAP_READ_DVD_ROM'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_RW: + result['DRIVE_CAP_READ_DVD_RW'] = True + if bitmask & pycdio.DRIVE_CAP_READ_DVD_RPW: + result['DRIVE_CAP_READ_DVD_RPW'] = True + if bitmask & pycdio.DRIVE_CAP_READ_C2_ERRS: + result['DRIVE_CAP_READ_C2_ERRS'] = True + if bitmask & pycdio.DRIVE_CAP_READ_MODE2_FORM1: + result['DRIVE_CAP_READ_MODE2_FORM1'] = True + if bitmask & pycdio.DRIVE_CAP_READ_MODE2_FORM2: + result['DRIVE_CAP_READ_MODE2_FORM2'] = True + if bitmask & pycdio.DRIVE_CAP_READ_MCN: + result['DRIVE_CAP_READ_MCN'] = True + if bitmask & pycdio.DRIVE_CAP_READ_ISRC: + result['DRIVE_CAP_READ_ISRC'] = True + return result + +def convert_drive_cap_write(bitmask): + """Convert bit mask for drive write properties + into a dictionary of drive capabilities""" + result={} + if bitmask & pycdio.DRIVE_CAP_WRITE_CD_R: + result['DRIVE_CAP_WRITE_CD_R'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_CD_RW: + result['DRIVE_CAP_WRITE_CD_RW'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_DVD_R: + result['DRIVE_CAP_WRITE_DVD_R'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_DVD_PR: + result['DRIVE_CAP_WRITE_DVD_PR'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_DVD_RAM: + result['DRIVE_CAP_WRITE_DVD_RAM'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_DVD_RW: + result['DRIVE_CAP_WRITE_DVD_RW'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_DVD_RPW: + result['DRIVE_CAP_WRITE_DVD_RPW'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_MT_RAINIER: + result['DRIVE_CAP_WRITE_MT_RAINIER'] = True + if bitmask & pycdio.DRIVE_CAP_WRITE_BURN_PROOF: + result['DRIVE_CAP_WRITE_BURN_PROOF'] = True + return result + +class Device: + """CD Input and control class for discs/devices""" + + def __init__(self, source=None, driver_id=None, + access_mode=None): + self.cd = None + if source is not None or driver_id is not None: + self.open(source, driver_id, access_mode) + + def audio_pause(self): + """ + audio_pause(cdio)->status + Pause playing CD through analog output. + A DeviceError exception may be raised. + """ + drc=pycdio.audio_pause(self.cd) + __possibly_raise_exception__(drc) + + def audio_play_lsn(self, start_lsn, end_lsn): + """ + auto_play_lsn(cdio, start_lsn, end_lsn)->status + + Playing CD through analog output at the given lsn to the ending lsn + A DeviceError exception may be raised. + """ + drc=pycdio.audio_play_lsn(self.cd, start_lsn, end_lsn) + __possibly_raise_exception__(drc) + + def audio_resume(self): + """ + audio_resume(cdio)->status + Resume playing an audio CD through the analog interface. + A DeviceError exception may be raised. + """ + drc=pycdio.audio_resume(self.cd) + __possibly_raise_exception__(drc) + + def audio_stop(self): + """ + audio_stop(cdio)->status + Stop playing an audio CD through the analog interface. + A DeviceError exception may be raised. + """ + drc=pycdio.audio_stop(self.cd) + __possibly_raise_exception__(drc) + + def close(self): + """close(self) + Free resources associated with p_cdio. Call this when done using + using CD reading/control operations for the current device. + """ + if self.cd is not None: + pycdio.close(self.cd) + else: + print "***No object to close" + self.cd=None + + def eject_media(self): + """eject_media(self) + Eject media in CD drive if there is a routine to do so. + A DeviceError exception may be raised. + """ + drc=pycdio.eject_media(self.cd) + self.cd = None + __possibly_raise_exception__(drc) + + ### FIXME: combine into above by testing if drive is the string + ### None versus drive = pycdio.DRIVER_UNKNOWN + def eject_media_drive(self, drive=None): + """eject_media_drive(self, drive=None) + Eject media in CD drive if there is a routine to do so. + An exception is thrown on error.""" + pycdio.eject_media_drive(drive) + + def get_arg(self, key): + """get_arg(self, key)->string + Get the value associatied with key.""" + return pycdio.get_arg(self.cd, key) + + def get_device(self): + """get_device(self)->str + Get the default CD device. + If we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + None even though there may be a hardware CD-ROM.""" + if self.cd is not None: + return pycdio.get_arg(self.cd, "source") + return pycdio.get_device(self.cd) + + def get_disc_last_lsn(self): + """ + get_disc_last_lsn(self)->int + Get the LSN of the end of the CD + + DriverError and IOError may raised on error. + """ + lsn = pycdio.get_disc_last_lsn(self.cd) + if lsn == pycdio.INVALID_LSN: + raise DriverError('Invalid LSN returned') + return lsn + + def get_disc_mode(self): + """ + get_disc_mode(p_cdio) -> str + + Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, etc. + that we've got. The notion of 'CD' is extended a little to include + DVD's. + """ + return pycdio.get_disc_mode(self.cd) + + def get_drive_cap(self): + """ + get_drive_cap(self)->(read_cap, write_cap, misc_cap) + + Get drive capabilities of device. + + In some situations of drivers or OS's we can't find a CD + device if there is no media in it. In this situation + capabilities will show up as empty even though there is a + hardware CD-ROM. get_drive_cap_dev()->(read_cap, write_cap, + misc_cap) + + Get drive capabilities of device. + + In some situations of drivers or OS's we can't find a CD + device if there is no media in it. In this situation + capabilities will show up as empty even though there is a + hardware CD-ROM.""" + + b_read_cap, b_write_cap, b_misc_cap = pycdio.get_drive_cap(self.cd) + return (convert_drive_cap_read(b_read_cap), \ + convert_drive_cap_write(b_write_cap), \ + convert_drive_cap_misc(b_misc_cap)) + + ### FIXME: combine into above by testing on the type of device. + def get_drive_cap_dev(self, device=None): + b_read_cap, b_write_cap, b_misc_cap = \ + pycdio.get_drive_cap_dev(device) + return (convert_drive_cap_read(b_read_cap), \ + convert_drive_cap_write(b_write_cap), \ + convert_drive_cap_misc(b_misc_cap)) + + def get_driver_name(self): + """ + get_driver_name(self)-> string + + return a string containing the name of the driver in use. + + An IOError exception is raised on error. + """ + return pycdio.get_driver_name(self.cd) + + def get_driver_id(self): + """ + get_driver_id(self)-> int + + Return the driver id of the driver in use. + if object has not been initialized or is None, + return pycdio.DRIVER_UNKNOWN. + """ + return pycdio.get_driver_id(self.cd) + + def get_first_track(self): + """ + get_first_track(self)->Track + + return a Track object of the first track. None is returned + if there was a problem. + """ + track = pycdio.get_first_track_num(self.cd) + if track == pycdio.INVALID_TRACK: + return None + return Track(self.cd, track) + + def get_hwinfo(self): + """ + get_hwinfo(self)->[vendor, model, release] + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + """ + return pycdio.get_hwinfo(self.cd) + + def get_joliet_level(self): + """ + get_joliet_level(self)->int + + Return the Joliet level recognized for cdio. + This only makes sense for something that has an ISO-9660 + filesystem. + """ + return pycdio.get_joliet_level(self.cd) + + def get_last_session(self): + """get_last_session(self) -> int + Get the LSN of the first track of the last session of on the CD. + An exception is thrown on error.""" + drc, session = pycdio.get_last_session(self.cd) + __possibly_raise_exception__(drc) + return session + + def get_last_track(self): + """ + get_last_track(self)->Track + + return a Track object of the first track. None is returned + if there was a problem. + """ + track = pycdio.get_last_track_num(self.cd) + if track == pycdio.INVALID_TRACK: + return None + return Track(self.cd, track) + + def get_mcn(self): + """ + get_mcn(self) -> str + + Get the media catalog number (MCN) from the CD. + """ + return pycdio.get_mcn(self.cd) + + def get_media_changed(self): + """ + get_media_changed(self) -> bool + + Find out if media has changed since the last call. + Return True if media has changed since last call. An exception + Error is given on error. + """ + drc = pycdio.get_media_changed(self.cd) + if drc == 0: return False + if drc == 1: return True + __possibly_raise_exception__(drc) + raise DeviceException('Unknown return value %d' % drc) + + def get_num_tracks(self): + """ + get_num_tracks(self)->int + + Return the number of tracks on the CD. + A TrackError or IOError exception may be raised on error. + """ + track = pycdio.get_num_tracks(self.cd) + if track == pycdio.INVALID_TRACK: + raise TrackError('Invalid track returned') + return track + + def get_track(self, track_num): + """ + get_track(self, track_num)->track + + Return a track object for the given track number. + """ + return Track(self.cd, track_num) + + def get_track_for_lsn(self, lsn): + """ + get_track_for_lsn(self, lsn)->Track + + Find the track which contains lsn. + None is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track, + A track object with a 0 track is returned. + Otherwise we return the track that spans the lsn. + """ + track = pycdio.get_last_track_num(self.cd) + if track == pycdio.INVALID_TRACK: + return None + return Track(self.cd, track) + + def have_ATAPI(self): + """have_ATAPI(self)->bool + return True if CD-ROM understand ATAPI commands.""" + return pycdio.have_ATAPI(self.cd) + + def lseek(self, offset, whence): + """ + lseek(self, offset, whence)->int + Reposition read offset + Similar to (if not the same as) libc's fseek() + + cdio is object to get adjested, offset is amount to seek and + whence is like corresponding parameter in libc's lseek, e.g. + it should be SEEK_SET or SEEK_END. + + the offset is returned or -1 on error. + """ + return pycdio.lseek(self.cd, offset, whence) + + def open(self, source=None, driver_id=pycdio.DRIVER_UNKNOWN, + access_mode=None): + """ + open(self, source=None, driver_id=pycdio.DRIVER_UNKNOWN, + access_mode=None) + + Sets up to read from place specified by source, driver_id and + access mode. This should be called before using any other routine + except those that act on a CD-ROM drive by name. + + If None is given as the source, we'll use the default driver device. + If None is given as the driver_id, we'll find a suitable device driver. + + If device object was, previously opened it is closed first. + + Device is opened so that subsequent operations can be performed. + + """ + if driver_id is None: driver_id=pycdio.DRIVER_UNKNOWN + if self.cd is not None: + self.close() + self.cd = pycdio.open_cd(source, driver_id, access_mode) + + def read(self, size): + """ + read(self, size)->[size, data] + + Reads the next size bytes. + Similar to (if not the same as) libc's read() + + The number of bytes read and the data is returned. + A DeviceError exception may be raised. + """ + size, data = pycdio.read_cd(self.cd, size) + __possibly_raise_exception__(size) + return [size, data] + + def read_data_blocks(self, lsn, blocks=1): + """ + read_data_blocks(blocks, lsn, blocks=1)->[size, data] + + Reads a number of data sectors (AKA blocks). + + lsn is sector to read, bytes is the number of bytes. + A DeviceError exception may be raised. + """ + size = pycdio.ISO_BLOCKSIZE*blocks + size, data = pycdio.read_data_bytes(self.cd, size, lsn, + pycdio.ISO_BLOCKSIZE) + if size < 0: + __possibly_raise_exception__(size) + return [size, data] + + def read_sectors(self, lsn, read_mode, blocks=1): + """ + read_sectors(self, lsn, read_mode, blocks=1)->[blocks, data] + Reads a number of sectors (AKA blocks). + + lsn is sector to read, bytes is the number of bytes. + + If read_mode is pycdio.MODE_AUDIO, the return buffer size will be + truncated to multiple of pycdio.CDIO_FRAMESIZE_RAW i_blocks bytes. + + If read_mode is pycdio.MODE_DATA, buffer will be truncated to a + multiple of pycdio.ISO_BLOCKSIZE, pycdio.M1RAW_SECTOR_SIZE or + pycdio.M2F2_SECTOR_SIZE bytes depending on what mode the data is in. + + If read_mode is pycdio.MODE_M2F1, buffer will be truncated to a + multiple of pycdio.M2RAW_SECTOR_SIZE bytes. + + If read_mode is pycdio.MODE_M2F2, the return buffer size will be + truncated to a multiple of pycdio.CD_FRAMESIZE bytes. + + The number of bytes read and the data is returned. + A DeviceError exception may be raised. + """ + try: + blocksize = read_mode2blocksize[read_mode] + size = blocks * blocksize + except KeyError: + raise DriverBadParameterError ('Bad read mode %d' % read_mode) + size, data = pycdio.read_sectors(self.cd, size, lsn, read_mode) + if size < 0: + __possibly_raise_exception__(size) + blocks = size / blocksize + return [blocks, data] + + def set_blocksize(self, blocksize): + """set_blocksize(self, blocksize) + Set the blocksize for subsequent reads. + An exception is thrown on error. + """ + drc = pycdio.set_blocksize(self.cd, blocksize) + __possibly_raise_exception__(drc) + + def set_speed(self, speed): + """set_speed(self, speed) + Set the drive speed. An exception is thrown on error.""" + drc = pycdio.set_speed(self.cd, speed) + __possibly_raise_exception__(drc) + +class Track: + """CD Input and control track class""" + + def __init__(self, device, track_num): + + if type(track_num) != types.IntType: + raise TrackError('track number parameter is not an integer') + self.track = track_num + + # See if the device parameter is a string or + # a device object. + if type(device) == types.StringType: + self.device = Device(device) + else: + test_device=Device() + ## FIXME: would like a way to test if device + ## is a PySwigObject + self.device = device + + def get_audio_channels(self): + """ + get_audio_channels(self, track)->int + + Return number of channels in track: 2 or 4 + Not meaningful if track is not an audio track. + An exception can be raised on error. + """ + channels = pycdio.get_track_channels(self.device, self.track) + if -2 == channels: + raise DriverUnsupportedError + elif -1 == channels: + raise TrackError + else: + return channels + + def get_copy_permit(self): + """ + get_copy_permit(self, track)->int + + Return copy protection status on a track. Is this meaningful + not an audio track? + + """ + if pycdio.get_track_copy_permit(self.device, self.track): + return "OK" + else: + return "no" + + def get_format(self): + """ + get_format(self)->format + + Get the format (e.g. 'audio', 'mode2', 'mode1') of track. + """ + return pycdio.get_track_format(self.device, self.track) + + def get_last_lsn(self): + """ + get_last_lsn(self)->lsn + + Return the ending LSN for a track + A TrackError or IOError exception may be raised on error. + """ + lsn = pycdio.get_track_last_lsn(self.device, self.track) + if lsn == pycdio.INVALID_LSN: + raise TrackError('Invalid LSN returned') + return lsn + + def get_lba(self): + """ + get_lsn(self)->lba + + Return the starting LBA for a track + A TrackError exception is raised on error. + """ + lba = pycdio.get_track_lba(self.device, self.track) + if lba == pycdio.INVALID_LBA: + raise TrackError('Invalid LBA returned') + return lba + + def get_lsn(self): + """ + get_lsn(self)->lsn + + Return the starting LSN for a track + A TrackError exception is raised on error. + """ + lsn = pycdio.get_track_lsn(self.device, self.track) + if lsn == pycdio.INVALID_LSN: + raise TrackError('Invalid LSN returned') + return lsn + + def get_msf(self): + """ + get_msf(self)->str + + Return the starting MSF (minutes/secs/frames) for track number track. + Track numbers usually start at something greater than 0, usually 1. + + Returns string of the form mm:ss:ff if all good, or string None on + error. + """ + return pycdio.get_track_msf(self.device, self.track) + + def get_preemphasis(self): + """ + get_preemphaisis(self)->result + + Get linear preemphasis status on an audio track. + This is not meaningful if not an audio track? + A TrackError exception is raised on error. + """ + rc = pycdio.get_track_preemphasis(self.device, self.track) + if rc == pycdio.TRACK_FLAG_FALSE: + return 'none' + elif rc == pycdio.TRACK_FLAG_TRUE: + return 'preemphasis' + elif rc == pycdio.TRACK_FLAG_UNKNOWN: + return 'unknown' + else: + raise TrackError('Invalid return value %d' % d) + + def get_track_sec_count(self): + """ + get_track_sec_count(self)->int + + Get the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Track numbers usually start at something + greater than 0, usually 1. + + A TrackError exception is raised on error. + """ + sec_count = pycdio.get_track_sec_count(self.device, self.track) + if sec_count == 0: + raise TrackError + return sec_count + + def is_green(self): + """ + is_track_green(self, track) -> bool + + Return True if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + """ + return pycdio.is_track_green(self.device, self.track) + + def set_track(self, track_num): + """ + set_track(self, track_num) + + Set a new track number. + """ + self.track = track_num + + +# +# Local variables: +# mode: Python +# End: diff --git a/data/copying.iso b/data/copying.iso new file mode 100644 index 00000000..b519e7fa Binary files /dev/null and b/data/copying.iso differ diff --git a/data/isofs-m1.bin b/data/isofs-m1.bin new file mode 100644 index 00000000..adaea5c3 Binary files /dev/null and b/data/isofs-m1.bin differ diff --git a/data/isofs-m1.cue b/data/isofs-m1.cue new file mode 100644 index 00000000..677a803f --- /dev/null +++ b/data/isofs-m1.cue @@ -0,0 +1,3 @@ +FILE "ISOFS-M1.BIN" BINARY + TRACK 01 MODE1/2352 + INDEX 01 00:00:00 diff --git a/example/audio.py b/example/audio.py new file mode 100755 index 00000000..2aeab6a4 --- /dev/null +++ b/example/audio.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +"""A program to show use of audio controls. See cdda-player from the +libcdio distribution for a more complete program. +""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys, os +from optparse import OptionParser + +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +def process_options(): + program = os.path.basename(sys.argv[0]) + usage_str="""%s [options] [device] + Issue analog audio CD controls - like playing""" % program + + optparser = OptionParser(usage=usage_str) + + optparser.add_option("-c", "--close", dest="close", + action="store", type='string', + help="close CD tray") + optparser.add_option("-p", "--play", dest="play", + action="store_true", default=False, + help="Play entire CD") + optparser.add_option("-P", "--pause", dest="pause", + action="store_true", default=False, + help="pause playing") + optparser.add_option("-E", "--eject", dest="eject", + action="store_true", default=False, + help="Eject CD") + optparser.add_option("-r", "--resume", dest="resume", + action="store_true", default=False, + help="resume playing") + optparser.add_option("-s", "--stop", dest="stop", + action="store_true", default=False, + help="stop playing") + optparser.add_option("-t", "--track", dest="track", + action="store", type='int', + help="play a single track") + return optparser.parse_args() + +opts, argv = process_options() + +# Handle closing the CD-ROM tray if that was specified. +if opts.close: + try: + device_name = opts.close + cdio.close_tray(device_name) + except cdio.DeviceException: + print "Closing tray of CD-ROM drive %s failed" % device_name + + +# While sys.argv[0] is a program name and sys.argv[1] the first +# option, argv[0] is the first unprocessed option -- roughly +# the equivalent of sys.argv[1]. +if argv[0:]: + try: + d = cdio.Device(argv[0]) + except IOError: + print "Problem opening CD-ROM: %s" % device_name + sys.exit(1) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +device_name=d.get_device() +if opts.play: + if d.get_disc_mode() != 'CD-DA': + print "The disc on %s I found was not CD-DA, but %s" \ + % (device_name, d.get_disc_mode()) + print "I have bad feeling about trying to play this..." + + try: + start_lsn = d.get_first_track().get_lsn() + end_lsn=d.get_disc_last_lsn() + print "Playing entire CD on %s" % device_name + d.audio_play_lsn(start_lsn, end_lsn) + except cdio.TrackError: + pass + +elif opts.track is not None: + try: + if opts.track > d.get_last_track().track: + print "Requested track %d but CD only has %d tracks" \ + % (opts.track, d.get_last_track().track) + sys.exit(2) + if opts.track < d.get_first_track().track: + print "Requested track %d but first track on CD is %d" \ + % (opts.track, d.get_first_track().track) + sys.exit(2) + print "Playing track %d on %s" % (opts.track, device_name) + start_lsn = d.get_track(opts.track).get_lsn() + end_lsn = d.get_track(opts.track+1).get_lsn() + d.audio_play_lsn(start_lsn, end_lsn) + except cdio.TrackError: + pass + +elif opts.pause: + try: + print "Pausing playing in drive %s" % device_name + d.audio_pause() + except cdio.DeviceException: + print "Pause failed on drive %s" % device_name +elif opts.resume: + try: + print "Resuming playing in drive %s" % device_name + d.audio_resume() + except cdio.DeviceException: + print "Resume failed on drive %s" % device_name +elif opts.stop: + try: + print "Stopping playing in drive %s" % device_name + d.audio_stop() + except cdio.DeviceException: + print "Stopping failed on drive %s" % device_name +elif opts.eject: + try: + print "Ejecting CD in drive %s" % device_name + d.eject_media() + except cdio.DriverUnsupportedError: + print "Eject not supported for %s" % device_name + except cdio.DeviceException: + print "Eject of CD-ROM drive %s failed" % device_name + +d.close() diff --git a/example/cd-read.py b/example/cd-read.py new file mode 100755 index 00000000..c195f5e1 --- /dev/null +++ b/example/cd-read.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +"""Program to read CD blocks. See read-cd from the libcdio distribution +for a more complete program. +""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys, os +from optparse import OptionParser + +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +read_modes = { + 'audio': pycdio.READ_MODE_AUDIO, + 'm1f1' : pycdio.READ_MODE_M1F1, + 'm1f2' : pycdio.READ_MODE_M1F2, + 'm2f1' : pycdio.READ_MODE_M2F1, + 'm2f2' : pycdio.READ_MODE_M2F2, + 'data' : None + } + +def process_options(): + program = os.path.basename(sys.argv[0]) + usage_str="""%s --mode *mode* [options] [device] + Read blocks of a CD""" % program + + optparser = OptionParser(usage=usage_str) + + optparser.add_option("-m", "--mode", dest="mode", + action="store", type='string', + help="CD Reading mode: audio, m1f1, m1f2, " + + "m2f1 or m2f2") + optparser.add_option("-s", "--start", dest="start", + action="store", type='int', default=1, + help="Starting block") + optparser.add_option("-n", "--number", dest="number", + action="store", type='int', default=1, + help="Number of blocks") + (opts, argv) = optparser.parse_args() + if opts.mode is None: + print "Mode option must given " + \ + "(and one of audio, m1f1, m1f2, m1f2 or m1f2)." + sys.exit(1) + try: + read_mode = read_modes[opts.mode] + except KeyError: + print "Need to use the --mode option with one of" + \ + "audio, m1f1, m1f2, m1f2 or m1f2" + sys.exit(2) + + return opts, argv, read_mode + +import re +PRINTABLE = r'[ -~]' +pat = re.compile(PRINTABLE) +def isprint(c): + global pat + return pat.match(c) + +def hexdump (buffer, just_hex=False): + i = 0 + while i < len(buffer): + if (i % 16) == 0: + print ("0x%04x: " % i), + print "%02x%02x" % (ord(buffer[i]), ord(buffer[i+1])), + if (i % 16) == 14: + if not just_hex: + s = " " + for j in range(i-14, i+2): + if isprint(buffer[j]): + s += buffer[j] + else: + s += '.' + print s, + print + i += 2 + print + return + +opts, argv, read_mode = process_options() +# While sys.argv[0] is a program name and sys.argv[1] the first +# option, argv[0] is the first unprocessed option -- roughly +# the equivalent of sys.argv[1]. +if argv[0:]: + try: + d = cdio.Device(argv[0]) + except IOError: + print "Problem opening CD-ROM: %s" % argv[0] + sys.exit(1) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +## All this setup just to issue this one of these commands. +if read_mode == None: + blocks, data=d.read_data_blocks(opts.start, opts.number) +else: + blocks, data=d.read_sectors(opts.start, read_mode, opts.number) +hexdump(data) + diff --git a/example/cdchange.py b/example/cdchange.py new file mode 100755 index 00000000..0fcfee9d --- /dev/null +++ b/example/cdchange.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +"""Program to show CD media changing""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import sys, time +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio +from time import sleep + +sleep_time = 15 +if sys.argv[1:]: + try: + d = cdio.Device(sys.argv[1]) + except IOError: + print "Problem opening CD-ROM: %s" % sys.argv[1] + sys.exit(1) + if sys.argv[2:]: + try: + sleep_time = int(sys.argv[2]) + except ValueError, msg: + print "Invalid sleep parameter %s" % sys.argv[2] + sys.exit(2) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +if d.get_media_changed(): + print "Initial media status: changed" +else: + print "Initial media status: not changed" + +print "Giving you %lu seconds to change CD if you want to do so." % sleep_time +sleep(sleep_time) +if d.get_media_changed(): + print "Media status: changed" +else: + print "Media status: not changed" +d.close() diff --git a/example/device.py b/example/device.py new file mode 100755 index 00000000..5e89c972 --- /dev/null +++ b/example/device.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +"""Program to show CD-ROM device information""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +def sort_dict_keys(dict): + """Return sorted keys of a dictionary. + There's probably an easier way to do this that I'm not aware of.""" + keys=dict.keys() + keys.sort() + return keys + +if sys.argv[1:]: + try: + drive_name = sys.argv[1] + d = cdio.Device(sys.argv[1]) + except IOError: + print "Problem opening CD-ROM: %s" % drive_name + sys.exit(1) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + drive_name = d.get_device() + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +# Should there should be no "ok"? +ok, vendor, model, release = d.get_hwinfo() + +print "drive: %s, vendor: %s, model: %s, release: %s" \ + % (drive_name, vendor, model, release) + +read_cap, write_cap, misc_cap = d.get_drive_cap() +print "Drive Capabilities for %s..." % drive_name + +print "\n".join(cap for cap in sort_dict_keys(read_cap) + + sort_dict_keys(write_cap) + sort_dict_keys(misc_cap)) + +print "\nDriver Availabiliity..." +for driver_name in sort_dict_keys(cdio.drivers): + try: + if cdio.have_driver(driver_name): + print "Driver %s is installed." % driver_name + except ValueError: + pass +d.close() diff --git a/example/drives.py b/example/drives.py new file mode 100755 index 00000000..294e6264 --- /dev/null +++ b/example/drives.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# $Id: drives.py,v 1.2 2008/11/24 00:53:59 rocky Exp $ +"""Program to read CD blocks. See read-cd from the libcdio distribution +for a more complete program.""" + +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +def print_drive_class(msg, bitmask, any): + cd_drives = cdio.get_devices_with_cap(bitmask, any) + + print "%s..." % msg + for drive in cd_drives: + print "Drive %s" % drive + print "-----" + +cd_drives = cdio.get_devices(pycdio.DRIVER_DEVICE) +for drive in cd_drives: + print "Drive %s" % drive + +print "-----" + +sys.exit(0) +# FIXME: there's a bug in get_drive_with_cap that corrupts memory. +print_drive_class("All CD-ROM drives (again)", pycdio.FS_MATCH_ALL, False); +print_drive_class("All CD-DA drives...", pycdio.FS_AUDIO, False); +print_drive_class("All drives with ISO 9660...", pycdio.FS_ISO_9660, False); +print_drive_class("VCD drives...", + (pycdio.FS_ANAL_SVCD|pycdio.FS_ANAL_CVD| + pycdio.FS_ANAL_VIDEOCD|pycdio.FS_UNKNOWN), True); + + + diff --git a/example/eject.py b/example/eject.py new file mode 100755 index 00000000..85e1d65f --- /dev/null +++ b/example/eject.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +"""Program to Eject and close CD-ROM drive""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +if sys.argv[1:]: + try: + drive_name=sys.argv[1] + d = cdio.Device(drive_name) + except IOError: + print "Problem opening CD-ROM: %s" % drive_name + sys.exit(1) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + drive_name = d.get_device() + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +try: + + print "Ejecting CD in drive %s" % drive_name + d.eject_media() + try: + cdio.close_tray(drive_name) + print "Closed tray of CD-ROM drive %s" % drive_name + except cdio.DeviceException: + print "Closing tray of CD-ROM drive %s failed" % drive_name + +except cdio.DriverUnsupportedError: + print "Eject not supported for %s" % drive_name +except cdio.DeviceException: + print "Eject of CD-ROM drive %s failed" % drive_name + + + + diff --git a/example/iso1.py b/example/iso1.py new file mode 100755 index 00000000..d2846987 --- /dev/null +++ b/example/iso1.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +""" A simple program to show using libiso9660 to list files in a directory of + an ISO-9660 image. + + If a single argument is given, it is used as the ISO 9660 image to + use in the listing. Otherwise a compiled-in default ISO 9660 image + name (that comes with the libcdio distribution) will be used.""" + +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import iso9660 + +# The default ISO 9660 image if none given +ISO9660_IMAGE_PATH="../data" +ISO9660_IMAGE=os.path.join(ISO9660_IMAGE_PATH, "copying.iso") + +if len(sys.argv) > 1: + iso_image_fname = sys.argv[1] +else: + iso_image_fname = ISO9660_IMAGE + +iso = iso9660.ISO9660.IFS(source=iso_image_fname) + +if not iso.is_open(): + print "Sorry, couldn't open %s as an ISO-9660 image." % iso_image_fname + sys.exit(1) + +path = '/' + +file_stats = iso.readdir(path) + +id = iso.get_application_id() +if id is not None: print "Application ID: %s" % id + +id = iso.get_preparer_id() +if id is not None: print "Preparer ID: %s" % id + +id = iso.get_publisher_id() +if id is not None: print "Publisher ID: %s" % id + +id = iso.get_system_id() +if id is not None: print "System ID: %s" % id + +id = iso.get_volume_id() +if id is not None: print "Volume ID: %s" % id + +id = iso.get_volumeset_id() +if id is not None: print "Volumeset ID: %s" % id + +dir_tr=['-', 'd'] + +for stat in file_stats: + # FIXME + filename = stat[0] + LSN = stat[1] + size = stat[2] + sec_size = stat[3] + is_dir = stat[4] == 2 + print "%s [LSN %6d] %8d %s%s" % (dir_tr[is_dir], LSN, size, path, + iso9660.name_translate(filename)) +iso.close() +sys.exit(0) + + diff --git a/example/iso2.py b/example/iso2.py new file mode 100755 index 00000000..3fa65856 --- /dev/null +++ b/example/iso2.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +"""A program to show using iso9660 to extract a file from an ISO-9660 +image. + +If a single argument is given, it is used as the ISO 9660 image to use +in the extraction. Otherwise a compiled in default ISO 9660 image name +(that comes with the libcdio distribution) will be used. A program to +show using iso9660 to extract a file from an ISO-9660 image.""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import iso9660 + +# Python has rounding (round) and trucation (int), but what about an integer +# ceiling function? Until I learn what it is... +def ceil(x): + return int(round(x+0.5)) + +# The default CD image if none given +cd_image_path="../data" +cd_image_fname=os.path.join(cd_image_path, "isofs-m1.cue") + +# File to extract if none given. +iso9660_path="/" +local_filename="COPYING" + +if len(sys.argv) > 1: + cd_image_fname = sys.argv[1] + if len(sys.argv) > 2: + local_filename = sys.argv[1] + if len(sys.argv) > 3: + print """ +usage: %s [CD-ROM-or-image [filename]] +Extracts filename from CD-ROM-or-image. +""" % sys.argv[0] + sys.exit(1) + +try: + cd = iso9660.ISO9660.FS(source=cd_image_fname) +except: + print "Sorry, couldn't open %s as a CD image." % cd_image_fname + sys.exit(1) + +statbuf = cd.stat (local_filename, False) + +if statbuf is None: + print "Could not get ISO-9660 file information for file %s in %s" \ + % (local_filename, cd_image_fname) + cd.close() + sys.exit(2) + +try: + OUTPUT=os.open(local_filename, os.O_CREAT|os.O_WRONLY, 0664) +except: + print "Can't open %s for writing" % local_filename + +# Copy the blocks from the ISO-9660 filesystem to the local filesystem. +blocks = ceil(statbuf['size'] / pycdio.ISO_BLOCKSIZE) +for i in range(blocks): + lsn = statbuf['LSN'] + i + size, buf = cd.read_data_blocks(lsn) + + if size < 0: + print "Error reading ISO 9660 file %s at LSN %d" % ( + local_filename, lsn) + sys.exit(4) + + os.write(OUTPUT, buf) + + +# Make sure the file size has the exact same byte size. Without the +# truncate below, the file will a multiple of ISO_BLOCKSIZE. + +os.ftruncate(OUTPUT, statbuf['size']) + +print "Extraction of file '%s' from %s successful." % ( + local_filename, cd_image_fname) + +os.close(OUTPUT) +cd.close() +sys.exit(0) diff --git a/example/iso3.py b/example/iso3.py new file mode 100755 index 00000000..b57baaad --- /dev/null +++ b/example/iso3.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +"""A program to show using iso9660 to extract a file from an ISO-9660 +image. If a single argument is given, it is used as the ISO 9660 +image to use in the extraction. Otherwise a compiled in default ISO +9660 image name (that comes with the libcdio distribution) will be +used.""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import iso9660 + +# Python has rounding (round) and truncation (int), but what about an integer +# ceiling function? Until I learn what it is... +def ceil(x): + return int(round(x+0.5)) + +# The default ISO 9660 image if none given +ISO9660_IMAGE_PATH="../data" +ISO9660_IMAGE=os.path.join(ISO9660_IMAGE_PATH, "copying.iso") + +# File to extract if none given. +local_filename="copying" + +iso_image_fname = ISO9660_IMAGE + +if len(sys.argv) > 1: + iso_image_fname = sys.argv[1] + if len(sys.argv) > 2: + local_filename = sys.argv[1] + if len(sys.argv) > 3: + print """ +usage: %s [ISO9660-image.ISO [filename]] +Extracts filename from ISO9660-image.ISO. +""" % sys.argv[0] + sys.exit(1) + +iso = iso9660.ISO9660.IFS(source=iso_image_fname) + +if not iso.is_open(): + print "Sorry, couldn't open %s as an ISO-9660 image." % iso_image_fname + sys.exit(1) + + +statbuf = iso.stat (local_filename, True) + +if statbuf is None: + print "Could not get ISO-9660 file information for file %s" \ + % local_filename + iso.close() + sys.exit(2) + +try: + OUTPUT=os.open(local_filename, os.O_CREAT|os.O_WRONLY, 0664) +except: + print "Can't open %s for writing" % local_filename + +# Copy the blocks from the ISO-9660 filesystem to the local filesystem. +blocks = ceil(statbuf['size'] / pycdio.ISO_BLOCKSIZE) +for i in range(blocks): + lsn = statbuf['LSN'] + i + size, buf = iso.seek_read (lsn) + + if size <= 0: + print "Error reading ISO 9660 file %s at LSN %d" % ( + local_filename, lsn) + sys.exit(4) + + os.write(OUTPUT, buf) + + +# Make sure the file size has the exact same byte size. Without the +# truncate below, the file will a multiple of ISO_BLOCKSIZE. + +os.ftruncate(OUTPUT, statbuf['size']) + +print "Extraction of file '%s' from %s successful." % ( + local_filename, iso_image_fname) + +os.close(OUTPUT) +iso.close() +sys.exit(0) diff --git a/example/tracks.py b/example/tracks.py new file mode 100755 index 00000000..c4aaea3d --- /dev/null +++ b/example/tracks.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +"""A program to show CD information.""" +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os, sys +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +if sys.argv[1:]: + try: + d = cdio.Device(sys.argv[1]) + except IOError: + print "Problem opening CD-ROM: %s" % sys.argv[1] + sys.exit(1) +else: + try: + d = cdio.Device(driver_id=pycdio.DRIVER_UNKNOWN) + except IOError: + print "Problem finding a CD-ROM" + sys.exit(1) + +t = d.get_first_track() +if t is None: + print "Problem getting first track" + sys.exit(2) + +first_track = t.track +num_tracks = d.get_num_tracks() +last_track = first_track+num_tracks-1 + +try: + last_session = d.get_last_session() + print "CD-ROM %s has %d track(s) and %d session(s)." % \ + (d.get_device(), d.get_num_tracks(), last_session) +except cdio.DriverUnsupportedError: + print "CD-ROM %s has %d track(s). " % (d.get_device(), d.get_num_tracks()) + +print "Track format is %s." % d.get_disc_mode() + +mcn = d.get_mcn() +if mcn: + print "Media Catalog Number: %s" % mcn + +print "%3s: %-6s %s" % ("#", "LSN", "Format") +i=first_track +while (i <= last_track): + try: + t = d.get_track(i) + print "%3d: %06lu %-6s %s" % (t.track, t.get_lsn(), + t.get_msf(), t.get_format()) + except cdio.TrackError: + pass + i += 1 + +print "%3X: %06lu leadout" \ + % (pycdio.CDROM_LEADOUT_TRACK, d.get_disc_last_lsn()) +d.close() diff --git a/iso9660.py b/iso9660.py new file mode 100644 index 00000000..d3f436ba --- /dev/null +++ b/iso9660.py @@ -0,0 +1,502 @@ +#!/usr/bin/python +# +# $Id: iso9660.py,v 1.12 2008/05/01 16:55:03 karl Exp $ +# +# Copyright (C) 2006, 2008 Rocky Bernstein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pyiso9660 +import cdio +import types + +check_types = { + 'nocheck' : pyiso9660.NOCHECK, + '7bit' : pyiso9660.SEVEN_BIT, + 'achars' : pyiso9660.ACHARS, + 'dchars' : pyiso9660.DCHARS + } + +def dirname_valid_p(path): + """dirname_valid_p(path)->bool + +Check that path is a valid ISO-9660 directory name. + +A valid directory name should not start out with a slash (/), +dot (.) or null byte, should be less than 37 characters long, +have no more than 8 characters in a directory component +which is separated by a /, and consist of only DCHARs. + +True is returned if path is valid.""" + return pyiso9660.dirname_valid(path) + + +def is_achar(achar): + """is_achar(achar)->bool + +Return 1 if achar is an ACHAR. achar should either be a string of +length one or the ord() of a string of length 1. + +These are the DCHAR's plus some ASCII symbols including the space +symbol.""" + if type(achar) != types.IntType: + # Not integer. Should be a string of length one then. + # We'll turn it into an integer. + try: + achar = ord(achar) + except: + return 0 + else: + # Is an integer. Is it too large? + if achar > 255: return 0 + return pyiso9660.is_achar(achar) + + +def is_dchar(dchar): + """is_dchar(dchar)->bool + +Return 1 if dchar is a DCHAR - a character that can appear in an an +ISO-9600 level 1 directory name. These are the ASCII capital +letters A-Z, the digits 0-9 and an underscore. + +dchar should either be a string of length one or the ord() of a string +of length 1.""" + + if type(dchar) != types.IntType: + # Not integer. Should be a string of length one then. + # We'll turn it into an integer. + try: + dchar = ord(dchar) + except: + return 0 + else: + # Is an integer. Is it too large? + if dchar > 255: return 0 + return pyiso9660.is_dchar(dchar) + + +def pathname_valid_p(path): + """pathname_valid_p(path)->bool + +Check that path is a valid ISO-9660 pathname. + +A valid pathname contains a valid directory name, if one appears and +the filename portion should be no more than 8 characters for the +file prefix and 3 characters in the extension (or portion after a +dot). There should be exactly one dot somewhere in the filename +portion and the filename should be composed of only DCHARs. + +True is returned if path is valid.""" + return pyiso9660.pathame_valid(path) + + +def pathname_isofy(path, version=1): + """pathname_valid_p(path, version=1)->string + +Take path and a version number and turn that into a ISO-9660 pathname. +(That's just the pathname followed by ';' and the version number. For +example, mydir/file.ext -> MYDIR/FILE.EXT;1 for version 1. The +resulting ISO-9660 pathname is returned.""" + return pyiso9660.pathname_isofy(path, version) + +def name_translate(filename, joliet_level=0): + """name_translate(name, joliet_level=0)->str + +Convert an ISO-9660 file name of the kind that is that stored in a ISO +9660 directory entry into what's usually listed as the file name in a +listing. Lowercase name if no Joliet Extension interpretation. Remove +trailing ;1's or .;1's and turn the other ;'s into version numbers. + +If joliet_level is not given it is 0 which means use no Joliet +Extensions. Otherwise use the specified the Joliet level. + +The translated string is returned and it will be larger than the input +filename.""" + return pyiso9660.name_translate_ext(filename, joliet_level) + + +# FIXME: should be +# def stat_array_to_dict(*args): +# Probably have a SWIG error. +def stat_array_to_dict(filename, LSN, size, sec_size, is_dir): + """stat_array_to_href(filename, LSN, size, sec_size, is_dir)->stat + +Convert a ISO 9660 array to an hash reference of the values. + +Used internally in convert from C code.""" + + stat = {} + stat['filename'] = filename + stat['LSN'] = LSN + stat['size'] = size + stat['sec_size'] = sec_size + stat['is_dir'] = is_dir == 2 + return stat + +def strncpy_pad(name, len, check): + """strncpy_pad(src, len, check='nocheck')->str + +Pad string 'name' with spaces to size len and return this. If 'len' is +less than the length of 'src', the return value will be truncated to +the first len characters of 'name'. + +'name' can also be scanned to see if it contains only ACHARs, DCHARs, +or 7-bit ASCII chars, and this is specified via the 'check' parameter. +If the I parameter is given it must be one of the 'nocheck', +'7bit', 'achars' or 'dchars'. Case is not significant.""" + if check not in check_types: + print "*** A CHECK parameter must be one of %s\n" % \ + ', '.join(check_types.keys()) + return None + return pyiso9660.strncpy_pad(name, len, check_types[check]) + +class ISO9660: + """ """ + class IFS: + """ISO 9660 Filesystem image reading""" + + def __init__(self, source=None, iso_mask=pyiso9660.EXTENSION_NONE): + + """Create a new ISO 9660 object. If source is given, open() + is called using that and the optional iso_mask parameter; + iso_mask is used only if source is specified. If source is + given but opening fails, undef is returned. If source is not + given, an object is always returned.""" + + self.iso9660 = None + if source is not None: + self.open(source, iso_mask) + + def close(self): + """close(self)->bool + + Close previously opened ISO 9660 image and free resources + associated with ISO9660. Call this when done using using + an ISO 9660 image.""" + + if self.iso9660 is not None: + pyiso9660.close(self.iso9660) + else: + print "***No object to close" + self.iso9660 = None + + def find_lsn(self, lsn): + """find_lsn(self, lsn)->[stat_href] + + Find the filesystem entry that contains LSN and return + file stat information about it. None is returned on + error.""" + + if pycdio.VERSION_NUM <= 76: + print "*** Routine available only in libcdio versions >= 0.76" + return None + + filename, LSN, size, sec_size, is_dir = \ + pyiso9660.ifs_find_lsn(self.iso9660, lsn) + return stat_array_to_href(filename, LSN, size, sec_size, is_dir) + + + def get_application_id(self): + """get_application_id(self)->id + + Get the application ID stored in the Primary Volume Descriptor. + None is returned if there is some problem.""" + + return pyiso9660.ifs_get_application_id(self.iso9660) + + def get_preparer_id(self): + """get_preparer_id(self)->id + + Get the preparer ID stored in the Primary Volume Descriptor. + None is returned if there is some problem.""" + + return pyiso9660.ifs_get_preparer_id(self.iso9660) + + + def get_publisher_id(self): + """get_publisher_id()->id + + Get the publisher ID stored in the Primary Volume Descriptor. + undef is returned if there is some problem.""" + return pyiso9660.ifs_get_publisher_id(self.iso9660) + + def get_root_lsn(self): + """get_root_lsn(self)->lsn + + Get the Root LSN stored in the Primary Volume Descriptor. + undef is returned if there is some problem.""" + + return pyiso9660.ifs_get_root_lsn(self.iso9660) + + def get_system_id(self): + """get_system_id(self)->id + + Get the Volume ID stored in the Primary Volume Descriptor. + undef is returned if there is some problem.""" + + return pyiso9660.ifs_get_system_id(self.iso9660) + + def get_volume_id(self): + """get_volume_id()->id + + Get the Volume ID stored in the Primary Volume Descriptor. + undef is returned if there is some problem.""" + + return pyiso9660.ifs_get_volume_id(self.iso9660) + + def get_volumeset_id(self): + """get_volume_id(self)->id + + Get the Volume ID stored in the Primary Volume Descriptor. + undef is returned if there is some problem.""" + + return pyiso9660.ifs_get_volumeset_id(self.iso9660) + + def is_open(self): + """is_open(self)->bool + Return true if we have an ISO9660 image open. + """ + return self.iso9660 is not None + + def open(self, source, iso_mask=pyiso9660.EXTENSION_NONE): + """open(source, iso_mask=pyiso9660.EXTENSION_NONE)->bool + + Open an ISO 9660 image for reading. Subsequent operations + will read from this ISO 9660 image. + + This should be called before using any other routine + except possibly new. It is implicitly called when a new is + done specifying a source. + + If device object was previously opened it is closed first. + + See also open_fuzzy.""" + + if self.iso9660 is not None: self.close() + + self.iso9660 = pyiso9660.open_ext(source, iso_mask) + return self.iso9660 is not None + + + def open_fuzzy(self, source, iso_mask=pyiso9660.EXTENSION_NONE, + fuzz=20): + """open_fuzzy(source, iso_mask=pyiso9660.EXTENSION_NONE, + fuzz=20)->bool + + Open an ISO 9660 image for reading. Subsequent operations + will read from this ISO 9660 image. Some tolerence allowed + for positioning the ISO9660 image. We scan for + pyiso9660.STANDARD_ID and use that to set the eventual + offset to adjust by (as long as that is <= fuzz). + + This should be called before using any other routine + except possibly new (which must be called first. It is + implicitly called when a new is done specifying a source. + + See also open.""" + + if self.iso9660 is not None: self.close() + + if type(fuzz) != types.IntType: + print "*** Expecting fuzz to be an integer; got 'fuzz'" + return False + + self.iso9660 = pyiso9660.open_fuzzy_ext(source, iso_mask, fuzz) + return self.iso9660 is not None + + def read_fuzzy_superblock(self, iso_mask=pyiso9660.EXTENSION_NONE, + fuzz=20): + """read_fuzzy_superblock(iso_mask=pyiso9660.EXTENSION_NONE, + fuzz=20)->bool + + Read the Super block of an ISO 9660 image but determine + framesize and datastart and a possible additional + offset. Generally here we are not reading an ISO 9660 image + but a CD-Image which contains an ISO 9660 filesystem.""" + + if type(fuzz) != types.IntType: + print "*** Expecting fuzz to be an integer; got 'fuzz'" + return False + + return pyiso9660.ifs_fuzzy_read_superblock(self.iso9660, iso_mask, + fuzz) + + def readdir(self, dirname): + """readdir(dirname)->[LSN, size, sec_size, filename, XA, is_dir] + + Read path (a directory) and return a list of iso9660 stat + references + + Each item of @iso_stat is a hash reference which contains + + * LSN - the Logical sector number (an integer) + * size - the total size of the file in bytes + * sec_size - the number of sectors allocated + * filename - the file name of the statbuf entry + * XA - if the file has XA attributes; 0 if not + * is_dir - 1 if a directory; 0 if a not; + + FIXME: If you look at iso9660.h you'll see more fields, such as for + Rock-Ridge specific fields or XA specific fields. Eventually these + will be added. Volunteers?""" + + return pyiso9660.ifs_readdir(self.iso9660, dirname) + + + def read_pvd(self): + """read_pvd()->pvd + + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental + Volume Descriptor if (Joliet) extensions are + acceptable.""" + + return pyiso9660.ifs_read_pvd(self.iso9660) + + def read_superblock(self, iso_mask=pyiso9660.EXTENSION_NONE): + """read_superblock(iso_mask=pyiso9660.EXTENSION_NONE)->bool + + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental + Volume Descriptor if (Joliet) extensions are + acceptable.""" + + return pyiso9660.ifs_read_superblock(self.iso9660, iso_mask) + + def seek_read(self, start, size=1): + """seek_read(self, start, size=1) + ->(size, str) + + Seek to a position and then read n blocks. A block is + pycdio.ISO_BLOCKSIZE (2048) bytes. The Size in BYTES (not blocks) + is returned.""" + size *= pyiso9660.ISO_BLOCKSIZE + return pyiso9660.seek_read(self.iso9660, start, size) + + def stat(self, path, translate=False): + """stat(self, path, translate=False)->{stat} + + Return file status for path name path. None is returned on + error. If translate is True, version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 + names are lowercased. + + Each item of the return is a hash reference which contains: + + * LSN - the Logical sector number (an integer) + * size - the total size of the file in bytes + * sec_size - the number of sectors allocated + * filename - the file name of the statbuf entry + * XA - if the file has XA attributes; False if not + * is_dir - True if a directory; False if a not.""" + + if translate: + values = pyiso9660.ifs_stat_translate(self.iso9660, path) + else: + values = pyiso9660.ifs_stat(self.iso9660, path) + return stat_array_to_dict(values[0], values[1], values[2], + values[3], values[4]) + + class FS(cdio.Device): + """ISO 9660 CD reading""" + + def __init__(self, source=None, driver_id=None, + access_mode=None): + + """Create a new ISO 9660 object. If source is given, open() + is called using that and the optional iso_mask parameter; + iso_mask is used only if source is specified. If source is + given but opening fails, undef is returned. If source is not + given, an object is always returned.""" + + cdio.Device.__init__(self, source, driver_id, access_mode) + + + def find_lsn(self, lsn): + """find_lsn(self, lsn)->[stat_href] + + Find the filesystem entry that contains LSN and return + file stat information about it. None is returned on + error.""" + + filename, LSN, size, sec_size, is_dir = \ + pyiso9660.fs_find_lsn(self.cd, lsn) + return stat_array_to_href(filename, LSN, size, sec_size, is_dir) + + + def readdir(self, dirname): + """readdir(dirname)->[LSN, size, sec_size, filename, XA, is_dir] + + Read path (a directory) and return a list of iso9660 stat + references + + Each item of @iso_stat is a hash reference which contains + + * LSN - the Logical sector number (an integer) + * size - the total size of the file in bytes + * sec_size - the number of sectors allocated + * filename - the file name of the statbuf entry + * XA - if the file has XA attributes; 0 if not + * is_dir - 1 if a directory; 0 if a not; + + FIXME: If you look at iso9660.h you'll see more fields, such as for + Rock-Ridge specific fields or XA specific fields. Eventually these + will be added. Volunteers?""" + + return pyiso9660.fs_readdir(self.cd, dirname) + + + def read_pvd(self): + """read_pvd()->pvd + + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental + Volume Descriptor if (Joliet) extensions are + acceptable.""" + + return pyiso9660.fs_read_pvd(self.cd) + + def read_superblock(self, iso_mask=pyiso9660.EXTENSION_NONE): + """read_superblock(iso_mask=pyiso9660.EXTENSION_NONE)->bool + + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental + Volume Descriptor if (Joliet) extensions are + acceptable.""" + + return pyiso9660.fs_read_superblock(self.cd, iso_mask) + + def stat(self, path, translate=False): + """stat(self, path, translate=False)->{stat} + + Return file status for path name path. None is returned on + error. If translate is True, version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 + names are lowercased. + + Each item of the return is a hash reference which contains: + + * LSN - the Logical sector number (an integer) + * size - the total size of the file in bytes + * sec_size - the number of sectors allocated + * filename - the file name of the statbuf entry + * XA - if the file has XA attributes; False if not + * is_dir - True if a directory; False if a not.""" + + if translate: + values = pyiso9660.fs_stat_translate(self.cd, path) + else: + values = pyiso9660.fs_stat(self.cd, path) + return stat_array_to_dict(values[0], values[1], values[2], + values[3], values[4]) diff --git a/pycdio.py b/pycdio.py new file mode 100644 index 00000000..61eec0de --- /dev/null +++ b/pycdio.py @@ -0,0 +1,803 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 1.3.31 +# +# Don't modify this file, modify the SWIG interface instead. +# This file is compatible with both classic and new-style classes. + +""" +This is a wrapper for The CD Input and Control library (libcdio) which +encapsulates CD-ROM reading and control. Python programs wishing to be +oblivious of the OS- and device-dependent properties of a CD-ROM can +use this library. +""" + +import _pycdio +import new +new_instancemethod = new.instancemethod +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "thisown"): return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'PySwigObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static) or hasattr(self,name): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + if (name == "thisown"): return self.this.own() + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError,name + +def _swig_repr(self): + try: strthis = "proxy of " + self.this.__repr__() + except: strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +import types +try: + _object = types.ObjectType + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 +del types + + +CDIO_READ_MODE_AUDIO = _pycdio.CDIO_READ_MODE_AUDIO +CDIO_READ_MODE_M1F1 = _pycdio.CDIO_READ_MODE_M1F1 +CDIO_READ_MODE_M1F2 = _pycdio.CDIO_READ_MODE_M1F2 +CDIO_READ_MODE_M2F1 = _pycdio.CDIO_READ_MODE_M2F1 +CDIO_READ_MODE_M2F2 = _pycdio.CDIO_READ_MODE_M2F2 +cdio_read_sectors = _pycdio.cdio_read_sectors +cdio_eject_media_drive = _pycdio.cdio_eject_media_drive +VERSION_NUM = _pycdio.VERSION_NUM +INVALID_LBA = _pycdio.INVALID_LBA +INVALID_LSN = _pycdio.INVALID_LSN +CD_FRAMESIZE = _pycdio.CD_FRAMESIZE +CD_FRAMESIZE_RAW = _pycdio.CD_FRAMESIZE_RAW +ISO_BLOCKSIZE = _pycdio.ISO_BLOCKSIZE +M2F2_SECTOR_SIZE = _pycdio.M2F2_SECTOR_SIZE +M2RAW_SECTOR_SIZE = _pycdio.M2RAW_SECTOR_SIZE + +def audio_pause(*args): + """ + audio_pause(cdio)->status + + Pause playing CD through analog output. + """ + return _pycdio.audio_pause(*args) + +def audio_play_lsn(*args): + """ + auto_play_lsn(cdio, start_lsn, end_lsn)->status + + Playing CD through analog output at the given lsn to the ending lsn + """ + return _pycdio.audio_play_lsn(*args) + +def audio_resume(*args): + """ + audio_resume(cdio)->status + Resume playing an audio CD. + """ + return _pycdio.audio_resume(*args) + +def audio_stop(*args): + """ + audio_stop(cdio)->status + Stop playing an audio CD. + """ + return _pycdio.audio_stop(*args) +READ_MODE_AUDIO = _pycdio.READ_MODE_AUDIO +READ_MODE_M1F1 = _pycdio.READ_MODE_M1F1 +READ_MODE_M1F2 = _pycdio.READ_MODE_M1F2 +READ_MODE_M2F1 = _pycdio.READ_MODE_M2F1 +READ_MODE_M2F2 = _pycdio.READ_MODE_M2F2 + +def lseek(*args): + """ + lseek(cdio, offset, whence)->int + Reposition read offset + Similar to (if not the same as) libc's fseek() + + cdio is object to get adjested, offset is amount to seek and + whence is like corresponding parameter in libc's lseek, e.g. + it should be SEEK_SET or SEEK_END. + + the offset is returned or -1 on error. + """ + return _pycdio.lseek(*args) + +def read_cd(*args): + """ + read_cd(cdio, size)->[size, data] + + Reads into buf the next size bytes. + Similar to (if not the same as) libc's read() + + The number of reads read is returned. -1 is returned on error. + """ + return _pycdio.read_cd(*args) + +def read_sectors(*args): + """ + read_sectors(bytes, lsn, read_mode)->[size, data] + Reads a number of sectors (AKA blocks). + + lsn is sector to read, bytes is the number of bytes. + + If read_mode is pycdio.MODE_AUDIO, the return buffer size will be + truncated to multiple of pycdio.CDIO_FRAMESIZE_RAW i_blocks bytes. + + If read_mode is pycdio.MODE_DATA, buffer will be truncated to a + multiple of pycdio.ISO_BLOCKSIZE, pycdio.M1RAW_SECTOR_SIZE or + pycdio.M2F2_SECTOR_SIZE bytes depending on what mode the data is in. + If read_mode is pycdio.CDIO_MODE_M2F1, buffer will be truncated to a + multiple of pycdio.M2RAW_SECTOR_SIZE bytes. + + If read_mode is CDIO_MODE_M2F2, the return buffer size will be + truncated to a multiple of pycdio.CD_FRAMESIZE bytes. + + If size <= 0 an error has occurred. + """ + return _pycdio.read_sectors(*args) + +def read_data_bytes(*args): + """ + read_data_bytes(lsn, bytes) + + Reads a number of data sectors (AKA blocks). + + lsn is sector to read, bytes is the number of bytes. + If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is pycdio.M2RAW_SECTOR_SIZE. + + If size <= 0 an error has occurred. + """ + return _pycdio.read_data_bytes(*args) +TRACK_FORMAT_AUDIO = _pycdio.TRACK_FORMAT_AUDIO +TRACK_FORMAT_CDI = _pycdio.TRACK_FORMAT_CDI +TRACK_FORMAT_XA = _pycdio.TRACK_FORMAT_XA +TRACK_FORMAT_DATA = _pycdio.TRACK_FORMAT_DATA +TRACK_FORMAT_PSX = _pycdio.TRACK_FORMAT_PSX +CDIO_TRACK_FLAG_FALSE = _pycdio.CDIO_TRACK_FLAG_FALSE +CDIO_TRACK_FLAG_TRUE = _pycdio.CDIO_TRACK_FLAG_TRUE +CDIO_TRACK_FLAG_ERROR = _pycdio.CDIO_TRACK_FLAG_ERROR +CDIO_TRACK_FLAG_UNKNOWN = _pycdio.CDIO_TRACK_FLAG_UNKNOWN +CDIO_CDROM_LBA = _pycdio.CDIO_CDROM_LBA +CDIO_CDROM_MSF = _pycdio.CDIO_CDROM_MSF +CDIO_CDROM_DATA_TRACK = _pycdio.CDIO_CDROM_DATA_TRACK +CDIO_CDROM_CDI_TRACK = _pycdio.CDIO_CDROM_CDI_TRACK +CDIO_CDROM_XA_TRACK = _pycdio.CDIO_CDROM_XA_TRACK +AUDIO = _pycdio.AUDIO +MODE1 = _pycdio.MODE1 +MODE1_RAW = _pycdio.MODE1_RAW +MODE2 = _pycdio.MODE2 +MODE2_FORM1 = _pycdio.MODE2_FORM1 +MODE2_FORM2 = _pycdio.MODE2_FORM2 +MODE2_FORM_MIX = _pycdio.MODE2_FORM_MIX +MODE2_RAW = _pycdio.MODE2_RAW +INVALID_TRACK = _pycdio.INVALID_TRACK +CDROM_LEADOUT_TRACK = _pycdio.CDROM_LEADOUT_TRACK + +def get_first_track_num(*args): + """ + get_first_track_num(p_cdio) -> int + Get the number of the first track. + + return the track number or pycdio.INVALID_TRACK if there was + a problem. + """ + return _pycdio.get_first_track_num(*args) + +def get_last_track_num(*args): + """ + get_last_track_num + Return the last track number. + pycdio.INVALID_TRACK is if there was a problem. + """ + return _pycdio.get_last_track_num(*args) + +def get_track(*args): + """ + cdio_get_track(lsn)->int + + Find the track which contains lsn. + pycdio.INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track, 0 is returned. + Otherwise we return the track that spans the lsn. + """ + return _pycdio.get_track(*args) + +def get_track_channels(*args): + """ + get_track_channels(cdio, track)->int + + Return number of channels in track: 2 or 4; -2 if implemented or -1 + for error. Not meaningful if track is not an audio track. + """ + return _pycdio.get_track_channels(*args) + +def get_track_copy_permit(*args): + """ + get_copy_permit(cdio, track)->int + + Return copy protection status on a track. Is this meaningful + not an audio track? + + """ + return _pycdio.get_track_copy_permit(*args) + +def get_track_format(*args): + """ + get_track_format(cdio, track)->format + + Get the format (audio, mode2, mode1) of track. + """ + return _pycdio.get_track_format(*args) + +def is_track_green(*args): + """ + is_track_green(cdio, track) -> bool + + Return True if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? + """ + return _pycdio.is_track_green(*args) + +def get_track_last_lsn(*args): + """ + cdio_get_track_last_lsn(cdio, track)->lsn + + Return the ending LSN for track number + track in cdio. CDIO_INVALID_LSN is returned on error. + """ + return _pycdio.get_track_last_lsn(*args) + +def get_track_lba(*args): + """ + cdio_get_track_lba + Get the starting LBA for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The 'leadout' track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LBA or CDIO_INVALID_LBA on error. + """ + return _pycdio.get_track_lba(*args) + +def get_track_lsn(*args): + """ + cdio_get_track_lsn (cdio, track)->int + + Return the starting LSN for track number. + Track numbers usually start at something greater than 0, usually 1. + + The 'leadout' track is specified either by + using i_track pycdio.CDROM_LEADOUT_TRACK or the total tracks+1. + + pycdio.INVALID_LSN is returned on error. + """ + return _pycdio.get_track_lsn(*args) + +def get_track_msf(*args): + """ + get_track_msf(cdio,track)->string + + Return the starting MSF (minutes/secs/frames) for track number + track. Track numbers usually start at something + greater than 0, usually 1. + + The 'leadout' track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @return string mm:ss:ff if all good, or string 'error' on error. + """ + return _pycdio.get_track_msf(*args) + +def get_track_preemphasis(*args): + """ + cdio_get_track_preemphasis(cdio, track) + + Get linear preemphasis status on an audio track. + This is not meaningful if not an audio track? + """ + return _pycdio.get_track_preemphasis(*args) + +def get_track_sec_count(*args): + """ + get_track_sec_count(cdio, track)->int + + Get the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Track numbers usually start at something + greater than 0, usually 1. + + 0 is returned if there is an error. + """ + return _pycdio.get_track_sec_count(*args) +DRIVE_CAP_ERROR = _pycdio.DRIVE_CAP_ERROR +DRIVE_CAP_UNKNOWN = _pycdio.DRIVE_CAP_UNKNOWN +DRIVE_CAP_MISC_CLOSE_TRAY = _pycdio.DRIVE_CAP_MISC_CLOSE_TRAY +DRIVE_CAP_MISC_EJECT = _pycdio.DRIVE_CAP_MISC_EJECT +DRIVE_CAP_MISC_LOCK = _pycdio.DRIVE_CAP_MISC_LOCK +DRIVE_CAP_MISC_SELECT_SPEED = _pycdio.DRIVE_CAP_MISC_SELECT_SPEED +DRIVE_CAP_MISC_SELECT_DISC = _pycdio.DRIVE_CAP_MISC_SELECT_DISC +DRIVE_CAP_MISC_MULTI_SESSION = _pycdio.DRIVE_CAP_MISC_MULTI_SESSION +DRIVE_CAP_MISC_MEDIA_CHANGED = _pycdio.DRIVE_CAP_MISC_MEDIA_CHANGED +DRIVE_CAP_MISC_RESET = _pycdio.DRIVE_CAP_MISC_RESET +DRIVE_CAP_MISC_FILE = _pycdio.DRIVE_CAP_MISC_FILE +DRIVE_CAP_READ_AUDIO = _pycdio.DRIVE_CAP_READ_AUDIO +DRIVE_CAP_READ_CD_DA = _pycdio.DRIVE_CAP_READ_CD_DA +DRIVE_CAP_READ_CD_G = _pycdio.DRIVE_CAP_READ_CD_G +DRIVE_CAP_READ_CD_R = _pycdio.DRIVE_CAP_READ_CD_R +DRIVE_CAP_READ_CD_RW = _pycdio.DRIVE_CAP_READ_CD_RW +DRIVE_CAP_READ_DVD_R = _pycdio.DRIVE_CAP_READ_DVD_R +DRIVE_CAP_READ_DVD_PR = _pycdio.DRIVE_CAP_READ_DVD_PR +DRIVE_CAP_READ_DVD_RAM = _pycdio.DRIVE_CAP_READ_DVD_RAM +DRIVE_CAP_READ_DVD_ROM = _pycdio.DRIVE_CAP_READ_DVD_ROM +DRIVE_CAP_READ_DVD_RW = _pycdio.DRIVE_CAP_READ_DVD_RW +DRIVE_CAP_READ_DVD_RPW = _pycdio.DRIVE_CAP_READ_DVD_RPW +DRIVE_CAP_READ_C2_ERRS = _pycdio.DRIVE_CAP_READ_C2_ERRS +DRIVE_CAP_READ_MODE2_FORM1 = _pycdio.DRIVE_CAP_READ_MODE2_FORM1 +DRIVE_CAP_READ_MODE2_FORM2 = _pycdio.DRIVE_CAP_READ_MODE2_FORM2 +DRIVE_CAP_READ_MCN = _pycdio.DRIVE_CAP_READ_MCN +DRIVE_CAP_READ_ISRC = _pycdio.DRIVE_CAP_READ_ISRC +DRIVE_CAP_WRITE_CD_R = _pycdio.DRIVE_CAP_WRITE_CD_R +DRIVE_CAP_WRITE_CD_RW = _pycdio.DRIVE_CAP_WRITE_CD_RW +DRIVE_CAP_WRITE_DVD_R = _pycdio.DRIVE_CAP_WRITE_DVD_R +DRIVE_CAP_WRITE_DVD_PR = _pycdio.DRIVE_CAP_WRITE_DVD_PR +DRIVE_CAP_WRITE_DVD_RAM = _pycdio.DRIVE_CAP_WRITE_DVD_RAM +DRIVE_CAP_WRITE_DVD_RW = _pycdio.DRIVE_CAP_WRITE_DVD_RW +DRIVE_CAP_WRITE_DVD_RPW = _pycdio.DRIVE_CAP_WRITE_DVD_RPW +DRIVE_CAP_WRITE_MT_RAINIER = _pycdio.DRIVE_CAP_WRITE_MT_RAINIER +DRIVE_CAP_WRITE_BURN_PROOF = _pycdio.DRIVE_CAP_WRITE_BURN_PROOF +DRIVE_CAP_WRITE_CD = _pycdio.DRIVE_CAP_WRITE_CD +DRIVE_CAP_WRITE_DVD = _pycdio.DRIVE_CAP_WRITE_DVD +DRIVE_CAP_WRITE = _pycdio.DRIVE_CAP_WRITE +MMC_HW_VENDOR_LEN = _pycdio.MMC_HW_VENDOR_LEN +MMC_HW_MODEL_LEN = _pycdio.MMC_HW_MODEL_LEN +MMC_HW_REVISION_LEN = _pycdio.MMC_HW_REVISION_LEN +SRC_IS_DISK_IMAGE_MASK = _pycdio.SRC_IS_DISK_IMAGE_MASK +SRC_IS_DEVICE_MASK = _pycdio.SRC_IS_DEVICE_MASK +SRC_IS_SCSI_MASK = _pycdio.SRC_IS_SCSI_MASK +SRC_IS_NATIVE_MASK = _pycdio.SRC_IS_NATIVE_MASK +DRIVER_UNKNOWN = _pycdio.DRIVER_UNKNOWN +DRIVER_AIX = _pycdio.DRIVER_AIX +DRIVER_BSDI = _pycdio.DRIVER_BSDI +DRIVER_FREEBSD = _pycdio.DRIVER_FREEBSD +DRIVER_LINUX = _pycdio.DRIVER_LINUX +DRIVER_SOLARIS = _pycdio.DRIVER_SOLARIS +DRIVER_OSX = _pycdio.DRIVER_OSX +DRIVER_WIN32 = _pycdio.DRIVER_WIN32 +DRIVER_CDRDAO = _pycdio.DRIVER_CDRDAO +DRIVER_BINCUE = _pycdio.DRIVER_BINCUE +DRIVER_NRG = _pycdio.DRIVER_NRG +DRIVER_DEVICE = _pycdio.DRIVER_DEVICE +MIN_DRIVER = _pycdio.MIN_DRIVER +MIN_DEVICE_DRIVER = _pycdio.MIN_DEVICE_DRIVER +MAX_DRIVER = _pycdio.MAX_DRIVER +MAX_DEVICE_DRIVER = _pycdio.MAX_DEVICE_DRIVER +DRIVER_OP_SUCCESS = _pycdio.DRIVER_OP_SUCCESS +DRIVER_OP_ERROR = _pycdio.DRIVER_OP_ERROR +DRIVER_OP_UNSUPPORTED = _pycdio.DRIVER_OP_UNSUPPORTED +DRIVER_OP_UNINIT = _pycdio.DRIVER_OP_UNINIT +DRIVER_OP_NOT_PERMITTED = _pycdio.DRIVER_OP_NOT_PERMITTED +DRIVER_OP_BAD_PARAMETER = _pycdio.DRIVER_OP_BAD_PARAMETER +DRIVER_OP_BAD_POINTER = _pycdio.DRIVER_OP_BAD_POINTER +DRIVER_OP_NO_DRIVER = _pycdio.DRIVER_OP_NO_DRIVER +FS_AUDIO = _pycdio.FS_AUDIO +FS_HIGH_SIERRA = _pycdio.FS_HIGH_SIERRA +FS_ISO_9660 = _pycdio.FS_ISO_9660 +FS_INTERACTIVE = _pycdio.FS_INTERACTIVE +FS_HFS = _pycdio.FS_HFS +FS_UFS = _pycdio.FS_UFS +FS_EXT2 = _pycdio.FS_EXT2 +FS_ISO_HFS = _pycdio.FS_ISO_HFS +FS_ISO_9660_INTERACTIVE = _pycdio.FS_ISO_9660_INTERACTIVE +FS_3DO = _pycdio.FS_3DO +FS_XISO = _pycdio.FS_XISO +FS_UDFX = _pycdio.FS_UDFX +FS_UDF = _pycdio.FS_UDF +FS_ISO_UDF = _pycdio.FS_ISO_UDF +FS_ANAL_XA = _pycdio.FS_ANAL_XA +FS_ANAL_MULTISESSION = _pycdio.FS_ANAL_MULTISESSION +FS_ANAL_PHOTO_CD = _pycdio.FS_ANAL_PHOTO_CD +FS_ANAL_HIDDEN_TRACK = _pycdio.FS_ANAL_HIDDEN_TRACK +FS_ANAL_CDTV = _pycdio.FS_ANAL_CDTV +FS_ANAL_BOOTABLE = _pycdio.FS_ANAL_BOOTABLE +FS_ANAL_VIDEOCD = _pycdio.FS_ANAL_VIDEOCD +FS_ANAL_ROCKRIDGE = _pycdio.FS_ANAL_ROCKRIDGE +FS_ANAL_JOLIET = _pycdio.FS_ANAL_JOLIET +FS_ANAL_SVCD = _pycdio.FS_ANAL_SVCD +FS_ANAL_CVD = _pycdio.FS_ANAL_CVD +FS_ANAL_XISO = _pycdio.FS_ANAL_XISO +FS_MATCH_ALL = _pycdio.FS_MATCH_ALL +FS_UNKNOWN = _pycdio.FS_UNKNOWN + +def close_tray(*args): + """ + close_tray(drive=None, driver_id=None) -> [status, driver_id] + + close media tray in CD drive if there is a routine to do so. + The driver id is returned. An exception is thrown on error. + """ + return _pycdio.close_tray(*args) + +def close(*args): + """ + destroy(p_cdio) + Free resources associated with p_cdio. Call this when done using + using CD reading/control operations for the current device. + + """ + return _pycdio.close(*args) + +def eject_media(*args): + """ + eject_media(cdio)->return_code + + Eject media in CD drive if there is a routine to do so. + + """ + return _pycdio.eject_media(*args) + +def eject_media_drive(*args): + """ + eject_media_drive(drive=None)->return_code + Eject media in CD drive if there is a routine to do so. + + psz_drive: the name of the device to be acted upon. + The operation status is returned. + """ + return _pycdio.eject_media_drive(*args) + +def get_arg(*args): + """ + get_arg(p_cdio, key)->string + + Get the value associatied with key. + """ + return _pycdio.get_arg(*args) + +def get_device(*args): + """ + get_device(cdio)->str + + Get the CD device associated with cdio. + If cdio is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + None even though there may be a hardware CD-ROM. + """ + return _pycdio.get_device(*args) + +def get_default_device_driver(*args): + """ + get_default_device_driver(driver_id=None)->[device, driver] + Return a string containing the default CD device if none is specified. + if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE then find a suitable + one set the default device for that. + + None is returned as the device if we couldn't get a default device. + """ + return _pycdio.get_default_device_driver(*args) + +def get_devices(*args): + """ + get_devices(driver_id)->[device1, device2, ...] + + Get an list of device names. + """ + return _pycdio.get_devices(*args) + +def get_devices_ret(*args): + """ + get_devices_ret(driver_id)->[device1, device2, ... driver_id] + + Like get_devices, but return the p_driver_id which may be different + from the passed-in driver_id if it was pycdio.DRIVER_DEVICE or + pycdio.DRIVER_UNKNOWN. The return driver_id may be useful because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds things + up for libcdio as well. + """ + return _pycdio.get_devices_ret(*args) + +def get_devices_with_cap(*args): + """ + get_devices_with_cap(capabilities, any)->[device1, device2...] + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. + + If any is False then every capability listed in the + extended portion of capabilities (i.e. not the basic filesystem) + must be satisified. If any is True, then if any of the + capabilities matches, we call that a success. + + To find a CD-drive of any type, use the mask pycdio.CDIO_FS_MATCH_ALL. + + The array of device names is returned or NULL if we couldn't get a + default device. It is also possible to return a non NULL but after + dereferencing the the value is NULL. This also means nothing was + found. + """ + return _pycdio.get_devices_with_cap(*args) + +def get_devices_with_cap_ret(*args): + """ + Like cdio_get_devices_with_cap but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. + """ + return _pycdio.get_devices_with_cap_ret(*args) + +def get_driver_name(*args): + """ + get_driver_name(cdio)-> string + + return a string containing the name of the driver in use. + + An IOError exception is raised on error. + + """ + return _pycdio.get_driver_name(*args) + +def get_driver_id(*args): + """ + get_driver_id(cdio)-> int + + Return the driver id of the driver in use. + if cdio has not been initialized or is None, + return pycdio.DRIVER_UNKNOWN. + """ + return _pycdio.get_driver_id(*args) + +def get_last_session(*args): + """ + get_last_session(p_cdio) -> int + Get the LSN of the first track of the last session of on the CD. + An exception is thrown on error. + """ + return _pycdio.get_last_session(*args) + +def have_driver(*args): + """ + have_driver(driver_id) -> int + + Return 1 if we have driver driver_id, 0 if not and -1 + if driver id is out of range. + """ + return _pycdio.have_driver(*args) + +def have_ATAPI(*args): + """ + have_ATAPI(CdIo_t *p_cdio)->bool + return True if CD-ROM understand ATAPI commands. + """ + return _pycdio.have_ATAPI(*args) + +def is_binfile(*args): + """ + is_binfile(binfile_name)->cue_name + + Determine if binfile_name is the BIN file part of a CDRWIN CD disk + image. + + Return the corresponding CUE file if bin_name is a BIN file or + None if not a BIN file. + """ + return _pycdio.is_binfile(*args) + +def is_cuefile(*args): + """ + is_cuefile(cuefile_name)->bin_name + + Determine if cuefile_name is the CUE file part of a CDRWIN CD disk + image. + + Return the corresponding BIN file if bin_name is a CUE file or + None if not a CUE file. + """ + return _pycdio.is_cuefile(*args) + +def is_device(*args): + """ + is_cuefile(cuefile_name)->bin_name + + Determine if cuefile_name is the CUE file part of a CDRWIN CD disk + image. + + Return the corresponding BIN file if bin_name is a CUE file or + None if not a CUE file. + """ + return _pycdio.is_device(*args) + +def is_nrg(*args): + """ + is_nrg(cue_name)->bool + + Determine if nrg_name is a Nero CD disc image + """ + return _pycdio.is_nrg(*args) + +def is_tocfile(*args): + """ + is_tocfile(tocfile_name)->bool + + Determine if tocfile_name is a cdrdao CD disc image + """ + return _pycdio.is_tocfile(*args) + +def get_media_changed(*args): + """ + get_media_changed(cdio) -> int + + Find out if media has changed since the last call. + Return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + """ + return _pycdio.get_media_changed(*args) + +def get_hwinfo(*args): + """ + get_hwinfo(p_cdio)->[drc, vendor, model, release] + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + """ + return _pycdio.get_hwinfo(*args) + +def set_blocksize(*args): + """ + set_blocksize(cdio, blocksize)->return_status + + Set the blocksize for subsequent reads. + """ + return _pycdio.set_blocksize(*args) + +def set_speed(*args): + """ + cdio_set_speed(cdio, speed)->return_status + Set the drive speed. + """ + return _pycdio.set_speed(*args) + +def open_cd(*args): + """ + open_cd(source=NULL, driver_id=None, access_mode=None) + + Sets up to read from place specified by source, driver_id and + access mode. This should be called before using any other routine + except those that act on a CD-ROM drive by name. + + If None is given as the source, we'll use the default driver device. + If None is given as the driver_id, we'll find a suitable device driver. + + Return the a pointer than can be used in subsequent operations or + None on error or no device. + """ + return _pycdio.open_cd(*args) + +def set_python_errstring(*args): + """ + open_cd(source=NULL, driver_id=None, access_mode=None) + + Sets up to read from place specified by source, driver_id and + access mode. This should be called before using any other routine + except those that act on a CD-ROM drive by name. + + If None is given as the source, we'll use the default driver device. + If None is given as the driver_id, we'll find a suitable device driver. + + Return the a pointer than can be used in subsequent operations or + None on error or no device. + """ + return _pycdio.set_python_errstring(*args) +DISC_MODE_CD_DA = _pycdio.DISC_MODE_CD_DA +DISC_MODE_CD_DATA = _pycdio.DISC_MODE_CD_DATA +DISC_MODE_CD_XA = _pycdio.DISC_MODE_CD_XA +DISC_MODE_CD_MIXED = _pycdio.DISC_MODE_CD_MIXED +DISC_MODE_DVD_ROM = _pycdio.DISC_MODE_DVD_ROM +DISC_MODE_DVD_RAM = _pycdio.DISC_MODE_DVD_RAM +DISC_MODE_DVD_R = _pycdio.DISC_MODE_DVD_R +DISC_MODE_DVD_RW = _pycdio.DISC_MODE_DVD_RW +DISC_MODE_DVD_PR = _pycdio.DISC_MODE_DVD_PR +DISC_MODE_DVD_PRW = _pycdio.DISC_MODE_DVD_PRW +DISC_MODE_DVD_OTHER = _pycdio.DISC_MODE_DVD_OTHER +DISC_MODE_NO_INFO = _pycdio.DISC_MODE_NO_INFO +DISC_MODE_ERROR = _pycdio.DISC_MODE_ERROR +DISC_MODE_CD_I = _pycdio.DISC_MODE_CD_I + +def get_disc_last_lsn(*args): + """ + get_disc_last_lsn(cdio)->lsn + Get the LSN of the end of the CD. + + pycdio.INVALID_LSN is returned on error. + """ + return _pycdio.get_disc_last_lsn(*args) + +def get_disc_mode(*args): + """ + get_disc_mode(p_cdio) -> str + + Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, ...) + that we've got. The notion of 'CD' is extended a little to include + DVD's. + """ + return _pycdio.get_disc_mode(*args) + +def get_joliet_level(*args): + """ + get_joliet_level(cdio)->int + + Return the Joliet level recognized for cdio. + This only makes sense for something that has an ISO-9660 + filesystem. + """ + return _pycdio.get_joliet_level(*args) + +def get_mcn(*args): + """ + get_mcn(cdio) -> str + + Get the media catalog number (MCN) from the CD. + """ + return _pycdio.get_mcn(*args) + +def get_num_tracks(*args): + """ + get_num_tracks(p_cdio)->int + + Return the number of tracks on the CD. + On error pycdio.INVALID_TRACK is returned. + """ + return _pycdio.get_num_tracks(*args) +INCLUDE_CLASS = _pycdio.INCLUDE_CLASS + + +def get_drive_cap(*args): + """ + get_drive_cap()->(read_cap, write_cap, misc_cap) + + Get drive capabilities of device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it. In this situation capabilities will show up as + empty even though there is a hardware CD-ROM. + get_drive_cap_dev()->(read_cap, write_cap, misc_cap) + + Get drive capabilities of device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it. In this situation capabilities will show up as + empty even though there is a hardware CD-ROM. + """ + return _pycdio.get_drive_cap(*args) +cvar = _pycdio.cvar + diff --git a/pyiso9660.py b/pyiso9660.py new file mode 100644 index 00000000..01244856 --- /dev/null +++ b/pyiso9660.py @@ -0,0 +1,532 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 1.3.31 +# +# Don't modify this file, modify the SWIG interface instead. +# This file is compatible with both classic and new-style classes. + +""" +This is a wrapper for The CD Input and Control library's ISO-9660 library +See also the ISO-9660 specification. The freely available European +equivalant standard is called ECMA-119. +""" + +import _pyiso9660 +import new +new_instancemethod = new.instancemethod +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "thisown"): return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'PySwigObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static) or hasattr(self,name): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + if (name == "thisown"): return self.this.own() + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError,name + +def _swig_repr(self): + try: strthis = "proxy of " + self.this.__repr__() + except: strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +import types +try: + _object = types.ObjectType + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 +del types + + +VERSION_NUM = _pyiso9660.VERSION_NUM +INVALID_LBA = _pyiso9660.INVALID_LBA +INVALID_LSN = _pyiso9660.INVALID_LSN +CD_FRAMESIZE = _pyiso9660.CD_FRAMESIZE +CD_FRAMESIZE_RAW = _pyiso9660.CD_FRAMESIZE_RAW +ISO_BLOCKSIZE = _pyiso9660.ISO_BLOCKSIZE +M2F2_SECTOR_SIZE = _pyiso9660.M2F2_SECTOR_SIZE +M2RAW_SECTOR_SIZE = _pyiso9660.M2RAW_SECTOR_SIZE +CDIO_READ_MODE_AUDIO = _pyiso9660.CDIO_READ_MODE_AUDIO +CDIO_READ_MODE_M1F1 = _pyiso9660.CDIO_READ_MODE_M1F1 +CDIO_READ_MODE_M1F2 = _pyiso9660.CDIO_READ_MODE_M1F2 +CDIO_READ_MODE_M2F1 = _pyiso9660.CDIO_READ_MODE_M2F1 +CDIO_READ_MODE_M2F2 = _pyiso9660.CDIO_READ_MODE_M2F2 +cdio_read_sectors = _pyiso9660.cdio_read_sectors +cdio_eject_media_drive = _pyiso9660.cdio_eject_media_drive +PVD_SECTOR = _pyiso9660.PVD_SECTOR +EVD_SECTOR = _pyiso9660.EVD_SECTOR +LEN_ISONAME = _pyiso9660.LEN_ISONAME +MAX_SYSTEM_ID = _pyiso9660.MAX_SYSTEM_ID +MAX_ISONAME = _pyiso9660.MAX_ISONAME +MAX_PREPARER_ID = _pyiso9660.MAX_PREPARER_ID +MAX_ISOPATHNAME = _pyiso9660.MAX_ISOPATHNAME +FILE = _pyiso9660.FILE +EXISTENCE = _pyiso9660.EXISTENCE +DIRECTORY = _pyiso9660.DIRECTORY +ASSOCIATED = _pyiso9660.ASSOCIATED +RECORD = _pyiso9660.RECORD +PROTECTION = _pyiso9660.PROTECTION +DRESERVED1 = _pyiso9660.DRESERVED1 +DRESERVED2 = _pyiso9660.DRESERVED2 +MULTIEXTENT = _pyiso9660.MULTIEXTENT +VD_BOOT_RECORD = _pyiso9660.VD_BOOT_RECORD +VD_PRIMARY = _pyiso9660.VD_PRIMARY +VD_SUPPLEMENTARY = _pyiso9660.VD_SUPPLEMENTARY +VD_PARITION = _pyiso9660.VD_PARITION +VD_END = _pyiso9660.VD_END +MAX_PUBLISHER_ID = _pyiso9660.MAX_PUBLISHER_ID +MAX_APPLICATION_ID = _pyiso9660.MAX_APPLICATION_ID +MAX_VOLUME_ID = _pyiso9660.MAX_VOLUME_ID +MAX_VOLUMESET_ID = _pyiso9660.MAX_VOLUMESET_ID +STANDARD_ID = _pyiso9660.STANDARD_ID +NOCHECK = _pyiso9660.NOCHECK +SEVEN_BIT = _pyiso9660.SEVEN_BIT +ACHARS = _pyiso9660.ACHARS +DCHARS = _pyiso9660.DCHARS +EXTENSION_JOLIET_LEVEL1 = _pyiso9660.EXTENSION_JOLIET_LEVEL1 +EXTENSION_JOLIET_LEVEL2 = _pyiso9660.EXTENSION_JOLIET_LEVEL2 +EXTENSION_JOLIET_LEVEL3 = _pyiso9660.EXTENSION_JOLIET_LEVEL3 +EXTENSION_ROCK_RIDGE = _pyiso9660.EXTENSION_ROCK_RIDGE +EXTENSION_HIGH_SIERRA = _pyiso9660.EXTENSION_HIGH_SIERRA +EXTENSION_ALL = _pyiso9660.EXTENSION_ALL +EXTENSION_NONE = _pyiso9660.EXTENSION_NONE +EXTENSION_JOLIET = _pyiso9660.EXTENSION_JOLIET + +def open_iso(*args): + """ + open_iso(path) + Open an ISO 9660 image for reading. Maybe in the future we will have + mode. None is returned on error. + + """ + return _pyiso9660.open_iso(*args) + +def open_ext(*args): + """ + Open an ISO 9660 image for reading allowing various ISO 9660 + extensions. Maybe in the future we will have a mode. None is + returned on error. + """ + return _pyiso9660.open_ext(*args) + +def open_fuzzy(*args): + """ + Open an ISO 9660 image for reading with some tolerence for positioning + of the ISO9660 image. We scan for ISO_STANDARD_ID and use that to set + the eventual offset to adjust by (as long as that is <= i_fuzz). + + Maybe in the future we will have a mode. None is returned on error. + + see iso9660_open + """ + return _pyiso9660.open_fuzzy(*args) + +def iso9660_open_fuzzy_ext(*args): + """ + Open an ISO 9660 image for reading with some tolerence for positioning + of the ISO9660 image. We scan for ISO_STANDARD_ID and use that to set + the eventual offset to adjust by (as long as that is <= i_fuzz). + + Maybe in the future we will have a mode. None is returned on error. + + see open_iso + + """ + return _pyiso9660.iso9660_open_fuzzy_ext(*args) + +def ifs_fuzzy_read_superblock(*args): + """ + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + + """ + return _pyiso9660.ifs_fuzzy_read_superblock(*args) + +def close(*args): + """ + Close previously opened ISO 9660 image. + True is unconditionally returned. If there was an error false would + be returned. + """ + return _pyiso9660.close(*args) + +def seek_read(*args): + """ + Seek to a position and then read n bytes. (buffer, size) are + returned. + """ + return _pyiso9660.seek_read(*args) + +def fs_read_pvd(*args): + """ + Read the Primary Volume Descriptor for a CD. + None is returned if there was an error. + """ + return _pyiso9660.fs_read_pvd(*args) + +def ifs_read_pvd(*args): + """ + Read the Primary Volume Descriptor for an ISO 9660 image. + None is returned if there was an error. + """ + return _pyiso9660.ifs_read_pvd(*args) + +def fs_read_superblock(*args): + """ + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. + """ + return _pyiso9660.fs_read_superblock(*args) + +def ifs_read_superblock(*args): + """ + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. + """ + return _pyiso9660.ifs_read_superblock(*args) + +def set_dtime(*args): + """Set time in format used in ISO 9660 directory index record""" + return _pyiso9660.set_dtime(*args) + +def set_ltime(*args): + """Set 'long' time in format used in ISO 9660 primary volume descriptor""" + return _pyiso9660.set_ltime(*args) + +def get_dtime(*args): + """ + Get Unix time structure from format use in an ISO 9660 directory index + record. Even though tm_wday and tm_yday fields are not explicitly in + idr_date, they are calculated from the other fields. + + If tm is to reflect the localtime, set 'use_localtime' true, otherwise + tm will reported in GMT. + """ + return _pyiso9660.get_dtime(*args) + +def get_ltime(*args): + """ + Get 'long' time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. + """ + return _pyiso9660.get_ltime(*args) + +def is_dchar(*args): + """ + Return true if c is a DCHAR - a character that can appear in an an + ISO-9600 level 1 directory name. These are the ASCII capital + letters A-Z, the digits 0-9 and an underscore. + """ + return _pyiso9660.is_dchar(*args) + +def is_achar(*args): + """ + Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol. + """ + return _pyiso9660.is_achar(*args) + +def name_translate(*args): + """ + Convert an ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. + Lowercase name, and remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @return length of the translated string is returned. + """ + return _pyiso9660.name_translate(*args) + +def name_translate_ext(*args): + """ + Convert an ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. Lowercase + name if no Joliet Extension interpretation. Remove trailing ;1's or + .;1's and turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @param i_joliet_level 0 if not using Joliet Extension. Otherwise the + Joliet level. + @return length of the translated string is returned. It will be no greater + than the length of psz_oldname. + """ + return _pyiso9660.name_translate_ext(*args) + +def strncpy_pad(*args): + """ + Pad string src with spaces to size len and copy this to dst. If + en is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated. + """ + return _pyiso9660.strncpy_pad(*args) + +def dirname_valid_p(*args): + """ + Check that psz_path is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + + True is returned if psz_path is valid. + """ + return _pyiso9660.dirname_valid_p(*args) + +def pathname_isofy(*args): + """ + Take psz_path and a version number and turn that into a ISO-9660 + pathname. (That's just the pathname followed by ';' and the version + number. For example, mydir/file.ext -> MYDIR/FILE.EXT;1 for version + 1. The resulting ISO-9660 pathname is returned. + """ + return _pyiso9660.pathname_isofy(*args) + +def pathname_valid_p(*args): + """ + Check that psz_path is a valid ISO-9660 pathname. + + A valid pathname contains a valid directory name, if one appears and + the filename portion should be no more than 8 characters for the + file prefix and 3 characters in the extension (or portion after a + dot). There should be exactly one dot somewhere in the filename + portion and the filename should be composed of only DCHARs. + + True is returned if psz_path is valid. + """ + return _pyiso9660.pathname_valid_p(*args) + +def fs_stat(*args): + """Return file status for psz_path. None is returned on error.""" + return _pyiso9660.fs_stat(*args) + +def fs_stat_translate(*args): + """ + Return file status for path name psz_path. None is returned on error. + pathname version numbers in the ISO 9660 name are dropped, i.e. ;1 + is removed and if level 1 ISO-9660 names are lowercased. + + The b_mode2 parameter is not used. + """ + return _pyiso9660.fs_stat_translate(*args) + +def ifs_stat(*args): + """Return file status for pathname. None is returned on error.""" + return _pyiso9660.ifs_stat(*args) + +def ifs_stat_translate(*args): + """ + Return file status for path name psz_path. undef is returned on error. + pathname version numbers in the ISO 9660 name are dropped, i.e. ;1 is + removed and if level 1 ISO-9660 names are lowercased. + """ + return _pyiso9660.ifs_stat_translate(*args) + +def fs_readdir(*args): + """ + Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. + """ + return _pyiso9660.fs_readdir(*args) + +def ifs_readdir(*args): + """ + Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. + """ + return _pyiso9660.ifs_readdir(*args) + +def get_application_id(*args): + """ + Return the PVD's application ID. + None is returned if there is some problem in getting this. + """ + return _pyiso9660.get_application_id(*args) + +def ifs_get_application_id(*args): + """ + Get the application ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.ifs_get_application_id(*args) + +def get_joliet_level(*args): + """Return the Joliet level recognized for p_iso.""" + return _pyiso9660.get_joliet_level(*args) + +def get_dir_len(*args): + """Return the Joliet level recognized for p_iso.""" + return _pyiso9660.get_dir_len(*args) + +def iso9660_dir_to_name(*args): + """Return the directory name stored in the iso9660_dir_t.""" + return _pyiso9660.iso9660_dir_to_name(*args) + +def get_preparer_id(*args): + """ + Return a string containing the preparer id with trailing + blanks removed. + """ + return _pyiso9660.get_preparer_id(*args) + +def ifs_get_preparer_id(*args): + """ + Get the preparer ID. Return None if there is some problem in + getting this. + """ + return _pyiso9660.ifs_get_preparer_id(*args) + +def get_publisher_id(*args): + """ + Return a string containing the PVD's publisher id with trailing + blanks removed. + """ + return _pyiso9660.get_publisher_id(*args) + +def ifs_get_publisher_id(*args): + """ + Get the publisher ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.ifs_get_publisher_id(*args) + +def get_pvd_type(*args): + """ + Get the publisher ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.get_pvd_type(*args) + +def get_pvd_id(*args): + """ + Get the publisher ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.get_pvd_id(*args) + +def get_pvd_space_size(*args): + """ + Get the publisher ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.get_pvd_space_size(*args) + +def get_pvd_block_size(*args): + """ + Get the publisher ID. Return None if there + is some problem in getting this. + """ + return _pyiso9660.get_pvd_block_size(*args) + +def get_pvd_version(*args): + """ + Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + """ + return _pyiso9660.get_pvd_version(*args) + +def get_system_id(*args): + """ + Return a string containing the PVD's system id with trailing + blanks removed. + """ + return _pyiso9660.get_system_id(*args) + +def ifs_get_system_id(*args): + """ + Get the system ID. None is returned if there + is some problem in getting this. + """ + return _pyiso9660.ifs_get_system_id(*args) + +def get_root_lsn(*args): + """ + Return the LSN of the root directory for pvd. If there is an error + INVALID_LSN is returned. + + """ + return _pyiso9660.get_root_lsn(*args) + +def get_volume_id(*args): + """Return the PVD's volume ID.""" + return _pyiso9660.get_volume_id(*args) + +def ifs_get_volume_id(*args): + """ + Get the system ID. None is returned if there + is some problem in getting this. + """ + return _pyiso9660.ifs_get_volume_id(*args) + +def get_volumeset_id(*args): + """ + Return the PVD's volumeset ID. + None is returned if there is some problem in getting this. + + """ + return _pyiso9660.get_volumeset_id(*args) + +def ifs_get_volumeset_id(*args): + """ + Get the volumeset ID. None is returned if there + is some problem in getting this. + """ + return _pyiso9660.ifs_get_volumeset_id(*args) + +def pathtable_init(*args): + """Zero's out pathable. Do this first.""" + return _pyiso9660.pathtable_init(*args) + +def pathtable_get_size(*args): + """Zero's out pathable. Do this first.""" + return _pyiso9660.pathtable_get_size(*args) + +def pathtable_l_add_entry(*args): + """Zero's out pathable. Do this first.""" + return _pyiso9660.pathtable_l_add_entry(*args) + +def pathtable_m_add_entry(*args): + """Zero's out pathable. Do this first.""" + return _pyiso9660.pathtable_m_add_entry(*args) + +def set_evd(*args): + """Zero's out pathable. Do this first.""" + return _pyiso9660.set_evd(*args) + +def is_xa(*args): + """Return true if ISO 9660 image has extended attrributes (XA).""" + return _pyiso9660.is_xa(*args) + + diff --git a/setup.py b/setup.py new file mode 100755 index 00000000..537d6f46 --- /dev/null +++ b/setup.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +""" +setup.py file for pycdio +""" + +from setuptools import setup +from distutils.core import Extension + +version = '0.14vc' + +import os +README = os.path.join(os.path.dirname(__file__), 'README.txt') +long_description = open(README).read() + '\n\n' + +pycdio_module = Extension('_pycdio', + sources=['pycdio_wrap.c', 'swig/pycdio.swg'], + ) +pyiso9660_module = Extension('_pyiso9660', + sources=['iso9660_wrap.c', 'swig/pyiso9660.swg'], + ) +setup (name = 'pycdio', + author = 'Rocky Bernstein', + author_email = 'rocky@gnu.org', + description = 'Python OO interface to libcdio (CD Input and Control library)', + py_ext_modules = [pycdio_module, pyiso9660_module], + license = 'GPL', + long_description = long_description, + name = 'pytracer', + py_modules = ['pycdio'], + test_suite = 'nose.collector', + url = 'http://freshmeat.net/projects/libcdio/?branch_id=62870', + version = version, + ) diff --git a/swig/audio.swg b/swig/audio.swg new file mode 100644 index 00000000..11fe88e9 --- /dev/null +++ b/swig/audio.swg @@ -0,0 +1,62 @@ +/* -*- c -*- + $Id: audio.swg,v 1.3 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** NOTE: THIS IS THE EASILY CONVERTED SUBSET OF LIBCDIO WE CAN DO. **/ + +/* See for more extensive documentation. */ + +%rename cdio_audio_pause audio_pause; +%feature("autodoc", +"audio_pause(cdio)->status + +Pause playing CD through analog output."); +driver_return_code_t cdio_audio_pause (CdIo_t *p_cdio); + + +%feature("autodoc", +"auto_play_lsn(cdio, start_lsn, end_lsn)->status + +Playing CD through analog output at the given lsn to the ending lsn"); +driver_return_code_t audio_play_lsn (CdIo_t *p_cdio, lsn_t start_lsn, + lsn_t end_lsn); +%inline %{ +driver_return_code_t audio_play_lsn (CdIo_t *p_cdio, lsn_t start_lsn, + lsn_t end_lsn) +{ + msf_t start_msf; + msf_t end_msf; + cdio_lsn_to_msf (start_lsn, &start_msf); + cdio_lsn_to_msf (end_lsn, &end_msf); + return cdio_audio_play_msf(p_cdio, &start_msf, &end_msf); +} +%} + +%rename cdio_audio_resume audio_resume; +%feature("autodoc", +"audio_resume(cdio)->status +Resume playing an audio CD."); +driver_return_code_t cdio_audio_resume (CdIo_t *p_cdio); + + +%rename cdio_audio_stop audio_stop; +%feature("autodoc", +"audio_stop(cdio)->status +Stop playing an audio CD."); +driver_return_code_t cdio_audio_stop (CdIo_t *p_cdio); + diff --git a/swig/compat.swg b/swig/compat.swg new file mode 100644 index 00000000..c3fa24dc --- /dev/null +++ b/swig/compat.swg @@ -0,0 +1,103 @@ +/* -*- c -*- + $Id: compat.swg,v 1.3 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +%inline %{ +/* When libcdio version > 0.76 comes out this won't be needed. */ +#include +#if LIBCDIO_VERSION_NUM <= 76 + +/**< Masks derived from above... */ +#undef CDIO_DRIVE_CAP_WRITE_DVD +#define CDIO_DRIVE_CAP_WRITE_DVD ( \ + CDIO_DRIVE_CAP_WRITE_DVD_R \ + | CDIO_DRIVE_CAP_WRITE_DVD_PR \ + | CDIO_DRIVE_CAP_WRITE_DVD_RAM \ + | CDIO_DRIVE_CAP_WRITE_DVD_RW \ + | CDIO_DRIVE_CAP_WRITE_DVD_RPW \ + ) + +/** All the different ways a block/sector can be read. */ +typedef enum { + CDIO_READ_MODE_AUDIO, /**< CD-DA, audio, Red Book */ + CDIO_READ_MODE_M1F1, /**< Mode 1 Form 1 */ + CDIO_READ_MODE_M1F2, /**< Mode 1 Form 2 */ + CDIO_READ_MODE_M2F1, /**< Mode 2 Form 1 */ + CDIO_READ_MODE_M2F2, /**< Mode 2 Form 2 */ +} cdio_read_mode_t; + +/*! + Reads a number of sectors (AKA blocks). + + @param p_buf place to read data into. The caller should make sure + this location is large enough. See below for size information. + @param read_mode the kind of "mode" to use in reading. + @param i_lsn sector to read + @param i_blocks number of sectors to read + @return DRIVER_OP_SUCCESS (0) if no error, other (negative) enumerations + are returned on error. + + If read_mode is CDIO_MODE_AUDIO, + *p_buf should hold at least CDIO_FRAMESIZE_RAW * i_blocks bytes. + + If read_mode is CDIO_MODE_DATA, + *p_buf should hold at least i_blocks times either ISO_BLOCKSIZE, + M1RAW_SECTOR_SIZE or M2F2_SECTOR_SIZE depending on the kind of + sector getting read. If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is M2RAW_SECTOR_SIZE. + + If read_mode is CDIO_MODE_M2F1, + *p_buf should hold at least M2RAW_SECTOR_SIZE * i_blocks bytes. + + If read_mode is CDIO_MODE_M2F2, + *p_buf should hold at least CDIO_CD_FRAMESIZE * i_blocks bytes. + + +*/ +driver_return_code_t +cdio_read_sectors(const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + cdio_read_mode_t read_mode, uint32_t i_blocks) +{ + switch(read_mode) { + case CDIO_READ_MODE_AUDIO: + return cdio_read_audio_sectors (p_cdio, p_buf, i_lsn, i_blocks); + case CDIO_READ_MODE_M1F1: + return cdio_read_mode1_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + case CDIO_READ_MODE_M1F2: + return cdio_read_mode1_sectors (p_cdio, p_buf, i_lsn, true, i_blocks); + case CDIO_READ_MODE_M2F1: + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + case CDIO_READ_MODE_M2F2: + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, true, i_blocks); + } + /* Can't happen. Just to shut up gcc. */ + return DRIVER_OP_ERROR; +} + +driver_return_code_t +cdio_eject_media_drive (const char *psz_drive) +{ + CdIo_t *p_cdio = cdio_open (psz_drive, DRIVER_DEVICE); + if (p_cdio) { + return cdio_eject_media(&p_cdio); + } else { + return DRIVER_OP_UNINIT; + } +} +#endif /* LIBCDIO_VERSION_NUM <= 76 */ +%} diff --git a/swig/device.swg b/swig/device.swg new file mode 100644 index 00000000..a934f4d3 --- /dev/null +++ b/swig/device.swg @@ -0,0 +1,531 @@ +/* -*- c -*- + $Id: device.swg,v 1.16 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* See for more extensive documentation. */ + +%include "device_const.swg" + +/* Set up to allow functions returning device lists of type "char + **". We'll use a typedef so we can make sure to isolate this. I + don't think we need to in this program, but it I think it makes + thing clearer. +*/ +%inline %{ +typedef char ** DeviceList_t; +%} + +%typemap(newfree) DeviceList_t "cdio_free_device_list($1);"; +%typemap(out,fragment="t_output_helper") DeviceList_t { + // result is of type DeviceList_t + char **p = result; + if (!result) goto fail; + + /* For each element in the array of strings, push that + * onto the result object. */ + for (p = result; *p; p++) { + PyObject *o = PyString_FromString(*p); + resultobj = t_output_helper(resultobj,o); + } +} + +%rename cdio_close_tray close_tray; +%feature("autodoc", +"close_tray(drive=None, driver_id=None) -> [status, driver_id] + +close media tray in CD drive if there is a routine to do so. +The driver id is returned. An exception is thrown on error."); +%apply driver_id_t *OUTPUT { driver_id_t *p_out_driver_id }; +driver_return_code_t close_tray(const char *psz_drive, + driver_id_t p_driver_id=DRIVER_UNKNOWN, + driver_id_t *p_out_driver_id); +%inline %{ +driver_return_code_t +close_tray(const char *psz_drive, driver_id_t p_driver_id, + driver_id_t *p_out_driver_id) +{ + *p_out_driver_id = p_driver_id; + return cdio_close_tray(psz_drive, p_out_driver_id); +} +%} + +%rename cdio_destroy close; +%feature("autodoc", +"destroy(p_cdio) +Free resources associated with p_cdio. Call this when done using +using CD reading/control operations for the current device. +"); +void cdio_destroy(CdIo_t *p_cdio); + +%feature("autodoc", +"eject_media(cdio)->return_code + +Eject media in CD drive if there is a routine to do so. +"); +driver_return_code_t eject_media (CdIo_t *p_cdio); +%inline %{ +driver_return_code_t +eject_media (CdIo_t *p_cdio) +{ + /* libcdio routines uses a Cdio_t **p_cdio, so we have to pass in + something it can clobber. + */ + CdIo_t **pp_cdio = &p_cdio; + return cdio_eject_media (pp_cdio); +} +%} + + +%rename cdio_eject_media_drive eject_media_drive; +%feature("autodoc", +"eject_media_drive(drive=None)->return_code +Eject media in CD drive if there is a routine to do so. + +psz_drive: the name of the device to be acted upon. +The operation status is returned."); +driver_return_code_t cdio_eject_media_drive (const char *psz_drive=NULL); + +%rename cdio_get_arg get_arg; +%feature("autodoc", +"get_arg(p_cdio, key)->string + +Get the value associatied with key."); +const char *cdio_get_arg (const CdIo_t *p_cdio, const char key[]); + +%newobject cdio_get_default_device; // free malloc'd return value +%rename cdio_get_default_device get_device; +%feature("autodoc", +"get_device(cdio)->str + +Get the CD device associated with cdio. +If cdio is NULL (we haven't initialized a specific device driver), +then find a suitable one and return the default device for that. + +In some situations of drivers or OS's we can't find a CD device if +there is no media in it and it is possible for this routine to return +None even though there may be a hardware CD-ROM."); +char *cdio_get_default_device (const CdIo_t *p_cdio=NULL); + +%newobject get_default_device_driver; // free malloc'd return value +%feature("autodoc", +"get_default_device_driver(driver_id=None)->[device, driver] +Return a string containing the default CD device if none is specified. +if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE then find a suitable +one set the default device for that. + +None is returned as the device if we couldn't get a default device."); +%apply driver_id_t *OUTPUT { driver_id_t *p_out_driver_id }; +char *get_default_device_driver (driver_id_t p_driver_id, + driver_id_t *p_out_driver_id); +%inline %{ +char * +get_default_device_driver(driver_id_t driver_id, driver_id_t *p_out_driver_id) +{ + *p_out_driver_id = driver_id; + return cdio_get_default_device_driver(p_out_driver_id); +} +%} + +%rename cdio_get_devices get_devices; +%newobject cdio_get_devices; +%feature("autodoc", +"get_devices(driver_id)->[device1, device2, ...] + +Get an list of device names."); +DeviceList_t cdio_get_devices (driver_id_t driver_id); + +%newobject get_devices_ret; +%feature("autodoc", +"get_devices_ret(driver_id)->[device1, device2, ... driver_id] + +Like get_devices, but return the p_driver_id which may be different +from the passed-in driver_id if it was pycdio.DRIVER_DEVICE or +pycdio.DRIVER_UNKNOWN. The return driver_id may be useful because +often one wants to get a drive name and then *open* it +afterwards. Giving the driver back facilitates this, and speeds things +up for libcdio as well."); + +DeviceList_t get_devices_ret (driver_id_t driver_id, + driver_id_t *p_out_driver_id); +%inline %{ +DeviceList_t get_devices_ret (driver_id_t driver_id, + driver_id_t *p_out_driver_id) { + *p_out_driver_id = driver_id; + return cdio_get_devices_ret (p_out_driver_id); + } +%} + +%feature("autodoc", +"get_devices_with_cap(capabilities, any)->[device1, device2...] +Get an array of device names in search_devices that have at least +the capabilities listed by the capabities parameter. + +If any is False then every capability listed in the +extended portion of capabilities (i.e. not the basic filesystem) +must be satisified. If any is True, then if any of the +capabilities matches, we call that a success. + +To find a CD-drive of any type, use the mask pycdio.CDIO_FS_MATCH_ALL. + +The array of device names is returned or NULL if we couldn't get a +default device. It is also possible to return a non NULL but after +dereferencing the the value is NULL. This also means nothing was +found."); +DeviceList_t get_devices_with_cap (unsigned int capabilities, bool b_any); +%inline %{ +DeviceList_t +get_devices_with_cap (unsigned int capabilities, bool b_any) { + /* FIXME: ? libcdio allows one to specify a list (char **) of devices + to search. Don't know how to do that via SWIG though. */ + return cdio_get_devices_with_cap (NULL, (cdio_fs_anal_t) capabilities, + b_any); + } +%} + +%apply driver_id_t *OUTPUT { driver_id_t *p_out_driver_id }; +%newobject get_devices_with_cap_ret; +%feature("autodoc", +"Like cdio_get_devices_with_cap but we return the driver we found +as well. This is because often one wants to search for kind of drive +and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well."); +DeviceList_t get_devices_with_cap_ret (unsigned int capabilities, bool b_any, + driver_id_t *p_out_driver_id); +%inline %{ +DeviceList_t +get_devices_with_cap_ret (unsigned int capabilities, bool b_any, + driver_id_t *p_out_driver_id) { + /* FIXME: ? libcdio allows one to specify a list (char **) of devices + to search. Don't know how to do that via SWIG though. */ + return cdio_get_devices_with_cap_ret (NULL, + (cdio_fs_anal_t) capabilities, b_any, + p_out_driver_id); + } +%} + +%rename cdio_get_drive_cap get_drive_cap; +%feature("autodoc", +"get_drive_cap()->(read_cap, write_cap, misc_cap) + +Get drive capabilities of device. + +In some situations of drivers or OS's we can't find a CD device if +there is no media in it. In this situation capabilities will show up as +empty even though there is a hardware CD-ROM."); +%apply uint32_t *OUTPUT { uint32_t *p_read_cap, + uint32_t *p_write_cap, + uint32_t *p_misc_cap }; +void cdio_get_drive_cap (const CdIo_t *p_cdio, + uint32_t *p_read_cap, + uint32_t *p_write_cap, + uint32_t *p_misc_cap); + +%rename cdio_get_drive_cap_dev get_drive_cap; +%feature("autodoc", +"get_drive_cap_dev()->(read_cap, write_cap, misc_cap) + +Get drive capabilities of device. + +In some situations of drivers or OS's we can't find a CD device if +there is no media in it. In this situation capabilities will show up as +empty even though there is a hardware CD-ROM."); + +void cdio_get_drive_cap_dev(const char *device=NULL, + uint32_t *p_read_cap, + uint32_t *p_write_cap, + uint32_t *p_misc_cap); + +%rename cdio_get_driver_name get_driver_name; +%feature("autodoc", +"get_driver_name(cdio)-> string + +return a string containing the name of the driver in use. + +An IOError exception is raised on error. +"); +%exception cdio_get_driver_name { + $action + if (NULL == result) { + PyErr_SetString(PyExc_IOError, "Error getting driver name."); + return NULL; + } +} +const char *cdio_get_driver_name (const CdIo_t *p_cdio); + +%rename cdio_get_driver_id get_driver_id; +%feature("autodoc", +"get_driver_id(cdio)-> int + +Return the driver id of the driver in use. +if cdio has not been initialized or is None, +return pycdio.DRIVER_UNKNOWN."); +driver_id_t cdio_get_driver_id (const CdIo_t *p_cdio); + +%rename cdio_get_last_session get_last_session; +%feature("autodoc", +"get_last_session(p_cdio) -> int +Get the LSN of the first track of the last session of on the CD. +An exception is thrown on error."); +%apply int *OUTPUT { lsn_t *i_last_session }; +driver_return_code_t cdio_get_last_session (CdIo_t *p_cdio, + lsn_t *i_last_session); + +%feature("autodoc", +"have_driver(driver_id) -> int + +Return 1 if we have driver driver_id, 0 if not and -1 +if driver id is out of range."); +%inline %{ +int +have_driver (unsigned int driver_id) +{ + if (driver_id < CDIO_MIN_DRIVER || driver_id > CDIO_MAX_DRIVER) + return -1; + if (cdio_have_driver(driver_id)) return 1; + return 0; +} +%} + +%feature("autodoc", +"have_ATAPI(CdIo_t *p_cdio)->bool +return True if CD-ROM understand ATAPI commands."); +%inline %{ +/*! True if CD-ROM understand ATAPI commands. */ +bool +have_ATAPI (CdIo_t *p_cdio) +{ + return cdio_have_atapi(p_cdio) == yep; +} +%} + +%newobject cdio_is_binfile; // free malloc'd return value +%rename cdio_is_binfile is_binfile; +%feature("autodoc", +"is_binfile(binfile_name)->cue_name + +Determine if binfile_name is the BIN file part of a CDRWIN CD disk +image. + +Return the corresponding CUE file if bin_name is a BIN file or +None if not a BIN file."); +char *cdio_is_binfile(const char *bin_name); + +%newobject cdio_is_cuefile; // free malloc'd return value +%rename cdio_is_cuefile is_cuefile; +%feature("autodoc", +"is_cuefile(cuefile_name)->bin_name + +Determine if cuefile_name is the CUE file part of a CDRWIN CD disk +image. + +Return the corresponding BIN file if bin_name is a CUE file or +None if not a CUE file."); +char *cdio_is_cuefile(const char *cue_name); + +#if LIBCDIO_VERSION_NUM <= 76 +/* There is a bug in the 0.76 code when driver_id==DRIVER_UNKNOWN + or DRIVER_DEVICE, so here we'll use code from compat.swg. +*/ +bool is_device(const char *psz_source, + driver_id_t driver_id=DRIVER_UNKNOWN); + +%inline %{ +bool +is_device(const char *psz_source, driver_id_t driver_id) +{ + if (DRIVER_UNKNOWN == driver_id || DRIVER_DEVICE == driver_id) { + char *psz_drive = cdio_get_default_device_driver(&driver_id); + /* We don't need the psz_drive, just the driver_id */ + free(psz_drive); + } + return cdio_is_device(psz_source, driver_id); +} +%} +#else +%rename cdio_is_device is_device; +bool cdio_is_device(const char *psz_source, + driver_id_t driver_id=DRIVER_UNKNOWN); +#endif /* LIBCDIO_VERSION_NUM */ + +%rename cdio_is_nrg is_nrg; +%feature("autodoc", +"is_nrg(cue_name)->bool + +Determine if nrg_name is a Nero CD disc image"); +bool cdio_is_nrg(const char *nrg_name); + +%rename cdio_is_tocfile is_tocfile; +%feature("autodoc", +"is_tocfile(tocfile_name)->bool + +Determine if tocfile_name is a cdrdao CD disc image"); +bool cdio_is_tocfile(const char *tocfile_name); + +%rename cdio_get_media_changed get_media_changed; +%feature("autodoc", +"get_media_changed(cdio) -> int + +Find out if media has changed since the last call. +Return 1 if media has changed since last call, 0 if not. Error +return codes are the same as driver_return_code_t"); +int cdio_get_media_changed(CdIo_t *p_cdio); + +%feature("autodoc", +"get_hwinfo(p_cdio)->[drc, vendor, model, release] +Get the CD-ROM hardware info via a SCSI MMC INQUIRY command."); + +%cstring_bounded_output(char *vendor, CDIO_MMC_HW_VENDOR_LEN); +%cstring_bounded_output(char *model, CDIO_MMC_HW_MODEL_LEN); +%cstring_bounded_output(char *revision, CDIO_MMC_HW_REVISION_LEN); +int get_hwinfo ( const CdIo_t *p_cdio, + char *vendor, char *model, char *revision ); +%inline %{ +int get_hwinfo ( const CdIo_t *p_cdio, + char *vendor, char *model, char *revision ) +{ + /** There's a bug somewhere here. If we take off the static, + we clobber our parameters. So get_hwinfo sizes must not + agree. + **/ + static cdio_hwinfo_t hw_info; + bool b_got_hwinfo = cdio_get_hwinfo(p_cdio, &hw_info); + + if (b_got_hwinfo) { + strncpy(vendor, hw_info.psz_vendor, CDIO_MMC_HW_VENDOR_LEN); + strncpy(model, hw_info.psz_model, CDIO_MMC_HW_MODEL_LEN); + strncpy(revision, hw_info.psz_revision, CDIO_MMC_HW_REVISION_LEN); + } + + return b_got_hwinfo; +} +%} + +%rename cdio_set_blocksize set_blocksize; +%feature("autodoc", +"set_blocksize(cdio, blocksize)->return_status + +Set the blocksize for subsequent reads."); +driver_return_code_t cdio_set_blocksize ( const CdIo_t *p_cdio, + int i_blocksize ); + +%rename cdio_set_speed set_speed; +%feature("autodoc", +"cdio_set_speed(cdio, speed)->return_status +Set the drive speed."); +driver_return_code_t cdio_set_speed ( const CdIo_t *p_cdio, int i_speed ); + + +/** FIXME: the below works, but is clunky. + + In the next release of libcdio (or CVS right now) we have a way to + get the strings from the exception code so we don't need the switch + statement. +*/ +%exception { + $action + if (DRIVER_OP_SUCCESS == drc) goto out; + set_python_errstring(drc); + return NULL; + out: ; +} + + +/*=================================================================* + NOTE: ALL ROUTINES DEFINED BELOW CAN GIVE A DRIVER_RETURN_CODE_T + EXCEPTION. +*=================================================================*/ + +/**** Using the name open() conflicts with some C routine. + So we use open_cd() instead. +***/ +%feature("autodoc", +"open_cd(source=NULL, driver_id=None, access_mode=None) + +Sets up to read from place specified by source, driver_id and +access mode. This should be called before using any other routine +except those that act on a CD-ROM drive by name. + +If None is given as the source, we'll use the default driver device. +If None is given as the driver_id, we'll find a suitable device driver. + +Return the a pointer than can be used in subsequent operations or +None on error or no device."); +CdIo_t *open_cd(const char *psz_source=NULL, + driver_id_t driver_id=DRIVER_UNKNOWN, + const char *psz_access_mode=NULL); + +%inline %{ + +/* FIXME: Instead of a static variable drc which doesn't allow for + multiple threads, we should change the C code to return the status + parameter and have the exception handling code work off of this. + Although this is easily done in C and the SWIG-generated C wrapper + code, I don't the SWIG lingo to make the generated C wrapper do + this. + + Basically, we need a way to tell SWIG not to translate the %inline + C code return value into a Python return value, but save the value + anyway to pick it value up in the SWIG %exception. +*/ +static driver_return_code_t drc = DRIVER_OP_SUCCESS; + +static void +set_python_errstring(driver_return_code_t drc) +{ + switch(drc) { + case DRIVER_OP_SUCCESS: + break; + case DRIVER_OP_ERROR: + PyErr_SetString(PyExc_IOError, "driver I/O error."); + break; + case DRIVER_OP_UNINIT: + PyErr_SetString(PyExc_IOError, "driver not initialized."); + break; + case DRIVER_OP_UNSUPPORTED: + PyErr_SetString(PyExc_IOError, "driver operatation not supported."); + break; + case DRIVER_OP_NOT_PERMITTED: + PyErr_SetString(PyExc_IOError, "driver operatation not permitted."); + break; + case DRIVER_OP_BAD_PARAMETER: + PyErr_SetString(PyExc_IOError, "bad parameter passed."); + break; + case DRIVER_OP_BAD_POINTER: + PyErr_SetString(PyExc_IOError, "bad pointer to memory area."); + break; + case DRIVER_OP_NO_DRIVER: + PyErr_SetString(PyExc_IOError, "driver not available."); + break; + default: + PyErr_SetString(PyExc_IOError, "unknown error."); + break; + } + } + +CdIo_t *open_cd(const char *psz_source, driver_id_t driver_id, + const char *psz_access_mode) { + /* FIXME: On error we return a funny "NULL" Object. + */ + CdIo_t *p_cdio = cdio_open_am(psz_source, driver_id, psz_access_mode); + if (NULL == p_cdio) { + drc = DRIVER_OP_ERROR; + } else { + drc = DRIVER_OP_SUCCESS; + } + return p_cdio; + } +%} diff --git a/swig/device_const.swg b/swig/device_const.swg new file mode 100644 index 00000000..c3275d01 --- /dev/null +++ b/swig/device_const.swg @@ -0,0 +1,143 @@ +/* -*- c -*- + $Id: device_const.swg,v 1.5 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* See for more extensive documentation. */ + +/**** ALL OF THESE ARE CONSTANTS *** */ +%immutable; + +/* Drive types returned by cdio_get_drive_cap() */ +%constant long int DRIVE_CAP_ERROR = CDIO_DRIVE_CAP_ERROR; +%constant long int DRIVE_CAP_UNKNOWN = CDIO_DRIVE_CAP_UNKNOWN; +%constant long int DRIVE_CAP_MISC_CLOSE_TRAY = CDIO_DRIVE_CAP_MISC_CLOSE_TRAY; +%constant long int DRIVE_CAP_MISC_EJECT = CDIO_DRIVE_CAP_MISC_EJECT; +%constant long int DRIVE_CAP_MISC_LOCK = CDIO_DRIVE_CAP_MISC_LOCK ; +%constant long int DRIVE_CAP_MISC_SELECT_SPEED = CDIO_DRIVE_CAP_MISC_SELECT_SPEED; +%constant long int DRIVE_CAP_MISC_SELECT_DISC = CDIO_DRIVE_CAP_MISC_SELECT_DISC; +%constant long int DRIVE_CAP_MISC_MULTI_SESSION = CDIO_DRIVE_CAP_MISC_MULTI_SESSION; +%constant long int DRIVE_CAP_MISC_MEDIA_CHANGED = CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED; +%constant long int DRIVE_CAP_MISC_RESET = CDIO_DRIVE_CAP_MISC_RESET; +%constant long int DRIVE_CAP_MISC_FILE = CDIO_DRIVE_CAP_MISC_FILE; + +/* Reading masks.. */ +%constant long int DRIVE_CAP_READ_AUDIO = CDIO_DRIVE_CAP_READ_AUDIO; +%constant long int DRIVE_CAP_READ_CD_DA = CDIO_DRIVE_CAP_READ_CD_DA; +%constant long int DRIVE_CAP_READ_CD_G = CDIO_DRIVE_CAP_READ_CD_G; +%constant long int DRIVE_CAP_READ_CD_R = CDIO_DRIVE_CAP_READ_CD_R; +%constant long int DRIVE_CAP_READ_CD_RW = CDIO_DRIVE_CAP_READ_CD_RW; +%constant long int DRIVE_CAP_READ_DVD_R = CDIO_DRIVE_CAP_READ_DVD_R; +%constant long int DRIVE_CAP_READ_DVD_PR = CDIO_DRIVE_CAP_READ_DVD_PR; +%constant long int DRIVE_CAP_READ_DVD_RAM = CDIO_DRIVE_CAP_READ_DVD_RAM; +%constant long int DRIVE_CAP_READ_DVD_ROM = CDIO_DRIVE_CAP_READ_DVD_ROM; +%constant long int DRIVE_CAP_READ_DVD_RW = CDIO_DRIVE_CAP_READ_DVD_RW; +%constant long int DRIVE_CAP_READ_DVD_RPW = CDIO_DRIVE_CAP_READ_DVD_RPW; +%constant long int DRIVE_CAP_READ_C2_ERRS = CDIO_DRIVE_CAP_READ_C2_ERRS; +%constant long int DRIVE_CAP_READ_MODE2_FORM1 = CDIO_DRIVE_CAP_READ_MODE2_FORM1; +%constant long int DRIVE_CAP_READ_MODE2_FORM2 = CDIO_DRIVE_CAP_READ_MODE2_FORM2; +%constant long int DRIVE_CAP_READ_MCN = CDIO_DRIVE_CAP_READ_MCN; +%constant long int DRIVE_CAP_READ_ISRC = CDIO_DRIVE_CAP_READ_ISRC; + +/* Writing masks.. */ +%constant long int DRIVE_CAP_WRITE_CD_R = CDIO_DRIVE_CAP_WRITE_CD_R; +%constant long int DRIVE_CAP_WRITE_CD_RW = CDIO_DRIVE_CAP_WRITE_CD_RW; +%constant long int DRIVE_CAP_WRITE_DVD_R = CDIO_DRIVE_CAP_WRITE_DVD_R; +%constant long int DRIVE_CAP_WRITE_DVD_PR = CDIO_DRIVE_CAP_WRITE_DVD_PR; +%constant long int DRIVE_CAP_WRITE_DVD_RAM = CDIO_DRIVE_CAP_WRITE_DVD_RAM; +%constant long int DRIVE_CAP_WRITE_DVD_RW = CDIO_DRIVE_CAP_WRITE_DVD_RW ; +%constant long int DRIVE_CAP_WRITE_DVD_RPW = CDIO_DRIVE_CAP_WRITE_DVD_RPW; +%constant long int DRIVE_CAP_WRITE_MT_RAINIER = CDIO_DRIVE_CAP_WRITE_MT_RAINIER; +%constant long int DRIVE_CAP_WRITE_BURN_PROOF = CDIO_DRIVE_CAP_WRITE_BURN_PROOF; + +/*** Masks derived from above... ***/ +/* Has some sort of CD writer ability. */ +%constant long int DRIVE_CAP_WRITE_CD = CDIO_DRIVE_CAP_WRITE_CD; +/* Has some sort of DVD writer ability */ +%constant long int DRIVE_CAP_WRITE_DVD = CDIO_DRIVE_CAP_WRITE_DVD; +%constant long int DRIVE_CAP_WRITE = CDIO_DRIVE_CAP_WRITE; + +/*! Size of fields returned by an INQUIRY command */ +%constant long int MMC_HW_VENDOR_LEN = CDIO_MMC_HW_VENDOR_LEN; +%constant long int MMC_HW_MODEL_LEN = CDIO_MMC_HW_MODEL_LEN; +%constant long int MMC_HW_REVISION_LEN = CDIO_MMC_HW_REVISION_LEN; + +/**! Flags specifying the category of device to open or is opened. */ +%constant long int SRC_IS_DISK_IMAGE_MASK = CDIO_SRC_IS_DISK_IMAGE_MASK; +%constant long int SRC_IS_DEVICE_MASK = CDIO_SRC_IS_DEVICE_MASK; +%constant long int SRC_IS_SCSI_MASK = CDIO_SRC_IS_SCSI_MASK; +%constant long int SRC_IS_NATIVE_MASK = CDIO_SRC_IS_NATIVE_MASK; + +/* driver_id_t enums. */ +%constant long int DRIVER_UNKNOWN = DRIVER_UNKNOWN; +%constant long int DRIVER_AIX = DRIVER_AIX; +%constant long int DRIVER_BSDI = DRIVER_BSDI; +%constant long int DRIVER_FREEBSD = DRIVER_FREEBSD; +%constant long int DRIVER_LINUX = DRIVER_LINUX; +%constant long int DRIVER_SOLARIS = DRIVER_SOLARIS; +%constant long int DRIVER_OSX = DRIVER_OSX; +%constant long int DRIVER_WIN32 = DRIVER_WIN32; +%constant long int DRIVER_CDRDAO = DRIVER_CDRDAO; +%constant long int DRIVER_BINCUE = DRIVER_BINCUE; +%constant long int DRIVER_NRG = DRIVER_NRG; +%constant long int DRIVER_DEVICE = DRIVER_DEVICE; + +%constant long int MIN_DRIVER = CDIO_MIN_DRIVER; +%constant long int MIN_DEVICE_DRIVER = CDIO_MIN_DEVICE_DRIVER; +%constant long int MAX_DRIVER = CDIO_MAX_DRIVER; +%constant long int MAX_DEVICE_DRIVER = CDIO_MAX_DEVICE_DRIVER; + + +%constant long int DRIVER_OP_SUCCESS = DRIVER_OP_SUCCESS; +%constant long int DRIVER_OP_ERROR = DRIVER_OP_ERROR; +%constant long int DRIVER_OP_UNSUPPORTED = DRIVER_OP_UNSUPPORTED; +%constant long int DRIVER_OP_UNINIT = DRIVER_OP_UNINIT; +%constant long int DRIVER_OP_NOT_PERMITTED = DRIVER_OP_NOT_PERMITTED; +%constant long int DRIVER_OP_BAD_PARAMETER = DRIVER_OP_BAD_PARAMETER; +%constant long int DRIVER_OP_BAD_POINTER = DRIVER_OP_BAD_POINTER; +%constant long int DRIVER_OP_NO_DRIVER = DRIVER_OP_NO_DRIVER; + +%constant unsigned int FS_AUDIO = CDIO_FS_AUDIO; +%constant unsigned int FS_HIGH_SIERRA = CDIO_FS_HIGH_SIERRA; +%constant unsigned int FS_ISO_9660 = CDIO_FS_ISO_9660; +%constant unsigned int FS_INTERACTIVE = CDIO_FS_INTERACTIVE; +%constant unsigned int FS_HFS = CDIO_FS_HFS; +%constant unsigned int FS_UFS = CDIO_FS_UFS; +%constant unsigned int FS_EXT2 = CDIO_FS_EXT2; +%constant unsigned int FS_ISO_HFS = CDIO_FS_ISO_HFS; +%constant unsigned int FS_ISO_9660_INTERACTIVE = CDIO_FS_ISO_9660_INTERACTIVE; +%constant unsigned int FS_3DO = CDIO_FS_3DO; +%constant unsigned int FS_XISO = CDIO_FS_XISO; +%constant unsigned int FS_UDFX = CDIO_FS_UDFX; +%constant unsigned int FS_UDF = CDIO_FS_UDF; +%constant unsigned int FS_ISO_UDF = CDIO_FS_ISO_UDF; + +%constant unsigned int FS_ANAL_XA = CDIO_FS_ANAL_XA; +%constant unsigned int FS_ANAL_MULTISESSION = CDIO_FS_ANAL_MULTISESSION; +%constant unsigned int FS_ANAL_PHOTO_CD = CDIO_FS_ANAL_PHOTO_CD; +%constant unsigned int FS_ANAL_HIDDEN_TRACK = CDIO_FS_ANAL_HIDDEN_TRACK; +%constant unsigned int FS_ANAL_CDTV = CDIO_FS_ANAL_CDTV; +%constant unsigned int FS_ANAL_BOOTABLE = CDIO_FS_ANAL_BOOTABLE; +%constant unsigned int FS_ANAL_VIDEOCD = CDIO_FS_ANAL_VIDEOCD; +%constant unsigned int FS_ANAL_ROCKRIDGE = CDIO_FS_ANAL_ROCKRIDGE; +%constant unsigned int FS_ANAL_JOLIET = CDIO_FS_ANAL_JOLIET; +%constant unsigned int FS_ANAL_SVCD = CDIO_FS_ANAL_SVCD; +%constant unsigned int FS_ANAL_CVD = CDIO_FS_ANAL_CVD; +%constant unsigned int FS_ANAL_XISO = CDIO_FS_ANAL_XISO; +%constant unsigned int FS_MATCH_ALL = CDIO_FS_MATCH_ALL; +%constant unsigned int FS_UNKNOWN = CDIO_FS_UNKNOWN; + +%mutable; diff --git a/swig/disc.swg b/swig/disc.swg new file mode 100644 index 00000000..93ebca7f --- /dev/null +++ b/swig/disc.swg @@ -0,0 +1,96 @@ +/* -*- c -*- + $Id: disc.swg,v 1.5 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* See for more extensive documentation. */ + +%constant long int DISC_MODE_CD_DA = CDIO_DISC_MODE_CD_DA; +%constant long int DISC_MODE_CD_DATA = CDIO_DISC_MODE_CD_DATA; +%constant long int DISC_MODE_CD_XA = CDIO_DISC_MODE_CD_XA; +%constant long int DISC_MODE_CD_MIXED = CDIO_DISC_MODE_CD_MIXED; +%constant long int DISC_MODE_DVD_ROM = CDIO_DISC_MODE_DVD_ROM; +%constant long int DISC_MODE_DVD_RAM = CDIO_DISC_MODE_DVD_RAM; +%constant long int DISC_MODE_DVD_R = CDIO_DISC_MODE_DVD_R; +%constant long int DISC_MODE_DVD_RW = CDIO_DISC_MODE_DVD_RW; +%constant long int DISC_MODE_DVD_PR = CDIO_DISC_MODE_DVD_PR; +%constant long int DISC_MODE_DVD_PRW = CDIO_DISC_MODE_DVD_PRW; +%constant long int DISC_MODE_DVD_OTHER = CDIO_DISC_MODE_DVD_OTHER; +%constant long int DISC_MODE_NO_INFO = CDIO_DISC_MODE_NO_INFO; +%constant long int DISC_MODE_ERROR = CDIO_DISC_MODE_ERROR; +%constant long int DISC_MODE_CD_I = CDIO_DISC_MODE_CD_I; + +%rename cdio_get_disc_last_lsn get_disc_last_lsn; +%feature("autodoc", +"get_disc_last_lsn(cdio)->lsn +Get the LSN of the end of the CD. + +pycdio.INVALID_LSN is returned on error."); +lsn_t cdio_get_disc_last_lsn(const CdIo_t *p_cdio); + + +%feature("autodoc", +"get_disc_mode(p_cdio) -> str + +Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, ...) +that we've got. The notion of 'CD' is extended a little to include +DVD's."); +%exception get_disc_mode { + $action + if (NULL == result) { + PyErr_SetString(PyExc_IOError, "Error getting disc mode."); + return NULL; + } +} +const char *get_disc_mode (CdIo_t *p_cdio); + +%rename cdio_get_joliet_level get_joliet_level; +%feature("autodoc", +"get_joliet_level(cdio)->int + +Return the Joliet level recognized for cdio. +This only makes sense for something that has an ISO-9660 +filesystem."); +int cdio_get_joliet_level(const CdIo_t *p_cdio); + +%newobject cdio_get_mcn; // free malloc'd return value +%rename cdio_get_mcn get_mcn; +%feature("autodoc", +"get_mcn(cdio) -> str + +Get the media catalog number (MCN) from the CD."); +char * cdio_get_mcn(CdIo_t *p_cdio); + +%rename cdio_get_num_tracks get_num_tracks; +%feature("autodoc", +"get_num_tracks(p_cdio)->int + +Return the number of tracks on the CD. +On error pycdio.INVALID_TRACK is returned."); +track_t cdio_get_num_tracks (const CdIo_t *p_cdio); + +/*========================INLINE======================================*/ + +%inline %{ +const char * +get_disc_mode (CdIo_t *p_cdio) +{ + discmode_t discmode = cdio_get_discmode(p_cdio); + if (CDIO_DISC_MODE_ERROR == discmode) return NULL; + return discmode2str[discmode]; +} + +%} diff --git a/swig/pycdio.swg b/swig/pycdio.swg new file mode 100644 index 00000000..1ee0cca6 --- /dev/null +++ b/swig/pycdio.swg @@ -0,0 +1,85 @@ +/* -*- c -*- + $Id: pycdio.swg,v 1.6 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +%define DOCSTRING +"This is a wrapper for The CD Input and Control library (libcdio) which +encapsulates CD-ROM reading and control. Python programs wishing to be +oblivious of the OS- and device-dependent properties of a CD-ROM can +use this library." +%enddef +%module(docstring=DOCSTRING) pycdio +%{ +/* Includes the header in the wrapper code */ +#include +#include +#include +%} + +#include +#include + +%include "compat.swg" + +%include "typemaps.i" +%include "cstring.i" + +/* Various libcdio constants and typedefs */ +%include "types.swg" + +%feature("autodoc", 1); + +%include "audio.swg" +%include "read.swg" +%include "track.swg" + +/* In the presence of the exception handling, the order is important. + The below use %exception. +*/ +%include "device.swg" +%include "disc.swg" + +/* Encapsulation is done in two parts. The lower-level python +interface is called pycdio and is generated by SWIG. + +The more object-oriented module is cdio; it is a Python class that +uses pycdio. Although pycdio is perfectly usable on its own, it is +expected that cdio is what most people will use. As pycdio more +closely models the C interface, it is conceivable (if unlikely) that +diehard libcdio C users who are very familiar with that interface +could prefer that. + +It is probably possible to change the SWIG in such a way to combine +these pieces. However there are the problems. First, I'm not that much +of a SWIG expert. Second it looks as though the resulting SWIG code +would be more complex. Third the separation makes translation very +straight forward to understand and maintain: first get what's in C +into Python as a one-to-one translation. Then we implement some nice +abstraction off of that. The abstraction can be modified without +having to redo the underlying translation. (But the reverse is +generally not true: usually changes to the C-to-python translation, +pycdio, do result in small, but obvious and straightforward changes to +the abstraction layer cdio.) + */ +#define INCLUDE_CLASS 0 +#if INCLUDE_CLASS +/* In spite of the above, if we were to do things as a one-step approach, + this might be how it would be done. */ +%pythoncode %{ +%include "cdio.py" +%} +#endif diff --git a/swig/pyiso9660.swg b/swig/pyiso9660.swg new file mode 100644 index 00000000..0573eb8a --- /dev/null +++ b/swig/pyiso9660.swg @@ -0,0 +1,823 @@ +/* -*- c -*- + $Id: pyiso9660.swg,v 1.14 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +%define DOCSTRING +"This is a wrapper for The CD Input and Control library's ISO-9660 library +See also the ISO-9660 specification. The freely available European +equivalant standard is called ECMA-119." +%enddef +%module(docstring=DOCSTRING) pyiso9660 + +%{ +/* Includes the header in the wrapper code */ +#include +#include +#include +%} + +#include +#include +#include + +/* Various libcdio constants and typedefs */ +%include "types.swg" +%include "compat.swg" + +%include "typemaps.i" +%include "cstring.i" + +%feature("autodoc", 1); + +typedef unsigned int iso_extension_mask_t; +typedef unsigned int uint8_t; +typedef unsigned int uint16_t; + +%constant long int ISO_BLOCKSIZE = CDIO_CD_FRAMESIZE; +%constant long int PVD_SECTOR = ISO_PVD_SECTOR ; +%constant long int EVD_SECTOR = ISO_EVD_SECTOR ; +%constant long int LEN_ISONAME = LEN_ISONAME; +%constant long int MAX_SYSTEM_ID = ISO_MAX_SYSTEM_ID ; +%constant long int MAX_ISONAME = MAX_ISONAME; +%constant long int MAX_PREPARER_ID = ISO_MAX_PREPARER_ID ; +%constant long int MAX_ISOPATHNAME = MAX_ISOPATHNAME; + +%constant long int FILE = ISO_FILE; +%constant long int EXISTENCE = ISO_EXISTENCE; +%constant long int DIRECTORY = ISO_DIRECTORY; +%constant long int ASSOCIATED = ISO_ASSOCIATED; +%constant long int RECORD = ISO_RECORD; + +#include +#if LIBCDIO_VERSION_NUM <= 76 +%constant long int PROTECTION = ISO_PROTECTION; +#else +%constant long int PROTECTION = 16; +#endif + +%constant long int DRESERVED1 = ISO_DRESERVED1; +%constant long int DRESERVED2 = ISO_DRESERVED2; +%constant long int MULTIEXTENT = ISO_MULTIEXTENT; + +%constant long int VD_BOOT_RECORD = ISO_VD_BOOT_RECORD; +%constant long int VD_PRIMARY = ISO_VD_PRIMARY; +%constant long int VD_SUPPLEMENTARY = ISO_VD_SUPPLEMENTARY; +%constant long int VD_PARITION = ISO_VD_PARITION; +%constant long int VD_END = ISO_VD_END; + +%constant long int MAX_PUBLISHER_ID = ISO_MAX_PUBLISHER_ID; +%constant long int MAX_APPLICATION_ID = ISO_MAX_APPLICATION_ID; +%constant long int MAX_VOLUME_ID = ISO_MAX_VOLUME_ID; +%constant long int MAX_VOLUMESET_ID = ISO_MAX_VOLUMESET_ID; +%constant long int STANDARD_ID = ISO_STANDARD_ID; + +%constant long int NOCHECK = ISO9660_NOCHECK; +%constant long int SEVEN_BIT = ISO9660_7BIT; +%constant long int ACHARS = ISO9660_ACHARS; +%constant long int DCHARS = ISO9660_DCHARS; + +%constant long int EXTENSION_JOLIET_LEVEL1 = ISO_EXTENSION_JOLIET_LEVEL1; +%constant long int EXTENSION_JOLIET_LEVEL2 = ISO_EXTENSION_JOLIET_LEVEL2; +%constant long int EXTENSION_JOLIET_LEVEL3 = ISO_EXTENSION_JOLIET_LEVEL3; +%constant long int EXTENSION_ROCK_RIDGE = ISO_EXTENSION_ROCK_RIDGE; +%constant long int EXTENSION_HIGH_SIERRA = ISO_EXTENSION_HIGH_SIERRA; + +%constant long int EXTENSION_ALL = ISO_EXTENSION_ALL; +%constant long int EXTENSION_NONE = ISO_EXTENSION_NONE; +%constant long int EXTENSION_JOLIET = ISO_EXTENSION_JOLIET; + +/* Set up to allow functions to return stat lists of type "stat + *". We'll use a typedef so we can make sure to isolate this. +*/ +%inline %{ +typedef CdioList_t IsoStatList_t; +typedef iso9660_stat_t IsoStat_t; +%} +typedef CdioList_t IsoStatList_t; +typedef iso9660_stat_t IsoStat_t; + +%typemap(newfree) IsoStatList_t "_cdio_list_free($1, 1);"; + +%typemap(out) IsoStatList_t *{ + CdioList_t *p_entlist = result; + CdioListNode_t *p_entnode; + + if (result) { + resultobj = PyList_New(0); + _CDIO_LIST_FOREACH (p_entnode, p_entlist) { + PyObject *py_list = PyList_New(5); + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + PyObject *o; + o = PyString_FromString(p_statbuf->filename); + PyList_SetItem(py_list, 0, o); + o = SWIG_From_long((long)(p_statbuf->lsn)); + PyList_SetItem(py_list, 1, o); + o = SWIG_From_long((long)(p_statbuf->size)); + PyList_SetItem(py_list, 2, o); + o = SWIG_From_long((long)(p_statbuf->secsize)); + PyList_SetItem(py_list, 3, o); + o = SWIG_From_long((long)(p_statbuf->type)); + PyList_SetItem(py_list, 4, o); + PyList_Append(resultobj, py_list); + } + } +} +%typemap(out) IsoStat_t *{ + iso9660_stat_t *p_statbuf = result; + + if (result) { + PyObject *o; + resultobj = PyList_New(0); + o = PyString_FromString(p_statbuf->filename); + PyList_Append(resultobj, o); + o = SWIG_From_long((long)(p_statbuf->lsn)); + PyList_Append(resultobj, o); + o = SWIG_From_long((long)(p_statbuf->size)); + PyList_Append(resultobj, o); + o = SWIG_From_long((long)(p_statbuf->secsize)); + PyList_Append(resultobj, o); + o = SWIG_From_long((long)(p_statbuf->type)); + PyList_Append(resultobj, o); + free (p_statbuf); + } +} + +%typemap(out) struct tm *{ + struct tm *p_tm = result; + + if (result) { + PyObject *o; + resultobj = PyList_New(9); + o = SWIG_From_int(p_tm->tm_year+1900); + PyList_SetItem(resultobj, 0, o); + o = SWIG_From_int(p_tm->tm_mon+1); + PyList_SetItem(resultobj, 1, o); + o = SWIG_From_int(p_tm->tm_mday); + PyList_SetItem(resultobj, 2, o); + o = SWIG_From_int(p_tm->tm_hour); + PyList_SetItem(resultobj, 3, o); + o = SWIG_From_int(p_tm->tm_min); + PyList_SetItem(resultobj, 4, o); + o = SWIG_From_int(p_tm->tm_sec); + PyList_SetItem(resultobj, 5, o); + o = SWIG_From_int((p_tm->tm_wday-1)%7); + PyList_SetItem(resultobj, 6, o); + o = SWIG_From_int(p_tm->tm_yday+1); + PyList_SetItem(resultobj, 7, o); + o = SWIG_From_int(p_tm->tm_isdst); + PyList_SetItem(resultobj, 8, o); + free (p_tm); + } +} + +#ifdef FINISHED +%typemap(out) IsoStatList_t *{ + // $1 is of type IsoStatList_t + CdioList_t *p_entlist = result; + CdioListNode_t *p_entnode; + unsigned int num = 0; + + if (!result) goto out; + + PPCODE: + /* Figure out how many items in the array */ + _CDIO_LIST_FOREACH (p_entnode, p_entlist) { + num +=5; + } + + /* For each element in the array of strings, create a new + * mortalscalar, and stuff it into the above array. */ + _CDIO_LIST_FOREACH (p_entnode, p_entlist) { + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + /* Have perl compute the length of the string using strlen() */ + XPUSHs(sv_2mortal(newSVpv(p_statbuf->filename, 0))); + XPUSHs(sv_2mortal(newSViv(p_statbuf->lsn))); + XPUSHs(sv_2mortal(newSViv(p_statbuf->size))); + XPUSHs(sv_2mortal(newSViv(p_statbuf->secsize))); + XPUSHs(sv_2mortal(newSViv(p_statbuf->type))); + } + _cdio_list_free (p_entlist, true); + argvi += num + 2; + out: ; +} +#endif + +%feature("autodoc", +"open_iso(path) + Open an ISO 9660 image for reading. Maybe in the future we will have + mode. None is returned on error. +"); +%rename iso9660_open open_iso; +iso9660_t *iso9660_open (const char *psz_path /*flags, mode */); + +%feature("autodoc", +"Open an ISO 9660 image for reading allowing various ISO 9660 + extensions. Maybe in the future we will have a mode. None is + returned on error."); +%rename iso9660_open_ext open_ext; +iso9660_t *iso9660_open_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask); + +%feature("autodoc", +"Open an ISO 9660 image for reading with some tolerence for positioning +of the ISO9660 image. We scan for ISO_STANDARD_ID and use that to set +the eventual offset to adjust by (as long as that is <= i_fuzz). + +Maybe in the future we will have a mode. None is returned on error. + +see iso9660_open"); +%rename iso9660_open_fuzzy open_fuzzy; +iso9660_t *iso9660_open_fuzzy (const char *psz_path /*flags, mode */, + uint16_t i_fuzz); + +%feature("autodoc", +"Open an ISO 9660 image for reading with some tolerence for positioning +of the ISO9660 image. We scan for ISO_STANDARD_ID and use that to set +the eventual offset to adjust by (as long as that is <= i_fuzz). + +Maybe in the future we will have a mode. None is returned on error. + +see open_iso +"); +%rename iso9660_open_fuzzy open_fuzzy_ext; +iso9660_t *iso9660_open_fuzzy_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz + /*flags, mode */); +%feature("autodoc", +"Read the Super block of an ISO 9660 image but determine framesize +and datastart and a possible additional offset. Generally here we are +not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 +filesystem. +"); +%rename iso9660_ifs_fuzzy_read_superblock ifs_fuzzy_read_superblock; +bool iso9660_ifs_fuzzy_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz); +%feature("autodoc", +"Close previously opened ISO 9660 image. +True is unconditionally returned. If there was an error false would +be returned."); +%rename iso9660_close close; +bool iso9660_close (iso9660_t * p_iso); + +%feature("autodoc", +"Seek to a position and then read n bytes. (buffer, size) are + returned."); +%cstring_output_withsize(char *p_buf, ssize_t *pi_size); +ssize_t seek_read (const iso9660_t *p_iso, + lsn_t start, char *p_buf, ssize_t *pi_size); +%inline %{ +ssize_t +seek_read (const iso9660_t *p_iso, lsn_t start, char *p_buf, + ssize_t *pi_size) +{ + *pi_size = iso9660_iso_seek_read(p_iso, p_buf, start, + (*pi_size) / ISO_BLOCKSIZE); + return *pi_size; + } +%} + + +%feature("autodoc", +"Read the Primary Volume Descriptor for a CD. +None is returned if there was an error."); +iso9660_pvd_t *fs_read_pvd ( const CdIo_t *p_cdio ); +%inline %{ +iso9660_pvd_t *fs_read_pvd ( const CdIo_t *p_cdio ) { + static iso9660_pvd_t pvd; + bool b_ok = iso9660_fs_read_pvd ( p_cdio, &pvd ); + if (!b_ok) return NULL; + return &pvd; + } +%} + +%feature("autodoc", +"Read the Primary Volume Descriptor for an ISO 9660 image. +None is returned if there was an error."); +iso9660_pvd_t *ifs_read_pvd ( const iso9660_t *p_iso ); +%inline %{ +iso9660_pvd_t *ifs_read_pvd ( const iso9660_t *p_iso ) { + static iso9660_pvd_t pvd; + bool b_ok = iso9660_ifs_read_pvd ( p_iso, &pvd ); + if (!b_ok) return NULL; + return &pvd; + } +%} + +%feature("autodoc", +"Read the Super block of an ISO 9660 image. This is the +Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume +Descriptor if (Joliet) extensions are acceptable."); +%rename iso9660_fs_read_superblock fs_read_superblock; +bool iso9660_fs_read_superblock (CdIo_t *p_cdio, + iso_extension_mask_t iso_extension_mask); + +%feature("autodoc", +"Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable."); +%rename iso9660_ifs_read_superblock ifs_read_superblock; +bool iso9660_ifs_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask); + + +/*==================================================== + Time conversion + ====================================================*/ +%feature("autodoc", +"Set time in format used in ISO 9660 directory index record"); +iso9660_dtime_t * +set_dtime ( int year, int mon, int mday, int hour, int min, int sec); +%inline %{ +iso9660_dtime_t * +set_dtime ( int year, int mon, int mday, int hour, int min, int sec) +{ + struct tm tm = { sec, min, hour, mday, mon-1, year-1900, 0, 0, -1 }; + static iso9660_dtime_t dtime; + iso9660_set_dtime (&tm, &dtime); + return &dtime; +} +%} + +%feature("autodoc", +"Set 'long' time in format used in ISO 9660 primary volume descriptor"); +iso9660_ltime_t * +set_ltime ( int year, int mon, int mday, int hour, int min, int sec); + +%inline %{ +iso9660_ltime_t * +set_ltime ( int year, int mon, int mday, int hour, int min, int sec) + { + struct tm tm = { sec, min, hour, mday, mon-1, year-1900, 0, 0, -1 }; + static iso9660_ltime_t ldate; + iso9660_set_ltime (&tm, &ldate); + return &ldate; +} +%} + +%feature("autodoc", +"Get Unix time structure from format use in an ISO 9660 directory index +record. Even though tm_wday and tm_yday fields are not explicitly in +idr_date, they are calculated from the other fields. + +If tm is to reflect the localtime, set 'use_localtime' true, otherwise +tm will reported in GMT."); +struct tm *get_dtime (const iso9660_dtime_t *p_dtime, bool use_localtime); +%inline %{ +struct tm *get_dtime (const iso9660_dtime_t *p_dtime, bool use_localtime) { + struct tm *p_tm = (struct tm *) calloc(1, sizeof(struct tm)); + if (!iso9660_get_dtime (p_dtime, use_localtime, p_tm)) { + free(p_tm); + return NULL; + } + return p_tm; + } +%} + +%feature("autodoc", +"Get 'long' time in format used in ISO 9660 primary volume descriptor + from a Unix time structure."); +struct tm *get_ltime (const iso9660_ltime_t *p_ltime); +%inline %{ +struct tm *get_ltime (const iso9660_ltime_t *p_ltime) +{ + struct tm *p_tm = (struct tm *) calloc(1, sizeof(struct tm)); + if (!iso9660_get_ltime (p_ltime, p_tm)) { + free(p_tm); + return NULL; + } + return p_tm; +} +%} + +/*======================================================== + Characters used in file and directory and manipulation + =======================================================*/ +%feature("autodoc", +" Return true if c is a DCHAR - a character that can appear in an an + ISO-9600 level 1 directory name. These are the ASCII capital + letters A-Z, the digits 0-9 and an underscore."); +%rename iso9660_isdchar is_dchar; +bool iso9660_isdchar (int c); + +%feature("autodoc", +"Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol."); +%rename iso9660_isachar is_achar; +bool iso9660_isachar (int c); + +%feature("autodoc", +"Convert an ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. + Lowercase name, and remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @return length of the translated string is returned."); +%newobject name_translate; +char * name_translate(const char *psz_oldname); +%inline %{ +char * +name_translate(const char *psz_oldname) { + char *psz_newname=calloc(sizeof(char), strlen(psz_oldname)+1); + iso9660_name_translate(psz_oldname, psz_newname); + return psz_newname; +} +%} + +%feature("autodoc", +"Convert an ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. Lowercase + name if no Joliet Extension interpretation. Remove trailing ;1's or + .;1's and turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @param i_joliet_level 0 if not using Joliet Extension. Otherwise the + Joliet level. + @return length of the translated string is returned. It will be no greater + than the length of psz_oldname."); +%newobject name_translate_ext; +char * name_translate_ext(const char *psz_oldname, uint8_t i_joliet_level); +%inline %{ +char * +name_translate_ext(const char *psz_oldname, uint8_t i_joliet_level) { + char *psz_newname=calloc(sizeof(char), strlen(psz_oldname)+1); + iso9660_name_translate_ext(psz_oldname, psz_newname, i_joliet_level); + return psz_newname; +} +%} + +%feature("autodoc", +"Pad string src with spaces to size len and copy this to dst. If + en is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated."); +%newobject strncpy_pad; // free malloc'd return value +char *strncpy_pad(const char src[], size_t len, enum strncpy_pad_check _check); +%inline %{ +char * +strncpy_pad(const char src[], size_t len, enum strncpy_pad_check _check) { + char *dst = calloc(sizeof(char), len+1); + return iso9660_strncpy_pad(dst, src, len, _check); +} +%} + +/*===================================================================== + Files and Directory Names +======================================================================*/ + +%feature("autodoc", +"Check that psz_path is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + + True is returned if psz_path is valid."); +%rename iso9660_dirname_valid_p dirname_valid_p; +bool iso9660_dirname_valid_p (const char psz_path[]); + +%feature("autodoc", +"Take psz_path and a version number and turn that into a ISO-9660 +pathname. (That's just the pathname followed by ';' and the version +number. For example, mydir/file.ext -> MYDIR/FILE.EXT;1 for version +1. The resulting ISO-9660 pathname is returned."); +%rename iso9660_pathname_isofy pathname_isofy; +%newobject iso9660_pathname_isofy; // free malloc'd return value +char *iso9660_pathname_isofy (const char psz_path[], uint16_t i_version=1); + +%feature("autodoc", +"Check that psz_path is a valid ISO-9660 pathname. + +A valid pathname contains a valid directory name, if one appears and +the filename portion should be no more than 8 characters for the +file prefix and 3 characters in the extension (or portion after a +dot). There should be exactly one dot somewhere in the filename +portion and the filename should be composed of only DCHARs. + +True is returned if psz_path is valid."); +%rename iso9660_pathname_valid_p pathname_valid_p; +bool iso9660_pathname_valid_p (const char psz_path[]); + +/* ... */ + +%feature("autodoc", +"Given a directory pointer, find the filesystem entry that contains +lsn and return information about it. + +Returns stat_t of entry if we found lsn, or None otherwise."); +#if 0 +%rename iso9660_find_fs_lsn fs_find_lsn; +IsoStat_t *iso9660_find_fs_lsn(CdIo_t *p_cdio, lsn_t i_lsn); + + +%feature("autodoc", +"Given a directory pointer, find the filesystem entry that contains +lsn and return information about it. + +Returns stat_t of entry if we found lsn, or None otherwise."); +%rename iso9660_find_ifs_lsn ifs_find_lsn; +IsoStat_t *iso9660_find_ifs_lsn(const iso9660_t *p_iso, lsn_t i_lsn); +#endif + + +%feature("autodoc", +"Return file status for psz_path. None is returned on error."); +%rename iso9660_fs_stat fs_stat; +IsoStat_t *iso9660_fs_stat (CdIo_t *p_cdio, const char psz_path[]); + + +%feature("autodoc", +"Return file status for path name psz_path. None is returned on error. +pathname version numbers in the ISO 9660 name are dropped, i.e. ;1 +is removed and if level 1 ISO-9660 names are lowercased. + +The b_mode2 parameter is not used."); +%rename iso9660_fs_stat_translate fs_stat_translate; +IsoStat_t *iso9660_fs_stat_translate (CdIo_t *p_cdio, + const char psz_path[], + bool b_mode2=false); + +%feature("autodoc", +"Return file status for pathname. None is returned on error."); +%rename iso9660_ifs_stat ifs_stat; +IsoStat_t *iso9660_ifs_stat (iso9660_t *p_iso, const char psz_path[]); + + +%feature("autodoc", +"Return file status for path name psz_path. undef is returned on error. +pathname version numbers in the ISO 9660 name are dropped, i.e. ;1 is +removed and if level 1 ISO-9660 names are lowercased."); +%rename iso9660_ifs_stat_translate ifs_stat_translate; +IsoStat_t *iso9660_ifs_stat_translate (iso9660_t *p_iso, + const char psz_path[]); + +%feature("autodoc", +"Read psz_path (a directory) and return a list of iso9660_stat_t +pointers for the files inside that directory."); +IsoStatList_t *fs_readdir (CdIo_t *p_cdio, const char psz_path[]); + +%inline %{ +IsoStatList_t *fs_readdir (CdIo_t *p_cdio, const char psz_path[]) +{ + CdioList_t *p_statlist = iso9660_fs_readdir (p_cdio, psz_path, false); + return p_statlist; +} +%} + +%feature("autodoc", +"Read psz_path (a directory) and return a list of iso9660_stat_t +pointers for the files inside that directory."); +IsoStatList_t *ifs_readdir (iso9660_t *p_iso, const char psz_path[]); + +%inline %{ +IsoStatList_t *ifs_readdir (iso9660_t *p_iso, const char psz_path[]) +{ + CdioList_t *p_statlist = iso9660_ifs_readdir (p_iso, psz_path); + return p_statlist; +} +%} + +%feature("autodoc", +"Return the PVD's application ID. +None is returned if there is some problem in getting this."); +%rename iso9660_get_application_id get_application_id; +char * iso9660_get_application_id(iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the application ID. Return None if there +is some problem in getting this."); +%newobject get_application_id; // free malloc'd return value +char *ifs_get_application_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_application_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_application_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + + +%feature("autodoc", +"Return the Joliet level recognized for p_iso."); +%rename iso9660_ifs_get_joliet_level get_joliet_level; +uint8_t iso9660_ifs_get_joliet_level(iso9660_t *p_iso); + +%rename iso9660_get_dir_len get_dir_len; +uint8_t iso9660_get_dir_len(const iso9660_dir_t *p_idr); + +%feature("autodoc", +"Return the directory name stored in the iso9660_dir_t."); +%newobject iso9660_dir_to_name; // free malloc'd return value +%rename iso9660_get_to_name get_to_name; +char * iso9660_dir_to_name (const iso9660_dir_t *p_iso9660_dir); + +#if LIBCDIO_VERSION_NUM > 76 +%feature("autodoc", +"Returns a POSIX mode for a given p_iso_dirent."); +%rename iso9660_get_posix_filemode get_posix_filemode; +mode_t iso9660_get_posix_filemode(const iso9660_stat_t *p_iso_dirent); +#endif + +%feature("autodoc", +"Return a string containing the preparer id with trailing +blanks removed."); +%rename iso9660_get_preparer_id get_preparer_id; +char *iso9660_get_preparer_id(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the preparer ID. Return None if there is some problem in + getting this."); +%newobject ifs_get_preparer_id; // free malloc'd return value +char *ifs_get_preparer_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_preparer_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_preparer_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + +%feature("autodoc", +"Return a string containing the PVD's publisher id with trailing + blanks removed."); +%rename iso9660_get_publisher_id get_publisher_id; +char *iso9660_get_publisher_id(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the publisher ID. Return None if there +is some problem in getting this."); +%newobject ifs_get_publisher_id; // free malloc'd return value +char *ifs_get_publisher_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_publisher_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_publisher_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + +%rename iso9660_get_pvd_type get_pvd_type; +uint8_t iso9660_get_pvd_type(const iso9660_pvd_t *p_pvd); + +%rename iso9660_get_pvd_id get_pvd_id; +const char * iso9660_get_pvd_id(const iso9660_pvd_t *p_pvd); + +%rename iso9660_get_pvd_space_size get_pvd_space_size; +int iso9660_get_pvd_space_size(const iso9660_pvd_t *p_pvd); + +%rename iso9660_get_pvd_block_size get_pvd_block_size; +int iso9660_get_pvd_block_size(const iso9660_pvd_t *p_pvd) ; + +%feature("autodoc", +"Return the primary volume id version number (of pvd). +If there is an error 0 is returned."); +%rename iso9660_get_pvd_version get_pvd_version; +int iso9660_get_pvd_version(const iso9660_pvd_t *pvd) ; + +%feature("autodoc", +"Return a string containing the PVD's system id with trailing + blanks removed."); +%rename iso9660_get_system_id get_system_id; +char *iso9660_get_system_id(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the system ID. None is returned if there +is some problem in getting this."); +%newobject ifs_get_system_id; // free malloc'd return value +char *ifs_get_system_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_system_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_system_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + +%feature("autodoc", +"Return the LSN of the root directory for pvd. If there is an error +INVALID_LSN is returned. +"); +%rename iso9660_get_root_lsn get_root_lsn; +lsn_t iso9660_get_root_lsn(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Return the PVD's volume ID."); +%rename iso9660_get_volume_id get_volume_id; +char *iso9660_get_volume_id(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the system ID. None is returned if there +is some problem in getting this."); +%newobject ifs_get_volume_id; // free malloc'd return value +char *ifs_get_volume_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_volume_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_volume_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + +%feature("autodoc", +" Return the PVD's volumeset ID. + None is returned if there is some problem in getting this. +"); +%rename iso9660_get_volumeset_id get_volumeset_id; +char *iso9660_get_volumeset_id(const iso9660_pvd_t *p_pvd); + +%feature("autodoc", +"Get the volumeset ID. None is returned if there +is some problem in getting this."); +%newobject ifs_get_volumeset_id; // free malloc'd return value +char *ifs_get_volumeset_id(iso9660_t *p_iso); +%inline %{ +char * +ifs_get_volumeset_id(iso9660_t *p_iso) { + char *psz; + bool ok = iso9660_ifs_get_volumeset_id(p_iso, &psz); + if (!ok) return NULL; + return psz; +} +%} + +/* ================= pathtable ================== */ + +%feature("autodoc", +"Zero's out pathable. Do this first."); +%rename iso9660_pathtable_init pathtable_init; +void iso9660_pathtable_init (void *pt); + +%rename iso9660_pathtable_get_size pathtable_get_size; +unsigned int iso9660_pathtable_get_size (const void *pt); + +%rename iso9660_pathtable_l_add_entry pathtable_l_add_entry; +uint16_t iso9660_pathtable_l_add_entry (void *pt, const char name[], + uint32_t extent, uint16_t parent); + +%rename iso9660_pathtable_m_add_entry pathtable_m_add_entry; +uint16_t iso9660_pathtable_m_add_entry (void *pt, const char name[], + uint32_t extent, uint16_t parent); + +/*====================================================================== + Volume Descriptors +========================================================================*/ + +#ifdef FINSHED +void iso9660_set_pvd (void *pd, const char volume_id[], + const char application_id[], + const char publisher_id[], const char preparer_id[], + uint32_t iso_size, const void *root_dir, + uint32_t path_table_l_extent, + uint32_t path_table_m_extent, + uint32_t path_table_size, const time_t *pvd_time); +#endif /*FINISHED*/ + +%rename iso9660_set_evd set_evd; +void iso9660_set_evd (void *pd); + +%feature("autodoc", +"Return true if ISO 9660 image has extended attrributes (XA)."); +%rename iso9660_ifs_is_xa is_xa; +bool iso9660_ifs_is_xa (const iso9660_t * p_iso); + +// %pythoncode %{ +//%} diff --git a/swig/read.swg b/swig/read.swg new file mode 100644 index 00000000..76a52fd6 --- /dev/null +++ b/swig/read.swg @@ -0,0 +1,167 @@ +/* -*- c -*- + $Id: read.swg,v 1.7 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* See for more extensive documentation. */ + +%constant long int READ_MODE_AUDIO = CDIO_READ_MODE_AUDIO; +%constant long int READ_MODE_M1F1 = CDIO_READ_MODE_M1F1; +%constant long int READ_MODE_M1F2 = CDIO_READ_MODE_M1F2; +%constant long int READ_MODE_M2F1 = CDIO_READ_MODE_M2F1; +%constant long int READ_MODE_M2F2 = CDIO_READ_MODE_M2F2; + +typedef int cdio_read_mode_t; + +%rename cdio_lseek lseek; +%feature("autodoc", +"lseek(cdio, offset, whence)->int +Reposition read offset +Similar to (if not the same as) libc's fseek() + +cdio is object to get adjested, offset is amount to seek and +whence is like corresponding parameter in libc's lseek, e.g. +it should be SEEK_SET or SEEK_END. + +the offset is returned or -1 on error."); +off_t cdio_lseek(const CdIo_t *p_cdio, int offset, int whence=SEEK_SET); + +%feature("autodoc", +"read_cd(cdio, size)->[size, data] + +Reads into buf the next size bytes. +Similar to (if not the same as) libc's read() + +The number of reads read is returned. -1 is returned on error."); +%cstring_output_withsize(char *p_buf, ssize_t *pi_size); +ssize_t read_cd(const CdIo_t *p_cdio, char *p_buf, ssize_t *pi_size); + +%inline %{ +ssize_t +read_cd(const CdIo_t *p_cdio, char *p_buf, ssize_t *pi_size) +{ + *pi_size = cdio_read(p_cdio, p_buf, *pi_size); + return *pi_size; +} +%} + + +%feature("autodoc", +"read_sectors(bytes, lsn, read_mode)->[size, data] +Reads a number of sectors (AKA blocks). + +lsn is sector to read, bytes is the number of bytes. + +If read_mode is pycdio.MODE_AUDIO, the return buffer size will be +truncated to multiple of pycdio.CDIO_FRAMESIZE_RAW i_blocks bytes. + +If read_mode is pycdio.MODE_DATA, buffer will be truncated to a +multiple of pycdio.ISO_BLOCKSIZE, pycdio.M1RAW_SECTOR_SIZE or +pycdio.M2F2_SECTOR_SIZE bytes depending on what mode the data is in. +If read_mode is pycdio.CDIO_MODE_M2F1, buffer will be truncated to a +multiple of pycdio.M2RAW_SECTOR_SIZE bytes. + +If read_mode is CDIO_MODE_M2F2, the return buffer size will be +truncated to a multiple of pycdio.CD_FRAMESIZE bytes. + +If size <= 0 an error has occurred."); + +%inline %{ + +ssize_t read_sectors(const CdIo_t *p_cdio, char *p_buf, ssize_t *pi_size, + lsn_t i_lsn, cdio_read_mode_t read_mode) +{ + + driver_return_code_t drc; + uint32_t i_blocks; + uint16_t i_blocksize; + switch (read_mode) { + case CDIO_READ_MODE_AUDIO: + i_blocksize = CDIO_CD_FRAMESIZE_RAW; + break; + case CDIO_READ_MODE_M1F1: + i_blocksize = M2RAW_SECTOR_SIZE; + break; + case CDIO_READ_MODE_M1F2: + i_blocksize = M2RAW_SECTOR_SIZE; + break; + case CDIO_READ_MODE_M2F1: + i_blocksize = CDIO_CD_FRAMESIZE; + break; + case CDIO_READ_MODE_M2F2: + i_blocksize = M2F2_SECTOR_SIZE; + break; + default: + pi_size = NULL; + return DRIVER_OP_BAD_PARAMETER; + } + + i_blocks = *pi_size / i_blocksize; + drc = cdio_read_sectors(p_cdio, p_buf, i_lsn, read_mode, i_blocks); + if (drc < 0) { + pi_size = NULL; + return drc; + } + return *pi_size; +} +%} + +%feature("autodoc", +"read_data_bytes(lsn, bytes) + +Reads a number of data sectors (AKA blocks). + +lsn is sector to read, bytes is the number of bytes. +If you don't know whether you have a Mode 1/2, +Form 1/ Form 2/Formless sector best to reserve space for the maximum +which is pycdio.M2RAW_SECTOR_SIZE. + +If size <= 0 an error has occurred."); +%inline %{ +ssize_t read_data_bytes(const CdIo_t *p_cdio, char *p_buf, ssize_t *pi_size, + lsn_t i_lsn, int16_t i_blocksize) +{ + driver_return_code_t drc; + uint32_t i_blocks = *pi_size / i_blocksize; + + switch (i_blocksize) { + case CDIO_CD_FRAMESIZE: + case CDIO_CD_FRAMESIZE_RAW: + case M2F2_SECTOR_SIZE: + case M2RAW_SECTOR_SIZE: + break; + default: + /* Don't know about these block sizes */ + pi_size = NULL; + return DRIVER_OP_BAD_PARAMETER; + } + +#if DEBUGGING + printf("p_cdio: %x, i_size: %d, lsn: %d, blocksize %d, blocks %d\n", + p_cdio, *pi_size, i_lsn, i_blocksize, i_blocks); +#endif + drc = cdio_read_data_sectors (p_cdio, p_buf, i_lsn, + i_blocksize, i_blocks); +#if DEBUGGING + printf("drc: %d\n", drc); +#endif + if (drc < 0) { + pi_size = NULL; + return drc; + } + return *pi_size; +} +%} diff --git a/swig/track.swg b/swig/track.swg new file mode 100644 index 00000000..a023c81a --- /dev/null +++ b/swig/track.swg @@ -0,0 +1,206 @@ +/* -*- c -*- + $Id: track.swg,v 1.4 2008/05/01 16:55:05 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* See For more extensive documentation */ +%constant long int TRACK_FORMAT_AUDIO = TRACK_FORMAT_AUDIO; +%constant long int TRACK_FORMAT_CDI = TRACK_FORMAT_CDI; +%constant long int TRACK_FORMAT_XA = TRACK_FORMAT_XA; +%constant long int TRACK_FORMAT_DATA = TRACK_FORMAT_DATA; +%constant long int TRACK_FORMAT_PSX = TRACK_FORMAT_PSX; + +%constant long int CDIO_TRACK_FLAG_FALSE = CDIO_TRACK_FLAG_FALSE; +%constant long int CDIO_TRACK_FLAG_TRUE = CDIO_TRACK_FLAG_TRUE; +%constant long int CDIO_TRACK_FLAG_ERROR = CDIO_TRACK_FLAG_ERROR; +%constant long int CDIO_TRACK_FLAG_UNKNOWN = CDIO_TRACK_FLAG_UNKNOWN; + +%constant long int CDIO_CDROM_LBA = CDIO_CDROM_LBA; +%constant long int CDIO_CDROM_MSF = CDIO_CDROM_MSF; +%constant long int CDIO_CDROM_DATA_TRACK = CDIO_CDROM_DATA_TRACK; +%constant long int CDIO_CDROM_CDI_TRACK = CDIO_CDROM_CDI_TRACK; +%constant long int CDIO_CDROM_XA_TRACK = CDIO_CDROM_XA_TRACK; + +%constant long int AUDIO = AUDIO; +%constant long int MODE1 = MODE1; +%constant long int MODE1_RAW = MODE1_RAW; +%constant long int MODE2 = MODE2; +%constant long int MODE2_FORM1 = MODE2_FORM1; +%constant long int MODE2_FORM2 = MODE2_FORM2; +%constant long int MODE2_FORM_MIX = MODE2_FORM_MIX; +%constant long int MODE2_RAW = MODE2_RAW; + +%constant long int INVALID_TRACK = CDIO_INVALID_TRACK; +%constant long int CDROM_LEADOUT_TRACK = 0xAA; + +typedef int track_flag_t; + +%rename cdio_get_first_track_num get_first_track_num; +%feature("autodoc", +"get_first_track_num(p_cdio) -> int +Get the number of the first track. + +return the track number or pycdio.INVALID_TRACK if there was +a problem."); +track_t cdio_get_first_track_num(const CdIo_t *p_cdio); + +%rename cdio_get_last_track_num get_last_track_num ; +%feature("autodoc", +"get_last_track_num +Return the last track number. +pycdio.INVALID_TRACK is if there was a problem."); +track_t cdio_get_last_track_num (const CdIo_t *p_cdio); + +%rename cdio_get_track get_track; +%feature("autodoc", +"cdio_get_track(lsn)->int + + Find the track which contains lsn. + pycdio.INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track, 0 is returned. + Otherwise we return the track that spans the lsn."); +track_t cdio_get_track(const CdIo_t *p_cdio, lsn_t lsn); + +%rename cdio_get_track_channels get_track_channels; +%feature("autodoc", +"get_track_channels(cdio, track)->int + +Return number of channels in track: 2 or 4; -2 if implemented or -1 +for error. Not meaningful if track is not an audio track."); +int cdio_get_track_channels(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_copy_permit get_track_copy_permit; +%feature("autodoc", +"get_copy_permit(cdio, track)->int + +Return copy protection status on a track. Is this meaningful +not an audio track? +"); +track_flag_t cdio_get_track_copy_permit(const CdIo_t *p_cdio, + track_t i_track); + +%feature("autodoc", +"get_track_format(cdio, track)->format + +Get the format (audio, mode2, mode1) of track. "); +const char *get_track_format(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_green is_track_green; +%feature("autodoc", +"is_track_green(cdio, track) -> bool + +Return True if we have XA data (green, mode2 form1) or +XA data (green, mode2 form2). That is track begins: + sync - header - subheader +12 4 - 8 + +FIXME: there's gotta be a better design for this and get_track_format?"); +bool cdio_get_track_green(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_last_lsn get_track_last_lsn; +%feature("autodoc", +"cdio_get_track_last_lsn(cdio, track)->lsn + +Return the ending LSN for track number +track in cdio. CDIO_INVALID_LSN is returned on error."); +lsn_t cdio_get_track_last_lsn(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_lba get_track_lba; +%feature("autodoc", +"cdio_get_track_lba + Get the starting LBA for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The 'leadout' track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LBA or CDIO_INVALID_LBA on error."); + lba_t cdio_get_track_lba(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_lsn get_track_lsn; +%feature("autodoc", +"cdio_get_track_lsn (cdio, track)->int + +Return the starting LSN for track number. +Track numbers usually start at something greater than 0, usually 1. + +The 'leadout' track is specified either by +using i_track pycdio.CDROM_LEADOUT_TRACK or the total tracks+1. + +pycdio.INVALID_LSN is returned on error."); +lsn_t cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track); + +%feature("autodoc", +"get_track_msf(cdio,track)->string + + Return the starting MSF (minutes/secs/frames) for track number + track. Track numbers usually start at something + greater than 0, usually 1. + + The 'leadout' track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + +@return string mm:ss:ff if all good, or string 'error' on error."); +char *get_track_msf(const CdIo_t *p_cdio, track_t i_track); +%inline %{ +char *get_track_msf(const CdIo_t *p_cdio, track_t i_track) +{ + msf_t msf; + + if (!cdio_get_track_msf( p_cdio, i_track, &msf )) { + return NULL; + } else { + return cdio_msf_to_str( &msf ); + } +} +%} + +%rename cdio_get_track_preemphasis get_track_preemphasis; +%feature("autodoc", +"cdio_get_track_preemphasis(cdio, track) + +Get linear preemphasis status on an audio track. +This is not meaningful if not an audio track?"); +track_flag_t cdio_get_track_preemphasis(const CdIo_t *p_cdio, track_t i_track); + +%rename cdio_get_track_sec_count get_track_sec_count; +%feature("autodoc", +"get_track_sec_count(cdio, track)->int + +Get the number of sectors between this track an the next. This +includes any pregap sectors before the start of the next track. +Track numbers usually start at something +greater than 0, usually 1. + +0 is returned if there is an error."); +unsigned int cdio_get_track_sec_count(const CdIo_t *p_cdio, track_t i_track); + +%inline %{ +const char +*get_track_format(const CdIo_t *p_cdio, track_t i_track) +{ + track_format_t track_format = cdio_get_track_format(p_cdio, i_track); + return track_format2str[track_format]; +} +%} + + diff --git a/swig/types.swg b/swig/types.swg new file mode 100644 index 00000000..883f9315 --- /dev/null +++ b/swig/types.swg @@ -0,0 +1,56 @@ +/* -*- c -*- + $Id: types.swg,v 1.6 2008/05/01 16:55:06 karl Exp $ + + Copyright (C) 2006, 2008 Rocky Bernstein + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Various typedef's "constant"s/variables + + Many of these are documented more fully in libcdio's + I'm not sure why, but including that directly will not allow + SWIG to understand say that a lsn_t is an int. +*/ +#include + +#ifdef NEED_LONG +#define LONG long +#else +#define LONG +#endif + +#define uint32_t LONG unsigned int +#define int32_t LONG int + +typedef long int driver_id_t; +typedef int lsn_t; +typedef int int16_t; +typedef int lba_t; +typedef unsigned int track_t; +typedef long int driver_return_code_t; +typedef long int ssize_t; + +%constant long int VERSION_NUM = LIBCDIO_VERSION_NUM; + +%constant long int INVALID_LBA = CDIO_INVALID_LBA; +%constant long int INVALID_LSN = CDIO_INVALID_LSN; + +/* More documentation on the below is in cdio/sector.h */ +%constant long int CD_FRAMESIZE = CDIO_CD_FRAMESIZE; +%constant long int CD_FRAMESIZE_RAW = CDIO_CD_FRAMESIZE_RAW; +%constant long int ISO_BLOCKSIZE = CDIO_CD_FRAMESIZE; +%constant long int M2F2_SECTOR_SIZE = M2F2_SECTOR_SIZE; +%constant long int M2RAW_SECTOR_SIZE = M2RAW_SECTOR_SIZE; diff --git a/test/cdda.bin b/test/cdda.bin new file mode 100644 index 00000000..b1ba1f5e Binary files /dev/null and b/test/cdda.bin differ diff --git a/test/cdda.cue b/test/cdda.cue new file mode 100644 index 00000000..e48e1a93 --- /dev/null +++ b/test/cdda.cue @@ -0,0 +1,7 @@ +TITLE "Join us now we have the software" +CATALOG 0000010271955 +PERFORMER "Richard Stallman" +FILE "BOING.BIN" BINARY + TRACK 01 AUDIO + FLAGS DCP + INDEX 01 00:00:00 diff --git a/test/cdda.toc b/test/cdda.toc new file mode 100644 index 00000000..31c6ce20 --- /dev/null +++ b/test/cdda.toc @@ -0,0 +1,14 @@ +// $Id: cdda.toc,v 1.1.1.1 2006/01/25 04:32:48 rocky Exp $ + +// Language number should always start with 0 +LANGUAGE 0 { + // Required fields - at least all CD-TEXT CDs I've seen so far have them. + TITLE "Join us now we have the software" + PERFORMER "Richard Stallman" +} +CATALOG "0000010271955" +CD_DA + +TRACK AUDIO +COPY +FILE "cdda.bin" 00:00:00 00:00:00 diff --git a/test/test-cdio.py b/test/test-cdio.py new file mode 100755 index 00000000..4e57cd8b --- /dev/null +++ b/test/test-cdio.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +"""Unit test for cdio + +Note: for compatibility with old unittest 1.46 we won't use assertTrue +or assertFalse.""" +import unittest, sys, os + +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import cdio + +class CdioTests(unittest.TestCase): + + def test_ops(self): + """Test running miscellaneous operations + No assumption about the CD-ROM drives is made, so + we're just going to run operations and see that they + don't crash.""" + self.device = cdio.Device() + if pycdio.VERSION_NUM >= 76: + # FIXME: Broken on Darwin? + # self.device.open() + self.device.have_ATAPI() + # FIXME: Broken on Darwin? + # self.device.get_media_changed() + self.assertEqual(True, True, "Test misc operations") + + def test_device_default(self): + """Test getting default device""" + result1=cdio.get_default_device_driver(pycdio.DRIVER_DEVICE) + result2=cdio.get_default_device_driver() + self.assertEqual(result1, result2, + "get_default_device with/out parameters") + self.device = cdio.Device() + result2=pycdio.get_device() + if result1 is not None: + self.assertEqual(result1[0], result2) + # Now try getting device using driver that we got back + try: + device=cdio.Device(driver_id=result1[1]) + result1 = device.get_device() + self.assertEqual(result1, result2, + "get_default_device using driver name") + except: + pass + + def test_exceptions(self): + """Test that various routines raise proper exceptions""" + self.device = cdio.Device() + # No CD or or CD image has been set yet. So these fail + try: + lsn = self.device.get_disc_last_lsn() + except IOError: + self.assertEqual(True, True, "get_last_lsn() IO Error") + except cdio.DriverError: + self.assertEqual(True, True, "get_last_lsn() DriverError") + else: + self.assertTrue(False, "get_last_lsn() should raise error") + self.assertRaises(IOError, self.device.get_disc_mode) + try: + track = self.device.get_num_tracks() + except IOError: + self.assertEqual(True, True, "get_num_tracks() IO Error") + except cdio.DriverError: + self.assertEqual(True, True, "get_num_tracks() DriverError") + except cdio.TrackError: + self.assertEqual(True, True, "get_num_tracks() TrackError") + else: + self.assertTrue(False, "get_last_lsn() should raise error") + self.assertRaises(IOError, self.device.get_driver_name) + self.assertRaises(cdio.DriverUninitError, + self.device.get_media_changed) + self.assertRaises(IOError, self.device.open, "***Invalid device***") + + def test_have_image_drivers(self): + """Test that we have image drivers""" + result = cdio.have_driver('CDRDAO') + self.assertEqual(True, result, "Have cdrdrao driver via string") + result = cdio.have_driver(pycdio.DRIVER_CDRDAO) + self.assertEqual(True, result, "Have cdrdrao driver via driver_id") + result = cdio.have_driver('NRG') + self.assertEqual(True, result, "Have NRG driver via string") + result = cdio.have_driver(pycdio.DRIVER_NRG) + self.assertEqual(True, result, "Have NRG driver via driver_id") + result = cdio.have_driver('BIN/CUE') + self.assertEqual(True, result, "Have BIN/CUE driver via string") + result = cdio.have_driver(pycdio.DRIVER_BINCUE) + self.assertEqual(True, result, "Have BIN/CUE driver via driver_id") + + def test_tocfile(self): + """Test functioning of cdrdao image routines""" + ## TOC reading needs to be done in the directory where the + ## TOC/BIN files reside. + olddir=os.getcwd() + os.chdir('.') + tocfile="cdda.toc" + device = cdio.Device(tocfile, pycdio.DRIVER_CDRDAO) + ok, vendor, model, revision = device.get_hwinfo() + self.assertEqual(True, ok, "get_hwinfo ok") + self.assertEqual('libcdio', vendor, "get_hwinfo vendor") + self.assertEqual('cdrdao', model, "get_hwinfo cdrdao") + # Test known values of various access parameters: + # access mode, driver name via string and via driver_id + # and cue name + result = device.get_arg("access-mode") + self.assertEqual(result, 'image', 'get_arg("access_mode")',) + result = device.get_driver_name() + self.assertEqual(result, 'CDRDAO', 'get_driver_name') + result = device.get_driver_id() + self.assertEqual(result, pycdio.DRIVER_CDRDAO, 'get_driver_id') + result = device.get_arg("source") + self.assertEqual(result, tocfile, 'get_arg("source")') + result = device.get_media_changed() + self.assertEqual(False, result, "tocfile: get_media_changed") + # Test getting is_tocfile + result = cdio.is_tocfile(tocfile) + self.assertEqual(True, result, "is_tocfile(tocfile)") + result = cdio.is_nrg(tocfile) + self.assertEqual(False, result, "is_nrgfile(tocfile)") + result = cdio.is_device(tocfile) + self.assertEqual(False, result, "is_device(tocfile)") + self.assertRaises(cdio.DriverUnsupportedError, + device.set_blocksize, 2048) + self.assertRaises(cdio.DriverUnsupportedError, + device.set_speed, 5) + device.close() + os.chdir(olddir) + + def test_read(self): + """Test functioning of read routines""" + cuefile="./../data/isofs-m1.cue" + device = cdio.Device(source=cuefile) + # Read the ISO Primary Volume descriptor + blocks, data=device.read_sectors(16, pycdio.READ_MODE_M1F1) + self.assertEqual(data[1:6], 'CD001') + self.assertEqual(blocks, 1) + blocks, data=device.read_data_blocks(26) + self.assertEqual(data[6:32], 'GNU GENERAL PUBLIC LICENSE') + + def test_bincue(self): + """Test functioning of BIN/CUE image routines""" + cuefile="./cdda.cue" + device = cdio.Device(source=cuefile) + # Test known values of various access parameters: + # access mode, driver name via string and via driver_id + # and cue name + result = device.get_arg("access-mode") + self.assertEqual(result, 'image', 'get_arg("access_mode")',) + result = device.get_driver_name() + self.assertEqual(result, 'BIN/CUE', 'get_driver_name') + result = device.get_driver_id() + self.assertEqual(result, pycdio.DRIVER_BINCUE, 'get_driver_id') + result = device.get_arg("cue") + self.assertEqual(result, cuefile, 'get_arg("cue")') + # Test getting is_binfile and is_cuefile + binfile = cdio.is_cuefile(cuefile) + self.assertEqual(True, binfile != None, "is_cuefile(cuefile)") + cuefile2 = cdio.is_binfile(binfile) + # Could check that cuefile2 == cuefile, but some OS's may + # change the case of files + self.assertEqual(True, cuefile2 != None, "is_cuefile(binfile)") + result = cdio.is_tocfile(cuefile) + self.assertEqual(False, result, "is_tocfile(tocfile)") + ok, vendor, model, revision = device.get_hwinfo() + self.assertEqual(True, ok, "get_hwinfo ok") + self.assertEqual('libcdio', vendor, "get_hwinfo vendor") + self.assertEqual('CDRWIN', model, "get_hwinfo model") + result = cdio.is_device(cuefile) + self.assertEqual(False, result, "is_device(tocfile)") + result = device.get_media_changed() + self.assertEqual(False, result, "binfile: get_media_changed") + if pycdio.VERSION_NUM >= 77: + # There's a bug in libcdio 0.76 that causes these to crash + self.assertRaises(cdio.DriverUnsupportedError, + device.set_blocksize, 2048) + self.assertRaises(cdio.DriverUnsupportedError, + device.set_speed, 5) + device.close() + + def test_cdda(self): + """Test functioning CD-DA""" + device = cdio.Device() + cuefile="./cdda.cue" + device.open(cuefile) + result = device.get_disc_mode() + self.assertEqual(result, 'CD-DA', 'get_disc_mode') + self.assertEqual(device.get_mcn(), '0000010271955', 'get_mcn') + self.assertRaises(cdio.DriverUnsupportedError, + device.get_last_session) + # self.assertRaises(IOError, device.get_joliet_level) + result = device.get_num_tracks() + self.assertEqual(result, 1, 'get_num_tracks') + disc_last_lsn = device.get_disc_last_lsn() + self.assertEqual(disc_last_lsn, 302, 'get_disc_last_lsn') + t=device.get_last_track() + self.assertEqual(t.track, 1, 'get_last_track') + self.assertEqual(t.get_last_lsn(), 301, '(track) get_last_lsn') + self.assertEqual(device.get_track_for_lsn(t.get_last_lsn()).track, + t.track) + t=device.get_first_track() + self.assertEqual(t.track, 1, 'get_first_track') + self.assertEqual(t.get_format(), 'audio', 'get_track_format') + device.close() + +if __name__ == "__main__": + unittest.main() diff --git a/test/test-iso.py b/test/test-iso.py new file mode 100755 index 00000000..7c253efb --- /dev/null +++ b/test/test-iso.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +"""Unit test for iso9660 + +Test some low-level ISO9660 routines +This is basically the same thing as libcdio's testiso9660.c""" + +import unittest, sys, os + +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pyiso9660 +import iso9660 + +def is_eq(a, b): + if len(a) != len(b): return False + + for i in range(len(a)): + if a[i] != b[i]: + print "position %d: %d != %d\n" % (i, a[i], b[i]) + return False + return True + +achars = ('!', '"', '%', '&', '(', ')', '*', '+', ',', '-', '.', + '/', '?', '<', '=', '>') + +class ISO9660Tests(unittest.TestCase): + + def test_chars(self): + """Test ACHAR and DCHAR""" + bad = 0 + c=ord('A') + while c<=ord('Z'): + if not pyiso9660.is_dchar(c): + print "Failed iso9660_is_achar test on %c" % c + bad += 1 + if not pyiso9660.is_achar(c): + print "Failed iso9660_is_achar test on %c" % c + bad += 1 + c += 1 + + self.assertEqual(True, bad==0, 'is_dchar & is_achar A..Z') + + bad=0 + c=ord('0') + while c<=ord('9'): + if not pyiso9660.is_dchar(c): + print "Failed iso9660_is_dchar test on %c" % c + bad += 1 + if not pyiso9660.is_achar(c): + print "Failed iso9660_is_achar test on %c" % c + bad += 1 + c += 1 + self.assertEqual(True, bad==0, 'is_dchar & is_achar 0..9') + + bad=0 + i=0 + while i<=13: + c=ord(achars[i]) + if pyiso9660.is_dchar(c): + print "Should not pass is_dchar test on %c" % c + bad += 1 + if not pyiso9660.is_achar(c): + print "Failed is_achar test on symbol %c" % c + bad += 1 + i += 1 + + self.assertEqual(True, bad==0, 'is_dchar & is_achar symbols') + + def test_strncpy_pad(self): + """Test pyiso9660.strncpy_pad""" + + dst = pyiso9660.strncpy_pad("1_3", 5, pyiso9660.DCHARS) + self.assertEqual(dst, "1_3 ", "strncpy_pad DCHARS") + + dst = pyiso9660.strncpy_pad("ABC!123", 2, pyiso9660.ACHARS) + self.assertEqual(dst, "AB", "strncpy_pad ACHARS truncation") + + def test_dirname(self): + """Test pyiso9660.dirname_valid_p""" + + self.assertEqual(False, pyiso9660.dirname_valid_p("/NOGOOD"), + "dirname_valid_p - /NOGOOD is no good.") + + self.assertEqual(False, + pyiso9660.dirname_valid_p("LONGDIRECTORY/NOGOOD"), + "pyiso9660.dirname_valid_p - too long directory") + + self.assertEqual(True, pyiso9660.dirname_valid_p("OKAY/DIR"), + "dirname_valid_p - OKAY/DIR should pass ") + + self.assertEqual(False, pyiso9660.dirname_valid_p("OKAY/FILE.EXT"), + "pyiso9660.dirname_valid_p - OKAY/FILENAME.EXT") + + def test_image_info(self): + """Test retrieving image information""" + + # The test ISO 9660 image + image_path="../data" + image_fname=os.path.join(image_path, "copying.iso") + iso = iso9660.ISO9660.IFS(source=image_fname) + self.assertNotEqual(iso, None, "Opening %s" % image_fname) + + self.assertEqual(iso.get_application_id(), +"MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING", + "get_application_id()") + + self.assertEqual(iso.get_system_id(), "LINUX", + "get_system_id() eq 'LINUX'") + self.assertEqual(iso.get_volume_id(), "CDROM", + "get_volume_id() eq 'CDROM'") + + file_stats = iso.readdir('/') + + okay_stats = [ + ['.', 23, 2048, 1, 2], + ['..', 23, 2048, 1, 2], + ['COPYING.;1', 24, 17992, 9, 1] + ] + self.assertEqual(file_stats, okay_stats, "file stat info") + + def test_pathname_valid(self): + """Test pyiso9660.pathname_valid_p""" + + self.assertEqual(True, pyiso9660.pathname_valid_p("OKAY/FILE.EXT"), + "pyiso9660.dirname_valid_p - OKAY/FILE.EXT ") + self.assertEqual(False, + pyiso9660.pathname_valid_p("OKAY/FILENAMELONG.EXT"), + 'invalid pathname, long basename') + + self.assertEqual(False, + pyiso9660.pathname_valid_p("OKAY/FILE.LONGEXT"), + "pathname_valid_p - long extension" ) + + dst = pyiso9660.pathname_isofy("this/file.ext", 1) + self.assertNotEqual(dst, "this/file.ext1", "iso9660_pathname_isofy") + + def test_time(self): + """Test time""" + import time + + tm = time.localtime(0) + dtime = pyiso9660.set_dtime(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]) + new_tm = pyiso9660.get_dtime(dtime, True) + + ### FIXME Don't know why the discrepancy, but there is an hour + ### difference, perhaps daylight savings time. + ### Versions before 0.77 have other bugs. + if new_tm is not None: + # if pyiso9660.VERSION_NUM < 77: new_tm[3] = tm[3] + new_tm[3] = tm[3] + self.assertEqual(True, is_eq(new_tm, tm), 'get_dtime(set_dtime())') + else: + self.assertEqual(True, False, 'get_dtime is None') + +# if pyiso9660.VERSION_NUM >= 77: +# tm = time.gmtime(0) +# ltime = pyiso9660.set_ltime(tm[0], tm[1], tm[2], tm[3], tm[4], +# tm[5]) +# new_tm = pyiso9660.get_ltime(ltime) +# self.assertEqual(True, is_eq(new_tm, tm), +# 'get_ltime(set_ltime())') + return + +if __name__ == "__main__": + unittest.main() diff --git a/test/test-isocopy.py b/test/test-isocopy.py new file mode 100755 index 00000000..bb8c8673 --- /dev/null +++ b/test/test-isocopy.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python +"""Unit test of iso9660 file extraction.""" + +import unittest, sys, os + +libdir = os.path.join(os.path.dirname(__file__), '..') +if libdir[-1] != os.path.sep: + libdir += os.path.sep +sys.path.insert(0, libdir) +import pycdio +import iso9660 + +# Python has rounding (round) and truncation (int), but what about an +# integer ceiling function? Until I learn what it is... +def ceil(x): + return int(round(x+0.5)) + + +# The test CD image +CD_IMAGE_PATH =os.path.join("..", "data") +cd_image_fname=os.path.join(CD_IMAGE_PATH, "isofs-m1.cue") +local_filename="COPYING" + +class ISO9660Tests(unittest.TestCase): + + def test_fs(self): + + cd = iso9660.ISO9660.FS(source=cd_image_fname) + self.assertEqual(True, cd is not None, + "Open CD image %s" % cd_image_fname) + statbuf = cd.stat (os.path.join("/", local_filename)) + + good_stat = { 'LSN': 26, 'filename': 'COPYING', 'is_dir': False, + 'sec_size': 9, 'size' :17992 } + + self.assertEqual(statbuf, good_stat, 'CD 9660 file stats') + + # Get file + buf_all =[] + blocks = ceil(statbuf['size'] / pycdio.ISO_BLOCKSIZE) + for i in range(blocks): + lsn = statbuf['LSN'] + i + size, buf = cd.read_data_blocks(lsn) + + self.assertEqual( True, size >= 0, + "Error reading ISO 9660 file %d" % lsn ) + buf_all.append(buf) + + length=statbuf['size']; + test_file_contents=''.join(buf_all)[0:length-1] + global file_contents + self.assertEqual(test_file_contents, + file_contents, 'File contents comparison') + cd.close() + +file_contents=""" GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.""" + +if __name__ == "__main__": + unittest.main() +