From f1d906d1567c0002cdf6160990ac22d61b7dfdc1 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 4 Mar 2017 14:14:27 -0500 Subject: [PATCH] appledouble. --- Makefile | 9 +- appledouble.cpp | 303 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 appledouble.cpp diff --git a/Makefile b/Makefile index 982db09..a98a0a9 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,19 @@ LINK.o = $(LINK.cc) CXXFLAGS += -std=c++11 -g -Wall -dot_clean: dot_clean.o mapped_file.o finder_info_helper.o xattr.o +.PHONY: all +all : dot_clean applesingle appledouble + +dot_clean : dot_clean.o mapped_file.o finder_info_helper.o xattr.o applesingle : applesingle.o mapped_file.o finder_info_helper.o xattr.o +appledouble : appledouble.o mapped_file.o finder_info_helper.o xattr.o mapped_file.o : mapped_file.cpp mapped_file.h unique_resource.h - dot_clean.o : dot_clean.cpp mapped_file.h applefile.h defer.h - applesingle.o : applesingle.cpp mapped_file.h applefile.h defer.h +appledouble.o : appledouble.cpp mapped_file.h applefile.h defer.h finder_info_helper.o: finder_info_helper.cpp finder_info_helper.h diff --git a/appledouble.cpp b/appledouble.cpp new file mode 100644 index 0000000..f0af4fe --- /dev/null +++ b/appledouble.cpp @@ -0,0 +1,303 @@ +/* + * convert a file to an appledouble file. + * + * + */ + +#include +#include + #include + +#include +#include +#include +#include +#include +#include +#include + +#include "mapped_file.h" +#include "xattr.h" +#include "finder_info_helper.h" + +#include "applefile.h" + + +#if defined(__linux__) +#define XATTR_RESOURCEFORK_NAME "user.com.apple.ResourceFork" +#endif + + +#if defined (_WIN32) +#define XATTR_FINDERINFO_NAME "AFP_Resource" +#endif + +#ifndef XATTR_FINDERINFO_NAME +#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo" +#endif + +#ifndef XATTR_RESOURCEFORK_NAME +#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork" +#endif + + +void usage() { + fputs("Usage: applesingle [-hv] [-o outfile] file ...\n", stderr); + exit(EX_USAGE); +} + +void help() { + fputs( + "Usage: appledouble [-hv] [-o outfile] file ...\n" + "\n" + " -o Specify outfile name\n" + " -h Display help\n" + " -v Be verbose\n", + stdout); + + exit(EX_OK); +} + +bool _v = false; +int _rv = 0; + + +std::vector read_resource_fork(const std::string &path, std::error_code ec) { + std::vector rv; + + ec.clear(); + + #if defined(__sun__) || defined(_WIN32) + int fd; + struct stat st; + + #if defined(__sun__) + fd = attropen(path.c_str(), XATTR_RESOURCEFORK_NAME, O_RDONLY); + #else + std::string p(path); + p += ":" XATTR_RESOURCEFORK_NAME; + + fd = open(p.c_str(), O_RDONLY); + #endif + if (fd < 0) { + ec = std::error_code(errno, std::generic_category()); + return rv; + } + + if (fstat(fd, &st) < 0) { + ec = std::error_code(errno, std::generic_category()); + close(fd); + return rv; + } + + if (st.st_size == 0) { + close(fd); + return rv; + } + + for(;;) { + rv.resize(st.st_size); + ssize_t ok = read(fd, rv.data(), st.st_size); + if (ok < 0) { + if (errno == EINTR) continue; + ec = std::error_code(errno, std::generic_category()); + rv.clear(); + break; + } + rv.resize(ok); + break; + } + close fd; + return rv; + #else + + int fd = open(path.c_str(), O_RDONLY); + if (fd < 0) { + ec = std::error_code(errno, std::generic_category()); + return rv; + } + + for(;;) { + ssize_t size = size_xattr(fd, XATTR_RESOURCEFORK_NAME); + if (size < 0) { + if (errno == EINTR) continue; + if (errno != ENOATTR) + ec = std::error_code(errno, std::generic_category()); + close(fd); + return rv; + } + if (size == 0) break; + rv.resize(size); + + ssize_t rsize = read_xattr(fd, XATTR_RESOURCEFORK_NAME, rv.data(), size); + if (rsize < 0) { + if (errno == ERANGE || errno == EINTR) continue; // try again. + ec = std::error_code(errno, std::generic_category()); + rv.clear(); + break; + } + rv.resize(rsize); + break; + } + close(fd); + return rv; + + #endif +} + + +/* check if a file is apple single or apple double format (or neither). */ +uint32_t classify(const mapped_file &mf) { + if (mf.size() < sizeof(ASHeader)) return 0; + + const ASHeader *header = (const ASHeader *)mf.data(); + if (header->magicNum == htonl(APPLESINGLE_MAGIC)) return APPLESINGLE_MAGIC; + if (header->magicNum == htonl(APPLEDOUBLE_MAGIC)) return APPLEDOUBLE_MAGIC; + + return 0; +} + +/* + * cases to consider + * 1. single file w/ fork/finder info + * 2. single file + appledouble file + * 3. single file w/o fork/finder info + * 4. file is already apple single / apple double format. + */ + +void one_file(const std::string &infile, const std::string &outfile) { + + // 1. check if apple single/apple double + mapped_file mf; + ASHeader head; + ASEntry e; + + + try { + mf = mapped_file(infile); + } catch(std::exception &ex) { + warnx("%s: %s", infile.c_str(), ex.what()); + _rv = 1; + return; + } + + uint32_t fmt = classify(mf); + if (fmt == APPLESINGLE_MAGIC) { + warnx("%s: File is apple single format.", infile.c_str()); + return; + } + + + if (fmt == APPLEDOUBLE_MAGIC) { + warnx("%s: File is apple double format.", infile.c_str()); + return; + } + + + // flag for preferred data source? + // 1. check for native fork + + // 2. check for apple double data. + + int count = 0; + + std::error_code ec; + + finder_info_helper fi; + bool fi_ok = fi.open(infile); + + + // ENOATTR is ok... but that's not an errc... + std::vector resource = read_resource_fork(infile, ec); + + if (ec && ec.value() != ENOATTR) { + warnc(ec.value(), "%s resource fork\n", infile.c_str()); + return; + } + + if (!fi_ok && resource.empty()) { + warnx("%s: File is not extended.", infile.c_str()); + return; + } + + if (resource.size()) count++; + if (fi_ok) count++; + + + int fd = open(outfile.c_str(), O_WRONLY | O_CREAT, 0666); + memset(&head, 0, sizeof(head)); + head.magicNum = htonl(APPLEDOUBLE_MAGIC); + head.versionNum = htonl(0x00020000); + head.numEntries = htons(count); + write(fd, &head, sizeof(head)); + + off_t offset = sizeof(ASHeader) + sizeof(ASEntry) * count; + + + // 1 - finder info + if (fi_ok) { + e.entryID = htonl(AS_FINDERINFO); + e.entryOffset = htonl(offset); + e.entryLength = htonl(32); + write(fd, &e, sizeof(e)); + + offset += 32; + } + // 2 - resource fork? + if (resource.size()) { + + e.entryID = htonl(AS_RESOURCE); + e.entryOffset = htonl(offset); + e.entryLength = htonl(resource.size()); + write(fd, &e, sizeof(e)); + + offset += resource.size(); + } + + // now write it.. + + // 1 - finder info + if (fi_ok) { + write(fd, fi.finder_info(), 32); + } + + // 2 - resource fork? + if (resource.size()) { + write(fd, resource.data(), resource.size()); + } + close(fd); +} + +int main(int argc, char **argv) { + + std::string _o; + + int c; + + while ((c = getopt(argc, argv, "o:v")) != -1) { + + switch(c) { + case 'v': _v = true; break; + case 'h': help(); break; + case 'o': _o = optarg; break; + case ':': + case '?': + default: + usage(); + break; + } + } + + argv += optind; + argc -= optind; + + if (!argc) usage(); + + for (int i = 0; i < argc; ++i) { + std::string s(argv[i]); + if (_o.empty()) _o = "._" + s; + one_file(s, _o); + _o.clear(); + } + return _rv; +} \ No newline at end of file