Haskell bindings to the libcdio disc reading/writing library. (https://ag.eitilt.life/hscdio/)

root

About

In C, libcdio provides a widely-ported and highly-usable library for retrieving data from CDs (and disc images). This is an area which Haskell has thus far been lacking a solution for; while modern technology has admittedly been moving away from discs in favour of downloading files over the internet, there is still some utility in the medium for any programmers whose projects might involve external storage (note, however, that libcdio only allows /reading/ discs, and leaves /authoring/ to other libraries).

While the majority of the core library has been translated, many supplemental headers (MMC commands, CD-Paranoia, etc.) are not yet available; for the current progress see the FEATURES file and the ROADMAP for the rough order in which those are planned to be developed, and please do report any bugs you find (or even fix them yourself!)

License

The original libcdio is licensed under the GPL, and these bindings, of course, follow suit. If you do use this in your own project, therefore, you are subject to the ambiguity of whether linking to a GPL library then requires your own code to also be released under the GPL. Look elsewhere for a full discussion on what arguments are given in that debate, but in short, the GPL says that "a work based on the Program" must also be released under the GPL.

To err on the side of caution, if you use this library, release your code under the GPL. To do otherwise is to take a firm stance against the linking argument at best, violate the copyright at worst, and make GPL proponents angry at you either way.

Installation

Requirements

As with nearly every Haskell project, the .cabal file maintains the list of dependencies, and that should be taken as authoritative over anywhere else except Hackage revisions, and I'll try to keep the code up to date with those.

Notably, the actual core of this package is the C libcdio library. All bindings should fail gracefully for any version within the supported range, and so should be safe to call blindly, but this is accomplished via preprocessor commands, and so this package must be recompiled whenever the base libcdio version changes. Minor version bumps to the underlying library (and certainly patch releases) should still run safely after being recompiled, but there may be some unexpected behaviour: version 2.1 added a few meta-values to the Language enum, for example, at values greater than 255, while the previous maximum value was below that. It is possible for a C compiler to have used a byte representation for <2.1 and a larger one for >=2.1; something like that could possibly result in things being set at/referenced from the wrong memory offset.

* libcdio: >= 0.93 && < 2.2
* c2hs: >= 0.26
* ghc: >= 8.0

c2hs + ghc

Some parts of the bindings have been written for the c2hs preprocessor, and while manual compilation is technically possible, it's much more involved than necessary. Instead, just use cabal-install or stack as usual. If you really do have to invoke gcc manually, however, run that preprocessor first:

c2hs --cppopts=-E --cppopts=-Iinclude/ --include=.../ src/Foreign/Libcdio/Types/Enums.chs
c2hs --cppopts=-E --cppopts=-Iinclude/ --include=.../ src/Foreign/Libcdio/Types/Offsets.chs

Usage

The original implementation revolves around a classically-C architecture of threading a mutable (though opaque) pointer through through many function calls. For those used to that or who are following pre-existing guides, a set of low-level bindings mirroring that architecture may be found under the "Foreign.Libcdio" tree; for anyone used to Haskell, the monadic bindings under "Sound.Libcdio" are recommended instead. A lookup table for translating existing code and/or knowledge into each progressively higher-level interface is provided in the documentation of the Foreign.Libcdio.* modules.

As a general rule, any project should /only/ import modules from a single set of bindings, not both, as while the datatypes can frequently be passed between them, several function names have been reused and so may collide.

Contributing

I welcome any patches, whether for bug fixes, new features, or anything else.
If you just want to point out a bug or ask for some yet-unmentioned feature, I maintain that list in ISSUES; see the README in that directory for the format if you want to submit something in a more complete form or contribute to any ongoing discussion, but otherwise feel free to simply email me and I'll wrangle it into the right form.

If you decide to contribute yourself, please enable the dev build flag.
For the moment, it just enables all warnings I want to enforce in the code, but it might provide other aids in the future as well. I've avoided enabling them during everyday building as while an end user seeing a warning might feel inclined to fix the program, chances are it's just going to be ignored as the list of built files scrolls by.

cabal configure -fdev

Depending on the size of your hack, I welcome either a diff file, or you can bundle your complete darcs patches with darcs send. Either way, attach the changes to an email addressed to ag@eitilt.life and I'll see about adding it to the codebase.

Patch format

If you do send the latter, every patch should have a comment with a (at least mildly) descriptive name prefixed with a tag indicating the general category addressed by the patch: for fixing issues, that is an "i/" followed by the issue number (padded with leading zeros to three digits); for general development, "f/" followed by the best topic in the FEATURES key; for completeness, version tags are "v/" followed by the package reference followed by the version. All are terminated with a final period.

Please do not squash patches. Each patch should represent a minimal but complete change: certainly enough context to successfully compile, hopefully enough context to not break any previously-passing tests, and potentially combining closely-related changes at your discretion, but if you wind up trying to decide between multiple tags or you're adding all of your work over the entire day, you should probably look closely at whether you can break the patch apart any farther.

Additionally, make good use of the --ask-deps flag. Until I get a CI integration together, it's easy to miss a dependency, but do your best to select anything which your patch may require to build successfully.