?? zip.c
字號:
break; filepos -= (maxread - 4); } /* while */ BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1); if (len != NULL) *len = filelen; return(filepos + i);} /* zip_find_end_of_central_dir */static int ZIP_isArchive(const char *filename, int forWriting){ PHYSFS_uint32 sig; int retval = 0; void *in; in = __PHYSFS_platformOpenRead(filename); BAIL_IF_MACRO(in == NULL, NULL, 0); /* * The first thing in a zip file might be the signature of the * first local file record, so it makes for a quick determination. */ if (readui32(in, &sig)) { retval = (sig == ZIP_LOCAL_FILE_SIG); if (!retval) { /* * No sig...might be a ZIP with data at the start * (a self-extracting executable, etc), so we'll have to do * it the hard way... */ retval = (zip_find_end_of_central_dir(in, NULL) != -1); } /* if */ } /* if */ __PHYSFS_platformClose(in); return(retval);} /* ZIP_isArchive */static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max){ PHYSFS_uint32 i; for (i = 0; i < max; i++) { ZIPentry *entry = &entries[i]; if (entry->name != NULL) free(entry->name); } /* for */ free(entries);} /* zip_free_entries *//* * This will find the ZIPentry associated with a path in platform-independent * notation. Directories don't have ZIPentries associated with them, but * (*isDir) will be set to non-zero if a dir was hit. */static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir){ ZIPentry *a = info->entries; PHYSFS_sint32 pathlen = strlen(path); PHYSFS_sint32 lo = 0; PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); PHYSFS_sint32 middle; const char *thispath = NULL; int rc; while (lo <= hi) { middle = lo + ((hi - lo) / 2); thispath = a[middle].name; rc = strncmp(path, thispath, pathlen); if (rc > 0) lo = middle + 1; else if (rc < 0) hi = middle - 1; else /* substring match...might be dir or entry or nothing. */ { if (isDir != NULL) { *isDir = (thispath[pathlen] == '/'); if (*isDir) return(NULL); } /* if */ if (thispath[pathlen] == '\0') /* found entry? */ return(&a[middle]); else hi = middle - 1; /* adjust search params, try again. */ } /* if */ } /* while */ if (isDir != NULL) *isDir = 0; BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);} /* zip_find_entry *//* Convert paths from old, buggy DOS zippers... */static void zip_convert_dos_path(ZIPentry *entry, char *path){ PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF); if (hosttype == 0) /* FS_FAT_ */ { while (*path) { if (*path == '\\') *path = '/'; path++; } /* while */ } /* if */} /* zip_convert_dos_path */static void zip_expand_symlink_path(char *path){ char *ptr = path; char *prevptr = path; while (1) { ptr = strchr(ptr, '/'); if (ptr == NULL) break; if (*(ptr + 1) == '.') { if (*(ptr + 2) == '/') { /* current dir in middle of string: ditch it. */ memmove(ptr, ptr + 2, strlen(ptr + 2) + 1); } /* else if */ else if (*(ptr + 2) == '\0') { /* current dir at end of string: ditch it. */ *ptr = '\0'; } /* else if */ else if (*(ptr + 2) == '.') { if (*(ptr + 3) == '/') { /* parent dir in middle: move back one, if possible. */ memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1); ptr = prevptr; while (prevptr != path) { prevptr--; if (*prevptr == '/') { prevptr++; break; } /* if */ } /* while */ } /* if */ if (*(ptr + 3) == '\0') { /* parent dir at end: move back one, if possible. */ *prevptr = '\0'; } /* if */ } /* if */ } /* if */ else { prevptr = ptr; } /* else */ } /* while */} /* zip_expand_symlink_path *//* * Look for the entry named by (path). If it exists, resolve it, and return * a pointer to that entry. If it's another symlink, keep resolving until you * hit a real file and then return a pointer to the final non-symlink entry. * If there's a problem, return NULL. (path) is always free()'d by this * function. */static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path){ ZIPentry *entry; zip_expand_symlink_path(path); entry = zip_find_entry(info, path, NULL); if (entry != NULL) { if (!zip_resolve(in, info, entry)) /* recursive! */ entry = NULL; else { if (entry->symlink != NULL) entry = entry->symlink; } /* else */ } /* if */ free(path); return(entry);} /* zip_follow_symlink */static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry){ char *path; PHYSFS_uint32 size = entry->uncompressed_size; int rc = 0; /* * We've already parsed the local file header of the symlink at this * point. Now we need to read the actual link from the file data and * follow it. */ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); path = (char *) malloc(size + 1); BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0); if (entry->compression_method == COMPMETH_NONE) rc = (__PHYSFS_platformRead(in, path, size, 1) == 1); else /* symlink target path is compressed... */ { z_stream stream; PHYSFS_uint32 compsize = entry->compressed_size; PHYSFS_uint8 *compressed = (PHYSFS_uint8 *) malloc(compsize); if (compressed != NULL) { if (__PHYSFS_platformRead(in, compressed, compsize, 1) == 1) { memset(&stream, '\0', sizeof (z_stream)); stream.next_in = compressed; stream.avail_in = compsize; stream.next_out = (unsigned char *) path; stream.avail_out = size; if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) { rc = zlib_err(inflate(&stream, Z_FINISH)); inflateEnd(&stream); /* both are acceptable outcomes... */ rc = ((rc == Z_OK) || (rc == Z_STREAM_END)); } /* if */ } /* if */ free(compressed); } /* if */ } /* else */ if (!rc) free(path); else { path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ zip_convert_dos_path(entry, path); entry->symlink = zip_follow_symlink(in, info, path); } /* else */ return(entry->symlink != NULL);} /* zip_resolve_symlink *//* * Parse the local file header of an entry, and update entry->offset. */static int zip_parse_local(void *in, ZIPentry *entry){ PHYSFS_uint32 ui32; PHYSFS_uint16 ui16; PHYSFS_uint16 fnamelen; PHYSFS_uint16 extralen; /* * crc and (un)compressed_size are always zero if this is a "JAR" * archive created with Sun's Java tools, apparently. We only * consider this archive corrupted if those entries don't match and * aren't zero. That seems to work well. */ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0); BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0); BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0); BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0); BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0); BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0); BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); entry->offset += fnamelen + extralen + 30; return(1);} /* zip_parse_local */static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry){ int retval = 1; ZipResolveType resolve_type = entry->resolved; /* Don't bother if we've failed to resolve this entry before. */ BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0); BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0); /* uhoh...infinite symlink loop! */ BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0); /* * We fix up the offset to point to the actual data on the * first open, since we don't want to seek across the whole file on * archive open (can be SLOW on large, CD-stored files), but we * need to check the local file header...not just for corruption, * but since it stores offset info the central directory does not. */ if (resolve_type != ZIP_RESOLVED) { entry->resolved = ZIP_RESOLVING; retval = zip_parse_local(in, entry); if (retval) { /* * If it's a symlink, find the original file. This will cause * resolution of other entries (other symlinks and, eventually, * the real file) if all goes well. */ if (resolve_type == ZIP_UNRESOLVED_SYMLINK) retval = zip_resolve_symlink(in, info, entry); } /* if */ if (resolve_type == ZIP_UNRESOLVED_SYMLINK) entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK); else if (resolve_type == ZIP_UNRESOLVED_FILE) entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); } /* if */ return(retval);} /* zip_resolve */static int zip_version_does_symlinks(PHYSFS_uint32 version){ int retval = 0; PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); switch (hosttype) { /* * These are the platforms that can NOT build an archive with * symlinks, according to the Info-ZIP project. */ case 0: /* FS_FAT_ */ case 1: /* AMIGA_ */ case 2: /* VMS_ */ case 4: /* VM_CSM_ */ case 6: /* FS_HPFS_ */ case 11: /* FS_NTFS_ */ case 14: /* FS_VFAT_ */ case 13: /* ACORN_ */ case 15: /* MVS_ */ case 18: /* THEOS_ */ break; /* do nothing. */ default: /* assume the rest to be unix-like. */ retval = 1; break; } /* switch */ return(retval);} /* zip_version_does_symlinks */static int zip_entry_is_symlink(ZIPentry *entry){ return((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || (entry->resolved == ZIP_BROKEN_SYMLINK) || (entry->symlink));} /* zip_entry_is_symlink */static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr){ PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF); return ( (zip_version_does_symlinks(entry->version)) && (entry->uncompressed_size > 0) && ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );} /* zip_has_symlink_attr */static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime){#ifdef _WIN32_WCE /* We have no struct tm and no mktime right now. FIXME: This should probably be fixed at some point. */ return -1;#else PHYSFS_uint32 dosdate; struct tm unixtime; memset(&unixtime, '\0', sizeof (unixtime)); dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF); dostime &= 0xFFFF; /* dissect date */ unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; unixtime.tm_mday = ((dosdate ) & 0x1F); /* dissect time */ unixtime.tm_hour = ((dostime >> 11) & 0x1F); unixtime.tm_min = ((dostime >> 5) & 0x3F); unixtime.tm_sec = ((dostime << 1) & 0x3E); /* let mktime calculate daylight savings time. */ unixtime.tm_isdst = -1; return((PHYSFS_sint64) mktime(&unixtime));#endif} /* zip_dos_time_to_physfs_time */static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup){ PHYSFS_uint16 fnamelen, extralen, commentlen; PHYSFS_uint32 external_attr; PHYSFS_uint16 ui16; PHYSFS_uint32 ui32; PHYSFS_sint64 si64; /* sanity check with central directory signature... */ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0); /* Get the pertinent parts of the record... */ BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0); BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0); BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits */ BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0); BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0); BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0); BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0); BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0); BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */ BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0); BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0); entry->offset += ofs_fixup; entry->symlink = NULL; /* will be resolved later, if necessary. */ entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; entry->name = (char *) malloc(fnamelen + 1); BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0); if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1) goto zip_load_entry_puked; entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ zip_convert_dos_path(entry, entry->name); si64 = __PHYSFS_platformTell(in); if (si64 == -1)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -