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:
committed by
GitHub
parent
f38969c7c8
commit
eeb9f0705d
@@ -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
378
src/scsi-darwin.c
Normal 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
|
||||
@@ -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
|
||||
|
||||
/*
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user