From 7e9d9075d9a8d352021b02dd713f4cfc2c8a4773 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Tue, 23 Aug 2016 20:55:13 -0400 Subject: [PATCH] Miscellaneous changes while enabling iOS support Put all of the logic for discovering iOS in one place in ios.h. Make TAR_DEBUG configurable and default it to 0. Various whitespace style pickiness. --- ios.h | 23 ++- ios.mm | 633 ++++++++++++++++++++++++++++++--------------------------- tz.cpp | 10 +- 3 files changed, 354 insertions(+), 312 deletions(-) diff --git a/ios.h b/ios.h index 7b9c3d9..3f791bd 100644 --- a/ios.h +++ b/ios.h @@ -27,16 +27,23 @@ #ifndef ios_hpp #define ios_hpp -#include +#if __APPLE__ +# include +# if TARGET_OS_IPHONE +# include -namespace date -{ -namespace iOSUtils -{ + namespace date + { + namespace iOSUtils + { -std::string get_tzdata_path(); + std::string get_tzdata_path(); -} // namespace iOSUtils -} // namespace date + } // namespace iOSUtils + } // namespace date +# endif // TARGET_OS_IPHONE +#else // !__APPLE__ +# define TARGET_OS_IPHONE 0 +#endif // !__APPLE__ #endif // ios_hpp diff --git a/ios.mm b/ios.mm index 3233f2b..ec95302 100644 --- a/ios.mm +++ b/ios.mm @@ -22,7 +22,9 @@ // SOFTWARE. // -#ifdef TARGET_OS_IPHONE +#include "ios.h" + +#if TARGET_OS_IPHONE #include @@ -30,11 +32,10 @@ #include #include -#include "ios.h" +#ifndef TAR_DEBUG +# define TAR_DEBUG 0 +#endif -#define TAR_DEBUG - -#define DIR_NAME "tzdata" #define INTERNAL_DIR "Library/tzdata" #define TARGZ_EXTENSION "tar.gz" @@ -47,322 +48,358 @@ namespace date { - namespace iOSUtils +namespace iOSUtils +{ + +struct TarInfo +{ + char objType; + std::string objName; + int64_t realContentSize; // writable size without padding zeroes + int64_t blocksContentSize; // adjusted size to 512 bytes blocks + bool success; +}; + +char* convertCFStringRefPathToCStringPath(CFStringRef ref); +bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); +TarInfo getTarObjectInfo(CFReadStreamRef readStream, int64_t location); +std::string getTarObject(CFReadStreamRef readStream, int64_t size); +bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, + int64_t realContentSize); + +std::string +date::iOSUtils::get_tzdata_path() +{ + CFURLRef ref = CFCopyHomeDirectoryURL(); + CFStringRef homePath = CFURLCopyPath(CFCopyHomeDirectoryURL()); + std::string tzdata_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + + INTERNAL_DIR); + + if (access(tzdata_path.c_str(), F_OK) == 0) { - - struct TarInfo { - char objType; - std::string objName; - int64_t realContentSize; // writable size without padding zeroes - int64_t blocksContentSize; // adjusted size to 512 bytes blocks - bool success; - }; - - char* convertCFStringRefPathToCStringPath(CFStringRef ref); - bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); - TarInfo getTarObjectInfo(CFReadStreamRef readStream, int64_t location); - std::string getTarObject(CFReadStreamRef readStream, int64_t size); - bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, int64_t realContentSize); - - std::string - date::iOSUtils::get_tzdata_path() - { - CFURLRef ref = CFCopyHomeDirectoryURL(); - CFStringRef homePath = CFURLCopyPath(CFCopyHomeDirectoryURL()); - std::string tzdata_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + INTERNAL_DIR); - - if (access(tzdata_path.c_str(), F_OK) == 0) { -#ifdef TAR_DEBUG - printf("tzdata exists\n"); +#if TAR_DEBUG + printf("tzdata exists\n"); #endif - return tzdata_path; - } - - CFBundleRef mainBundle = CFBundleGetMainBundle(); - CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), NULL); - - if (CFArrayGetCount(paths) != 0) { - // get archive path, assume there is no other tar.gz in bundle - CFURLRef archiveUrl = static_cast(CFArrayGetValueAtIndex(paths, 0)); - CFStringRef archiveName= CFURLCopyPath(archiveUrl); - archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); - - extractTzdata(CFCopyHomeDirectoryURL(), archiveUrl, tzdata_path); - } - - return tzdata_path; + return tzdata_path; + } + + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), + NULL); + + if (CFArrayGetCount(paths) != 0) + { + // get archive path, assume there is no other tar.gz in bundle + CFURLRef archiveUrl = static_cast(CFArrayGetValueAtIndex(paths, 0)); + CFStringRef archiveName= CFURLCopyPath(archiveUrl); + archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); + + extractTzdata(CFCopyHomeDirectoryURL(), archiveUrl, tzdata_path); + } + + return tzdata_path; +} + +char* +convertCFStringRefPathToCStringPath(CFStringRef ref) +{ + CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); + char *buffer = new char[bufferSize]; + CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); + return buffer; +} + +bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) +{ + const char *TAR_TMP_PATH = "/tmp.tar"; + + // create Library path + CFStringRef libraryStr = CFStringCreateWithCString(NULL, "Library", + CFStringGetSystemEncoding()); + CFURLRef libraryUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, + homeUrl, libraryStr, + false); + + // create tzdata path + CFStringRef tzdataPathRef = CFStringCreateWithCString(NULL, INTERNAL_DIR, + CFStringGetSystemEncoding()); + CFURLRef tzdataPathUrl = CFURLCreateCopyAppendingPathComponent(NULL, homeUrl, + tzdataPathRef, false); + + // create src archive path + CFStringRef archivePath = CFURLCopyPath(archiveUrl); + gzFile tarFile = gzopen(convertCFStringRefPathToCStringPath(archivePath), "rb"); + + // create tar unpacking path + CFStringRef tarName = CFStringCreateWithCString(NULL, TAR_TMP_PATH, + CFStringGetSystemEncoding()); + CFURLRef tarUrl = CFURLCreateCopyAppendingPathComponent(NULL, libraryUrl, tarName, + false); + const char *tarPath = convertCFStringRefPathToCStringPath(CFURLCopyPath(tarUrl)); + + // create tzdata directory + mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + // create stream + CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, tarUrl); + bool success = true; + + if (!CFWriteStreamOpen(writeStream)) + { + CFStreamError err = CFWriteStreamGetError(writeStream); + + if (err.domain == kCFStreamErrorDomainPOSIX) + { + printf("kCFStreamErrorDomainPOSIX %i\n", err.error); + } + else if(err.domain == kCFStreamErrorDomainMacOSStatus) + { + printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); } - char* convertCFStringRefPathToCStringPath(CFStringRef ref) + success = false; + } + + if (!success) + { + remove(tarPath); + return false; + } + + // ======= extract tar ======== + + unsigned int bufferLength = 1024 * 256; // 256Kb + void *buffer = malloc(bufferLength); + + while (true) + { + int readBytes = gzread(tarFile, buffer, bufferLength); + + if (readBytes > 0) { - CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); - char *buffer = new char[bufferSize]; - CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); - return buffer; - } - - bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) - { - const char *TAR_TMP_PATH = "/tmp.tar"; + CFIndex writtenBytes = CFWriteStreamWrite(writeStream, (unsigned char*)buffer, + readBytes); - // create Library path - CFStringRef libraryStr = CFStringCreateWithCString(NULL, "Library", CFStringGetSystemEncoding()); - CFURLRef libraryUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, homeUrl, libraryStr, false); - - // create tzdata path - CFStringRef tzdataPathRef = CFStringCreateWithCString(NULL, INTERNAL_DIR, CFStringGetSystemEncoding()); - CFURLRef tzdataPathUrl = CFURLCreateCopyAppendingPathComponent(NULL, homeUrl, tzdataPathRef, false); - - // create src archive path - CFStringRef archivePath = CFURLCopyPath(archiveUrl); - gzFile tarFile = gzopen(convertCFStringRefPathToCStringPath(archivePath), "rb"); - - // create tar unpacking path - CFStringRef tarName = CFStringCreateWithCString(NULL, TAR_TMP_PATH, CFStringGetSystemEncoding()); - CFURLRef tarUrl = CFURLCreateCopyAppendingPathComponent(NULL, libraryUrl, tarName, false); - const char *tarPath = convertCFStringRefPathToCStringPath(CFURLCopyPath(tarUrl)); - - // create tzdata directory - mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - // create stream - CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, tarUrl); - bool success = true; - - if (!CFWriteStreamOpen(writeStream)) { - CFStreamError err = CFWriteStreamGetError(writeStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) { - printf("kCFStreamErrorDomainPOSIX %i\n", err.error); - } else if(err.domain == kCFStreamErrorDomainMacOSStatus) { - printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); - } - - success = false; - } - - if (!success) { - remove(tarPath); - return false; - } - - // ======= extract tar ======== - - unsigned int bufferLength = 1024 * 256; // 256Kb - void *buffer = malloc(bufferLength); - - while (true) { - int readBytes = gzread(tarFile, buffer, bufferLength); - - if (readBytes > 0) { - CFIndex writtenBytes = CFWriteStreamWrite(writeStream, (unsigned char*)buffer, readBytes); - - if (writtenBytes < 0) { - CFStreamError err = CFWriteStreamGetError(writeStream); - printf("write stream error %i\n", err.error); - success = false; - break; - } - } else if (readBytes == 0) { - break; - } else { - if (readBytes == -1) { - printf("decompression failed\n"); - success = false; - break; - } - else { - printf("unexpected zlib state\n"); - success = false; - break; - } - } - } - - CFWriteStreamClose(writeStream); - CFRelease(writeStream); - free(buffer); - gzclose(tarFile); - - if (!success) { - remove(tarPath); - return false; - } - - // ======== extract files ========= - - uint64_t location = 0; // Position in the file - - // get file size - struct stat stat_buf; - int res = stat(tarPath, &stat_buf); - if (res != 0) { - printf("error file size\n"); - remove(tarPath); - return false; - } - int64_t tarSize = stat_buf.st_size; - - // create read stream - CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, tarUrl); - - if (!CFReadStreamOpen(readStream)) { - CFStreamError err = CFReadStreamGetError(readStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) { - printf("kCFStreamErrorDomainPOSIX %i", err.error); - } else if(err.domain == kCFStreamErrorDomainMacOSStatus) { - printf("kCFStreamErrorDomainMacOSStatus %i", err.error); - } - - success = false; - } - - if (!success) { - CFRelease(readStream); - remove(tarPath); - return false; - } - - int count = 0; - long size = 0; - - // process files - while (location < tarSize) { - TarInfo info = getTarObjectInfo(readStream, location); - - if (!info.success || info.realContentSize == 0) { - break; // something wrong or all files are read - } - - switch (info.objType) { - case '0': // file - case '\0': // - { - std::string obj = getTarObject(readStream, info.blocksContentSize); -#ifdef TAR_DEBUG - size += info.realContentSize; - printf("#%i %s file size %lld written total %ld from %lld\n", - ++count, info.objName.c_str(), info.realContentSize, size, tarSize); -#endif - writeFile(tzdataPathUrl, info.objName, obj, info.realContentSize); - location += info.blocksContentSize; - - break; - } - - } - } - - CFReadStreamClose(readStream); - CFRelease(readStream); - - remove(tarPath); - - return true; - } - - TarInfo getTarObjectInfo(CFReadStreamRef readStream, int64_t location) - { - int64_t length = TAR_BLOCK_SIZE; - uint8_t buffer[length]; - - char type; - char name[TAR_NAME_SIZE + 1]; - char sizeBuf[TAR_SIZE_SIZE + 1]; - CFIndex bytesRead; - - bool avail = CFReadStreamHasBytesAvailable(readStream); - - bytesRead = CFReadStreamRead(readStream, buffer, length); - - if (bytesRead < 0) { - CFStreamError err = CFReadStreamGetError(readStream); - printf("error reading tar object info %i", err.error); - return {false}; - } - - memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); - - memset(&name, '\0', TAR_NAME_SIZE + 1); - memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); - - memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); - memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); - int64_t realSize = strtol(sizeBuf, NULL, 8); - int64_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); - - return {type, std::string(name), realSize, blocksSize, true}; - } - - std::string getTarObject(CFReadStreamRef readStream, int64_t size) - { - uint8_t buffer[size]; - - CFIndex bytesRead = CFReadStreamRead(readStream, buffer, size); - - if (bytesRead < 0) { - CFStreamError err = CFReadStreamGetError(readStream); - printf("error reading tar object info %i", err.error); - } - - return std::string((char *)buffer); - } - - bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, int64_t realContentSize) - { - // create stream - CFStringRef fileNameRef = CFStringCreateWithCString(NULL, fileName.c_str(), CFStringGetSystemEncoding()); - CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, tzdataUrl, fileNameRef, false); - CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, url); - - // open stream - if (!CFWriteStreamOpen(writeStream)) { - CFStreamError err = CFWriteStreamGetError(writeStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) { - printf("kCFStreamErrorDomainPOSIX %i\n", err.error); - } else if(err.domain == kCFStreamErrorDomainMacOSStatus) { - printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); - } - - CFRelease(writeStream); - return false; - } - - // trim empty space - uint8_t trimmedData[realContentSize + 1]; - memset(&trimmedData, '\0', realContentSize); - memcpy(&trimmedData, data.c_str(), realContentSize); - - // write - CFIndex writtenBytes = CFWriteStreamWrite(writeStream, trimmedData, realContentSize); - - if (writtenBytes < 0) { + if (writtenBytes < 0) + { CFStreamError err = CFWriteStreamGetError(writeStream); printf("write stream error %i\n", err.error); + success = false; + break; } - - CFWriteStreamClose(writeStream); - CFRelease(writeStream); - writeStream = NULL; - - return true; + } + else if (readBytes == 0) + { + break; + } + else if (readBytes == -1) + { + printf("decompression failed\n"); + success = false; + break; + } + else + { + printf("unexpected zlib state\n"); + success = false; + break; + } + } + + CFWriteStreamClose(writeStream); + CFRelease(writeStream); + free(buffer); + gzclose(tarFile); + + if (!success) + { + remove(tarPath); + return false; + } + + // ======== extract files ========= + + uint64_t location = 0; // Position in the file + + // get file size + struct stat stat_buf; + int res = stat(tarPath, &stat_buf); + if (res != 0) + { + printf("error file size\n"); + remove(tarPath); + return false; + } + int64_t tarSize = stat_buf.st_size; + + // create read stream + CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, tarUrl); + + if (!CFReadStreamOpen(readStream)) + { + CFStreamError err = CFReadStreamGetError(readStream); + + if (err.domain == kCFStreamErrorDomainPOSIX) + { + printf("kCFStreamErrorDomainPOSIX %i", err.error); + } + else if(err.domain == kCFStreamErrorDomainMacOSStatus) + { + printf("kCFStreamErrorDomainMacOSStatus %i", err.error); } + success = false; } + + if (!success) + { + CFRelease(readStream); + remove(tarPath); + return false; + } + + int count = 0; + long size = 0; + + // process files + while (location < tarSize) + { + TarInfo info = getTarObjectInfo(readStream, location); + + if (!info.success || info.realContentSize == 0) + { + break; // something wrong or all files are read + } + + switch (info.objType) + { + case '0': // file + case '\0': // + { + std::string obj = getTarObject(readStream, info.blocksContentSize); +#if TAR_DEBUG + size += info.realContentSize; + printf("#%i %s file size %lld written total %ld from %lld\n", ++count, + info.objName.c_str(), info.realContentSize, size, tarSize); +#endif + writeFile(tzdataPathUrl, info.objName, obj, info.realContentSize); + location += info.blocksContentSize; + + break; + } + } + } + + CFReadStreamClose(readStream); + CFRelease(readStream); + + remove(tarPath); + + return true; +} + +TarInfo +getTarObjectInfo(CFReadStreamRef readStream, int64_t location) +{ + int64_t length = TAR_BLOCK_SIZE; + uint8_t buffer[length]; + + char type; + char name[TAR_NAME_SIZE + 1]; + char sizeBuf[TAR_SIZE_SIZE + 1]; + CFIndex bytesRead; + + bool avail = CFReadStreamHasBytesAvailable(readStream); + + bytesRead = CFReadStreamRead(readStream, buffer, length); + + if (bytesRead < 0) + { + CFStreamError err = CFReadStreamGetError(readStream); + printf("error reading tar object info %i", err.error); + return {false}; + } + + memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); + + memset(&name, '\0', TAR_NAME_SIZE + 1); + memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); + + memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); + memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); + int64_t realSize = strtol(sizeBuf, NULL, 8); + int64_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); + + return {type, std::string(name), realSize, blocksSize, true}; } -#endif +std::string +getTarObject(CFReadStreamRef readStream, int64_t size) +{ + uint8_t buffer[size]; + CFIndex bytesRead = CFReadStreamRead(readStream, buffer, size); + if (bytesRead < 0) + { + CFStreamError err = CFReadStreamGetError(readStream); + printf("error reading tar object info %i", err.error); + } + return std::string((char *)buffer); +} +bool +writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, + int64_t realContentSize) +{ + // create stream + CFStringRef fileNameRef = CFStringCreateWithCString(NULL, fileName.c_str(), + CFStringGetSystemEncoding()); + CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, tzdataUrl, fileNameRef, + false); + CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, url); + // open stream + if (!CFWriteStreamOpen(writeStream)) + { + CFStreamError err = CFWriteStreamGetError(writeStream); + if (err.domain == kCFStreamErrorDomainPOSIX) + { + printf("kCFStreamErrorDomainPOSIX %i\n", err.error); + } + else if(err.domain == kCFStreamErrorDomainMacOSStatus) + { + printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); + } + CFRelease(writeStream); + return false; + } + // trim empty space + uint8_t trimmedData[realContentSize + 1]; + memset(&trimmedData, '\0', realContentSize); + memcpy(&trimmedData, data.c_str(), realContentSize); + // write + CFIndex writtenBytes = CFWriteStreamWrite(writeStream, trimmedData, realContentSize); + if (writtenBytes < 0) + { + CFStreamError err = CFWriteStreamGetError(writeStream); + printf("write stream error %i\n", err.error); + } + CFWriteStreamClose(writeStream); + CFRelease(writeStream); + writeStream = NULL; + return true; +} + +} // namespace iOSUtils +} // namespace date + +#endif // TARGET_OS_IPHONE diff --git a/tz.cpp b/tz.cpp index d6d2567..de2090f 100644 --- a/tz.cpp +++ b/tz.cpp @@ -75,6 +75,7 @@ #endif // _WIN32 #include "tz_private.h" +#include "ios.h" #include #include @@ -117,9 +118,6 @@ # endif //!USE_SHELL_API #endif // !WIN32 -#ifdef TARGET_OS_IPHONE -#include "ios.h" -#endif #if HAS_REMOTE_API // Note curl includes windows.h so we must include curl AFTER definitions of things @@ -184,15 +182,15 @@ static std::string expand_path(std::string path) { -#ifndef TARGET_OS_IPHONE +#if TARGET_OS_IPHONE + return date::iOSUtils::get_tzdata_path(); +#else ::wordexp_t w{}; ::wordexp(path.c_str(), &w, 0); assert(w.we_wordc == 1); path = w.we_wordv[0]; ::wordfree(&w); return path; -#else - return date::iOSUtils::get_tzdata_path(); #endif }