$OpenBSD$
--- src/disc_openbsd.c.orig	Fri Apr 22 09:35:42 2011
+++ src/disc_openbsd.c	Fri Apr 22 10:00:05 2011
@@ -0,0 +1,191 @@
+/* --------------------------------------------------------------------------
+
+   MusicBrainz -- The Internet music metadatabase
+
+   Copyright (C) 2008 Patrick Hurrelmann 
+   Copyright (C) 2006 Matthias Friedrich
+   Copyright (C) 2000 Robert Kaye
+   Copyright (C) 1999 Marc E E van Woerkom
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+   
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+     $Id: disc_freebsd.c 9646 2008-01-12 11:41:50Z matt $
+
+--------------------------------------------------------------------------- */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/cdio.h>
+#include <unistd.h>
+#include <assert.h>
+
+
+#include "discid/discid_private.h"
+
+#define		CD_FRAMES		75 /* per second */
+#define		CD_DATA_TRACK		0x04
+#define		CD_LEADOUT		0xAA
+
+#define MB_DEFAULT_DEVICE		"/dev/rcd0c"
+
+#define XA_INTERVAL			((60 + 90 + 2) * CD_FRAMES)
+
+
+/* TODO: make sure it's available */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+
+static int read_toc_header(int fd, int *first, int *last) {
+	struct ioc_toc_header th;
+
+	int ret = ioctl(fd, CDIOREADTOCHEADER, &th);
+
+	if ( ret < 0 )
+		return ret; /* error */
+
+	*first = th.starting_track;
+	*last = th.ending_track;
+
+#if 0 /* XXX */
+	/*
+	 * Hide the last track if this is a multisession disc. Note that
+	 * currently only dual-session discs with one track in the second
+	 * session are handled correctly.
+	 */
+	te.address_format = CD_LBA_FORMAT;
+	te.track = th.ending_track;
+	ret = ioctl(fd, CDIOREADTOCENTRIES, &te);
+
+	if (( te.entry.control & CD_DATA_TRACK) != 0 )
+		(*last)--;
+#endif
+
+	return ret;
+}
+
+
+static struct cd_toc_entry *read_toc_entries(int fd, int first, int last) {
+	struct ioc_read_toc_entry te;
+	int error;
+
+	te.address_format = CD_LBA_FORMAT;
+	te.starting_track = first;
+	te.data_len = sizeof(struct cd_toc_entry) * (last - first + 1);
+	te.data = malloc(te.data_len);
+
+	error = ioctl(fd, CDIOREADTOCENTRIES, &te);
+	assert( te.address_format == CD_LBA_FORMAT );
+
+	if ( error != 0 ) {
+		free(te.data);
+		te.data = NULL;
+	}
+
+	return te.data;
+}
+
+
+char *mb_disc_get_default_device_unportable(void) {
+	return MB_DEFAULT_DEVICE;
+}
+
+
+int mb_disc_read_unportable(mb_disc_private *disc, const char *device) {
+	struct cd_toc_entry *entries, *entry;
+	int fd;
+	unsigned long lba;
+	int first, last;
+	int i;
+
+	if ( (fd = open(device, O_RDONLY | O_NONBLOCK)) < 0 ) {
+		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
+			"cannot open device `%s'", device);
+		return 0;
+	}
+
+	/*
+	 * Find the numbers of the first track (usually 1) and the last track.
+	 */
+	if ( read_toc_header(fd, &first, &last) < 0 ) {
+		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
+			"cannot read table of contents");
+		close(fd);
+		return 0;
+	}
+
+	/* basic error checking */
+	if ( last == 0 ) {
+		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
+			"this disc has no tracks");
+		close(fd);
+		return 0;
+	}
+
+	disc->first_track_num = first;
+	disc->last_track_num = last;
+
+	/* Read all the TOC entries.  */
+	entries = read_toc_entries(fd, first, last);
+	if (entries == NULL) {
+		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
+			"unable to read TOC");
+		close(fd);
+		return 0;
+	}
+
+	/*
+	 * Now, for every track, find out the block address where it starts.
+	 */
+	for (i = first, entry = entries; i <= last; i++, entry++) {
+		/* Stop if we hit a data track (multi-session) */
+		if ((entry->control & CD_DATA_TRACK) != 0)
+			break;
+		lba = entry->addr.lba;
+		disc->track_offsets[i] = lba + 150;
+	}
+
+	/*
+	 * Get the logical block address (lba) for the end of the audio data.
+	 * The "LEADOUT" track is the track beyond the final audio track, so
+	 * we're looking for the block address of the LEADOUT track.
+	 */
+	if (i <= last && (entry->control & CD_DATA_TRACK) != 0) {
+		/* multi-session disc, get audio session lead-out */
+		lba = entry->addr.lba - 11400;
+	} else {
+		/* audio-only disc, get disk lead-out */
+		entry = read_toc_entries(fd, CD_LEADOUT, CD_LEADOUT);
+		if (entry == NULL) {
+			snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
+				"unable to read lead-out");
+			free(entries);
+			close(fd);
+			return 0;
+		}
+		lba = entry->addr.lba;
+		free(entry);
+	}
+	disc->track_offsets[0] = lba + 150;
+
+	free(entries);
+	close(fd);
+
+	return 1;
+}
+
+/* EOF */
