/* cdrdao - write audio CD-Rs in disc-at-once mode
*
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <gtkmm.h>
#include <gnome.h>
#include "TocEdit.h"
#include "CdDevice.h"
#include "ProcessMonitor.h"
#include "xcdrdao.h"
#include "guiUpdate.h"
#include "ProgressDialog.h"
#include "Settings.h"
#include "config.h"
#include "remote.h"
#include "ScsiIf.h"
#include "CdrDriver.h"
#include "util.h"
#include "Toc.h"
#define DRIVER_IDS 13
#define DRIVER_ID_DEFAULT 2
CdDevice *CdDevice::DEVICE_LIST_ = NULL;
char *CdDevice::DRIVER_NAMES_[DRIVER_IDS] = {
"Undefined",
"cdd2600",
"generic-mmc",
"generic-mmc-raw",
"plextor",
"plextor-scan",
"ricoh-mp6200",
"sony-cdu920",
"sony-cdu948",
"taiyo-yuden",
"teac-cdr55",
"toshiba",
"yamaha-cdr10x"
};
CdDevice::CdDevice(const char* dev, const char *vendor, const char *product)
{
dev_ = dev;
vendor_ = vendor;
product_ = product;
driverId_ = 0;
driverOptions_ = 0;
deviceType_ = CD_R;
manuallyConfigured_ = false;
status_ = DEV_UNKNOWN;
exitStatus_ = 0;
progressStatusChanged_ = 0;
progressStatus_ = 0;
progressTotalTracks_ = 0;
progressTrack_ = 0;
progressTotal_ = 0;
progressTrackRelative_ = 0;
progressBufferFill_ = 0;
progressWriterFill_ = 0;
process_ = NULL;
scsiIf_ = NULL;
scsiIfInitFailed_ = 0;
next_ = NULL;
slaveDevice_ = NULL;
autoSelectDriver();
}
CdDevice::~CdDevice()
{
delete scsiIf_;
scsiIf_ = NULL;
}
char *CdDevice::settingString() const
{
char buf[100];
std::string s;
s = "'" + dev_ + "','";
s += vendor_;
s += "','";
s += product_;
s += "',";
switch (deviceType_) {
case CD_R:
s += "CD_R";
break;
case CD_RW:
s += "CD_RW";
break;
case CD_ROM:
s+= "CD_ROM";
break;
}
s += ",";
s += driverName(driverId_);
s += ",";
sprintf(buf, "0x%lx", driverOptions_);
s += buf;
return strdupCC(s.c_str());
}
void CdDevice::driverId(int id)
{
if (id >= 0 && id < DRIVER_IDS)
driverId_ = id;
}
void CdDevice::status(Status s)
{
status_ = s;
}
int CdDevice::exitStatus() const
{
return exitStatus_;
}
int CdDevice::autoSelectDriver()
{
unsigned long options = 0;
const char *driverName;
driverName = CdrDriver::selectDriver(1, vendor_.c_str(), product_.c_str(),
&options);
if (driverName) {
driverId_ = driverName2Id(driverName);
driverOptions_ = options;
} else {
bool r_cdr, w_cdr, r_cdrw, w_cdrw;
ScsiIf* sif = new ScsiIf(dev_.c_str());
if (sif && sif->init() == 0 &&
sif->checkMmc(&r_cdr, &w_cdr, &r_cdrw, &w_cdrw)) {
driverId_ = driverName2Id("generic-mmc");
if (r_cdr) deviceType_ = CD_ROM;
if (w_cdr) deviceType_ = CD_R;
if (w_cdrw) deviceType_ = CD_RW;
} else {
driverId_ = DRIVER_ID_DEFAULT;
driverOptions_ = 0;
}
if (sif) delete sif;
}
return 1;
}
int CdDevice::updateStatus()
{
Status newStatus = status_;
if (process_ != NULL) {
if (process_->exited()) {
newStatus = DEV_UNKNOWN;
exitStatus_ = process_->exitStatus();
progressStatusChanged_ = 1;
PROCESS_MONITOR->remove(process_);
process_ = NULL;
if (slaveDevice_ != NULL) {
slaveDevice_->status(DEV_UNKNOWN);
slaveDevice_ = NULL;
}
}
}
if (status_ == DEV_READY || status_ == DEV_BUSY || status_ == DEV_NO_DISK ||
status_ == DEV_UNKNOWN) {
if (scsiIf_ == NULL)
createScsiIf();
if (scsiIf_ != NULL) {
switch (scsiIf_->testUnitReady()) {
case 0:
newStatus = DEV_READY;
break;
case 1:
newStatus = DEV_BUSY;
break;
case 2:
newStatus = DEV_NO_DISK;
break;
case 3:
// Most likely a timeout error.
newStatus = DEV_BUSY;
break;
}
}
else {
newStatus = DEV_FAULT;
}
}
if (newStatus != status_) {
status_ = newStatus;
return 1;
}
return 0;
}
bool CdDevice::updateProgress(Glib::IOCondition cond, int fd)
{
static char msgSync[4] = { 0xff, 0x00, 0xff, 0x00 };
fd_set fds;
int state = 0;
char buf[10];
struct timeval timeout = { 0, 0 };
if (process_ == NULL)
return false;
if (!(cond & Gdk::INPUT_READ))
return false;
FD_ZERO(&fds);
FD_SET(fd, &fds);
while (select(fd + 1, &fds, NULL, NULL, &timeout) > 0 &&
FD_ISSET(fd, &fds)) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
state = 0;
while (state < 4) {
if (read(fd, buf, 1) != 1) {
//message(-2, "Reading of msg sync failed.");
return false;
}
if (buf[0] == msgSync[state]) {
state++;
}
else {
state = 0;
if (buf[0] == msgSync[state]) {
state++;
}
}
}
ProgressMsg msg;
int msgsize = read(fd, (char *)&msg, sizeof(msg));
if (msgsize >= PSGMSG_MINSIZE) {
if (msg.status >= PGSMSG_MIN && msg.status <= PGSMSG_MAX &&
msg.track >= 0 &&
msg.totalProgress >= 0 && msg.totalProgress <= 1000 &&
msg.bufferFillRate >= 0 && msg.bufferFillRate <= 100) {
progressStatus_ = msg.status;
progressTotalTracks_ = msg.totalTracks;
progressTrack_ = msg.track;
progressTrackRelative_ = msg.trackProgress;
progressTotal_ = msg.totalProgress;
progressBufferFill_ = msg.bufferFillRate;
if (msgsize == sizeof(msg))
progressWriterFill_ = msg.writerFillRate;
else
progressWriterFill_ = 0;
progressStatusChanged_ = 1;
}
}
else {
message(-1, _("Reading of progress message failed."));
}
}
if (progressStatusChanged_)
guiUpdate(UPD_PROGRESS_STATUS);
return true;
}
CdDevice::DeviceType CdDevice::deviceType() const
{
return deviceType_;
}
void CdDevice::deviceType(DeviceType t)
{
deviceType_ = t;
}
unsigned long CdDevice::driverOptions() const
{
return driverOptions_;
}
void CdDevice::driverOptions(unsigned long o)
{
driverOptions_ = o;
}
bool CdDevice::ejectCd(bool load)
{
bool success = false;
if (!scsiIf_)
createScsiIf();
if (scsiIf_) {
CdrDriver* driver = CdrDriver::createDriver(driverName(driverId_),
driverOptions_,
scsiIf_);
int ret = driver->loadUnload((load ? 0 : 1));
success = (ret == 0);
delete(driver);
}
return success;
}
// Starts a 'cdrdao' for recording given toc. Returns false if an
// error occured and the process was not successfully launched.
bool CdDevice::recordDao(Gtk::Window& parent, TocEdit *tocEdit, int simulate,
int multiSession, int speed, int eject, int reload,
int buffer, int overburn)
{
char tocFileName[30];
char *args[30];
int n = 0;
char devname[30];
char drivername[50];
char speedbuf[20];
char *execName;
const char *s;
char bufferbuf[20];
int remoteFdArgNum = 0;
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
|| process_ != NULL)
return false;
sprintf(tocFileName, "/tmp/gcdm.toc.XXXXXX");
int fd = mkstemp(tocFileName);
if (!fd) {
message(-2, _("Cannot create temporary toc-file: %s"), strerror(errno));
return false;
}
// Write out temporary toc file containing all the converted wav
// files (don't want to rely on cdrdao doing the mp3->wav
// translation, besides it's already been done).
if (!tocEdit->toc()->write(fd, true)) {
close(fd);
message(-2, _("Cannot write temporary toc-file."));
return false;
}
close(fd);
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
execName = strdupCC(s);
else
execName = strdupCC("cdrdao");
args[n++] = execName;
if (simulate)
args[n++] = "simulate";
else
args[n++] = "write";
args[n++] = "--remote";
remoteFdArgNum = n;
args[n++] = NULL;
args[n++] = "-v0";
if (multiSession)
args[n++] = "--multi";
if (speed > 0) {
sprintf(speedbuf, "%d", speed);
args[n++] = "--speed";
args[n++] = speedbuf;
}
if (eject)
args[n++] = "--eject";
if (reload)
args[n++] = "--reload";
if (overburn)
args[n++] = "--overburn";
args[n++] = "--device";
args[n++] = (char*)dev_.c_str();
if (driverId_ > 0) {
sprintf(drivername, "%s:0x%lx", driverName(driverId_), driverOptions_);
args[n++] = "--driver";
args[n++] = drivername;
}
if (buffer >= 10) {
sprintf(bufferbuf, "%i", buffer);
args[n++] = "--buffers";
args[n++] = bufferbuf;
}
args[n++] = tocFileName;
args[n++] = NULL;
assert(n <= 20);
PROGRESS_POOL->start(parent, this, tocEdit->filename());
// Remove the SCSI interface of this device to avoid problems with double
// usage of device nodes.
delete scsiIf_;
scsiIf_ = NULL;
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
delete execName;
if (process_ != NULL) {
status_ = DEV_RECORDING;
action_ = A_RECORD;
if (process_->commFd() >= 0) {
Glib::signal_io().connect(bind(mem_fun(*this, &CdDevice::updateProgress),
process_->commFd()),
process_->commFd(),
Glib::IO_IN | Glib::IO_HUP);
}
return true;
}
else {
unlink(tocFileName);
free(tocFileName);
return false;
}
}
void CdDevice::abortDaoRecording()
{
if (process_ != NULL && !process_->exited()) {
PROCESS_MONITOR->stop(process_);
}
}
int CdDevice::progressStatusChanged()
{
if (progressStatusChanged_) {
progressStatusChanged_ = 0;
return 1;
}
return 0;
}
void CdDevice::progress(int *status, int *totalTracks, int *track,
int *trackProgress, int *totalProgress,
int *bufferFill, int *writerFill) const
{
*status = progressStatus_;
*totalTracks = progressTotalTracks_;
*track = progressTrack_;
*trackProgress = progressTrackRelative_;
*totalProgress = progressTotal_;
*bufferFill = progressBufferFill_;
*writerFill = progressWriterFill_;
}
// Starts a 'cdrdao' for reading whole cd.
// Return: 0: OK, process succesfully launched
// 1: error occured
int CdDevice::extractDao(Gtk::Window& parent, const char *tocFileName,
int correction, int readSubChanMode)
{
char *args[30];
int n = 0;
char devname[30];
char drivername[50];
char *execName;
const char *s;
char correctionbuf[20];
int remoteFdArgNum = 0;
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
|| process_ != NULL)
return 1;
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
execName = strdupCC(s);
else
execName = strdupCC("cdrdao");
args[n++] = execName;
args[n++] = "read-cd";
args[n++] = "--remote";
remoteFdArgNum = n;
args[n++] = NULL;
args[n++] = "-v0";
args[n++] = "--read-raw";
switch (readSubChanMode) {
case 1:
args[n++] = "--read-subchan";
args[n++] = "rw";
break;
case 2:
args[n++] = "--read-subchan";
args[n++] = "rw_raw";
break;
}
args[n++] = "--device";
args[n++] = (char*)dev_.c_str();
if (driverId_ > 0) {
sprintf(drivername, "%s:0x%lx", driverName(driverId_), driverOptions_);
args[n++] = "--driver";
args[n++] = drivername;
}
sprintf(correctionbuf, "%d", correction);
args[n++] = "--paranoia-mode";
args[n++] = correctionbuf;
args[n++] = "--datafile";
args[n++] = g_strdup_printf("%s.bin", tocFileName);
args[n++] = g_strdup_printf("%s.toc", tocFileName);
args[n++] = NULL;
assert(n <= 20);
PROGRESS_POOL->start(parent, this, tocFileName, false, false);
// Remove the SCSI interface of this device to avoid problems with double
// usage of device nodes.
delete scsiIf_;
scsiIf_ = NULL;
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
delete[] execName;
if (process_ != NULL) {
status_ = DEV_READING;
action_ = A_READ;
if (process_->commFd() >= 0) {
Glib::signal_io().connect(bind(mem_fun(*this, &CdDevice::updateProgress),
process_->commFd()),
process_->commFd(),
Glib::IO_IN | Glib::IO_PRI |
Glib::IO_ERR | Glib::IO_HUP);
}
return 0;
}
else {
return 1;
}
}
void CdDevice::abortDaoReading()
{
if (process_ != NULL && !process_->exited()) {
PROCESS_MONITOR->stop(process_);
}
}
// Starts a 'cdrdao' for duplicating a CD.
// Return: 0: OK, process succesfully launched
// 1: error occured
int CdDevice::duplicateDao(Gtk::Window& parent, int simulate, int multiSession,
int speed, int eject, int reload, int buffer,
int onthefly, int correction, int readSubChanMode,
CdDevice *readdev)
{
char *args[30];
int n = 0;
char devname[30];
char drivername[50];
char r_drivername[50];
char speedbuf[20];
char correctionbuf[20];
char *execName;
const char *s;
char bufferbuf[20];
int remoteFdArgNum = 0;
int rdstat = readdev->status();
if ((rdstat != DEV_READY && rdstat != DEV_UNKNOWN && rdstat != DEV_FAULT) ||
readdev->process() != NULL)
return 1;
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
|| process_ != NULL)
return 1;
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
execName = strdupCC(s);
else
execName = strdupCC("cdrdao");
args[n++] = execName;
args[n++] = "copy";
if (simulate)
args[n++] = "--simulate";
args[n++] = "--remote";
remoteFdArgNum = n;
args[n++] = NULL;
args[n++] = "-v0";
if (multiSession)
args[n++] = "--multi";
sprintf(correctionbuf, "%d", correction);
args[n++] = "--paranoia-mode";
args[n++] = correctionbuf;
if (speed > 0) {
sprintf(speedbuf, "%d", speed);
args[n++] = "--speed";
args[n++] = speedbuf;
}
if (eject)
args[n++] = "--eject";
if (reload)
args[n++] = "--reload";
if (onthefly)
args[n++] = "--on-the-fly";
switch (readSubChanMode) {
case 1:
args[n++] = "--read-subchan";
args[n++] = "rw";
break;
case 2:
args[n++] = "--read-subchan";
args[n++] = "rw_raw";
break;
}
args[n++] = "--device";
args[n++] = (char*)dev_.c_str();
if (driverId_ > 0) {
sprintf(drivername, "%s:0x%lx", driverName(driverId_), driverOptions_);
args[n++] = "--driver";
args[n++] = drivername;
}
if (readdev != this) { // reader and write the same, skip source device
args[n++] = "--source-device";
args[n++] = (char*)readdev->dev();
if (readdev->driverId() > 0) {
sprintf(r_drivername, "%s:0x%lx", driverName(readdev->driverId()),
readdev->driverOptions());
args[n++] = "--source-driver";
args[n++] = r_drivername;
}
}
if (buffer >= 10) {
sprintf(bufferbuf, "%i", buffer);
args[n++] = "--buffers";
args[n++] = bufferbuf;
}
args[n++] = NULL;
assert(n <= 25);
PROGRESS_POOL->start(parent, this, _("CD to CD copy"));
// Remove the SCSI interface of this device to avoid problems with double
// usage of device nodes.
delete scsiIf_;
scsiIf_ = NULL;
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
delete[] execName;
if (process_ != NULL) {
slaveDevice_ = readdev;
slaveDevice_->status(DEV_READING);
status_ = DEV_RECORDING;
action_ = A_DUPLICATE;
if (process_->commFd() >= 0) {
Glib::signal_io().connect(bind(mem_fun(*this, &CdDevice::updateProgress),
process_->commFd()),
process_->commFd(),
Glib::IO_IN | Glib::IO_HUP);
}
return 0;
}
else {
return 1;
}
}
void CdDevice::abortDaoDuplication()
{
if (process_ != NULL && !process_->exited()) {
PROCESS_MONITOR->stop(process_);
}
}
// Starts a 'cdrdao' for blanking a CD.
// Return: 0: OK, process succesfully launched
// 1: error occured
int CdDevice::blank(Gtk::Window* parent, int fast, int speed, int eject,
int reload)
{
char *args[20];
int n = 0;
char devname[30];
char drivername[50];
char speedbuf[20];
char *execName;
const char *s;
int remoteFdArgNum = 0;
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
|| process_ != NULL)
return 1;
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
execName = strdupCC(s);
else
execName = strdupCC("cdrdao");
args[n++] = execName;
args[n++] = "blank";
args[n++] = "--remote";
remoteFdArgNum = n;
args[n++] = NULL;
args[n++] = "-v0";
args[n++] = "--blank-mode";
if (fast)
args[n++] = "minimal";
else
args[n++] = "full";
if (speed > 0) {
sprintf(speedbuf, "%d", speed);
args[n++] = "--speed";
args[n++] = speedbuf;
}
if (eject)
args[n++] = "--eject";
if (reload)
args[n++] = "--reload";
args[n++] = "--device";
args[n++] = (char*)dev_.c_str();
if (driverId_ > 0) {
sprintf(drivername, "%s:0x%lx", driverName(driverId_), driverOptions_);
args[n++] = "--driver";
args[n++] = drivername;
}
args[n++] = NULL;
assert(n <= 20);
if (parent)
PROGRESS_POOL->start(*parent, this, _("Blanking CDRW"), false, false);
else
PROGRESS_POOL->start(this, _("Blanking CDRW"), false, false);
// Remove the SCSI interface of this device to avoid problems with double
// usage of device nodes.
delete scsiIf_;
scsiIf_ = NULL;
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
delete[] execName;
if (process_ != NULL) {
status_ = DEV_BLANKING;
action_ = A_BLANK;
if (process_->commFd() >= 0) {
Glib::signal_io().connect(bind(mem_fun(*this, &CdDevice::updateProgress),
process_->commFd()),
process_->commFd(),
Glib::IO_IN | Glib::IO_HUP);
}
return 0;
}
else {
return 1;
}
}
void CdDevice::abortBlank()
{
if (process_ != NULL && !process_->exited()) {
PROCESS_MONITOR->stop(process_);
}
}
void CdDevice::createScsiIf()
{
char buf[100];
if (scsiIfInitFailed_)
return;
delete scsiIf_;
scsiIf_ = new ScsiIf(dev_.c_str());
if (scsiIf_->init() != 0) {
delete scsiIf_;
scsiIf_ = NULL;
scsiIfInitFailed_ = 1;
}
}
int CdDevice::driverName2Id(const char *driverName)
{
int i;
for (i = 1; i < DRIVER_IDS; i++) {
if (strcmp(DRIVER_NAMES_[i], driverName) == 0)
return i;
}
return 0;
}
int CdDevice::maxDriverId()
{
return DRIVER_IDS - 1;
}
const char *CdDevice::driverName(int id)
{
if (id >= 0 && id < DRIVER_IDS) {
return DRIVER_NAMES_[id];
}
else {
return "Undefined";
}
}
const char *CdDevice::status2string(Status s)
{
char *ret = NULL;
switch (s) {
case DEV_READY:
ret = "Ready";
break;
case DEV_RECORDING:
ret = "Recording";
break;
case DEV_READING:
ret = "Reading";
break;
case DEV_WAITING:
ret = "Waiting";
break;
case DEV_BLANKING:
ret = "Blanking";
break;
case DEV_BUSY:
ret = "Busy";
break;
case DEV_NO_DISK:
ret = "No disk";
break;
case DEV_FAULT:
ret = "Not available";
break;
case DEV_UNKNOWN:
ret = "Unknown";
break;
}
return ret;
}
const char *CdDevice::deviceType2string(DeviceType t)
{
char *ret = NULL;
switch (t) {
case CD_R:
ret = "CD-R";
break;
case CD_RW:
ret = "CD-RW";
break;
case CD_ROM:
ret = "CD-ROM";
break;
}
return ret;
}
/* reads configured devices from gnome settings
*/
void CdDevice::importSettings()
{
int i, n;
char *s;
char buf[20];
CdDevice *dev;
n = gnome_config_get_int(SET_DEVICES_NUM);
if (n > 0) {
gnome_config_push_prefix(SET_SECTION_DEVICES);
for (i = 0; i < n; i++) {
sprintf(buf, "%d", i);
s = gnome_config_get_string(buf);
if (s != NULL) {
if ((dev = CdDevice::add(s)) != NULL)
dev->manuallyConfigured(true);
}
}
gnome_config_pop_prefix();
}
}
/* saves manually configured devices as gnome settings
*/
void CdDevice::exportSettings()
{
int i, n;
char *s;
char buf[20];
CdDevice *drun;
gnome_config_clean_section(SET_SECTION_DEVICES);
for (drun = first(), n = 0; drun != NULL; drun = next(drun)) {
if (drun->manuallyConfigured()) {
n++;
}
}
if (n > 0) {
gnome_config_set_int(SET_DEVICES_NUM, n);
gnome_config_push_prefix(SET_SECTION_DEVICES);
for (drun = first(), i = 0; drun != NULL; drun = next(drun)) {
if (drun->manuallyConfigured()) {
sprintf(buf, "%d", i);
s = drun->settingString();
gnome_config_set_string(buf, s);
delete[] s;
i++;
}
}
gnome_config_pop_prefix();
}
}
CdDevice *CdDevice::add(const char* dev, const char *vendor,
const char *product)
{
CdDevice *run, *pred, *ent;
for (pred = NULL, run = DEVICE_LIST_; run != NULL;
pred = run, run = run->next_) {
if (strcmp(run->dev(), dev) == 0)
return run;
}
ent = new CdDevice(dev, vendor, product);
if (pred != NULL) {
ent->next_ = pred->next_;
pred->next_ = ent;
}
else {
ent->next_ = DEVICE_LIST_;
DEVICE_LIST_ = ent;
}
return ent;
}
static char *nextToken(char *&p)
{
char *val = NULL;
if (p == NULL || *p == 0)
return NULL;
while (*p != 0 && isspace(*p))
p++;
if (*p == 0)
return NULL;
if (*p == '\'') {
p++;
val = p;
while (*p != 0 && *p != '\'')
p++;
if (*p == 0) {
// error, no matching ' found
return NULL;
}
else {
*p++ = 0;
// skip over ,
while (*p != 0 && *p != ',')
p++;
if (*p == ',')
p++;
}
}
else {
val = p;
while (*p != 0 && *p != ',')
p++;
if (*p == ',')
*p++ = 0;
}
return val;
}
static CdDevice *addImpl(char *s)
{
char *p;
int driverId;
std::string dev;
std::string vendor;
std::string model;
std::string device;
unsigned long options;
char *val;
CdDevice::DeviceType type;
CdDevice *cddev;
if (s[0] != '\'')
return NULL;
p = s;
if ((val = nextToken(p)) == NULL)
return NULL;
dev = val;
if ((val = nextToken(p)) == NULL)
return NULL;
vendor = val;
if ((val = nextToken(p)) == NULL)
return NULL;
model = val;
if ((val = nextToken(p)) == NULL)
return NULL;
if (strcasecmp(val, "CD_R") == 0)
type = CdDevice::CD_R;
else if (strcasecmp(val, "CD_RW") == 0)
type = CdDevice::CD_RW;
else if (strcasecmp(val, "CD_ROM") == 0)
type = CdDevice::CD_ROM;
else
type = CdDevice::CD_R;
if ((val = nextToken(p)) == NULL)
return NULL;
driverId = CdDevice::driverName2Id(val);
if ((val = nextToken(p)) == NULL)
return NULL;
options = strtoul(val, NULL, 0);
cddev = CdDevice::add(dev.c_str(), vendor.c_str(), model.c_str());
cddev->driverId(driverId);
cddev->deviceType(type);
cddev->driverOptions(options);
return cddev;
}
CdDevice *CdDevice::add(const char *setting)
{
char *s = strdupCC(setting);
CdDevice *dev = addImpl(s);
delete[] s;
return dev;
}
CdDevice *CdDevice::find(const char* dev)
{
CdDevice *run;
for (run = DEVICE_LIST_; run != NULL; run = run->next_) {
if (strcmp(run->dev(), dev) == 0)
return run;
}
return NULL;
}
void CdDevice::scan()
{
int i, len;
ScsiIf::ScanData *sdata = ScsiIf::scan(&len);
if (sdata) {
for (i = 0; i < len; i++)
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
delete[] sdata;
}
#ifdef SCSI_ATAPI
sdata = ScsiIf::scan(&len, "ATA");
if (sdata) {
for (i = 0; i < len; i++)
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
delete[] sdata;
} else {
// Only scan for ATAPI devices if we got nothing on the ATA
// interface, otherwise every device would show up twice on the
// list.
sdata = ScsiIf::scan(&len, "ATAPI");
if (sdata) {
for (i = 0; i < len; i++)
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
delete[] sdata;
}
}
#endif
}
void CdDevice::remove(const char* dev)
{
CdDevice *run, *pred;
for (pred = NULL, run = DEVICE_LIST_; run != NULL;
pred = run, run = run->next_) {
if (strcmp(run->dev(), dev) == 0) {
if (run->status() == DEV_RECORDING || run->status() == DEV_BLANKING ||
run->status() == DEV_READING || run->status() == DEV_WAITING)
return;
if (pred != NULL)
pred->next_ = run->next_;
else
DEVICE_LIST_ = run->next_;
delete run;
return;
}
}
}
void CdDevice::clear()
{
CdDevice *next;
while (DEVICE_LIST_ != NULL) {
next = DEVICE_LIST_->next_;
delete DEVICE_LIST_;
DEVICE_LIST_ = next;
}
}
CdDevice *CdDevice::first()
{
return DEVICE_LIST_;
}
CdDevice *CdDevice::next(const CdDevice *run)
{
if (run != NULL)
return run->next_;
else
return NULL;
}
int CdDevice::count()
{
CdDevice *run;
int cnt = 0;
for (run = DEVICE_LIST_; run != NULL; run = run->next_)
cnt++;
return cnt;
}
int CdDevice::updateDeviceStatus()
{
int newStatus = 0;
CdDevice *run;
blockProcessMonitorSignals();
for (run = DEVICE_LIST_; run != NULL; run = run->next_) {
if (run->updateStatus())
newStatus = 1;
}
unblockProcessMonitorSignals();
return newStatus;
}
syntax highlighted by Code2HTML, v. 0.9.1