/ CFBundle_Grok.c
CFBundle_Grok.c
   1  /*
   2   * Copyright (c) 2015 Apple Inc. All rights reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   *
   6   * This file contains Original Code and/or Modifications of Original Code
   7   * as defined in and that are subject to the Apple Public Source License
   8   * Version 2.0 (the 'License'). You may not use this file except in
   9   * compliance with the License. Please obtain a copy of the License at
  10   * http://www.opensource.apple.com/apsl/ and read it before using this
  11   * file.
  12   *
  13   * The Original Code and all software distributed under the License are
  14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   * Please see the License for the specific language governing rights and
  19   * limitations under the License.
  20   *
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  /*      CFBundle_Grok.c
  25          Copyright (c) 1999-2014, Apple Inc.  All rights reserved.
  26          Responsibility: Tony Parker
  27  */
  28  
  29  #include "CFBundle_Internal.h"
  30  
  31  #if defined(BINARY_SUPPORT_DYLD)
  32  // Import the mach-o headers that define the macho magic numbers
  33  #include <mach-o/loader.h>
  34  #include <mach-o/fat.h>
  35  #include <mach-o/arch.h>
  36  #include <mach-o/dyld.h>
  37  #include <mach-o/getsect.h>
  38  #include <unistd.h>
  39  #include <fcntl.h>
  40  #include <sys/mman.h>
  41  #include <crt_externs.h>
  42  #if defined(USE_DYLD_PRIV)
  43  #include <mach-o/dyld_priv.h>
  44  #endif /* USE_DYLD_PRIV */
  45  #endif /* BINARY_SUPPORT_DYLD */
  46  
  47  #if defined(BINARY_SUPPORT_DLFCN)
  48  #include <dlfcn.h>
  49  #endif /* BINARY_SUPPORT_DLFCN */
  50  
  51  #include <sys/stat.h>
  52  #include <ctype.h>
  53  
  54  #if DEPLOYMENT_TARGET_WINDOWS
  55  #define statinfo _stat
  56  #define stat(x,y) _NS_stat(x,y)
  57  #define open _NS_open
  58  #define MAP_FAILED 0
  59  
  60  // Windows isspace implementation limits the input chars to < 256 in the ASCII range.  It will
  61  // assert in debug builds.  This is annoying.  We merrily grok chars > 256.
  62  static inline BOOL isspace(char c) {
  63      return (c == ' ' || c == '\t' || c == '\n' || c == '\r'|| c == '\v' || c == '\f');
  64  }
  65  
  66  #else
  67  #define statinfo stat
  68  #endif
  69  
  70  #define UNKNOWN_FILETYPE 0x0
  71  #define PEF_FILETYPE 0x1000
  72  #define PEF_MAGIC 0x4a6f7921
  73  #define PEF_CIGAM 0x21796f4a
  74  #define TEXT_SEGMENT "__TEXT"
  75  #define PLIST_SECTION "__info_plist"
  76  #define OBJC_SEGMENT "__OBJC"
  77  #define IMAGE_INFO_SECTION "__image_info"
  78  #define OBJC_SEGMENT_64 "__DATA"
  79  #define IMAGE_INFO_SECTION_64 "__objc_imageinfo"
  80  #define LIB_X11 "/usr/X11R6/lib/libX"
  81  
  82  #define XLS_NAME "Book"
  83  #define XLS_NAME2 "Workbook"
  84  #define DOC_NAME "WordDocument"
  85  #define PPT_NAME "PowerPoint Document"
  86  
  87  #define ustrncmp(x, y, z) strncmp((char *)(x), (char *)(y), (z))
  88  #define ustrncasecmp(x, y, z) strncasecmp_l((char *)(x), (char *)(y), (z), NULL)
  89  
  90  static const uint32_t __CFBundleMagicNumbersArray[] = {
  91      0xcafebabe, 0xbebafeca, 0xfeedface, 0xcefaedfe, 0xfeedfacf, 0xcffaedfe, 0x4a6f7921, 0x21796f4a, 
  92      0x7f454c46, 0xffd8ffe0, 0x4d4d002a, 0x49492a00, 0x47494638, 0x89504e47, 0x69636e73, 0x00000100, 
  93      0x7b5c7274, 0x25504446, 0x2e7261fd, 0x2e524d46, 0x2e736e64, 0x2e736400, 0x464f524d, 0x52494646, 
  94      0x38425053, 0x000001b3, 0x000001ba, 0x4d546864, 0x504b0304, 0x53495421, 0x53495432, 0x53495435, 
  95      0x53495444, 0x53747566, 0x30373037, 0x3c212d2d, 0x25215053, 0xd0cf11e0, 0x62656769, 0x3d796265,
  96      0x6b6f6c79, 0x3026b275, 0x0000000c, 0xfe370023, 0x09020600, 0x09040600, 0x4f676753, 0x664c6143, 
  97      0x00010000, 0x74727565, 0x4f54544f, 0x41433130, 0xc809fe02, 0x0809fe02, 0x2356524d, 0x67696d70, 
  98      0x3c435058, 0x28445746, 0x424f4d53, 0x49544f4c, 0x72746664, 0x63616666, 0x802a5fd7, 0x762f3101
  99  };
 100  
 101  // string, with groups of 5 characters being 1 element in the array
 102  static const char * __CFBundleExtensionsArray =
 103      "mach\0"  "mach\0"  "mach\0"  "mach\0"  "mach\0"  "mach\0"  "pef\0\0" "pef\0\0" 
 104      "elf\0\0" "jpeg\0"  "tiff\0"  "tiff\0"  "gif\0\0" "png\0\0" "icns\0"  "ico\0\0" 
 105      "rtf\0\0" "pdf\0\0" "ra\0\0\0""rm\0\0\0""au\0\0\0""au\0\0\0""iff\0\0" "riff\0"  
 106      "psd\0\0" "mpeg\0"  "mpeg\0"  "mid\0\0" "zip\0\0" "sit\0\0" "sit\0\0" "sit\0\0" 
 107      "sit\0\0" "sit\0\0" "cpio\0"  "html\0"  "ps\0\0\0""ole\0\0" "uu\0\0\0""ync\0\0"
 108      "dmg\0\0" "wmv\0\0" "jp2\0\0" "doc\0\0" "xls\0\0" "xls\0\0" "ogg\0\0" "flac\0"
 109      "ttf\0\0" "ttf\0\0" "otf\0\0" "dwg\0\0" "dgn\0\0" "dgn\0\0" "wrl\0\0" "xcf\0\0"
 110      "cpx\0\0" "dwf\0\0" "bom\0\0" "lit\0\0" "rtfd\0"  "caf\0\0" "cin\0\0" "exr\0\0";
 111  
 112  static const char * __CFBundleOOExtensionsArray = "sxc\0\0" "sxd\0\0" "sxg\0\0" "sxi\0\0" "sxm\0\0" "sxw\0\0";
 113  static const char * __CFBundleODExtensionsArray = "odc\0\0" "odf\0\0" "odg\0\0" "oth\0\0" "odi\0\0" "odm\0\0" "odp\0\0" "ods\0\0" "odt\0\0";
 114  
 115  #define EXTENSION_LENGTH                5
 116  #define NUM_EXTENSIONS                  64
 117  #define MAGIC_BYTES_TO_READ             512
 118  #define DMG_BYTES_TO_READ               512
 119  #define ZIP_BYTES_TO_READ               1024
 120  #define OLE_BYTES_TO_READ               512
 121  #define X11_BYTES_TO_READ               4096
 122  #define IMAGE_INFO_BYTES_TO_READ        4096
 123  
 124  #if defined(BINARY_SUPPORT_DYLD)
 125  
 126  static CFMutableDictionaryRef _CFBundleCreateInfoDictFromData(const char *bytes, uint32_t length) {
 127      CFMutableDictionaryRef result = NULL;
 128      CFDataRef infoData = NULL;
 129      if (bytes && 0 < length) {
 130          infoData = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (uint8_t *)bytes, length, kCFAllocatorNull);
 131          if (infoData) {
 132              result = (CFMutableDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorSystemDefault, infoData, kCFPropertyListMutableContainers, NULL, NULL);
 133              if (result && CFDictionaryGetTypeID() != CFGetTypeID(result)) {
 134                  CFRelease(result);
 135                  result = NULL;
 136              }
 137              CFRelease(infoData);
 138          }
 139          if (!result) result = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 140      }
 141      if (result) _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef)result);
 142      return result;
 143  }
 144  
 145  static char *_CFBundleGetSectData(const char *segname, const char *sectname, unsigned long *size) {
 146      char *retval = NULL;
 147      unsigned long localSize = 0;
 148      uint32_t i, numImages = _dyld_image_count();
 149      const void *mhp = (const void *)_NSGetMachExecuteHeader();
 150      
 151      for (i = 0; i < numImages; i++) {
 152          if (mhp == (void *)_dyld_get_image_header(i)) {
 153  #if __LP64__
 154              const struct section_64 *sp = getsectbynamefromheader_64((const struct mach_header_64 *)mhp, segname, sectname);
 155              if (sp) {
 156                  retval = (char *)(sp->addr + _dyld_get_image_vmaddr_slide(i));
 157                  localSize = (unsigned long)sp->size;
 158              }
 159  #else /* __LP64__ */
 160              const struct section *sp = getsectbynamefromheader((const struct mach_header *)mhp, segname, sectname);
 161              if (sp) {
 162                  retval = (char *)(sp->addr + _dyld_get_image_vmaddr_slide(i));
 163                  localSize = (unsigned long)sp->size;
 164              }
 165  #endif /* __LP64__ */
 166              break;
 167          }
 168      }
 169      if (size) *size = localSize;
 170      return retval;
 171  }
 172  
 173  CF_PRIVATE CFMutableDictionaryRef _CFBundleCreateInfoDictFromMainExecutable() {
 174      char *bytes = NULL;
 175      unsigned long length = 0;
 176      if (getsegbyname(TEXT_SEGMENT)) bytes = _CFBundleGetSectData(TEXT_SEGMENT, PLIST_SECTION, &length);
 177      return _CFBundleCreateInfoDictFromData(bytes, length);
 178  }
 179  
 180  CF_PRIVATE Boolean _CFBundleGrokObjCImageInfoFromMainExecutable(uint32_t *objcVersion, uint32_t *objcFlags) {
 181      Boolean retval = false;
 182      uint32_t localVersion = 0, localFlags = 0;
 183      char *bytes = NULL;
 184      unsigned long length = 0;
 185  #if __LP64__
 186      if (getsegbyname(OBJC_SEGMENT_64)) bytes = _CFBundleGetSectData(OBJC_SEGMENT_64, IMAGE_INFO_SECTION_64, &length);
 187  #else /* __LP64__ */
 188      if (getsegbyname(OBJC_SEGMENT)) bytes = _CFBundleGetSectData(OBJC_SEGMENT, IMAGE_INFO_SECTION, &length);
 189  #endif /* __LP64__ */
 190      if (bytes && length >= 8) {
 191          localVersion = *(uint32_t *)bytes;
 192          localFlags = *(uint32_t *)(bytes + 4);
 193          retval = true;
 194      }
 195      if (objcVersion) *objcVersion = localVersion;
 196      if (objcFlags) *objcFlags = localFlags;
 197      return retval;
 198  }
 199  
 200  static Boolean _CFBundleGrokX11FromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) {
 201      static const char libX11name[] = LIB_X11;
 202      char *buffer = NULL;
 203      const char *loc = NULL;
 204      unsigned i;
 205      Boolean result = false;
 206      
 207      if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) {
 208          buffer = malloc(X11_BYTES_TO_READ);
 209          if (buffer && read(fd, buffer, X11_BYTES_TO_READ) >= X11_BYTES_TO_READ) loc = buffer;
 210      } else if (bytes && length >= offset + X11_BYTES_TO_READ) {
 211          loc = bytes + offset;
 212      }
 213      if (loc) {
 214          if (sixtyFour) {
 215              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->ncmds, swapped);
 216              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->sizeofcmds, swapped);
 217              const char *startofcmds = loc + sizeof(struct mach_header_64);
 218              const char *endofcmds = startofcmds + sizeofcmds;
 219              struct dylib_command *dlp = (struct dylib_command *)startofcmds;
 220              if (endofcmds > loc + X11_BYTES_TO_READ) endofcmds = loc + X11_BYTES_TO_READ;
 221              for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) {
 222                  if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) {
 223                      uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped);
 224                      const char *name = (const char *)dlp + nameoffset;
 225                      if (startofcmds <= name && name + sizeof(libX11name) <= endofcmds && 0 == strncmp(name, libX11name, sizeof(libX11name) - 1)) result = true;
 226                  }
 227                  dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped));
 228              }
 229          } else {
 230              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->ncmds, swapped);
 231              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->sizeofcmds, swapped);
 232              const char *startofcmds = loc + sizeof(struct mach_header);
 233              const char *endofcmds = startofcmds + sizeofcmds;
 234              struct dylib_command *dlp = (struct dylib_command *)startofcmds;
 235              if (endofcmds > loc + X11_BYTES_TO_READ) endofcmds = loc + X11_BYTES_TO_READ;
 236              for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) {
 237                  if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) {
 238                      uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped);
 239                      const char *name = (const char *)dlp + nameoffset;
 240                      if (startofcmds <= name && name + sizeof(libX11name) <= endofcmds && 0 == strncmp(name, libX11name, sizeof(libX11name) - 1)) result = true;
 241                  }
 242                  dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped));
 243              }
 244          }
 245      }
 246      if (buffer) free(buffer);
 247      return result;
 248  }
 249      
 250  static CFDictionaryRef _CFBundleCreateInfoDictFromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) {
 251      struct statinfo statBuf;
 252      off_t fileLength = 0;
 253      char *maploc = NULL;
 254      const char *loc;
 255      unsigned i, j;
 256      CFDictionaryRef result = NULL;
 257      Boolean foundit = false;
 258      if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) {
 259          loc = maploc;
 260          fileLength = statBuf.st_size;
 261      } else {
 262          loc = bytes;
 263          fileLength = length;
 264      }
 265      if (fileLength > offset + sizeof(struct mach_header_64)) {
 266          if (sixtyFour) {
 267              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->ncmds, swapped);
 268              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->sizeofcmds, swapped);
 269              const char *startofcmds = loc + offset + sizeof(struct mach_header_64);
 270              const char *endofcmds = startofcmds + sizeofcmds;
 271              struct segment_command_64 *sgp = (struct segment_command_64 *)startofcmds;
 272              if (endofcmds > loc + fileLength) endofcmds = loc + fileLength;
 273              for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) {
 274                  if (LC_SEGMENT_64 == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) {
 275                      struct section_64 *sp = (struct section_64 *)((char *)sgp + sizeof(struct segment_command_64));
 276                      uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped);
 277                      for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) {
 278                          if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, TEXT_SEGMENT, sizeof(sp->segname))) {
 279                              uint64_t sectlength64 = _CFBundleSwapInt64Conditional(sp->size, swapped);
 280                              uint32_t sectlength = (uint32_t)(sectlength64 & 0xffffffff);
 281                              uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped);
 282                              const char *sectbytes = loc + offset + sectoffset;
 283                              // we don't support huge-sized plists
 284                              if (sectlength64 <= 0xffffffff && loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = (CFDictionaryRef)_CFBundleCreateInfoDictFromData(sectbytes, sectlength);
 285                              foundit = true;
 286                          }
 287                          sp = (struct section_64 *)((char *)sp + sizeof(struct section_64));
 288                      }
 289                  }
 290                  sgp = (struct segment_command_64 *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped));
 291              }
 292          } else {
 293              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->ncmds, swapped);
 294              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->sizeofcmds, swapped);
 295              const char *startofcmds = loc + offset + sizeof(struct mach_header);
 296              const char *endofcmds = startofcmds + sizeofcmds;
 297              struct segment_command *sgp = (struct segment_command *)startofcmds;
 298              if (endofcmds > loc + fileLength) endofcmds = loc + fileLength;
 299              for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) {
 300                  if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) {
 301                      struct section *sp = (struct section *)((char *)sgp + sizeof(struct segment_command));
 302                      uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped);
 303                      for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) {
 304                          if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, TEXT_SEGMENT, sizeof(sp->segname))) {
 305                              uint32_t sectlength = _CFBundleSwapInt32Conditional(sp->size, swapped);
 306                              uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped);
 307                              const char *sectbytes = loc + offset + sectoffset;
 308                              if (loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = (CFDictionaryRef)_CFBundleCreateInfoDictFromData(sectbytes, sectlength);
 309                              foundit = true;
 310                          }
 311                          sp = (struct section *)((char *)sp + sizeof(struct section));
 312                      }
 313                  }
 314                  sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped));
 315              }
 316          }
 317      }
 318      if (maploc) munmap(maploc, statBuf.st_size);
 319      return result;
 320  }
 321  
 322  static void _CFBundleGrokObjcImageInfoFromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) {
 323      uint32_t sectlength = 0, sectoffset = 0, localVersion = 0, localFlags = 0;
 324      char *buffer = NULL;
 325      char sectbuffer[8];
 326      const char *loc = NULL;
 327      unsigned i, j;
 328      Boolean foundit = false, localHasObjc = false;
 329      
 330      if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) {
 331          buffer = malloc(IMAGE_INFO_BYTES_TO_READ);
 332          if (buffer && read(fd, buffer, IMAGE_INFO_BYTES_TO_READ) >= IMAGE_INFO_BYTES_TO_READ) loc = buffer;
 333      } else if (bytes && length >= offset + IMAGE_INFO_BYTES_TO_READ) {
 334          loc = bytes + offset;
 335      }
 336      if (loc) {
 337          if (sixtyFour) {
 338              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->ncmds, swapped);
 339              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->sizeofcmds, swapped);
 340              const char *startofcmds = loc + sizeof(struct mach_header_64);
 341              const char *endofcmds = startofcmds + sizeofcmds;
 342              struct segment_command_64 *sgp = (struct segment_command_64 *)startofcmds;
 343              if (endofcmds > loc + IMAGE_INFO_BYTES_TO_READ) endofcmds = loc + IMAGE_INFO_BYTES_TO_READ;
 344              for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) {
 345                  if (LC_SEGMENT_64 == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) {
 346                      struct section_64 *sp = (struct section_64 *)((char *)sgp + sizeof(struct segment_command_64));
 347                      uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped);
 348                      for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) {
 349                          if (0 == strncmp(sp->segname, OBJC_SEGMENT_64, sizeof(sp->segname))) localHasObjc = true;
 350                          if (0 == strncmp(sp->sectname, IMAGE_INFO_SECTION_64, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, OBJC_SEGMENT_64, sizeof(sp->segname))) {
 351                              uint64_t sectlength64 = _CFBundleSwapInt64Conditional(sp->size, swapped);
 352                              sectlength = (uint32_t)(sectlength64 & 0xffffffff);
 353                              sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped);
 354                              foundit = true;
 355                          }
 356                          sp = (struct section_64 *)((char *)sp + sizeof(struct section_64));
 357                      }
 358                  }
 359                  sgp = (struct segment_command_64 *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped));
 360              }
 361          } else {
 362              uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->ncmds, swapped);
 363              uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->sizeofcmds, swapped);
 364              const char *startofcmds = loc + sizeof(struct mach_header);
 365              const char *endofcmds = startofcmds + sizeofcmds;
 366              struct segment_command *sgp = (struct segment_command *)startofcmds;
 367              if (endofcmds > loc + IMAGE_INFO_BYTES_TO_READ) endofcmds = loc + IMAGE_INFO_BYTES_TO_READ;
 368              for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) {
 369                  if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) {
 370                      struct section *sp = (struct section *)((char *)sgp + sizeof(struct segment_command));
 371                      uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped);
 372                      for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) {
 373                          if (0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) localHasObjc = true;
 374                          if (0 == strncmp(sp->sectname, IMAGE_INFO_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) {
 375                              sectlength = _CFBundleSwapInt32Conditional(sp->size, swapped);
 376                              sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped);
 377                              foundit = true;
 378                          }
 379                          sp = (struct section *)((char *)sp + sizeof(struct section));
 380                      }
 381                  }
 382                  sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped));
 383              }
 384          }
 385          if (sectlength >= 8) {
 386              if (fd >= 0 && lseek(fd, offset + sectoffset, SEEK_SET) == (off_t)(offset + sectoffset) && read(fd, sectbuffer, 8) >= 8) {
 387                  localVersion = _CFBundleSwapInt32Conditional(*(uint32_t *)sectbuffer, swapped);
 388                  localFlags = _CFBundleSwapInt32Conditional(*(uint32_t *)(sectbuffer + 4), swapped);
 389              } else if (bytes && length >= offset + sectoffset + 8) {
 390                  localVersion = _CFBundleSwapInt32Conditional(*(uint32_t *)(bytes + offset + sectoffset), swapped);
 391                  localFlags = _CFBundleSwapInt32Conditional(*(uint32_t *)(bytes + offset + sectoffset + 4), swapped);
 392              }
 393          }
 394      }
 395      if (buffer) free(buffer);
 396      if (hasObjc) *hasObjc = localHasObjc;
 397      if (objcVersion) *objcVersion = localVersion;
 398      if (objcFlags) *objcFlags = localFlags;
 399  }
 400  
 401  static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, const void *bytes, CFIndex length, Boolean swap, Boolean *isX11, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) {
 402      CFIndex headerLength = length;
 403      unsigned char headerBuffer[MAGIC_BYTES_TO_READ];
 404      UInt32 machtype = UNKNOWN_FILETYPE, magic, numFatHeaders, maxFatHeaders, i;
 405      unsigned char buffer[sizeof(struct mach_header_64)];
 406      const unsigned char *moreBytes = NULL;
 407      const NXArchInfo *archInfo = NXGetLocalArchInfo();
 408      SInt32 curArch = _CFBundleCurrentArchitecture();
 409      
 410      struct fat_arch *fat = NULL;
 411  
 412      if (isX11) *isX11 = false;
 413      if (architectures) *architectures = NULL;
 414      if (infodict) *infodict = NULL;
 415      if (hasObjc) *hasObjc = false;
 416      if (objcVersion) *objcVersion = 0;
 417      if (objcFlags) *objcFlags = 0;
 418  
 419      if (headerLength > MAGIC_BYTES_TO_READ) headerLength = MAGIC_BYTES_TO_READ;
 420      (void)memmove(headerBuffer, bytes, headerLength);
 421      if (swap) {
 422          for (i = 0; i < headerLength; i += 4) *(UInt32 *)(headerBuffer + i) = CFSwapInt32(*(UInt32 *)(headerBuffer + i));
 423      }
 424      numFatHeaders = ((struct fat_header *)headerBuffer)->nfat_arch;
 425      maxFatHeaders = (headerLength - sizeof(struct fat_header)) / sizeof(struct fat_arch);
 426      if (numFatHeaders > maxFatHeaders) numFatHeaders = maxFatHeaders;
 427      if (numFatHeaders > 0) {
 428          if (archInfo) fat = NXFindBestFatArch(archInfo->cputype, archInfo->cpusubtype, (struct fat_arch *)(headerBuffer + sizeof(struct fat_header)), numFatHeaders);
 429          if (!fat && curArch != 0) fat = NXFindBestFatArch((cpu_type_t)curArch, (cpu_subtype_t)0, (struct fat_arch *)(headerBuffer + sizeof(struct fat_header)), numFatHeaders);
 430          if (!fat) fat = (struct fat_arch *)(headerBuffer + sizeof(struct fat_header));
 431          if (architectures) {
 432              CFMutableArrayRef mutableArchitectures = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
 433              for (i = 0; i < numFatHeaders; i++) {
 434                  CFNumberRef architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, headerBuffer + sizeof(struct fat_header) + i * sizeof(struct fat_arch));
 435                  if (CFArrayGetFirstIndexOfValue(mutableArchitectures, CFRangeMake(0, CFArrayGetCount(mutableArchitectures)), architecture) < 0) CFArrayAppendValue(mutableArchitectures, architecture);
 436                  CFRelease(architecture);
 437              }
 438              *architectures = (CFArrayRef)mutableArchitectures;
 439          }
 440      } 
 441      if (fat) {
 442          if (fd >= 0 && lseek(fd, fat->offset, SEEK_SET) == (off_t)fat->offset && read(fd, buffer, sizeof(struct mach_header_64)) >= (int)sizeof(struct mach_header_64)) {
 443              moreBytes = buffer;
 444          } else if (bytes && (uint32_t)length >= fat->offset + sizeof(struct mach_header_64)) {
 445              moreBytes = bytes + fat->offset;
 446          }
 447          if (moreBytes) {
 448              magic = *((UInt32 *)moreBytes);
 449              if (MH_MAGIC == magic) {
 450                  machtype = ((struct mach_header *)moreBytes)->filetype;
 451                  if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, false, false);
 452                  if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, fat->offset, false, false);
 453                  if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, false, false, hasObjc, objcVersion, objcFlags);
 454              } else if (MH_CIGAM == magic) {
 455                  machtype = CFSwapInt32(((struct mach_header *)moreBytes)->filetype);
 456                  if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, true, false);
 457                  if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, fat->offset, true, false);
 458                  if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, true, false, hasObjc, objcVersion, objcFlags);
 459              } else if (MH_MAGIC_64 == magic) {
 460                  machtype = ((struct mach_header_64 *)moreBytes)->filetype;
 461                  if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, false, true);
 462                  if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, fat->offset, false, true);
 463                  if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, false, true, hasObjc, objcVersion, objcFlags);
 464              } else if (MH_CIGAM_64 == magic) {
 465                  machtype = CFSwapInt32(((struct mach_header_64 *)moreBytes)->filetype);
 466                  if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, true, true);
 467                  if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, fat->offset, true, true);
 468                  if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, true, true, hasObjc, objcVersion, objcFlags);
 469              }
 470          }
 471      }
 472      return machtype;
 473  }
 474  
 475  static UInt32 _CFBundleGrokMachType(int fd, const void *bytes, CFIndex length, Boolean *isX11, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) {
 476      unsigned int magic = *((UInt32 *)bytes), machtype = UNKNOWN_FILETYPE, cputype;
 477      CFNumberRef architecture = NULL;
 478  
 479      if (isX11) *isX11 = false;
 480      if (architectures) *architectures = NULL;
 481      if (infodict) *infodict = NULL;
 482      if (hasObjc) *hasObjc = false;
 483      if (objcVersion) *objcVersion = 0;
 484      if (objcFlags) *objcFlags = 0;
 485      if (MH_MAGIC == magic) {
 486          machtype = ((struct mach_header *)bytes)->filetype;
 487          cputype = ((struct mach_header *)bytes)->cputype;
 488          if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &cputype);
 489          if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, false, false);
 490          if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, 0, false, false);
 491          if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, false, false, hasObjc, objcVersion, objcFlags);
 492      } else if (MH_CIGAM == magic) {
 493          machtype = CFSwapInt32(((struct mach_header *)bytes)->filetype);
 494          cputype = CFSwapInt32(((struct mach_header *)bytes)->cputype);
 495          if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &cputype);
 496          if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, true, false);
 497          if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, 0, true, false);
 498          if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, true, false, hasObjc, objcVersion, objcFlags);
 499      } else if (MH_MAGIC_64 == magic) {
 500          machtype = ((struct mach_header_64 *)bytes)->filetype;
 501          cputype = ((struct mach_header_64 *)bytes)->cputype;
 502          if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &cputype);
 503          if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, false, true);
 504          if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, 0, false, true);
 505          if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, false, true, hasObjc, objcVersion, objcFlags);
 506      } else if (MH_CIGAM_64 == magic) {
 507          machtype = CFSwapInt32(((struct mach_header_64 *)bytes)->filetype);
 508          cputype = CFSwapInt32(((struct mach_header_64 *)bytes)->cputype);
 509          if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &cputype);
 510          if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, true, true);
 511          if (infodict) *infodict = _CFBundleCreateInfoDictFromFile(fd, bytes, length, 0, true, true);
 512          if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, true, true, hasObjc, objcVersion, objcFlags);
 513      } else if (FAT_MAGIC == magic) {
 514          machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, false, isX11, architectures, infodict, hasObjc, objcVersion, objcFlags);
 515      } else if (FAT_CIGAM == magic) {
 516          machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, true, isX11, architectures, infodict, hasObjc, objcVersion, objcFlags);
 517      } else if (PEF_MAGIC == magic || PEF_CIGAM == magic) {
 518          machtype = PEF_FILETYPE;
 519      }
 520      if (architectures && architecture) *architectures = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&architecture, 1, &kCFTypeArrayCallBacks);
 521      if (architecture) CFRelease(architecture);
 522      return machtype;
 523  }
 524  
 525  #endif /* BINARY_SUPPORT_DYLD */
 526  
 527  static Boolean _CFBundleGrokFileTypeForZipMimeType(const unsigned char *bytes, CFIndex length, const char **ext) {
 528      unsigned namelength = CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 26))), extralength = CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 28)));
 529      const unsigned char *data = bytes + 30 + namelength + extralength;
 530      int i = -1;
 531      if (bytes < data && data + 56 <= bytes + length && 0 == CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 8))) && (0 == ustrncasecmp(data, "application/vnd.", 16) || 0 == ustrncasecmp(data, "application/x-vnd.", 18))) {
 532          data += ('.' == *(data + 15)) ? 16 : 18;
 533          if (0 == ustrncasecmp(data, "sun.xml.", 8)) {
 534              data += 8;
 535              if (0 == ustrncasecmp(data, "calc", 4)) i = 0;
 536              else if (0 == ustrncasecmp(data, "draw", 4)) i = 1;
 537              else if (0 == ustrncasecmp(data, "writer.global", 13)) i = 2;
 538              else if (0 == ustrncasecmp(data, "impress", 7)) i = 3;
 539              else if (0 == ustrncasecmp(data, "math", 4)) i = 4;
 540              else if (0 == ustrncasecmp(data, "writer", 6)) i = 5;
 541              if (i >= 0 && ext) *ext = __CFBundleOOExtensionsArray + i * EXTENSION_LENGTH;
 542          } else if (0 == ustrncasecmp(data, "oasis.opendocument.", 19)) {
 543              data += 19;
 544              if (0 == ustrncasecmp(data, "chart", 5)) i = 0;
 545              else if (0 == ustrncasecmp(data, "formula", 7)) i = 1;
 546              else if (0 == ustrncasecmp(data, "graphics", 8)) i = 2;
 547              else if (0 == ustrncasecmp(data, "text-web", 8)) i = 3;
 548              else if (0 == ustrncasecmp(data, "image", 5)) i = 4;
 549              else if (0 == ustrncasecmp(data, "text-master", 11)) i = 5;
 550              else if (0 == ustrncasecmp(data, "presentation", 12)) i = 6;
 551              else if (0 == ustrncasecmp(data, "spreadsheet", 11)) i = 7;
 552              else if (0 == ustrncasecmp(data, "text", 4)) i = 8;
 553              if (i >= 0 && ext) *ext = __CFBundleODExtensionsArray + i * EXTENSION_LENGTH;
 554          }
 555      } else if (bytes < data && data + 41 <= bytes + length && 8 == CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 8))) && 0x4b2c28c8 == CFSwapInt32HostToBig(*((UInt32 *)data)) && 0xc94c4e2c == CFSwapInt32HostToBig(*((UInt32 *)(data + 4)))) {
 556          // AbiWord compressed mimetype odt
 557          if (ext) *ext = "odt";
 558          // almost certainly this should set i to 0 but I don't want to upset the apple cart now
 559      } else if (bytes < data && data + 29 <= bytes + length && (0 == ustrncasecmp(data, "application/oebps-package+xml", 29))) {
 560          // epub, official epub 3 mime type
 561          if (ext) *ext = "epub";
 562          i = 0;
 563      } else if (bytes < data && data + 20 <= bytes + length && (0 == ustrncasecmp(data, "application/epub+zip", 20))) {
 564          // epub, unofficial epub 2 mime type
 565          if (ext) *ext = "epub";
 566          i = 0;
 567      }
 568      return (i >= 0);
 569  }
 570  
 571  static const char *_CFBundleGrokFileTypeForZipFile(int fd, const unsigned char *bytes, CFIndex length, off_t fileLength) {
 572      const char *ext = "zip";
 573      const unsigned char *moreBytes = NULL;
 574      unsigned char *buffer = NULL;
 575      CFIndex i;
 576      Boolean foundMimetype = false, hasMetaInf = false, hasContentXML = false, hasManifestMF = false, hasManifestXML = false, hasRels = false, hasContentTypes = false, hasWordDocument = false, hasExcelDocument = false, hasPowerPointDocument = false, hasOPF = false, hasSMIL = false;
 577  
 578      if (bytes) {
 579          for (i = 0; !foundMimetype && i + 30 < length; i++) {
 580              if (0x50 == bytes[i] && 0x4b == bytes[i + 1]) {
 581                  unsigned namelength = 0, offset = 0;
 582                  if (0x01 == bytes[i + 2] && 0x02 == bytes[i + 3]) {
 583                      namelength = (unsigned)CFSwapInt16HostToLittle(*((UInt16 *)(bytes + i + 28)));
 584                      offset = 46;
 585                  } else if (0x03 == bytes[i + 2] && 0x04 == bytes[i + 3]) {
 586                      namelength = (unsigned)CFSwapInt16HostToLittle(*((UInt16 *)(bytes + i + 26)));
 587                      offset = 30;
 588                  }
 589                  if (offset > 0 && (CFIndex)(i + offset + namelength) <= length) {
 590                      //printf("%.*s\n", namelength, bytes + i + offset);
 591                      if (8 == namelength && 30 == offset && 0 == ustrncasecmp(bytes + i + offset, "mimetype", 8)) foundMimetype = _CFBundleGrokFileTypeForZipMimeType(bytes + i, length - i, &ext);
 592                      else if (9 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/", 9)) hasMetaInf = true;
 593                      else if (11 == namelength && 0 == ustrncasecmp(bytes + i + offset, "content.xml", 11)) hasContentXML = true;
 594                      else if (11 == namelength && 0 == ustrncasecmp(bytes + i + offset, "_rels/.rels", 11)) hasRels = true;
 595                      else if (19 == namelength && 0 == ustrncasecmp(bytes + i + offset, "[Content_Types].xml", 19)) hasContentTypes = true;
 596                      else if (20 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/MANIFEST.MF", 20)) hasManifestMF = true;
 597                      else if (21 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/manifest.xml", 21)) hasManifestXML = true;
 598                      else if (4 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".opf", 4)) hasOPF = true;
 599                      else if (4 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".sml", 4)) hasSMIL = true;
 600                      else if (5 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 5, ".smil", 5)) hasSMIL = true;
 601                      else if (7 < namelength && 0 == ustrncasecmp(bytes + i + offset, "xl/", 3) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true;
 602                      else if (8 < namelength && 0 == ustrncasecmp(bytes + i + offset, "ppt/", 4) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true;
 603                      else if (9 < namelength && 0 == ustrncasecmp(bytes + i + offset, "word/", 5) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasWordDocument = true;
 604                      else if (10 < namelength && 0 == ustrncasecmp(bytes + i + offset, "excel/", 6) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true;
 605                      else if (15 < namelength && 0 == ustrncasecmp(bytes + i + offset, "powerpoint/", 11) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true;
 606                      i += offset + namelength - 1;
 607                  }
 608              }
 609          }
 610      }
 611      if (!foundMimetype) {
 612          if (fileLength >= ZIP_BYTES_TO_READ) {
 613              if (fd >= 0 && lseek(fd, fileLength - ZIP_BYTES_TO_READ, SEEK_SET) == fileLength - ZIP_BYTES_TO_READ) {
 614                  buffer = (unsigned char *)malloc(ZIP_BYTES_TO_READ);
 615                  if (buffer && read(fd, buffer, ZIP_BYTES_TO_READ) >= ZIP_BYTES_TO_READ) moreBytes = buffer;
 616              } else if (bytes && length >= ZIP_BYTES_TO_READ) {
 617                  moreBytes = bytes + length - ZIP_BYTES_TO_READ;
 618              }
 619          }
 620          if (moreBytes) {
 621              for (i = 0; i + 30 < ZIP_BYTES_TO_READ; i++) {
 622                  if (0x50 == moreBytes[i] && 0x4b == moreBytes[i + 1]) {
 623                      unsigned namelength = 0, offset = 0;
 624                      if (0x01 == moreBytes[i + 2] && 0x02 == moreBytes[i + 3]) {
 625                          namelength = CFSwapInt16HostToLittle(*((UInt16 *)(moreBytes + i + 28)));
 626                          offset = 46;
 627                      } else if (0x03 == moreBytes[i + 2] && 0x04 == moreBytes[i + 3]) {
 628                          namelength = CFSwapInt16HostToLittle(*((UInt16 *)(moreBytes + i + 26)));
 629                          offset = 30;
 630                      }
 631                      if (offset > 0 && i + offset + namelength <= ZIP_BYTES_TO_READ) {
 632                          //printf("%.*s\n", namelength, moreBytes + i + offset);
 633                          if (9 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/", 9)) hasMetaInf = true;
 634                          else if (11 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "content.xml", 11)) hasContentXML = true;
 635                          else if (11 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "_rels/.rels", 11)) hasRels = true;
 636                          else if (19 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "[Content_Types].xml", 19)) hasContentTypes = true;
 637                          else if (20 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/MANIFEST.MF", 20)) hasManifestMF = true;
 638                          else if (21 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/manifest.xml", 21)) hasManifestXML = true;
 639                          else if (4 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".opf", 4)) hasOPF = true;
 640                          else if (4 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".sml", 4)) hasSMIL = true;
 641                          else if (5 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 5, ".smil", 5)) hasSMIL = true;
 642                          else if (7 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "xl/", 3) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true;
 643                          else if (8 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "ppt/", 4) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true;
 644                          else if (9 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "word/", 5) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasWordDocument = true;
 645                          else if (10 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "excel/", 6) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true;
 646                          else if (15 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "powerpoint/", 11) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true;
 647                          i += offset + namelength - 1;
 648                      }
 649                  }
 650              }
 651          }
 652          //printf("hasManifestMF %d hasManifestXML %d hasContentXML %d hasRels %d hasContentTypes %d hasWordDocument %d hasExcelDocument %d hasPowerPointDocument %d hasMetaInf %d hasOPF %d hasSMIL %d\n", hasManifestMF, hasManifestXML, hasContentXML, hasRels, hasContentTypes, hasWordDocument, hasExcelDocument, hasPowerPointDocument, hasMetaInf, hasOPF, hasSMIL);
 653          if (hasManifestMF) ext = "jar";
 654          else if ((hasRels || hasContentTypes) && hasWordDocument) ext = "docx";
 655          else if ((hasRels || hasContentTypes) && hasExcelDocument) ext = "xlsx";
 656          else if ((hasRels || hasContentTypes) && hasPowerPointDocument) ext = "pptx";
 657          else if (hasManifestXML || hasContentXML) ext = "odt";
 658          else if (hasMetaInf) ext = "jar";
 659          else if (hasOPF && hasSMIL) ext = "dtb";
 660          else if (hasOPF) ext = "oeb";
 661  
 662          if (buffer) free(buffer);
 663      }
 664      return ext;
 665  }
 666  
 667  static Boolean _CFBundleCheckOLEName(const char *name, const char *bytes, unsigned length) {
 668      Boolean retval = true;
 669      unsigned j;
 670      for (j = 0; retval && j < length; j++) if (bytes[2 * j] != name[j]) retval = false;
 671      return retval;
 672  }
 673  
 674  static const char *_CFBundleGrokFileTypeForOLEFile(int fd, const void *bytes, CFIndex length, off_t offset) {
 675      const char *ext = "ole", *moreBytes = NULL;
 676      char *buffer = NULL;
 677      
 678      if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) {
 679          buffer = (char *)malloc(OLE_BYTES_TO_READ);
 680          if (buffer && read(fd, buffer, OLE_BYTES_TO_READ) >= OLE_BYTES_TO_READ) moreBytes = buffer;
 681      } else if (bytes && length >= offset + OLE_BYTES_TO_READ) {
 682          moreBytes = (char *)bytes + offset;
 683      }
 684      if (moreBytes) {
 685          Boolean foundit = false;
 686          unsigned i;
 687          for (i = 0; !foundit && i < 4; i++) {
 688              char namelength = moreBytes[128 * i + 64] / 2;
 689              foundit = true;
 690              if (sizeof(XLS_NAME) == namelength && _CFBundleCheckOLEName(XLS_NAME, moreBytes + 128 * i, namelength - 1)) ext = "xls";
 691              else if (sizeof(XLS_NAME2) == namelength && _CFBundleCheckOLEName(XLS_NAME2, moreBytes + 128 * i, namelength - 1)) ext = "xls";
 692              else if (sizeof(DOC_NAME) == namelength && _CFBundleCheckOLEName(DOC_NAME, moreBytes + 128 * i, namelength - 1)) ext = "doc";
 693              else if (sizeof(PPT_NAME) == namelength && _CFBundleCheckOLEName(PPT_NAME, moreBytes + 128 * i, namelength - 1)) ext = "ppt";
 694              else foundit = false;
 695          }
 696      }
 697      if (buffer) free(buffer);
 698      return ext;
 699  }
 700  
 701  static Boolean _CFBundleGrokFileType(CFURLRef url, CFDataRef data, CFStringRef *extension, UInt32 *machtype, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) {
 702      int fd = -1;
 703      const unsigned char *bytes = NULL;
 704      unsigned char buffer[MAGIC_BYTES_TO_READ];
 705      CFIndex i, length = 0;
 706      off_t fileLength = 0;
 707      const char *ext = NULL;
 708      UInt32 mt = UNKNOWN_FILETYPE;
 709  #if defined(BINARY_SUPPORT_DYLD)
 710      Boolean isX11 = false;
 711  #endif /* BINARY_SUPPORT_DYLD */
 712      Boolean isFile = false, isPlain = true, isZero = true, isSpace = true, hasBOM = false;
 713      // extensions returned:  o, tool, x11app, pef, core, dylib, bundle, elf, jpeg, jp2, tiff, gif, png, pict, icns, ico, rtf, rtfd, pdf, ra, rm, au, aiff, aifc, caf, wav, avi, wmv, ogg, flac, psd, mpeg, mid, zip, jar, sit, cpio, html, ps, mov, qtif, ttf, otf, sfont, bmp, hqx, bin, class, tar, txt, gz, Z, uu, ync, bz, bz2, sh, pl, py, rb, dvi, sgi, tga, mp3, xml, plist, xls, doc, ppt, mp4, m4a, m4b, m4p, m4v, 3gp, 3g2, dmg, cwk, webarchive, dwg, dgn, pfa, pfb, afm, tfm, xcf, cpx, dwf, swf, swc, abw, bom, lit, svg, rdf, x3d, oeb, dtb, docx, xlsx, pptx, sxc, sxd, sxg, sxi, sxm, sxw, odc, odf, odg, oth, odi, odm, odp, ods, cin, exr
 714      // ??? we do not distinguish between different wm types, returning wmv for any of wmv, wma, or asf
 715      // ??? we do not distinguish between ordinary documents and template versions (often there is no difference in file contents)
 716      // ??? the distinctions between docx, xlsx, and pptx may not be entirely reliable
 717      if (architectures) *architectures = NULL;
 718      if (infodict) *infodict = NULL;
 719      if (hasObjc) *hasObjc = false;
 720      if (objcVersion) *objcVersion = 0;
 721      if (objcFlags) *objcFlags = 0;
 722      if (url) {
 723          Boolean gotPath = FALSE;
 724          char path[CFMaxPathSize];
 725          gotPath = CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize);
 726          struct statinfo statBuf;
 727          if (gotPath && stat(path, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG && (fd = open(path, O_RDONLY | CF_OPENFLGS, 0777)) >= 0) {
 728              length = read(fd, buffer, MAGIC_BYTES_TO_READ);
 729              fileLength = statBuf.st_size;
 730              bytes = buffer;
 731              isFile = true;
 732          }
 733      }
 734      if (!isFile && data) {
 735          length = CFDataGetLength(data);
 736          fileLength = (off_t)length;
 737          bytes = CFDataGetBytePtr(data);
 738          if (length == 0) ext = "txt";
 739      }
 740      if (bytes) {
 741          if (length >= 4) {
 742              UInt32 magic = CFSwapInt32HostToBig(*((UInt32 *)bytes));
 743              for (i = 0; !ext && i < NUM_EXTENSIONS; i++) {
 744                  if (__CFBundleMagicNumbersArray[i] == magic) ext = __CFBundleExtensionsArray + i * EXTENSION_LENGTH;
 745              }
 746              if (ext) {
 747                  if (0xcafebabe == magic && 8 <= length && 0 != *((UInt16 *)(bytes + 4))) ext = "class";
 748  #if defined(BINARY_SUPPORT_DYLD)
 749                  else if ((int)sizeof(struct mach_header_64) <= length) mt = _CFBundleGrokMachType(fd, bytes, length, extension ? &isX11 : NULL, architectures, infodict, hasObjc, objcVersion, objcFlags);
 750                  
 751                  if (MH_OBJECT == mt) ext = "o";
 752                  else if (MH_EXECUTE == mt) ext = isX11 ? "x11app" : "tool";
 753                  else if (PEF_FILETYPE == mt) ext = "pef";
 754                  else if (MH_CORE == mt) ext = "core";
 755                  else if (MH_DYLIB == mt) ext = "dylib";
 756                  else if (MH_BUNDLE == mt) ext = "bundle";
 757  #endif /* BINARY_SUPPORT_DYLD */
 758                  else if (0x7b5c7274 == magic && (6 > length || 'f' != bytes[4])) ext = NULL;
 759                  else if (0x25504446 == magic && (6 > length || '-' != bytes[4])) ext = NULL;
 760                  else if (0x00010000 == magic && (6 > length || 0 != bytes[4])) ext = NULL;
 761                  else if (0x47494638 == magic && (6 > length || (0x3761 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && 0x3961 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))))  ext = NULL;
 762                  else if (0x0000000c == magic && (6 > length || 0x6a50 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL;
 763                  else if (0x2356524d == magic && (6 > length || 0x4c20 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL;
 764                  else if (0x28445746 == magic && (6 > length || 0x2056 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL;
 765                  else if (0x30373037 == magic && (6 > length || 0x30 != bytes[4] || !isdigit(bytes[5]))) ext = NULL;
 766                  else if (0x41433130 == magic && (6 > length || 0x31 != bytes[4] || !isdigit(bytes[5]))) ext = NULL;
 767                  else if (0x89504e47 == magic && (8 > length || 0x0d0a1a0a != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 768                  else if (0x53747566 == magic && (8 > length || 0x66497420 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 769                  else if (0x3026b275 == magic && (8 > length || 0x8e66cf11 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 770                  else if (0x67696d70 == magic && (8 > length || 0x20786366 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 771                  else if (0x424f4d53 == magic && (8 > length || 0x746f7265 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 772                  else if (0x49544f4c == magic && (8 > length || 0x49544c53 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 773                  else if (0x72746664 == magic && (8 > length || 0x00000000 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL;
 774                  else if (0x3d796265 == magic && (12 > length || 0x67696e20 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || (0x6c696e65 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8))) && 0x70617274 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))))) ext = NULL;
 775                  else if (0x63616666 == magic && (12 > length || 0 != bytes[4] || 0x64657363 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8))))) ext = NULL;
 776                  else if (0x504b0304 == magic) ext = _CFBundleGrokFileTypeForZipFile(fd, bytes, length, fileLength);
 777                  else if (0x25215053 == magic) {
 778                      if (11 <= length && 0 == ustrncmp(bytes + 4, "-Adobe-", 7)) ext = "ps";
 779                      else if (14 <= length && 0 == ustrncmp(bytes + 4, "-AdobeFont", 10)) ext = "pfa";
 780                      else ext = NULL;
 781                  } else if (0x464f524d == magic) {
 782                      // IFF
 783                      ext = NULL;
 784                      if (12 <= length) {
 785                          UInt32 iffMagic = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)));
 786                          if (0x41494646 == iffMagic) ext = "aiff";
 787                          else if (0x414946 == iffMagic) ext = "aifc";
 788                      }
 789                  } else if (0x52494646 == magic) {
 790                      // RIFF
 791                      ext = NULL;
 792                      if (12 <= length) {
 793                          UInt32 riffMagic = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)));
 794                          if (0x57415645 == riffMagic) ext = "wav";
 795                          else if (0x41564920 == riffMagic) ext = "avi";
 796                      }
 797                  } else if (0xd0cf11e0 == magic) {
 798                      // OLE
 799                      if (52 <= length) ext = _CFBundleGrokFileTypeForOLEFile(fd, bytes, length, 512 * (1 + CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 48)))));
 800                  } else if (0x62656769 == magic) {
 801                      // uu
 802                      ext = NULL;
 803                      if (76 <= length && 'n' == bytes[4] && ' ' == bytes[5] && isdigit(bytes[6]) && isdigit(bytes[7]) && isdigit(bytes[8]) && ' ' == bytes[9]) {
 804                          CFIndex endOfLine = 0;
 805                          for (i = 10; 0 == endOfLine && i < length; i++) if ('\n' == bytes[i]) endOfLine = i;
 806                          if (10 <= endOfLine && endOfLine + 62 < length && 'M' == bytes[endOfLine + 1] && '\n' == bytes[endOfLine + 62]) {
 807                              ext = "uu";
 808                              for (i = endOfLine + 1; ext && i < endOfLine + 62; i++) if (!isprint(bytes[i])) ext = NULL;
 809                          }
 810                      }
 811                  }
 812              }
 813              if (extension && !ext) {
 814                  UInt16 shortMagic = CFSwapInt16HostToBig(*((UInt16 *)bytes));
 815                  if (5 <= length && 0 == bytes[3] && 0 == bytes[4] && ((1 == bytes[1] && 1 == (0xf7 & bytes[2])) || (0 == bytes[1] && (2 == (0xf7 & bytes[2]) || (3 == (0xf7 & bytes[2])))))) ext = "tga";
 816                  else if (8 <= length && (0x6d6f6f76 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x6d646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x77696465 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "mov";
 817                  else if (8 <= length && (0x69647363 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x69646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "qtif";
 818                  else if (8 <= length && 0x424f424f == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) ext = "cwk";
 819                  else if (8 <= length && 0x62706c69 == magic && 0x7374 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && isdigit(bytes[6]) && isdigit(bytes[7])) {
 820                      for (i = 8; !ext && i < 128 && i + 16 <= length; i++) {
 821                          if (0 == ustrncmp(bytes + i, "WebMainResource", 15)) ext = "webarchive";
 822                      }
 823                      if (!ext) ext = "plist";
 824                  } else if (0 == shortMagic && 12 <= length && 0x66747970 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) {
 825                      // ??? may want more ftyp values
 826                      UInt32 ftyp = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)));
 827                      if (0x6d703431 == ftyp || 0x6d703432 == ftyp || 0x69736f6d == ftyp || 0x69736f32 == ftyp) ext = "mp4";
 828                      else if (0x4d344120 == ftyp) ext = "m4a";
 829                      else if (0x4d344220 == ftyp) ext = "m4b";
 830                      else if (0x4d345020 == ftyp) ext = "m4p";
 831                      else if (0x4d345620 == ftyp || 0x4d345648 == ftyp || 0x4d345650 == ftyp) ext = "m4v";
 832                      else if (0x3367 == (ftyp >> 16)) {
 833                          UInt16 remainder = (ftyp & 0xffff);
 834                          if (0x6536 == remainder || 0x6537 == remainder || 0x6736 == remainder || 0x7034 == remainder || 0x7035 == remainder || 0x7036 == remainder || 0x7236 == remainder || 0x7336 == remainder || 0x7337 == remainder) ext = "3gp";
 835                          else if (0x3261 == remainder) ext = "3g2";
 836                      }
 837                  } else if (0x424d == shortMagic && 18 <= length) {
 838                      UInt32 btyp = CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 14)));
 839                      if (40 == btyp || btyp == 12 || btyp == 64 || btyp == 108 || btyp == 124) ext = "bmp";
 840                  } else if (20 <= length && 0 == ustrncmp(bytes + 6, "%!PS-AdobeFont", 14)) ext = "pfb";
 841                  else if (40 <= length && 0x42696e48 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 34))) && 0x6578 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 38)))) ext = "hqx";
 842                  else if (128 <= length && 0x6d42494e == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 102)))) ext = "bin";
 843                  else if (128 <= length && 0 == bytes[0] && 0 < bytes[1] && bytes[1] < 64 && 0 == bytes[74] && 0 == bytes[82] && 0 == (fileLength % 128)) {
 844                      UInt32 df = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 83))), rf = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 87))), blocks = 1 + (df + 127) / 128 + (rf + 127) / 128;
 845                      if (df < 0x00800000 && rf < 0x00800000 && 1 < blocks && (off_t)(128 * blocks) == fileLength) ext = "bin";
 846                  } else if (265 <= length && 0x75737461 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 257))) && (0x72202000 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 261))) || 0x7200 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 261))))) ext = "tar";
 847                  else if (0xfeff == shortMagic || 0xfffe == shortMagic) {
 848                      ext = "txt";
 849                      if (12 <= length && ((0x3cfeff == *((UInt32 *)bytes) && 0x740068 == *((UInt32 *)(bytes + 4)) && 0x6c006d == *((UInt32 *)(bytes + 8))) || (0xfffe3c00 == *((UInt32 *)bytes) && 0x68007400 == *((UInt32 *)(bytes + 4)) && 0x6d006c00 == *((UInt32 *)(bytes + 8))))) ext = "html";
 850                  } else if (0x1f9d == shortMagic) ext = "Z";
 851                  else if (0x1f8b == shortMagic) ext = "gz";
 852                  else if (0x71c7 == shortMagic || 0xc771 == shortMagic) ext = "cpio";
 853                  else if (0xf702 == shortMagic) ext = "dvi";
 854                  else if (0x01da == shortMagic && (0 == bytes[2] || 1 == bytes[2]) && (0 < bytes[3] && 16 > bytes[3])) ext = "sgi";
 855                  else if (0x2321 == shortMagic) {
 856                      CFIndex endOfLine = 0, lastSlash = 0;
 857                      for (i = 2; 0 == endOfLine && i < length; i++) if ('\n' == bytes[i]) endOfLine = i;
 858                      if (endOfLine > 3) {
 859                          for (i = endOfLine - 1; 0 == lastSlash && i > 1; i--) if ('/' == bytes[i]) lastSlash = i;
 860                          if (lastSlash > 0) {
 861                              if (0 == ustrncmp(bytes + lastSlash + 1, "perl", 4)) ext = "pl";
 862                              else if (0 == ustrncmp(bytes + lastSlash + 1, "python", 6)) ext = "py";
 863                              else if (0 == ustrncmp(bytes + lastSlash + 1, "ruby", 4)) ext = "rb";
 864                              else ext = "sh";
 865                          }
 866                      } 
 867                  } else if (0xffd8 == shortMagic && 0xff == bytes[2]) ext = "jpeg";
 868                  else if (0x4657 == shortMagic && 0x53 == bytes[2]) ext = "swf";
 869                  else if (0x4357 == shortMagic && 0x53 == bytes[2]) ext = "swc";
 870                  else if (0x4944 == shortMagic && '3' == bytes[2] && 0x20 > bytes[3]) ext = "mp3";
 871                  else if (0x425a == shortMagic && isdigit(bytes[2]) && isdigit(bytes[3])) ext = "bz";
 872                  else if (0x425a == shortMagic && 'h' == bytes[2] && isdigit(bytes[3]) && 8 <= length && (0x31415926 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x17724538 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "bz2";
 873                  else if (0x0011 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2))) || 0x0012 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2)))) ext = "tfm";
 874              }
 875          }
 876          if (extension && !ext) {
 877              //??? what about MacOSRoman?
 878              if (0xef == bytes[0] && 0xbb == bytes[1] && 0xbf == bytes[2]) {   // UTF-8 BOM
 879                  hasBOM = true;
 880                  isZero = false;
 881              }
 882              for (i = (hasBOM ? 3 : 0); (isPlain || isZero) && !ext && i < length && i < 512; i++) {
 883                  char c = bytes[i];
 884                  if (isPlain && '<' == c && i + 14 <= length && 0 == ustrncasecmp(bytes + i + 1, "!doctype html", 13)) ext = "html";
 885                  if (isSpace && '<' == c && i + 14 <= length) {
 886                      if (0 == ustrncasecmp(bytes + i + 1, "!doctype html", 13) || 0 == ustrncasecmp(bytes + i + 1, "head", 4) || 0 == ustrncasecmp(bytes + i + 1, "title", 5) || 0 == ustrncasecmp(bytes + i + 1, "script", 6) || 0 == ustrncasecmp(bytes + i + 1, "html", 4)) {
 887                          ext = "html";
 888                      } else if (0 == ustrncasecmp(bytes + i + 1, "?xml", 4)) {
 889                          for (i += 4; !ext && i < 128 && i + 20 <= length; i++) {
 890                              if ('<' == bytes[i]) {
 891                                  if (0 == ustrncasecmp(bytes + i + 1, "abiword", 7)) ext = "abw";
 892                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype svg", 12)) ext = "svg";
 893                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype rdf", 12)) ext = "rdf";
 894                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype x3d", 12)) ext = "x3d";
 895                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype html", 13)) ext = "html";
 896                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype posingfont", 19)) ext = "sfont";
 897                                  else if (0 == ustrncasecmp(bytes + i + 1, "!doctype plist", 14)) {
 898                                      for (i += 14; !ext && i < 256 && i + 16 <= length; i++) {
 899                                          if (0 == ustrncmp(bytes + i, "WebMainResource", 15)) ext = "webarchive";
 900                                      }
 901                                      if (!ext) ext = "plist";
 902                                  }
 903                              }
 904                          }
 905                          if (!ext) ext = "xml";
 906                      }
 907                  }
 908                  if (0 != c) isZero = false;
 909                  if (isZero || 0x7f <= c || (0x20 > c && !isspace(c))) isPlain = false;
 910                  if (isZero || !isspace(c)) isSpace = false;
 911              }
 912              if (!ext) {
 913                  if (isPlain) {
 914                      if (16 <= length && 0 == ustrncmp(bytes, "StartFontMetrics", 16)) ext = "afm";
 915                      else ext = "txt";
 916                  } else if (isZero && length >= MAGIC_BYTES_TO_READ && fileLength >= 526) {
 917                      if (isFile) {
 918                          if (lseek(fd, 512, SEEK_SET) == 512 && read(fd, buffer, MAGIC_BYTES_TO_READ) >= 14) {
 919                              if (0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(buffer + 10)))) ext = "pict";
 920                          }
 921                      } else {
 922                          if (526 <= length && 0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 522)))) ext = "pict";
 923                      }
 924                  }
 925              }
 926          }
 927          if (extension && (!ext || 0 == strcmp(ext, "bz2")) && length >= MAGIC_BYTES_TO_READ && fileLength >= DMG_BYTES_TO_READ) {
 928              if (isFile) {
 929                  if (lseek(fd, fileLength - DMG_BYTES_TO_READ, SEEK_SET) == fileLength - DMG_BYTES_TO_READ && read(fd, buffer, DMG_BYTES_TO_READ) >= DMG_BYTES_TO_READ) {
 930                      if (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)buffer)) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + DMG_BYTES_TO_READ - 8))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + DMG_BYTES_TO_READ - 4))))) ext = "dmg";
 931                  }
 932              } else {
 933                  if (DMG_BYTES_TO_READ <= length && (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - DMG_BYTES_TO_READ))) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 8))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 4)))))) ext = "dmg";
 934              }
 935          }
 936      }
 937      if (extension) *extension = ext ? CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, ext, kCFStringEncodingUTF8, kCFAllocatorNull) : NULL;
 938      if (machtype) *machtype = mt;
 939      if (fd >= 0) close(fd);
 940      return (ext ? true : false);
 941  }
 942  
 943  CFStringRef _CFBundleCopyFileTypeForFileURL(CFURLRef url) {
 944      CFStringRef extension = NULL;
 945      (void)_CFBundleGrokFileType(url, NULL, &extension, NULL, NULL, NULL, NULL, NULL, NULL);
 946      return extension;
 947  }
 948  
 949  CFStringRef _CFBundleCopyFileTypeForFileData(CFDataRef data) {
 950      CFStringRef extension = NULL;
 951      (void)_CFBundleGrokFileType(NULL, data, &extension, NULL, NULL, NULL, NULL, NULL, NULL);
 952      return extension;
 953  }
 954  
 955  CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInExecutable(CFURLRef url) {
 956      CFDictionaryRef result = NULL;
 957      (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, NULL, &result, NULL, NULL, NULL);
 958      return result;
 959  }
 960  
 961  CF_PRIVATE CFArrayRef _CFBundleCopyArchitecturesForExecutable(CFURLRef url) {
 962      CFArrayRef result = NULL;
 963      (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, &result, NULL, NULL, NULL, NULL);
 964      return result;
 965  }
 966  
 967  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
 968  static Boolean _CFBundleGetObjCImageInfoForExecutable(CFURLRef url, uint32_t *objcVersion, uint32_t *objcFlags) {
 969      Boolean retval = false;
 970      (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, NULL, NULL, &retval, objcVersion, objcFlags);
 971      return retval;
 972  }
 973  
 974  CF_PRIVATE Boolean _CFBundleGetObjCImageInfo(CFBundleRef bundle, uint32_t *objcVersion, uint32_t *objcFlags) {
 975      Boolean retval = false;
 976      uint32_t localVersion = 0, localFlags = 0;
 977      CFURLRef executableURL = CFBundleCopyExecutableURL(bundle);
 978      if (executableURL) {
 979          retval = _CFBundleGetObjCImageInfoForExecutable(executableURL, &localVersion, &localFlags);
 980          CFRelease(executableURL);
 981      }
 982      if (objcVersion) *objcVersion = localVersion;
 983      if (objcFlags) *objcFlags = localFlags;
 984      return retval;
 985  }
 986  #endif
 987  
 988  #if defined(BINARY_SUPPORT_DYLD)
 989  
 990  CF_PRIVATE __CFPBinaryType _CFBundleGrokBinaryType(CFURLRef executableURL) {
 991      // Attempt to grok the type of the binary by looking for DYLD magic numbers.  If one of the DYLD magic numbers is found, find out what type of Mach-o file it is.  Otherwise, look for the PEF magic numbers to see if it is CFM.
 992      __CFPBinaryType result = executableURL ? __CFBundleUnreadableBinary : __CFBundleNoBinary;
 993      UInt32 machtype = UNKNOWN_FILETYPE;
 994      if (_CFBundleGrokFileType(executableURL, NULL, NULL, &machtype, NULL, NULL, NULL, NULL, NULL)) {
 995          switch (machtype) {
 996              case MH_EXECUTE:
 997                  result = __CFBundleDYLDExecutableBinary;
 998                  break;
 999              case MH_BUNDLE:
1000                  result = __CFBundleDYLDBundleBinary;
1001                  break;
1002              case MH_DYLIB:
1003                  result = __CFBundleDYLDFrameworkBinary;
1004                  break;
1005              case PEF_FILETYPE:
1006                  result = __CFBundleCFMBinary;
1007                  break;
1008          }
1009      }
1010      return result;
1011  }
1012  
1013  #endif /* BINARY_SUPPORT_DYLD */
1014