feat: re-introduced MacOS support (#71)

* preparatory changes to make dvdisaster compilable under MacOS
* Implemented workaround to macOS Test Unit Ready bug
* fixed manual.pdf not opening on macOS
* Added case for binary being in app bundle
* updated locale files due to change in closure.c
* added back mac app bundle specific files
* reverted make-dist.sh to windows/linux only version and created separate mac script which uses dylibbundler
* altered release.yml accordingly
* chore: fix build under MacOS

---------

Co-authored-by: Stéphane Lesimple <speed47_github@speed47.net>
Co-authored-by: jlnbxn <julianboxan@gmail.com>
Co-authored-by: wojas <github@m.wojas.nl>
This commit is contained in:
Stéphane Lesimple
2023-09-17 19:13:47 +02:00
committed by GitHub
parent f38969c7c8
commit eeb9f0705d
30 changed files with 1689 additions and 1348 deletions

View File

@@ -58,6 +58,35 @@ static char* get_exe_path()
}
#endif
#ifdef SYS_DARWIN
#include <mach-o/dyld.h>
// Get the path of the current executable
static char* get_bin_path() {
char path[PATH_MAX];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0) {
char *lastSlash = strrchr(path, '/');
if (lastSlash) {
*lastSlash = '\0';
return strdup(path);
}
}
return strdup(".");
}
// Check if the given path is within an app bundle
int isWithinAppBundle(const char *filePath) {
// Check if "Contents/MacOS" exists in the path
if (strstr(filePath, ".app/Contents/MacOS") != NULL) {
return 1; // The binary is within an app bundle
}
return 0; // The binary is not within an app bundle
}
#endif
#ifdef WITH_GUI_YES
/***
@@ -396,7 +425,7 @@ static void get_base_dirs()
source directory is supposed to hold the most recent files,
so try this first. */
#if defined(WITH_EMBEDDED_SRC_PATH_YES) && !defined(SYS_MINGW)
#if defined(WITH_EMBEDDED_SRC_PATH_YES) && !defined(SYS_MINGW) && !defined(SYS_DARWIN)
if(DirStat(SRCDIR))
{ Closure->binDir = g_strdup(SRCDIR);
Closure->docDir = g_strdup_printf("%s/documentation",SRCDIR);
@@ -405,6 +434,29 @@ static void get_base_dirs()
}
#endif /* WITH_EMBEDDED_SRC_PATH_YES */
#if defined(WITH_EMBEDDED_SRC_PATH_YES) && defined(SYS_DARWIN)
char *binPath = get_bin_path();
Closure->binDir = g_strdup_printf("%s/../Resources",binPath);
Closure->docDir = g_strdup_printf("%s/../Resources/documentation",binPath);
free(binPath);
goto find_dotfile;
#endif
// We may also be in an app bundle
#if defined(SYS_DARWIN)
// Obtain the path to the executable
const char *pathToExecutable = get_bin_path();
// Check if the executable is within an app bundle
if (isWithinAppBundle(pathToExecutable)) {
// Inside an app bundle, set directory paths accordingly
Closure->binDir = g_strdup_printf("%s/../Resources", pathToExecutable);
Closure->docDir = g_strdup_printf("%s/../Resources/documentation", pathToExecutable);
goto find_dotfile;
}
#endif /* SYS_DARWIN */
/*** Otherwise try the installation directory.
On Unices this is a hardcoded directory. */

378
src/scsi-darwin.c Normal file
View File

@@ -0,0 +1,378 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2011 Carsten Gnoerlich.
*
* Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org
* Project homepage: http://www.dvdisaster.org
*
* This file is part of dvdisaster.
*
* dvdisaster 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.
*
* dvdisaster 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 dvdisaster. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Darwin support by Julian Einwag <julian@einwag.de>.
* This is still an early version.
*/
#include "dvdisaster.h"
#include "scsi-layer.h"
#include "udf.h"
#ifdef SYS_DARWIN
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/usb/USBSpec.h>
#include <stdlib.h>
/*
* Unmount media before trying to access them.
* Added by Bernd Heller, <bdheller@users.sourceforge.net>
*/
static void unmountDVD(io_object_t scsiDevice)
{
CFStringRef bsdName = NULL;
bsdName = (CFStringRef) IORegistryEntrySearchCFProperty(scsiDevice,
kIOServicePlane,
CFSTR(kIOBSDNameKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if (bsdName != NULL) {
// unmount all partitions
char cmd[4096];
char bsdNameStr[100];
CFStringGetCString(bsdName, bsdNameStr, sizeof(bsdNameStr), kCFStringEncodingUTF8);
sprintf(cmd, "/usr/sbin/diskutil unmountDisk /dev/%s", bsdNameStr);
system(cmd);
CFRelease(bsdName);
} else {
// no media to unmount
return;
}
}
char *getProductName(io_object_t device)
{
CFDictionaryRef devCharacteristics;
CFStringRef nameRef;
CFIndex length;
char *prodName;
devCharacteristics = IORegistryEntryCreateCFProperty(device, CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, 0);
if (CFDictionaryGetValueIfPresent(devCharacteristics, CFSTR("Product Name"), (const void **) &nameRef)) {
length = CFStringGetLength(nameRef)+1;
prodName = malloc(length);
CFStringGetCString(nameRef, prodName, length, 0);
CFRelease(nameRef);
return prodName;
} else
return NULL;
}
io_iterator_t getDVDIterator(char* ioClass)
{
io_iterator_t scsiObjectIterator = (io_iterator_t) NULL;
CFMutableDictionaryRef matchingDict = NULL;
IOReturn ioReturnValue = kIOReturnSuccess;
matchingDict = IOServiceMatching(ioClass);
if (matchingDict == (CFMutableDictionaryRef) NULL) {
return (io_iterator_t) NULL;
}
ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &scsiObjectIterator);
if (scsiObjectIterator == (io_iterator_t) NULL || (ioReturnValue != kIOReturnSuccess)) {
return (io_iterator_t) NULL;
}
return scsiObjectIterator;
}
IOCFPlugInInterface **getPlugInInterface(io_object_t scsiDevice)
{
IOReturn ioReturnValue;
SInt32 score = 0;
IOCFPlugInInterface **plugInInterface = NULL;
ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,&plugInInterface, &score);
if (ioReturnValue != kIOReturnSuccess) {
return NULL;
}
return plugInInterface;
}
MMCDeviceInterface** getMMCInterface(IOCFPlugInInterface** plugInInterface)
{
MMCDeviceInterface **mmcDeviceInterface = NULL;
HRESULT pluginResult;
pluginResult = (*plugInInterface)->QueryInterface(plugInInterface,
CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
(LPVOID)&mmcDeviceInterface);
if (pluginResult != KERN_SUCCESS) {
return NULL;
}
return mmcDeviceInterface;
}
char *DefaultDevice()
{
int i;
io_iterator_t scsiObjectIterator = (io_iterator_t) NULL;
io_object_t scsiDevice;
char *deviceName, *prodName;
/* As a convenience, add the simulated drive first. */
InitSimulatedCD();
/* Now probe the physical drives. */
scsiObjectIterator = getDVDIterator("IOBDServices");
for (i = 1; (scsiDevice = (io_object_t) IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
deviceName = g_malloc0(80);
sprintf(deviceName,"IOBDServices/%d",i);
prodName = getProductName(scsiDevice);
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
g_ptr_array_add(Closure->deviceNames, g_strdup(prodName));
g_free(deviceName);
}
IOObjectRelease(scsiObjectIterator);
scsiObjectIterator = getDVDIterator("IODVDServices");
for (i = 1; (scsiDevice = (io_object_t) IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
deviceName = g_malloc0(80);
sprintf(deviceName,"IODVDServices/%d",i);
prodName = getProductName(scsiDevice);
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
g_ptr_array_add(Closure->deviceNames, g_strdup(prodName));
g_free(deviceName);
}
IOObjectRelease(scsiObjectIterator);
scsiObjectIterator = getDVDIterator("IOCompactDiscServices");
for (i = 1; (scsiDevice = IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
deviceName = g_malloc0(80);
sprintf(deviceName,"IOCompactDiscServices/%d",i);
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
g_ptr_array_add(Closure->deviceNames, g_strdup(deviceName));
g_free(deviceName);
}
if(Closure->deviceNodes->len)
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
else { PrintLog(_("No CD/DVD drives found."));
return NULL;
}
}
DeviceHandle* OpenDevice(char *device)
{
DeviceHandle *dh;
io_iterator_t scsiObjectIterator;
io_object_t scsiDevice;
IOReturn ioReturnValue = kIOReturnSuccess;
int numericalId = 1, i;
char *realdevice = NULL, *tmp;
/* use a naming scheme identical to cdrtools */
realdevice = tmp = strdup(device);
tmp = strchr(tmp, '/');
if (tmp != NULL) {
*tmp++ = '\0';
numericalId = atoi(tmp);
}
dh = g_malloc0(sizeof(DeviceHandle));
dh->senseSize = sizeof(SCSI_Sense_Data);
if(!strcmp(device, "sim-cd"))
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
{ g_free(dh);
return NULL;
}
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
if(!dh->simImage)
{ g_free(dh);
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
return NULL;
}
dh->device = g_strdup(device);
return dh;
}
scsiObjectIterator = getDVDIterator(realdevice);
if (scsiObjectIterator == (io_iterator_t) NULL) {
g_free(dh);
Stop(_("Could not open %s (%s)."), device, realdevice);
return NULL;
}
/* look up handle for the selected device */
for (i = 1; (scsiDevice = IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
if (i == numericalId) {
break;
}
}
unmountDVD(scsiDevice);
dh->plugInInterface = getPlugInInterface(scsiDevice);
if (dh->plugInInterface == NULL) {
g_free(dh);
Stop("Could not get PlugInInterface.");
return NULL;
}
dh->mmcDeviceInterface = getMMCInterface(dh->plugInInterface);
if (dh->mmcDeviceInterface == NULL) {
g_free(dh);
Stop("Could not get MMCDeviceInterface.");
return NULL;
}
(dh->scsiTaskDeviceInterface) = (*dh->mmcDeviceInterface)->GetSCSITaskDeviceInterface(dh->mmcDeviceInterface);
if (dh->scsiTaskDeviceInterface == NULL) {
g_free(dh);
Stop("Could not get SCSITaskDeviceInterface.");
return NULL;
}
ioReturnValue = (*dh->scsiTaskDeviceInterface)->ObtainExclusiveAccess(dh->scsiTaskDeviceInterface);
if (ioReturnValue != kIOReturnSuccess) {
Stop("Couldn't obtain exclusive access to drive.");
return NULL;
}
dh->taskInterface = (*dh->scsiTaskDeviceInterface)->CreateSCSITask(dh->scsiTaskDeviceInterface);
if (dh->taskInterface == NULL) {
Stop("Could not create taskInterface.");
return NULL;
}
(*dh->taskInterface)->SetTimeoutDuration(dh->taskInterface, 120*1000);
dh->device = g_strdup(device);
return dh;
}
void CloseDevice(DeviceHandle *dh)
{
if(dh->simImage)
LargeClose(dh->simImage);
if(dh->canReadDefective)
SetRawMode(dh, MODE_PAGE_UNSET);
if(dh->rawBuffer)
FreeRawBuffer(dh->rawBuffer);
if (dh->taskInterface) {
(*dh->taskInterface)->Release(dh->taskInterface);
}
if (dh->scsiTaskDeviceInterface) {
(*dh->scsiTaskDeviceInterface)->ReleaseExclusiveAccess(dh->scsiTaskDeviceInterface);
(*dh->scsiTaskDeviceInterface)->Release(dh->scsiTaskDeviceInterface);
}
if (dh->mmcDeviceInterface) {
(*dh->mmcDeviceInterface)->Release(dh->mmcDeviceInterface);
}
if(dh->plugInInterface) {
IODestroyPlugInInterface(dh->plugInInterface);
}
if(dh->range) {
g_free(dh->range);
}
if(dh->typeDescr)
g_free(dh->typeDescr);
if(dh->mediumDescr)
g_free(dh->mediumDescr);
if(dh->defects)
FreeBitmap(dh->defects);
g_free(dh);
}
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{
u_int32_t flags = 0;
SCSITaskInterface **taskInterface;
SCSITaskStatus taskStatus = kSCSITaskStatus_No_Status;
IOReturn ioReturnValue;
if(dh->simImage)
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
switch(data_mode) {
case DATA_READ:
flags = kSCSIDataTransfer_FromTargetToInitiator;
break;
case DATA_WRITE:
flags = kSCSIDataTransfer_FromInitiatorToTarget;
break;
case DATA_NONE:
flags = kSCSIDataTransfer_NoDataTransfer;
break;
default:
Stop("illegal data_mode: %d",data_mode);
}
taskInterface = dh->taskInterface;
if (flags == kSCSIDataTransfer_FromTargetToInitiator || flags == kSCSIDataTransfer_FromInitiatorToTarget) {
dh->range = (IOVirtualRange *) g_malloc(sizeof(IOVirtualRange));
dh->range->address = (IOVirtualAddress) buf;
dh->range->length = size;
}
ioReturnValue = (*taskInterface)->SetCommandDescriptorBlock(taskInterface, cmd, cdb_size);
if (ioReturnValue != kIOReturnSuccess) {
Stop("Couldn't set command descriptor block.");
}
if (flags == kSCSIDataTransfer_FromTargetToInitiator || flags == kSCSIDataTransfer_FromInitiatorToTarget) {
ioReturnValue = (*taskInterface)->SetScatterGatherEntries(taskInterface, dh->range, 1, size, flags);
if (ioReturnValue != kIOReturnSuccess) {
Stop("Couldn't set scatter-gather.");
}
}
// Set the timeout in the task
ioReturnValue = ( *taskInterface )->SetTimeoutDuration ( taskInterface, 5000 );
if ( ioReturnValue != kIOReturnSuccess ) {
Stop("Operation timed out.");;
}
ioReturnValue = (*taskInterface)->ExecuteTaskSync(taskInterface, (SCSI_Sense_Data*) sense, &taskStatus, NULL);
if (ioReturnValue != kIOReturnSuccess) {
return -1;
}
if (taskStatus == kSCSITaskStatus_DeliveryFailure && cmd[0] == 0x00) {
return 0;
}
if (taskStatus != kSCSITaskStatus_GOOD) {
return -1;
}
return 0;
}
#endif

View File

@@ -37,6 +37,20 @@
#include <camlib.h>
#endif
#ifdef SYS_DARWIN
#define REAL_VERSION VERSION
#undef VERSION
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/scsi/SCSITaskLib.h>
#include <IOKit/storage/IODVDTypes.h>
#include <mach/mach.h>
#include <string.h>
#include <stdlib.h>
#define VERSION REAL_VERSION
#endif
/***
*** Global settings
***/
@@ -113,6 +127,12 @@ typedef struct _DeviceHandle
union ccb *ccb;
#elif defined(SYS_MINGW)
HANDLE fd; /* Windows SPTI file handle for the device */
#elif defined(SYS_DARWIN)
IOCFPlugInInterface **plugInInterface;
MMCDeviceInterface **mmcDeviceInterface;
SCSITaskDeviceInterface **scsiTaskDeviceInterface;
SCSITaskInterface **taskInterface;
IOVirtualRange *range;
#endif
/*

View File

@@ -30,7 +30,7 @@
#include "shellapi.h"
#endif
#ifndef SYS_MINGW
#if !defined(SYS_MINGW) && !defined(SYS_DARWIN)
static void send_errormsg(int fd, char *format, ...)
{ va_list argp;
char *msg;
@@ -63,7 +63,7 @@ void GuiShowURL(char *target)
int hyperlink = 0;
char *path;
#ifndef SYS_MINGW
#if !defined(SYS_MINGW) && !defined(SYS_DARWIN)
pid_t pid;
char *msg;
int err_pipe[2]; /* child may send down err msgs to us here */
@@ -105,15 +105,29 @@ void GuiShowURL(char *target)
/* Okay, Billy wins big time here ;-) */
ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWNORMAL);
#elif defined(SYS_DARWIN)
char command[256];
snprintf(command, sizeof(command), "open \"%s\"", path);
system(command);
#else
/* fork xdg-open */
result = pipe2(err_pipe, O_CLOEXEC);
result = pipe(err_pipe);
if(result == -1)
{ GuiCreateMessage(_("Could not create pipe before fork"), GTK_MESSAGE_ERROR);
return;
}
result = fcntl(err_pipe[0], F_SETFL, O_CLOEXEC);
if(result == -1)
{ GuiCreateMessage(_("Could not set pipe flags before fork"), GTK_MESSAGE_ERROR);
return;
}
result = fcntl(err_pipe[1], F_SETFL, O_CLOEXEC);
if(result == -1)
{ GuiCreateMessage(_("Could not set pipe flags before fork"), GTK_MESSAGE_ERROR);
return;
}
pid = fork();
if(pid == -1)