#include "CFBundle.h"
#ifdef __APPLE__
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <CoreFoundation/CoreFoundation.h>
#include <assert.h>
#include <glob.h>
template <class T> class CFReleaser {
public:
CFReleaser(T ptr = NULL) : _ptr(ptr) {}
CFReleaser(const CFReleaser &rhs) : _ptr(rhs.get()) {
if (get())
::CFRetain(get());
}
virtual ~CFReleaser() { reset(); }
CFReleaser &operator=(const CFReleaser<T> &rhs) {
if (this != &rhs) {
reset(rhs.get());
if (get())
::CFRetain(get());
}
return *this;
}
T *ptr_address() {
assert(_ptr == NULL);
return &_ptr;
}
T get() { return _ptr; }
const T get() const { return _ptr; }
void reset(T ptr = NULL) {
if ((_ptr != NULL) && (ptr != _ptr))
::CFRelease(_ptr);
_ptr = ptr;
}
T release() {
T tmp = _ptr;
_ptr = NULL;
return tmp;
}
private:
T _ptr;
};
class CFString : public CFReleaser<CFStringRef> {
public:
CFString(CFStringRef cf_str = NULL);
CFString(const char *s, CFStringEncoding encoding);
CFString(const CFString &rhs);
CFString &operator=(const CFString &rhs);
virtual ~CFString();
const char *GetFileSystemRepresentation(std::string &str);
CFStringRef SetFileSystemRepresentation(const char *path);
CFStringRef SetFileSystemRepresentationFromCFType(CFTypeRef cf_type);
CFStringRef SetFileSystemRepresentationAndExpandTilde(const char *path);
const char *UTF8(std::string &str);
CFIndex GetLength() const;
static const char *UTF8(CFStringRef cf_str, std::string &str);
static const char *FileSystemRepresentation(CFStringRef cf_str,
std::string &str);
};
class CFBundle : public CFReleaser<CFBundleRef> {
public:
CFBundle(const char *path = NULL);
CFBundle(const CFBundle &rhs);
CFBundle(CFURLRef url);
CFBundle &operator=(const CFBundle &rhs);
virtual ~CFBundle();
bool SetPath(const char *path);
CFStringRef GetIdentifier() const;
CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const;
};
static const char *CreatePathByExpandingTildePath(const char *path,
std::string &expanded_path) {
glob_t globbuf;
if (glob(path, GLOB_TILDE, NULL, &globbuf) == 0) {
expanded_path = globbuf.gl_pathv[0];
globfree(&globbuf);
} else
expanded_path.clear();
return expanded_path.c_str();
}
CFString::CFString(CFStringRef s) : CFReleaser<CFStringRef>(s) {}
CFString::CFString(const CFString &rhs) : CFReleaser<CFStringRef>(rhs) {}
CFString &CFString::operator=(const CFString &rhs) {
if (this != &rhs)
*this = rhs;
return *this;
}
CFString::CFString(const char *cstr, CFStringEncoding cstr_encoding)
: CFReleaser<CFStringRef>() {
if (cstr && cstr[0]) {
reset(
::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
}
}
CFString::~CFString() {}
const char *CFString::GetFileSystemRepresentation(std::string &s) {
return CFString::FileSystemRepresentation(get(), s);
}
CFStringRef CFString::SetFileSystemRepresentation(const char *path) {
CFStringRef new_value = NULL;
if (path && path[0])
new_value =
::CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
reset(new_value);
return get();
}
CFStringRef CFString::SetFileSystemRepresentationFromCFType(CFTypeRef cf_type) {
CFStringRef new_value = NULL;
if (cf_type != NULL) {
CFTypeID cf_type_id = ::CFGetTypeID(cf_type);
if (cf_type_id == ::CFStringGetTypeID()) {
new_value = (CFStringRef)::CFRetain(cf_type);
} else if (cf_type_id == ::CFURLGetTypeID()) {
new_value =
::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle);
}
}
reset(new_value);
return get();
}
CFStringRef
CFString::SetFileSystemRepresentationAndExpandTilde(const char *path) {
std::string expanded_path;
if (CreatePathByExpandingTildePath(path, expanded_path))
SetFileSystemRepresentation(expanded_path.c_str());
else
reset();
return get();
}
const char *CFString::UTF8(std::string &str) {
return CFString::UTF8(get(), str);
}
const char *CFString::UTF8(CFStringRef cf_str, std::string &str) {
if (cf_str) {
const CFStringEncoding encoding = kCFStringEncodingUTF8;
CFIndex max_utf8_str_len = CFStringGetLength(cf_str);
max_utf8_str_len =
CFStringGetMaximumSizeForEncoding(max_utf8_str_len, encoding);
if (max_utf8_str_len > 0) {
str.resize(max_utf8_str_len);
if (!str.empty()) {
if (CFStringGetCString(cf_str, &str[0], str.size(), encoding)) {
str.resize(strlen(str.c_str()));
return str.c_str();
}
}
}
}
return NULL;
}
const char *CFString::FileSystemRepresentation(CFStringRef cf_str,
std::string &str) {
if (cf_str) {
CFIndex max_length =
::CFStringGetMaximumSizeOfFileSystemRepresentation(cf_str);
if (max_length > 0) {
str.resize(max_length);
if (!str.empty()) {
if (::CFStringGetFileSystemRepresentation(cf_str, &str[0],
str.size())) {
str.erase(::strlen(str.c_str()));
return str.c_str();
}
}
}
}
str.erase();
return NULL;
}
CFIndex CFString::GetLength() const {
CFStringRef str = get();
if (str)
return CFStringGetLength(str);
return 0;
}
CFBundle::CFBundle(const char *path) : CFReleaser<CFBundleRef>() {
if (path && path[0])
SetPath(path);
}
CFBundle::CFBundle(const CFBundle &rhs) : CFReleaser<CFBundleRef>(rhs) {}
CFBundle::CFBundle(CFURLRef url)
: CFReleaser<CFBundleRef>(url ? CFBundleCreate(NULL, url) : NULL) {}
CFBundle &CFBundle::operator=(const CFBundle &rhs) {
if (this != &rhs)
*this = rhs;
return *this;
}
CFBundle::~CFBundle() {}
bool CFBundle::SetPath(const char *in_path) {
reset();
if (in_path && in_path[0]) {
char resolved_path[PATH_MAX];
const char *path = ::realpath(in_path, resolved_path);
if (path == NULL)
path = in_path;
CFAllocatorRef alloc = kCFAllocatorDefault;
CFReleaser<CFURLRef> bundle_url(::CFURLCreateFromFileSystemRepresentation(
alloc, (const UInt8 *)path, strlen(path), false));
if (bundle_url.get()) {
CFIndex last_length = LONG_MAX;
while (bundle_url.get() != NULL) {
CFRange rangeIncludingSeparators;
CFRange range = ::CFURLGetByteRangeForComponent(
bundle_url.get(), kCFURLComponentPath, &rangeIncludingSeparators);
if (range.length > last_length)
break;
reset(::CFBundleCreate(alloc, bundle_url.get()));
if (get() != NULL) {
if (GetIdentifier() != NULL)
break;
reset();
}
bundle_url.reset(::CFURLCreateCopyDeletingLastPathComponent(
alloc, bundle_url.get()));
last_length = range.length;
}
}
}
return get() != NULL;
}
CFStringRef CFBundle::GetIdentifier() const {
CFBundleRef bundle = get();
if (bundle != NULL)
return ::CFBundleGetIdentifier(bundle);
return NULL;
}
CFTypeRef CFBundle::GetValueForInfoDictionaryKey(CFStringRef key) const {
CFBundleRef bundle = get();
if (bundle != NULL)
return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
return NULL;
}
#endif
void getBundleInfo(llvm::StringRef ExePath, std::string &bundleVersionStr,
std::string &bundleShortVersionStr, std::string &bundleIDStr,
bool &OmitShortVersion) {
#ifdef __APPLE__
CFTypeRef cf = NULL;
CFTypeID cf_type_id = 0;
if (!ExePath.empty() && llvm::sys::fs::exists(ExePath)) {
const char *exe_path = ExePath.data();
CFBundle bundle(exe_path);
CFStringRef bundleID = bundle.GetIdentifier();
if (bundleID != NULL) {
CFString::UTF8(bundleID, bundleIDStr);
cf = bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"));
if (cf != NULL) {
cf_type_id = ::CFGetTypeID(cf);
if (cf_type_id == ::CFStringGetTypeID())
CFString::UTF8((CFStringRef)cf, bundleVersionStr);
else {
CFString cf_type_id_cfstr(::CFCopyTypeIDDescription(cf_type_id));
std::string cf_type_id_str;
llvm::errs() << "The Info.plist key \"CFBundleVersion\" is a "
<< cf_type_id_cfstr.UTF8(cf_type_id_str)
<< "but it should be a string in: " << exe_path << ".\n";
}
}
cf = bundle.GetValueForInfoDictionaryKey(
CFSTR("CFBundleShortVersionString"));
if (cf != NULL) {
cf_type_id = ::CFGetTypeID(cf);
if (::CFGetTypeID(cf) == ::CFStringGetTypeID())
CFString::UTF8((CFStringRef)cf, bundleShortVersionStr);
else {
CFString cf_type_id_cfstr(::CFCopyTypeIDDescription(cf_type_id));
std::string cf_type_id_str;
llvm::errs() << "The Info.plist key \"CFBundleShortVersionString\" is"
<< "a " << cf_type_id_cfstr.UTF8(cf_type_id_str)
<< ", but it should be a string in: " << exe_path
<< ".\n";
}
}
OmitShortVersion = bundleShortVersionStr.empty();
}
}
#endif
if (bundleVersionStr.empty())
bundleVersionStr = "1";
if (bundleShortVersionStr.empty() && !OmitShortVersion)
bundleShortVersionStr = "1.0";
}