mirror of
https://github.com/ksherlock/dot_clean.git
synced 2025-01-05 06:29:21 +00:00
initial version.
This commit is contained in:
commit
7f24d8f588
360
applefile.h
Normal file
360
applefile.h
Normal file
@ -0,0 +1,360 @@
|
||||
#ifndef __applefile_h__
|
||||
#define __applefile_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* applefile.h - Data structures used by AppleSingle/AppleDouble
|
||||
* file format
|
||||
*
|
||||
* Written by Lee Jones, 22-Oct-1993
|
||||
*
|
||||
* For definitive information, see "AppleSingle/AppleDouble
|
||||
* Formats for Foreign Files Developer's Note"; Apple Computer
|
||||
* Inc.; (c) 1990.
|
||||
*
|
||||
* Other details were added from:
|
||||
* Inside Macintosh [old version], volumes II to VI,
|
||||
* Apple include files supplied with Think C 5.0.1,
|
||||
* Microsoft MS-DOS Programmer's Reference, version 5, and
|
||||
* Microsoft C 6.00a's dos.h include file.
|
||||
*
|
||||
* I don't have ProDOS or AFP Server documentation so related
|
||||
* entries may be a bit skimpy.
|
||||
*
|
||||
* Edit history:
|
||||
*
|
||||
* when who why
|
||||
* --------- --- ------------------------------------------
|
||||
* 22-Oct-93 LMJ Pull together from Inside Macintosh,
|
||||
* Developer's Note, etc
|
||||
* 26-Oct-93 LMJ Finish writing first version and list
|
||||
* references
|
||||
* 06-Feb-94 EEF Very minor cleanup
|
||||
*/
|
||||
|
||||
#pragma pack(push, 2)
|
||||
|
||||
|
||||
/* REMINDER: the Motorola 680x0 is a big-endian architecture! */
|
||||
|
||||
typedef uint32_t OSType; /* 32 bit field */
|
||||
|
||||
/* In the QuickDraw coordinate plane, each coordinate is
|
||||
* -32767..32767. Each point is at the intersection of a
|
||||
* horizontal grid line and a vertical grid line. Horizontal
|
||||
* coordinates increase from left to right. Vertical
|
||||
* coordinates increase from top to bottom. This is the way
|
||||
* both a TV screen and page of English text are scanned:
|
||||
* from top left to bottom right.
|
||||
*/
|
||||
|
||||
struct Point /* spot in QuickDraw 2-D grid */
|
||||
{
|
||||
uint16_t v; /* vertical coordinate */
|
||||
uint16_t h; /* horizontal coordinate */
|
||||
}; /* Point */
|
||||
|
||||
typedef struct Point Point;
|
||||
|
||||
/* See older Inside Macintosh, Volume II page 84 or Volume IV
|
||||
* page 104.
|
||||
*/
|
||||
|
||||
struct FInfo /* Finder information */
|
||||
{
|
||||
OSType fdType; /* File type, 4 ASCII chars */
|
||||
OSType fdCreator; /* File's creator, 4 ASCII chars */
|
||||
uint16_t fdFlags; /* Finder flag bits */
|
||||
Point fdLocation; /* file's location in folder */
|
||||
uint16_t fdFldr; /* file 's folder (aka window) */
|
||||
}; /* FInfo */
|
||||
|
||||
typedef struct FInfo FInfo;
|
||||
|
||||
/*
|
||||
* Masks for finder flag bits (field fdFlags in struct
|
||||
* FInfo).
|
||||
*/
|
||||
|
||||
#define F_fOnDesk 0x0001 /* file is on desktop (HFS only) */
|
||||
#define F_maskColor 0x000E /* color coding (3 bits) */
|
||||
/* 0x0010 reserved (System 7) */
|
||||
#define F_fSwitchLaunch 0x0020 /* reserved (System 7) */
|
||||
#define F_fShared 0x0040 /* appl available to multiple users */
|
||||
#define F_fNoINITs 0x0080 /* file contains no INIT resources */
|
||||
#define F_fBeenInited 0x0100 /* Finder has loaded bundle res. */
|
||||
/* 0x0200 reserved (System 7) */
|
||||
#define F_fCustomIcom 0x0400 /* file contains custom icon */
|
||||
#define F_fStationary 0x0800 /* file is a stationary pad */
|
||||
#define F_fNameLocked 0x1000 /* file can't be renamed by Finder */
|
||||
#define F_fHasBundle 0x2000 /* file has a bundle */
|
||||
#define F_fInvisible 0x4000 /* file's icon is invisible */
|
||||
#define F_fAlias 0x8000 /* file is an alias file (System 7) */
|
||||
|
||||
/* See older Inside Macintosh, Volume IV, page 105.
|
||||
*/
|
||||
|
||||
struct FXInfo /* Extended finder information */
|
||||
|
||||
{
|
||||
uint16_t fdIconID; /* icon ID number */
|
||||
uint16_t fdUnused[3]; /* spare */
|
||||
int8_t fdScript; /* scrip flag and code */
|
||||
int8_t fdXFlags; /* reserved */
|
||||
uint16_t fdComment; /* comment ID number */
|
||||
uint32_t fdPutAway; /* home directory ID */
|
||||
}; /* FXInfo */
|
||||
|
||||
typedef struct FXInfo FXInfo;
|
||||
|
||||
/* Pieces used by AppleSingle & AppleDouble (defined later). */
|
||||
|
||||
#define APPLESINGLE_MAGIC 0x00051600
|
||||
#define APPLEDOUBLE_MAGIC 0x00051607
|
||||
#define APPLESINGLE_CIGAM 0x00160500
|
||||
#define APPLEDOUBLE_CIGAM 0x07160500
|
||||
|
||||
struct ASHeader /* header portion of AppleSingle */
|
||||
{
|
||||
/* AppleSingle = 0x00051600; AppleDouble = 0x00051607 */
|
||||
uint32_t magicNum; /* internal file type tag */
|
||||
uint32_t versionNum; /* format version: 2 = 0x00020000 */
|
||||
uint8_t filler[16]; /* filler, currently all bits 0 */
|
||||
uint16_t numEntries; /* number of entries which follow */
|
||||
}; /* ASHeader */
|
||||
|
||||
typedef struct ASHeader ASHeader;
|
||||
|
||||
struct ASEntry /* one AppleSingle entry descriptor */
|
||||
{
|
||||
uint32_t entryID; /* entry type: see list, 0 invalid */
|
||||
uint32_t entryOffset; /* offset, in octets, from beginning */
|
||||
/* of file to this entry's data */
|
||||
uint32_t entryLength; /* length of data in octets */
|
||||
}; /* ASEntry */
|
||||
|
||||
typedef struct ASEntry ASEntry;
|
||||
|
||||
/* Apple reserves the range of entry IDs from 1 to 0x7FFFFFFF.
|
||||
* Entry ID 0 is invalid. The rest of the range is available
|
||||
* for applications to define their own entry types. "Apple does
|
||||
* not arbitrate the use of the rest of the range."
|
||||
*/
|
||||
|
||||
#define AS_DATA 1 /* data fork */
|
||||
#define AS_RESOURCE 2 /* resource fork */
|
||||
#define AS_REALNAME 3 /* File's name on home file system */
|
||||
#define AS_COMMENT 4 /* standard Mac comment */
|
||||
#define AS_ICONBW 5 /* Mac black & white icon */
|
||||
#define AS_ICONCOLOR 6 /* Mac color icon */
|
||||
/* 7 not used */
|
||||
#define AS_FILEDATES 8 /* file dates; create, modify, etc */
|
||||
#define AS_FINDERINFO 9 /* Mac Finder info & extended info */
|
||||
#define AS_MACINFO 10 /* Mac file info, attributes, etc */
|
||||
#define AS_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
|
||||
#define AS_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
|
||||
#define AS_AFPNAME 13 /* Short name on AFP server */
|
||||
#define AS_AFPINFO 14 /* AFP file info, attrib., etc */
|
||||
|
||||
#define AS_AFPDIRID 15 /* AFP directory ID */
|
||||
|
||||
/* matrix of entry types and their usage:
|
||||
*
|
||||
* Macintosh Pro-DOS MS-DOS AFP server
|
||||
* --------- ------- ------ ----------
|
||||
* 1 AS_DATA xxx xxx xxx xxx
|
||||
* 2 AS_RESOURCE xxx xxx
|
||||
* 3 AS_REALNAME xxx xxx xxx xxx
|
||||
*
|
||||
* 4 AS_COMMENT xxx
|
||||
* 5 AS_ICONBW xxx
|
||||
* 6 AS_ICONCOLOR xxx
|
||||
*
|
||||
* 8 AS_FILEDATES xxx xxx xxx xxx
|
||||
* 9 AS_FINDERINFO xxx
|
||||
* 10 AS_MACINFO xxx
|
||||
*
|
||||
* 11 AS_PRODOSINFO xxx
|
||||
* 12 AS_MSDOSINFO xxx
|
||||
*
|
||||
* 13 AS_AFPNAME xxx
|
||||
* 14 AS_AFPINFO xxx
|
||||
* 15 AS_AFPDIRID xxx
|
||||
*/
|
||||
|
||||
/* entry ID 1, data fork of file - arbitrary length octet string */
|
||||
|
||||
/* entry ID 2, resource fork - arbitrary length opaque octet string;
|
||||
* as created and managed by Mac O.S. resoure manager
|
||||
*/
|
||||
|
||||
/* entry ID 3, file's name as created on home file system - arbitrary
|
||||
* length octet string; usually short, printable ASCII
|
||||
*/
|
||||
|
||||
/* entry ID 4, standard Macintosh comment - arbitrary length octet
|
||||
* string; printable ASCII, claimed 200 chars or less
|
||||
*/
|
||||
|
||||
/* This is probably a simple duplicate of the 128 octet bitmap
|
||||
* stored as the 'ICON' resource or the icon element from an 'ICN#'
|
||||
* resource.
|
||||
*/
|
||||
|
||||
/* entry ID 5, standard Mac black and white icon */
|
||||
struct ASIconBW
|
||||
{
|
||||
uint32_t bitrow[32]; /* 32 rows of 32 1-bit pixels */
|
||||
}; /* ASIconBW */
|
||||
|
||||
typedef struct ASIconBW ASIconBW;
|
||||
|
||||
/* entry ID 6, "standard" Macintosh color icon - several competing
|
||||
* color icons are defined. Given the copyright dates
|
||||
* of the Inside Macintosh volumes, the 'cicn' resource predominated
|
||||
* when the AppleSingle Developer's Note was written (most probable
|
||||
* candidate). See Inside Macintosh, Volume V, pages 64 & 80-81 for
|
||||
* a description of 'cicn' resources.
|
||||
*
|
||||
* With System 7, Apple introduced icon families. They consist of:
|
||||
* large (32x32) B&W icon, 1-bit/pixel, type 'ICN#',
|
||||
* small (16x16) B&W icon, 1-bit/pixel, type 'ics#',
|
||||
* large (32x32) color icon, 4-bits/pixel, type 'icl4',
|
||||
* small (16x16) color icon, 4-bits/pixel, type 'ics4',
|
||||
* large (32x32) color icon, 8-bits/pixel, type 'icl8', and
|
||||
* small (16x16) color icon, 8-bits/pixel, type 'ics8'.
|
||||
* If entry ID 6 is one of these, take your pick. See Inside
|
||||
* Macintosh, Volume VI, pages 2-18 to 2-22 and 9-9 to 9-13, for
|
||||
* descriptions.
|
||||
*/
|
||||
|
||||
/* entry ID 7, not used */
|
||||
|
||||
/* Times are stored as a "signed number of seconds before of after
|
||||
* 12:00 a.m. (midnight), January 1, 2000 Greenwich Mean Time (GMT).
|
||||
* Applications must convert to their native date and time
|
||||
* conventions." Any unknown entries are set to 0x80000000
|
||||
* (earliest reasonable time).
|
||||
*/
|
||||
|
||||
/* entry ID 8, file dates info */
|
||||
struct ASFileDates
|
||||
{
|
||||
uint32_t create; /* file creation date/time */
|
||||
uint32_t modify; /* last modification date/time */
|
||||
uint32_t backup; /* last backup date/time */
|
||||
uint32_t access; /* last access date/time */
|
||||
}; /* ASFileDates */
|
||||
|
||||
typedef struct ASFileDates ASFileDates;
|
||||
|
||||
/* See older Inside Macintosh, Volume II, page 115 for
|
||||
* PBGetFileInfo(), and Volume IV, page 155, for PBGetCatInfo().
|
||||
*/
|
||||
|
||||
/* entry ID 9, Macintosh Finder info & extended info */
|
||||
struct ASFinderInfo
|
||||
{
|
||||
FInfo ioFlFndrInfo; /* PBGetFileInfo() or PBGetCatInfo() */
|
||||
FXInfo ioFlXFndrInfo; /* PBGetCatInfo() (HFS only) */
|
||||
}; /* ASFinderInfo */
|
||||
|
||||
typedef struct ASFinderInfo ASFinderInfo;
|
||||
|
||||
/* entry ID 10, Macintosh file information */
|
||||
struct ASMacInfo
|
||||
{
|
||||
uint8_t filler[3]; /* filler, currently all bits 0 */
|
||||
uint8_t ioFlAttrib; /* PBGetFileInfo() or PBGetCatInfo() */
|
||||
}; /* ASMacInfo */
|
||||
|
||||
typedef struct ASMacInfo ASMacInfo;
|
||||
|
||||
#define AS_PROTECTED 0x0002 /* protected bit */
|
||||
#define AS_LOCKED 0x0001 /* locked bit */
|
||||
|
||||
/* NOTE: ProDOS-16 and GS/OS use entire fields. ProDOS-8 uses low
|
||||
* order half of each item (low byte in access & filetype, low word
|
||||
* in auxtype); remainder of each field should be zero filled.
|
||||
*/
|
||||
|
||||
/* entry ID 11, ProDOS file information */
|
||||
struct ASProdosInfo
|
||||
{
|
||||
uint16_t access; /* access word */
|
||||
uint16_t filetype; /* file type of original file */
|
||||
uint32_t auxtype; /* auxiliary type of the orig file */
|
||||
}; /* ASProDosInfo */
|
||||
|
||||
typedef struct ASProdosInfo ASProdosInfo;
|
||||
|
||||
/* MS-DOS file attributes occupy 1 octet; since the Developer Note
|
||||
* is unspecific, I've placed them in the low order portion of the
|
||||
* field (based on example of other ASMacInfo & ASProdosInfo).
|
||||
*/
|
||||
|
||||
/* entry ID 12, MS-DOS file information */
|
||||
struct ASMsdosInfo
|
||||
{
|
||||
uint8_t filler; /* filler, currently all bits 0 */
|
||||
uint8_t attr; /* _dos_getfileattr(), MS-DOS */
|
||||
/* interrupt 21h function 4300h */
|
||||
}; /* ASMsdosInfo */
|
||||
|
||||
typedef struct ASMsdosInfo ASMsdosInfo;
|
||||
|
||||
#define AS_DOS_NORMAL 0x00 /* normal file (all bits clear) */
|
||||
#define AS_DOS_READONLY 0x01 /* file is read-only */
|
||||
#define AS_DOS_HIDDEN 0x02 /* hidden file (not shown by DIR) */
|
||||
#define AS_DOS_SYSTEM 0x04 /* system file (not shown by DIR) */
|
||||
#define AS_DOS_VOLID 0x08 /* volume label (only in root dir) */
|
||||
#define AS_DOS_SUBDIR 0x10 /* file is a subdirectory */
|
||||
#define AS_DOS_ARCHIVE 0x20 /* new or modified (needs backup) */
|
||||
|
||||
/* entry ID 13, short file name on AFP server - arbitrary length
|
||||
* octet string; usualy printable ASCII starting with
|
||||
* '!' (0x21)
|
||||
*/
|
||||
|
||||
/* entry ID 12, AFP server file information */
|
||||
struct ASAfpInfo
|
||||
{
|
||||
uint8_t filler[3]; /* filler, currently all bits 0 */
|
||||
uint8_t attr; /* file attributes */
|
||||
}; /* ASAfpInfo */
|
||||
|
||||
typedef struct ASAfpInfo ASAfpInfo;
|
||||
|
||||
#define AS_AFP_Invisible 0x01 /* file is invisible */
|
||||
#define AS_AFP_MultiUser 0x02 /* simultaneous access allowed */
|
||||
#define AS_AFP_System 0x04 /* system file */
|
||||
#define AS_AFP_BackupNeeded 0x40 /* new or modified (needs backup) */
|
||||
|
||||
/* entry ID 15, AFP server directory ID */
|
||||
struct ASAfpDirId
|
||||
{
|
||||
uint32_t dirid; /* file's directory ID on AFP server */
|
||||
}; /* ASAfpDirId */
|
||||
|
||||
typedef struct ASAfpDirId ASAfpDirId;
|
||||
|
||||
/*
|
||||
* The format of an AppleSingle/AppleDouble header
|
||||
*/
|
||||
struct AppleSingle /* format of disk file */
|
||||
{
|
||||
ASHeader header; /* AppleSingle header part */
|
||||
ASEntry entry[1]; /* array of entry descriptors */
|
||||
/* uint8_t filedata[]; followed by rest of file */
|
||||
}; /* AppleSingle */
|
||||
|
||||
typedef struct AppleSingle AppleSingle;
|
||||
|
||||
/*
|
||||
* FINAL REMINDER: the Motorola 680x0 is a big-endian architecture!
|
||||
*/
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/* End of applefile.h */
|
||||
#endif
|
369
mapped_file.cpp
Normal file
369
mapped_file.cpp
Normal file
@ -0,0 +1,369 @@
|
||||
#include "mapped_file.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
#include "unique_resource.h"
|
||||
|
||||
namespace {
|
||||
class defer {
|
||||
public:
|
||||
typedef std::function<void()> FX;
|
||||
defer() = default;
|
||||
|
||||
defer(FX fx) : _fx(fx) {}
|
||||
defer(const defer &) = delete;
|
||||
defer(defer &&) = default;
|
||||
defer & operator=(const defer &) = delete;
|
||||
defer & operator=(defer &&) = default;
|
||||
|
||||
void cancel() { _fx = nullptr; }
|
||||
~defer() { if (_fx) _fx(); }
|
||||
private:
|
||||
FX _fx;
|
||||
};
|
||||
|
||||
void throw_error(int error) {
|
||||
throw std::system_error(error, std::system_category());
|
||||
}
|
||||
|
||||
|
||||
void throw_error(int error, const std::string &what) {
|
||||
throw std::system_error(error, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
namespace {
|
||||
|
||||
void throw_error() {
|
||||
throw_error(GetLastError());
|
||||
}
|
||||
|
||||
void throw_error(const std::string &what) {
|
||||
throw_error(GetLastError(), what);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mapped_file_base::close() {
|
||||
if (is_open()) {
|
||||
|
||||
UnmapViewOfFile(_data);
|
||||
CloseHandle(_map_handle);
|
||||
CloseHandle(_file_handle);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
||||
|
||||
|
||||
HANDLE fh;
|
||||
HANDLE mh;
|
||||
LARGE_INTEGER file_size;
|
||||
|
||||
// length of 0 in CreateFileMapping / MapViewOfFile
|
||||
// means map the entire file.
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
||||
}
|
||||
|
||||
fh = CreateFile(p.c_str(),
|
||||
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
flags == readonly ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
if (fh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||
|
||||
GetFileSizeEx(fh, &file_size);
|
||||
|
||||
if (length == -1)
|
||||
length = file_size.QuadPart;
|
||||
|
||||
DWORD protect = 0;
|
||||
DWORD access = 0;
|
||||
switch (flags) {
|
||||
case readonly:
|
||||
protect = PAGE_READONLY;
|
||||
access = FILE_MAP_READ;
|
||||
break;
|
||||
case readwrite:
|
||||
protect = PAGE_READWRITE;
|
||||
access = FILE_MAP_WRITE;
|
||||
break;
|
||||
case priv:
|
||||
protect = PAGE_WRITECOPY;
|
||||
access = FILE_MAP_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||
if (mh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||
|
||||
|
||||
_data = MapViewOfFileEx(mh,
|
||||
access,
|
||||
(DWORD)(offset >> 32),
|
||||
(DWORD)offset,
|
||||
length,
|
||||
nullptr);
|
||||
if (!_data) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
|
||||
_file_handle = fh_close.release();
|
||||
_map_handle = mh_close.release();
|
||||
_size = length;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
|
||||
void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
const size_t offset = 0;
|
||||
|
||||
HANDLE fh;
|
||||
HANDLE mh;
|
||||
LARGE_INTEGER file_size;
|
||||
|
||||
const DWORD protect = PAGE_READWRITE;
|
||||
const DWORD access = FILE_MAP_WRITE;
|
||||
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
||||
}
|
||||
|
||||
|
||||
fh = CreateFile(p.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
if (fh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||
|
||||
|
||||
file_size.QuadPart = length;
|
||||
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
|
||||
if (!SetEndOfFile(fh)) throw_error();
|
||||
|
||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||
if (mh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||
|
||||
_data = MapViewOfFileEx(mh,
|
||||
access,
|
||||
(DWORD)(offset >> 32),
|
||||
(DWORD)offset,
|
||||
length,
|
||||
nullptr);
|
||||
|
||||
if (!_data) {
|
||||
throw_error();
|
||||
}
|
||||
|
||||
_file_handle = fh_close.release();
|
||||
_map_handle = mh_close.release();
|
||||
|
||||
_size = length;
|
||||
_flags = readwrite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cerrno>
|
||||
|
||||
namespace {
|
||||
|
||||
void throw_error() {
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
void throw_error(const std::string &what) {
|
||||
throw_error(errno, what);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mapped_file_base::close() {
|
||||
if (is_open()) {
|
||||
::munmap(_data, _size);
|
||||
::close(_fd);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
||||
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
int oflags = 0;
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
||||
}
|
||||
|
||||
switch (flags) {
|
||||
case readonly:
|
||||
oflags = O_RDONLY;
|
||||
break;
|
||||
default:
|
||||
oflags = O_RDWR;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = ::open(p.c_str(), oflags);
|
||||
if (fd < 0) {
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
//defer([fd](){::close(fd); });
|
||||
auto close_fd = make_unique_resource(fd, ::close);
|
||||
|
||||
if (::fstat(fd, &st) < 0) {
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
|
||||
if (length == -1) length = st.st_size;
|
||||
|
||||
_data = ::mmap(0, length,
|
||||
flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE,
|
||||
flags == priv ? MAP_PRIVATE : MAP_SHARED,
|
||||
fd, offset);
|
||||
|
||||
if (_data == MAP_FAILED) {
|
||||
_data = nullptr;
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
_fd = close_fd.release();
|
||||
_size = length;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
int fd;
|
||||
const size_t offset = 0;
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
||||
}
|
||||
|
||||
|
||||
|
||||
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
|
||||
if (fd < 0) {
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
//defer([fd](){::close(fd); });
|
||||
|
||||
auto close_fd = make_unique_resource(fd, ::close);
|
||||
|
||||
|
||||
if (::ftruncate(fd, length) < 0) {
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
|
||||
_data = ::mmap(0, length,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd, offset);
|
||||
|
||||
if (_data == MAP_FAILED) {
|
||||
_data = nullptr;
|
||||
throw_error(errno);
|
||||
}
|
||||
|
||||
_fd = close_fd.release();
|
||||
_size = length;
|
||||
_flags = readwrite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void mapped_file_base::reset() {
|
||||
_data = nullptr;
|
||||
_size = 0;
|
||||
_flags = readonly;
|
||||
#ifdef _WIN32
|
||||
_file_handle = nullptr;
|
||||
_map_handle = nullptr;
|
||||
#else
|
||||
_fd = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mapped_file_base::swap(mapped_file_base &rhs)
|
||||
{
|
||||
if (std::addressof(rhs) != this) {
|
||||
std::swap(_data, rhs._data);
|
||||
std::swap(_size, rhs._size);
|
||||
std::swap(_flags, rhs._flags);
|
||||
#ifdef _WIN32
|
||||
std::swap(_file_handle, rhs._file_handle);
|
||||
std::swap(_map_handle, rhs._map_handle);
|
||||
#else
|
||||
std::swap(_fd, rhs._fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
mapped_file::mapped_file(mapped_file &&rhs) : mapped_file() {
|
||||
swap(rhs);
|
||||
//rhs.reset();
|
||||
}
|
||||
|
||||
mapped_file& mapped_file::operator=(mapped_file &&rhs) {
|
||||
if (std::addressof(rhs) == this) return *this;
|
||||
|
||||
swap(rhs);
|
||||
rhs.close();
|
||||
//rhs.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
146
mapped_file.h
Normal file
146
mapped_file.h
Normal file
@ -0,0 +1,146 @@
|
||||
#ifndef __mapped_file_h__
|
||||
#define __mapped_file_h__
|
||||
|
||||
#ifdef HAVE_TSFS
|
||||
#include <filesystem>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
class mapped_file_base {
|
||||
public:
|
||||
|
||||
#ifdef HAVE_TSFS
|
||||
typedef std::filesystem::path path_type ;
|
||||
#else
|
||||
typedef std::string path_type ;
|
||||
#endif
|
||||
|
||||
enum mapmode { readonly, readwrite, priv };
|
||||
|
||||
void close();
|
||||
|
||||
bool is_open() const {
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
operator bool() const { return is_open(); }
|
||||
bool operator !() const { return !is_open(); }
|
||||
|
||||
~mapped_file_base() { close(); }
|
||||
|
||||
protected:
|
||||
|
||||
void swap(mapped_file_base &rhs);
|
||||
|
||||
void open(const path_type& p, mapmode flags, size_t length, size_t offset);
|
||||
void create(const path_type &p, size_t new_size); // always creates readwrite.
|
||||
void reset();
|
||||
|
||||
|
||||
size_t _size = 0;
|
||||
void *_data = nullptr;
|
||||
mapmode _flags = readonly;
|
||||
|
||||
#ifdef _WIN32
|
||||
void *_file_handle = nullptr;
|
||||
void *_map_handle = nullptr;
|
||||
#else
|
||||
int _fd = -1;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
class mapped_file : public mapped_file_base {
|
||||
|
||||
typedef mapped_file_base base;
|
||||
|
||||
public:
|
||||
|
||||
typedef unsigned char value_type;
|
||||
|
||||
typedef value_type *iterator;
|
||||
typedef const value_type *const_iterator;
|
||||
|
||||
typedef value_type &reference ;
|
||||
typedef const value_type &const_reference;
|
||||
|
||||
|
||||
mapped_file() = default;
|
||||
mapped_file(const path_type& p, mapmode flags = readonly, size_t length = -1, size_t offset = 0) {
|
||||
open(p, flags, length, offset);
|
||||
}
|
||||
|
||||
mapped_file(mapped_file &&);
|
||||
mapped_file(const mapped_file &) = delete;
|
||||
|
||||
mapped_file &operator=(mapped_file &&);
|
||||
mapped_file &operator=(const mapped_file &) = delete;
|
||||
|
||||
|
||||
void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) {
|
||||
base::open(p, flags, length, offset);
|
||||
}
|
||||
|
||||
|
||||
const value_type *data() const {
|
||||
return (const value_type *)_data;
|
||||
}
|
||||
|
||||
value_type *data() {
|
||||
return _flags == readonly ? (value_type *)nullptr : (value_type *)_data;
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return data();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return data() + size();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return cend();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator begin() {
|
||||
return _flags == readonly ? (iterator)nullptr : (iterator)_data;
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return _flags == readonly ? (iterator)nullptr : (iterator)_data + size();
|
||||
}
|
||||
|
||||
mapmode flags() const {
|
||||
return _flags;
|
||||
}
|
||||
|
||||
void swap(mapped_file &rhs) {
|
||||
base::swap(rhs);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<class T>
|
||||
void swap(mapped_file &a, mapped_file &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
192
unfork.cpp
Normal file
192
unfork.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "applefile.h"
|
||||
#include "mapped_file.h"
|
||||
|
||||
|
||||
void damaged_file() {
|
||||
throw std::runtime_error("File is damaged.");
|
||||
}
|
||||
|
||||
void throw_errno() {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
|
||||
|
||||
void throw_errno(const std::string &what) {
|
||||
throw std::system_error(errno, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
class defer {
|
||||
public:
|
||||
typedef std::function<void()> FX;
|
||||
defer() = default;
|
||||
|
||||
defer(FX fx) : _fx(fx) {}
|
||||
defer(const defer &) = delete;
|
||||
defer(defer &&) = default;
|
||||
defer & operator=(const defer &) = delete;
|
||||
defer & operator=(defer &&) = default;
|
||||
|
||||
void cancel() { _fx = nullptr; }
|
||||
~defer() { if (_fx) _fx(); }
|
||||
private:
|
||||
FX _fx;
|
||||
};
|
||||
|
||||
void unfork(const char *in, const char *out) {
|
||||
|
||||
|
||||
static_assert(sizeof(ASHeader) == 26, "ASHeader size is wrong.");
|
||||
static_assert(sizeof(ASEntry) == 12, "ASEntry size is wrong.");
|
||||
|
||||
|
||||
mapped_file mf(in, mapped_file::priv);
|
||||
|
||||
if (mf.size() < sizeof(ASHeader)) damaged_file();
|
||||
|
||||
ASHeader *header = (ASHeader *)mf.data();
|
||||
header->magicNum = ntohl(header->magicNum);
|
||||
header->versionNum = ntohl(header->versionNum);
|
||||
header->numEntries = ntohs(header->numEntries);
|
||||
|
||||
|
||||
|
||||
if (header->magicNum != 0x00051600 || header->versionNum != 0x00020000 || header->numEntries == 0)
|
||||
throw std::runtime_error("Not an AppleSingle File");
|
||||
|
||||
if (header->numEntries * sizeof(ASEntry) + sizeof(ASHeader) > mf.size()) damaged_file();
|
||||
|
||||
ASEntry *begin = (ASEntry *)(mf.data() + sizeof(ASHeader));
|
||||
ASEntry *end = &begin[header->numEntries];
|
||||
|
||||
std::for_each(begin, end, [](ASEntry &e){
|
||||
e.entryID = ntohl(e.entryID);
|
||||
e.entryOffset = ntohl(e.entryOffset);
|
||||
e.entryLength = ntohl(e.entryLength);
|
||||
});
|
||||
|
||||
|
||||
// check for truncation....
|
||||
|
||||
for (auto iter = begin; iter != end; ++iter) {
|
||||
const auto &e = *iter;
|
||||
if (!e.entryLength) continue;
|
||||
if (e.entryOffset > mf.size()) damaged_file();
|
||||
if (e.entryOffset + e.entryLength > mf.size()) damaged_file();
|
||||
}
|
||||
|
||||
std::string outname = out ? out : "";
|
||||
// if no name, pull it from the name record.
|
||||
if (!out) {
|
||||
auto iter = std::find_if(begin, end, [](const ASEntry &e) { return e.entryID == AS_REALNAME; });
|
||||
if (iter != end) {
|
||||
outname.assign((const char *)mf.data() + iter->entryOffset, iter->entryLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (outname.empty()) throw std::runtime_error("No filename");
|
||||
|
||||
int fd = open(outname.c_str(), O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (fd < 1) throw_errno();
|
||||
defer close_fd([fd](){ close(fd); });
|
||||
|
||||
for (auto iter = begin; iter != end; ++iter) {
|
||||
const auto &e = *iter;
|
||||
|
||||
if (e.entryLength == 0) continue;
|
||||
switch(e.entryID) {
|
||||
|
||||
case AS_DATA: {
|
||||
ssize_t ok = write(fd, mf.data() + e.entryOffset, e.entryLength);
|
||||
if (ok < 0) throw_errno();
|
||||
//if (ok != e.entryLength) return -1;
|
||||
break;
|
||||
}
|
||||
case AS_RESOURCE: {
|
||||
int fd = fopenattr(fd, "com.apple.ResourceFork", O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (fd < 0) throw_errno("com.apple.ResourceFork");
|
||||
defer close_fd([fd](){ close(fd); });
|
||||
|
||||
ssize_t ok = write(fd, mf.data() + e.entryOffset, e.entryLength);
|
||||
if (ok < 0) throw_errno("com.apple.ResourceFork");
|
||||
//if (ok != e.entryLength) return -1;
|
||||
break;
|
||||
}
|
||||
case AS_FINDERINFO: {
|
||||
if (e.entryLength != 32) {
|
||||
fputs("Warning: Invalid Finder Info size.\n", stderr);
|
||||
break;
|
||||
}
|
||||
int fd = fopenattr(fd, "com.apple.FinderInfo", O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (fd < 0) throw_errno("com.apple.ResourceFork");
|
||||
defer close_fd([fd](){ close(fd); });
|
||||
|
||||
ssize_t ok = write(fd, mf.data() + e.entryOffset, e.entryLength);
|
||||
if (ok < 0) throw_errno("com.apple.FinderInfo");
|
||||
//if (ok != e.entryLength) return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
fputs("Usage: applesingle [-o file] file...\n", stderr);
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
|
||||
int c;
|
||||
char *_o;
|
||||
|
||||
while ((c = getopt(argc, argv, "o:")) != -1) {
|
||||
|
||||
switch(c) {
|
||||
case 'o':
|
||||
_o = optarg;
|
||||
break;
|
||||
case ':':
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
int rv = 0;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
|
||||
try { unfork(argv[i], _o); }
|
||||
catch (std::exception &ex) {
|
||||
fprintf(stderr, "%s : %s\n", argv[i], ex.what());
|
||||
rv = 1;
|
||||
}
|
||||
_o = nullptr;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
Loading…
Reference in New Issue
Block a user