/* Runtime MacOSX compatibility functionality Copyright (C) 2000 Free Software Foundation, Inc. Written by: Richard frith-Macdonald Date: August 2000 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include #include #ifndef HAVE_RINT #include static double rint(double a) { return (floor(a+0.5)); } #endif /* * Runtime MacOS-X compatibility flags. */ static BOOL setupDone = NO; static BOOL MacOSXCompatible = NO; static BOOL MacOSXCompatibleGeometry = NO; static BOOL MacOSXCompatiblePropertyLists = NO; /* * A trivial class to monitor user defaults to see how we should be * producing strings describing geometry structures. */ @interface GSBaseDefaultObserver : NSObject + (void) defaultsChanged: (NSNotification*)aNotification; @end @implementation GSBaseDefaultObserver + (void) defaultsChanged: (NSNotification*)aNotification { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; id def; Class sClass = [NSString class]; MacOSXCompatible = [defaults boolForKey: @"GSMacOSXCompatible"]; def = [defaults objectForKey: @"GSMacOSXCompatibleGeometry"]; if (def != nil && [def isKindOfClass: sClass] == YES) { MacOSXCompatibleGeometry = [def boolValue]; } else { MacOSXCompatibleGeometry = MacOSXCompatible; } def = [defaults objectForKey: @"GSMacOSXCompatiblePropertyLists"]; if (def != nil && [def isKindOfClass: sClass] == YES) { MacOSXCompatiblePropertyLists = [def boolValue]; } else { def = [defaults objectForKey: @"NSWriteOldStylePropertyLists"]; if (def != nil && [def isKindOfClass: sClass] == YES) { if ([def boolValue] == YES) { MacOSXCompatiblePropertyLists = NO; } else { MacOSXCompatiblePropertyLists = YES; } } else { MacOSXCompatiblePropertyLists = MacOSXCompatible; } } } @end static void compatibilitySetup() { if (setupDone == NO) { setupDone = YES; [[NSNotificationCenter defaultCenter] addObserver: [GSBaseDefaultObserver class] selector: @selector(defaultsChanged:) name: NSUserDefaultsDidChangeNotification object: nil]; [[GSBaseDefaultObserver class] defaultsChanged: nil]; } } BOOL GSMacOSXCompatible() { if (setupDone == NO) compatibilitySetup(); return MacOSXCompatible; } BOOL GSMacOSXCompatibleGeometry() { if (setupDone == NO) compatibilitySetup(); return MacOSXCompatibleGeometry; } BOOL GSMacOSXCompatiblePropertyLists() { if (setupDone == NO) compatibilitySetup(); return MacOSXCompatiblePropertyLists; } #include #include #include static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static NSString* encodeBase64(NSData *source) { int length = [source length]; int enclen = length / 3; int remlen = length - 3 * enclen; int destlen = 4 * ((length - 1) / 3) + 5; unsigned char *sBuf = (unsigned char*)[source bytes]; unsigned char *dBuf = NSZoneMalloc(NSDefaultMallocZone(), destlen); int sIndex = 0; int dIndex = 0; dBuf[destlen - 1] = '\0'; for (sIndex = 0; sIndex < length - 2; sIndex += 3, dIndex += 4) { dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; dBuf[dIndex + 1] = base64[((sBuf[sIndex] << 4) | (sBuf[sIndex + 1] >> 4)) & 0x3f]; dBuf[dIndex + 2] = base64[((sBuf[sIndex + 1] << 2) | (sBuf[sIndex + 2] >> 6)) & 0x3f]; dBuf[dIndex + 3] = base64[sBuf[sIndex + 2] & 0x3f]; } if (remlen == 1) { dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30; dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]]; dBuf[dIndex + 2] = '='; dBuf[dIndex + 3] = '='; } else if (remlen == 2) { dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30; dBuf[dIndex + 1] |= sBuf[sIndex + 1] >> 4; dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]]; dBuf[dIndex + 2] = (sBuf[sIndex + 1] << 2) & 0x3c; dBuf[dIndex + 2] = base64[dBuf[dIndex + 2]]; dBuf[dIndex + 3] = '='; } return [[NSString allocWithZone: NSDefaultMallocZone()] initWithCStringNoCopy: dBuf length: destlen-1 freeWhenDone: YES]; } static NSCharacterSet *quotables = nil; static void setupQuotables() { if (quotables == nil) { NSMutableCharacterSet *s; s = [[NSCharacterSet characterSetWithCharactersInString: @"&<>'\\\""] mutableCopy]; [s addCharactersInRange: NSMakeRange(0x0001, 0x001f)]; [s removeCharactersInRange: NSMakeRange(0x0009, 0x0002)]; [s removeCharactersInRange: NSMakeRange(0x000D, 0x0001)]; [s addCharactersInRange: NSMakeRange(0xD800, 0x07FF)]; [s addCharactersInRange: NSMakeRange(0xFFFE, 0x0002)]; quotables = [s copy]; RELEASE(s); } } static NSString* XMLString(NSString* obj) { static char *hexdigits = "0123456789ABCDEF"; unsigned end; end = [obj length]; if (end == 0) { return obj; } if (quotables == nil) { setupQuotables(); } if ([obj rangeOfCharacterFromSet: quotables].length > 0) { unichar *base; unichar *map; unichar c; unsigned len; unsigned rpos; unsigned wpos; base = NSZoneMalloc(NSDefaultMallocZone(), end * sizeof(unichar)); [obj getCharacters: base]; for (len = rpos = 0; rpos < end; rpos++) { c = base[rpos]; switch (c) { case '&': len += 5; break; case '<': case '>': len += 4; break; case '\'': case '"': len += 6; break; case '\\': len += 1; break; default: if (c < 0x20) { if (c == 0x09 || c == 0x0A || c == 0x0D) { len++; } else { len += 4; } } else if (c > 0xD7FF && c < 0xE000) { len += 6; } else if (c > 0xFFFD) { len += 6; } else { len++; } break; } } map = NSZoneMalloc(NSDefaultMallocZone(), len * sizeof(unichar)); for (wpos = rpos = 0; rpos < end; rpos++) { c = base[rpos]; switch (c) { case '&': map[wpos++] = '&'; map[wpos++] = 'a'; map[wpos++] = 'm'; map[wpos++] = 'p'; map[wpos++] = ';'; break; case '<': map[wpos++] = '&'; map[wpos++] = 'l'; map[wpos++] = 't'; map[wpos++] = ';'; break; case '>': map[wpos++] = '&'; map[wpos++] = 'g'; map[wpos++] = 't'; map[wpos++] = ';'; break; case '\'': map[wpos++] = '&'; map[wpos++] = 'a'; map[wpos++] = 'p'; map[wpos++] = 'o'; map[wpos++] = 's'; map[wpos++] = ';'; break; case '"': map[wpos++] = '&'; map[wpos++] = 'q'; map[wpos++] = 'u'; map[wpos++] = 'o'; map[wpos++] = 't'; map[wpos++] = ';'; break; case '\\': map[wpos++] = '\\'; map[wpos++] = '\\'; break; default: if (c < 0x20) { if (c == 0x09 || c == 0x0A || c == 0x0D) { map[wpos++] = c; } else { map[wpos++] = '\\'; map[wpos++] = '0' + ((c / 64) & 7); map[wpos++] = '0' + ((c / 8) & 7); map[wpos++] = '0' + (c & 7); } } else if (c > 0xD7FF && c < 0xE000) { map[wpos++] = '\\'; map[wpos++] = 'U'; map[wpos++] = hexdigits[(c>>12) & 0xf]; map[wpos++] = hexdigits[(c>>8) & 0xf]; map[wpos++] = hexdigits[(c>>4) & 0xf]; map[wpos++] = hexdigits[c & 0xf]; } else if (c > 0xFFFD) { map[wpos++] = '\\'; map[wpos++] = hexdigits[(c>>12) & 0xf]; map[wpos++] = hexdigits[(c>>8) & 0xf]; map[wpos++] = hexdigits[(c>>4) & 0xf]; map[wpos++] = hexdigits[c & 0xf]; map[wpos++] = '\\'; } else { map[wpos++] = c; } break; } } NSZoneFree(NSDefaultMallocZone(), base); return [NSString stringWithCharacters: map length: len]; } else { return obj; } } static NSString *indentStrings[] = { @"", @" ", @"\t", @"\t ", @"\t\t", @"\t\t ", @"\t\t\t", @"\t\t\t ", @"\t\t\t\t", @"\t\t\t\t ", @"\t\t\t\t\t", @"\t\t\t\t\t ", @"\t\t\t\t\t\t" }; static void XMLPlObject(NSMutableString *dest, id obj, NSDictionary *loc, unsigned lev) { if (lev >= sizeof(indentStrings) / sizeof(*indentStrings)) lev = sizeof(indentStrings) / sizeof(*indentStrings) - 1; [dest appendString: indentStrings[lev]]; if ([obj isKindOfClass: [NSString class]]) { [dest appendString: @""]; [dest appendString: XMLString(obj)]; [dest appendString: @"\n"]; } else if ([obj isKindOfClass: [NSNumber class]]) { double val = [obj doubleValue]; if (val == 1.0) { [dest appendString: @"\n"]; } else if (val == 0.0) { [dest appendString: @"\n"]; } else if (rint(val) == val) { [dest appendString: @""]; [dest appendString: [obj stringValue]]; [dest appendString: @"\n"]; } else { [dest appendString: @""]; [dest appendString: [obj stringValue]]; [dest appendString: @"\n"]; } } else if ([obj isKindOfClass: [NSData class]]) { [dest appendString: @""]; [dest appendString: encodeBase64(obj)]; [dest appendString: @"\n"]; } else if ([obj isKindOfClass: [NSDate class]]) { [dest appendString: @""]; [dest appendString: [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"]]; [dest appendString: @"\n"]; } else if ([obj isKindOfClass: [NSArray class]]) { NSEnumerator *e; [dest appendString: @"\n"]; e = [obj objectEnumerator]; while ((obj = [e nextObject])) { XMLPlObject(dest, obj, loc, lev + 1); } [dest appendString: indentStrings[lev]]; [dest appendString: @"\n"]; } else if ([obj isKindOfClass: [NSDictionary class]]) { NSEnumerator *e; id key; unsigned nxt = lev + 1; if (lev >= sizeof(indentStrings) / sizeof(*indentStrings)) lev = sizeof(indentStrings) / sizeof(*indentStrings) - 1; [dest appendString: @"\n"]; e = [obj keyEnumerator]; while ((key = [e nextObject])) { id val; val = [obj objectForKey: key]; [dest appendString: indentStrings[nxt]]; [dest appendString: @""]; [dest appendString: XMLString(key)]; [dest appendString: @"\n"]; XMLPlObject(dest, val, loc, nxt); } [dest appendString: indentStrings[lev]]; [dest appendString: @"\n"]; } else { NSLog(@"Non-property-list class encoded as string"); [dest appendString: @""]; [dest appendString: [obj description]]; [dest appendString: @"\n"]; } } NSString* GSXMLPlMake(id obj, NSDictionary *loc, unsigned lev) { NSMutableString *dest; dest = [NSMutableString stringWithCString: "\n\n" "\n"]; XMLPlObject(dest, obj, loc, 0); [dest appendString: @""]; return dest; }