#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <mach-o/dyld.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "utils.h"
char*
cfstrdup(CFStringRef str) {
char* result = NULL;
if (str != NULL) {
CFIndex length = CFStringGetLength(str);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
result = malloc(size+1);
if (result != NULL) {
length = CFStringGetBytes(str, CFRangeMake(0, length), kCFStringEncodingUTF8, '?', 0, (UInt8*)result, size, NULL);
result[length] = 0;
}
}
return result;
}
CFStringRef
cfstr(const char* str) {
CFStringRef result = NULL;
if (str) result = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
return result;
}
void
cfperror(CFStringRef str) {
char* cstr = cfstrdup(str);
if (cstr) {
fprintf(stderr, "%s", cstr);
free(cstr);
}
}
CFPropertyListRef
read_plist(const char* path, const char **errstr) {
CFPropertyListRef result = NULL;
int fd = open(path, O_RDONLY, (mode_t)0);
if (fd != -1) {
struct stat sb;
if (stat(path, &sb) != -1) {
off_t size = sb.st_size;
void* buffer = mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, (off_t)0);
if (buffer != (void*)-1) {
CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, buffer, size, kCFAllocatorNull);
if (data) {
CFStringRef str = NULL;
result = CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListMutableContainers, &str);
CFRelease(data);
if (result == NULL && errstr) {
*errstr = (const char *)cfstrdup(str);
}
} else {
if (errstr) *errstr = "CFDataCreateWithBytesNoCopy failed";
}
munmap(buffer, size);
} else if (errstr) {
*errstr = (const char *)strerror(errno);
}
} else if (errstr) {
*errstr = (const char *)strerror(errno);
}
close(fd);
} else if (errstr) {
*errstr = (const char *)strerror(errno);
}
return result;
}
int
write_plist_fd(int fd, CFPropertyListRef plist) {
CFIndex len, n;
const UInt8 *ptr;
CFDataRef xmlData;
errno = 0;
xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
if (xmlData == NULL) {
if (errno == 0) errno = ENOMEM;
return -1;
}
len = CFDataGetLength(xmlData);
ptr = CFDataGetBytePtr(xmlData);
while(len > 0) {
if ((n = write(fd, ptr, len)) < 0) {
int save = errno;
if (errno == EAGAIN || errno == EINTR)
continue;
CFRelease(xmlData);
errno = save;
return -1;
}
ptr += n;
len -= n;
}
CFRelease(xmlData);
return 0;
}
int
write_plist(const char *path, CFPropertyListRef plist) {
int fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0) return -1;
int ret = write_plist_fd(fd, plist);
close(fd);
return ret;
}
int
cfprintf(FILE* file, const char* format, ...) {
char* cstr;
int result;
va_list args;
va_start(args, format);
CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull);
CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args);
va_end(args);
cstr = cfstrdup(str);
result = fprintf(file, "%s", cstr);
free(cstr);
CFRelease(str);
CFRelease(formatStr);
return result;
}
CFArrayRef
dictionaryGetSortedKeys(CFDictionaryRef dictionary) {
CFIndex count = CFDictionaryGetCount(dictionary);
const void** keys = malloc(sizeof(CFStringRef) * count);
CFDictionaryGetKeysAndValues(dictionary, keys, NULL);
CFArrayRef keysArray = CFArrayCreate(NULL, keys, count, &kCFTypeArrayCallBacks);
CFMutableArrayRef sortedKeys = CFArrayCreateMutableCopy(NULL, count, keysArray);
CFRelease(keysArray);
free(keys);
CFArraySortValues(sortedKeys, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, 0);
return sortedKeys;
}
void
dictionaryApplyFunctionSorted(CFDictionaryRef dict,
CFDictionaryApplierFunction applier,
void* context) {
CFArrayRef keys = dictionaryGetSortedKeys(dict);
if (keys) {
CFIndex i, count = CFArrayGetCount(keys);
for (i = 0; i < count; ++i) {
CFTypeRef key = CFArrayGetValueAtIndex(keys, i);
CFTypeRef val = CFDictionaryGetValue(dict, key);
applier(key, val, context);
}
CFRelease(keys);
}
}
#ifdef notdef
int
writePlist(FILE* f, CFPropertyListRef p, int tabs) {
int result = 0;
CFTypeID type = CFGetTypeID(p);
if (tabs == 0) {
result += fprintf(f, "// !$*UTF8*$!\n");
}
char* t = malloc(tabs+1);
int i;
for (i = 0; i < tabs; ++i) {
t[i] = '\t';
}
t[tabs] = 0;
if (type == CFStringGetTypeID()) {
char* utf8 = cfstrdup(p);
int quote = 0;
for (i = 0 ;; ++i) {
int c = utf8[i];
if (c == 0) break;
if (!((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '/' ||
c == '.' ||
c == '_' )) {
quote = 1;
break;
}
}
if (utf8[0] == 0) quote = 1;
if (quote) result += fprintf(f, "\"");
for (i = 0 ;; ++i) {
int c = utf8[i];
if (c == 0) break;
if (c == '\"' || c == '\\') fprintf(f, "\\");
result += fprintf(f, "%c", c);
}
if (quote) result += fprintf(f, "\"");
free(utf8);
} else if (type == CFArrayGetTypeID()) {
result += fprintf(f, "(\n");
int count = CFArrayGetCount(p);
for (i = 0; i < count; ++i) {
CFTypeRef pp = CFArrayGetValueAtIndex(p, i);
result += fprintf(f, "%s\t", t);
result += writePlist(f, pp, tabs+1);
result += fprintf(f, ",\n");
}
result += fprintf(f, "%s)", t);
} else if (type == CFDictionaryGetTypeID()) {
result += fprintf(f, "{\n");
CFArrayRef keys = dictionaryGetSortedKeys(p);
int count = CFArrayGetCount(keys);
for (i = 0; i < count; ++i) {
CFStringRef key = CFArrayGetValueAtIndex(keys, i);
result += fprintf(f, "\t%s", t);
result += writePlist(f, key, tabs+1);
result += fprintf(f, " = ");
result += writePlist(f, CFDictionaryGetValue(p,key), tabs+1);
result += fprintf(f, ";\n");
}
CFRelease(keys);
result += fprintf(f, "%s}", t);
}
if (tabs == 0) result += fprintf(f, "\n");
free(t);
return result;
}
#endif
CFArrayRef
tokenizeString(CFStringRef str) {
if (!str) return NULL;
CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFCharacterSetRef whitespace = CFCharacterSetCreateWithCharactersInString(NULL, CFSTR(" \t\n\r"));
CFStringInlineBuffer buf;
CFIndex i, length = CFStringGetLength(str);
CFIndex start = 0;
CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, length));
for (i = 0; i < length; ++i) {
UniChar c = CFStringGetCharacterFromInlineBuffer(&buf, i);
if (CFCharacterSetIsCharacterMember(whitespace, c)) {
if (start != kCFNotFound) {
CFStringRef sub = CFStringCreateWithSubstring(NULL, str, CFRangeMake(start, i - start));
start = kCFNotFound;
CFArrayAppendValue(result, sub);
CFRelease(sub);
}
} else if (start == kCFNotFound) {
start = i;
}
}
if (start != kCFNotFound) {
CFStringRef sub = CFStringCreateWithSubstring(NULL, str, CFRangeMake(start, i - start));
CFArrayAppendValue(result, sub);
CFRelease(sub);
}
CFRelease(whitespace);
return result;
}
void
arrayAppendArrayDistinct(CFMutableArrayRef array, CFArrayRef other) {
CFIndex i, count = CFArrayGetCount(other);
CFRange range = CFRangeMake(0, CFArrayGetCount(array));
for (i = 0; i < count; ++i) {
CFTypeRef o = CFArrayGetValueAtIndex(other, i);
if (!CFArrayContainsValue(array, range, o)) {
CFArrayAppendValue(array, o);
}
}
}
int
is_file(const char* path) {
struct stat sb;
return (stat(path, &sb) == 0 && S_ISREG(sb.st_mode));
}
void
upper_ident(char *str) {
char *cp;
for(cp = str; *cp; cp++) {
if (islower(*cp)) *cp = toupper(*cp);
else if (!isalnum(*cp) && *cp != '_') *cp = '_';
}
}
const char *
lockfilebylink(const char *lockfile) {
struct timeval t;
char *linkfile;
int fd;
struct stat st;
int n = 60;
if ((fd = open(lockfile, O_RDONLY|O_CREAT, 0644)) < 0) return NULL;
close(fd);
if (gettimeofday(&t, NULL) < 0) return NULL;
asprintf(&linkfile, "%s.%d-%lx.%x", lockfile, getpid(), t.tv_sec, t.tv_usec);
if (linkfile == NULL) return NULL;
while(n-- > 0) {
if (link(lockfile, linkfile) < 0) {
free((void *)linkfile);
return NULL;
}
if (stat(linkfile, &st) < 0) {
unlink(linkfile);
free((void *)linkfile);
return NULL;
}
if (st.st_nlink == 2) return linkfile;
unlink(linkfile);
sleep(1);
}
unlink(linkfile);
free((void *)linkfile);
return NULL;
}
void
unlockfilebylink(const char *linkfile) {
unlink(linkfile);
free((void *)linkfile);
}